euler314 commited on
Commit
29a56dd
Β·
verified Β·
1 Parent(s): de80961

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -146
app.py CHANGED
@@ -1,154 +1,139 @@
1
- import os
2
- import streamlit as st
 
 
 
 
 
 
 
 
 
 
3
  import zipfile
 
4
  from pathlib import Path
5
- import io
6
 
7
- # Fix permission errors by setting environment variables BEFORE importing streamlit
8
- os.environ["STREAMLIT_BROWSER_GATHER_USAGE_STATS"] = "false"
9
- os.environ["STREAMLIT_SERVER_HEADLESS"] = "true"
10
- os.environ["STREAMLIT_SERVER_ENABLE_CORS"] = "false"
11
- os.environ["STREAMLIT_SERVER_ENABLE_XSRF_PROTECTION"] = "false"
12
-
13
- def is_safe_extension(extension):
14
- """Check if the extension is safe to convert to"""
15
- dangerous_extensions = ['.exe', '.bin', '.bat', '.cmd', '.com', '.scr', '.pif', '.vbs', '.js']
16
- return extension.lower() not in dangerous_extensions
17
-
18
- def change_file_extension(file_content, original_name, new_extension):
19
- """Change file extension and return new filename"""
20
- if new_extension.startswith('.'):
21
- new_extension = new_extension[1:]
22
-
23
- base_name = Path(original_name).stem
24
- new_filename = f"{base_name}.{new_extension}"
25
-
26
- return new_filename, file_content
27
-
28
- def create_download_zip(files_data):
29
- """Create a zip file containing all converted files"""
30
- zip_buffer = io.BytesIO()
31
-
32
- with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
33
- for filename, content in files_data:
34
- zipf.writestr(filename, content)
35
-
36
- zip_buffer.seek(0)
37
- return zip_buffer.getvalue()
38
-
39
- # Configure page
40
- st.set_page_config(
41
- page_title="File Extension Converter",
42
- page_icon="πŸ”„",
43
- layout="wide"
44
- )
45
-
46
- st.title("πŸ”„ File Extension Converter")
47
- st.markdown("Convert any file extension to another (safely)")
48
-
49
- # Sidebar with instructions
50
- with st.sidebar:
51
- st.header("πŸ“‹ Instructions")
52
- st.markdown("""
53
- 1. Upload one or more files
54
- 2. Enter the target extension
55
- 3. Download converted files
56
-
57
- **Safety Note:**
58
- - .exe and .bin files are blocked for security
59
- - Only the extension is changed, not the file content
60
- """)
61
-
62
- st.header("⚠️ Blocked Extensions")
63
- st.code(".exe, .bin, .bat, .cmd, .com, .scr, .pif, .vbs, .js")
64
-
65
- # Main interface
66
- col1, col2 = st.columns([2, 1])
67
-
68
- with col1:
69
- st.header("Upload Files")
70
- uploaded_files = st.file_uploader(
71
- "Choose files to convert",
 
72
  accept_multiple_files=True,
73
- help="Upload any files you want to change the extension for"
 
74
  )
75
 
76
- with col2:
77
- st.header("Target Extension")
78
- new_extension = st.text_input(
79
- "New extension",
80
- placeholder="e.g., txt, pdf, jpg",
81
- help="Enter the extension you want to convert to (without the dot)"
82
- ).strip()
83
-
84
- if uploaded_files and new_extension:
85
- if new_extension.startswith('.'):
86
- new_extension = new_extension[1:]
87
-
88
- full_extension = f".{new_extension}"
89
-
90
- if not is_safe_extension(full_extension):
91
- st.error(f"❌ Extension '{full_extension}' is not allowed for security reasons")
92
- st.stop()
93
-
94
- st.success(f"βœ… Converting to: **{full_extension}**")
95
-
96
- converted_files = []
97
-
98
- st.header("πŸ“ Conversion Results")
99
-
100
- for uploaded_file in uploaded_files:
101
- original_name = uploaded_file.name
102
- file_content = uploaded_file.read()
103
-
104
- new_filename, new_content = change_file_extension(
105
- file_content, original_name, new_extension
106
- )
107
-
108
- converted_files.append((new_filename, new_content))
109
-
110
- col_orig, col_arrow, col_new = st.columns([3, 1, 3])
111
- with col_orig:
112
- st.text(f"πŸ“„ {original_name}")
113
- with col_arrow:
114
- st.text("➑️")
115
- with col_new:
116
- st.text(f"πŸ“„ {new_filename}")
117
-
118
- st.divider()
119
-
120
- st.header("πŸ’Ύ Download")
121
-
122
- if len(converted_files) == 1:
123
- filename, content = converted_files[0]
124
  st.download_button(
125
- label=f"πŸ“₯ Download {filename}",
126
- data=content,
127
- file_name=filename,
128
- mime="application/octet-stream"
129
  )
130
- else:
131
- try:
132
- zip_content = create_download_zip(converted_files)
133
- st.download_button(
134
- label=f"πŸ“₯ Download All Files ({len(converted_files)} files)",
135
- data=zip_content,
136
- file_name="converted_files.zip",
137
- mime="application/zip"
138
- )
139
-
140
- st.info(f"πŸ’‘ {len(converted_files)} files will be downloaded as a ZIP archive")
141
- except Exception as e:
142
- st.error(f"Error creating ZIP file: {str(e)}")
143
-
144
- elif uploaded_files and not new_extension:
145
- st.warning("⚠️ Please enter a target extension")
146
- elif new_extension and not uploaded_files:
147
- st.warning("⚠️ Please upload files to convert")
148
-
149
- st.divider()
150
- st.markdown("""
151
- <div style='text-align: center; color: #666; font-size: 0.8em;'>
152
- Built with Streamlit 🎈 | Safe file extension conversion
153
- </div>
154
- """, unsafe_allow_html=True)
 
1
+ """
2
+ Streamlit File Extension Renamer
3
+ --------------------------------
4
+ A lightweight Streamlit app intended for deployment on Hugging Face Spaces.
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 io
13
  import zipfile
14
+ from datetime import datetime
15
  from pathlib import Path
 
16
 
17
+ import streamlit as st
18
+
19
+ # -----------------------------------------------------------------------------
20
+ # Configuration
21
+ # -----------------------------------------------------------------------------
22
+ # List of target extensions displayed to the user. Extend this list as needed.
23
+ ALLOWED_TARGET_EXTS: list[str] = [
24
+ ".txt",
25
+ ".pdf",
26
+ ".doc",
27
+ ".docx",
28
+ ".json",
29
+ ".csv",
30
+ ".xml",
31
+ ".html",
32
+ ".css",
33
+ ".js",
34
+ ".md",
35
+ ".jpg",
36
+ ".jpeg",
37
+ ".png",
38
+ ".gif",
39
+ ".bmp",
40
+ ".tiff",
41
+ ".svg",
42
+ ".ico",
43
+ ".mp3",
44
+ ".wav",
45
+ ".mp4",
46
+ ".avi",
47
+ ".mkv",
48
+ ".mov",
49
+ ".zip",
50
+ ".tar",
51
+ ".gz",
52
+ ".7z",
53
+ ]
54
+
55
+ # Source‐file extensions that are disallowed (will be skipped if uploaded).
56
+ DISALLOWED_SOURCE_EXTS: set[str] = {".exe", ".bin"}
57
+
58
+ # -----------------------------------------------------------------------------
59
+ # UI helpers
60
+ # -----------------------------------------------------------------------------
61
+
62
+ def make_sidebar() -> str:
63
+ """Return the user-chosen target extension from sidebar controls."""
64
+ st.sidebar.header("Settings")
65
+ target_ext = st.sidebar.selectbox(
66
+ label="Target extension (applied to **all** uploaded files)",
67
+ options=ALLOWED_TARGET_EXTS,
68
+ index=ALLOWED_TARGET_EXTS.index(".pdf"),
69
+ )
70
+
71
+ st.sidebar.markdown(
72
+ """❗ **Note**: This tool only renames the file extension. It does **not**
73
+ convert the underlying file format. Opening a renamed file with an
74
+ incompatible program may fail or lead to data corruption."""
75
+ )
76
+ return target_ext
77
+
78
+
79
+ def make_uploader():
80
+ """Return the list of files uploaded by the user."""
81
+ return st.file_uploader(
82
+ "Upload one or more files",
83
  accept_multiple_files=True,
84
+ type=[ext.lstrip(".") for ext in ALLOWED_TARGET_EXTS],
85
+ help="Drag-and-drop or browse for files to rename.",
86
  )
87
 
88
+
89
+ # -----------------------------------------------------------------------------
90
+ # Core logic
91
+ # -----------------------------------------------------------------------------
92
+
93
+ def write_zip(uploaded_files: list[st.runtime.uploaded_file_manager.UploadedFile], target_ext: str) -> io.BytesIO:
94
+ """Return an in-memory ZIP archive of the uploaded files with the new extension."""
95
+ buffer = io.BytesIO()
96
+ with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
97
+ for file in uploaded_files:
98
+ orig_path = Path(file.name)
99
+ if orig_path.suffix.lower() in DISALLOWED_SOURCE_EXTS:
100
+ st.warning(f"⏭️ Skipping disallowed file: **{orig_path.name}**")
101
+ continue
102
+
103
+ renamed = orig_path.with_suffix(target_ext)
104
+ zf.writestr(renamed.name, file.read())
105
+ st.success(f"βœ… Renamed **{orig_path.name}** β†’ **{renamed.name}**")
106
+
107
+ buffer.seek(0)
108
+ return buffer
109
+
110
+
111
+ # -----------------------------------------------------------------------------
112
+ # Main app
113
+ # -----------------------------------------------------------------------------
114
+
115
+ def main() -> None:
116
+ st.set_page_config("Extension Renamer", page_icon="πŸ“„", layout="centered")
117
+ st.title("πŸ“„ Universal File-Extension Renamer")
118
+ st.write(
119
+ "Upload files, choose a new extension, and download them renamed in a single ZIP archive."
120
+ )
121
+
122
+ target_ext = make_sidebar()
123
+ uploaded_files = make_uploader()
124
+
125
+ if uploaded_files and st.button("πŸ”„ Rename & Package"):
126
+ zip_buffer = write_zip(uploaded_files, target_ext)
127
+ timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
 
 
 
 
 
 
 
 
128
  st.download_button(
129
+ label="⬇️ Download ZIP",
130
+ data=zip_buffer,
131
+ file_name=f"renamed_{timestamp}.zip",
132
+ mime="application/zip",
133
  )
134
+
135
+ st.caption("Β© 2025 File-Extension Renamer β€’ Built with Streamlit β€’ Deployed on Hugging Face Spaces")
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()