#!/usr/bin/env python3 import argparse import sys from pathlib import Path from services.iview import iViewService # ------------------------- # Metadata paths # ------------------------- DOWNLOAD_META = Path("./.config") HISTORY_FILE = DOWNLOAD_META / "history" SERIES_FILE = DOWNLOAD_META / "series" # ------------------------- # Service registry # ------------------------- SERVICES = { "IVIEW": iViewService(), } # ------------------------- # Core Autograbber # ------------------------- class AutoGrabber: def __init__( self, download_dir, dry_run=False, mark_existing=False ): self.download_dir = Path(download_dir) self.dry_run = dry_run self.mark_existing = mark_existing self.history = self.load_history() self.series_list = self.load_series() # ------------------------- # History handling # ------------------------- def load_history(self): history = {} if not HISTORY_FILE.exists(): HISTORY_FILE.touch() with open(HISTORY_FILE, "r") as f: for line in f: line = line.strip() if not line: continue if "|" in line: ep_id, filename = line.split("|", 1) history[ep_id.strip()] = filename.strip() else: history[line.strip()] = None return history def save_history(self): with open(HISTORY_FILE, "w") as f: for ep_id in sorted(self.history.keys()): filename = self.history[ep_id] if filename: f.write(f"{ep_id} | {filename}\n") else: f.write(f"{ep_id}\n") # ------------------------- # Series loading # ------------------------- def load_series(self): if not SERIES_FILE.exists(): print( "โŒ Missing required file: " f"{SERIES_FILE}" ) sys.exit(1) series = [] with open(SERIES_FILE, "r") as f: for line in f: line = line.strip() if not line: continue if "/" not in line: continue service_name, remainder = line.split("/", 1) if "|" in remainder: source_title, output_title = remainder.split("|", 1) else: source_title = remainder output_title = remainder series.append(( service_name.strip(), source_title.strip(), output_title.strip() )) return series # ------------------------- # Process a single show # ------------------------- def process_show( self, service_name, source_title, output_title ): service = SERVICES.get(service_name.upper()) if not service: print(f"โŒ Unknown service: {service_name}") return print("\n==============================") print(f"๐Ÿ“บ Show: {output_title}") print(f"๐Ÿ“ก Service: {service_name}") print("==============================") seasons = service.discover_seasons(source_title) if not seasons: print("โš ๏ธ No seasons found") return for season_data in seasons: season_num = season_data["season"] data = season_data["data"] print(f"\n๐Ÿ“ฆ Processing Season {season_num}") for entry in data["entries"]: episode = service.normalize_episode( output_title, entry ) ep_id = episode["episode_id"] if not ep_id: continue filename = episode["filename"] # already downloaded if ep_id in self.history: print( f"โฉ " f"{self.history[ep_id]} " f"(already downloaded)" ) continue # dry-run mode if self.dry_run: print( f"๐Ÿงช " f"{filename} " f"(would download)" ) continue # mark-existing mode if self.mark_existing: print( f"๐Ÿ“ " f"{filename} " f"(added to history)" ) self.history[ep_id] = filename self.save_history() continue # normal download mode print(f"โœ… {filename} โ†’ downloading") success = service.download_episode( episode, entry, self.download_dir ) if success: self.history[ep_id] = filename self.save_history() # ------------------------- # Main execution # ------------------------- def run(self): if not self.series_list: print("โš ๏ธ No shows found in .series") return for ( service_name, source_title, output_title ) in self.series_list: self.process_show( service_name, source_title, output_title ) print("\nโœ… Autograbber run complete") # ------------------------- # CLI # ------------------------- def parse_args(): parser = argparse.ArgumentParser() parser.add_argument( "--dry-run", action="store_true", help="Do not download anything" ) parser.add_argument( "-d", "--downloads", dest="downloads", default="./downloads", help="Directory where videos will be saved." ) parser.add_argument( "--mark-existing", action="store_true", help="Add all available episodes to history without downloading" ) return parser.parse_args() # ------------------------- # Entry point # ------------------------- if __name__ == "__main__": args = parse_args() # Convert to Path object and ensure it exists download_path = Path(args.downloads).resolve() if not args.dry_run and not download_path.exists(): print(f"๐Ÿ“‚ Creating download directory: {download_path}") download_path.mkdir(parents=True, exist_ok=True) AutoGrabber( download_dir=download_path, dry_run=args.dry_run, mark_existing=args.mark_existing ).run()