diff --git a/README.md b/README.md index f12543c..b0b8e52 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,157 @@ -# OfflineU: Self-Hosted Local Course Loader & Progress Tracker +# OfflineU -**OfflineU** is a sleek, self-hosted web app designed to load and view your offline video, audio, text, and quiz-based training courses. Whether it's Udemy downloads, "open sourced" training archives, or personal content, OfflineU turns your course folder into a fully navigable dashboard with automatic progress tracking. +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.* --- -## ✨ Features +## Deployment -* 📁 **Dynamic folder parsing**: Scans and maps your course structure into a browsable tree view. -* 🎥 **Video & Audio player**: Integrated media player with resume & completion tracking. -* 📄 **Text & HTML viewer**: Supports .txt, .md, .html, .pdf, and more. -* ✅ **Lesson progress tracking**: Auto-saves your time spent and marks lessons as completed. -* ♻️ **Continue where you left off**: Resume instantly from your last-accessed lesson. -* 💾 **Local-first & private**: 100% offline. No cloud, no tracking, no nonsense. -* 🧑‍💻 **Works with any course format**: No metadata required, just structured folders. -* 🧠 **Ideal for hoarders, students, or offline learning setups** +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. --- -## 🗈️ Screenshots - -> ![image](https://github.com/WhiskeyCoder/OfflineU/blob/main/images/lesson-0-8-2025-08-04-04_58_17.png) - ---- - -## 🛠️ Installation - -### 🔁 Quick Start (Local) - -1. Clone the repo: - - ```bash - git clone https://github.com/WhiskeyCoder/OfflineU.git - cd OfflineU - ``` - -2. Install Python dependencies: - - ```bash - pip install flask - ``` - -3. Run the app: - - ```bash - python offlineu_core.py --create-templates - ``` - -4. Open your browser: - - ``` - http://127.0.0.1:5000 - ``` - ---- - -## 📂 Folder Structure Example +## 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 @@ -66,64 +161,19 @@ MyCourse/ │ ├── 04 - Advanced Tips.mp4 │ └── resources/ │ └── extras.md -└── .offlineu_progress.json ← created automatically +├── .offlineu_progress.json ← created automatically +└── .offlineu_duration_cache.json ← created automatically ``` -> 🌟 File types are detected automatically — videos, audio, quizzes, and docs. +No metadata files needed — course/section/lesson names come straight from +folder and file names. ---- +## Supported file types -## 📁 Supported File Types - -| Type | Extensions | -| --------- | ----------------------------------------------------------- | -| Videos | `.mp4`, `.mkv`, `.webm`, `.mov`, `.avi`, etc. | -| Audio | `.mp3`, `.wav`, `.aac`, etc. | -| Docs | `.txt`, `.md`, `.html`, `.pdf`, `.docx` | -| Subtitles | `.srt`, `.vtt` | -| Quizzes | Detected if file name contains `quiz`, `exam`, `test`, etc. | - ---- - -## ⚙️ CLI Options - -| Option | Description | -| -------------------- | ------------------------------- | -| `--host` | Set host (default: `127.0.0.1`) | -| `--port` | Set port (default: `5000`) | -| `--debug` | Enable Flask debug mode | -| `--create-templates` | Generate default HTML templates | -| `` | Load course directly at startup | - ---- - -## 🧠 Roadmap - -* [x] Base function and testing -* [ ] Multi-user profile support -* [ ] Dark/light theme switcher -* [ ] Built-in quiz interactivity -* [ ] Import/export course metadata -* [ ] Mobile app wrapper -* [ ] Self hosted Docker Deployment - ---- - -## 💬 Community - -Join the development, suggest features, or ask questions via: - -* GitHub Issues: [https://github.com/WhiskeyCoder/OfflineU/issues](https://github.com/WhiskeyCoder/OfflineU/issues) - ---- - -## 🛡️ License - -MIT License — Use freely, modify locally, share widely. - ---- - -## ✨ Author - -Built with ❤️ by [@WhiskeyCoder](https://github.com/WhiskeyCoder) -Inspired by the dream of **learning freely, offline, and without limits.** +| 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` |