OfflineAcademy — Your Personal Offline Learning Center

# OfflineAcademy

A self-hosted, LAN-first video course library for private learning.

Turn a local folder of course videos into a clean learning dashboard with progress tracking, bookmarks, tags, categories, and optional AI-generated quizzes.

Next.js React TypeScript Prisma Docker Donate on Ko-fi

--- ## Table of Contents - [Why OfflineAcademy?](#why-offlineacademy) - [Feature Highlights](#feature-highlights) - [Screenshots](#screenshots) - [Tech Stack](#tech-stack) - [Quick Start: Docker Hub](#quick-start-docker-hub) - [Other Deployment Options](#other-deployment-options) - [CI/CD Automation](#cicd-automation) - [Configuration](#configuration) - [Course Folder Structure](#course-folder-structure) - [Data & Persistence](#data--persistence) - [Updating](#updating) - [Backup](#backup) - [Security Notes](#security-notes) - [Support the Project](#support-the-project) - [License](#license) - [Changelog](#changelog) --- ## Why OfflineAcademy? Most online course platforms assume cloud storage, user accounts, subscriptions, and constant internet access. OfflineAcademy is built for a different workflow: - You already have course videos stored locally. - You want a clean interface to browse, watch, and resume lessons. - You want progress tracking without uploading your learning data anywhere. - You want a private LAN app that works from your desktop, laptop, tablet, or phone. - You want optional quiz practice without turning the app into a cloud product. OfflineAcademy is designed for **single-user local/LAN deployments**: home servers, NAS boxes, mini PCs, Docker hosts, homelabs, and personal workstations. --- ## Feature Highlights ### Course Library - Scan a local course directory and build a browsable library. - Display course cards with progress, metadata, tags, categories, and quick actions. - Pin/favorite important courses. - Rename courses from the UI. - **Delete from library** to hide/archive without deleting files. - **Delete from disk** when you intentionally want to remove course files. - Unified action menus across dashboard and course pages. ### Video Learning Experience - Browser-based playback for local video files. - Resume playback from the last watched position. - Track lesson progress automatically. - Continue watching card for the most recent lesson. - Mobile-friendly controls and responsive layout. - Playback speed controls. - Fullscreen and picture-in-picture support where supported by the browser. - Keyboard shortcuts overlay. ### Bookmarks & Learning Notes - Bookmark lessons for later review. - Store bookmark notes alongside lessons. - Revisit saved moments without hunting through folders manually. ### Course Organization - Add custom tags to courses. - Add categories to courses. - Filter and search the course library. - Keep all organization metadata local to the app/database. ### AI Quiz Practice - Generate module-level practice quizzes. - Supports QuizAPI and The Trivia API. - Per-video quiz topic overrides. - Clear or regenerate quizzes from course actions. - Smart skip logic for low-value quiz targets: - introductions - footnotes - appendices - bonus lectures - modules without useful quiz topics - Graceful fallback handling when providers rate-limit or lack matching categories. ### Offline & Local-First Design - No accounts. - No user profiles. - No required cloud storage. - Local SQLite database via Prisma. - Offline-capable PWA behavior through service worker caching. - Machine-scoped settings from the app UI. ### Deployment-Friendly - Docker Hub image for the fastest install. - Docker Compose support for repeatable local/LAN deployment. - Local Node.js deployment for development or customization. - Portable folder mounts for courses and database. - Suitable for trusted LAN/homelab environments. --- ## Screenshots ### Dashboard Browse courses, continue the most recent lesson, filter your library, and see learning stats at a glance. ![OfflineAcademy dashboard](docs/screenshots/dashboard.png) ### Course Page View course progress, modules, lessons, and quick actions from a focused course detail page. ![OfflineAcademy course page](docs/screenshots/course-page.png) ### Watch Page Watch lessons with progress tracking, course navigation, and bookmark notes in one place. ![OfflineAcademy watch page](docs/screenshots/watch-page.png) ### Settings Configure the course directory, quiz provider, auto-fetch behavior, and API key from the local settings page. ![OfflineAcademy settings page](docs/screenshots/settings.png) --- ## Tech Stack ```text Frontend Next.js 14, React 18, TypeScript Styling Tailwind CSS, Radix UI Database SQLite ORM Prisma Runtime Node.js Deploy Docker Hub / Docker Compose recommended; local Node.js supported ``` --- ## Quick Start: Docker Hub Docker is the recommended way to run OfflineAcademy. It avoids local Node.js setup, uses the published image, and keeps your courses and database safely mounted outside the container. ### Prerequisites - Docker - Docker Compose - A folder containing your course videos ### 1. Create an app folder ```bash mkdir -p offlineacademy/My_Courses offlineacademy/prisma_data cd offlineacademy ``` ### 2. Download the Docker Compose file and environment template ```bash curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/nicetry247/offlineacademy/main/docker-compose.hub.yml curl -fsSL -o .env https://raw.githubusercontent.com/nicetry247/offlineacademy/main/.env.example ``` ### 3. Start OfflineAcademy ```bash docker compose up -d ``` Open: ```text http://localhost:6969 ``` For another device on your LAN, replace `localhost` with your server IP: ```text http://YOUR_SERVER_IP:6969 ``` ### Docker Hub Image OfflineAcademy is published on Docker Hub: ```text https://hub.docker.com/r/nicetry247/offlineacademy ``` Available tags: ```text nicetry247/offlineacademy:latest nicetry247/offlineacademy:1.0.0 ``` Direct pull: ```bash docker pull nicetry247/offlineacademy:latest ``` --- ## Other Deployment Options ### Build locally from source with Docker Use this if you want to modify the app and build your own image locally. ```bash git clone https://github.com/nicetry247/offlineacademy.git cd offlineacademy cp .env.example .env mkdir -p My_Courses prisma_data docker compose up --build -d ``` The source-build Compose file uses the same runtime layout as the Docker Hub deployment: ```yaml ports: - "6969:6767" environment: DATABASE_URL: file:/app/prisma/data/dev.db COURSES_ROOT: /app/My_Courses NEXT_PUBLIC_APP_URL: http://localhost:6969 volumes: - ./My_Courses:/app/My_Courses - ./prisma_data:/app/prisma/data ``` The `prisma_data` directory safely persists the SQLite database. The container creates and manages `dev.db` inside `/app/prisma/data`, so users do not need to pre-create an empty database file. ### Local Node.js development Use this for development, debugging, or source-level customization. #### Prerequisites - Node.js 18+ recommended - npm - A folder containing your course videos #### 1. Clone the repository ```bash git clone https://github.com/nicetry247/offlineacademy.git cd offlineacademy ``` #### 2. Create your environment file ```bash cp .env.example .env ``` Edit `.env` if needed: ```env DATABASE_URL="file:/app/prisma/data/dev.db" COURSES_ROOT="/app/My_Courses" NEXT_PUBLIC_APP_URL="http://localhost:6969" QUIZAPI_KEY="" ``` For non-Docker local Node.js development, use paths that exist on your local machine, such as `DATABASE_URL="file:./prisma/dev.db"` and `COURSES_ROOT="./My_Courses"`. #### 3. Install dependencies ```bash npm ci ``` #### 4. Prepare the database ```bash npx prisma generate npx prisma db push ``` #### 5. Add courses ```bash mkdir -p My_Courses ``` Place your course folders inside `My_Courses`. #### 6. Build and run ```bash npm run build npx next start -p 6969 ``` ### Custom Course Folder, NAS, or USB Drive Courses should live outside the Docker image. Docker maps a host folder into the container using this pattern: ```text HOST_PATH:CONTAINER_PATH ``` The container path should stay: ```text /app/My_Courses ``` For the default local folder, use: ```yaml volumes: - ./My_Courses:/app/My_Courses - ./prisma_data:/app/prisma/data ``` For a NAS, USB datastore, or other external drive, change only the left side of the course mount: ```yaml volumes: - /path/to/your/courses:/app/My_Courses - ./prisma_data:/app/prisma/data ``` More examples: ```yaml # Linux - /mnt/media/courses:/app/My_Courses # macOS - /Users/you/Videos/Courses:/app/My_Courses # Windows with Docker Desktop - C:/Users/you/Videos/Courses:/app/My_Courses ``` Keep the app's internal course setting at the container path: ```text /app/My_Courses ``` This lets Docker handle the host-to-container translation while OfflineAcademy always scans the same internal path. ### Publishing to Docker Hub Maintainers can publish manually if needed: ```bash docker login docker build --no-cache -t nicetry247/offlineacademy:latest . docker tag nicetry247/offlineacademy:latest nicetry247/offlineacademy:1.0.0 docker push nicetry247/offlineacademy:latest docker push nicetry247/offlineacademy:1.0.0 ``` --- ## CI/CD Automation OfflineAcademy includes a GitHub Actions workflow for automated Docker publishing: ```text .github/workflows/docker-build.yml ``` When changes are pushed to the `main` branch, the workflow builds the standalone Next.js Docker image and pushes it directly to Docker Hub: ```text nicetry247/offlineacademy ``` Version tags are supported too. Cutting a tag such as: ```text v1.0.0 ``` publishes the matching Docker image tag alongside `latest`. This keeps the public Docker Hub image aligned with the source repository without requiring manual release builds. --- ## Configuration | Variable | Required | Purpose | Example | |---|---:|---|---| | `DATABASE_URL` | Yes | SQLite database path inside the container | `file:/app/prisma/data/dev.db` | | `COURSES_ROOT` | Yes | Course folder path inside the container | `/app/My_Courses` | | `NEXT_PUBLIC_APP_URL` | Recommended | Public app URL used by metadata and links | `http://localhost:6969` | | `QUIZAPI_KEY` | Optional | QuizAPI key for quiz generation | `qa_...` | Settings can also be managed inside the app at: ```text /settings ``` --- ## Course Folder Structure OfflineAcademy works best when courses are organized as folders containing module folders and video files. ```text My_Courses/ └── Example Course/ ├── 01 - Introduction/ │ └── 01 - Welcome.mp4 ├── 02 - Core Concepts/ │ ├── 01 - Lesson One.mp4 │ ├── 01 - Lesson One.en.vtt │ ├── 01 - Lesson One.es.vtt │ ├── 01 - Lesson One.ja.vtt │ └── 02 - Lesson Two.mp4 └── 03 - Practice/ └── 01 - Lab Walkthrough.mp4 ``` Supported video extensions include common browser-playable formats such as `.mp4`, plus additional local media formats depending on browser support. ### Subtitles OfflineAcademy supports subtitle files stored beside the matching video. `.vtt` files are served directly; `.srt` files are detected and converted to browser-compatible WebVTT when served to the video player. Single subtitle track: ```text 01 - Lesson.mp4 01 - Lesson.vtt ``` Multiple language tracks: ```text 01 - Lesson.mp4 01 - Lesson.en.vtt 01 - Lesson.es.vtt 01 - Lesson.ja.vtt 01 - Lesson.fil.vtt ``` Language suffixes become selectable tracks in the video player settings menu. Examples: `en`, `es`, `fr`, `ja`, `fil`, `zh-CN`, `pt-BR`. --- ## Data & Persistence OfflineAcademy stores app data locally. ```text SQLite database prisma_data/dev.db Course files My_Courses/ or your configured host course mount Environment .env Quiz cache Course/module folders when generated ``` Important user data: - watch progress - bookmarks - bookmark notes - course metadata - tags and categories - quiz records/cache - local app settings --- ## Updating ### Local Node.js ```bash git pull npm ci npx prisma generate npx prisma db push npm run build npx next start -p 6969 ``` ### Docker ```bash git pull docker compose up --build -d ``` --- ## Backup Back up these items regularly: ```text prisma_data/dev.db Main SQLite database .env Local configuration and optional API key My_Courses/ Your source course library, if not backed up elsewhere ``` A simple backup can be as easy as copying `prisma_data/dev.db` somewhere safe before updates. --- ## Security Notes OfflineAcademy is designed for trusted local networks and personal/homelab use. Before exposing it to the public internet, add: - HTTPS - reverse proxy authentication - network access controls - regular database backups - careful handling of `.env` and API keys Do not commit `.env`, local databases, course files, or private API keys. --- ## Support the Project

Support OfflineAcademy on Ko-fi

If OfflineAcademy saves you time, feeds your homelab brain, or keeps your course chaos under control, consider fueling the next round.
Support OfflineAcademy on Ko-fi →

--- ## License OfflineAcademy is released under the **MIT License**. You are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, subject to the terms of the MIT License. See [`LICENSE`](LICENSE) for the full license text. --- ## Changelog ### v1.0.2 — Subtitle & Analytics Polish This release focuses on making OfflineAcademy smoother for real course libraries, especially Udemy-style folders with external subtitle files. #### Added - Multi-language subtitle support for local course videos. - SRT and VTT subtitle detection during course scans. - Visible subtitle controls in the video player when tracks are available. - Support for multiple subtitle tracks per lesson, such as English and Persian. #### Fixed - Same-folder subtitle imports for files stored outside the app container and mounted into the course library. - Subtitle filename matching for common Udemy-style exports, including patterns like `-english-test.us.srt` and `-persian-test.us.srt`. - Lesson API subtitle ordering so tracks load consistently in the player. - Analytics page data loading by reading analytics directly on the server instead of relying on a fragile internal fetch. #### Improved - Course scanning now better handles real-world course folder layouts. - Subtitle files are served as browser-compatible `text/vtt`, including converted SRT files. - Local development workflow was verified on port `6969` after cache cleanup and rebuild.