Layer overview
Layer responsibilities
CLI Layer
CLI Layer
The only entry point for human users. Uses Typer for argument parsing and Rich for terminal output. Heavy dependencies are loaded lazily inside each command body to keep startup time low.
| File | Role |
|---|---|
cli/app.py | Command definitions |
cli/_batch_runner.py | Multi-video orchestration, feeds events to PipelineDashboard |
cli/_single_runner.py | Single-video run path |
cli/_runtime.py | CliProcessRunner top-level coordinator |
cli/_display.py | Rich table and panel helpers |
cli/_admin.py | info / stats / history / cache / logs implementations |
The CLI layer may import from all other layers. It must not contain business logic — it delegates entirely to the pipeline.
Pipeline Layer
Pipeline Layer
Owns the core processing workflow. The only layer that coordinates between YouTube extraction, LLM generation, and storage.
| File | Role |
|---|---|
pipeline/core.py | CorePipeline: state, semaphores, DB singleton, public run() |
pipeline/_execution.py | process_single_video and run_pipeline implementations |
pipeline/generation.py | StudyMaterialGenerator: token counting, chunking, LLM calls |
May import from YouTube, LLM, storage, and domain layers. Must not import from the CLI layer.
YouTube Layer
YouTube Layer
Handles all YouTube-specific I/O: transcript fetching, metadata retrieval, playlist enumeration, and URL parsing.
| File | Role |
|---|---|
youtube/parser.py | URL and bare-ID parsing → ParsedURL |
youtube/transcript.py | fetch_transcript() with language fallback and retries |
youtube/metadata.py | get_video_metadata(), get_playlist_info(), get_source_metadata() |
youtube/playlist.py | extract_playlist_videos() |
youtube/extractor/ | Low-level HTTP client built from mixins |
Must not import from the LLM or pipeline layers.
LLM Layer
LLM Layer
Provider-agnostic interface over LiteLLM.
| File | Role |
|---|---|
llm/provider.py | LLMProvider, UsageTotals, get_provider() factory |
llm/prompts/study_notes.py | Study note generation prompts |
llm/prompts/chapter_notes.py | Per-chapter generation prompts |
llm/prompts/quiz.py | Quiz generation prompts |
Must not import from the YouTube, pipeline, or CLI layers.
Storage Layer
Storage Layer
Thread-safe SQLite persistence built on SQLAlchemy 2.0.
| File | Role |
|---|---|
storage/repository.py | DatabaseRepository: singleton per path, sync/async read/write |
storage/models.py | ORM models: VideoRecord, TranscriptRecord, RunStatsRecord, ExportRecord |
storage/schemas.py | Pydantic schemas for reading data out of the DB |
storage/migrations.py | Hand-rolled migration runner (no Alembic) |
Must not import from YouTube, LLM, pipeline, or CLI layers.
Domain Layer
Domain Layer
Pure Python value objects — no I/O of any kind. Safe to import from everywhere.
| File | Role |
|---|---|
domain/youtube.py | VideoMetadata, VideoTranscript, TranscriptSegment, VideoChapter, ParsedURL |
domain/events.py | EventType enum, PipelineEvent dataclass |
domain/results.py | PipelineResult, PipelineMetrics |
Cross-cutting concerns
Configuration
config.py exports a module-level settings object (a _LazyAppSettings facade over AppSettings). All modules import from notewise.config. Loaded once on first access and cached.
Error handling
All custom exceptions are defined inerrors.py. No other module defines project-specific exceptions. See Error Reference for the full hierarchy.
Logging
logging.py configures structlog at CLI startup. All modules obtain a logger via structlog.get_logger(__name__). API keys and other sensitive values are automatically redacted before writing to log files.
State directory
NOTEWISE_HOME.