Releases: Anime123450/anime-sh
Release list
anime-sh 0.2.84
Changed
-
curl-cffi is an optional extra now, not part of every install. It is a C
extension that fetches a libcurl-impersonate binary while it builds, and
nothing in the project turns it on:HttpClient(impersonate=...)is the only
way in, no bundled provider passes it, and anizone explicitly does not — with
a comment explaining the cold-start cost. It was weight in every install and
in the standalone.exe, and the hardest single thing to package for Homebrew
and the AUR.A plugin that wants browser-TLS impersonation installs
anime-sh[impersonate]. Without it, asking for impersonation fails with that
sentence rather than an ImportError three frames down.
Added
-
Homebrew and AUR packaging. Three of the four install channels were
Windows-only; everyone else was told to install Python first.
brew tap Anime123450/anime-sh && brew install anime-shcovers macOS and
Linux, andpackaging/aur/carries a PKGBUILD for Arch.Both are generated rather than hand-maintained — the formula pins a sha256
for 31 transitive dependencies, and the PKGBUILD's two fields are exactly the
ones that go wrong silently. Both generators download the real sdist and hash
what they actually received rather than trusting a published digest.Neither can be built on Windows, so CI is the verification: the tap's own repo
installs the formula from source on macOS and Linux, and a new workflow here
builds the Arch package in a container, installs it, and checks.SRCINFO
still matches the PKGBUILD.
anime-sh 0.2.83
Added
-
anime wrapped— your watching, summed up: episodes, hours, shows, top
titles and genres, longest streak, biggest day, and a month-by-month shape.
-o card.svgwrites a self-contained card you can drop into a message or a
README;--yearnarrows it;--jsonis there for scripts.The counting is pure and lives in the domain, because every interesting case
is an edge case: a binge is one day however many episodes it was, a streak
spans a month boundary, and dates are read in local time — a 01:00 UTC
session is the previous evening for much of the world, and a streak that
breaks on a timezone is a wrong answer about someone's habits.The card carries no script and no external fonts. GitHub strips scripted SVG,
and that is exactly the kind of place this file is meant to be pasted.
Added
-
anime prefetch— download what you are about to watch next, before you
need it. It walks Continue Watching and saves the next unwatched episode of
each show;-n 3stocks three each,--dry-runshows what it would take.A part-watched episode counts as the next one you want — you stopped in the
middle of it — and it never runs past the finale of a show whose length is
known. Episodes already on disk are skipped, so running it twice costs
nothing and it is safe in a shell alias. One show's provider being down does
not cost you the rest.The argument for it is the week this shipped: a downloaded episode needs no
provider, no resolver and no network, and it was the only thing still working
the day every CDN was down.
Added
-
anime doctor --streams— "can I actually watch right now?" It resolves a
real episode through every installed provider, from your machine, and names
the CDN that answered:[DEAD] stream anikoto: found the show, but no host played [OK ] stream hianime: playable via hls.1embed.buzz (1.5s) You can watch right now — working: hianime.The nightly status badge cannot answer this for you — it runs from a
datacenter IP, where anizone serves a Cloudflare interstitial it never shows a
home connection. When your machine and the badge disagree, your machine is the
one that matters.It never fails the command: a dead provider is the normal operating state, not
a broken install. It also goes through the app's own resolve path rather than
a copy of it, because a diagnostic that takes a different route to the answer
can pass while the thing it diagnoses is broken.
Fixed
-
A dead CDN was handed to the player instead of being skipped. The
pre-flight probe treated every 5xx as "might be transient, let mpv try". But
Cloudflare's 520-527 are not the origin answering badly, they are Cloudflare
saying it could not reach the origin at all — and on 17/09/2026 hianime's CDN
answered 522 for every episode of every show. Nothing behind that edge was
serving media, so the fan-out should have moved on; instead mpv was handed a
URL that could not play while a working provider sat one place behind it.
Those statuses now count as dead. A plain 5xx still does not. -
The circuit breaker never heard whether anything actually played. Only
matching fed it, so a provider whose hosts had all gone dead stayed healthy
for ever and kept sorting first. anikoto spent five days from 13/09/2026 like
that — matching instantly, resolving nothing — and every single play paid the
full fan-out into it before falling through to a provider that worked.Playback outcomes now feed the same breaker, so a provider that cannot produce
a stream demotes itself and recovers on its own when its hosts come back.
Not carrying a show is still a miss, not a failure. Measured on a warm
library: 6.7s to start an episode, down to 4s.
Tooling
-
The canary called a provider healthy while nothing it offered could be
played. anikoto spent five days serving 28 episodes across two hosts,
resolving none of them, and every nightly run reported "All checked providers
healthy". Any resolve failure was being written off as host flakiness — true
of one host among several, false when it is all of them.There is now a
degradedstatus for "the read path works, nothing watchable
came out of it": every host failed to resolve, no installed resolver handles
the hosts on offer, or a stream resolved and then would not load. It exits
non-zero and opens the tracking issue, the same as a hard failure, because
from where a viewer sits there is no difference. -
"Playable" meant a resolver returned a URL, not that the URL served
anything. The canary now fetches what it resolves. On the day this was
written hianime resolved perfectly while every playlist behind it answered
HTTP 522 — its CDN was down — and the old check called that a healthy
provider. -
hianime could never open a tracking issue. The workflow's alerting matrix
still listed onlyanizoneandanikoto; the job that did check hianime
swallows its own failures. It is in the matrix now. -
A live test followed AniList's search ranking onto the wrong show. It
looked up "Frieren: Beyond Journey's End" by title; AniList began returning a
spin-off first, and the test reported a hianime regression that did not exist.
It pins the AniList id instead — which show is meant was never the thing under
test.
anime-sh 0.2.82
Fixed
-
Only
playsurvived an AniList outage. It already fell back to your
local library to identify a show, butsources,download,favorite add
andfavorite rmasked AniList directly, so on a day AniList was down
anime play Frierenworked andanime sources Frieren, one command over,
failed on the same show. All four now use the same fallback.searchgained one too: a title search answers from the shows on this
machine, with a notice on stderr so--jsonstays parseable. Filtered
browsing (--genre,--year, …) deliberately does not; it is a question
about AniList's whole catalogue, and "the shows you happen to have watched" is
a wrong answer to it, not a degraded one.Left alone on purpose:
next,recommendandrelatedneed AniList's
relations after identifying the show, so an offline identity gains them
nothing;markwrites to AniList; andunmarkclears progress with no
confirmation, which is not something to do on a best guess. -
The error said where, not why. AniList explained its outage in the
response body — "The AniList API has been temporarily disabled due to severe
stability issues." — and the HTTP client threw the body away, so you saw
POST https://graphql.anilist.co -> 403. Errors now carry the status and
body, and AniList's own sentence is what gets shown. It also says
unavailable rather than unreachable: the server was answering fine, it had
just switched its API off.
anime-sh 0.2.81
Added
-
A third provider: HiAnime (hianime.at), plus the
zokoresolver that
plays it. It is the same family as anikoto but a shorter road to a stream —
three requests instead of four, with the embed URL arriving base64'd in the
markup rather than behind another token exchange. Its catalogue is deep
(One Piece lists all 1177 episodes) and both sub and dub resolve.Its routes are
/api/theme/…, not the/ajax/…the rest of the family uses;
those 404 here. They were read off the site's own player rather than guessed,
after route-guessing produced six 404s in a row.Not every server is playable: HD-1 and Vidstream-2 answer with an encrypted
blob, which this project decided years ago is not a maintenance burden worth
carrying. They are still emitted as candidates — deciding what is playable is
the resolver chain's job, and the fan-out makes an unresolvable candidate
harmless. ZokoAnime carries the load, on every title tried, in both audios. -
The zoko resolver recovers its decoding key instead of hardcoding it. The
player config is XOR'd with a short repeating key; because the plaintext is
JSON beginning{"download_url", the key falls out of the payload itself. A
hardcoded key would be one silent site update away from every episode failing.
The tests build payloads with keys the code has never seen.
Changed
- Title matching moved to
providers/_matching.py, shared by both
HiAnime-family providers. The ranking heuristics — particularly the
airing-status inversion that stops a finished spin-off outranking the show you
asked for — were tuned against real mismatches, and a second copy would drift.
anime-sh 0.2.80
Fixed
-
The canary blamed the providers for someone else's outage. It resolves a
show through AniList before it ever contacts a provider, and a failure in that
lookup fell through to the provider's verdict. When AniList disabled its own
public API on 07/09/2026 the nightly run filed "anikoto provider is failing"
and "anizone provider is failing" against a repo whose providers were both
fine, two mornings running.The identity lookup is a precondition of the probe, not part of it: failing it
means the provider was never reached, so it cannot be a verdict on the
provider. It now reportsblocked— the status that already existed for "this
is the environment, not a regression" — which exits 0 and files nothing.This is the failure mode the
blockedstatus was invented for. A canary that
cries wolf is worse than no canary: it trains everyone to ignore the one alert
that would have mattered.
anime-sh 0.2.79
Added
-
A provider picker on
p. Which sources anime-sh may search was only
reachable throughanime providers enable|disable— a thing you had to know
existed, and the setting most worth reaching when nothing will play. It is in
the footer now, beside the theme picker.Shows every installed provider highest-priority first, which is the order they
are actually tried in, with a glyph rather than only a colour so the on/off
state survives a terminal without one.Enterorspacetoggles; the choice
is written toproviders.disabled.Two things it refuses to do. It will not let you turn off the last provider —
a configuration with none enabled cannot find a single episode, and that is
better refused as a choice than discovered later as "no sources". And it opens
even when a plugin fails to import, because the picker is where you would go
to turn that plugin off.The hint says "applies next launch", which is the truth: the provider list is
resolved once, when the container is built.
anime-sh 0.2.78
Fixed
-
The home screen went blank when AniList went down, and did not say why.
On 08/09/2026 AniList disabled its own public API —403, with the message
"The AniList API has been temporarily disabled due to severe stability
issues". Continue Watching kept working because it reads the local database;
Airing This Season and Trending went empty, with a heading, no count, and a
blank plate underneath.The failure was announced, once, in a toast — which is gone by the time
anyone looks. What is left reads as "nothing is trending", not as "we could
not reach AniList". A section that fails to load now says unavailable in
its heading and hides its empty plate, and keeps saying so: another worker
finishing afterwards used to overwrite the heading with an ordinary empty one. -
The cache threw away the last good answer at the worst possible moment.
KvCache.getdeleted an entry when it noticed the TTL had passed — and the
read that discovers an entry is stale is the read that is about to ask the
upstream. If the upstream is down, that deleted row was the only copy left.Expired entries are kept now (the write-cadence sweep and
cache purgestill
bound the file), and when a fetch fails the last known value is served if it
expired less than seven days ago. Past that it is not served: stale and wrong
become the same thing eventually, and a month-old trending list is a lie with
a timestamp on it. Nothing stale is written back, so the real value takes over
the moment the upstream answers again.
anime-sh 0.2.77
Fixed
-
The detail screen kept the poster's column even when there was no poster.
#detail-coveris a fixed 34 cells, reserved before the image is fetched so
the metadata does not jump sideways when the art lands. Every way that fetch
can fail — a show with no cover URL, an unreachable host, no Pillow to decode
with — returned early and left the reservation standing, so the panel sat
indented past an empty gutter a third of the screen wide with nothing in it.The column is taken back once there is definitely nothing to show. It is still
held while the fetch is in flight, so a cover that does arrive still does not
make the metadata jump.
anime-sh 0.2.76
Added
-
A compact home density, on
v. Every section spends four rows on chrome —
the heading, the plate's padding above and below its rows, and the gap to the
next heading — plus three for the search box. On a 34-row laptop terminal that
is most of the screen before a single show appears.Compact closes the vertical padding and flattens the search box's border.
Measured on a 34-row terminal with the same library: 19 body rows becomes
23, and where comfortable reached Trending's heading, compact reaches the end
of Trending's rows.The plate keeps its sideways padding. What makes a background change read as
a surface rather than a highlight is that the tint does not stop flush against
the text, and sideways is where that reads — so compact buys rows without
giving up the layering the whole screen is built on.vmeans the same thing on both screens now: home density here, episode layout
on a show. Alsoanime config set ui.density compact. -
?and the README listv, andn(next season), which was bound on the
detail screen and documented nowhere.
anime-sh 0.2.75
Added
-
Episode layouts —
grid,listandcompact, switched withv. There is
no arrangement that suits everyone: a twelve-part season reads best as a block,
a four-figure series wants the tightest grid it can get, and some people would
rather have one episode per line with its state spelled out, which is what this
screen looked like before the grid. So it is a setting now, remembered across
runs like the theme.layout ONE PIECE's 1175 episodes grid18 across, 66 rows compact23 across, 52 rows listone per line, each carrying its own state Also settable with
anime config set ui.episodes list, and an unknown name is
refused where it is typed rather than silently falling back to the default.
Fixed
-
The episode grid was strung across the whole window.
grid_size_columns
sets how many columns a Textual grid has, not how wide they are — the grid
shares its full width between them. Twelve columns on a 190-column terminal
came out fifteen cells wide holding five cells of content, so a twelve-episode
season sat with its numbers fourteen spaces apart and the season unreadable as
a group.Columns are a fixed number of cells now, and no longer capped at twelve, so a
wide terminal gets more of them rather than wider ones. -
?now listsn(next season), which has been bound on the detail screen and
documented nowhere. The guard that keeps the cheat sheet honest only knew about
the home screen and the app, so any detail-screen key could have named anything
at all without anything noticing; it covers all three now.