diff --git a/README.md b/README.md index bfba264..fe1bea7 100644 --- a/README.md +++ b/README.md @@ -7,65 +7,186 @@

Go SQLite + Bun Tailwind HTMX License

-MyAnimeList is a small self-hosted anime tracker and playback app. It keeps the catalog, watchlist, -progress tracking, and player in one place, backed by a single SQLite database and a single Go -server. +MyAnimeList is a self-hosted anime tracker, catalog browser, watchlist, and playback app built as a +portfolio-grade full-stack project. It brings the parts of an anime discovery workflow that usually +live across several services into one local application: browsing titles, saving a watchlist, +tracking progress, continuing episodes, reading metadata, finding recommendations, and watching +through a browser player backed by provider integrations. -Most of the UI is rendered on the server. HTMX handles lightweight updates like pagination and -watchlist changes, while TypeScript is kept for the parts that need real browser state: the video -player, search page, theme handling, and skip segment editor. The app also includes local users, API -tokens, subtitle support, playlist rewriting, provider integrations, migrations, and startup data -fixes. +The project is intentionally built like a small real-world product instead of a throwaway demo. The +backend is a Go application with feature-oriented modules, SQLite persistence, schema migrations, +startup data fixes, provider clients, and tests around the parts that are easy to regress. The UI is +mostly server-rendered with Go templates and enhanced with HTMX where partial updates make sense. +TypeScript is reserved for browser-heavy behavior such as the video player, search interactions, +theme handling, progress updates, subtitles, skip segments, and carousel controls. -## Running +## Why This Exists -Requires [`mise`](https://mise.jdx.dev/), a C compiler for SQLite, and the tools managed in -`.mise.toml`. +I made this project to explore what a focused, personal media app can look like when it is treated +with the same care as a production product. Anime tracking is a good problem space because it touches +many practical engineering concerns without needing a large team or cloud platform: + +- local authentication and user-owned data +- catalog search, browse filters, details pages, and recommendations +- long-lived state such as watchlists, playback progress, and continue-watching rows +- server-rendered pages with small, deliberate frontend islands +- integration boundaries around external metadata and playback providers +- background refresh policies, cache behavior, migrations, and data repair jobs +- observability, request context, error handling, and testable service boundaries + +The goal is not to replace a commercial anime platform. The goal is to show how I think about product +engineering: keeping the architecture understandable, making features feel cohesive, choosing boring +tools where possible, and building enough operational structure that the app feels like it could keep +growing. + +## What It Can Do + +- Browse and search anime using external catalog data. +- View detail pages with metadata, synopsis, reviews, characters, statistics, related titles, themes, + and watch-order information. +- Maintain a local watchlist with status-oriented user state. +- Continue watching from stored playback progress. +- Play episodes through an HLS-capable web player. +- Rewrite playlists and proxy playback/subtitle requests through the server. +- Track progress, completion, episode navigation, quality, keyboard controls, and player state in + TypeScript. +- Support subtitles, subtitle caching, and VTT parsing. +- Manage skip segments and local skip-segment overrides. +- Generate personalized top picks from watchlist taste signals and recommendation data. +- Run SQLite migrations and one-off data fixes as the data model evolves. +- Create local users and run maintenance commands from `cmd/user`. + +## Architecture + +The repository is organized around product features and integration boundaries: + +| Path | Purpose | +| --- | --- | +| `cmd/server` | Application entry point for the web server. | +| `cmd/user` | Local admin and maintenance commands. | +| `internal/anime` | Catalog, details, browse, search, reviews, and recommendations wiring. | +| `internal/auth` | Local authentication, middleware, and user session behavior. | +| `internal/watchlist` | Watchlist handlers, service logic, and persistence access. | +| `internal/playback` | Playback state, progress, proxy tokens, skip segments, and watch data. | +| `internal/episodes` | Episode refresh and provider mapping logic. | +| `internal/database` | SQLite setup, migrations, and startup data fixes. | +| `internal/db` | Generated and helper database access code. | +| `integrations/jikan` | Jikan API client, rate limiting, query helpers, and catalog types. | +| `integrations/playback/allanime` | Playback provider client and extraction logic. | +| `templates` | Server-rendered pages and reusable Go template components. | +| `static` | TypeScript source for client-side interactions and player behavior. | +| `scripts` | Bun-powered development and maintenance scripts. | + +The application uses Go for the long-running server, domain services, provider clients, and data +access. SQLite keeps the app simple to run locally while still supporting real schema evolution +through migrations. Bun is used for the frontend toolchain, TypeScript checks, formatting, linting, +and small developer scripts. `mise` pins the tool versions so a fresh checkout can get to a working +environment quickly. + +## Technology Choices + +- **Go** keeps the backend fast, explicit, and easy to ship as a single binary. +- **Gin** provides HTTP routing and middleware without hiding the request lifecycle. +- **Uber Fx** wires modules together in a way that keeps feature packages independent. +- **SQLite** makes the project self-contained and easy to run without external infrastructure. +- **Goose** manages migrations as the schema changes. +- **Go templates** keep most UI rendering close to the server data model. +- **HTMX** adds small partial updates without turning the app into a full SPA. +- **TypeScript** handles the browser behavior that benefits from strong client-side structure. +- **Tailwind CSS** provides a compact styling workflow. +- **Bun** runs the frontend build, lint, format, and script tasks. +- **mise** pins local tool versions for reproducible development. + +## Developer Experience + +This project is set up around a small number of repeatable commands. The main workflow is: ```bash mise install bun install -just build +just dev +``` + +`just dev` runs the app through Air, which rebuilds the Go server and frontend assets when relevant +files change. The default server address is `http://localhost:3000`. + +Configuration is read from environment variables, and a local `.env` file is loaded automatically. +Useful variables include: + +| Variable | Default | Purpose | +| --- | --- | --- | +| `PORT` | `3000` | HTTP port for the local server. | +| `DATABASE_FILE` | `mal.db` | SQLite database path. | +| `GIN_MODE` | release default | Gin runtime mode. | +| `MAL_CORS_ALLOW_ALL` | disabled | Allows any origin when set to `1`; useful only for local or proxy setups. | +| `PLAYBACK_PROXY_SECRET` | empty | Enables signed playback proxy tokens when set. | +| `EPISODE_AVAILABILITY_MODE` | `auto` | Episode availability strategy: `auto`, `legacy`, or `jikan`. | +| `MAL_JIKAN_TRACE` | disabled | Enables optional Jikan client tracing when truthy. | + +## Common Commands + +The project uses `just` as the command runner: + +| Command | Description | +| --- | --- | +| `just setup` | Install pinned tools with `mise` and install Bun dependencies. | +| `just dev` | Start the local development server with live rebuilds. | +| `just build` | Build the Go server, Tailwind CSS, and TypeScript assets. | +| `just test` | Run the Go test suite. | +| `just fmt` | Format Go code. | +| `bun run format` | Format TypeScript and related frontend files with `oxfmt`. | +| `just lint-go` | Run `golangci-lint` across Go packages. | +| `just lint-ts` | Run type-aware `oxlint` against the TypeScript source. | +| `just typecheck` | Run `tsc` without emitting files. | +| `just check` | Run linting, tests, typechecking, and a full build. | +| `just run` | Build and run the compiled server binary. | +| `just clean` | Remove generated build output. | +| `just new-data-fix name` | Scaffold a new data-fix file. | +| `just run-fixes` | Run registered data fixes through the user command. | +| `just fix-all` | Run the Bun maintenance script for data fixes. | + +To create a local user after setup: + +```bash go run ./cmd/user -just dev ``` -The app starts on `http://localhost:3000` by default. Configuration comes from environment -variables, and a local `.env` file is loaded automatically. The most useful options are `PORT`, -`DATABASE_FILE`, `PLAYBACK_PROXY_SECRET`, `EPISODE_AVAILABILITY_MODE`, and -`ANIMESCHEDULE_API_TOKEN`. +## Quality Bar -## Development +The codebase aims for a practical production style: -The codebase is split between Go feature packages, external integrations, server-rendered templates, -and a small frontend asset pipeline. `cmd/server` starts the web app, `cmd/user` contains local -admin tools, `internal` holds the application modules, `integrations` holds provider clients, and -`templates`, `static`, and `dist` contain the UI. +- feature packages own their handlers, services, repositories, and module wiring; +- external provider behavior is isolated in `integrations`; +- migrations are versioned and committed with the application code; +- template components use named props instead of implicit positional state; +- tests focus on database helpers, provider behavior, rendering helpers, recommendations, playback, + observability, and request handling; +- linting and typechecking are part of the normal local workflow; +- maintenance scripts are kept in the repo instead of living as undocumented one-off commands. -The common development commands are in the `justfile`. +## Project Status -```bash -just setup -just dev -just fmt -just test -just lint-go -just lint-ts -just typecheck -just build -``` +This is a personal portfolio project and local-first application. It is still evolving, but the repo +is structured to make future work straightforward: add migrations when the data model changes, keep +provider-specific logic behind integration packages, prefer server-rendered flows by default, and use +TypeScript only where browser state is genuinely doing work. -Run the full local check with: +## Community And Security -```bash -just check -``` +Please read the project docs before contributing or using code from this repository: + +- [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) describes the expected standard for respectful + participation. +- [`SECURITY.md`](SECURITY.md) explains how to report security issues and how this project treats + sensitive playback, auth, and local data concerns. +- [`LICENSE`](LICENSE) contains the MIT license for this project. ## License -MIT. See [`LICENSE`](LICENSE). +This project is released under the MIT License. See [`LICENSE`](LICENSE) for the full text.