A cross-platform desktop app for discovering, streaming and downloading anime β with an embedded mpv player and a plugin system for sources.
English Β· Π ΡΡΡΠΊΠΈΠΉ
β¬ Download Β Β·Β Features Β Β·Β Quick start Β Β·Β Architecture Β Β·Β Documentation
Anisync is a desktop anime browser and player for Windows, macOS and Linux. Search a source site, open a title, pick a dub and play episodes in the built-in libmpv player β or queue them for offline viewing β while a local library keeps your lists, history and resume positions. It has an Apple TV / Crunchyroll-inspired dark UI, and every source site is a small plugin: today that is the YummyAnime provider with the Kodik player resolver.
| π¬ In-app playback | Embedded libmpv rendered into a Qt OpenGL surface: HLS/DASH, hardware decoding, custom headers, every codec. No external player needed. Honours autoplay next episode and your preferred dub. |
| π Home | Hero banner plus Continue Watching (from your local history) and Trending Now carousels; providers are queried in parallel and rows are cached for 10 minutes. |
| π Live search | Debounced as-you-type results across providers, provider filter chips, Ctrl+F / Cmd+F from anywhere. |
| π Details & dubs | Episode tiles grouped by dub track, with play and save actions per episode. |
| π₯ Download manager | Persistent queue (survives restarts), per-episode and per-anime progress, speed display, cancel/retry, configurable concurrency and quality. Powered by yt-dlp. |
| π Library | Favorites, Watching / Planning / Completed / Dropped lists and full watch history with resume positions, stored locally in SQLite. |
| π Auto-updates | Checks GitHub Releases on startup, shows the changelog of every version you missed, downloads with live progress and installs in place. Full release history in Settings β About β What's new. |
| π§© Modular by design | Any anime site is a provider plugin, any embed player is a resolver plugin. Adding a source touches zero core code. |
| π₯ Truly cross-platform | One codebase, native-feeling on Windows, macOS and Linux (X11/Wayland). Icons are drawn with QPainter, so the UI never depends on platform fonts. |
| π Cinematic dark UI | Frameless window, poster-first cards, smooth page transitions and hover animations, a single orange accent. |
Grab the latest build from Releases β fully self-contained (libmpv included), nothing else to install.
| Platform | File | Notes |
|---|---|---|
| Windows | Anisync-X.Y.Z-win.zip |
Unzip anywhere, run Anisync.exe. SmartScreen: More info β Run anyway (unsigned build). |
| macOS | Anisync-X.Y.Z-mac.dmg |
Drag to Applications. First launch: right-click β Open (ad-hoc signed). |
| Linux | Anisync-X.Y.Z.AppImage |
chmod +x Anisync-*.AppImage && ./Anisync-*.AppImage |
Once installed, Anisync keeps itself up to date β when a new version is published you'll get a prompt with the full changelog and a one-click Install update.
# Python 3.11+
git clone https://github.com/DenisHumen/Anisync.git
cd Anisync
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
python -m anisync # launch the GUI (or just: anisync)
pytest -q # run the test suite (offline)For in-app playback a native libmpv is required in dev runs (packaged builds bundle it):
| OS | How |
|---|---|
| macOS | brew install mpv |
| Debian/Ubuntu | sudo apt install libmpv2 (or libmpv1) |
| Windows | put libmpv-2.dll (mpv-dev builds) on PATH |
python -m anisync --selfcheck boots the plugin registries without opening the GUI and reports the version, config, loaded providers/players and whether libmpv was found.
Most options are in Settings; they are stored in config.toml inside the app data folder.
| Key | Default | Description |
|---|---|---|
downloads_dir |
<Videos>/Anisync |
Where downloaded episodes are saved |
max_concurrent_downloads |
2 |
Parallel downloads |
preferred_quality |
best |
best, 2160p, 1440p, 1080p, 720p, 480p or 360p |
preferred_dub |
(empty) | Dub to start with (case-insensitive substring match); empty = first available |
autoplay_next |
true |
Play the next episode automatically |
check_updates_on_start |
true |
Check GitHub Releases on launch |
theme |
dark |
UI theme |
update_repo |
DenisHumen/Anisync |
Repository the updater checks |
auth_backend_url |
(empty) | Account backend URL β empty keeps the app in offline mode (see docs/AUTH.md) |
App data folder (resolved with platformdirs) β holds config.toml, the SQLite library.db, cache/ and account.json:
| OS | Location |
|---|---|
| Windows | %LOCALAPPDATA%\Anisync |
| macOS | ~/Library/Application Support/Anisync |
| Linux | ~/.local/share/Anisync |
| Environment variable | Effect |
|---|---|
ANISYNC_DATA_DIR |
Use this folder as the app data folder instead |
ANISYNC_TEST_MODE |
Use a throwaway temp folder (used by the test suite) |
The top navigation has Home, Search, Library, History, Downloads, Account and Settings. Opening a title shows its details page with episodes grouped by dub; from there you can play an episode, add the title to a list, or save episodes to the download queue. The Account page is a scaffold for future cloud accounts β this build runs in offline mode and keeps everything local.
| Key | Action |
|---|---|
Space |
Play / pause |
β / β |
Seek β10 s / +10 s |
Shift+β / Shift+β |
Seek β60 s / +60 s |
β / β |
Volume |
M |
Mute |
F |
Toggle fullscreen |
Esc |
Exit fullscreen |
Ctrl+F / Cmd+F |
Jump to search |
Stack: Python 3.11+ Β· PySide6 (Qt 6) Β· python-mpv / libmpv Β· httpx Β· selectolax Β· yt-dlp Β· platformdirs Β· SQLite Β· PyInstaller (packaging).
Three rules hold everything together:
- Plain dataclasses cross layers (
core/models.py) β no Qt or httpx objects leak between UI, providers and the downloader. - Plugins self-register β dropping a file into
providers/orplayers/is the whole integration (@register_provider/@register_player). - The UI never blocks β all I/O runs on a persistent asyncio loop in a worker thread (
utils/async_runner.run_async), results come back through Qt signals.
# anisync/providers/mysource.py
from anisync.core.registry import register_provider
from anisync.providers.base import BaseProvider
@register_provider
class MySourceProvider(BaseProvider):
id = "mysource"
display_name = "MySource"
base_url = "https://example.com"
async def search(self, query, *, limit=20): ...
async def get_anime(self, anime_url): ...
async def list_episodes(self, anime): ...That's it β search, details, playback and downloads pick it up automatically. See docs/PROVIDERS.md and docs/PLAYERS.md for the full contracts, and docs/CONTRIBUTING.md for the 5-step checklist (import, fixtures, tests).
- On startup (and on demand from Settings β About) Anisync queries the GitHub Releases API.
- If a newer version exists you get a dialog with the combined release notes of every version you missed β dates and notes come straight from the release descriptions on GitHub.
- Install update downloads the platform asset with live progress, then a tiny helper swaps the build and relaunches the app. Dev runs get an Open installer fallback instead.
- Releases are produced by CI on every
v*tag β per-version notes live in.github/releases/. See docs/UPDATER.md and docs/RELEASES.md.
anisync/
βββ core/ # models, plugin registries, SQLite library, config,
β # download manager, auth scaffold, GitHub updater + self-update
βββ providers/ # site scrapers β one module per source (yummyanime, β¦)
βββ players/ # embed resolvers β one module per player (kodik, β¦)
βββ ui/ # PySide6 pages, widgets, dialogs, QPainter icons, theme
βββ utils/ # async runner, http client factory, paths, mpv loader
tests/ # pytest suite β fully offline by default (fixtures in tests/fixtures/)
packaging/ # PyInstaller spec, app icons, logo generator
docs/ # design docs for humans & agents
| Document | Purpose |
|---|---|
| docs/ARCHITECTURE.md | High-level design, layers, data flow |
| docs/PROVIDERS.md | How to add a new anime source |
| docs/PLAYERS.md | How to add a new embed resolver |
| docs/DOWNLOADER.md | Download manager design |
| docs/UI.md | UI structure, theming, cross-platform rules |
| docs/AUTH.md | Account system (offline mode today) |
| docs/UPDATER.md | Auto-update system |
| docs/RELEASES.md | Release engineering / CI pipeline |
| docs/ROADMAP.md | Phased plan and progress |
| docs/CONTRIBUTING.md | Developer / agent workflow |
pip install -e ".[dev]"
pytest -q # offline suite
pytest -m live # live network tests (optional)
python -m compileall anisync # quick syntax gateGround rules (the longer list lives in docs/CONTRIBUTING.md):
- style via theme tokens β no hardcoded colors in widgets;
- icons are QPainter-drawn (
ui/widgets/icons.py) β never unicode glyphs/emoji, they break on Linux fonts; - every page must stay responsive from 1024 px up;
- all I/O through
run_asyncβ never block the Qt event loop.
Releasing: bump the version in anisync/__init__.py and pyproject.toml, write the notes to .github/releases/vX.Y.Z.md, then push a vX.Y.Z tag. CI builds the PyInstaller bundles (packaging/Anisync.spec) on macOS, Windows and Linux, smoke-tests each with --selfcheck, and attaches the DMG, zip and AppImage to the release. Details in docs/RELEASES.md.
Open items from docs/ROADMAP.md:
- Subtitle picker
- More providers (Anilibria, Animevost) and player resolvers (Aniboom, Sibnet)
- Pre-download the next episode automatically
- A real registration backend for accounts
- Stretch: airing schedule view, Discord rich presence, plugin store / hot-reload providers
Issues and pull requests are welcome. New sources belong in anisync/providers/ and new embed hosts in anisync/players/ β never in the UI. Tests must stay offline (fixtures + httpx.MockTransport), and docs are updated in the same change. See docs/CONTRIBUTING.md.
MIT. Anisync is a personal/educational project: it ships no content and hosts nothing β it only embeds what source sites publicly serve. Respect the Terms of Service of the sources you use and support official releases where available.