Spaces:
Sleeping
Sleeping
""" | |
Streamlit File Extension Renamer | |
-------------------------------- | |
Lightweight Streamlit app for Hugging Face Spaces. | |
Updates (2025‑05‑22): | |
* Ensures Streamlit has a writable config dir (`STREAMLIT_HOME`) inside container. | |
* Sets a fallback `HOME` env so `~/.streamlit` resolves safely. | |
* Compatible with Streamlit >=1.45.1 (bug‑fixed event‑loop). | |
""" | |
from __future__ import annotations | |
import os | |
import pathlib | |
import io | |
import zipfile | |
from datetime import datetime | |
from pathlib import Path | |
# --------------------------------------------------------------------- | |
# Environment hardening (MUST run before `import streamlit`) | |
# --------------------------------------------------------------------- | |
os.environ.setdefault("STREAMLIT_HOME", "/tmp/.streamlit") | |
os.environ.setdefault("HOME", "/tmp") | |
pathlib.Path(os.environ["STREAMLIT_HOME"]).mkdir(parents=True, exist_ok=True) | |
import streamlit as st | |
# --------------------------------------------------------------------- | |
# Configuration | |
# --------------------------------------------------------------------- | |
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", | |
] | |
DISALLOWED_SOURCE_EXTS: set[str] = {".exe", ".bin"} | |
# --------------------------------------------------------------------- | |
# UI helpers | |
# --------------------------------------------------------------------- | |
def make_sidebar() -> str: | |
st.sidebar.header("Settings") | |
target_ext = st.sidebar.selectbox( | |
"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 file extensions. It does **not** " | |
"convert the underlying format." | |
) | |
return target_ext | |
def make_uploader(): | |
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: | |
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 | |
# --------------------------------------------------------------------- | |
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 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 • Streamlit • Hugging Face Spaces") | |
if __name__ == "__main__": | |
main() | |