mirror of
https://github.com/p-stream/providers.git
synced 2026-05-21 19:52:30 +00:00
update zoro and consumet
This commit is contained in:
parent
640c82ba52
commit
4487e9e818
3 changed files with 100 additions and 64 deletions
|
|
@ -4,7 +4,7 @@ import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
export const hianimeHd1DubEmbed = makeEmbed({
|
export const hianimeHd1DubEmbed = makeEmbed({
|
||||||
id: 'hianime-hd1-dub',
|
id: 'hianime-hd1-dub',
|
||||||
name: 'Hianime HD-1 (Dub)',
|
name: 'HD-1 (Dub)',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
rank: 250,
|
rank: 250,
|
||||||
async scrape(ctx): Promise<EmbedOutput> {
|
async scrape(ctx): Promise<EmbedOutput> {
|
||||||
|
|
@ -42,7 +42,7 @@ export const hianimeHd1DubEmbed = makeEmbed({
|
||||||
|
|
||||||
export const hianimeHd2DubEmbed = makeEmbed({
|
export const hianimeHd2DubEmbed = makeEmbed({
|
||||||
id: 'hianime-hd2-dub',
|
id: 'hianime-hd2-dub',
|
||||||
name: 'Hianime HD-2 (Dub)',
|
name: 'HD-2 (Dub)',
|
||||||
rank: 251,
|
rank: 251,
|
||||||
async scrape(ctx): Promise<EmbedOutput> {
|
async scrape(ctx): Promise<EmbedOutput> {
|
||||||
const query = JSON.parse(ctx.url);
|
const query = JSON.parse(ctx.url);
|
||||||
|
|
@ -55,11 +55,34 @@ export const hianimeHd2DubEmbed = makeEmbed({
|
||||||
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
stream: [
|
||||||
|
{
|
||||||
|
type: 'hls',
|
||||||
|
id: 'primary',
|
||||||
|
playlist: `https://proxy-m3u8.uira.live/m3u8-proxy?url=${data.data.sources[0].url}&headers=${encodeURIComponent(JSON.stringify(data.data.headers))}`,
|
||||||
|
flags: [flags.CORS_ALLOWED],
|
||||||
|
captions: [],
|
||||||
|
...(thumbnailTrack
|
||||||
|
? {
|
||||||
|
thumbnailTrack: {
|
||||||
|
type: 'vtt',
|
||||||
|
url: thumbnailTrack,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
// headers: data.data.headers,
|
||||||
|
},
|
||||||
|
],
|
||||||
// stream: [
|
// stream: [
|
||||||
// {
|
// {
|
||||||
// type: 'hls',
|
// type: 'file',
|
||||||
// id: 'primary',
|
// id: 'primary',
|
||||||
// playlist: data.data.sources[0].url,
|
// qualities: {
|
||||||
|
// unknown: {
|
||||||
|
// type: 'mp4',
|
||||||
|
// url: data.data.sources[0].url,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
// flags: [flags.CORS_ALLOWED],
|
// flags: [flags.CORS_ALLOWED],
|
||||||
// captions: [],
|
// captions: [],
|
||||||
// ...(thumbnailTrack
|
// ...(thumbnailTrack
|
||||||
|
|
@ -73,36 +96,13 @@ export const hianimeHd2DubEmbed = makeEmbed({
|
||||||
// headers: data.data.headers,
|
// headers: data.data.headers,
|
||||||
// },
|
// },
|
||||||
// ],
|
// ],
|
||||||
stream: [
|
|
||||||
{
|
|
||||||
type: 'file',
|
|
||||||
id: 'primary',
|
|
||||||
qualities: {
|
|
||||||
unknown: {
|
|
||||||
type: 'mp4',
|
|
||||||
url: data.data.sources[0].url,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
flags: [flags.CORS_ALLOWED],
|
|
||||||
captions: [],
|
|
||||||
...(thumbnailTrack
|
|
||||||
? {
|
|
||||||
thumbnailTrack: {
|
|
||||||
type: 'vtt',
|
|
||||||
url: thumbnailTrack,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
headers: data.data.headers,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const hianimeHd1SubEmbed = makeEmbed({
|
export const hianimeHd1SubEmbed = makeEmbed({
|
||||||
id: 'hianime-hd1-sub',
|
id: 'hianime-hd1-sub',
|
||||||
name: 'Hianime HD-1 (Sub)',
|
name: 'HD-1 (Sub)',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
rank: 252,
|
rank: 252,
|
||||||
async scrape(ctx): Promise<EmbedOutput> {
|
async scrape(ctx): Promise<EmbedOutput> {
|
||||||
|
|
@ -140,7 +140,7 @@ export const hianimeHd1SubEmbed = makeEmbed({
|
||||||
|
|
||||||
export const hianimeHd2SubEmbed = makeEmbed({
|
export const hianimeHd2SubEmbed = makeEmbed({
|
||||||
id: 'hianime-hd2-sub',
|
id: 'hianime-hd2-sub',
|
||||||
name: 'Hianime HD-2 (Sub)',
|
name: 'HD-2 (Sub)',
|
||||||
rank: 253,
|
rank: 253,
|
||||||
async scrape(ctx): Promise<EmbedOutput> {
|
async scrape(ctx): Promise<EmbedOutput> {
|
||||||
const query = JSON.parse(ctx.url);
|
const query = JSON.parse(ctx.url);
|
||||||
|
|
@ -153,11 +153,34 @@ export const hianimeHd2SubEmbed = makeEmbed({
|
||||||
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
const thumbnailTrack = data.data.tracks?.find((track: { kind: string }) => track.kind === 'thumbnails')?.file;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
stream: [
|
||||||
|
{
|
||||||
|
type: 'hls',
|
||||||
|
id: 'primary',
|
||||||
|
playlist: `https://proxy-m3u8.uira.live/m3u8-proxy?url=${data.data.sources[0].url}&headers=${encodeURIComponent(JSON.stringify(data.data.headers))}`,
|
||||||
|
flags: [flags.CORS_ALLOWED],
|
||||||
|
captions: [],
|
||||||
|
...(thumbnailTrack
|
||||||
|
? {
|
||||||
|
thumbnailTrack: {
|
||||||
|
type: 'vtt',
|
||||||
|
url: thumbnailTrack,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
// headers: data.data.headers,
|
||||||
|
},
|
||||||
|
],
|
||||||
// stream: [
|
// stream: [
|
||||||
// {
|
// {
|
||||||
// type: 'hls',
|
// type: 'file',
|
||||||
// id: 'primary',
|
// id: 'primary',
|
||||||
// playlist: data.data.sources[0].url,
|
// qualities: {
|
||||||
|
// unknown: {
|
||||||
|
// type: 'mp4',
|
||||||
|
// url: data.data.sources[0].url,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
// flags: [flags.CORS_ALLOWED],
|
// flags: [flags.CORS_ALLOWED],
|
||||||
// captions: [],
|
// captions: [],
|
||||||
// ...(thumbnailTrack
|
// ...(thumbnailTrack
|
||||||
|
|
@ -171,29 +194,6 @@ export const hianimeHd2SubEmbed = makeEmbed({
|
||||||
// headers: data.data.headers,
|
// headers: data.data.headers,
|
||||||
// },
|
// },
|
||||||
// ],
|
// ],
|
||||||
stream: [
|
|
||||||
{
|
|
||||||
type: 'file',
|
|
||||||
id: 'primary',
|
|
||||||
qualities: {
|
|
||||||
unknown: {
|
|
||||||
type: 'mp4',
|
|
||||||
url: data.data.sources[0].url,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
flags: [flags.CORS_ALLOWED],
|
|
||||||
captions: [],
|
|
||||||
...(thumbnailTrack
|
|
||||||
? {
|
|
||||||
thumbnailTrack: {
|
|
||||||
type: 'vtt',
|
|
||||||
url: thumbnailTrack,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
headers: data.data.headers,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ async function consumetScraper(ctx: ShowScrapeContext): Promise<SourcererOutput>
|
||||||
|
|
||||||
export const ConsumetScraper = makeSourcerer({
|
export const ConsumetScraper = makeSourcerer({
|
||||||
id: 'consumet',
|
id: 'consumet',
|
||||||
name: 'Zoro (Anime)',
|
name: 'Consumet (Anime)',
|
||||||
rank: 4,
|
rank: 4,
|
||||||
flags: [flags.CORS_ALLOWED],
|
flags: [flags.CORS_ALLOWED],
|
||||||
scrapeShow: consumetScraper,
|
scrapeShow: consumetScraper,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
/* eslint-disable no-console */
|
||||||
import { flags } from '@/entrypoint/utils/targets';
|
import { flags } from '@/entrypoint/utils/targets';
|
||||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||||
import { ShowScrapeContext } from '@/utils/context';
|
import { ShowScrapeContext } from '@/utils/context';
|
||||||
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
interface HianimeSearchResult {
|
interface HianimeSearchResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|
@ -32,25 +34,59 @@ async function searchAnime(title: string): Promise<string> {
|
||||||
throw new Error('Anime not found');
|
throw new Error('Anime not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the ID of the first matching anime
|
// Try to find exact match (case-insensitive)
|
||||||
return data.data.animes[0].id;
|
const match = data.data.animes.find((anime) => anime.name.toLowerCase() === title.toLowerCase());
|
||||||
|
|
||||||
|
// Return the matched ID or fallback to the first result
|
||||||
|
return match?.id ?? data.data.animes[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchTmdbSeasonEpisodes(tmdbShowId: string, seasonNumber: number): Promise<any[]> {
|
||||||
|
const apiKey = '5b9790d9305dca8713b9a0afad42ea8d'; // plz dont abuse
|
||||||
|
const response = await fetch(
|
||||||
|
`https://api.themoviedb.org/3/tv/${tmdbShowId}/season/${seasonNumber}?api_key=${apiKey}`,
|
||||||
|
);
|
||||||
|
if (!response.ok) throw new NotFoundError('Failed to fetch season data from TMDB');
|
||||||
|
const data = await response.json();
|
||||||
|
return data.episodes; // each item has 'episode_number' and 'season_number'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function calculateAbsoluteEpisodeNumber(
|
||||||
|
tmdbShowId: string,
|
||||||
|
seasonNumber: number,
|
||||||
|
episodeNumber: number,
|
||||||
|
): Promise<number> {
|
||||||
|
const previousSeasons = await Promise.all(
|
||||||
|
Array.from({ length: seasonNumber - 1 }, (_, i) => fetchTmdbSeasonEpisodes(tmdbShowId, i + 1)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const episodesBefore = previousSeasons.reduce((sum, seasonEpisodes) => sum + seasonEpisodes.length, 0);
|
||||||
|
|
||||||
|
return episodesBefore + episodeNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchEpisodeData(animeId: string): Promise<HianimeEpisodeResult> {
|
async function fetchEpisodeData(animeId: string): Promise<HianimeEpisodeResult> {
|
||||||
const response = await fetch(`https://hianime.pstream.org/api/v2/hianime/anime/${animeId}/episodes`);
|
const response = await fetch(`https://hianime.pstream.org/api/v2/hianime/anime/${animeId}/episodes`);
|
||||||
if (!response.ok) throw new Error('Failed to fetch episode data');
|
if (!response.ok) throw new NotFoundError('Failed to fetch episode data');
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function comboScraper(ctx: ShowScrapeContext): Promise<SourcererOutput> {
|
async function comboScraper(ctx: ShowScrapeContext): Promise<SourcererOutput> {
|
||||||
// First, search for the anime to get its Hianime ID
|
|
||||||
const animeId = await searchAnime(ctx.media.title);
|
const animeId = await searchAnime(ctx.media.title);
|
||||||
|
// console.log(animeId);
|
||||||
|
|
||||||
|
const absoluteEp = await calculateAbsoluteEpisodeNumber(
|
||||||
|
ctx.media.tmdbId,
|
||||||
|
ctx.media.season.number,
|
||||||
|
ctx.media.episode.number,
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log(absoluteEp);
|
||||||
|
|
||||||
// Then, get the episode data
|
|
||||||
const episodeData = await fetchEpisodeData(animeId);
|
const episodeData = await fetchEpisodeData(animeId);
|
||||||
const episode = episodeData.data.episodes.find((ep) => ep.number === ctx.media.episode.number);
|
// console.log(episodeData);
|
||||||
|
const episode = episodeData.data.episodes.find((ep) => ep.number === absoluteEp);
|
||||||
if (!episode) throw new Error('Episode not found');
|
if (!episode) throw new NotFoundError('Episode not found');
|
||||||
|
|
||||||
const embeds = [
|
const embeds = [
|
||||||
{
|
{
|
||||||
|
|
@ -84,7 +120,7 @@ async function comboScraper(ctx: ShowScrapeContext): Promise<SourcererOutput> {
|
||||||
|
|
||||||
export const hianimeScraper = makeSourcerer({
|
export const hianimeScraper = makeSourcerer({
|
||||||
id: 'hianime',
|
id: 'hianime',
|
||||||
name: 'Hianime',
|
name: 'Zoro (Anime)',
|
||||||
rank: 7,
|
rank: 7,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
flags: [flags.CORS_ALLOWED],
|
flags: [flags.CORS_ALLOWED],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue