Rewrite README as internal deployment/reference documentation
Replace the original upstream template README (public-project framing, roadmap items already built, links to a different GitHub repo) with documentation matching the actual private deployment: real docker-compose.yml, the Gitea->Dockhand rebuild-on-push behavior, env vars/CLI flags, data file locations, and the full current feature set (theming, notes hub, video duration/ffprobe caching, backup export, Outline integration) that had never been documented. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||

|
||||
*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
|
||||
## 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 |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Installation
|
||||
## Features
|
||||
|
||||
### 🔁 Quick Start (Local)
|
||||
**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)
|
||||
|
||||
1. Clone the repo:
|
||||
**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
|
||||
|
||||
```bash
|
||||
git clone https://github.com/WhiskeyCoder/OfflineU.git
|
||||
cd OfflineU
|
||||
```
|
||||
**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
|
||||
|
||||
2. Install Python dependencies:
|
||||
**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)
|
||||
|
||||
```bash
|
||||
pip install flask
|
||||
```
|
||||
**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)
|
||||
|
||||
3. Run the app:
|
||||
|
||||
```bash
|
||||
python offlineu_core.py --create-templates
|
||||
```
|
||||
|
||||
4. Open your browser:
|
||||
|
||||
```
|
||||
http://127.0.0.1:5000
|
||||
```
|
||||
**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
|
||||
|
||||
---
|
||||
|
||||
## 📂 Folder Structure Example
|
||||
## Data files
|
||||
|
||||
```bash
|
||||
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 |
|
||||
| `<course_path>` | 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.**
|
||||
| --------- | ------------------------------------------------------------------ |
|
||||
| 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` |
|
||||
|
||||
Reference in New Issue
Block a user