Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import os | |
| import threading | |
| import time | |
| import requests | |
| from bs4 import BeautifulSoup | |
| from urllib.parse import urljoin | |
| # Global state | |
| stop_flag = False | |
| found_count = 0 | |
| found_lock = threading.Lock() | |
| def download_image(url, folder): | |
| global found_count | |
| file_name = url.split("/")[-1].split("?")[0] | |
| file_path = os.path.join(folder, file_name) | |
| if os.path.exists(file_path): | |
| return False | |
| try: | |
| r = requests.get(url, timeout=10, stream=True) | |
| if r.status_code == 200 and 'image' in r.headers.get('Content-Type', ''): | |
| with open(file_path, "wb") as f: | |
| for chunk in r.iter_content(1024 * 8): | |
| f.write(chunk) | |
| with found_lock: | |
| found_count += 1 | |
| return True | |
| except requests.RequestException: | |
| pass | |
| return False | |
| def correct_image_url(url): | |
| filename = os.path.basename(url) | |
| filename = filename.replace("thumb_", "").replace("normal_", "") | |
| return url.replace(os.path.basename(filename), filename) | |
| def scrape_album_page(url, folder, progress_callback): | |
| try: | |
| r = requests.get(url, timeout=10) | |
| if r.status_code != 200: | |
| return [] | |
| soup = BeautifulSoup(r.text, "html.parser") | |
| images = set() | |
| for img in soup.find_all("img"): | |
| src = img.get("src") | |
| if src and (".jpg" in src.lower() or ".jpeg" in src.lower()): | |
| full_url = urljoin(url, src) | |
| original_url = correct_image_url(full_url) | |
| images.add(original_url) | |
| return list(images) | |
| except: | |
| return [] | |
| def worker(album_url, folder, progress_callback): | |
| global stop_flag | |
| page_index = 1 | |
| while not stop_flag: | |
| page_url = f"{album_url}?page={page_index}" | |
| image_urls = scrape_album_page(page_url, folder, progress_callback) | |
| if not image_urls: | |
| break | |
| for img_url in image_urls: | |
| if stop_flag: | |
| break | |
| download_image(img_url, folder) | |
| progress_callback() | |
| time.sleep(0.2) | |
| page_index += 1 | |
| def start_download(album_url, folder, threads): | |
| global stop_flag, found_count | |
| stop_flag = False | |
| found_count = 0 | |
| # Create folder if it doesn't exist | |
| os.makedirs(folder, exist_ok=True) | |
| yield gr.update(value="Download started...", visible=True), gr.update(value=0), gr.update(value=f"Found images: {found_count}") | |
| def progress_callback(): | |
| return gr.update(value=f"Found images: {found_count}") | |
| threads_list = [] | |
| for _ in range(int(threads)): | |
| t = threading.Thread(target=worker, args=(album_url, folder, progress_callback), daemon=True) | |
| threads_list.append(t) | |
| t.start() | |
| for t in threads_list: | |
| t.join() | |
| if not stop_flag: | |
| yield gr.update(value="Download completed!", visible=True), gr.update(value=100), gr.update(value=f"Found images: {found_count}") | |
| else: | |
| yield gr.update(value="Download stopped.", visible=True), gr.update(value=0), gr.update(value=f"Found images: {found_count}") | |
| def stop_download(): | |
| global stop_flag | |
| stop_flag = True | |
| return gr.update(value="Download stopped.") | |
| def select_folder(): | |
| import tkinter as tk | |
| from tkinter import filedialog | |
| root = tk.Tk() | |
| root.withdraw() | |
| folder_selected = filedialog.askdirectory() | |
| root.destroy() | |
| return folder_selected if folder_selected else "" | |
| # Create Gradio interface | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo: | |
| gr.Markdown("# 🖼️ Coppermine Original Image Downloader") | |
| gr.Markdown("Download original images from Coppermine photo albums") | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("## Configuration") | |
| folder_input = gr.Textbox( | |
| label="Download Folder", | |
| placeholder="Select or enter folder path", | |
| value="coppermine_download" | |
| ) | |
| folder_button = gr.Button("📁 Select Folder", variant="secondary") | |
| url_input = gr.Textbox( | |
| label="Album URL", | |
| placeholder="https://example.com/gallery/index.php?album=1", | |
| value="https://example.com/gallery/index.php?album=1" | |
| ) | |
| threads_input = gr.Slider( | |
| minimum=1, | |
| maximum=10, | |
| value=3, | |
| step=1, | |
| label="Number of Threads" | |
| ) | |
| start_button = gr.Button("🚀 Start Download", variant="primary") | |
| stop_button = gr.Button("🛑 Stop Download", variant="stop") | |
| with gr.Column(): | |
| gr.Markdown("## Status") | |
| status_output = gr.Textbox( | |
| label="Status", | |
| placeholder="Ready to download...", | |
| lines=2 | |
| ) | |
| progress_bar = gr.Number(label="Progress") | |
| found_output = gr.Textbox( | |
| label="Found Images", | |
| value="Found images: 0" | |
| ) | |
| # Examples | |
| gr.Markdown("### Examples") | |
| gr.Examples( | |
| examples=[ | |
| ["https://example.com/gallery/index.php?album=1", 3], | |
| ["https://demo.coppermine-gallery.net/demo/albums/animals/cat/index.php?album=1", 2], | |
| ], | |
| inputs=[url_input, threads_input] | |
| ) | |
| # Event handlers | |
| folder_button.click( | |
| fn=select_folder, | |
| outputs=folder_input | |
| ) | |
| start_button.click( | |
| fn=start_download, | |
| inputs=[url_input, folder_input, threads_input], | |
| outputs=[status_output, progress_bar, found_output] | |
| ) | |
| stop_button.click( | |
| fn=stop_download, | |
| outputs=status_output | |
| ) | |
| # Add footer link | |
| demo.launch( | |
| theme=gr.themes.Soft(primary_hue="blue"), | |
| footer_links=[ | |
| {"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"} | |
| ] | |
| ) |