241 lines
9.3 KiB
Python
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()
|