- courses.py: curs "quantic" (shop.cursnlp.ro/curs/34, root nlp-quantic/) - download.py: yt-dlp cu 8 fragmente paralele + fara progress bar - rename_phone_quantic.py: copii MP3 cu ID3 (titlu, album per modul, track) + coperta - rename_phone_practitioner.py: font fallback Linux, an pe coperta din YEAR - nlp-quantic/: coperti M1-M5, pagina index.html pentru descarcare pe telefon, PDF-uri rezumat Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017NC1T6XwMepWVPuGZELZtT
90 lines
3.0 KiB
Python
90 lines
3.0 KiB
Python
"""
|
|
Shared course configuration for the NLP Master pipeline.
|
|
|
|
A single pipeline (download -> transcribe -> summarize -> pdf) runs on
|
|
multiple courses by passing --course <key>. Scripts resolve all artifact
|
|
paths against course["root"], so curs master remains in-place at repo root
|
|
and subsequent courses land in their own subdirectory.
|
|
"""
|
|
from pathlib import Path
|
|
|
|
COURSES = {
|
|
"master": {
|
|
"name": "NLP Master Practitioner Bucuresti 2025",
|
|
"base_url": "https://cursuri.aresens.ro",
|
|
"course_path": "/curs/26",
|
|
"login_path": "/login",
|
|
"env_user": "COURSE_USERNAME",
|
|
"env_pass": "COURSE_PASSWORD",
|
|
# Curs master stays at repo root for backward-compat with M1-M6 outputs.
|
|
"root": Path("."),
|
|
},
|
|
"practitioner": {
|
|
"name": "NLP Practitioner (cursnlp.ro)",
|
|
"base_url": "https://shop.cursnlp.ro",
|
|
"course_path": "/curs/50",
|
|
"login_path": "/login",
|
|
"env_user": "PRACTITIONER_USERNAME",
|
|
"env_pass": "PRACTITIONER_PASSWORD",
|
|
"root": Path("nlp-practitioner"),
|
|
},
|
|
"quantic": {
|
|
"name": "NLP Quantic (cursnlp.ro)",
|
|
"base_url": "https://shop.cursnlp.ro",
|
|
"course_path": "/curs/34",
|
|
"login_path": "/login",
|
|
"env_user": "PRACTITIONER_USERNAME",
|
|
"env_pass": "PRACTITIONER_PASSWORD",
|
|
"root": Path("nlp-quantic"),
|
|
},
|
|
}
|
|
|
|
|
|
def get_course(key: str) -> dict:
|
|
"""Return course config by key; SystemExit on unknown key."""
|
|
if key not in COURSES:
|
|
raise SystemExit(
|
|
f"Unknown course '{key}'. Available: {sorted(COURSES)}"
|
|
)
|
|
c = dict(COURSES[key])
|
|
c["key"] = key
|
|
c["course_url"] = c["base_url"] + c["course_path"]
|
|
c["login_url"] = c["base_url"] + c["login_path"]
|
|
return c
|
|
|
|
|
|
def course_paths(course: dict) -> dict:
|
|
"""Resolve artifact paths under course['root']."""
|
|
root = course["root"]
|
|
return {
|
|
"root": root,
|
|
"manifest": root / "manifest.json",
|
|
"audio_dir": root / "audio",
|
|
"wav_cache_dir": root / "audio_wav",
|
|
"transcripts_dir": root / "transcripts",
|
|
"summaries_dir": root / "summaries",
|
|
"pdf_dir": root / "summaries" / "pdf",
|
|
"master_guide": root / "SUPORT_CURS.md",
|
|
}
|
|
|
|
|
|
def validate_manifest_course(manifest: dict, course_key: str) -> None:
|
|
"""
|
|
Ensure a pre-existing manifest belongs to the course currently being run.
|
|
|
|
Legacy policy: a manifest without `course_key` (written before this refactor)
|
|
is treated as `master`. This keeps backward-compat with the existing
|
|
curs_26 manifest.json from M1-M6.
|
|
"""
|
|
mck = manifest.get("course_key")
|
|
if mck is None:
|
|
effective = "master"
|
|
else:
|
|
effective = mck
|
|
if effective != course_key:
|
|
raise SystemExit(
|
|
f"Manifest belongs to course '{effective}' but --course='{course_key}'. "
|
|
f"Refusing to corrupt cross-course state. "
|
|
f"Delete {course_key}'s manifest to start fresh, or run with --course={effective}."
|
|
)
|