Tutorio is a local-first desktop application that reconstructs long-form tutorials as practical learning manuals. It is intentionally not a summariser: generated guides preserve procedures, explanations, commands, warnings, mistakes, shortcuts, and source timestamps.
Everything runs on the user's machine. There are no cloud services, accounts, or authentication.
Compile a YouTube tutorial or local document, then return to it from the local guide library.
Tutorio keeps processing local, but its runtime dependencies are installed separately and are not bundled with release downloads:
- Ollama running locally with the configured model. Tutorio recommends
gemma4:e4b. yt-dlpfor YouTube sources.- Poppler's
pdftotextandpdftocairofor PDF extraction and page previews.
For example, on macOS:
brew install yt-dlp poppler
ollama pull gemma4:e4bEnsure the Ollama service is running before compiling. The Ollama desktop application normally manages it automatically. If you installed only the command-line service and nothing else is running it, start ollama serve in a terminal and leave that process open. An “address already in use” error means an Ollama service is already listening, so a second one is unnecessary.
Scanned or image-only PDFs are detected but require a future OCR adapter; Tutorio does not silently generate a guide from missing text. Developer requirements such as Go, Wails, and Node.js are listed under Development.
Running wails dev from a terminal gives Tutorio the terminal's PATH. A downloaded application launched from Finder, the Windows desktop, or a graphical Linux launcher may receive a different, more restricted PATH. The tool can therefore be installed and work during development while the packaged application cannot find it.
Find each executable from a terminal:
command -v yt-dlp
command -v pdftotext
command -v pdftocairoOn Windows PowerShell, use Get-Command yt-dlp, Get-Command pdftotext, and Get-Command pdftocairo, then read each command's Source value.
Then place the returned absolute paths in the installed application's config.yaml. On Apple Silicon Homebrew, this commonly looks like:
tools:
yt_dlp_path: /opt/homebrew/bin/yt-dlp
pdftotext_path: /opt/homebrew/bin/pdftotext
pdftocairo_path: /opt/homebrew/bin/pdftocairoIntel macOS Homebrew commonly uses /usr/local/bin instead. Use the paths reported by your own system rather than assuming either location. See Configuration for the platform-specific config.yaml location. Bare command names remain suitable when the application's environment already contains the installation directory.
Prebuilt macOS, Windows, and Linux artifacts are published on the GitHub Releases page.
To try Tutorio:
- Open the latest release.
- Download the artifact for your platform.
- Extract the archive.
- Run
tutorio.
macOS release builds are signed with Developer ID and notarized by Apple.
This repository provides a buildable Wails application and the architectural spine for the MVP:
- YouTube subtitle retrieval through
yt-dlp. - local
.txt,.srt, and.vtttranscript ingestion. - local text-based PDF ingestion with durable source chunks and page-aware evidence through Poppler.
- transcript cleaning and source-aware, cue-preserving segmentation.
- structured JSON generation through a local Ollama model.
- structural verification before persistence.
- a single-worker background compilation queue with cancellation and automatic restart recovery.
- interrupted-job-first recovery and user-controlled “Run first” preemption for pending compilations; completed sections and source identity are retained.
- persistent per-section results with targeted retry after interruption.
- active-section timing, slow-call indicators, inspectable local model diagnostics, source-neutral extracted-text metrics, and separate prompt/generation token rates for model speed comparisons.
- verified transcript excerpts, clickable source timestamps, and exact PDF evidence previews.
- section-level overviews, prerequisite deduplication, and locally bundled KaTeX formula rendering.
- visible source sections, guide editing, single-section regeneration, source-grounded deep dives, and portable HTML/Markdown export.
- collapsible guide sections that double as a compact index, plus compact reference blocks for supporting material.
- SQLite storage and a Wails guide library/reader.
The UI is deliberately small. Model setup and native transcript-file selection remain in the usable-MVP phase described in the roadmap.
Dependency direction points inward. Domain and orchestration packages do not import Wails, SQLite, Ollama, or process-execution details.
Wails UI ─> jobs.Manager ─> jobs.Pipeline ─> source / transcript / guide interfaces
yt-dlp ───┤ ▲ ▲
Ollama ───┤ │ │
SQLite ───┘ domain models use-case rules
flowchart TD
A[YouTube URL or transcript file] --> B[Source adapter]
B --> C[Timestamped transcript or page-aware source chunks]
C --> D[Clean and split into bounded segments]
D --> E[Generate structured JSON and source chunk IDs with Ollama]
E --> F[Validate citations and resolve exact source evidence]
F --> G[Merge sections, deduplicate, and build cheat sheet]
G --> H[Synthesize a concise guide overview]
H --> I[Verify guide structure]
I --> J[(Save complete guide in SQLite)]
J --> K[Display in library and guide reader]
- The UI persists a pending job and returns immediately; a single background worker runs queued jobs without competing Ollama requests.
- The source registry selects the YouTube or local-file adapter.
yt-dlpretrieves YouTube subtitles; TXT, SRT, and VTT files are parsed directly; Poppler extracts text PDFs by physical page.- Transcript text is cleaned while timestamps are preserved. PDF text is persisted as immutable, content-addressed source chunks before generation.
- Tutorio prefers explicit YouTube chapters and conservative PDF heading boundaries, then uses long transcript pauses as a soft boundary when enough content has accumulated. The configured Unicode-character budget remains a hard maximum and fallback.
- Ollama reconstructs each segment as structured guide content and must return a concise title specific to that section; live progress is sent to Wails.
- Model variations are normalized. For PDFs, returned chunk IDs are checked against the exact chunks supplied to that request, deduplicated, capped, and resolved to stored text. Unknown IDs are discarded and unsupported steps remain uncited.
- Each completed section and its performance metadata are persisted so failed work can resume without repeating successful sections.
- A small, non-blocking Ollama request synthesizes a guide-level overview from stored section titles and summaries. If it fails, the complete guide is still saved and the reader offers a retry.
- The result is verified, stored as structured JSON in SQLite, and loaded by the library reader after restart.
Package responsibilities:
| Package | Responsibility |
|---|---|
repository main.go |
Wails-required composition root and lifecycle |
cmd/app |
Desktop frontend assets (kept under the conventional app shell) |
internal/source |
Source contract and adapter registry |
internal/source/youtube |
yt-dlp process adapter |
internal/source/local |
Local transcript-file adapter |
internal/transcript |
Source-neutral parsing, cleaning, segmentation |
internal/llm |
Model-provider contract and Ollama HTTP adapter |
internal/guide |
Guide domain, generation and verification contracts |
internal/evidence |
Registered sources, immutable source chunks, evidence resolution |
internal/jobs |
Background queue, pipeline use case, recovery, and stage orchestration |
internal/storage/sqlite |
SQLite connection, schema, guide repository |
internal/config |
YAML configuration and local defaults |
internal/ui |
Thin Wails-facing application API |
internal/exporter |
Output contract and portable HTML/Markdown implementations |
Interfaces are owned near the code that consumes their behavior and kept small. context.Context crosses every operation that can block. Dependencies are assembled only in the root main.go; no package-level mutable state is used. Wails v2's binding generator requires its Go entrypoint at the project root, so this is the one intentional variation from the usual cmd/app/main.go layout.
New content sources implement source.Source and register at startup. Whisper can become another source/transcription adapter; screenshots can be introduced as an optional enrichment stage; vision models can implement a provider boundary parallel to llm.Provider. Exporters and learning-artifact generators should be independent use cases over persisted guide.Guide values. Playlist support should compose child jobs rather than enlarge the YouTube adapter.
This keeps future media capabilities out of the text-only MVP while leaving clear seams for them.
The stored guide includes a synthesized overview with readiness metadata, prerequisites, final outcome, ordered steps, citations, transcript evidence, important concepts, commands, keyboard shortcuts, warnings, common mistakes, cheat sheet, appendix, source timestamps, source-grounded deep dives, and generation metadata. SQLite stores searchable identity/summary columns plus the complete versionable guide as validated JSON. Registered sources, immutable source chunks, and evidence identities are normalized for reuse. Jobs and individual transcript/model sections are persisted separately for recovery, targeted regeneration, overview retry, timing, and local diagnostics. The base schema is in internal/storage/sqlite/schema.sql, with numbered changes in internal/storage/sqlite/migrations.
Generated guides are organised into navigable sections that can be expanded, regenerated, or explored in more depth.
For a newly generated PDF guide, selecting a citation opens a lightweight evidence drawer containing the exact extracted chunk, its neighbouring chunks, the source title, physical PDF page, and a locally rendered page preview for figures, tables, and formatting. “Open full PDF” remains a secondary native-viewer fallback. Older saved guides with page-only references still show the source and physical page, but correctly show no excerpt; recompile the PDF to create durable evidence. See the evidence architecture decision.
PDF citations open exact extracted evidence alongside a page preview, with the original document still available when more context is needed.
For production evolution, add numbered embedded migrations rather than editing an already-released migration.
The reader's Export menu offers:
- HTML — the recommended sharing format. It produces one dependency-free, responsive file with embedded styling, print rules, structured steps, commands, shortcuts, warnings, and source links. It can be opened offline in phone and desktop browsers or printed to PDF by the recipient.
- Markdown — a lightweight source format for note-taking tools, version control, and further editing.
YouTube timestamp links remain portable. PDF citations are grouped once per physical page and link to deduplicated exact-text evidence embedded later in the HTML file. The complete original PDF and page images are not included, which keeps exports compact and avoids exposing absolute filesystem paths. Older page-only citations remain clearly labelled but cannot include evidence that was never stored; share the original PDF separately when recipients need diagrams, formatting, or broader context.
Generation details describe model workload and speed, not guide quality:
- Model is the Ollama model that generated the guide sections.
- Sections is the number of bounded source segments sent as separate generation requests. More sections can add request overhead even when total token counts are similar.
- Duration is the sum of Ollama's total duration for the successful section requests. It includes more than token evaluation, so it will be longer than the two evaluation times alone. It does not currently include source extraction, final verification/storage, or overview synthesis.
- Tokens in/out are Ollama's prompt and generated token counts. Input tokens include the extracted source plus Tutorio's repeated instructions, structured-output schema, timestamps, and source metadata; they are therefore a model-workload measure rather than the exact size of the original source.
- Prompt speed is prompt tokens divided by Ollama's prompt-evaluation duration. Prompt processing is highly parallel and is normally much faster than output generation.
- Generation speed is output tokens divided by Ollama's output-evaluation duration. Output is generated autoregressively, so each token depends on the preceding token.
- Source content is the word and Unicode-character count of the cleaned text before it is sent to the model. It is independent of Ollama's tokenizer and does not include Tutorio's prompts.
- Source records the extraction method plus a source-specific extent when available: transcript duration for timed media or the highest physical page containing extracted PDF text.
The speed figures are weighted aggregates across all sections: total tokens divided by total corresponding evaluation time. They are useful for comparing inference speed on the same hardware and broadly similar workloads, but they do not measure completeness, accuracy, concision, or source fidelity. Older guides created before evaluation timing was stored omit these rates rather than estimate them.
Raw file bytes are intentionally not shown as an ingestion metric. They are not comparable across PDFs, subtitles, videos, and future source types, and may include images or compression unrelated to extracted text. Extracted words and characters provide the source-neutral comparison instead. Word counts are whitespace-delimited and therefore less representative for languages that do not separate words with spaces; the Unicode-character count remains the more stable cross-language measure. Changing caption, transcription, OCR, or cleaning implementations can still change both counts, so the stored extraction method provides necessary context.
Tutorio resolves configuration in this order:
- The path in
TUTORIO_CONFIG_PATH, when set. config.yamlin the current working directory (convenient forwails dev).- The platform user config directory:
- macOS:
~/Library/Application Support/tutorio/config.yaml - Linux:
$XDG_CONFIG_HOME/tutorio/config.yaml, or~/.config/tutorio/config.yaml - Windows:
%AppData%\tutorio\config.yaml
- macOS:
If no configuration exists, Tutorio creates one at the resolved platform path on first launch. It records absolute paths for supported tools when they can be discovered from the application environment or common installation locations, and otherwise retains editable command-name defaults. Existing configuration files are never overwritten.
Copy config.example.yaml to config.yaml in the repository root when you want a project-specific development configuration for wails dev. The startup log reports the selected path and Ollama model. The library also shows a non-blocking local-setup panel when Ollama, the configured model, or a source-specific tool cannot be found. Each executable path can be validated and saved independently in that panel; Tutorio updates only that tool-path setting and activates it immediately for new work. No restart is required.
Missing yt-dlp disables YouTube compilation only. Missing pdftotext disables PDF import, while TXT/SRT/VTT import remains available. Missing pdftocairo disables PDF page previews but does not prevent PDF guide generation or access to saved guides. The generated configuration path remains visible for users who prefer to edit YAML manually; values changed outside Tutorio are applied on the next launch.
ollama:
base_url: http://127.0.0.1:11434
model: gemma4:e4b
max_output_tokens: 8192
context_window: 32768
tools:
yt_dlp_path: yt-dlp
pdftotext_path: pdftotext
pdftocairo_path: pdftocairo
processing:
segment_characters: 12000The database path can be overridden when needed:
database:
path: /absolute/path/to/tutorio.dbPrefer an absolute path for this override. Relative paths are resolved from the application's current working directory and can therefore select a different database depending on how Tutorio is launched.
segment_characters is a hard Unicode-character budget, not a tokenizer target. Explicit YouTube chapters may create shorter sections. PDF headings and long transcript pauses are used only after enough content has accumulated, which avoids turning every small heading or silence into a separate section. When no trustworthy structure exists, Tutorio falls back to cue boundaries near the configured limit. A future semantic segmenter can replace these heuristics without changing the pipeline.
Tutorio invokes configured tools locally and sends model requests only to the configured Ollama URL.
Tutorio stores its SQLite library outside the executable or application bundle. Replacing Tutorio with a newer build therefore preserves generated guides, jobs, source chunks, citations, and stored text evidence. On startup, the application opens the existing database and applies any required embedded migrations.
The default database is:
- macOS:
~/Library/Application Support/tutorio/tutorio.db - Linux:
$XDG_CONFIG_HOME/tutorio/tutorio.db, or~/.config/tutorio/tutorio.db - Windows:
%AppData%\tutorio\tutorio.db
This platform-native location is intentional. It follows operating-system conventions and is preferable to creating a hidden $HOME/.tutorio directory.
Imported files are not copied into the database. The generated guide and extracted text evidence remain available if an original PDF is moved or deleted, but opening the full PDF or rendering its page preview requires the file to remain at its registered path.
For a basic backup, close Tutorio and copy tutorio.db. Restoring that file to the same configured location restores the local library. A future in-app backup/export flow can package the database and registered source files more comprehensively.
There is no universal best model: tutorial reconstruction trades generation time, completeness, concision, schema reliability, and local memory use.
| Model | Expected fit for tutorio |
|---|---|
gemma4:e4b |
Recommended quality baseline when concise, tightly scoped guide sections matter more than generation speed. In initial testing it was slower but less verbose. |
qwen3.5:9b |
Useful detail-oriented alternative. Initial testing extracted more material, but sometimes produced overly verbose or unnecessary content. |
qwen3.5:4b |
A useful newer, smaller Qwen competitor for speed/quality experiments against Gemma 4 E4B. It has not yet been validated by this project. |
qwen3:8b |
Older text-only Qwen 3 baseline. The tag means “Qwen 3, 8B parameters”; it is not a Qwen 3.8 release and does not supersede Qwen 3.5. |
Keep max_output_tokens, context_window, segmentation, source URL, and prompt settings identical when comparing models. Compare total duration, step usefulness, timestamp accuracy, shortcut/command extraction, schema reliability, and unnecessary repetition—not output length alone.
Development additionally requires:
- Go 1.25 or newer.
- Wails v2 and its platform prerequisites.
- Node.js/npm for the Vite frontend.
Install dependencies and run tests:
go mod tidy
go test ./...Run the desktop app with live frontend reload:
wails devBuild the production desktop bundle:
wails buildUseful equivalents are available through make test, make dev, and make build.
When adding a feature, put domain data/rules in an inner package, define the smallest needed interface, implement infrastructure in an adapter package, and wire it at the composition root. Use fakes at the interface boundary in unit tests. External-process and HTTP adapters should additionally have fixture-driven contract tests.
Run the opt-in local integration test against a real URL and the configured Ollama model with:
TUTORIO_CONFIG_PATH="$PWD/config.yaml" \
TUTORIO_TEST_URL="https://www.youtube.com/watch?v=VIDEO_ID" \
go test -tags=integration ./internal/integration -run TestCompileYouTube -v -count=1 -timeout=20mThe test uses a temporary SQLite database and does not add its output to the desktop library.
The release workflow builds macOS, Windows, and Linux artifacts from tags matching v* and publishes them to a GitHub Release.
Current release limitations:
- Windows signing is not configured.
- Linux packaging contains the Wails build output rather than a distribution-specific package.
- Runtime tools such as Ollama,
yt-dlp, and Poppler are not bundled.
- Long transcripts are generated section-by-section and merged deterministically. A later synthesis pass may improve cross-section narrative cohesion without sacrificing provenance.
- Deep dives deliberately use only the saved source transcript and current section steps; optional cited web research is not implemented.
- PDF step citations are grounded to exact extracted chunks. Broader claim-level semantic verification and support classification remain future work.
- The queue deliberately runs one compilation at a time to avoid concurrent local-model memory pressure.
- The backend supports transcript-file import, while native file selection and its frontend control are Phase 1 UI work.
- Audio transcription, screenshots, vision, playlists, PDF export, flashcards, quizzes, and progressive learning are architecture extension points only.
See docs/ROADMAP.md for phased delivery criteria.


