[add] animeflv

This commit is contained in:
MoonPic 2025-05-18 01:38:20 +00:00 committed by Pas
parent dc7f11d489
commit b056cade35
4 changed files with 152 additions and 33 deletions

View file

@ -47,9 +47,14 @@ import {
import { FedAPIPrivateScraper, FedDBScraper } from './embeds/fedapi';
import { mp4hydraServer1Scraper, mp4hydraServer2Scraper } from './embeds/mp4hydra';
import { ridooScraper } from './embeds/ridoo';
import { streamtapeScraper } from './embeds/streamtape';
import { streamtapeLatinoScraper, streamtapeScraper } from './embeds/streamtape';
import { streamvidScraper } from './embeds/streamvid';
import { streamwishEnglishScraper, streamwishLatinoScraper, streamwishSpanishScraper } from './embeds/streamwish';
import {
streamwishEnglishScraper,
streamwishJapaneseScraper,
streamwishLatinoScraper,
streamwishSpanishScraper,
} from './embeds/streamwish';
import { vidCloudScraper } from './embeds/vidcloud';
import {
VidsrcsuServer10Scraper,
@ -82,6 +87,7 @@ import {
} from './embeds/xprime';
import { oneServerScraper } from './sources/1server';
import { EightStreamScraper } from './sources/8stream';
import { animeflvScraper } from './sources/animeflv';
import { coitusScraper } from './sources/coitus';
import { ConsumetScraper } from './sources/consumet';
import { cuevana3Scraper } from './sources/cuevana3';
@ -135,6 +141,7 @@ export function gatherAllSources(): Array<Sourcerer> {
hianimeScraper,
oneServerScraper,
wecimaScraper,
animeflvScraper,
];
}
@ -204,8 +211,10 @@ export function gatherAllEmbeds(): Array<Embed> {
oneServerHianimeEmbed,
oneServerAnimepaheEmbed,
oneServerAnizoneEmbed,
streamwishJapaneseScraper,
streamwishLatinoScraper,
streamwishSpanishScraper,
streamwishEnglishScraper,
streamtapeLatinoScraper,
];
}

View file

@ -1,39 +1,56 @@
import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '@/providers/base';
export const streamtapeScraper = makeEmbed({
id: 'streamtape',
name: 'Streamtape',
rank: 160,
async scrape(ctx) {
const embed = await ctx.proxiedFetcher<string>(ctx.url);
const providers = [
{
id: 'streamtape',
name: 'Streamtape',
rank: 160,
},
{
id: 'streamtape-latino',
name: 'Streamtape (Latino)',
rank: 159,
},
];
const match = embed.match(/robotlink'\).innerHTML = (.*)'/);
if (!match) throw new Error('No match found');
function embed(provider: { id: string; name: string; rank: number }) {
return makeEmbed({
id: provider.id,
name: provider.name,
rank: provider.rank,
async scrape(ctx) {
const embedHtml = await ctx.proxiedFetcher<string>(ctx.url);
const [fh, sh] = match?.[1]?.split("+ ('") ?? [];
if (!fh || !sh) throw new Error('No match found');
const match = embedHtml.match(/robotlink'\).innerHTML = (.*)'/);
if (!match) throw new Error('No match found');
const url = `https:${fh?.replace(/'/g, '').trim()}${sh?.substring(3).trim()}`;
const [fh, sh] = match?.[1]?.split("+ ('") ?? [];
if (!fh || !sh) throw new Error('No match found');
return {
stream: [
{
id: 'primary',
type: 'file',
flags: [flags.CORS_ALLOWED, flags.IP_LOCKED],
captions: [],
qualities: {
unknown: {
type: 'mp4',
url,
const url = `https:${fh?.replace(/'/g, '').trim()}${sh?.substring(3).trim()}`;
return {
stream: [
{
id: 'primary',
type: 'file',
flags: [flags.CORS_ALLOWED, flags.IP_LOCKED],
captions: [],
qualities: {
unknown: {
type: 'mp4',
url,
},
},
headers: {
Referer: 'https://streamtape.com',
},
},
headers: {
Referer: 'https://streamtape.com',
},
},
],
};
},
});
],
};
},
});
}
export const [streamtapeScraper, streamtapeLatinoScraper] = providers.map(embed);

View file

@ -4,6 +4,11 @@ import { makeEmbed } from '@/providers/base';
import { NotFoundError } from '@/utils/errors';
const providers = [
{
id: 'streamwish-japanese',
name: 'StreamWish (Japones Sub Español)',
rank: 171,
},
{
id: 'streamwish-latino',
name: 'StreamWish (Latino)',
@ -58,4 +63,5 @@ function embed(provider: { id: string; name: string; rank: number }) {
});
}
export const [streamwishLatinoScraper, streamwishSpanishScraper, streamwishEnglishScraper] = providers.map(embed);
export const [streamwishJapaneseScraper, streamwishLatinoScraper, streamwishSpanishScraper, streamwishEnglishScraper] =
providers.map(embed);

View file

@ -0,0 +1,87 @@
/* eslint-disable no-console */
import { flags } from '@/entrypoint/utils/targets';
import { SourcererOutput, makeSourcerer } from '@/providers/base';
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
import { NotFoundError } from '@/utils/errors';
const apiBase = 'https://ws-m3u8.moonpic.qzz.io:3006';
async function searchAnimeFlvAPI(title: string): Promise<string> {
const res = await fetch(`${apiBase}/search?title=${encodeURIComponent(title)}`);
if (!res.ok) throw new NotFoundError('Anime not found in API');
const data = await res.json();
if (!data.url) throw new NotFoundError('Anime not found in API');
return data.url;
}
async function getEpisodesAPI(animeUrl: string): Promise<{ number: number; url: string }[]> {
const res = await fetch(`${apiBase}/episodes?url=${encodeURIComponent(animeUrl)}`);
if (!res.ok) throw new NotFoundError('Episodes not found in API');
const data = await res.json();
if (!data.episodes) throw new NotFoundError('Episodes not found in API');
return data.episodes;
}
async function getEmbedsAPI(episodeUrl: string): Promise<Record<string, string>> {
const res = await fetch(`${apiBase}/embeds?episodeUrl=${encodeURIComponent(episodeUrl)}`);
if (!res.ok) throw new NotFoundError('No embed found for this content');
const data = await res.json();
return data;
}
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
const title = ctx.media.title;
if (!title) throw new NotFoundError('Missing title');
// Search anime/movie by title using the API
const animeUrl = await searchAnimeFlvAPI(title);
// Get episodes using the API
const episodes = await getEpisodesAPI(animeUrl);
let episodeUrl = animeUrl;
if (ctx.media.type === 'show') {
const episode = ctx.media.episode?.number;
if (!episode) throw new NotFoundError('Missing episode data');
const ep = episodes.find((e) => e.number === episode);
if (!ep) throw new NotFoundError('Episode not found');
episodeUrl = ep.url;
} else if (ctx.media.type === 'movie') {
// For movies, use the first episode (by convention)
const ep = episodes.find((e) => e.number === 1) || episodes[0];
if (!ep) throw new NotFoundError('Movie episode not found');
episodeUrl = ep.url;
}
// Get all embeds using the API
const embedsData = await getEmbedsAPI(episodeUrl);
const embeds = [];
if (embedsData['streamwish-japanese']) {
embeds.push({
embedId: 'streamwish-japanese',
url: embedsData['streamwish-japanese'],
});
}
if (embedsData['streamtape-latino']) {
embeds.push({
embedId: 'streamtape-latino',
url: embedsData['streamtape-latino'],
});
}
if (embeds.length === 0) throw new NotFoundError('No valid embed found for this content');
return { embeds };
}
export const animeflvScraper = makeSourcerer({
id: 'animeflv',
name: 'AnimeFLV',
rank: 90,
disabled: false,
flags: [flags.CORS_ALLOWED],
scrapeShow: comboScraper,
scrapeMovie: comboScraper,
});