Git-hf / app.py
Opera8's picture
Update app.py
1434eda verified
Raw
History Blame Contribute Delete
18.8 kB
import streamlit as st
import subprocess
import shutil
import os
import zipfile
import io
# تابع اجرای دستورات ترمینال
def run_command(command):
try:
# استفاده از shell=True برای دستورات ترکیبی
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
return True, result.stdout
except subprocess.CalledProcessError as e:
# ترکیب خروجی استاندارد و خطای ترمینال برای گزارش دقیق خطایابی
err_msg = e.stderr if e.stderr else ""
out_msg = e.stdout if e.stdout else ""
return False, f"{err_msg}\n{out_msg}"
# تابع ساخت فایل زیپ در حافظه
def create_zip_buffer(source_dir, flatten=False):
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(source_dir):
if '.git' in root:
continue
for file in files:
file_path = os.path.join(root, file)
if flatten:
arcname = file
else:
arcname = os.path.relpath(file_path, source_dir)
zipf.write(file_path, arcname=arcname)
return zip_buffer.getvalue()
# تنظیمات صفحه
st.set_page_config(page_title="Hugging Face Space Tools", page_icon="🛠️")
st.title("🛠️ ابزارهای مدیریت اسپیس هاگینگ‌فیس")
# ایجاد سه تب
tab1, tab2, tab3 = st.tabs([
"🔄 انتقال به گیت‌هاب",
"📤 انتقال به هاگینگ‌فیس",
"📦 دانلود اسپیس (فایل ZIP)"
])
# ---------------------------------------------------------
# تب اول: انتقال به گیت‌هاب (پشتیبانی از لینک و فایل زیپ + حل مشکل پوشه اضافه)
# ---------------------------------------------------------
with tab1:
st.info("منبع خود را برای انتقال به گیت‌هاب انتخاب کنید:")
# انتخاب نوع منبع خارج از فرم برای رندر داینامیک
source_type = st.radio("نوع منبع:", ["لینک اسپیس هاگینگ‌فیس", "آپلود فایل ZIP"])
with st.form("transfer_form"):
hf_url = ""
uploaded_zip = None
if source_type == "لینک اسپیس هاگینگ‌فیس":
hf_url = st.text_input("لینک صفحه اسپیس هاگینگ فیس (منبع)",
placeholder="https://huggingface.co/spaces/user/space")
else:
uploaded_zip = st.file_uploader("آپلود فایل ZIP (منبع)", type=["zip"])
st.markdown("---")
gh_url = st.text_input("لینک کامل ریپازیتوری گیت‌هاب (مقصد)",
placeholder="https://github.com/Hamed744/Zirnavis.git")
gh_token = st.text_input("توکن گیت‌هاب (Classic Token)", type="password")
submitted = st.form_submit_button("شروع عملیات انتقال 🚀")
if submitted:
if not gh_url or not gh_token:
st.error("لطفاً لینک گیت‌هاب و توکن را وارد کنید.")
elif source_type == "لینک اسپیس هاگینگ‌فیس" and not hf_url:
st.error("لطفاً لینک اسپیس را وارد کنید.")
elif source_type == "لینک اسپیس هاگینگ‌فیس" and "hf.space" in hf_url:
st.error("❌ لینک اشتباه است! لطفاً لینک اصلی پروفایل را وارد کنید.")
elif source_type == "آپلود فایل ZIP" and not uploaded_zip:
st.error("لطفاً فایل ZIP را آپلود کنید.")
else:
status_area = st.empty()
temp_dir = "temp_repo_gh"
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir, ignore_errors=True)
try:
auth_gh_url = gh_url.replace("https://", f"https://{gh_token}@")
# مرحله 1: آماده‌سازی فایل‌ها (دانلود یا اکسترکت)
if source_type == "لینک اسپیس هاگینگ‌فیس":
status_area.info("📥 در حال دانلود از هاگینگ فیس...")
success, msg = run_command(f"git clone {hf_url} {temp_dir}")
if not success:
st.error(f"خطا در دانلود: {msg}")
st.stop()
else:
run_command(f"git -C {temp_dir} lfs pull")
else:
status_area.info("📥 در حال استخراج فایل ZIP...")
os.makedirs(temp_dir, exist_ok=True)
with zipfile.ZipFile(uploaded_zip, 'r') as zip_ref:
zip_ref.extractall(temp_dir)
# بررسی و رفع مشکل پوشه اضافه (انتقال محتویات به روت اصلی)
extracted_items = [item for item in os.listdir(temp_dir) if item != '__MACOSX']
if len(extracted_items) == 1:
single_folder_path = os.path.join(temp_dir, extracted_items[0])
if os.path.isdir(single_folder_path):
for item in os.listdir(single_folder_path):
shutil.move(os.path.join(single_folder_path, item), temp_dir)
os.rmdir(single_folder_path)
# مرحله 2: عملیات گیت و پوش
os.chdir(temp_dir)
status_area.info("⚙️ در حال تنظیمات گیت...")
if os.path.exists(".git"):
shutil.rmtree(".git", ignore_errors=True)
run_command("git init")
run_command("git lfs install")
run_command("git config lfs.allowincompletepush true") # نادیده گرفتن خطای عدم دسترسی موقت به فایل‌های LFS
run_command(f"git remote add origin {auth_gh_url}")
run_command("git add .")
run_command('git config user.email "bot@transfer.com"')
run_command('git config user.name "Transfer Bot"')
run_command('git commit -m "Moved to GitHub automatically"')
run_command("git branch -M main")
status_area.info("📤 در حال آپلود به گیت‌هاب...")
push_success, push_msg = run_command("git push -f origin main")
if push_success:
st.success("✅ تمام شد! فایل‌ها دقیقاً در روت ریپازیتوری منتقل شدند.")
st.balloons()
else:
clean_error = push_msg.replace(gh_token, "***")
st.error(f"خطا در آپلود: {clean_error}")
os.chdir("..")
except Exception as e:
st.error(f"خطای سیستمی: {e}")
finally:
try:
if os.getcwd().endswith(temp_dir):
os.chdir("..")
except:
pass
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir, ignore_errors=True)
# ---------------------------------------------------------
# تب دوم: انتقال به هاگینگ‌فیس (پشتیبانی از لینک ریپازیتوری و فایل ZIP)
# ---------------------------------------------------------
with tab2:
st.info("منبع خود را برای انتقال به اسپیس هاگینگ‌فیس انتخاب کنید:")
# انتخاب نوع منبع خارج از فرم برای رندر داینامیک
source_type_hf = st.radio("نوع منبع (برای هاگینگ‌فیس):", ["لینک ریپازیتوری (گیت‌هاب و غیره)", "آپلود فایل ZIP"], key="src_type_hf")
with st.form("transfer_hf_form"):
src_url_hf = ""
uploaded_zip_hf = None
if source_type_hf == "لینک ریپازیتوری (گیت‌هاب و غیره)":
src_url_hf = st.text_input("لینک ریپازیتوری منبع (مثلاً گیت‌هاب یا یک اسپیس دیگر)",
placeholder="https://github.com/user/repo.git", key="src_url_hf_input")
else:
uploaded_zip_hf = st.file_uploader("آپلود فایل ZIP (منبع)", type=["zip"], key="zip_hf_input")
st.markdown("---")
hf_target_url = st.text_input("لینک کامل اسپیس هاگینگ‌فیس (مقصد)",
placeholder="https://huggingface.co/spaces/user/space-name", key="target_hf_url")
hf_token = st.text_input("توکن هاگینگ‌فیس (با دسترسی Write)", type="password", key="hf_token_input")
submitted_hf = st.form_submit_button("شروع عملیات انتقال به هاگینگ‌فیس 🚀")
if submitted_hf:
if not hf_target_url or not hf_token:
st.error("لطفاً لینک اسپیس مقصد و توکن هاگینگ‌فیس را وارد کنید.")
elif source_type_hf == "لینک ریپازیتوری (گیت‌هاب و غیره)" and not src_url_hf:
st.error("لطفاً لینک ریپازیتوری منبع را وارد کنید.")
elif source_type_hf == "آپلود فایل ZIP" and not uploaded_zip_hf:
st.error("لطفاً فایل ZIP را آپلود کنید.")
elif "huggingface.co/spaces/" not in hf_target_url:
st.error("❌ لینک اسپیس مقصد اشتباه است! لینک باید با ساختار huggingface.co/spaces/... باشد.")
else:
status_area_hf = st.empty()
temp_dir_hf = "temp_repo_hf"
if os.path.exists(temp_dir_hf):
shutil.rmtree(temp_dir_hf, ignore_errors=True)
try:
# استخراج هوشمند نام‌کاربری مقصد از آدرس وارد شده جهت احراز هویت بدون مشکل
username = "user"
try:
cleaned_url = hf_target_url.replace("https://", "").replace("http://", "")
parts = cleaned_url.split("huggingface.co/spaces/")[-1].strip("/").split("/")
if len(parts) >= 1 and parts[0] != "":
username = parts[0]
except Exception:
pass
# ایجاد آدرس با الگوی معتبر نام‌کاربری و توکن
auth_hf_url = hf_target_url.replace("https://", f"https://{username}:{hf_token}@")
# مرحله 1: آماده‌سازی فایل‌ها (دانلود یا استخراج زیپ)
if source_type_hf == "لینک ریپازیتوری (گیت‌هاب و غیره)":
status_area_hf.info("📥 در حال دانلود (کلون) ریپازیتوری منبع...")
success, msg = run_command(f"git clone {src_url_hf} {temp_dir_hf}")
if not success:
st.error(f"خطا در کلون کردن ریپازیتوری منبع: {msg}")
st.stop()
else:
# دانلود کامل تمامی لایبرری‌ها و فایل‌های سیستمی LFS
run_command(f"git -C {temp_dir_hf} lfs pull")
else:
status_area_hf.info("📥 در حال استخراج فایل ZIP...")
os.makedirs(temp_dir_hf, exist_ok=True)
with zipfile.ZipFile(uploaded_zip_hf, 'r') as zip_ref:
zip_ref.extractall(temp_dir_hf)
# رفع مشکل پوشه اضافه درون فایل ZIP
extracted_items = [item for item in os.listdir(temp_dir_hf) if item != '__MACOSX']
if len(extracted_items) == 1:
single_folder_path = os.path.join(temp_dir_hf, extracted_items[0])
if os.path.isdir(single_folder_path):
for item in os.listdir(single_folder_path):
shutil.move(os.path.join(single_folder_path, item), temp_dir_hf)
os.rmdir(single_folder_path)
# مرحله 2: ساخت مخزن تمیز گیت و پوش به هاگینگ‌فیس
os.chdir(temp_dir_hf)
status_area_hf.info("⚙️ در حال تنظیمات گیت...")
if os.path.exists(".git"):
shutil.rmtree(".git", ignore_errors=True)
run_command("git init")
run_command("git lfs install") # فعال‌سازی و راه‌اندازی محلی LFS
run_command("git config lfs.allowincompletepush true") # نادیده گرفتن خطای اشاره‌گرهای ناقص فایل‌های LFS
run_command(f"git remote add origin {auth_hf_url}") # ایجاد ریموت رسمی جهت شناسایی آپلودهای LFS
run_command("git add .")
run_command('git config user.email "bot@transfer.com"')
run_command('git config user.name "Transfer Bot"')
run_command('git commit -m "Moved to Hugging Face automatically"')
run_command("git branch -M main")
status_area_hf.info("📤 در حال آپلود فایل‌ها به اسپیس هاگینگ‌فیس...")
push_success, push_msg = run_command("git push -f origin main")
if push_success:
st.success("✅ انتقال موفقیت‌آمیز بود! فرآیند بیلد (Build) اسپیس شما در هاگینگ‌فیس آغاز شد.")
st.balloons()
else:
clean_error = push_msg.replace(hf_token, "***")
st.error(f"خطا در آپلود به هاگینگ‌فیس:\n{clean_error}")
os.chdir("..")
except Exception as e:
st.error(f"خطای سیستمی: {e}")
finally:
try:
if os.getcwd().endswith(temp_dir_hf):
os.chdir("..")
except:
pass
if os.path.exists(temp_dir_hf):
shutil.rmtree(temp_dir_hf, ignore_errors=True)
# ---------------------------------------------------------
# تب سوم: دانلود فایل‌ها به صورت زیپ
# ---------------------------------------------------------
with tab3:
st.info("با استفاده از این بخش می‌توانید کل فایل‌های یک اسپیس عمومی را به صورت ZIP دانلود کنید.")
hf_url_zip = st.text_input("لینک صفحه اسپیس هاگینگ فیس",
placeholder="https://huggingface.co/spaces/elias207/zirnavis52", key="zip_url")
zip_mode = st.radio("ساختار فایل ZIP خروجی را انتخاب کنید:",
("حفظ ساختار پوشه‌ها (فایل‌ها در پوشه‌های اصلی خودشان)",
"همه فایل‌ها در روت (حذف پوشه‌بندی و انتقال همه فایل‌ها به روت زیپ)"))
if st.button("آماده‌سازی فایل دانلود 🗜️"):
if not hf_url_zip:
st.error("لطفاً لینک اسپیس را وارد کنید.")
elif "hf.space" in hf_url_zip:
st.error("❌ لینک اشتباه است! لینک اصلی (huggingface.co/spaces/...) را وارد کنید.")
else:
status_zip = st.empty()
temp_dir_zip = "temp_repo_zip"
if os.path.exists(temp_dir_zip):
shutil.rmtree(temp_dir_zip, ignore_errors=True)
try:
status_zip.info("📥 در حال دریافت فایل‌ها از هاگینگ‌فیس...")
success, msg = run_command(f"git clone {hf_url_zip} {temp_dir_zip}")
if not success:
st.error(f"خطا در دریافت فایل‌ها: {msg}")
else:
# اطمینان از دانلود کامل تمامی فایل‌های LFS قبل از عملیات فشرده‌سازی
run_command(f"git -C {temp_dir_zip} lfs pull")
status_zip.info("⚙️ در حال فشرده‌سازی فایل‌ها...")
flatten_files = True if zip_mode == "همه فایل‌ها در روت (حذف پوشه‌بندی و انتقال همه فایل‌ها به روت زیپ)" else False
zip_data = create_zip_buffer(temp_dir_zip, flatten=flatten_files)
status_zip.success("✅ فایل با موفقیت آماده شد!")
repo_name = hf_url_zip.strip("/").split("/")[-1]
file_suffix = "flat" if flatten_files else "structured"
download_filename = f"{repo_name}_{file_suffix}.zip"
st.download_button(
label="⬇️ دانلود فایل ZIP",
data=zip_data,
file_name=download_filename,
mime="application/zip",
type="primary"
)
except Exception as e:
st.error(f"خطای سیستمی: {e}")
finally:
if os.path.exists(temp_dir_zip):
shutil.rmtree(temp_dir_zip, ignore_errors=True)