Skip to content

Repository files navigation

AniAutoWatchList

AniAutoWatchList adds a local watchlist, episode tracker, and desktop GUI around ani-cli.

It keeps ani-cli's normal playback path intact. The patched ani-cli script only adds hook calls so the Python app can record launches, selected titles, listed episodes, playback starts, and playback finishes.

Safety

This project does not bypass DRM, paywalls, or login systems, and does not use EverythingMoe or link-list sites as playback resolvers. Playback resolves against the same class of public source ani-cli already targets: anidb.app first, with hianime.at as a fallback when anidb.app is unavailable. AniList is used only for metadata and cover art.

Features

  • Local SQLite watchlist
  • Dark Tkinter desktop GUI
  • Trending, top airing, most popular, and release schedule tabs powered by AniList metadata
  • AniList community ratings shown on a five-star scale across discovery, watchlist, and anime-detail views
  • Status tabs: Watching, Completed, Dropped, On Hold, Plan to Watch
  • Episode watched/unwatched tracking
  • Continue button in the GUI for launching a selected episode through ani-cli
  • Automatic playback failover from anidb.app to hianime.at when the primary provider is down
  • Automatic repair of image-wrapped HLS segments that mpv, VLC, and ffmpeg otherwise refuse to play
  • Startup update check against the GitHub main branch
  • Global AniList title search with suggestions and card results
  • Related seasons on anime detail pages
  • Automatic released episode refresh from AllAnime when titles are added or opened
  • Automatic duplicate cleanup for matching AniList titles and season-title aliases
  • AniList metadata search and cover caching
  • English-first display titles when metadata is available
  • Notes per anime
  • JSON, XML, and CSV export
  • Automatic JSON and XML watchlist backups whenever the GUI closes
  • SQLite backups and restore
  • Duplicate detection and merge command
  • Activity log and repair checks
  • User-level install that does not overwrite system ani-cli

Install

Linux Mint/Ubuntu dependencies:

sudo apt install git python3 python3-venv python3-tk curl fzf mpv openssl

Clone and install:

git clone https://github.com/GenshinPapi/AniAutoWatchList.git
cd AniAutoWatchList
scripts/install-user.sh
export PATH="$HOME/.local/bin:$PATH"
hash -r
ani-watch doctor

The installer creates a virtual environment at:

~/.local/share/ani-watchlist/venv

It installs the Python package dependencies used by the GUI and bundled ani-cli fixes into that virtual environment.

It symlinks these commands into ~/.local/bin:

ani-cli
ani-watch
ani-watch-gui
ani-watch-hook
ani-watch-sync

The original system ani-cli is not overwritten.

Launch

Run ani-cli normally:

ani-cli

Open the GUI:

ani-watch-gui

or:

ani-watch gui

The GUI does not need to be open for tracking to work.

By default, an episode is marked watched when the player exits successfully. Playback failures are recorded but do not mark episodes watched.

Playback resolves stream links through anidb.app first and falls back to hianime.at when anidb.app cannot serve the episode. See Playback providers.

In the GUI detail page, select an episode and click Continue to choose Sub or Dub and open ani-cli for that title and episode. Continue uses selected metadata to resolve the intended AllAnime show when it can do so confidently, then opens that show through ani-cli. If the metadata match is not confident enough, it falls back to the normal ani-cli title search. Dub launches use ani-cli's --dub option; if no dub is found for the selected episode, the GUI offers to search sub instead.

The anime detail page also includes a Watch Party menu. Hosting a watch party starts the selected episode, opens a host control window, and generates a share link. Guests can paste the link into Watch Party > Join Watch Party or run:

ani-watch party join URL

The watch party plays video inside its own window rather than in a separate player. Double-click the video, or press F11, to fill the screen, and press Escape or double-click again to drop back into the panel.

Two mpv settings make that embedding work, and both apply only to the embedded player:

  • An X11 GPU context. mpv is embedded with --wid, which it only honours on X11, so on a Wayland session the player is pinned to x11egl (falling back to x11vk then x11) and runs through XWayland. Without the pin mpv reaches for its Wayland backend, silently ignores --wid, and opens a window of its own beside an empty video panel. X11 sessions are left to mpv's own choice.
  • mpv's own frame timing. The embedded player runs with --video-sync=audio instead of the --video-sync=display-resample used elsewhere. A child window's presents are not synchronised with the compositor, and display sync times them against a refresh clock the compositor never agrees with, so the screen shows black blocks flickering through video that mpv itself renders correctly. It only shows while the window is not fullscreen, because a fullscreen surface is presented whole.

Setting ANI_WATCH_MPV_EXTRA_ARGS replaces both for every player, so an explicit choice always wins. Set ANI_WATCH_PARTY_DISABLE_EMBED=1 to keep the player in its own window instead.

Watch parties synchronize local playback control only; video is not rebroadcast. For friends outside your local network, AniAutoWatchList uses Cloudflare Tunnel through cloudflared to create a temporary public link. If cloudflared is not already installed, the app downloads a user-local copy into its app data directory. If the download or tunnel startup fails, the generated link is local-only and the host window shows the tunnel error.

To open the GUI automatically for a single ani-cli run:

ANI_WATCH_OPEN_GUI=1 ani-cli

To temporarily disable tracking hooks:

ANI_WATCH_DISABLE=1 ani-cli

Data Locations

Database: ~/.local/share/ani-watchlist/watchlist.sqlite3
Config:   ~/.config/ani-watchlist/config.toml
Covers:   ~/.cache/ani-watchlist/covers/
Logs:     ~/.local/state/ani-watchlist/logs/
Auto JSON backup: <AniAutoWatchList checkout>/jsonbackup.json
Auto XML backup:  <AniAutoWatchList checkout>/xmlbackup.xml

Every normal GUI exit atomically creates or replaces each automatic backup file, including an automatic close from the Still watching? idle prompt. The JSON file is the full-fidelity AniAutoWatchList recovery backup. The XML file uses the existing MAL-style portable export and includes every local title without delaying shutdown for network ID lookups. Use the manual XML export with refreshed MAL IDs when preparing a file specifically for MAL import. These generated files are ignored by Git so personal watch history is not committed accidentally.

Commands

List and inspect:

ani-watch list
ani-watch list --status watching
ani-watch show "Anime Title"
ani-watch dashboard
ani-watch continue
ani-watch next "Anime Title"
ani-watch discover trending
ani-watch discover trending --refresh
ani-watch schedule
ani-watch schedule --refresh

Edit progress:

ani-watch mark "Anime Title" 12 --watched
ani-watch mark "Anime Title" 12 --unwatched
ani-watch status "Anime Title" completed
ani-watch add "Anime Title" --episodes "1,2,3"
ani-watch delete "Anime Title"

Metadata:

ani-watch metadata search "Anime Title"
ani-watch metadata set "Anime Title" --anilist-id 12345
ani-watch metadata refresh "Anime Title"
ani-watch refresh-metadata "Anime Title"
ani-watch-sync

Import, export, and backups:

ani-watch export --format json --output watchlist.json
ani-watch export --format xml --refresh-mal-ids --skip-missing-mal-ids --output watchlist.xml
ani-watch export --format csv --output watchlist.csv
ani-watch import watchlist.json
ani-watch import watchlist.xml --mode sync
ani-watch import watchlist.json --mode replace
ani-watch backup
ani-watch restore PATH
ani-watch import-history --search "Anime Title"

Maintenance:

ani-watch doctor
ani-watch doctor --no-network
ani-watch events "Anime Title"
ani-watch events --recent 20
ani-watch logs
ani-watch logs --tail
ani-watch duplicates
ani-watch merge "Title A" "Title B" --yes
ani-watch repair
ani-watch repair --yes
ani-watch config get tracking.mark_watched_after_seconds
ani-watch config set tracking.mark_watched_after_seconds 120
ani-watch install-desktop-entry

tracking.mark_watched_after_seconds defaults to 0, which means any successful player exit marks the episode watched. Set it higher if you want a minimum watch time.

GUI

The GUI includes:

  • Trending tab
  • Top Airing tab
  • Most Popular tab
  • Global search box in the top navigation with title suggestions
  • Genre/tag filter for Most Popular
  • Discovery tabs load 100 titles at a time and fetch the next batch as you page forward
  • Long card titles can be scrolled inside the fixed-size cards
  • 7-day release schedule tab
  • Status tabs
  • Cover grid
  • Search/filter box
  • Watchlist JSON/XML import and export
  • Automatic local and cross-machine Google Drive JSON/XML synchronization, with cloud import
  • One-click background metadata and cover refresh for every watchlist entry
  • Anime detail page
  • Related seasons and side stories on detail pages
  • Episode checklist
  • Automatic released episode list refresh from AllAnime
  • Startup cleanup for duplicate watchlist rows that refer to the same anime
  • Continue selected episode through ani-cli
  • Notes editor
  • Metadata refresh
  • AniList match selection
  • Recent activity panel

Type in the top search box to fetch AniList title suggestions. Press Enter to show matching titles as discovery-style cards with the same watchlist-status and AniList actions.

Each discovery, search-result, and related-title card shows AniList's weighted community score as a gold star rating on a five-point scale, replacing the less useful score/trend statistics. The same rating appears on watchlist cards and anime detail pages; unrated titles are identified clearly. Existing AniList-linked entries have missing ratings filled in with a small batched request when the Watchlist or a detail page is first opened.

Each discovery, search-result, and related-title card has a status menu for adding the anime directly to Watching, Completed, Dropped, On Hold, or Plan to Watch. When the title is already present, the menu shows its current status and can move it to another section without creating a duplicate. Clicking the card's cover, title, or metadata area opens the normal watchlist detail and episode view once that anime has been added; the status menu and AniList button remain independent actions.

Use Refresh Metadata on the Watchlist page after a JSON/XML import to update AniList details and cover art for every entry. The refresh runs in the background, displays progress, and reports titles that could not be linked confidently so they can be handled with the detail page's match selector.

Google Drive backups

AniAutoWatchList always writes jsonbackup.json and xmlbackup.xml in the project directory when the GUI closes, including an automatic close after the Still watching? timeout. Once Google Drive is connected, the app synchronizes on launch, manual sync, and exit. It writes the local snapshots first, downloads and merges the existing cloud watchlist, regenerates both formats from the combined database, and only then creates or updates the Drive files. A cloud failure is recorded but does not remove or invalidate the local snapshots.

Google Drive access uses the private appDataFolder and the narrow drive.appdata OAuth scope. The backup files are hidden from the normal My Drive view and AniAutoWatchList cannot read or change the user's other Drive files. Use Watchlist → Cloud → Sync with Google Drive Now to synchronize immediately, view the latest status, or disconnect. Use Watchlist → Import → Import JSON/XML from Google Drive when an explicit replace or add-only recovery is preferable to automatic synchronization.

JSON is the canonical cross-machine source because it retains individual episodes, timestamps, metadata, and activity. XML is regenerated as the portable companion backup. Titles present on either machine are retained; newer per-entry fields such as status and notes win based on their update timestamps; watched episode state is combined conservatively so a stale machine cannot erase watched progress. This is a preservation-oriented backup sync, so deleting an entry or marking an episode unwatched on one machine does not remove a preserved cloud copy automatically.

For a release with the publisher's Google OAuth identity bundled, end-user setup is only:

  1. Choose Cloud → Connect Google Drive....
  2. Sign in and approve AniAutoWatchList in the browser.

An initial synchronization runs immediately, automatic launch/exit synchronization is enabled, and Google's refresh token keeps later syncs signed in. After reinstalling or moving to another machine, connect the same Google account; the existing cloud list is pulled and merged before that machine can upload. A manual cloud import is no longer required for normal cross-machine recovery. End users do not need a Google Cloud project or an OAuth JSON file.

On every later GUI launch, AniAutoWatchList automatically verifies the saved authorization, refreshes an ordinary expired access token without opening the browser, and synchronizes the watchlist in the background. The Watchlist Cloud button shows the result:

  • Green Cloud ✓: the Drive connection and automatic synchronization succeeded.
  • Red Cloud !: no saved authorization exists or Google Drive could not be reached/authorized.
  • Neutral Cloud ...: the background check is still running.

Use Cloud → Test Google Drive Connection to repeat the check immediately. Users normally sign in only once. Google can still require reconnection if the user revokes access, the refresh token is unused for six months, an account/administrator policy invalidates it, or another Google token-lifetime rule applies.

One-time publisher setup

Google requires every application that calls its APIs to have a registered OAuth identity. This cannot be created silently on an end user's behalf, but it is only a maintainer/release task:

  1. Create separate development and production projects in Google Cloud Console, then enable the Google Drive API.
  2. Configure Google Auth Platform branding and an External audience. Add maintainer accounts as test users in the development project.
  3. Create an OAuth client with application type Desktop app.
  4. For a production build, save its downloaded JSON as src/ani_watchlist/_google_drive_oauth_client.json. The packaging configuration includes that file automatically.
  5. Move the production project to In production and complete Google's brand requirements. The drive.appdata scope is non-sensitive, so sensitive-scope verification is not required for this design.

Google explicitly treats an installed desktop application's client ID and client secret as an embedded app identity rather than a confidential secret. Never place a user's access token or refresh token in the package or repository. During development, when no built-in identity is present, the GUI exposes Developer: Configure Google OAuth... so a maintainer can select a local Desktop client JSON without changing the source tree.

Google's Testing publishing status limits authorizations to seven days, so it is unsuitable for the intended sign-in-once experience. User refresh tokens and optional local developer overrides are stored with user-only permissions under ~/.config/ani-watchlist/ (or the configured XDG config directory) and excluded from Git.

When a watchlist entry has an AniList match, the detail page shows related anime from AniList below the episode and activity panels. Sequels, prequels, parent entries, side stories, and spin-offs appear as discovery-style cards. Prequel, sequel, and parent links are followed across the relation chain so later seasons can appear even when AniList links them through an intermediate entry.

When an anime is added from discovery/search or opened from the watchlist, the GUI checks AllAnime for currently released sub episodes and upserts those episode rows. This keeps the watchlist progress count and detail episode list populated before launching playback.

Discovery and schedule data refresh from AniList at most once per local day on GUI startup, unless you press Refresh or use the CLI --refresh option. During AniList's current degraded 30-requests-per-minute period, AniAutoWatchList caps traffic at a conservative 20 requests per minute, spaces every request globally, honors retry/reset headers, and automatically slows further if AniList advertises a lower ceiling. Discovery initially fetches one 50-title page per card section; use the page control to load more on demand. On startup, the GUI also checks GitHub for a newer main branch commit and checks whether the AniAutoWatchList-bundled patched ani-cli has moved beyond the local installed copy. If an AniAutoWatchList update is available and you accept it, a terminal opens, pulls the latest code, reruns scripts/install-user.sh, and prompts you to relaunch the GUI.

How ani-cli Is Patched

The bundled ani-cli/ani-cli script is based on upstream ani-cli. The local patch adds calls to ani-watch-hook at these points:

  • launch
  • title selected
  • episodes listed
  • playback started
  • playback finished

Search still uses ani-cli's existing AllAnime API path. The patch requests the existing englishName field so search results can display English titles when available.

Playback is still handled by ani-cli and the configured player.

Playback providers

Titles, episode lists, and every tracking hook come from ani-cli's existing AllAnime API path. Only the stream links for an already selected title and episode are resolved by a playback provider.

There are two, tried in order:

  1. anidb.app — the primary provider, unchanged.
  2. hianime.at — a fallback used only when anidb.app fails.

The fallback exists because anidb.app periodically goes offline behind a 503 maintenance page, which previously made every launch fail. When that happens the terminal prints why anidb.app failed and then retries the same title and episode on hianime.at:

anidb.app playback failed: anidb.app is down for maintenance. Trying hianime.at...
hianime.at Links Fetched

If both providers fail, the error names each one separately. hianime.at also distinguishes an episode that is still being encoded from one it has no source for, so those cases no longer read as a generic parse failure:

hianime.at is still transcoding episode 1, try again in a few minutes
hianime.at has no sub source for episode 8

hianime.at serves subtitles as a separate WebVTT track rather than burning them into the video, so AniAutoWatchList hands the English track to the player (--sub-file for mpv/IINA/Syncplay, :input-slave= for VLC). Its playlists require a Referer header, so Android intents, catt, and the iSH vlc:// handler cannot play from it. Most titles offer 360p, 720p and 1080p, but some are encoded at a single resolution, in which case -q falls back to best.

Image-wrapped segments

hianime.at ships a minority of its episodes with every .ts segment hidden behind a decoy PNG header. Browsers play them because hls.js resynchronises on the transport-stream sync byte, but mpv, VLC and ffmpeg probe the segment, decide it is a 1x1 image and refuse it — the symptom is one episode of a show failing while its neighbours play.

AniAutoWatchList detects this after the quality is chosen. It reads the first few kilobytes of the first segment, finds where the real transport stream begins, measures every segment, and writes a local copy of the playlist whose entries carry an #EXT-X-BYTERANGE that starts past the decoy. The player is then handed that local playlist:

Segments hide behind a 252 byte image header, measuring them...

Detecting the wrapper costs one playlist request and one 4 KB range request, about 0.4 s per launch. Measuring the segments costs a further HEAD sweep, roughly five seconds for a 24 minute episode, and only happens for affected episodes. Unaffected streams are handed to the player exactly as before. Anything unexpected — an unmeasurable segment, an encrypted or byte-ranged playlist, a fragmented-mp4 stream — leaves the original URL in place, so this can only ever add working episodes. Set ANI_CLI_HLS_FIX=0 to skip the check entirely.

Watch parties use the same launcher, so they get the repair too, including the embedded player, the mpv IPC socket, and seek/pause synchronisation.

Local playlists live in ${TMPDIR:-/tmp}/ani-cli-hls.XXXXXX/stream.m3u8 and are swept on the next run once they are a day old; they outlive the script because a detached player still needs them. mpv, IINA and Syncplay additionally receive --demuxer-lavf-o-append=protocol_whitelist=..., because mpv drops http from the nested protocol whitelist when the playlist itself is a local file. Downloads switch from yt-dlp to ffmpeg for these episodes. Android intents, catt, iSH and --debug cannot use a local playlist, so they keep the original URL.

These environment variables control provider selection:

ANI_CLI_ANIDB=0            # skip anidb.app and go straight to hianime.at
ANI_CLI_HIANIME=0          # disable the fallback and use anidb.app only
ANI_CLI_HIANIME_BASE=...   # point the fallback at a different hianime domain
ANI_CLI_HLS_FIX=0          # never rewrite a playlist, even when segments are image-wrapped

Setting ANI_CLI_ANIDB=0 is worth doing while anidb.app is in maintenance: it removes one failed round trip per episode. Unset it when anidb.app returns.

Do not use ani-cli -U against the ~/.local/bin/ani-cli symlink installed by this project. The bundled script disables that direct upstream self-patcher so the hook integration, embedded-player mpv flags, and mp4 provider fixes are not overwritten. Update through AniAutoWatchList instead.

Development

python3 -m venv .venv
. .venv/bin/activate
pip install -e '.[test]'
make test
make shellcheck
make package-check

make shellcheck skips if shellcheck is not installed.

Uninstall

Remove the user-level command shims and virtual environment:

scripts/uninstall-user.sh
hash -r
command -v ani-cli

The watchlist database is preserved.

Remove app data too:

scripts/uninstall-user.sh --purge-data

Manual rollback for only ani-cli:

rm -f ~/.local/bin/ani-cli
hash -r
command -v ani-cli

After rollback, your shell should use the previous system ani-cli, usually /usr/local/bin/ani-cli or /usr/bin/ani-cli.

License

GPL-3.0-or-later.

The bundled ani-cli/ani-cli script is derived from the upstream ani-cli project. See NOTICE.md and ani-cli/LICENSE for attribution and license details.

About

Local ani-cli watchlist and tracker with a dark desktop GUI, SQLite storage, AniList metadata/cover art, episode progress tracking, backups, imports/exports, and safe user-level installation without overwriting your system ani-cli.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages