Skip to content

Releases: Anime123450/anime-sh

anime-sh 0.2.84

Choose a tag to compare

@github-actions github-actions released this 18 Sep 06:10
382ae2d

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-sh covers macOS and
    Linux, and packaging/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

Choose a tag to compare

@github-actions github-actions released this 17 Sep 20:30
beea92c

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.svg writes a self-contained card you can drop into a message or a
    README; --year narrows it; --json is 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 3 stocks three each, --dry-run shows 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 degraded status 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 only anizone and anikoto; 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

Choose a tag to compare

@github-actions github-actions released this 10 Sep 13:59
a8dfc06

Fixed

  • Only play survived an AniList outage. It already fell back to your
    local library to identify a show, but sources, download, favorite add
    and favorite rm asked AniList directly, so on a day AniList was down
    anime play Frieren worked and anime sources Frieren, one command over,
    failed on the same show. All four now use the same fallback.

    search gained one too: a title search answers from the shows on this
    machine, with a notice on stderr so --json stays 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, recommend and related need AniList's
    relations after identifying the show, so an offline identity gains them
    nothing; mark writes to AniList; and unmark clears 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

Choose a tag to compare

@github-actions github-actions released this 10 Sep 05:48
8e492c5

Added

  • A third provider: HiAnime (hianime.at), plus the zoko resolver 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

Choose a tag to compare

@github-actions github-actions released this 08 Sep 17:14
9e7ce9a

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 reports blocked — 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 blocked status 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

Choose a tag to compare

@github-actions github-actions released this 08 Sep 16:42
605e828

Added

  • A provider picker on p. Which sources anime-sh may search was only
    reachable through anime 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. Enter or space toggles; the choice
    is written to providers.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

Choose a tag to compare

@github-actions github-actions released this 08 Sep 15:58
ad4cca3

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.get deleted 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 purge still
    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

Choose a tag to compare

@github-actions github-actions released this 06 Sep 11:26
196df8a

Fixed

  • The detail screen kept the poster's column even when there was no poster.
    #detail-cover is 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

Choose a tag to compare

@github-actions github-actions released this 06 Sep 08:22
dc67677

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.

    v means the same thing on both screens now: home density here, episode layout
    on a show. Also anime config set ui.density compact.

  • ? and the README list v, and n (next season), which was bound on the
    detail screen and documented nowhere.

anime-sh 0.2.75

Choose a tag to compare

@github-actions github-actions released this 06 Sep 08:01
41039ab

Added

  • Episode layouts — grid, list and compact, switched with v. 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
    grid 18 across, 66 rows
    compact 23 across, 52 rows
    list one 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 lists n (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.