attendantelectro commited on
Commit
974c0fa
·
verified ·
1 Parent(s): 53c6fd5

Upload 3 files

Browse files
Files changed (3) hide show
  1. api.py +115 -67
  2. app.py +85 -39
  3. requirements.txt +11 -10
api.py CHANGED
@@ -1,78 +1,126 @@
1
- #api.py
2
-
3
- import os
4
- import zipfile
5
- import tempfile
6
- import shutil
7
- import rarfile
8
- from pdf2image import convert_from_path
9
- from PIL import Image
10
- from moviepy.editor import VideoFileClip
11
  from pydub import AudioSegment
12
 
13
- Allowed extensions for conversion
 
14
 
15
- ALLOWED_EXTENSIONS = {'zip', 'rar', 'pdf', 'wav', 'mp4', 'jpg', 'jpeg', 'png'}
 
16
 
17
- def allowed_file(filename: str) -> bool: """Check if the file extension is allowed.""" return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
 
 
 
 
 
 
 
18
 
19
- def convert_single_file(input_path: str, output_dir: str = None) -> str: """ Convert a single file based on its extension. Returns the path to the converted file. """ if output_dir is None: output_dir = os.path.dirname(input_path) base, ext = os.path.splitext(os.path.basename(input_path)) ext = ext.lower()
 
 
 
20
 
21
- if ext == '.pdf':
22
- images = convert_from_path(input_path)
23
- out_pdf = os.path.join(output_dir, f"{base}_converted.pdf")
24
- webp_images = []
25
- for i, img in enumerate(images, start=1):
26
- webp_path = os.path.join(output_dir, f"{base}_page_{i}.webp")
27
- img.save(webp_path, 'WEBP')
28
- webp_images.append(Image.open(webp_path))
29
- webp_images[0].save(out_pdf, 'PDF', save_all=True, append_images=webp_images[1:])
30
- # Cleanup
31
- for img in webp_images:
32
- img.close()
33
- for i in range(1, len(images) + 1):
34
- try:
35
- os.remove(os.path.join(output_dir, f"{base}_page_{i}.webp"))
36
- except OSError:
37
- pass
38
- return out_pdf
39
-
40
- elif ext == '.mp4':
41
- out_vid = os.path.join(output_dir, f"{base}.mkv")
42
- clip = VideoFileClip(input_path)
43
- clip.write_videofile(out_vid, codec='libx264', audio_codec='aac')
44
- clip.reader.close()
45
- if clip.audio:
46
- clip.audio.reader.close_proc()
47
- return out_vid
48
-
49
- elif ext == '.wav':
50
- out_audio = os.path.join(output_dir, f"{base}.mp3")
51
- AudioSegment.from_wav(input_path).export(out_audio, format='mp3')
52
- return out_audio
53
-
54
- elif ext in {'.png', '.jpg', '.jpeg'}:
55
- out_img = os.path.join(output_dir, f"{base}.webp")
56
- Image.open(input_path).save(out_img, 'WEBP')
57
- return out_img
58
-
59
- else:
60
  return input_path
61
 
62
- def extract_archive(input_path: str, extract_to: str) -> None: """Extract ZIP or RAR archives.""" if input_path.lower().endswith('.zip'): with zipfile.ZipFile(input_path, 'r') as zf: zf.extractall(extract_to) else: with rarfile.RarFile(input_path, 'r') as rf: rf.extractall(extract_to)
 
 
 
 
 
 
 
 
 
 
63
 
64
- def convert_path(input_path: str, work_dir: str) -> list[str]: """ Convert a file or archive located at input_path within work_dir. Returns a list of paths to converted files. """ outputs: list[str] = [] ext = os.path.splitext(input_path)[1].lower().lstrip('.')
 
 
 
 
65
 
66
- if ext in {'zip', 'rar'}:
67
- extract_dir = os.path.join(work_dir, 'extracted')
68
- os.makedirs(extract_dir, exist_ok=True)
69
- extract_archive(input_path, extract_dir)
70
- for root, _, files in os.walk(extract_dir):
71
- for fname in files:
72
- fpath = os.path.join(root, fname)
73
- if allowed_file(fname):
74
- outputs.append(convert_single_file(fpath, work_dir))
75
- else:
76
- outputs.append(convert_single_file(input_path, work_dir))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- return outputs
 
 
1
+ from flask import Flask, request, send_file
2
+ import os
3
+ import zipfile
4
+ import tempfile
5
+ import shutil
6
+ import rarfile
7
+ from werkzeug.utils import secure_filename
8
+ from pdf2image import convert_from_path
9
+ from PIL import Image
10
+ from moviepy.editor import VideoFileClip
11
  from pydub import AudioSegment
12
 
13
+ app = Flask(__name__)
14
+ ALLOWED_EXTENSIONS = {'zip', 'rar', 'pdf', 'wav', 'mp4', 'jpg', 'png', 'jpeg'}
15
 
16
+ def allowed_file(filename):
17
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
18
 
19
+ def convert_single_file(input_path):
20
+ base_name, ext = os.path.splitext(input_path)
21
+
22
+ if ext.lower() == '.pdf':
23
+ # PDF تبدیل به WEBP سپس به PDF
24
+ images = convert_from_path(input_path)
25
+ output_path = f"{base_name}_converted.pdf"
26
+ webp_images = []
27
 
28
+ for i, image in enumerate(images):
29
+ webp_path = f"{base_name}_page_{i+1}.webp"
30
+ image.save(webp_path, 'WEBP')
31
+ webp_images.append(Image.open(webp_path))
32
 
33
+ webp_images[0].save(output_path, 'PDF', save_all=True, append_images=webp_images[1:])
34
+ return output_path
35
+
36
+ elif ext.lower() == '.mp4':
37
+ # MP4 تبدیل به MKV
38
+ output_path = f"{base_name}.mkv"
39
+ VideoFileClip(input_path).write_videofile(output_path, codec='libx264')
40
+ return output_path
41
+
42
+ elif ext.lower() == '.wav':
43
+ # WAV تبدیل به MP3
44
+ output_path = f"{base_name}.mp3"
45
+ AudioSegment.from_wav(input_path).export(output_path, format='mp3')
46
+ return output_path
47
+
48
+ elif ext.lower() in {'.png', '.jpg', '.jpeg'}:
49
+ # تصویر تبدیل به WEBP
50
+ output_path = f"{base_name}.webp"
51
+ Image.open(input_path).save(output_path, 'WEBP')
52
+ return output_path
53
+
54
+ # در صورت نداشتن تغییر، همان مسیر بازگردانده می‌شود.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  return input_path
56
 
57
+ @app.route('/convert', methods=['POST'])
58
+ def handle_conversion():
59
+ if 'file' not in request.files:
60
+ return {"error": "No file uploaded"}, 400
61
+
62
+ file = request.files['file']
63
+ if not file or file.filename == '':
64
+ return {"error": "Empty filename"}, 400
65
+
66
+ if not allowed_file(file.filename):
67
+ return {"error": "Unsupported file type"}, 400
68
 
69
+ try:
70
+ with tempfile.TemporaryDirectory() as temp_dir:
71
+ filename = secure_filename(file.filename)
72
+ upload_path = os.path.join(temp_dir, filename)
73
+ file.save(upload_path)
74
 
75
+ file_ext = filename.split('.')[-1].lower()
76
+
77
+ # اگر فایل تکی باشد، عملیات تبدیل روی همان فایل اعمال شود.
78
+ if file_ext in {'pdf', 'wav', 'mp4', 'jpg', 'png', 'jpeg'}:
79
+ converted_path = convert_single_file(upload_path)
80
+ converted_filename = os.path.basename(converted_path)
81
+ return send_file(
82
+ converted_path,
83
+ as_attachment=True,
84
+ download_name=converted_filename
85
+ )
86
+
87
+ # در صورتی که فایل آرشیو باشد: ابتدا استخراج شود
88
+ if filename.endswith('.zip'):
89
+ with zipfile.ZipFile(upload_path, 'r') as z:
90
+ z.extractall(temp_dir)
91
+ else:
92
+ with rarfile.RarFile(upload_path, 'r') as r:
93
+ r.extractall(temp_dir)
94
+
95
+ # تبدیل تمام فایل‌های موجود در آرشیو
96
+ for root, _, files in os.walk(temp_dir):
97
+ for f in files:
98
+ if f != filename:
99
+ input_file = os.path.join(root, f)
100
+ convert_single_file(input_file)
101
+
102
+ # ایجاد فایل آرشیو خروجی
103
+ output_path = os.path.join(temp_dir, "converted.zip")
104
+ with zipfile.ZipFile(output_path, 'w') as z:
105
+ for root, _, files in os.walk(temp_dir):
106
+ for f in files:
107
+ if f != filename:
108
+ full_path = os.path.join(root, f)
109
+ z.write(full_path, arcname=f)
110
+
111
+ return send_file(
112
+ output_path,
113
+ as_attachment=True,
114
+ download_name="converted_files.zip"
115
+ )
116
+
117
+ except Exception as e:
118
+ return {"error": f"Processing failed: {str(e)}"}, 500
119
+ finally:
120
+ try:
121
+ shutil.rmtree(temp_dir, ignore_errors=True)
122
+ except Exception:
123
+ pass
124
 
125
+ if __name__ == '__main__':
126
+ app.run(host='0.0.0.0', port=7860)
app.py CHANGED
@@ -1,51 +1,97 @@
1
- #app.py
2
-
3
  import os
4
- import io
5
- import zipfile
6
- import tempfile
 
7
  from werkzeug.utils import secure_filename
 
 
 
 
8
  import streamlit as st
9
- from api import ALLOWED_EXTENSIONS, allowed_file, convert_path
10
 
11
- Streamlit page configuration
 
 
 
 
 
 
 
 
12
 
13
- st.set_page_config(page_title="Universal File Converter", layout="wide")
 
 
 
14
 
15
- Multilingual interface text
 
 
16
 
17
- TEXTS = { "English": { "title": "Universal File Converter", "desc": "Convert files or archives (ZIP, RAR, PDF, WAV, MP4, Images)", "upload": "Upload File", "convert_btn": "Convert", "processing": "Processing...", "error_type": "Unsupported file type!", "success": "Conversion successful!", "download": "Download" }, "فارسی": { "title": "مبدل جهانی فایل", "desc": "تبدیل فایل یا آرشیو (ZIP, RAR, PDF, WAV, MP4, تصاویر)", "upload": "آپلود فایل", "convert_btn": "تبدیل", "processing": "در حال پردازش...", "error_type": "نوع فایل پشتیبانی نمیشود!", "success": "تبدیل با موفقیت انجام شد!", "download": "دانلود" } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- def main(): lang = st.sidebar.selectbox("", list(TEXTS.keys())) T = TEXTS[lang]
 
20
 
21
- st.title(T['title'])
22
- st.markdown(f"**{T['desc']}**")
23
 
24
- uploaded = st.file_uploader(T['upload'], type=list(ALLOWED_EXTENSIONS))
25
- if uploaded:
26
- fname = secure_filename(uploaded.name)
27
- if not allowed_file(fname):
28
- st.error(T['error_type'])
29
- elif st.button(T['convert_btn']):
30
- with st.spinner(T['processing']):
31
- with tempfile.TemporaryDirectory() as tmpdir:
32
- in_path = os.path.join(tmpdir, fname)
33
- with open(in_path, 'wb') as f:
34
- f.write(uploaded.getbuffer())
35
- converted_files = convert_path(in_path, tmpdir)
36
 
37
- if len(converted_files) == 1:
38
- out_path = converted_files[0]
39
- with open(out_path, 'rb') as f2:
40
- data = f2.read()
41
- st.success(T['success'])
42
- st.download_button(T['download'], data, os.path.basename(out_path))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  else:
44
- zip_buffer = io.BytesIO()
45
- with zipfile.ZipFile(zip_buffer, 'w') as zf:
46
- for p in converted_files:
47
- zf.write(p, arcname=os.path.basename(p))
48
- st.success(T['success'])
49
- st.download_button(T['download'], zip_buffer.getvalue(), 'converted_files.zip')
50
-
51
- if name == 'main': main()
 
 
 
 
 
1
  import os
2
+ import subprocess
3
+ import requests
4
+ from threading import Thread
5
+ from flask import Flask, request, send_file
6
  from werkzeug.utils import secure_filename
7
+ from pdf2image import convert_from_path
8
+ from PIL import Image
9
+ from moviepy.editor import VideoFileClip
10
+ from pydub import AudioSegment
11
  import streamlit as st
 
12
 
13
+ # Fix werkzeug compatibility for older versions if needed
14
+ from werkzeug.urls import url_quote
15
+
16
+ # Flask API Setup (برای اینجا فقط به هدف اجرا به عنوان پس‌زمینه استفاده می‌شود)
17
+ app = Flask(__name__)
18
+ ALLOWED_EXTENSIONS = {'zip', 'rar', 'pdf', 'wav', 'mp4', 'jpg', 'png', 'jpeg'}
19
+
20
+ def allowed_file(filename):
21
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
22
 
23
+ # راه‌اندازی API به عنوان پردازش پس‌زمینه
24
+ def run_api():
25
+ # اجرای فایل api.py به صورت subprocess.
26
+ subprocess.run(['python', 'api.py'])
27
 
28
+ # Streamlit UI
29
+ def streamlit_app():
30
+ st.set_page_config(page_title="File Converter", layout="wide")
31
 
32
+ TEXTS = {
33
+ "English": {
34
+ "title": "Universal File Converter",
35
+ "desc": "Convert files/archives (ZIP/RAR/PDF/WAV/MP4/IMG)",
36
+ "upload": "Upload File",
37
+ "error_type": "Unsupported file type!",
38
+ "success": "Conversion Successful!",
39
+ "download": "Download"
40
+ },
41
+ "فارسی": {
42
+ "title": "مبدل جهانی فایل",
43
+ "desc": "تبدیل فایل/آرشیو (ZIP/RAR/PDF/WAV/MP4/تصویر)",
44
+ "upload": "آپلود فایل",
45
+ "error_type": "نوع فایل پشتیبانی نمیشود!",
46
+ "success": "تبدیل با موفقیت انجام شد!",
47
+ "download": "دانلود"
48
+ }
49
+ }
50
 
51
+ lang = st.sidebar.selectbox("", ["English", "فارسی"])
52
+ t = TEXTS[lang]
53
 
54
+ st.title(t["title"])
55
+ st.markdown(f"**{t['desc']}**")
56
 
57
+ uploaded = st.file_uploader(t["upload"], type=ALLOWED_EXTENSIONS)
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ if uploaded:
60
+ file_extension = uploaded.name.split('.')[-1].lower()
61
+ if file_extension not in ALLOWED_EXTENSIONS:
62
+ st.error(t["error_type"])
63
+ elif st.button("Convert"):
64
+ try:
65
+ with st.spinner("Processing..."):
66
+ response = requests.post(
67
+ "http://localhost:7860/convert",
68
+ files={"file": (uploaded.name, uploaded.getvalue(), uploaded.type)},
69
+ timeout=300
70
+ )
71
+
72
+ if response.status_code == 200:
73
+ # استخراج نام فایل خروجی از header Content-Disposition
74
+ cd = response.headers.get('Content-Disposition', '')
75
+ filename = None
76
+ if 'filename=' in cd:
77
+ filename = cd.split("filename=")[1].strip().strip('"')
78
+ else:
79
+ filename = f"converted_{uploaded.name}"
80
+
81
+ st.success(t["success"])
82
+ st.download_button(
83
+ label=t["download"],
84
+ data=response.content,
85
+ file_name=filename,
86
+ )
87
  else:
88
+ st.error(f"Error {response.status_code}: {response.text}")
89
+
90
+ except Exception as e:
91
+ st.error(f"Connection error: {str(e)}")
92
+
93
+ if __name__ == '__main__':
94
+ # اجرای API داخل یک ترد پس‌زمینه (در صورتی که نیاز به اجرای همزمان داشته باشید)
95
+ Thread(target=run_api, daemon=True).start()
96
+ # اجرای رابط کاربری Streamlit
97
+ streamlit_app()
requirements.txt CHANGED
@@ -1,10 +1,11 @@
1
- Flask
2
- python-magic
3
- requests
4
- streamlit
5
- pdf2image
6
- Pillow
7
- moviepy
8
- pydub
9
- rarfile
10
- werkzeug
 
 
1
+ # requirements.txt
2
+ Flask==2.0.3
3
+ Werkzeug==2.0.3 # نسخه سازگار با url_quote
4
+ streamlit==1.22.0
5
+ python-magic==0.4.27
6
+ pdf2image==1.16.3
7
+ Pillow==10.0.1
8
+ pydub==0.25.1
9
+ moviepy==1.0.3
10
+ rarfile==4.0
11
+ requests