diff --git a/Euni/NOTES.md b/Euni/NOTES.md index 7ef5072..7765b0f 100644 --- a/Euni/NOTES.md +++ b/Euni/NOTES.md @@ -81,6 +81,12 @@ python euni_stahni.py --seaweed-backfill --from-json ## Použití +Nejjednodušší: **`python euni_menu.py`** — interaktivní menu s volbami 1–9 +(test / dokumenty / vše / 720p / dashboard / obnova / backfill / re-scrape). +Po doběhnutí akce se vrátí do menu, `Ctrl+C` přeruší jen aktuální akci. + +Ručně přes CLI: + ```bat python euni_stahni.py --scrape-only # jen inventura → Mongo + JSON python euni_stahni.py --no-videos # scrape + stáhne jen dokumenty @@ -89,6 +95,8 @@ python euni_stahni.py --from-json --no-videos # přeskočí scrape, stáhne z M python euni_stahni.py --professions all # všechny profese (2,4,5,6,7) python euni_stahni.py --limit 3 # jen prvních 3 kurzy (test) python euni_stahni.py --no-mongo # bez zápisu do Mongo +python euni_stahni.py --frags 20 # víc paralelních HLS fragmentů (rychlejší) +python euni_stahni.py --video-format "bestvideo[height<=720]+bestaudio/best" # 720p python euni_report.py # přehled stavu python euni_report.py --soukroma # seznam přeskočených videí ``` diff --git a/Euni/euni_menu.py b/Euni/euni_menu.py new file mode 100644 index 0000000..7e21b86 --- /dev/null +++ b/Euni/euni_menu.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +""" +euni_menu.py — interaktivní menu pro stahování kurzů z euni.cz. + +Spuštění: + python euni_menu.py + +Jen vyber číslo a Enter. Každá volba spustí příslušný skript a po doběhnutí +se vrátíš do menu (Ctrl+C přeruší aktuální akci, ne celé menu). +""" + +import os +import subprocess +import sys +from pathlib import Path + +for _s in (sys.stdout, sys.stderr): + try: + _s.reconfigure(errors="backslashreplace") + except Exception: + pass + +SKRIPT_DIR = Path(__file__).resolve().parent +PY = sys.executable + +# klíč -> (popis, skript, argumenty) +AKCE = { + "1": ("Test - 3 kurzy, jen dokumenty (rychle)", + "euni_stahni.py", ["--from-json", "--no-videos", "--limit", "3"]), + "2": ("Vsechny dokumenty (PDF/prezentace)", + "euni_stahni.py", ["--from-json", "--no-videos"]), + "3": ("Vse vcetne videi - nejvyssi kvalita (1080p, velke)", + "euni_stahni.py", ["--from-json"]), + "4": ("Vse vcetne videi - 720p (mensi, rychlejsi)", + "euni_stahni.py", + ["--from-json", "--video-format", + "bestvideo[height<=720]+bestaudio/best"]), + "5": ("Jen videa (1080p)", + "euni_stahni.py", ["--from-json", "--no-docs"]), + "6": ("Prehled stavu (dashboard)", "euni_report.py", []), + "7": ("Obnova ze SeaweedFS na disk", "euni_restore.py", []), + "8": ("Backfill - dohrat chybejici kopie do SeaweedFS", + "euni_stahni.py", ["--seaweed-backfill", "--from-json"]), + "9": ("Aktualizovat seznam kurzu (znovu scrape do Mongo)", + "euni_stahni.py", ["--scrape-only"]), +} + + +def vycisti_obrazovku(): + os.system("cls" if os.name == "nt" else "clear") + + +def vypis_menu(): + print("=" * 60) + print(" EUNI - stahovani kurzu z euni.cz") + print("=" * 60) + print() + for k in sorted(AKCE): + print(f" {k}) {AKCE[k][0]}") + print() + print(" 0) Konec") + print() + + +def main(): + while True: + vycisti_obrazovku() + vypis_menu() + try: + volba = input("Vyber cislo a stiskni Enter: ").strip() + except (EOFError, KeyboardInterrupt): + print() + break + + if volba in ("0", "q", "exit", "konec"): + break + akce = AKCE.get(volba) + if not akce: + continue + + _, skript, args = akce + print() + try: + subprocess.run([PY, str(SKRIPT_DIR / skript), *args], + cwd=str(SKRIPT_DIR)) + except KeyboardInterrupt: + print("\nPreruseno uzivatelem.") + + try: + input("\n=== HOTOVO. Stiskni Enter pro navrat do menu ===") + except (EOFError, KeyboardInterrupt): + break + + +if __name__ == "__main__": + main() diff --git a/Euni/euni_stahni.py b/Euni/euni_stahni.py index fd9841d..0fcd38c 100644 --- a/Euni/euni_stahni.py +++ b/Euni/euni_stahni.py @@ -376,21 +376,23 @@ def stahni_dokument(s, url, out_dir: Path, label=""): return ("staženo", dest.name) -def stahni_video(embed, out_dir: Path, referer): - """Stáhne video přes yt-dlp; soukromé/nedostupné přeskočí. Vrací (stav, info).""" +def stahni_video(embed, out_dir: Path, referer, fmt="bestvideo*+bestaudio/best", + frags=10): + """Stáhne video přes yt-dlp; soukromé/nedostupné přeskočí. Vrací (stav, info, fp).""" if sv is None: - return ("chyba", "modul stahni_video není dostupný") + return ("chyba", "modul stahni_video není dostupný", None) try: import yt_dlp from yt_dlp.utils import DownloadError except ImportError: - return ("chyba", "yt-dlp není nainstalován") + return ("chyba", "yt-dlp není nainstalován", None) out_dir.mkdir(parents=True, exist_ok=True) ff_dir = sv.priprav_ffmpeg() opts = { "outtmpl": str(out_dir / "%(title)s [%(id)s].%(ext)s"), - "format": "bestvideo*+bestaudio/best", + "format": fmt, + "concurrent_fragment_downloads": frags, # paralelní HLS fragmenty = rychlejší "merge_output_format": "mp4", "logger": sv._TichyLogger(), "progress_hooks": [sv._progress_hook], @@ -458,6 +460,10 @@ def main(): help="přeskočí scrape, použije existující euni_kurzy.json") p.add_argument("--no-videos", action="store_true", help="nestahovat videa") p.add_argument("--no-docs", action="store_true", help="nestahovat dokumenty") + p.add_argument("--video-format", default="bestvideo*+bestaudio/best", + help="yt-dlp formát videa (např. \"bestvideo[height<=720]+bestaudio/best\")") + p.add_argument("--frags", type=int, default=10, + help="počet paralelně stahovaných HLS fragmentů videa (default 10)") p.add_argument("--limit", type=int, default=0, help="jen prvních N kurzů (test)") p.add_argument("--out", default=str(SKRIPT_DIR / "stazeno"), help="výstupní adresář") p.add_argument("--json", default=str(SKRIPT_DIR / "euni_kurzy.json"), @@ -602,7 +608,8 @@ def main(): if not a.no_videos: for v in c.get("videos", []): klic = material_klic("video", v)[0] - stav, info, fp = stahni_video(v["embed"], folder / "videa", c["url"]) + stav, info, fp = stahni_video(v["embed"], folder / "videa", c["url"], + fmt=a.video_format, frags=a.frags) if stav == "staženo": stat["vid_ok"] += 1 print(f" [VIDEO] {info}")