An all-in-one enhancement extension for Crunchyroll (Chrome / Edge, Manifest V3). Version 0.1.0.
It lives in a persistent Chrome side panel that adapts to what you’re doing: a live show companion while you watch, and a home dashboard everywhere else.
On Crunchyroll — the live show panel: now-playing hero, MyAnimeList sync, your episode/status/score, plus the show's synopsis, seasons, characters and reviews. |
Anywhere else — the home dashboard: your skip stats and activity, a Resume card, Continue-watching, your MyAnimeList "watching" list, and what's trending this season. |
Settings — skip method, per-segment auto-skip toggles, playback options, and your MyAnimeList connection — all inline in the panel. |
Continue watching — your recently opened episodes, with one-click resume and per-entry remove. |
chrome.storage.sync.This is a personal, client-side enhancement that only automates actions you can already perform yourself (clicking Skip / Next). It does not bypass paywalls, DRM, or advertising.
Crunchyroll publishes per-episode skip timings as static JSON — the same data that powers its own Skip Intro button:
https://static.crunchyroll.com/skip-events/production/{episodeId}.json
The extension supports two methods (Options → Skip method):
<video> straight past each enabled segment. If an
episode has no published data, it falls back to clicking Crunchyroll’s
native skip button.Because Crunchyroll is a single-page app, the content script watches History API navigation (and polls as a safety net) and re-initialises for each new episode without a page reload.
Users open Options → MyAnimeList → Connect MyAnimeList, log into their own MAL account, and toggle Sync watched episodes on. Nothing else to set up.
–/+ (and type-to-set) episode stepper, status dropdown (Watching
/ Completed / …), a 1–10 star rating, and a link to the entry on MAL.https://jcfmdllkakmjkihgphmmimhiehcbbfei.chromiumapp.org/
(This ID is pinned by the key in the manifest, so it’s stable.)src/shared/mal-config.ts
(MAL_CLIENT_ID) and rebuild.Auth is OAuth2 authorization-code + PKCE; tokens are stored locally and refreshed
automatically. The client ID is safe to ship (it’s not a secret in PKCE flows);
the signing key (mal-signing-key.pem) is gitignored.
src/
├─ content/ # runs on the watch page (all frames)
│ ├─ index.ts # entry: wires the per-episode session together
│ ├─ navigation.ts # SPA episode-change detection (History API + poll)
│ ├─ player.ts # locate <video>, seek helper
│ ├─ meta.ts # scrape series/season/episode (JSON-LD, og:title)
│ ├─ skip-api.ts # ask the worker for skip-events data
│ ├─ skip-engine.ts # seek-mode auto-skip
│ ├─ dom-skip.ts # fallback: click the native skip button
│ ├─ autonext.ts # auto-play next episode
│ ├─ keep-watching.ts # dismiss "still watching?" / profile prompts
│ ├─ progress.ts # report the current episode to the tracker
│ └─ toast.ts # "Skipped X — Undo" overlay
├─ background/
│ └─ service-worker.ts # skip-events fetch (avoids CORS) + MyAnimeList sync hub
├─ options/ # full settings page (fallback)
├─ sidepanel/ # the side panel: show view, home dashboard, settings,
│ # MAL card, rails (seasons/characters/reviews/trending)
├─ shared/ # settings, messages, MAL client, tracker store,
│ # history, stats, runtime guards, types, parsers
└─ assets/icons/
scripts/
└─ build.mjs # esbuild bundler + MV3 manifest generation → dist/
npm install
npm run build # type-checks (tsc --noEmit), then esbuild-bundles to dist/
Each entry is bundled as a single self-contained IIFE (no code-splitting, no
dynamic import()) and the content script is declared directly in the manifest.
This matters: Crunchyroll’s player runs in a cross-origin iframe
(static.crunchyroll.com/.../player.html) with a strict CSP, and a
dynamic-import-based content-script loader (e.g. @crxjs) gets blocked there — so
the skip code would never run where the video actually is. Manifest-declared
content scripts are injected by Chrome and bypass the page CSP.
Then in Chrome / Edge:
chrome://extensions.dist/ folder./watch/... episode.After each rebuild, click the reload ↻ icon on the extension’s card in
chrome://extensionsso Chrome picks up the newdist/, then reload the Crunchyroll tab (content scripts aren’t re-injected into already-open tabs).
Click the toolbar icon to open the side panel (needs Chrome 114+); its Settings view covers everything, with the standalone options page as a fallback.
404s (normal for episodes with no published
data), and watched: … lines tracing each MyAnimeList sync.MIT