euler314's picture
Update app.py
b70652d verified
raw
history blame
4.58 kB
"""
Streamlit File Extension Renamer
--------------------------------
A lightweight Streamlit app intended for deployment on Hugging Face Spaces.
The app lets users upload one or more files and rename (not convert) their
extensions to any common file-type extension the user selects. Executable
and binary files are blocked for security reasons.
"""
from __future__ import annotations
import os, pathlib
# 1️⃣ give Streamlit a real home BEFORE importing it
os.environ.setdefault("STREAMLIT_HOME", "/tmp/.streamlit")
pathlib.Path(os.environ["STREAMLIT_HOME"]).mkdir(parents=True, exist_ok=True)
import streamlit as st
import zipfile
from datetime import datetime
from pathlib import Path
# -----------------------------------------------------------------------------
# Configuration
# -----------------------------------------------------------------------------
# List of target extensions displayed to the user. Extend this list as needed.
ALLOWED_TARGET_EXTS: list[str] = [
".txt",
".pdf",
".doc",
".docx",
".json",
".csv",
".xml",
".html",
".css",
".js",
".md",
".jpg",
".jpeg",
".png",
".gif",
".bmp",
".tiff",
".svg",
".ico",
".mp3",
".wav",
".mp4",
".avi",
".mkv",
".mov",
".zip",
".tar",
".gz",
".7z",
]
# Source‐file extensions that are disallowed (will be skipped if uploaded).
DISALLOWED_SOURCE_EXTS: set[str] = {".exe", ".bin"}
# -----------------------------------------------------------------------------
# UI helpers
# -----------------------------------------------------------------------------
def make_sidebar() -> str:
"""Return the user-chosen target extension from sidebar controls."""
st.sidebar.header("Settings")
target_ext = st.sidebar.selectbox(
label="Target extension (applied to **all** uploaded files)",
options=ALLOWED_TARGET_EXTS,
index=ALLOWED_TARGET_EXTS.index(".pdf"),
)
st.sidebar.markdown(
"""❗ **Note**: This tool only renames the file extension. It does **not**
convert the underlying file format. Opening a renamed file with an
incompatible program may fail or lead to data corruption."""
)
return target_ext
def make_uploader():
"""Return the list of files uploaded by the user."""
return st.file_uploader(
"Upload one or more files",
accept_multiple_files=True,
type=[ext.lstrip(".") for ext in ALLOWED_TARGET_EXTS],
help="Drag-and-drop or browse for files to rename.",
)
# -----------------------------------------------------------------------------
# Core logic
# -----------------------------------------------------------------------------
def write_zip(uploaded_files: list[st.runtime.uploaded_file_manager.UploadedFile], target_ext: str) -> io.BytesIO:
"""Return an in-memory ZIP archive of the uploaded files with the new extension."""
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
for file in uploaded_files:
orig_path = Path(file.name)
if orig_path.suffix.lower() in DISALLOWED_SOURCE_EXTS:
st.warning(f"⏭️ Skipping disallowed file: **{orig_path.name}**")
continue
renamed = orig_path.with_suffix(target_ext)
zf.writestr(renamed.name, file.read())
st.success(f"✅ Renamed **{orig_path.name}** → **{renamed.name}**")
buffer.seek(0)
return buffer
# -----------------------------------------------------------------------------
# Main app
# -----------------------------------------------------------------------------
def main() -> None:
st.set_page_config("Extension Renamer", page_icon="📄", layout="centered")
st.title("📄 Universal File-Extension Renamer")
st.write(
"Upload files, choose a new extension, and download them renamed in a single ZIP archive."
)
target_ext = make_sidebar()
uploaded_files = make_uploader()
if uploaded_files and st.button("🔄 Rename & Package"):
zip_buffer = write_zip(uploaded_files, target_ext)
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
st.download_button(
label="⬇️ Download ZIP",
data=zip_buffer,
file_name=f"renamed_{timestamp}.zip",
mime="application/zip",
)
st.caption("© 2025 File-Extension Renamer • Built with Streamlit • Deployed on Hugging Face Spaces")
if __name__ == "__main__":
main()