Fix broken --download switch
This commit is contained in:
+56
-202
@@ -1,23 +1,10 @@
|
|||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from services.iview import iViewService
|
from services.iview import iViewService
|
||||||
|
|
||||||
|
|
||||||
# -------------------------
|
|
||||||
# Metadata paths
|
|
||||||
# -------------------------
|
|
||||||
DOWNLOAD_META = Path("./.config")
|
|
||||||
|
|
||||||
HISTORY_FILE = DOWNLOAD_META / "history"
|
|
||||||
SERIES_FILE = DOWNLOAD_META / "series"
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------
|
# -------------------------
|
||||||
# Service registry
|
# Service registry
|
||||||
# -------------------------
|
# -------------------------
|
||||||
@@ -25,284 +12,151 @@ SERVICES = {
|
|||||||
"IVIEW": iViewService(),
|
"IVIEW": iViewService(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# -------------------------
|
|
||||||
# Core Autograbber
|
|
||||||
# -------------------------
|
|
||||||
class AutoGrabber:
|
class AutoGrabber:
|
||||||
|
def __init__(self, download_dir, config_dir, dry_run=False, mark_existing=False):
|
||||||
def __init__(
|
# The main media output directory (Required)
|
||||||
self,
|
|
||||||
download_dir,
|
|
||||||
dry_run=False,
|
|
||||||
mark_existing=False
|
|
||||||
):
|
|
||||||
|
|
||||||
self.download_dir = Path(download_dir)
|
self.download_dir = Path(download_dir)
|
||||||
|
|
||||||
|
# The config directory (Defaults to PWD/.tvgrabber or CLI override)
|
||||||
|
self.config_dir = Path(config_dir)
|
||||||
|
|
||||||
|
self.history_file = self.config_dir / "history"
|
||||||
|
self.series_file = self.config_dir / "series"
|
||||||
|
|
||||||
self.dry_run = dry_run
|
self.dry_run = dry_run
|
||||||
self.mark_existing = mark_existing
|
self.mark_existing = mark_existing
|
||||||
|
|
||||||
|
# Initialize
|
||||||
self.history = self.load_history()
|
self.history = self.load_history()
|
||||||
self.series_list = self.load_series()
|
self.series_list = self.load_series()
|
||||||
|
|
||||||
# -------------------------
|
|
||||||
# History handling
|
|
||||||
# -------------------------
|
|
||||||
def load_history(self):
|
def load_history(self):
|
||||||
|
|
||||||
history = {}
|
history = {}
|
||||||
|
# Ensure .tvgrabber exists before we try to handle the history file
|
||||||
|
if not self.config_dir.exists():
|
||||||
|
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if not HISTORY_FILE.exists():
|
if not self.history_file.exists():
|
||||||
HISTORY_FILE.touch()
|
self.history_file.touch()
|
||||||
|
|
||||||
with open(HISTORY_FILE, "r") as f:
|
|
||||||
|
|
||||||
|
with open(self.history_file, "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
|
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
if not line or "|" not in line:
|
||||||
if not line:
|
if line: history[line] = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if "|" in line:
|
|
||||||
|
|
||||||
ep_id, filename = line.split("|", 1)
|
ep_id, filename = line.split("|", 1)
|
||||||
|
|
||||||
history[ep_id.strip()] = filename.strip()
|
history[ep_id.strip()] = filename.strip()
|
||||||
|
|
||||||
else:
|
|
||||||
history[line.strip()] = None
|
|
||||||
|
|
||||||
return history
|
return history
|
||||||
|
|
||||||
def save_history(self):
|
def save_history(self):
|
||||||
|
with open(self.history_file, "w") as f:
|
||||||
with open(HISTORY_FILE, "w") as f:
|
|
||||||
|
|
||||||
for ep_id in sorted(self.history.keys()):
|
for ep_id in sorted(self.history.keys()):
|
||||||
|
|
||||||
filename = self.history[ep_id]
|
filename = self.history[ep_id]
|
||||||
|
f.write(f"{ep_id} | {filename}\n" if filename else f"{ep_id}\n")
|
||||||
|
|
||||||
if filename:
|
|
||||||
f.write(f"{ep_id} | {filename}\n")
|
|
||||||
else:
|
|
||||||
f.write(f"{ep_id}\n")
|
|
||||||
|
|
||||||
# -------------------------
|
|
||||||
# Series loading
|
|
||||||
# -------------------------
|
|
||||||
def load_series(self):
|
def load_series(self):
|
||||||
|
if not self.series_file.exists():
|
||||||
if not SERIES_FILE.exists():
|
print(f"❌ Error: Series file not found at {self.series_file}")
|
||||||
|
print(f"Please ensure your series list is in {self.config_dir}/series")
|
||||||
print(
|
|
||||||
"❌ Missing required file: "
|
|
||||||
f"{SERIES_FILE}"
|
|
||||||
)
|
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
series = []
|
series = []
|
||||||
|
with open(self.series_file, "r") as f:
|
||||||
with open(SERIES_FILE, "r") as f:
|
|
||||||
|
|
||||||
for line in f:
|
for line in f:
|
||||||
|
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
if not line or "/" not in line: continue
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if "/" not in line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
service_name, remainder = line.split("/", 1)
|
service_name, remainder = line.split("/", 1)
|
||||||
|
|
||||||
if "|" in remainder:
|
if "|" in remainder:
|
||||||
source_title, output_title = remainder.split("|", 1)
|
source_title, output_title = remainder.split("|", 1)
|
||||||
else:
|
else:
|
||||||
source_title = remainder
|
source_title = output_title = remainder
|
||||||
output_title = remainder
|
|
||||||
|
|
||||||
series.append((
|
|
||||||
service_name.strip(),
|
|
||||||
source_title.strip(),
|
|
||||||
output_title.strip()
|
|
||||||
))
|
|
||||||
|
|
||||||
|
series.append((service_name.strip(), source_title.strip(), output_title.strip()))
|
||||||
return series
|
return series
|
||||||
|
|
||||||
# -------------------------
|
def process_show(self, service_name, source_title, output_title):
|
||||||
# Process a single show
|
|
||||||
# -------------------------
|
|
||||||
def process_show(
|
|
||||||
self,
|
|
||||||
service_name,
|
|
||||||
source_title,
|
|
||||||
output_title
|
|
||||||
):
|
|
||||||
|
|
||||||
service = SERVICES.get(service_name.upper())
|
service = SERVICES.get(service_name.upper())
|
||||||
|
|
||||||
if not service:
|
if not service:
|
||||||
|
|
||||||
print(f"❌ Unknown service: {service_name}")
|
print(f"❌ Unknown service: {service_name}")
|
||||||
return
|
return
|
||||||
|
|
||||||
print("\n==============================")
|
print(f"\n📺 Show: {output_title} ({service_name})")
|
||||||
print(f"📺 Show: {output_title}")
|
|
||||||
print(f"📡 Service: {service_name}")
|
|
||||||
print("==============================")
|
|
||||||
|
|
||||||
seasons = service.discover_seasons(source_title)
|
seasons = service.discover_seasons(source_title)
|
||||||
|
|
||||||
if not seasons:
|
if not seasons:
|
||||||
|
|
||||||
print("⚠️ No seasons found")
|
print("⚠️ No seasons found")
|
||||||
return
|
return
|
||||||
|
|
||||||
for season_data in seasons:
|
for season_data in seasons:
|
||||||
|
|
||||||
season_num = season_data["season"]
|
season_num = season_data["season"]
|
||||||
data = season_data["data"]
|
print(f"📦 Season {season_num}")
|
||||||
|
|
||||||
print(f"\n📦 Processing Season {season_num}")
|
|
||||||
|
|
||||||
for entry in data["entries"]:
|
|
||||||
|
|
||||||
episode = service.normalize_episode(
|
|
||||||
output_title,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
|
|
||||||
|
for entry in season_data["data"]["entries"]:
|
||||||
|
episode = service.normalize_episode(output_title, entry)
|
||||||
ep_id = episode["episode_id"]
|
ep_id = episode["episode_id"]
|
||||||
|
|
||||||
if not ep_id:
|
|
||||||
continue
|
|
||||||
|
|
||||||
filename = episode["filename"]
|
filename = episode["filename"]
|
||||||
|
|
||||||
# already downloaded
|
|
||||||
if ep_id in self.history:
|
if ep_id in self.history:
|
||||||
|
print(f" ⏩ {filename} (Skipped)")
|
||||||
print(
|
|
||||||
f"⏩ "
|
|
||||||
f"{self.history[ep_id]} "
|
|
||||||
f"(already downloaded)"
|
|
||||||
)
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# dry-run mode
|
|
||||||
if self.dry_run:
|
if self.dry_run:
|
||||||
|
print(f" 🧪 {filename} (Dry Run)")
|
||||||
print(
|
|
||||||
f"🧪 "
|
|
||||||
f"{filename} "
|
|
||||||
f"(would download)"
|
|
||||||
)
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# mark-existing mode
|
|
||||||
if self.mark_existing:
|
if self.mark_existing:
|
||||||
|
print(f" 📝 {filename} (Marked)")
|
||||||
print(
|
|
||||||
f"📝 "
|
|
||||||
f"{filename} "
|
|
||||||
f"(added to history)"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.history[ep_id] = filename
|
self.history[ep_id] = filename
|
||||||
self.save_history()
|
self.save_history()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
print(f" ✅ {filename} (Downloading...)")
|
||||||
# normal download mode
|
if service.download_episode(episode, entry, self.download_dir):
|
||||||
print(f"✅ {filename} → downloading")
|
|
||||||
|
|
||||||
success = service.download_episode(
|
|
||||||
episode,
|
|
||||||
entry,
|
|
||||||
self.download_dir
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
|
|
||||||
self.history[ep_id] = filename
|
self.history[ep_id] = filename
|
||||||
self.save_history()
|
self.save_history()
|
||||||
|
|
||||||
# -------------------------
|
|
||||||
# Main execution
|
|
||||||
# -------------------------
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
for service, source, output in self.series_list:
|
||||||
if not self.series_list:
|
self.process_show(service, source, output)
|
||||||
|
print("\n✅ Done.")
|
||||||
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
|
# CLI
|
||||||
# -------------------------
|
# -------------------------
|
||||||
def parse_args():
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description="Australian FTA TV AutoGrabber")
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
# Required positional argument for the download directory
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--dry-run",
|
"download_dir",
|
||||||
action="store_true",
|
help="REQUIRED: Path to the directory where media will be saved"
|
||||||
help="Do not download anything"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Optional config directory override
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-d", "--downloads",
|
"-c", "--config",
|
||||||
dest="downloads",
|
default="~/.tvgrabber",
|
||||||
default="./downloads",
|
help="Path to the config folder containing 'series' and 'history' (default: ~/.tvgrabber)"
|
||||||
help="Directory where videos will be saved."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument("--dry-run", action="store_true")
|
||||||
"--mark-existing",
|
parser.add_argument("--mark-existing", action="store_true")
|
||||||
action="store_true",
|
|
||||||
help="Add all available episodes to history without downloading"
|
|
||||||
)
|
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
# -------------------------
|
|
||||||
# Entry point
|
|
||||||
# -------------------------
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
# Convert to Path object and ensure it exists
|
# Ensure the download directory exists
|
||||||
download_path = Path(args.downloads).resolve()
|
out_path = Path(args.download_dir).resolve()
|
||||||
|
if not out_path.exists() and not args.dry_run:
|
||||||
if not args.dry_run and not download_path.exists():
|
out_path.mkdir(parents=True, exist_ok=True)
|
||||||
print(f"📂 Creating download directory: {download_path}")
|
|
||||||
download_path.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
AutoGrabber(
|
AutoGrabber(
|
||||||
download_dir=download_path,
|
download_dir=out_path,
|
||||||
|
config_dir=Path(args.config).resolve(),
|
||||||
dry_run=args.dry_run,
|
dry_run=args.dry_run,
|
||||||
mark_existing=args.mark_existing
|
mark_existing=args.mark_existing
|
||||||
).run()
|
).run()
|
||||||
|
|||||||
Reference in New Issue
Block a user