|
18 | 18 | import shutil |
19 | 19 | import subprocess |
20 | 20 | import sys |
| 21 | +import tempfile |
21 | 22 | import threading |
22 | 23 | from dataclasses import dataclass, field |
23 | 24 | from pathlib import Path |
24 | 25 | from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple |
25 | 26 |
|
26 | 27 | from loguru import logger |
27 | 28 | from tqdm import tqdm |
| 29 | +from urllib.error import HTTPError, URLError |
| 30 | +from urllib.request import urlopen |
28 | 31 |
|
29 | 32 | YT_CHANNEL_ID = "UC4eYXhJI4-7wSWc8UNRwD4A" |
30 | 33 | STANDARD_OPS: Sequence[str] = ( |
|
42 | 45 | ENGLISH_PREFIXES = ("en", "eng") |
43 | 46 | SUB_LINE_RE = re.compile(r"^([A-Za-z0-9][\w\.-]*)\s") |
44 | 47 | PLAYLIST_ORDER_CHOICES = ("shuffle", "forward", "reverse") |
| 48 | +CURATED_JAMLIST_URL = ( |
| 49 | + "https://raw.githubusercontent.com/aryakaul/tinyjam/refs/heads/main/arya-curated" |
| 50 | +) |
45 | 51 |
|
46 | 52 |
|
47 | 53 | def configure_logging(verbose: bool) -> None: |
@@ -159,6 +165,35 @@ def build_subtitle_args(target_language: str) -> List[str]: |
159 | 165 | ] |
160 | 166 |
|
161 | 167 |
|
| 168 | +def fetch_curated_jamlist() -> Path: |
| 169 | + try: |
| 170 | + with urlopen(CURATED_JAMLIST_URL, timeout=30) as response: |
| 171 | + payload = response.read() |
| 172 | + except (HTTPError, URLError) as exc: |
| 173 | + logger.error("failed to download curated jam list | {}", exc) |
| 174 | + sys.exit(1) |
| 175 | + except OSError as exc: |
| 176 | + logger.error("network error loading curated jam list | {}", exc) |
| 177 | + sys.exit(1) |
| 178 | + |
| 179 | + if not payload: |
| 180 | + logger.error("curated jam list download returned no data") |
| 181 | + sys.exit(1) |
| 182 | + |
| 183 | + try: |
| 184 | + with tempfile.NamedTemporaryFile( |
| 185 | + "wb", delete=False, prefix="tinyjam-curated-", suffix=".txt" |
| 186 | + ) as handle: |
| 187 | + handle.write(payload) |
| 188 | + temp_path = Path(handle.name) |
| 189 | + except OSError as exc: |
| 190 | + logger.error("failed to persist curated jam list | {}", exc) |
| 191 | + sys.exit(1) |
| 192 | + |
| 193 | + logger.info("using curated jam list from {}", CURATED_JAMLIST_URL) |
| 194 | + return temp_path |
| 195 | + |
| 196 | + |
162 | 197 | @dataclass |
163 | 198 | class TinyJamContext: |
164 | 199 | jamlist: Path |
@@ -820,15 +855,19 @@ def run(self) -> int: |
820 | 855 | return 0 |
821 | 856 |
|
822 | 857 |
|
823 | | -def parse_args(argv: Optional[Sequence[str]] = None) -> TinyJamContext: |
| 858 | +def parse_args( |
| 859 | + argv: Optional[Sequence[str]] = None, |
| 860 | + *, |
| 861 | + require_list: bool = True, |
| 862 | +) -> TinyJamContext: |
824 | 863 | parser = argparse.ArgumentParser( |
825 | 864 | description="Jam to tiny desks with tinyjam (Python edition)", |
826 | 865 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
827 | 866 | ) |
828 | 867 | parser.add_argument( |
829 | 868 | "-l", |
830 | 869 | "--list", |
831 | | - required=True, |
| 870 | + required=require_list, |
832 | 871 | type=Path, |
833 | 872 | help="path to artist list file (one per line)", |
834 | 873 | ) |
@@ -917,8 +956,14 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> TinyJamContext: |
917 | 956 |
|
918 | 957 |
|
919 | 958 | def main(argv: Optional[Sequence[str]] = None) -> int: |
920 | | - ctx = parse_args(argv) |
| 959 | + args_list = list(argv) if argv is not None else sys.argv[1:] |
| 960 | + use_curated_default = len(args_list) == 0 |
| 961 | + ctx = parse_args(args_list, require_list=not use_curated_default) |
921 | 962 | configure_logging(ctx.verbose) |
| 963 | + if use_curated_default: |
| 964 | + ctx.jamlist = fetch_curated_jamlist() |
| 965 | + ctx.nodownload = True |
| 966 | + logger.info("no options provided | defaulting to streaming mode") |
922 | 967 | app = TinyJam(ctx) |
923 | 968 | return app.run() |
924 | 969 |
|
|
0 commit comments