220 lines
6.0 KiB
Python
220 lines
6.0 KiB
Python
"""Command-line interface for Zotero addons scraper."""
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from .cache.release_cache import ReleaseCache
|
|
from .config.settings import ScraperConfig
|
|
from .services.cache_builder import ReleaseCacheBuilder
|
|
from .services.cache_scraper import CacheScraper
|
|
from .utils.logging import setup_logging
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
"""Parse command line arguments."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Scrape Zotero addon information from GitHub"
|
|
)
|
|
|
|
# Mode options
|
|
parser.add_argument(
|
|
"--build-cache-only",
|
|
action="store_true",
|
|
help="Only build/update release cache, don't generate output",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-build-cache",
|
|
action="store_true",
|
|
help="Skip building cache, only generate output from existing cache",
|
|
)
|
|
|
|
# Release cache options
|
|
parser.add_argument(
|
|
"--release-cache-dir",
|
|
type=str,
|
|
default="release_cache",
|
|
help="Release cache directory (default: release_cache)",
|
|
)
|
|
parser.add_argument(
|
|
"--full-rebuild",
|
|
action="store_true",
|
|
help="Rebuild cache from scratch, ignoring existing cache",
|
|
)
|
|
parser.add_argument(
|
|
"--parallel",
|
|
action="store_true",
|
|
help="Build cache in parallel (faster but uses more resources)",
|
|
)
|
|
parser.add_argument(
|
|
"--max-workers",
|
|
type=int,
|
|
default=4,
|
|
help="Max parallel workers for cache building (default: 4)",
|
|
)
|
|
|
|
# GitHub options
|
|
parser.add_argument(
|
|
"--github_repository",
|
|
type=str,
|
|
default=None,
|
|
help="GitHub repository (owner/repo) for publishing",
|
|
)
|
|
parser.add_argument(
|
|
"--github_token",
|
|
type=str,
|
|
default=None,
|
|
help="GitHub API token",
|
|
)
|
|
|
|
# Input/Output options
|
|
parser.add_argument(
|
|
"-i",
|
|
"--input",
|
|
type=str,
|
|
default="addons",
|
|
help="Input addon config directory (default: addons)",
|
|
)
|
|
parser.add_argument(
|
|
"-o",
|
|
"--output",
|
|
type=str,
|
|
default="addon_infos.json",
|
|
help="Output JSON file path (default: addon_infos.json)",
|
|
)
|
|
|
|
# Cache options
|
|
parser.add_argument(
|
|
"--cache_directory",
|
|
type=str,
|
|
default="caches",
|
|
help="XPI download cache directory (default: caches)",
|
|
)
|
|
parser.add_argument(
|
|
"--runtime_xpi_directory",
|
|
type=str,
|
|
default="xpis",
|
|
help="Runtime XPI directory (default: xpis)",
|
|
)
|
|
|
|
# Fallback options
|
|
parser.add_argument(
|
|
"--previous_info_urls",
|
|
nargs="+",
|
|
default=[],
|
|
help="URLs to previous addon_infos.json for fallback",
|
|
)
|
|
|
|
# Publish options
|
|
parser.add_argument(
|
|
"--create_release",
|
|
type=lambda x: x.lower() in ("true", "1", "yes"),
|
|
default=True,
|
|
help="Create GitHub release (default: True)",
|
|
)
|
|
|
|
# Logging
|
|
parser.add_argument(
|
|
"--log_level",
|
|
type=str,
|
|
default="INFO",
|
|
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
help="Logging level (default: INFO)",
|
|
)
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> int:
|
|
"""Main entry point.
|
|
|
|
Default flow:
|
|
1. Build/update release cache (parse new releases)
|
|
2. Generate addon_infos.json from cache
|
|
3. Publish to GitHub release (if --create_release)
|
|
"""
|
|
args = parse_args()
|
|
|
|
# Setup logging
|
|
import logging
|
|
|
|
log_level = getattr(logging, args.log_level.upper())
|
|
setup_logging(level=log_level)
|
|
|
|
logger = logging.getLogger("zotero_scraper")
|
|
|
|
# Validate arguments
|
|
if args.create_release and not args.github_repository:
|
|
logger.error("--github_repository is required when --create_release is True")
|
|
return 1
|
|
|
|
# Create configuration
|
|
config = ScraperConfig.from_args(args)
|
|
|
|
# Ensure directories exist
|
|
config.cache.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
config.cache.runtime_xpi_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Initialize release cache
|
|
release_cache_dir = Path(args.release_cache_dir)
|
|
release_cache = ReleaseCache(release_cache_dir)
|
|
|
|
# Step 1: Build/update cache (unless skipped)
|
|
if not args.skip_build_cache:
|
|
logger.info("Step 1: Building/updating release cache...")
|
|
|
|
builder = ReleaseCacheBuilder(config, release_cache)
|
|
|
|
if config.github.token:
|
|
builder.github.get_rate_limit()
|
|
|
|
if args.parallel:
|
|
stats = builder.build_cache_parallel(
|
|
full_rebuild=args.full_rebuild,
|
|
max_workers=args.max_workers,
|
|
)
|
|
else:
|
|
stats = builder.build_cache(
|
|
full_rebuild=args.full_rebuild,
|
|
)
|
|
|
|
logger.info(f"Cache build stats: {stats}")
|
|
|
|
if args.build_cache_only:
|
|
logger.info("Done! (build-cache-only mode)")
|
|
return 0
|
|
else:
|
|
if not release_cache_dir.exists():
|
|
logger.error(
|
|
f"Release cache directory not found: {release_cache_dir}. "
|
|
"Cannot use --skip-build-cache without existing cache."
|
|
)
|
|
return 1
|
|
logger.info("Step 1: Skipped (using existing cache)")
|
|
|
|
# Step 2: Generate addon_infos.json from cache
|
|
logger.info("Step 2: Generating addon_infos.json from cache...")
|
|
|
|
scraper = CacheScraper(config, release_cache)
|
|
|
|
if config.github.token:
|
|
scraper.github.get_rate_limit()
|
|
|
|
addon_infos = scraper.scrape_all()
|
|
logger.info(f"Generated info for {len(addon_infos)} addons")
|
|
|
|
# Step 3: Publish to GitHub release (if requested)
|
|
if args.create_release and args.github_repository:
|
|
logger.info("Step 3: Publishing to GitHub release...")
|
|
scraper.publisher.publish(config.output_file)
|
|
scraper.publisher.cleanup_caches(keep_count=1)
|
|
else:
|
|
logger.info("Step 3: Skipped (--create_release is False or no repository)")
|
|
|
|
logger.info("Done!")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|