mirror of
https://github.com/sussy-code/providers.git
synced 2026-08-20 14:06:48 +00:00
add iosmirrors (netflix and prime)
This commit is contained in:
parent
0833880c14
commit
e338d85aef
3 changed files with 270 additions and 0 deletions
|
|
@ -85,6 +85,8 @@ import { embedsuScraper } from './sources/embedsu';
|
|||
import { FedAPIScraper } from './sources/fedapi';
|
||||
import { goojaraScraper } from './sources/goojara';
|
||||
import { hdRezkaScraper } from './sources/hdrezka';
|
||||
import { iosmirrorScraper } from './sources/iosmirror';
|
||||
import { iosmirrorPVScraper } from './sources/iosmirrorpv';
|
||||
import { m4uScraper } from './sources/m4ufree';
|
||||
import { nepuScraper } from './sources/nepu';
|
||||
import { nitesScraper } from './sources/nites';
|
||||
|
|
@ -138,6 +140,8 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
vidlinkScraper,
|
||||
FedAPIScraper,
|
||||
slidemoviesScraper,
|
||||
iosmirrorScraper,
|
||||
iosmirrorPVScraper,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
135
src/providers/sources/iosmirror.ts
Normal file
135
src/providers/sources/iosmirror.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { compareTitle } from '@/utils/compare';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { makeCookieHeader } from '@/utils/cookie';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
// thanks @TPN for this
|
||||
// See how to set this up yourself: https://gist.github.com/Pasithea0/9ba31d16580800e899c245a4379e902b
|
||||
|
||||
const baseUrl = 'https://iosmirror.cc';
|
||||
const baseUrl2 = 'https://m3u8-proxy.pstream.org/iosmirror.cc:443';
|
||||
|
||||
type metaT = {
|
||||
year: string;
|
||||
type: 'm' | 't';
|
||||
season: { s: string; id: string; ep: string }[];
|
||||
};
|
||||
|
||||
type searchT = { status: 'y' | 'n'; searchResult?: { id: string; t: string }[]; error: string };
|
||||
|
||||
type episodeT = { episodes: { id: string; s: string; ep: string }[]; nextPageShow: number };
|
||||
|
||||
const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> => {
|
||||
const hash = decodeURIComponent(await ctx.proxiedFetcher('https://iosmirror-hash.pstream.org/'));
|
||||
ctx.progress(10);
|
||||
|
||||
const searchRes = await ctx.proxiedFetcher<searchT>('/search.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { s: ctx.media.title },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
if (searchRes.status !== 'y' || !searchRes.searchResult) throw new NotFoundError(searchRes.error);
|
||||
|
||||
async function getMeta(id: string) {
|
||||
return ctx.proxiedFetcher<metaT>('/post.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { id },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
}
|
||||
ctx.progress(30);
|
||||
|
||||
let metaRes: metaT | undefined;
|
||||
|
||||
// todo: use promise.alp
|
||||
let id = searchRes.searchResult.find(async (x) => {
|
||||
metaRes = await getMeta(x.id);
|
||||
return (
|
||||
compareTitle(x.t, ctx.media.title) &&
|
||||
(Number(metaRes.year) === ctx.media.releaseYear || metaRes.type === (ctx.media.type === 'movie' ? 'm' : 't'))
|
||||
);
|
||||
})?.id;
|
||||
if (!id) throw new NotFoundError('No watchable item found');
|
||||
|
||||
if (ctx.media.type === 'show') {
|
||||
metaRes = await getMeta(id); // shouldnt need this, idunno why it doesnt work without this
|
||||
const showMedia = ctx.media;
|
||||
|
||||
const seasonId = metaRes?.season.find((x) => Number(x.s) === showMedia.season.number)?.id;
|
||||
if (!seasonId) throw new NotFoundError('Season not available');
|
||||
|
||||
const episodeRes = await ctx.proxiedFetcher<episodeT>('/episodes.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { s: seasonId, series: id },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
|
||||
let episodes = [...episodeRes.episodes];
|
||||
let currentPage = 2;
|
||||
while (episodeRes.nextPageShow === 1) {
|
||||
const nextPageRes = await ctx.proxiedFetcher<episodeT>('/episodes.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { s: seasonId, series: id, page: currentPage.toString() },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
|
||||
episodes = [...episodes, ...nextPageRes.episodes];
|
||||
episodeRes.nextPageShow = nextPageRes.nextPageShow;
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
const episodeId = episodes.find(
|
||||
(x) => x.ep === `E${showMedia.episode.number}` && x.s === `S${showMedia.season.number}`,
|
||||
)?.id;
|
||||
if (!episodeId) throw new NotFoundError('Episode not available');
|
||||
|
||||
id = episodeId;
|
||||
}
|
||||
|
||||
const playlistRes: { sources: { file: string; label: string }[] }[] = await ctx.proxiedFetcher('/playlist.php?', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { id },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
ctx.progress(50);
|
||||
|
||||
let autoFile = playlistRes[0].sources.find((source) => source.label === 'Auto')?.file;
|
||||
if (!autoFile) {
|
||||
autoFile = playlistRes[0].sources.find((source) => source.label === 'Full HD')?.file;
|
||||
}
|
||||
if (!autoFile) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('"Full HD" or "Auto" file not found, falling back to first source');
|
||||
autoFile = playlistRes[0].sources[0].file;
|
||||
}
|
||||
|
||||
if (!autoFile) throw new Error('Failed to fetch playlist');
|
||||
|
||||
const playlist = `https://m3u8-proxy.pstream.org/m3u8-proxy?url=${encodeURIComponent(`${baseUrl}${autoFile}`)}&headers=${encodeURIComponent(JSON.stringify({ referer: baseUrl, cookie: makeCookieHeader({ hd: 'on' }) }))}`;
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
playlist,
|
||||
type: 'hls',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const iosmirrorScraper = makeSourcerer({
|
||||
id: 'iosmirror',
|
||||
name: 'NetMirror',
|
||||
rank: 182,
|
||||
disabled: false,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: universalScraper,
|
||||
scrapeShow: universalScraper,
|
||||
});
|
||||
131
src/providers/sources/iosmirrorpv.ts
Normal file
131
src/providers/sources/iosmirrorpv.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { compareTitle } from '@/utils/compare';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { makeCookieHeader } from '@/utils/cookie';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
// thanks @TPN for this
|
||||
// See how to set this up yourself: https://gist.github.com/Pasithea0/9ba31d16580800e899c245a4379e902b
|
||||
|
||||
const baseUrl = 'https://iosmirror.cc';
|
||||
const baseUrl2 = 'https://m3u8-proxy.pstream.org/iosmirror.cc:443/pv';
|
||||
|
||||
type metaT = {
|
||||
year: string;
|
||||
type: 'm' | 't';
|
||||
season: { s: string; id: string; ep: string }[];
|
||||
};
|
||||
|
||||
type searchT = { searchResult?: { id: string; t: string; y: string }[]; error: string };
|
||||
|
||||
type episodeT = { episodes: { id: string; s: string; ep: string }[]; nextPageShow: number };
|
||||
|
||||
const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> => {
|
||||
const hash = decodeURIComponent(await ctx.proxiedFetcher('https://iosmirror-hash.pstream.org/'));
|
||||
ctx.progress(10);
|
||||
|
||||
const searchRes = await ctx.proxiedFetcher<searchT>('/search.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { s: ctx.media.title },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
if (!searchRes.searchResult) throw new NotFoundError(searchRes.error);
|
||||
|
||||
async function getMeta(id: string) {
|
||||
return ctx.proxiedFetcher<metaT>('/post.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { id },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
}
|
||||
ctx.progress(30);
|
||||
|
||||
let id = searchRes.searchResult.find(async (x) => {
|
||||
const metaRes = await getMeta(x.id);
|
||||
return (
|
||||
compareTitle(x.t, ctx.media.title) &&
|
||||
(Number(x.y) === ctx.media.releaseYear || metaRes.type === (ctx.media.type === 'movie' ? 'm' : 't'))
|
||||
);
|
||||
})?.id;
|
||||
if (!id) throw new NotFoundError('No watchable item found');
|
||||
|
||||
if (ctx.media.type === 'show') {
|
||||
const metaRes = await getMeta(id);
|
||||
const showMedia = ctx.media;
|
||||
|
||||
const seasonId = metaRes?.season.find((x) => Number(x.s) === showMedia.season.number)?.id;
|
||||
if (!seasonId) throw new NotFoundError('Season not available');
|
||||
|
||||
const episodeRes = await ctx.proxiedFetcher<episodeT>('/episodes.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { s: seasonId, series: id },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
|
||||
let episodes = [...episodeRes.episodes];
|
||||
let currentPage = 2;
|
||||
while (episodeRes.nextPageShow === 1) {
|
||||
const nextPageRes = await ctx.proxiedFetcher<episodeT>('/episodes.php', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { s: seasonId, series: id, page: currentPage.toString() },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
|
||||
episodes = [...episodes, ...nextPageRes.episodes];
|
||||
episodeRes.nextPageShow = nextPageRes.nextPageShow;
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
const episodeId = episodes.find(
|
||||
(x) => x.ep === `E${showMedia.episode.number}` && x.s === `S${showMedia.season.number}`,
|
||||
)?.id;
|
||||
if (!episodeId) throw new NotFoundError('Episode not available');
|
||||
|
||||
id = episodeId;
|
||||
}
|
||||
|
||||
const playlistRes: { sources: { file: string; label: string }[] }[] = await ctx.proxiedFetcher('/playlist.php?', {
|
||||
baseUrl: baseUrl2,
|
||||
query: { id },
|
||||
headers: { cookie: makeCookieHeader({ t_hash_t: hash, hd: 'on' }) },
|
||||
});
|
||||
ctx.progress(50);
|
||||
|
||||
let autoFile = playlistRes[0].sources.find((source) => source.label === 'Auto')?.file;
|
||||
if (!autoFile) {
|
||||
autoFile = playlistRes[0].sources.find((source) => source.label === 'Full HD')?.file;
|
||||
}
|
||||
if (!autoFile) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('"Full HD" or "Auto" file not found, falling back to first source');
|
||||
autoFile = playlistRes[0].sources[0].file;
|
||||
}
|
||||
|
||||
if (!autoFile) throw new Error('Failed to fetch playlist');
|
||||
|
||||
const playlist = `https://m3u8-proxy.pstream.org/m3u8-proxy?url=${encodeURIComponent(`${baseUrl}${autoFile}`)}&headers=${encodeURIComponent(JSON.stringify({ referer: baseUrl, cookie: makeCookieHeader({ hd: 'on' }) }))}`;
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
playlist,
|
||||
type: 'hls',
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const iosmirrorPVScraper = makeSourcerer({
|
||||
id: 'iosmirrorpv',
|
||||
name: 'PrimeMirror',
|
||||
rank: 183,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: universalScraper,
|
||||
scrapeShow: universalScraper,
|
||||
});
|
||||
Loading…
Reference in a new issue