{"_id":"@aphrody/animesama","_rev":"2-b2a23195b046955ac93f13ad7d32def6","name":"@aphrody/animesama","dist-tags":{"latest":"0.2.1"},"versions":{"0.2.0":{"name":"@aphrody/animesama","version":"0.2.0","keywords":["anime-sama","anime","scraper","streaming","vostfr"],"license":"Apache-2.0","_id":"@aphrody/animesama@0.2.0","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"dist":{"shasum":"6cf2619bb3b0d90429432494d768993547f5a0d9","tarball":"https://registry.npmjs.org/@aphrody/animesama/-/animesama-0.2.0.tgz","fileCount":3,"integrity":"sha512-s+MmTh4RdeLjt+Iu419+A7I1dB64yCfdSZOKTyrQdStMbef6eygsUAqvWZC2Ud/uXb1Nyzz+CyTLrwHKEWesHg==","signatures":[{"sig":"MEQCIBAyboPBV1afpKyVtG5CNFl2LaUikLbOyk3WUWkTEtIJAiAtmiZ+gZXF2zm+67A73rvxCBqb5Syj5O17N8R0XA5oaA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49131},"main":"./src/index.ts","type":"module","types":"./src/index.ts","shasum":"6cf2619bb3b0d90429432494d768993547f5a0d9","engines":{"bun":">=1.3.14"},"exports":{".":"./src/index.ts"},"scripts":{"test":"bun test src/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-s+MmTh4RdeLjt+Iu419+A7I1dB64yCfdSZOKTyrQdStMbef6eygsUAqvWZC2Ud/uXb1Nyzz+CyTLrwHKEWesHg==","_npmVersion":"10.8.3","directories":{},"_nodeVersion":"24.3.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/animesama_0.2.0_1788431536730_0.4901815932543454","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aphrody/animesama","version":"0.2.1","publishConfig":{"access":"public"},"type":"module","main":"./src/index.ts","types":"./src/index.ts","exports":{".":"./src/index.ts"},"scripts":{"typecheck":"tsc --noEmit","test":"bun test src/"},"keywords":["anime-sama","anime","scraper","streaming","vostfr"],"dependencies":{},"engines":{"bun":">=1.3.14"},"license":"Apache-2.0","_id":"@aphrody/animesama@0.2.1","_integrity":"sha512-bZPYIgvSvZ5PEmUGppgZLv/X1i5kiJfbxAt5IGlsBvUaMMPRYRQ8wWra6+0OO+VrsS6h0eXSrCxCebPaPKbtYA==","_nodeVersion":"24.3.0","_npmVersion":"10.8.3","shasum":"36e6f7df1a789e6faec42123dc00d7add7f9fc2b","dist":{"integrity":"sha512-bZPYIgvSvZ5PEmUGppgZLv/X1i5kiJfbxAt5IGlsBvUaMMPRYRQ8wWra6+0OO+VrsS6h0eXSrCxCebPaPKbtYA==","shasum":"36e6f7df1a789e6faec42123dc00d7add7f9fc2b","tarball":"https://registry.npmjs.org/@aphrody/animesama/-/animesama-0.2.1.tgz","fileCount":3,"unpackedSize":49131,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAGeupYP4F862GWh3MUnn8Kjz19HVNWwEEptPSqm/5gXAiBr2BWQ+pBVboJosA0TcUj9YNIm1SvzsN4JjAwrCdvL3w=="}]},"_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/animesama_0.2.1_1788681494194_0.30670221467168046"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-03T10:32:16.454Z","modified":"2026-09-06T07:58:14.479Z","0.2.0":"2026-09-03T10:32:16.855Z","0.2.1":"2026-09-06T07:58:14.311Z"},"license":"Apache-2.0","keywords":["anime-sama","anime","scraper","streaming","vostfr"],"maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"readme":"# @aphrody/animesama\n\nScraper typé pour **[anime-sama.to](https://anime-sama.to)** : catalogue,\nsaisons, langues, épisodes et résolution des lecteurs vers un flux direct.\n\nExtraction **purement textuelle** — pas de DOM, pas d'exécution de JS. Le module\nanalyse le HTML rendu côté serveur et les fichiers `episodes.js`, donc il\nfonctionne aussi bien sur un miroir persisté que sur une réponse live. Le\ntransport HTTP est **injectable**, ce qui rend l'ensemble testable sans réseau.\n\n- Code : `packages/animesama/src/index.ts`\n- Tests (sans réseau) : `packages/animesama/src/index.test.ts`\n- Fixtures réelles : `packages/animesama/test/fixtures/`\n\n## Anatomie du site (rétro-ingénierie du 2026-09-03)\n\n| Ressource | Chemin | Contenu |\n| --- | --- | --- |\n| Fiche d'une œuvre | `/catalogue/<slug>/` | titre, titres alternatifs, synopsis, genres, état, année, studios — et les saisons **en JavaScript** |\n| Page de saison | `/catalogue/<slug>/<saison>/<langue>/` | titre, sélecteurs, et le script qui compose les libellés d'épisodes |\n| Lecteurs | `/catalogue/<slug>/<saison>/<langue>/episodes.js` | une variable `epsN` par lecteur, une case par épisode |\n| Recherche instantanée | `POST /template-php/defaut/fetch.php` (`query=<texte>`) | fragment de `<a class=\"asn-search-result\">` |\n| Catalogue paginé | `GET /catalogue/?search=<texte>&page=N` | cartes `.catalog-card` |\n\nQuatre pièges que le module absorbe :\n\n1. **Les saisons ne sont pas du HTML.** Elles sont écrites par des appels\n   `panneauAnime(\"Saison 1\", \"saison1/vf\")` (et `panneauScan(…)` pour les scans)\n   exécutés via `document.write`. C'est la seule source de vérité.\n2. **`episodes.js` n'a pas de forme canonique.** On croise `var eps2` déclaré\n   avant `var eps1`, `eps1` absent, une déclaration entière sur une seule ligne,\n   des virgules traînantes. `videos.js` **échange** ensuite `eps1` et `eps2` à\n   l'affichage ; ce module conserve la numérotation brute du fichier\n   (`Lecteur.index`) et nomme les lecteurs dans l'ordre croissant (`Lecteur.nom`).\n3. **Les libellés d'épisodes sont calculés.** La page appelle `resetListe()`,\n   `creerListe(debut, fin)`, `newSP(n)`, `newSPF(\"nom libre\")`, `finirListe(debut)`.\n   Le dernier `resetListe()` gagne : le bloc par défaut est souvent suivi d'un\n   second bloc (films). Un gabarit **commenté** contient les mêmes appels — les\n   commentaires sont retirés avant analyse, sans casser les `//` des URLs.\n4. **Les drapeaux de langue ne disent rien.** Le gabarit imprime les dix\n   drapeaux, tous `hidden` ; c'est `videos.js` qui sonde `../<langue>` en HTTP\n   pour révéler ceux qui existent. Seul `listerLangues()` dit la vérité.\n\n### Hébergeurs rencontrés\n\n`ansembed.net`, `lpayer.embed4me.com`, `video.sibnet.ru`, `sendvid.com`,\n`movearnpre.com`, `oneupload.to`, `s22.anime-sama.fr` (mp4 direct),\n`www.youtube.com/embed`, `www.dailymotion.com/embed`, `vidmoly`, `myvi.top`.\n\n## CLI\n\n```bash\nbxc animesama search <requête>        # recherche instantanée (POST fetch.php)\nbxc animesama info <slug|url>         # fiche + saisons déclarées\nbxc animesama seasons <slug>          # saisons + langues réellement publiées\nbxc animesama episodes <slug>         # épisodes d'une saison (--season/--lang)\nbxc animesama resolve <url-embed>     # embed → flux direct (+ variantes HLS)\n```\n\nOptions : `--season <dossier>` (défaut `saison1`, accepte `film`, `oav`…),\n`--lang <code>` (`vostfr` par défaut ; `vf`, `va`, `var`, `vkr`, `vcn`, `vqc`,\n`vf1`, `vf2`), `--profile static|fast|http|stealth|max` (défaut `static`),\n`--timeout <ms>`.\n\n```bash\n$ bxc animesama search inazuma\n[{ \"slug\": \"inazuma-eleven\", \"titre\": \"Inazuma Eleven\", … }]\n\n$ bxc animesama seasons inazuma-eleven\n{ \"slug\": \"inazuma-eleven\", \"saisons\": [{ \"saison\": \"saison1\", \"noms\": [\"Saison 1\"], \"langues\": [\"vf\"] }, …] }\n\n$ bxc animesama episodes inazuma-eleven --season saison1 --lang vf\n{ \"titre\": \"Inazuma Eleven\", \"libelle\": \"Saison 1\", \"lecteurs\": [ … ], \"episodes\": [ … ] }\n\n$ bxc animesama resolve \"https://video.sibnet.ru/shell.php?videoid=4826196\"\n{ \"hebergeur\": \"sibnet\", \"type\": \"mp4\", \"url\": \"https://video.sibnet.ru/v/…/4826196.mp4\" }\n```\n\n## API\n\n```ts\nimport { AnimesamaScraper } from \"@aphrody/animesama\";\n\nconst as = new AnimesamaScraper();               // profile \"static\" par défaut\n\nconst resultats = await as.rechercher(\"inazuma\");\nconst fiche = await as.getAnime(\"inazuma-eleven\");\nconsole.log(fiche.titre, fiche.saisons.map((s) => s.nom));\n\nconst langues = await as.listerLangues(\"inazuma-eleven\", \"saison1\");  // [\"vf\"]\n\nconst saison = await as.getSaison(\"inazuma-eleven\", \"saison1\", \"vf\");\nconsole.log(saison.episodes.length);             // 26\nconsole.log(saison.episodes[0].lecteurs);        // [{ hebergeur: \"youtube\", … }, …]\n\nconst source = await as.resoudreLecteur(saison.episodes[0].lecteurs[1], {\n  enumererQualites: true,\n});\nconsole.log(source.type, source.url, source.enTetes.Referer);\n\nawait as.close();\n```\n\n### Surface exportée\n\n| Symbole | Rôle |\n| --- | --- |\n| `AnimesamaScraper` | client haut niveau (défaut du module) |\n| `AnimesamaOptions` | `profile`, `baseUrl`, `timeoutMs`, `retries`, `transport` |\n| `rechercher` / `parcourirCatalogue` | recherche instantanée / catalogue paginé |\n| `getAnime` / `getAnimeComplet` | fiche seule / fiche + toutes ses saisons |\n| `getSaison` / `getEpisodesJs` / `listerLangues` | une saison, son `episodes.js` brut, ses langues sondées |\n| `resoudreLecteur` / `enumererQualitesHls` | embed → flux direct, variantes d'un master HLS |\n| `parserFicheAnime`, `parserSaisonsDeclarees`, `parserLecteurs`, `parserNomsEpisodes`, `parserSaison`, `parserResultatsRecherche`, `parserCartesCatalogue`, `parserDrapeauxLangues`, `composerEpisodes` | analyseurs **purs** (chaîne → objets), utilisables sur un miroir |\n| `hebergeurDepuisUrl`, `normaliserUrlLecteur`, `chercherMedia`, `classerMedia`, `deballerPacker`, `numeroDepuisNom`, `estLangue`, `texteBrut`, `retirerCommentairesJs` | utilitaires |\n| `FicheAnime`, `SaisonRef`, `SaisonAnimesama`, `EpisodeAnimesama`, `Lecteur`, `LecteurEpisode`, `SourceResolue`, `QualiteMedia`, `ResultatRecherche`, `LangueAnimesama`, `LANGUES_ANIMESAMA` | types publics |\n\n### Transport injectable\n\n`AnimesamaScraper` n'ouvre une page bxc que si aucun `transport` n'est fourni.\nEn injecter un permet de brancher un cache, un miroir — ou des fixtures :\n\n```ts\nconst scraper = new AnimesamaScraper({\n  transport: async ({ url }) => ({ status: 200, corps: htmlDeFixture[url] }),\n});\n```\n\nLe transport par défaut (`creerTransportBxc`) fait les `GET` via une page bxc\n(`profile`, `static` = zéro spawn) et le seul `POST` — la recherche — via `fetch`,\npuisque `page.goto()` n'envoie pas de corps.\n\n## Tests\n\n```bash\nbun test packages/animesama          # 47 cas, aucun accès réseau\n```\n\nLes fixtures de `test/fixtures/` sont des extraits **réels** capturés sur\nanime-sama.to (fiche Inazuma Eleven, `episodes.js` de la saison 1 VF, `episodes.js`\nde One Piece saison 1 VOSTFR pour le cas `eps2` avant `eps1`, bloc de liste des\nfilms de Dragon Ball Super, résultats de `fetch.php`, cartes de catalogue, embeds\nansembed et sibnet).\n\n## Limites connues\n\n- **Cloudflare** est devant le site. Les requêtes simples passent aujourd'hui\n  avec un User-Agent de navigateur (`profile: \"static\"`) ; en cas de challenge,\n  escalader vers `--profile fast` ou `stealth`.\n- **YouTube et Dailymotion** ne sont pas résolus : ce sont des lecteurs\n  propriétaires, pas des embeds de fichier. `resoudreLecteur` le signale sans\n  émettre de requête.\n- **Les URLs résolues sont signées et éphémères** (jeton `t=` + `e=` chez\n  ansembed / embed4me) et exigent l'en-tête `Referer` renvoyé dans `enTetes`.\n- Les hébergeurs fortement obfusqués (voe, mail.ru et clones) ne sont pas\n  couverts : ils demanderaient un rendu JS.\n- **Le sondage des langues coûte une requête par langue et par saison**\n  (`listerLangues` : 9 langues × N saisons). Restreindre la liste quand on sait\n  déjà ce qu'on cherche.\n\n## Résolution des lecteurs\n\nLa reconnaissance de l'hébergeur, le déballage des scripts compressés,\nl'extraction de la piste et la lecture des playlists HLS ne vivent plus dans ce\npaquet : ils sont dans le **cœur média de bxc** (`@aphrody/bxc/media`), partagé\navec `@aphrody/voiranime`. Les mêmes hébergeurs servent les deux sites — un\ndomaine qui change ou une page qui bouge se corrige à un seul endroit.\n\nCe paquet ne garde que la traduction du résultat dans son vocabulaire\n(`hebergeur`, `enTetes`, `qualites`) et la connaissance propre à anime-sama :\n`episodes.js`, `panneauAnime`, la numérotation des lecteurs, les langues.\n\n`resoudreLecteur` rend donc, en plus de l'URL : les en-têtes `Referer`/`Origin`\nà rejouer, l'aperçu, les variantes du master HLS en **URL absolues** (deux\nqualités de même hauteur sont départagées par leur débit), et une erreur\nexplicite quand le lecteur est propriétaire (YouTube, Dailymotion) ou obfusqué.\n","readmeFilename":"README.md"}