8.4 KiB
MyAnimeList
A local-first anime catalog, watchlist, recommendation, and playback app.
MyAnimeList is a self-hosted media app for browsing anime, managing a watchlist, resuming episodes, and playing streams through a browser-based player. It collects the parts of an anime workflow that usually live across several products and keeps them in one small Go application backed by SQLite.
I built it as a portfolio project, but the goal was never to make a disposable demo. The interesting part of the project is the product shape: server-rendered pages, a local database, provider integrations, playback proxying, recommendations, migrations, tests, and a TypeScript player that only appears where browser state actually earns its place.
Note
This is a personal, local-first project. It is written to demonstrate product engineering choices, not to present itself as an official MyAnimeList client or a hosted streaming platform.
Contents
What This Project Is
This project started from a simple idea: anime tracking becomes more interesting when catalog data, personal progress, and playback live in the same interface. A user should be able to discover a title, inspect its metadata, add it to a watchlist, watch an episode, come back later, and continue from the right place without stitching that flow together manually.
That makes the app a useful playground for real application concerns. It has authentication, long-lived user state, external APIs, background refresh behavior, migrations, data fixes, cache boundaries, provider-specific code, and enough frontend complexity to justify TypeScript without turning the whole product into a single-page app.
The project is also intentionally modest. It uses a single Go server and a SQLite database because those choices make the system easy to run, inspect, and reason about. The architecture is more about clear ownership than novelty: feature packages own their handlers and services, integrations stay at the edges, and the UI is mostly rendered by the server.
What It Includes
| Area | What it does |
|---|---|
| Catalog | Browse, search, and inspect anime metadata from external catalog sources. |
| Details | Render synopsis, reviews, characters, statistics, relations, themes, and watch-order data. |
| Watchlist | Store local user state for saved titles, statuses, and progress-driven flows. |
| Playback | Serve watch pages, proxy streams/subtitles, rewrite playlists, and track progress. |
| Player | Handle HLS playback, quality selection, subtitles, keyboard controls, episode navigation, and skip segments. |
| Recommendations | Generate personal top picks from watchlist signals and recommendation data. |
| Maintenance | Run migrations, startup fixes, local user commands, and data repair scripts. |
Implementation notes
The backend is written in Go with Gin for HTTP routing and Fx for module wiring. SQLite is used for local persistence, with migrations and data fixes committed alongside the application. Templates are rendered on the server, HTMX handles small partial updates, and TypeScript powers the interactive parts of the browser experience.
The most stateful frontend code lives under static/player, where the app handles playback mode,
source loading, progress storage, subtitles, timelines, quality changes, keyboard shortcuts, skip
segments, episode completion, and thumbnail navigation.
How It Is Built
The application is organized around product boundaries rather than framework layers. internal/anime
owns catalog-facing behavior, internal/watchlist owns saved user state, internal/playback owns
watch data and proxy behavior, and integrations contains provider clients. This keeps the core app
from depending directly on the details of a specific metadata or playback source.
Server-rendered templates are the default because most pages are content-heavy and benefit from simple request-response rendering. TypeScript is used where the browser has real ongoing state: search interactions, theme handling, carousels, watchlist actions, toast messages, and especially the video player.
The result is a codebase that behaves like a small product rather than a tutorial project: it has a repeatable toolchain, database evolution, local maintenance commands, focused tests, and a clear split between app code and external integrations.
Working Locally
The local workflow assumes mise for tool versions and just for common
commands.
mise install
bun install
just dev
The development server runs on http://localhost:3000 by default. just dev uses Air to rebuild the
Go server and frontend assets when relevant files change.
Create a local user with:
go run ./cmd/user <username> <password>
Commands
| Command | Use it for |
|---|---|
just setup |
Install pinned tools and Bun dependencies. |
just dev |
Run the app locally with live rebuilds. |
just build |
Build the Go binary, CSS, and TypeScript assets. |
just test |
Run the Go test suite. |
just check |
Run linting, tests, typechecking, and a full build. |
just lint-go / just lint-ts |
Run backend or frontend linting separately. |
just typecheck |
Run TypeScript without emitting files. |
just run |
Build and run the compiled server. |
just clean |
Remove generated build output. |
Configuration
Configuration is loaded from environment variables, and a local .env file is read automatically.
| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
HTTP port for the 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; intended for local/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. |
Maintenance commands
| Command | Use it for |
|---|---|
just new-data-fix name |
Scaffold a new data-fix file. |
just run-fixes |
Run registered data fixes through cmd/user. |
just fix-all |
Run the Bun maintenance script for data fixes. |
bun run format |
Format TypeScript and related frontend files with oxfmt. |
Repository Map
| Path | Responsibility |
|---|---|
cmd/server |
Web server entry point. |
cmd/user |
Local user and maintenance commands. |
internal/anime |
Catalog, details, browse, search, reviews, and recommendations. |
internal/auth |
Authentication, middleware, and local user handling. |
internal/watchlist |
Watchlist handlers, service logic, and persistence. |
internal/playback |
Watch data, progress, proxy tokens, and skip segments. |
internal/episodes |
Episode refresh and provider mapping. |
internal/database |
SQLite setup, migrations, and startup data fixes. |
integrations/jikan |
Jikan API client and catalog types. |
integrations/playback/allanime |
Playback provider client and extraction logic. |
templates |
Server-rendered pages and reusable components. |
static |
TypeScript source for client-side behavior. |
scripts |
Bun-powered development and maintenance scripts. |
Released under the MIT License.
