Update app.py
Browse files
app.py
CHANGED
|
@@ -31,11 +31,11 @@ st.markdown("""
|
|
| 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
|
| 39 |
if os.path.exists(EXPIRATION_FILE):
|
| 40 |
with open(EXPIRATION_FILE, "r") as f:
|
| 41 |
expirations = json.load(f)
|
|
@@ -44,11 +44,12 @@ else:
|
|
| 44 |
|
| 45 |
st.title("📂 File Manager por Categoria")
|
| 46 |
|
| 47 |
-
# --- Função
|
| 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,16 +58,19 @@ def remove_expired_files():
|
|
| 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
|
| 69 |
-
def
|
| 70 |
major_brands = ["isom", "mp42", "iso5", "avc1"]
|
| 71 |
compatible_sets = [
|
| 72 |
["isom", "iso2", "avc1", "mp41"],
|
|
@@ -77,10 +81,12 @@ def randomize_video_metadata(input_path, output_path, progress_placeholder):
|
|
| 77 |
major = random.choice(major_brands)
|
| 78 |
compatible = ",".join(random.choice(compatible_sets))
|
| 79 |
|
| 80 |
-
#
|
| 81 |
start_date = datetime.datetime.now() - datetime.timedelta(days=180)
|
| 82 |
-
random_date = start_date + datetime.timedelta(
|
| 83 |
-
|
|
|
|
|
|
|
| 84 |
date_str = random_date.strftime("%Y-%m-%d %H:%M:%S")
|
| 85 |
|
| 86 |
def randstr(n=12):
|
|
@@ -96,33 +102,36 @@ def randomize_video_metadata(input_path, output_path, progress_placeholder):
|
|
| 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 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
for k, v in metadata.items():
|
| 106 |
cmd += ["-metadata", f"{k}={v}"]
|
| 107 |
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
|
|
|
|
|
|
| 111 |
|
| 112 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 126 |
|
| 127 |
if categoria:
|
| 128 |
uploaded_files = st.file_uploader(
|
|
@@ -137,19 +146,23 @@ if categoria:
|
|
| 137 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 138 |
file_path = os.path.join(folder, uploaded_file.name)
|
| 139 |
|
| 140 |
-
|
|
|
|
|
|
|
| 141 |
f.write(uploaded_file.read())
|
| 142 |
|
| 143 |
-
# Se for vídeo e checkbox
|
| 144 |
-
if
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
|
|
|
|
|
|
| 153 |
if auto_delete:
|
| 154 |
key = f"{categoria}|||{uploaded_file.name}"
|
| 155 |
expirations[key] = time.time() + 24 * 60 * 60
|
|
@@ -159,31 +172,42 @@ if categoria:
|
|
| 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 |
-
|
| 180 |
-
|
| 181 |
-
st.markdown(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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))
|
|
@@ -192,6 +216,7 @@ for categoria in CATEGORIES:
|
|
| 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}"):
|
|
@@ -201,4 +226,5 @@ for categoria in CATEGORIES:
|
|
| 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)
|
|
|
|
| 31 |
</style>
|
| 32 |
""", unsafe_allow_html=True)
|
| 33 |
|
| 34 |
+
# Cria as pastas se não existirem
|
| 35 |
for cat in CATEGORIES:
|
| 36 |
os.makedirs(os.path.join(BASE_FOLDER, cat), exist_ok=True)
|
| 37 |
|
| 38 |
+
# Carrega dados de expiração
|
| 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: apagar arquivos expirados ---
|
| 48 |
def remove_expired_files():
|
| 49 |
changed = False
|
| 50 |
now = time.time()
|
| 51 |
expired_files = []
|
| 52 |
+
|
| 53 |
for file_full, expire_time in list(expirations.items()):
|
| 54 |
cat, file = file_full.split("|||")
|
| 55 |
file_path = os.path.join(BASE_FOLDER, cat, file)
|
|
|
|
| 58 |
os.remove(file_path)
|
| 59 |
expired_files.append(file_full)
|
| 60 |
changed = True
|
| 61 |
+
|
| 62 |
for file_full in expired_files:
|
| 63 |
expirations.pop(file_full)
|
| 64 |
+
|
| 65 |
if changed:
|
| 66 |
with open(EXPIRATION_FILE, "w") as f:
|
| 67 |
json.dump(expirations, f)
|
| 68 |
|
| 69 |
+
# --- Apagar arquivos vencidos ao iniciar ---
|
| 70 |
remove_expired_files()
|
| 71 |
|
| 72 |
+
# --- Função: randomizar metadados de vídeo ---
|
| 73 |
+
def randomize_video_metadata_ffmpeg(input_path, output_path):
|
| 74 |
major_brands = ["isom", "mp42", "iso5", "avc1"]
|
| 75 |
compatible_sets = [
|
| 76 |
["isom", "iso2", "avc1", "mp41"],
|
|
|
|
| 81 |
major = random.choice(major_brands)
|
| 82 |
compatible = ",".join(random.choice(compatible_sets))
|
| 83 |
|
| 84 |
+
# Data aleatória nos últimos 6 meses
|
| 85 |
start_date = datetime.datetime.now() - datetime.timedelta(days=180)
|
| 86 |
+
random_date = start_date + datetime.timedelta(
|
| 87 |
+
days=random.randint(0, 180),
|
| 88 |
+
seconds=random.randint(0, 86400)
|
| 89 |
+
)
|
| 90 |
date_str = random_date.strftime("%Y-%m-%d %H:%M:%S")
|
| 91 |
|
| 92 |
def randstr(n=12):
|
|
|
|
| 102 |
"publisher": randstr(10),
|
| 103 |
"encoded_by": randstr(10),
|
| 104 |
"creation_time": date_str,
|
|
|
|
|
|
|
| 105 |
}
|
| 106 |
|
| 107 |
+
cmd = [
|
| 108 |
+
"ffmpeg", "-i", input_path,
|
| 109 |
+
"-map_metadata", "-1", # remove metadados antigos
|
| 110 |
+
"-metadata", f"major_brand={major}",
|
| 111 |
+
"-metadata", f"compatible_brands={compatible}",
|
| 112 |
+
]
|
| 113 |
+
|
| 114 |
for k, v in metadata.items():
|
| 115 |
cmd += ["-metadata", f"{k}={v}"]
|
| 116 |
|
| 117 |
+
cmd += [
|
| 118 |
+
"-c:v", "libx264", "-preset", "fast", "-crf", "18",
|
| 119 |
+
"-c:a", "aac", "-b:a", "192k",
|
| 120 |
+
output_path
|
| 121 |
+
]
|
| 122 |
|
| 123 |
+
subprocess.run(cmd, check=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
# --- Upload ---
|
| 126 |
+
|
| 127 |
st.header("📤 Upload de Arquivos")
|
| 128 |
+
|
| 129 |
+
st.subheader("Selecione uma categoria:")
|
| 130 |
+
|
| 131 |
categoria = st.radio("Categoria:", CATEGORIES, index=None)
|
| 132 |
+
|
| 133 |
+
# Checkbox para randomização
|
| 134 |
+
apply_metadata = st.checkbox("🔀 Aleatorizar metadados de vídeos")
|
| 135 |
|
| 136 |
if categoria:
|
| 137 |
uploaded_files = st.file_uploader(
|
|
|
|
| 146 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 147 |
file_path = os.path.join(folder, uploaded_file.name)
|
| 148 |
|
| 149 |
+
# Salva temporário
|
| 150 |
+
temp_path = file_path + ".tmp"
|
| 151 |
+
with open(temp_path, "wb") as f:
|
| 152 |
f.write(uploaded_file.read())
|
| 153 |
|
| 154 |
+
# Se for vídeo e checkbox marcado, randomiza
|
| 155 |
+
if apply_metadata and uploaded_file.name.lower().endswith((".mp4", ".mov", ".mkv")):
|
| 156 |
+
try:
|
| 157 |
+
randomize_video_metadata_ffmpeg(temp_path, file_path)
|
| 158 |
+
os.remove(temp_path)
|
| 159 |
+
except Exception as e:
|
| 160 |
+
st.error(f"Erro ao processar metadados: {e}")
|
| 161 |
+
os.rename(temp_path, file_path)
|
| 162 |
+
else:
|
| 163 |
+
os.rename(temp_path, file_path)
|
| 164 |
+
|
| 165 |
+
# Salvar expiração se marcada
|
| 166 |
if auto_delete:
|
| 167 |
key = f"{categoria}|||{uploaded_file.name}"
|
| 168 |
expirations[key] = time.time() + 24 * 60 * 60
|
|
|
|
| 172 |
st.success("Arquivos enviados com sucesso!")
|
| 173 |
st.rerun()
|
| 174 |
|
| 175 |
+
# --- Lista de Arquivos agrupada por pasta ---
|
| 176 |
st.header("📄 Arquivos Disponíveis")
|
| 177 |
+
|
| 178 |
for categoria in CATEGORIES:
|
| 179 |
folder = os.path.join(BASE_FOLDER, categoria)
|
| 180 |
files = os.listdir(folder)
|
| 181 |
+
|
| 182 |
st.subheader(f"📁 {categoria}")
|
| 183 |
+
|
| 184 |
if not files:
|
| 185 |
st.info("Nenhum arquivo na categoria.")
|
| 186 |
else:
|
| 187 |
for file in files:
|
| 188 |
st.markdown(f"<div class='file-box'>", unsafe_allow_html=True)
|
| 189 |
st.markdown(f"<div class='file-name'>{file}</div>", unsafe_allow_html=True)
|
| 190 |
+
|
| 191 |
key = f"{categoria}|||{file}"
|
| 192 |
+
|
| 193 |
expira = expirations.get(key)
|
| 194 |
if expira:
|
| 195 |
restante = int(expira - time.time())
|
| 196 |
if restante > 0:
|
| 197 |
+
restante_horas = restante // 3600
|
| 198 |
+
restante_min = (restante % 3600) // 60
|
| 199 |
+
st.markdown(
|
| 200 |
+
f"<div class='expire-text'>⏳ Expira em {restante_horas}h {restante_min}min</div>",
|
| 201 |
+
unsafe_allow_html=True)
|
| 202 |
+
else:
|
| 203 |
+
st.markdown("<div class='expire-text'>⚠ Expiração iminente</div>", unsafe_allow_html=True)
|
| 204 |
|
| 205 |
col1, col2, col3 = st.columns(3)
|
| 206 |
+
|
| 207 |
with col1:
|
| 208 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
| 209 |
st.download_button("⬇ Download", f_obj, file_name=file, key=f"down_{categoria}_{file}")
|
| 210 |
+
|
| 211 |
with col2:
|
| 212 |
if st.button("🗑 Excluir", key=f"delete_{categoria}_{file}"):
|
| 213 |
os.remove(os.path.join(folder, file))
|
|
|
|
| 216 |
json.dump(expirations, f)
|
| 217 |
st.success(f"Arquivo '{file}' excluído.")
|
| 218 |
st.rerun()
|
| 219 |
+
|
| 220 |
with col3:
|
| 221 |
with open(os.path.join(folder, file), "rb") as f_obj:
|
| 222 |
if st.download_button("⬇ Baixar & Apagar", f_obj, file_name=file, key=f"download_delete_{categoria}_{file}"):
|
|
|
|
| 226 |
json.dump(expirations, f)
|
| 227 |
st.success(f"Arquivo '{file}' baixado e removido.")
|
| 228 |
st.rerun()
|
| 229 |
+
|
| 230 |
st.markdown("</div>", unsafe_allow_html=True)
|