{"_id":"@aphrody/ietv","_rev":"4-4b474699d2802887953c89cf43553179","name":"@aphrody/ietv","dist-tags":{"latest":"0.2.2"},"versions":{"0.1.0":{"name":"@aphrody/ietv","version":"0.1.0","keywords":["inazuma-eleven","youtube","scraper","streaming","anime"],"license":"Apache-2.0","_id":"@aphrody/ietv@0.1.0","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"dist":{"shasum":"88675133e89540432960863ca4f358bd2e7ea3a8","tarball":"https://registry.npmjs.org/@aphrody/ietv/-/ietv-0.1.0.tgz","fileCount":19,"integrity":"sha512-oqKb9j+E+8uwsU2smd9/kx6OW13ICFHQnsGpdmrEK6ceEB8KT0Q7aalWIBcnBn46HlGvjKfN0KieL1v4vkMNWw==","signatures":[{"sig":"MEQCIBG0i9AcuB3a6+Rj2lAdsfSHoqdqUpOeOrLS2Ptq7jsRAiBPCOjNWLrLTz5O7FsTI53W+cgospeW6fcaWMjkXZ19fA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205429},"main":"./src/index.ts","type":"module","types":"./src/index.ts","shasum":"88675133e89540432960863ca4f358bd2e7ea3a8","exports":{".":"./src/index.ts","./cache":"./src/cache.ts","./video":"./src/video.ts","./video-codec":"./src/video-codec.ts","./video-player":"./src/video-player.ts","./video-search":"./src/video-search.ts"},"scripts":{"test":"bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-oqKb9j+E+8uwsU2smd9/kx6OW13ICFHQnsGpdmrEK6ceEB8KT0Q7aalWIBcnBn46HlGvjKfN0KieL1v4vkMNWw==","_npmVersion":"10.8.3","directories":{},"_nodeVersion":"24.3.0","dependencies":{"mediabunny":"^1.55.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"hls.js":"^1.7.1","media-chrome":"^4.19.2"},"peerDependencies":{"hls.js":"^1.7.1","media-chrome":"^4.19.2","@mediabunny/server":"^1.55.5"},"peerDependenciesMeta":{"hls.js":{"optional":true},"media-chrome":{"optional":true},"@mediabunny/server":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ietv_0.1.0_1788311206320_0.7312196036883127","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aphrody/ietv","version":"0.2.0","keywords":["inazuma-eleven","youtube","scraper","streaming","anime"],"license":"Apache-2.0","_id":"@aphrody/ietv@0.2.0","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"dist":{"shasum":"6160be53fa80d5593df97fa159e08863b9d5d3c9","tarball":"https://registry.npmjs.org/@aphrody/ietv/-/ietv-0.2.0.tgz","fileCount":11,"integrity":"sha512-vhnfLIbn43+rQWbq0XelEI7k0oA5hXrrNdr56ewOj7y/u5HvFU2gBdWt3dP/Ua3qm/1ANlpWSKQmWzqh37PROg==","signatures":[{"sig":"MEUCIADC5X4nkJneTzsFADld6tywaOMvKdyncWhkWJgIBlPzAiEA23v1jeo9+82TEy2RCshRraybH4LIDGhUzmPjWgHm2Mw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":133995},"main":"./src/index.ts","type":"module","types":"./src/index.ts","shasum":"6160be53fa80d5593df97fa159e08863b9d5d3c9","engines":{"bun":">=1.3.14"},"exports":{".":"./src/index.ts","./cache":"./src/cache.ts","./video":"./src/video.ts","./video-codec":"./src/video-codec.ts","./video-player":"./src/video-player.ts","./video-search":"./src/video-search.ts"},"scripts":{"test":"bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-vhnfLIbn43+rQWbq0XelEI7k0oA5hXrrNdr56ewOj7y/u5HvFU2gBdWt3dP/Ua3qm/1ANlpWSKQmWzqh37PROg==","_npmVersion":"10.8.3","directories":{},"_nodeVersion":"24.3.0","dependencies":{"mediabunny":"^1.55.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"hls.js":"^1.7.1","media-chrome":"^4.19.2"},"peerDependencies":{"hls.js":"^1.7.1","media-chrome":"^4.19.2","@mediabunny/server":"^1.55.5"},"peerDependenciesMeta":{"hls.js":{"optional":true},"media-chrome":{"optional":true},"@mediabunny/server":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ietv_0.2.0_1788431543219_0.7993326901923421","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aphrody/ietv","version":"0.2.1","keywords":["inazuma-eleven","youtube","scraper","streaming","anime"],"license":"Apache-2.0","_id":"@aphrody/ietv@0.2.1","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"dist":{"shasum":"5c21d602c960aae181e2027da959c9c85789afe0","tarball":"https://registry.npmjs.org/@aphrody/ietv/-/ietv-0.2.1.tgz","fileCount":11,"integrity":"sha512-eBFGTDWNKAWov7swdP59fxP7lRLVsTOMkmdRSYLsP47JiSDD5GPzKlpC7efUr71biEPBx0AiduHOZp3z+HMNrA==","signatures":[{"sig":"MEQCIGjpXIlhNaQceTrWswSxkK2jCazyTcNPSDPG54WP9shQAiAxOhvR9joGpeQGkvf+CcaCCQ0+tIvuU1PkpIf6wgkDLg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":135236},"main":"./src/index.ts","type":"module","types":"./src/index.ts","shasum":"5c21d602c960aae181e2027da959c9c85789afe0","engines":{"bun":">=1.3.14"},"exports":{".":"./src/index.ts","./cache":"./src/cache.ts","./video":"./src/video.ts","./video-codec":"./src/video-codec.ts","./video-player":"./src/video-player.ts","./video-search":"./src/video-search.ts"},"scripts":{"test":"bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-eBFGTDWNKAWov7swdP59fxP7lRLVsTOMkmdRSYLsP47JiSDD5GPzKlpC7efUr71biEPBx0AiduHOZp3z+HMNrA==","_npmVersion":"10.8.3","directories":{},"_nodeVersion":"24.3.0","dependencies":{"mediabunny":"^1.55.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"hls.js":"^1.7.1","media-chrome":"^4.19.2"},"peerDependencies":{"hls.js":"^1.7.1","media-chrome":"^4.19.2","@mediabunny/server":"^1.55.5"},"peerDependenciesMeta":{"hls.js":{"optional":true},"media-chrome":{"optional":true},"@mediabunny/server":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ietv_0.2.1_1788681500489_0.41736761684612467","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@aphrody/ietv","version":"0.2.2","publishConfig":{"access":"public"},"type":"module","main":"./src/index.ts","types":"./src/index.ts","exports":{".":"./src/index.ts","./cache":"./src/cache.ts","./plateformes":"./src/plateformes.ts","./official":"./src/official.ts","./verifier":"./src/verifier.ts","./playlist-youtube":"./src/playlist-youtube.ts","./video":"./src/video.ts","./video-player":"./src/video-player.ts","./video-codec":"./src/video-codec.ts","./video-search":"./src/video-search.ts"},"scripts":{"typecheck":"bunx tsc --noEmit","test":"bun test"},"keywords":["inazuma-eleven","youtube","scraper","streaming","anime"],"dependencies":{"@aphrody/bxc":"catalog:","mediabunny":"catalog:"},"peerDependencies":{"@mediabunny/server":"^1.55.5","hls.js":"^1.7.1","media-chrome":"^4.19.2"},"peerDependenciesMeta":{"@mediabunny/server":{"optional":true},"hls.js":{"optional":true},"media-chrome":{"optional":true}},"devDependencies":{"hls.js":"catalog:","media-chrome":"catalog:","typescript":"catalog:","@types/bun":"catalog:"},"license":"Apache-2.0","gitHead":"27f4b6e928b4b99143784f90d3bd1313a6fc6403","description":"**Inazuma Eleven TV (IETV)** — YouTube channel scraper for Inazuma Eleven French streaming","_id":"@aphrody/ietv@0.2.2","_nodeVersion":"24.3.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-Dp32upKRaNmUh3ZjjsHdH2GcPSyoYG4OxIfB4saFMLDjjBRqUbGC88jEOPaLMrBU+zPQEyaN6EPShaY7y+zqRw==","shasum":"79f2ce850741fbf4ae2d273e4ff2d4ce5a275d2b","tarball":"https://registry.npmjs.org/@aphrody/ietv/-/ietv-0.2.2.tgz","fileCount":29,"unpackedSize":366967,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCentSYsdhQKptOFGa0k9SUG2DgxpXmiu3mtMFtDkHfngIhAKYRNRg1ZiDVI+XZs1+caathK/pF9eC96BPv7srj1Mny"}]},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"directories":{},"maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ietv_0.2.2_1789022257376_0.9931310903786932"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-02T01:06:46.113Z","modified":"2026-09-10T06:37:37.778Z","0.1.0":"2026-09-02T01:06:46.454Z","0.2.0":"2026-09-03T10:32:23.348Z","0.2.1":"2026-09-06T07:58:20.629Z","0.2.2":"2026-09-10T06:37:37.592Z"},"license":"Apache-2.0","keywords":["inazuma-eleven","youtube","scraper","streaming","anime"],"maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"readme":"# @aphrody/ietv\n\n**Inazuma Eleven TV (IETV)** — YouTube channel scraper for Inazuma Eleven French streaming\n\nDedicated scraper for aggregating episodes across four official Inazuma Eleven French YouTube channels, resolving episodes by season and episode number.\n\n## Installation\n\n```bash\nnpm install @aphrody/ietv\n# or\nbun install @aphrody/ietv\n```\n\n## Usage\n\n### Programmatic API\n\n```ts\nimport IETVScraper from \"@aphrody/ietv\";\n\nconst scraper = new IETVScraper({ profile: \"fast\" });\n\n// Get episodes from a single channel\nconst channelInfo = await scraper.getChannelEpisodes(\"inazumaelevenfrance1\");\nconsole.log(`${channelInfo.channel}: ${channelInfo.totalEpisodes} episodes`);\n\n// Aggregate all 4 channels\nconst allChannels = await scraper.getAllChannelEpisodes();\nfor (const ch of allChannels) {\n  console.log(`${ch.title}: ${ch.seasons.length} seasons`);\n}\n\nawait scraper.close();\n```\n\n### CLI\n\n```bash\n# Get episodes from a specific YouTube channel\nbxc ietv channel inazumaelevenfrance1\nbxc ietv channel inazumatvfr --profile fast\n\n# Scrape official inazuma-eleven.fr streaming site (most complete)\nbxc ietv official\n\n# Aggregate all episodes from all 4 YouTube channels + official site\nbxc ietv all\n\n# Discover additional Inazuma Eleven channels via Google Search\nbxc ietv discover\n\n# Verify YouTube API credentials\nbxc ietv check-auth\n\n# List canonical YouTube channels\nbxc ietv list\n```\n\n## Channels & Sources\n\n### Official Streaming Site\n- **inazuma-eleven.fr** (most complete) — https://inazuma-eleven.fr/tv/watch?lang=fr\n  - Official French streaming platform\n  - Complete episode catalog with proper metadata\n\n### YouTube Channels\n- `@inazumaelevenfrance1` — https://www.youtube.com/@inazumaelevenfrance1\n- `@inazumatvfr` — https://www.youtube.com/@inazumatvfr\n- `@inazumaelevengofrance` — https://www.youtube.com/@inazumaelevengofrance\n- `@InazumaTVFR__` — https://www.youtube.com/@InazumaTVFR__\n\n## Output Format\n\n### Channel Info\n\n```json\n{\n  \"channel\": \"inazumaelevenfrance1\",\n  \"title\": \"Inazuma Eleven France officiel\",\n  \"description\": \"Chaîne officielle française d'Inazuma Eleven\",\n  \"avatar\": \"https://...\",\n  \"seasons\": [\n    {\n      \"season\": 1,\n      \"episodes\": [\n        {\n          \"title\": \"Inazuma Eleven - Saison 1 Episode 01\",\n          \"videoId\": \"abc123def456\",\n          \"url\": \"https://www.youtube.com/watch?v=abc123def456\",\n          \"description\": null,\n          \"thumbnail\": null,\n          \"publishDate\": null,\n          \"season\": 1,\n          \"episode\": 1,\n          \"duration\": null,\n          \"viewCount\": null\n        }\n        // ... more episodes\n      ],\n      \"totalEpisodes\": 51\n    }\n    // ... more seasons\n  ],\n  \"totalEpisodes\": 153\n}\n```\n\n## Episode & Language Parsing\n\n### Episode Numbers\n\nEpisode numbers are parsed from video titles using these patterns (in order of precedence):\n\n1. `S##E##` format: \"Season 1 Episode 5\" → season 1, episode 5\n2. `Saison/Season X Épisode/Episode Y`: \"Saison 1 Épisode 5\" → season 1, episode 5\n3. `Ep. N` format: \"Inazuma Eleven Ep. 5\" → season 1, episode 5 (defaults to season 1)\n4. Trailing numbers: last numeric sequence is treated as episode number\n\n### Language Versions\n\nVideos are automatically classified as:\n\n- **VF** (Version Française) — dubbed in French\n  - Detected from: \"VF\", \"Version Française\", \"Doublage\", \"Dubbed\"\n  - Default when title contains \"Saison\"\n\n- **VOSTFR** (Version Originale Sous-Titrée Française) — original audio + French subtitles\n  - Detected from: \"VOSTFR\", \"V.O.STFR\", \"VO Japonaise\", \"Japanese Original\", \"JP French Subs\"\n  - Takes precedence over VF markers when both found\n\n- **Unknown** — unable to determine from title\n\nThis allows filtering episodes by preferred language version:\n\n```ts\nconst vfOnly = episodes.filter(ep => ep.language === \"vf\");\nconst vostfrOnly = episodes.filter(ep => ep.language === \"vostfr\");\n```\n\n## Options\n\n```ts\ninterface IETVOptions {\n  /** Transport profile: \"static\" (fastest, no JS), \"fast\", \"http\", \"stealth\", \"max\" */\n  profile?: \"static\" | \"http\" | \"fast\" | \"stealth\" | \"max\";\n  \n  /** Per-request timeout in ms (default 30000) */\n  timeoutMs?: number;\n  \n  /** Retries per fetch on transient failure (default 2) */\n  retries?: number;\n}\n```\n\n**Note**: YouTube requires JavaScript execution to load dynamic content. Default profile is `\"fast\"` which executes JavaScript. Use `\"static\"` only if you know the page has server-rendered video lists.\n\n## Exports\n\nThe module exports the following for advanced use:\n\n```ts\n// Types\nexport type LanguageVersion = \"vf\" | \"vostfr\" | \"unknown\";\nexport interface VideoRef { /* ... */ }\nexport interface SeasonInfo { /* ... */ }\nexport interface ChannelInfo { /* ... */ }\nexport interface IETVOptions { /* ... */ }\n\n// Functions\nexport function parseSeasonEpisode(title: string): { season, episode }\nexport function detectLanguage(title: string): LanguageVersion\nexport class IETVScraper { /* ... */ }\n```\n\nSubpath exports: `./cache` (SQLite cache), `./video-player`, `./video-codec`,\n`./video-search`, and `./video` (barrel over the three).\n\n## Video: playback & transcoding\n\nTwo optional surfaces, published under their own subpaths so the scraper stays\ndependency-free:\n\n| Subpath | Purpose | Runtime deps |\n| --- | --- | --- |\n| `@aphrody/ietv/video-player` | HLS/MP4 playback with a skinnable UI | `media-chrome`, `hls.js` (optional peers) |\n| `@aphrody/ietv/video-codec` | Transcoding & compression profiles | `mediabunny` (bundled), `@mediabunny/server` (optional peer) |\n| `@aphrody/ietv/video` | Barrel re-exporting both plus the search layer | — |\n\n### Player (browser)\n\n`media-chrome` provides the controls, `hls.js` the HLS protocol. Both are\nloaded lazily at mount time, so importing the module server-side is free.\n\n```bash\nnpm install media-chrome hls.js\n```\n\n```ts\nimport { IETVPlayer } from \"@aphrody/ietv/video-player\";\n\nconst player = new IETVPlayer({ autoplay: false, startQuality: \"auto\" });\nawait player.mount(document.querySelector(\"#player\")!);\nawait player.load(\"https://cdn.example/ep1.m3u8\"); // format inferred from the URL\n\nplayer.getQualities();     // [{ index: -1, label: \"auto\" }, { index: 0, label: \"360p\" }, …]\nplayer.setQuality(1);      // pin a variant, or \"auto\" to hand back to the ABR\nplayer.getStats();         // resolution, bitrate, measured fps, buffer health, bandwidth\nplayer.destroy();\n```\n\nNative HLS (Safari) is preferred when available. Fatal `hls.js` errors are\nrecovered per the upstream playbook — reload on network errors, buffer flush on\nmedia errors — and only surface to `onError` when unrecoverable. Without\n`media-chrome` installed the player falls back to the browser's native\ncontrols instead of failing.\n\n### Transcoding (server)\n\n[mediabunny](https://mediabunny.dev) encodes through WebCodecs. Bun and Node\ndon't implement it, so install `@mediabunny/server` (FFmpeg bindings) to get\nencoders; `ensureNativeCodecs()` registers them on first use.\n\n```bash\nnpm install @mediabunny/server\n```\n\n```ts\nimport {\n  COMPRESSION_PROFILES,\n  VideoTranscoder,\n} from \"@aphrody/ietv/video-codec\";\n\nconst transcoder = new VideoTranscoder();\n\nawait transcoder.probe(\"ep1.mkv\");\n// { durationSeconds, video: { codec, width, height, rotation }, audio: {…} }\n\nconst result = await transcoder.transcode(\"ep1.mkv\", \"ep1.mp4\", {\n  profile: COMPRESSION_PROFILES.web_720,\n  onProgress: (p) => process.stdout.write(`\\r${(p * 100).toFixed(0)} %`),\n});\n// { sizeBytes, elapsedMs, videoCodec: \"h265\", discarded: [] }\n```\n\nProfiles carry both a target `bitrate` (bits/s) and a `quantizer` (FFmpeg's CRF\nequivalent) so the encoder can hold quality constant when it supports it:\n\n| Profile | Codec | Resolution | Bitrate | Quantizer |\n| --- | --- | --- | --- | --- |\n| `mobile_360` | H.264 / AAC | 360p | 500 kbps | 28 |\n| `mobile_480` | H.264 / AAC | 480p | 1 Mbps | 26 |\n| `web_720` | H.265 / AAC | 720p | 2 Mbps | 26 |\n| `desktop_1080` | H.265 / AAC | 1080p | 4 Mbps | 24 |\n| `av1_1080` | AV1 / Opus | 1080p | 1.5 Mbps | 32 |\n\n`VideoCodec.recommendProfile(device, bandwidthMbps)` picks one, and\n`VideoCodec.estimateFileSize(durationSeconds, profile)` sizes the output before\nrunning the job. Without an available encoder, `transcode()` fails up front with\nthe install hint rather than an opaque WebCodecs error.\n\n## Authentication & Credentials\n\nThe scraper automatically loads YouTube API credentials from secure sources (in order):\n\n1. **YOUTUBE_API_KEY** environment variable\n2. **~/.ietv/auth.json** (JSON file with `key` field)\n3. **~/.aphrody/ietv-credentials.json** (JSON file with `youtube_api_key` field)\n4. **gcloud** (requires `gcloud auth application-default login`)\n\n### Setup via Aphrody\n\n```bash\n# Create credentials directory\nmkdir -p ~/.aphrody\n\n# Store YouTube API key securely\necho '{\"youtube_api_key\": \"YOUR_API_KEY\"}' > ~/.aphrody/ietv-credentials.json\nchmod 600 ~/.aphrody/ietv-credentials.json\n```\n\n### Setup via gcloud\n\n```bash\n# Authenticate with Google Cloud\ngcloud auth application-default login\n\n# Or set service account credentials\nexport GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/service-account.json\n```\n\n## Channel Discovery\n\nThe scraper can discover additional Inazuma Eleven channels beyond the canonical four:\n\n```ts\nconst scraper = new IETVScraper();\n// Credentials are auto-loaded from ~/.aphrody/ or environment\nconst discoveredChannels = await scraper.discoverChannels(\n  \"Inazuma Eleven français replay\"\n);\n```\n\nDiscovery methods (in order of preference):\n\n1. **YouTube Data API** (automatic if credentials found) — most accurate, ~50 results\n2. **Google Search** (fallback) — finds YouTube channels via Google results, slower but free\n\n```bash\n# Discover with auto-loaded credentials\nbxc ietv discover\n\n# Or override with explicit API key\nbxc ietv discover --youtube-api-key \"YOUR_API_KEY\"\n\n# List credentials status\nbxc ietv --check-auth\n```\n\n## Limitations\n\n- YouTube's anti-bot measures may rate-limit or block requests in some profiles.\n- Episode parsing relies on title conventions; inconsistently-named videos may not parse correctly.\n- Video descriptions, durations, and view counts are extracted from HTML when available but may not be complete.\n- Discovery via Google Search is slower and may miss some channels.\n- For production use, consider using the official [YouTube Data API](https://developers.google.com/youtube/v3) with authentication.\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md","description":"**Inazuma Eleven TV (IETV)** — YouTube channel scraper for Inazuma Eleven French streaming"}