Update app.py
Browse files
app.py
CHANGED
|
@@ -2,12 +2,9 @@ import streamlit as st
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import time
|
| 5 |
-
import subprocess
|
| 6 |
import random
|
| 7 |
-
import
|
| 8 |
-
import
|
| 9 |
-
import shutil
|
| 10 |
-
import mimetypes
|
| 11 |
|
| 12 |
BASE_FOLDER = "uploaded_files"
|
| 13 |
CATEGORIES = ["AVATAR WORLD", "BLOX FRUITS", "TOCA LIFE"]
|
|
@@ -34,11 +31,11 @@ st.markdown("""
|
|
| 34 |
</style>
|
| 35 |
""", unsafe_allow_html=True)
|
| 36 |
|
| 37 |
-
# Cria
|
| 38 |
for cat in CATEGORIES:
|
| 39 |
os.makedirs(os.path.join(BASE_FOLDER, cat), exist_ok=True)
|
| 40 |
|
| 41 |
-
# Carrega
|
| 42 |
if os.path.exists(EXPIRATION_FILE):
|
| 43 |
with open(EXPIRATION_FILE, "r") as f:
|
| 44 |
expirations = json.load(f)
|
|
@@ -47,13 +44,11 @@ else:
|
|
| 47 |
|
| 48 |
st.title("📂 File Manager por Categoria")
|
| 49 |
|
| 50 |
-
# ---
|
| 51 |
def remove_expired_files():
|
| 52 |
-
"""Remove arquivos expirados automaticamente"""
|
| 53 |
changed = False
|
| 54 |
now = time.time()
|
| 55 |
expired_files = []
|
| 56 |
-
|
| 57 |
for file_full, expire_time in list(expirations.items()):
|
| 58 |
cat, file = file_full.split("|||")
|
| 59 |
file_path = os.path.join(BASE_FOLDER, cat, file)
|
|
@@ -62,82 +57,72 @@ def remove_expired_files():
|
|
| 62 |
os.remove(file_path)
|
| 63 |
expired_files.append(file_full)
|
| 64 |
changed = True
|
| 65 |
-
|
| 66 |
for file_full in expired_files:
|
| 67 |
expirations.pop(file_full)
|
| 68 |
-
|
| 69 |
if changed:
|
| 70 |
with open(EXPIRATION_FILE, "w") as f:
|
| 71 |
json.dump(expirations, f)
|
| 72 |
|
| 73 |
-
|
| 74 |
-
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
|
| 75 |
-
|
| 76 |
-
def process_video(file_path, hard_mode=False):
|
| 77 |
-
"""Processa vídeo removendo metadados e mascarando vestígios do ffmpeg"""
|
| 78 |
-
temp_fd, temp_out = tempfile.mkstemp(suffix=".mp4")
|
| 79 |
-
os.close(temp_fd)
|
| 80 |
-
|
| 81 |
-
if not hard_mode:
|
| 82 |
-
shutil.copy(file_path, temp_out)
|
| 83 |
-
return temp_out
|
| 84 |
-
|
| 85 |
-
# Randomizações técnicas
|
| 86 |
-
crf = str(random.randint(18, 26)) # qualidade variável
|
| 87 |
-
bitrate = f"{random.randint(96, 192)}k"
|
| 88 |
-
fps = f"{random.uniform(29.5, 30.5):.2f}"
|
| 89 |
-
brand = random.choice(["isom", "iso2", "mp42", "avc1", "dash", "iso3"])
|
| 90 |
-
compressor = random_string(random.randint(6, 12))
|
| 91 |
-
encoder_name = random_string(random.randint(6, 14))
|
| 92 |
-
|
| 93 |
-
# Metadados falsos
|
| 94 |
-
meta = {
|
| 95 |
-
"title": random_string(random.randint(8, 20)),
|
| 96 |
-
"comment": random_string(random.randint(8, 20)),
|
| 97 |
-
"description": random_string(random.randint(8, 20)),
|
| 98 |
-
"artist": random_string(random.randint(8, 20)),
|
| 99 |
-
"album": random_string(random.randint(8, 20)),
|
| 100 |
-
"genre": random_string(random.randint(5, 15)),
|
| 101 |
-
"publisher": random_string(random.randint(5, 15)),
|
| 102 |
-
"encoded_by": random_string(random.randint(5, 15)),
|
| 103 |
-
"location": random_string(random.randint(5, 15)),
|
| 104 |
-
"encoder": encoder_name
|
| 105 |
-
}
|
| 106 |
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
"
|
| 112 |
-
"
|
| 113 |
-
"
|
| 114 |
-
"
|
| 115 |
-
"-brand", brand,
|
| 116 |
-
"-metadata", f"encoder={encoder_name}",
|
| 117 |
-
"-metadata", f"CompressorName={compressor}",
|
| 118 |
-
"-fflags", "+bitexact",
|
| 119 |
-
"-flags", "+bitexact",
|
| 120 |
]
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
-
#
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
# --- Upload ---
|
| 133 |
st.header("📤 Upload de Arquivos")
|
| 134 |
-
st.subheader("Selecione uma categoria:")
|
| 135 |
-
|
| 136 |
categoria = st.radio("Categoria:", CATEGORIES, index=None)
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
if "last_upload" not in st.session_state:
|
| 140 |
-
st.session_state.last_upload = None
|
| 141 |
|
| 142 |
if categoria:
|
| 143 |
uploaded_files = st.file_uploader(
|
|
@@ -149,77 +134,56 @@ if categoria:
|
|
| 149 |
|
| 150 |
if uploaded_files:
|
| 151 |
for uploaded_file in uploaded_files:
|
| 152 |
-
if st.session_state.last_upload == uploaded_file.name:
|
| 153 |
-
continue
|
| 154 |
-
|
| 155 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 156 |
-
os.
|
| 157 |
-
|
| 158 |
-
mime, _ = mimetypes.guess_type(uploaded_file.name)
|
| 159 |
-
is_video = mime and mime.startswith("video")
|
| 160 |
-
|
| 161 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_file.name)[1]) as tmp:
|
| 162 |
-
tmp.write(uploaded_file.read())
|
| 163 |
-
tmp_path = tmp.name
|
| 164 |
-
|
| 165 |
-
if is_video and hard_mode:
|
| 166 |
-
final_tmp = process_video(tmp_path, hard_mode=True)
|
| 167 |
-
else:
|
| 168 |
-
final_tmp = tmp_path
|
| 169 |
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
else:
|
| 173 |
-
new_name = uploaded_file.name
|
| 174 |
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
if auto_delete:
|
| 179 |
-
key = f"{categoria}|||{
|
| 180 |
expirations[key] = time.time() + 24 * 60 * 60
|
| 181 |
with open(EXPIRATION_FILE, "w") as f:
|
| 182 |
json.dump(expirations, f)
|
| 183 |
|
| 184 |
-
|
| 185 |
-
|
| 186 |
|
| 187 |
-
# --- Lista de Arquivos
|
| 188 |
st.header("📄 Arquivos Disponíveis")
|
| 189 |
-
|
| 190 |
for categoria in CATEGORIES:
|
| 191 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 192 |
files = os.listdir(folder)
|
| 193 |
-
|
| 194 |
st.subheader(f"📁 {categoria}")
|
| 195 |
-
|
| 196 |
if not files:
|
| 197 |
st.info("Nenhum arquivo na categoria.")
|
| 198 |
else:
|
| 199 |
for file in files:
|
| 200 |
st.markdown(f"<div class='file-box'>", unsafe_allow_html=True)
|
| 201 |
st.markdown(f"<div class='file-name'>{file}</div>", unsafe_allow_html=True)
|
| 202 |
-
|
| 203 |
key = f"{categoria}|||{file}"
|
| 204 |
-
|
| 205 |
expira = expirations.get(key)
|
| 206 |
if expira:
|
| 207 |
restante = int(expira - time.time())
|
| 208 |
if restante > 0:
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
st.markdown(
|
| 212 |
-
f"<div class='expire-text'>⏳ Expira em {restante_horas}h {restante_min}min</div>",
|
| 213 |
-
unsafe_allow_html=True)
|
| 214 |
-
else:
|
| 215 |
-
st.markdown("<div class='expire-text'>⚠ Expiração iminente</div>", unsafe_allow_html=True)
|
| 216 |
|
| 217 |
col1, col2, col3 = st.columns(3)
|
| 218 |
-
|
| 219 |
with col1:
|
| 220 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
| 221 |
st.download_button("⬇ Download", f_obj, file_name=file, key=f"down_{categoria}_{file}")
|
| 222 |
-
|
| 223 |
with col2:
|
| 224 |
if st.button("🗑 Excluir", key=f"delete_{categoria}_{file}"):
|
| 225 |
os.remove(os.path.join(folder, file))
|
|
@@ -227,7 +191,7 @@ for categoria in CATEGORIES:
|
|
| 227 |
with open(EXPIRATION_FILE, "w") as f:
|
| 228 |
json.dump(expirations, f)
|
| 229 |
st.success(f"Arquivo '{file}' excluído.")
|
| 230 |
-
|
| 231 |
with col3:
|
| 232 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
| 233 |
if st.download_button("⬇ Baixar & Apagar", f_obj, file_name=file, key=f"download_delete_{categoria}_{file}"):
|
|
@@ -236,5 +200,5 @@ for categoria in CATEGORIES:
|
|
| 236 |
with open(EXPIRATION_FILE, "w") as f:
|
| 237 |
json.dump(expirations, f)
|
| 238 |
st.success(f"Arquivo '{file}' baixado e removido.")
|
| 239 |
-
|
| 240 |
st.markdown("</div>", unsafe_allow_html=True)
|
|
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import time
|
|
|
|
| 5 |
import random
|
| 6 |
+
import datetime
|
| 7 |
+
import subprocess
|
|
|
|
|
|
|
| 8 |
|
| 9 |
BASE_FOLDER = "uploaded_files"
|
| 10 |
CATEGORIES = ["AVATAR WORLD", "BLOX FRUITS", "TOCA LIFE"]
|
|
|
|
| 31 |
</style>
|
| 32 |
""", unsafe_allow_html=True)
|
| 33 |
|
| 34 |
+
# Cria pastas
|
| 35 |
for cat in CATEGORIES:
|
| 36 |
os.makedirs(os.path.join(BASE_FOLDER, cat), exist_ok=True)
|
| 37 |
|
| 38 |
+
# Carrega expirações
|
| 39 |
if os.path.exists(EXPIRATION_FILE):
|
| 40 |
with open(EXPIRATION_FILE, "r") as f:
|
| 41 |
expirations = json.load(f)
|
|
|
|
| 44 |
|
| 45 |
st.title("📂 File Manager por Categoria")
|
| 46 |
|
| 47 |
+
# --- Função remover expirados ---
|
| 48 |
def remove_expired_files():
|
|
|
|
| 49 |
changed = False
|
| 50 |
now = time.time()
|
| 51 |
expired_files = []
|
|
|
|
| 52 |
for file_full, expire_time in list(expirations.items()):
|
| 53 |
cat, file = file_full.split("|||")
|
| 54 |
file_path = os.path.join(BASE_FOLDER, cat, file)
|
|
|
|
| 57 |
os.remove(file_path)
|
| 58 |
expired_files.append(file_full)
|
| 59 |
changed = True
|
|
|
|
| 60 |
for file_full in expired_files:
|
| 61 |
expirations.pop(file_full)
|
|
|
|
| 62 |
if changed:
|
| 63 |
with open(EXPIRATION_FILE, "w") as f:
|
| 64 |
json.dump(expirations, f)
|
| 65 |
|
| 66 |
+
remove_expired_files()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
+
# --- Função aleatorizar metadados com ffmpeg ---
|
| 69 |
+
def randomize_video_metadata(input_path, output_path, progress_placeholder):
|
| 70 |
+
major_brands = ["isom", "mp42", "iso5", "avc1"]
|
| 71 |
+
compatible_sets = [
|
| 72 |
+
["isom", "iso2", "avc1", "mp41"],
|
| 73 |
+
["mp42", "isom", "avc1"],
|
| 74 |
+
["iso5", "mp41", "isom"],
|
| 75 |
+
["iso2", "mp42", "isom"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
]
|
| 77 |
+
major = random.choice(major_brands)
|
| 78 |
+
compatible = ",".join(random.choice(compatible_sets))
|
| 79 |
+
|
| 80 |
+
# Gera datas aleatórias
|
| 81 |
+
start_date = datetime.datetime.now() - datetime.timedelta(days=180)
|
| 82 |
+
random_date = start_date + datetime.timedelta(days=random.randint(0, 180),
|
| 83 |
+
seconds=random.randint(0, 86400))
|
| 84 |
+
date_str = random_date.strftime("%Y-%m-%d %H:%M:%S")
|
| 85 |
+
|
| 86 |
+
def randstr(n=12):
|
| 87 |
+
return ''.join(random.choices("abcdefghijklmnopqrstuvwxyz0123456789", k=n))
|
| 88 |
+
|
| 89 |
+
metadata = {
|
| 90 |
+
"title": randstr(8),
|
| 91 |
+
"artist": randstr(10),
|
| 92 |
+
"album": randstr(10),
|
| 93 |
+
"comment": randstr(15),
|
| 94 |
+
"description": randstr(12),
|
| 95 |
+
"genre": randstr(8),
|
| 96 |
+
"publisher": randstr(10),
|
| 97 |
+
"encoded_by": randstr(10),
|
| 98 |
+
"creation_time": date_str,
|
| 99 |
+
"major_brand": major,
|
| 100 |
+
"compatible_brands": compatible,
|
| 101 |
+
}
|
| 102 |
|
| 103 |
+
# Monta comando ffmpeg
|
| 104 |
+
cmd = ["ffmpeg", "-i", input_path, "-map_metadata", "-1"]
|
| 105 |
+
for k, v in metadata.items():
|
| 106 |
+
cmd += ["-metadata", f"{k}={v}"]
|
| 107 |
+
|
| 108 |
+
# Mantém qualidade alta
|
| 109 |
+
cmd += ["-c:v", "libx264", "-preset", "fast", "-crf", "18",
|
| 110 |
+
"-c:a", "aac", "-b:a", "192k", output_path]
|
| 111 |
+
|
| 112 |
+
# Executa com barra de progresso
|
| 113 |
+
progress = 0
|
| 114 |
+
with subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True) as proc:
|
| 115 |
+
for line in proc.stderr:
|
| 116 |
+
if "time=" in line:
|
| 117 |
+
progress = min(progress + 5, 100) # fake progress
|
| 118 |
+
progress_placeholder.progress(progress)
|
| 119 |
+
proc.wait()
|
| 120 |
+
progress_placeholder.progress(100)
|
| 121 |
|
| 122 |
# --- Upload ---
|
| 123 |
st.header("📤 Upload de Arquivos")
|
|
|
|
|
|
|
| 124 |
categoria = st.radio("Categoria:", CATEGORIES, index=None)
|
| 125 |
+
aleatorizar = st.checkbox("🔀 Aleatorizar metadados de vídeos")
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
if categoria:
|
| 128 |
uploaded_files = st.file_uploader(
|
|
|
|
| 134 |
|
| 135 |
if uploaded_files:
|
| 136 |
for uploaded_file in uploaded_files:
|
|
|
|
|
|
|
|
|
|
| 137 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 138 |
+
file_path = os.path.join(folder, uploaded_file.name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
+
with open(file_path, "wb") as f:
|
| 141 |
+
f.write(uploaded_file.read())
|
|
|
|
|
|
|
| 142 |
|
| 143 |
+
# Se for vídeo e checkbox ativo → processa metadados
|
| 144 |
+
if aleatorizar and uploaded_file.type == "video/mp4":
|
| 145 |
+
progress_placeholder = st.empty()
|
| 146 |
+
st.info(f"🔄 Processando metadados de {uploaded_file.name}...")
|
| 147 |
+
randomized_path = os.path.join(folder, f"rnd_{uploaded_file.name}")
|
| 148 |
+
randomize_video_metadata(file_path, randomized_path, progress_placeholder)
|
| 149 |
+
os.remove(file_path) # remove original
|
| 150 |
+
os.rename(randomized_path, file_path)
|
| 151 |
+
st.success(f"✅ {uploaded_file.name} processado com metadados aleatórios!")
|
| 152 |
|
| 153 |
if auto_delete:
|
| 154 |
+
key = f"{categoria}|||{uploaded_file.name}"
|
| 155 |
expirations[key] = time.time() + 24 * 60 * 60
|
| 156 |
with open(EXPIRATION_FILE, "w") as f:
|
| 157 |
json.dump(expirations, f)
|
| 158 |
|
| 159 |
+
st.success("Arquivos enviados com sucesso!")
|
| 160 |
+
st.rerun()
|
| 161 |
|
| 162 |
+
# --- Lista de Arquivos ---
|
| 163 |
st.header("📄 Arquivos Disponíveis")
|
|
|
|
| 164 |
for categoria in CATEGORIES:
|
| 165 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 166 |
files = os.listdir(folder)
|
|
|
|
| 167 |
st.subheader(f"📁 {categoria}")
|
|
|
|
| 168 |
if not files:
|
| 169 |
st.info("Nenhum arquivo na categoria.")
|
| 170 |
else:
|
| 171 |
for file in files:
|
| 172 |
st.markdown(f"<div class='file-box'>", unsafe_allow_html=True)
|
| 173 |
st.markdown(f"<div class='file-name'>{file}</div>", unsafe_allow_html=True)
|
|
|
|
| 174 |
key = f"{categoria}|||{file}"
|
|
|
|
| 175 |
expira = expirations.get(key)
|
| 176 |
if expira:
|
| 177 |
restante = int(expira - time.time())
|
| 178 |
if restante > 0:
|
| 179 |
+
h = restante // 3600
|
| 180 |
+
m = (restante % 3600) // 60
|
| 181 |
+
st.markdown(f"<div class='expire-text'>⏳ Expira em {h}h {m}min</div>", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
col1, col2, col3 = st.columns(3)
|
|
|
|
| 184 |
with col1:
|
| 185 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
| 186 |
st.download_button("⬇ Download", f_obj, file_name=file, key=f"down_{categoria}_{file}")
|
|
|
|
| 187 |
with col2:
|
| 188 |
if st.button("🗑 Excluir", key=f"delete_{categoria}_{file}"):
|
| 189 |
os.remove(os.path.join(folder, file))
|
|
|
|
| 191 |
with open(EXPIRATION_FILE, "w") as f:
|
| 192 |
json.dump(expirations, f)
|
| 193 |
st.success(f"Arquivo '{file}' excluído.")
|
| 194 |
+
st.rerun()
|
| 195 |
with col3:
|
| 196 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
| 197 |
if st.download_button("⬇ Baixar & Apagar", f_obj, file_name=file, key=f"download_delete_{categoria}_{file}"):
|
|
|
|
| 200 |
with open(EXPIRATION_FILE, "w") as f:
|
| 201 |
json.dump(expirations, f)
|
| 202 |
st.success(f"Arquivo '{file}' baixado e removido.")
|
| 203 |
+
st.rerun()
|
| 204 |
st.markdown("</div>", unsafe_allow_html=True)
|