Update app.py
Browse files
app.py
CHANGED
|
@@ -5,6 +5,8 @@ import time
|
|
| 5 |
import subprocess
|
| 6 |
import random
|
| 7 |
import string
|
|
|
|
|
|
|
| 8 |
import mimetypes
|
| 9 |
|
| 10 |
BASE_FOLDER = "uploaded_files"
|
|
@@ -46,43 +48,8 @@ else:
|
|
| 46 |
st.title("📂 File Manager por Categoria")
|
| 47 |
|
| 48 |
# --- Funções auxiliares ---
|
| 49 |
-
def random_string(n=12):
|
| 50 |
-
return ''.join(random.choices(string.ascii_letters + string.digits, k=n))
|
| 51 |
-
|
| 52 |
-
def randomize_video_metadata(file_path):
|
| 53 |
-
"""FULL HARD REAL: remove LvMetaInfo/Aigc e reinsere metadados randômicos"""
|
| 54 |
-
mime, _ = mimetypes.guess_type(file_path)
|
| 55 |
-
if not mime or not mime.startswith("video"):
|
| 56 |
-
return
|
| 57 |
-
|
| 58 |
-
temp_path = file_path + ".tmp"
|
| 59 |
-
|
| 60 |
-
cmd = [
|
| 61 |
-
"ffmpeg", "-y", "-i", file_path,
|
| 62 |
-
"-map", "0:v", "-map", "0:a", # ⚡ só vídeo e áudio
|
| 63 |
-
"-map_metadata", "-1", # limpa tudo
|
| 64 |
-
"-c:v", "libx264", "-preset", "fast", "-crf", "23",
|
| 65 |
-
"-c:a", "aac", "-b:a", "128k",
|
| 66 |
-
"-movflags", "use_metadata_tags+faststart",
|
| 67 |
-
"-brand", random.choice(["mp42", "isom", "iso2", "avc1"]),
|
| 68 |
-
"-metadata", f"encoder={random_string(10)}",
|
| 69 |
-
"-metadata", f"title={random_string(12)}",
|
| 70 |
-
"-metadata", f"comment={random_string(12)}",
|
| 71 |
-
"-metadata", f"description={random_string(12)}",
|
| 72 |
-
"-metadata", f"artist={random_string(10)}",
|
| 73 |
-
"-metadata", f"album={random_string(10)}",
|
| 74 |
-
temp_path
|
| 75 |
-
]
|
| 76 |
-
|
| 77 |
-
try:
|
| 78 |
-
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 79 |
-
os.replace(temp_path, file_path)
|
| 80 |
-
print(f"✅ FULL HARD REAL aplicado em {file_path}")
|
| 81 |
-
except Exception as e:
|
| 82 |
-
print(f"⚠ Erro ao randomizar {file_path}: {e}")
|
| 83 |
-
|
| 84 |
def remove_expired_files():
|
| 85 |
-
"""Remove arquivos
|
| 86 |
changed = False
|
| 87 |
now = time.time()
|
| 88 |
expired_files = []
|
|
@@ -103,6 +70,48 @@ def remove_expired_files():
|
|
| 103 |
with open(EXPIRATION_FILE, "w") as f:
|
| 104 |
json.dump(expirations, f)
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
# --- Apagar arquivos vencidos ao iniciar ---
|
| 107 |
remove_expired_files()
|
| 108 |
|
|
@@ -112,12 +121,7 @@ st.subheader("Selecione uma categoria:")
|
|
| 112 |
|
| 113 |
categoria = st.radio("Categoria:", CATEGORIES, index=None)
|
| 114 |
|
| 115 |
-
|
| 116 |
-
randomize_meta = st.checkbox("🔀 Randomizar metadados de vídeos (e renomear)")
|
| 117 |
-
auto_delete = st.checkbox("🕒 Excluir automaticamente após 24 horas")
|
| 118 |
-
|
| 119 |
-
if "uploaded_once" not in st.session_state:
|
| 120 |
-
st.session_state["uploaded_once"] = False
|
| 121 |
|
| 122 |
if categoria:
|
| 123 |
uploaded_files = st.file_uploader(
|
|
@@ -125,37 +129,44 @@ if categoria:
|
|
| 125 |
accept_multiple_files=True,
|
| 126 |
key=f"uploader_{categoria}"
|
| 127 |
)
|
|
|
|
| 128 |
|
| 129 |
-
if uploaded_files
|
| 130 |
for uploaded_file in uploaded_files:
|
| 131 |
folder = os.path.join(BASE_FOLDER, categoria)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
else:
|
| 138 |
new_name = uploaded_file.name
|
| 139 |
|
| 140 |
file_path = os.path.join(folder, new_name)
|
|
|
|
| 141 |
|
| 142 |
-
#
|
| 143 |
-
with open(file_path, "wb") as f:
|
| 144 |
-
f.write(uploaded_file.read())
|
| 145 |
-
|
| 146 |
-
# Se marcado → randomizar metadados (apenas vídeos)
|
| 147 |
-
if randomize_meta:
|
| 148 |
-
randomize_video_metadata(file_path)
|
| 149 |
-
|
| 150 |
-
# Salvar expiração se marcada
|
| 151 |
if auto_delete:
|
| 152 |
key = f"{categoria}|||{new_name}"
|
| 153 |
expirations[key] = time.time() + 24 * 60 * 60
|
| 154 |
with open(EXPIRATION_FILE, "w") as f:
|
| 155 |
json.dump(expirations, f)
|
| 156 |
|
| 157 |
-
st.
|
| 158 |
-
st.
|
| 159 |
|
| 160 |
# --- Lista de Arquivos agrupada por pasta ---
|
| 161 |
st.header("📄 Arquivos Disponíveis")
|
|
@@ -200,6 +211,7 @@ for categoria in CATEGORIES:
|
|
| 200 |
with open(EXPIRATION_FILE, "w") as f:
|
| 201 |
json.dump(expirations, f)
|
| 202 |
st.success(f"Arquivo '{file}' excluído.")
|
|
|
|
| 203 |
|
| 204 |
with col3:
|
| 205 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
|
@@ -209,5 +221,6 @@ for categoria in CATEGORIES:
|
|
| 209 |
with open(EXPIRATION_FILE, "w") as f:
|
| 210 |
json.dump(expirations, f)
|
| 211 |
st.success(f"Arquivo '{file}' baixado e removido.")
|
|
|
|
| 212 |
|
| 213 |
st.markdown("</div>", unsafe_allow_html=True)
|
|
|
|
| 5 |
import subprocess
|
| 6 |
import random
|
| 7 |
import string
|
| 8 |
+
import tempfile
|
| 9 |
+
import shutil
|
| 10 |
import mimetypes
|
| 11 |
|
| 12 |
BASE_FOLDER = "uploaded_files"
|
|
|
|
| 48 |
st.title("📂 File Manager por Categoria")
|
| 49 |
|
| 50 |
# --- Funções auxiliares ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
def remove_expired_files():
|
| 52 |
+
"""Remove arquivos expirados automaticamente"""
|
| 53 |
changed = False
|
| 54 |
now = time.time()
|
| 55 |
expired_files = []
|
|
|
|
| 70 |
with open(EXPIRATION_FILE, "w") as f:
|
| 71 |
json.dump(expirations, f)
|
| 72 |
|
| 73 |
+
def random_string(length=12):
|
| 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. Se hard_mode=True, usa FFmpeg + MP4Box"""
|
| 78 |
+
temp_fd, temp_out = tempfile.mkstemp(suffix=".mp4")
|
| 79 |
+
os.close(temp_fd)
|
| 80 |
+
|
| 81 |
+
if not hard_mode:
|
| 82 |
+
# Upload normal sem mexer
|
| 83 |
+
shutil.copy(file_path, temp_out)
|
| 84 |
+
return temp_out
|
| 85 |
+
|
| 86 |
+
# 1. Reencode básico com ffmpeg
|
| 87 |
+
temp_ffmpeg = temp_out + ".ffmpeg.mp4"
|
| 88 |
+
cmd_ffmpeg = [
|
| 89 |
+
"ffmpeg", "-y", "-i", file_path,
|
| 90 |
+
"-map", "0:v", "-map", "0:a?",
|
| 91 |
+
"-map_metadata", "-1",
|
| 92 |
+
"-c:v", "libx264", "-preset", "fast", "-crf", "23",
|
| 93 |
+
"-c:a", "aac", "-b:a", "128k",
|
| 94 |
+
"-movflags", "use_metadata_tags+faststart",
|
| 95 |
+
"-brand", random.choice(["mp42", "isom", "iso2", "avc1"]),
|
| 96 |
+
"-metadata", f"encoder={random_string(10)}",
|
| 97 |
+
"-metadata", f"title={random_string(12)}",
|
| 98 |
+
"-metadata", f"comment={random_string(12)}",
|
| 99 |
+
"-metadata", f"description={random_string(12)}",
|
| 100 |
+
"-metadata", f"artist={random_string(10)}",
|
| 101 |
+
"-metadata", f"album={random_string(10)}",
|
| 102 |
+
temp_ffmpeg
|
| 103 |
+
]
|
| 104 |
+
subprocess.run(cmd_ffmpeg, check=True)
|
| 105 |
+
|
| 106 |
+
# 2. Remove UUID atoms (LvMetaInfo, AigcInfo) com MP4Box
|
| 107 |
+
cmd_mp4box = ["MP4Box", "-rem-uuid", temp_ffmpeg, "-out", temp_out]
|
| 108 |
+
subprocess.run(cmd_mp4box, check=True)
|
| 109 |
+
|
| 110 |
+
# 3. Apaga temporário
|
| 111 |
+
os.remove(temp_ffmpeg)
|
| 112 |
+
|
| 113 |
+
return temp_out
|
| 114 |
+
|
| 115 |
# --- Apagar arquivos vencidos ao iniciar ---
|
| 116 |
remove_expired_files()
|
| 117 |
|
|
|
|
| 121 |
|
| 122 |
categoria = st.radio("Categoria:", CATEGORIES, index=None)
|
| 123 |
|
| 124 |
+
hard_mode = st.checkbox("Ativar randomização HARD (vídeos apenas)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
if categoria:
|
| 127 |
uploaded_files = st.file_uploader(
|
|
|
|
| 129 |
accept_multiple_files=True,
|
| 130 |
key=f"uploader_{categoria}"
|
| 131 |
)
|
| 132 |
+
auto_delete = st.checkbox("Excluir automaticamente após 24 horas")
|
| 133 |
|
| 134 |
+
if uploaded_files:
|
| 135 |
for uploaded_file in uploaded_files:
|
| 136 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 137 |
+
os.makedirs(folder, exist_ok=True)
|
| 138 |
+
|
| 139 |
+
mime, _ = mimetypes.guess_type(uploaded_file.name)
|
| 140 |
+
is_video = mime and mime.startswith("video")
|
| 141 |
|
| 142 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_file.name)[1]) as tmp:
|
| 143 |
+
tmp.write(uploaded_file.read())
|
| 144 |
+
tmp_path = tmp.name
|
| 145 |
+
|
| 146 |
+
# Se for vídeo + hard_mode ativo → processa
|
| 147 |
+
if is_video and hard_mode:
|
| 148 |
+
final_tmp = process_video(tmp_path, hard_mode=True)
|
| 149 |
+
else:
|
| 150 |
+
final_tmp = tmp_path
|
| 151 |
+
|
| 152 |
+
# Renomeia com string aleatória se hard_mode
|
| 153 |
+
if hard_mode and is_video:
|
| 154 |
+
new_name = random_string(16) + ".mp4"
|
| 155 |
else:
|
| 156 |
new_name = uploaded_file.name
|
| 157 |
|
| 158 |
file_path = os.path.join(folder, new_name)
|
| 159 |
+
shutil.move(final_tmp, file_path)
|
| 160 |
|
| 161 |
+
# Salvar expiração
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
if auto_delete:
|
| 163 |
key = f"{categoria}|||{new_name}"
|
| 164 |
expirations[key] = time.time() + 24 * 60 * 60
|
| 165 |
with open(EXPIRATION_FILE, "w") as f:
|
| 166 |
json.dump(expirations, f)
|
| 167 |
|
| 168 |
+
st.success("Arquivos enviados com sucesso!")
|
| 169 |
+
st.rerun()
|
| 170 |
|
| 171 |
# --- Lista de Arquivos agrupada por pasta ---
|
| 172 |
st.header("📄 Arquivos Disponíveis")
|
|
|
|
| 211 |
with open(EXPIRATION_FILE, "w") as f:
|
| 212 |
json.dump(expirations, f)
|
| 213 |
st.success(f"Arquivo '{file}' excluído.")
|
| 214 |
+
st.rerun()
|
| 215 |
|
| 216 |
with col3:
|
| 217 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
|
|
|
| 221 |
with open(EXPIRATION_FILE, "w") as f:
|
| 222 |
json.dump(expirations, f)
|
| 223 |
st.success(f"Arquivo '{file}' baixado e removido.")
|
| 224 |
+
st.rerun()
|
| 225 |
|
| 226 |
st.markdown("</div>", unsafe_allow_html=True)
|