Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,103 +1,75 @@
|
|
1 |
"""
|
2 |
Streamlit File Extension Renamer
|
3 |
--------------------------------
|
4 |
-
|
5 |
-
The app lets users upload one or more files and rename (not convert) their
|
6 |
-
extensions to any common file-type extension the user selects. Executable
|
7 |
-
and binary files are blocked for security reasons.
|
8 |
-
"""
|
9 |
|
|
|
|
|
|
|
|
|
|
|
10 |
from __future__ import annotations
|
11 |
|
12 |
-
import os
|
13 |
-
|
14 |
-
|
15 |
-
pathlib.Path(os.environ["STREAMLIT_HOME"]).mkdir(parents=True, exist_ok=True)
|
16 |
-
|
17 |
-
import streamlit as st
|
18 |
-
|
19 |
import zipfile
|
20 |
from datetime import datetime
|
21 |
from pathlib import Path
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
|
|
24 |
|
25 |
-
#
|
26 |
# Configuration
|
27 |
-
#
|
28 |
-
# List of target extensions displayed to the user. Extend this list as needed.
|
29 |
ALLOWED_TARGET_EXTS: list[str] = [
|
30 |
-
".txt",
|
31 |
-
".
|
32 |
-
".
|
33 |
-
".
|
34 |
-
".json",
|
35 |
-
".csv",
|
36 |
-
".xml",
|
37 |
-
".html",
|
38 |
-
".css",
|
39 |
-
".js",
|
40 |
-
".md",
|
41 |
-
".jpg",
|
42 |
-
".jpeg",
|
43 |
-
".png",
|
44 |
-
".gif",
|
45 |
-
".bmp",
|
46 |
-
".tiff",
|
47 |
-
".svg",
|
48 |
-
".ico",
|
49 |
-
".mp3",
|
50 |
-
".wav",
|
51 |
-
".mp4",
|
52 |
-
".avi",
|
53 |
-
".mkv",
|
54 |
-
".mov",
|
55 |
-
".zip",
|
56 |
-
".tar",
|
57 |
-
".gz",
|
58 |
-
".7z",
|
59 |
]
|
60 |
|
61 |
-
# Source‐file extensions that are disallowed (will be skipped if uploaded).
|
62 |
DISALLOWED_SOURCE_EXTS: set[str] = {".exe", ".bin"}
|
63 |
|
64 |
-
#
|
65 |
# UI helpers
|
66 |
-
#
|
67 |
|
68 |
def make_sidebar() -> str:
|
69 |
-
"""Return the user-chosen target extension from sidebar controls."""
|
70 |
st.sidebar.header("Settings")
|
71 |
target_ext = st.sidebar.selectbox(
|
72 |
-
|
73 |
options=ALLOWED_TARGET_EXTS,
|
74 |
index=ALLOWED_TARGET_EXTS.index(".pdf"),
|
75 |
)
|
76 |
-
|
77 |
st.sidebar.markdown(
|
78 |
-
"
|
79 |
-
convert the underlying
|
80 |
-
incompatible program may fail or lead to data corruption."""
|
81 |
)
|
82 |
return target_ext
|
83 |
|
84 |
|
85 |
def make_uploader():
|
86 |
-
"""Return the list of files uploaded by the user."""
|
87 |
return st.file_uploader(
|
88 |
"Upload one or more files",
|
89 |
accept_multiple_files=True,
|
90 |
type=[ext.lstrip(".") for ext in ALLOWED_TARGET_EXTS],
|
91 |
-
help="Drag
|
92 |
)
|
93 |
|
94 |
-
|
95 |
-
# -----------------------------------------------------------------------------
|
96 |
# Core logic
|
97 |
-
#
|
98 |
|
99 |
-
def write_zip(uploaded_files: list[st.runtime.uploaded_file_manager.UploadedFile],
|
100 |
-
|
101 |
buffer = io.BytesIO()
|
102 |
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
|
103 |
for file in uploaded_files:
|
@@ -105,25 +77,21 @@ def write_zip(uploaded_files: list[st.runtime.uploaded_file_manager.UploadedFile
|
|
105 |
if orig_path.suffix.lower() in DISALLOWED_SOURCE_EXTS:
|
106 |
st.warning(f"⏭️ Skipping disallowed file: **{orig_path.name}**")
|
107 |
continue
|
108 |
-
|
109 |
renamed = orig_path.with_suffix(target_ext)
|
110 |
zf.writestr(renamed.name, file.read())
|
111 |
st.success(f"✅ Renamed **{orig_path.name}** → **{renamed.name}**")
|
112 |
-
|
113 |
buffer.seek(0)
|
114 |
return buffer
|
115 |
|
116 |
-
|
117 |
-
#
|
118 |
-
#
|
119 |
-
# -----------------------------------------------------------------------------
|
120 |
|
121 |
def main() -> None:
|
122 |
st.set_page_config("Extension Renamer", page_icon="📄", layout="centered")
|
123 |
-
st.title("📄 Universal File
|
124 |
-
st.write(
|
125 |
-
|
126 |
-
)
|
127 |
|
128 |
target_ext = make_sidebar()
|
129 |
uploaded_files = make_uploader()
|
@@ -138,8 +106,7 @@ def main() -> None:
|
|
138 |
mime="application/zip",
|
139 |
)
|
140 |
|
141 |
-
st.caption("© 2025 File
|
142 |
-
|
143 |
|
144 |
if __name__ == "__main__":
|
145 |
main()
|
|
|
1 |
"""
|
2 |
Streamlit File Extension Renamer
|
3 |
--------------------------------
|
4 |
+
Lightweight Streamlit app for Hugging Face Spaces.
|
|
|
|
|
|
|
|
|
5 |
|
6 |
+
Updates (2025‑05‑22):
|
7 |
+
* Ensures Streamlit has a writable config dir (`STREAMLIT_HOME`) inside container.
|
8 |
+
* Sets a fallback `HOME` env so `~/.streamlit` resolves safely.
|
9 |
+
* Compatible with Streamlit >=1.45.1 (bug‑fixed event‑loop).
|
10 |
+
"""
|
11 |
from __future__ import annotations
|
12 |
|
13 |
+
import os
|
14 |
+
import pathlib
|
15 |
+
import io
|
|
|
|
|
|
|
|
|
16 |
import zipfile
|
17 |
from datetime import datetime
|
18 |
from pathlib import Path
|
19 |
|
20 |
+
# ---------------------------------------------------------------------
|
21 |
+
# Environment hardening (MUST run before `import streamlit`)
|
22 |
+
# ---------------------------------------------------------------------
|
23 |
+
os.environ.setdefault("STREAMLIT_HOME", "/tmp/.streamlit")
|
24 |
+
os.environ.setdefault("HOME", "/tmp")
|
25 |
+
pathlib.Path(os.environ["STREAMLIT_HOME"]).mkdir(parents=True, exist_ok=True)
|
26 |
|
27 |
+
import streamlit as st
|
28 |
|
29 |
+
# ---------------------------------------------------------------------
|
30 |
# Configuration
|
31 |
+
# ---------------------------------------------------------------------
|
|
|
32 |
ALLOWED_TARGET_EXTS: list[str] = [
|
33 |
+
".txt", ".pdf", ".doc", ".docx", ".json", ".csv", ".xml", ".html",
|
34 |
+
".css", ".js", ".md", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff",
|
35 |
+
".svg", ".ico", ".mp3", ".wav", ".mp4", ".avi", ".mkv", ".mov",
|
36 |
+
".zip", ".tar", ".gz", ".7z",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
]
|
38 |
|
|
|
39 |
DISALLOWED_SOURCE_EXTS: set[str] = {".exe", ".bin"}
|
40 |
|
41 |
+
# ---------------------------------------------------------------------
|
42 |
# UI helpers
|
43 |
+
# ---------------------------------------------------------------------
|
44 |
|
45 |
def make_sidebar() -> str:
|
|
|
46 |
st.sidebar.header("Settings")
|
47 |
target_ext = st.sidebar.selectbox(
|
48 |
+
"Target extension (applied to **all** uploaded files)",
|
49 |
options=ALLOWED_TARGET_EXTS,
|
50 |
index=ALLOWED_TARGET_EXTS.index(".pdf"),
|
51 |
)
|
|
|
52 |
st.sidebar.markdown(
|
53 |
+
"❗ **Note**: This tool only renames file extensions. It does **not** "
|
54 |
+
"convert the underlying format."
|
|
|
55 |
)
|
56 |
return target_ext
|
57 |
|
58 |
|
59 |
def make_uploader():
|
|
|
60 |
return st.file_uploader(
|
61 |
"Upload one or more files",
|
62 |
accept_multiple_files=True,
|
63 |
type=[ext.lstrip(".") for ext in ALLOWED_TARGET_EXTS],
|
64 |
+
help="Drag‑and‑drop or browse for files to rename.",
|
65 |
)
|
66 |
|
67 |
+
# ---------------------------------------------------------------------
|
|
|
68 |
# Core logic
|
69 |
+
# ---------------------------------------------------------------------
|
70 |
|
71 |
+
def write_zip(uploaded_files: list[st.runtime.uploaded_file_manager.UploadedFile],
|
72 |
+
target_ext: str) -> io.BytesIO:
|
73 |
buffer = io.BytesIO()
|
74 |
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
|
75 |
for file in uploaded_files:
|
|
|
77 |
if orig_path.suffix.lower() in DISALLOWED_SOURCE_EXTS:
|
78 |
st.warning(f"⏭️ Skipping disallowed file: **{orig_path.name}**")
|
79 |
continue
|
|
|
80 |
renamed = orig_path.with_suffix(target_ext)
|
81 |
zf.writestr(renamed.name, file.read())
|
82 |
st.success(f"✅ Renamed **{orig_path.name}** → **{renamed.name}**")
|
|
|
83 |
buffer.seek(0)
|
84 |
return buffer
|
85 |
|
86 |
+
# ---------------------------------------------------------------------
|
87 |
+
# Main
|
88 |
+
# ---------------------------------------------------------------------
|
|
|
89 |
|
90 |
def main() -> None:
|
91 |
st.set_page_config("Extension Renamer", page_icon="📄", layout="centered")
|
92 |
+
st.title("📄 Universal File‑Extension Renamer")
|
93 |
+
st.write("Upload files, choose a new extension, and download them renamed "
|
94 |
+
"in a ZIP archive.")
|
|
|
95 |
|
96 |
target_ext = make_sidebar()
|
97 |
uploaded_files = make_uploader()
|
|
|
106 |
mime="application/zip",
|
107 |
)
|
108 |
|
109 |
+
st.caption("© 2025 File‑Extension Renamer • Streamlit • Hugging Face Spaces")
|
|
|
110 |
|
111 |
if __name__ == "__main__":
|
112 |
main()
|