Files

241 lines
9.3 KiB
Python

import threading
import datetime
import re
import os
from urllib.parse import urlparse, parse_qs
from .downloader_core.bunkr import BunkrDownloader
from .downloader_core.downloader import Downloader
from .downloader_core.erome import EromeDownloader
from .downloader_core.simpcity import SimpCity
from .downloader_core.jpg5 import Jpg5Downloader
from .samba_manager import SambaManager
from .storage import SambaStorage
from .config import settings
# Dummy translator
def tr(key, **kwargs):
return key.format(**kwargs)
def extract_ck_parameters(url):
match = re.search(r"/(?P<service>[^/?]+)(/user/(?P<user>[^/?]+)(/post/(?P<post>[^/?]+))?)?", url.path)
if match:
return match.group("service"), match.group("user"), match.group("post")
return None, None, None
def extract_ck_query(url):
query = parse_qs(url.query)
q = query.get("q")[0] if query.get("q") and len(query.get("q")) > 0 else "0"
o = query.get("o")[0] if query.get("o") and len(query.get("o")) > 0 else "0"
return q, int(o) if str.isdigit(o) else 0
class DownloaderManager:
def __init__(self):
self.active_downloader = None
self.download_thread = None
self.logs = []
self.progress = {
"status": "Idle",
"active": False,
"downloaded": 0,
"total": 0,
"speed": 0,
"eta": 0,
"current_file": ""
}
self.download_folder = os.path.join(os.getcwd(), "downloads")
os.makedirs(self.download_folder, exist_ok=True)
self.samba_manager = None
def log_callback(self, message):
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
self.logs.append(f"[{timestamp}] {message}")
if len(self.logs) > 1000:
self.logs.pop(0)
def update_progress_callback(self, downloaded, total, **kwargs):
self.progress["downloaded"] = downloaded
self.progress["total"] = total
if "speed" in kwargs:
self.progress["speed"] = kwargs["speed"]
if "eta" in kwargs:
self.progress["eta"] = kwargs["eta"]
if "file_path" in kwargs:
self.progress["current_file"] = os.path.basename(kwargs["file_path"])
def enable_widgets_callback(self):
# Called when a single downloader finishes
# We don't set active=False here because we might have more URLs
pass
def start_download(self, urls, options):
if self.progress["active"]:
raise Exception("Download already in progress")
self.logs = []
self.progress["active"] = True
self.progress["status"] = "Starting..."
self.progress["downloaded"] = 0
self.progress["total"] = 0
self.download_thread = threading.Thread(target=self._run_download, args=(urls, options))
self.download_thread.start()
def _run_download(self, urls, options):
try:
for i, url in enumerate(urls):
# Check for cancellation between URLs
if self.progress["status"] == "Cancelling...":
self.log_callback("Queue processing cancelled.")
break
self.log_callback(f"Processing URL {i+1}/{len(urls)}: {url}")
try:
self._process_single_url(url, options)
except Exception as e:
self.log_callback(f"Error processing URL {url}: {e}")
# Continue to next URL? Yes, usually desired behavior for bulk.
except Exception as e:
self.log_callback(f"Fatal error in download thread: {e}")
self.progress["status"] = "Error"
finally:
self.progress["active"] = False
self.progress["status"] = "Finished"
if self.samba_manager:
self.samba_manager.close()
self.samba_manager = None
self.active_downloader = None
def _process_single_url(self, url, options):
# Options
download_images = options.get("download_images", True)
download_videos = options.get("download_videos", True)
max_workers = options.get("max_workers", 3)
force_overwrite = options.get("force_overwrite", False)
parsed_url = urlparse(url)
target = None
args = ()
# Cleanup previous samba manager if exists (though we try to clean up in finally)
if self.samba_manager:
self.samba_manager.close()
self.samba_manager = None
# Dispatch Logic
if "erome.com" in url:
self.log_callback("Starting Erome Download")
is_profile = "/a/" not in url
self.active_downloader = EromeDownloader(
download_folder=self.download_folder,
log_callback=self.log_callback,
enable_widgets_callback=self.enable_widgets_callback,
update_progress_callback=self.update_progress_callback,
tr=tr
)
if "/a/" in url:
target = self.active_downloader.process_album_page
args = (url, self.download_folder, download_images, download_videos)
else:
target = self.active_downloader.process_profile_page
args = (url, self.download_folder, download_images, download_videos)
elif re.search(r"https?://([a-z0-9-]+\.)?bunkr\.[a-z]{2,}", url):
self.log_callback("Starting Bunkr Download")
self.active_downloader = BunkrDownloader(
download_folder=self.download_folder,
log_callback=self.log_callback,
enable_widgets_callback=self.enable_widgets_callback,
update_progress_callback=self.update_progress_callback,
tr=tr
)
if any(sub in url for sub in ["/v/", "/i/", "/f/"]):
target = self.active_downloader.descargar_post_bunkr
args = (url,)
else:
target = self.active_downloader.descargar_perfil_bunkr
args = (url,)
elif parsed_url.netloc in ["coomer.st", "kemono.cr", "coomer.su", "kemono.su"]:
self.log_callback("Starting Coomer/Kemono Download (Direct SMB)")
try:
self.samba_manager = SambaManager(
settings.samba_server_ip,
"isolation",
settings.samba_username,
settings.samba_password
)
self.log_callback(f"Connected to SMB Share: isolation at {settings.samba_server_ip}")
storage = SambaStorage(self.samba_manager, base_path="videos/coomerparty")
except Exception as e:
self.log_callback(f"Failed to connect to SMB: {e}")
raise e
self.active_downloader = Downloader(
download_folder="",
max_workers=max_workers,
log_callback=self.log_callback,
enable_widgets_callback=self.enable_widgets_callback,
update_progress_callback=self.update_progress_callback,
tr=tr,
download_images=download_images,
download_videos=download_videos,
storage=storage,
force_overwrite=force_overwrite
)
site = parsed_url.netloc
service, user, post = extract_ck_parameters(parsed_url)
if not service or not user:
raise Exception("Invalid Coomer/Kemono URL")
if post:
target = self.active_downloader.download_single_post
args = (site, post, service, user)
else:
query, offset = extract_ck_query(parsed_url)
target = self.active_downloader.download_media
args = (site, user, service, query, True, offset)
elif "simpcity.cr" in url:
self.log_callback("Starting SimpCity Download")
self.active_downloader = SimpCity(
download_folder=self.download_folder,
log_callback=self.log_callback,
enable_widgets_callback=self.enable_widgets_callback,
update_progress_callback=self.update_progress_callback,
tr=tr
)
target = self.active_downloader.download_images_from_simpcity
args = (url,)
elif "jpg5.su" in url:
self.log_callback("Starting Jpg5 Download")
self.active_downloader = Jpg5Downloader(
url=url,
carpeta_destino=self.download_folder,
log_callback=self.log_callback,
tr=tr
)
target = self.active_downloader.descargar_imagenes
args = ()
else:
raise Exception(f"Unsupported URL: {url}")
# Execute
self.progress["status"] = "Running"
target(*args)
def cancel_download(self):
self.progress["status"] = "Cancelling..."
self.log_callback("Cancellation requested...")
if self.active_downloader and hasattr(self.active_downloader, 'request_cancel'):
self.active_downloader.request_cancel()
manager = DownloaderManager()