# OfflineU Self-hosted course viewer for a local video/audio/text training library. Runs as a single Flask app in Docker, tracks watch progress and notes per lesson, and gives the whole library a dashboard-style home page instead of a bare file browser. This is a personal deployment, not a public project — this doc is internal reference for running/maintaining it, not a pitch. ![Lesson view](images/lesson-0-8-2025-08-04-04_58_17.png) *Screenshot predates the current theming/icon work — kept for a rough idea of layout, not pixel-accurate.* --- ## Deployment Deployed via `docker-compose.yml`, built directly from the `Dockerfile` in this repo (`pull_policy: build`, not a registry pull): ```yaml services: offlineu: build: . pull_policy: build container_name: offlineu network_mode: host ports: - "5000:5000" environment: - PUID=1000 - PGID=10 - TZ=America/New_York - FLASK_ENV=production volumes: - /volume1/files/training:/app/courses # course library - /volume2/docker/offlineu/data:/app/data # settings/progress/notes restart: unless-stopped ``` **Auto-deploy:** the Gitea repo has a webhook to Dockhand — a push to `main` triggers a full image **rebuild** from the Dockerfile (confirmed, not just a container restart), so Dockerfile changes (e.g. adding `ffmpeg`) take effect on the very next push without any manual step. --- ## Local development ```bash pip install -r requirements.txt python offlineu_core.py --library-path /path/to/courses --debug ``` Opens on `http://127.0.0.1:5000`. `ffprobe` (from ffmpeg) needs to be on `PATH` for video-duration lookups to work locally; without it those just silently stay blank instead of erroring. ### CLI options | Option | Description | | ---------------------- | ----------------------------------------------------- | | `--host` | Bind host (default `0.0.0.0`) | | `--port` | Bind port (default `5000`) | | `--debug` | Enable Flask debug mode | | `--create-templates` | Regenerate default templates if missing | | `--library-path` | Course library root (overrides `COURSES_LIBRARY_PATH`) | | `course_path` (positional) | Load a specific course directly at startup | ### Environment variables | Variable | Default | Purpose | | ---------------------- | -------------- | ------------------------------------------ | | `COURSES_LIBRARY_PATH` | `/app/courses` | Course library root | | `OFFLINEU_DATA_DIR` | `/app/data` | Where settings/progress/notes data lives | | `AUTO_LOAD_COURSE` | — | Course path to load automatically on start | --- ## Features **Library browsing** - Lazy-loading folder browser (grid or list view) with search, including an opt-in transcript search across subtitle files - Course cards show media file count, total video/audio runtime, and completion % at a glance - Hide courses/folders from the browser without touching anything on disk; bulk-select to hide or queue several at once - Bulk rename across course/folder names (find & replace) **Dashboard** - Library-wide stats (courses, lessons completed, time watched, day streak) and a 90-day activity heatmap - Next Up queue (manually curated, reorderable) - Pick Back Up (courses with progress that have gone stale) - Recently Added / Recently Viewed - Surprise Me — random pick, weighted toward incomplete courses **Playback & progress** - Video/audio player with resize, playback-speed presets, and resume-from- last-position - Auto-tracks watch progress and completion per lesson - Video/audio durations read via `ffprobe` and cached persistently per course (`.offlineu_duration_cache.json`), so total runtime and per-lesson length show up before you've ever pressed play — not just after - Settings → "Precompute Video Lengths" walks the whole library in the background to populate that cache up front, with live progress **Notes** - Timestamped notes per lesson, capturable via a keyboard shortcut without leaving the player - Notes Hub: every note across the whole library, searchable, in one list - Per-course study guide export (markdown, organized by section) **Personalization** (Settings) - Built-in themes or a custom accent color, corner radius, and card style - Font, text size, page width, and spacing density - Default library path override + manual "Refresh Library" (bypasses the 5-minute filesystem-scan cache) **Backup & integrations** - One-click backup export: settings, hidden-path choices, recent-view history, and every course's progress/notes as a zip (not the course files themselves) - Optional [Outline](https://www.getoutline.com/) integration — push a lesson's notes to an Outline document/collection --- ## Data files Everything under `OFFLINEU_DATA_DIR` (app-wide, not tied to a course): | File | Contents | | ---------------------- | ------------------------------------------ | | `settings.json` | Theme, layout, library path, etc. | | `hidden_paths.json` | Courses/folders curated out of the browser | | `next_up.json` | The Next Up queue, in order | | `recent_views.json` | Cross-course "Recently Viewed" history | | `outline_config.json` | Outline API token/collection mapping | Per-course, written inside the course's own folder on the library volume: | File | Contents | | ----------------------------------- | ---------------------------------------------------- | | `.offlineu_progress.json` | Per-lesson completed/progress/duration/notes | | `.offlineu_duration_cache.json` | ffprobe duration cache, keyed by (path, size, mtime) | --- ## Folder structure example ``` MyCourse/ ├── Section 1/ │ ├── 01 - Intro.mp4 │ ├── 02 - Setup Guide.pdf │ └── 03 - Quiz.html ├── Section 2/ │ ├── 04 - Advanced Tips.mp4 │ └── resources/ │ └── extras.md ├── .offlineu_progress.json ← created automatically └── .offlineu_duration_cache.json ← created automatically ``` No metadata files needed — course/section/lesson names come straight from folder and file names. ## Supported file types | Type | Extensions | | --------- | ------------------------------------------------------------------ | | Video | `.mp4` `.mkv` `.avi` `.mov` `.webm` `.m4v` `.flv` `.wmv` | | Audio | `.mp3` `.wav` `.m4a` `.aac` `.ogg` `.flac` | | Documents | `.txt` `.md` `.html` `.htm` `.pdf` `.docx` `.doc` `.rtf` | | Subtitles | `.srt` `.vtt` `.ass` `.sub` `.sbv` | | Quizzes | Any doc file whose name contains `quiz`, `exam`, `test`, `assessment`, `exercise`, `assignment`, or `homework` |