Files
tvgrabber/autograbber.py
T

271 lines
5.9 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import sys
from pathlib import Path
from services.iview import iViewService
# -------------------------
# Metadata paths
# -------------------------
DOWNLOAD_META = Path("./.download")
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):
self.download_dir = Path(download_dir)
self.dry_run = dry_run
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
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(
"--downloads",
default="./downloads",
help="Download directory"
)
return parser.parse_args()
# -------------------------
# Entry point
# -------------------------
if __name__ == "__main__":
args = parse_args()
AutoGrabber(
download_dir=args.downloads,
dry_run=args.dry_run
).run()