commit
ee539bc0ed
|
@ -18,6 +18,7 @@ import { autoembedScraper } from '@/providers/sources/autoembed';
|
|||
import { catflixScraper } from '@/providers/sources/catflix';
|
||||
import { ee3Scraper } from '@/providers/sources/ee3';
|
||||
import { flixhqScraper } from '@/providers/sources/flixhq/index';
|
||||
import { fsharetvScraper } from '@/providers/sources/fsharetv';
|
||||
import { goMoviesScraper } from '@/providers/sources/gomovies/index';
|
||||
import { insertunitScraper } from '@/providers/sources/insertunit';
|
||||
import { kissAsianScraper } from '@/providers/sources/kissasian/index';
|
||||
|
@ -96,6 +97,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
tugaflixScraper,
|
||||
ee3Scraper,
|
||||
whvxScraper,
|
||||
fsharetvScraper,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import { load } from 'cheerio';
|
||||
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { FileBasedStream } from '@/providers/streams';
|
||||
import { compareMedia } from '@/utils/compare';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { getValidQualityFromString } from '@/utils/quality';
|
||||
|
||||
const baseUrl = 'https://fsharetv.co';
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const searchPage = await ctx.proxiedFetcher('/search', {
|
||||
baseUrl,
|
||||
query: {
|
||||
q: ctx.media.title,
|
||||
},
|
||||
});
|
||||
|
||||
const search$ = load(searchPage);
|
||||
const searchResults: { title: string; year?: number; url: string }[] = [];
|
||||
|
||||
search$('.movie-item').each((_, element) => {
|
||||
const [, title, year] =
|
||||
search$(element)
|
||||
.find('b')
|
||||
.text()
|
||||
?.match(/^(.*?)\s*(?:\(?\s*(\d{4})(?:\s*-\s*\d{0,4})?\s*\)?)?\s*$/) || [];
|
||||
const url = search$(element).find('a').attr('href');
|
||||
if (!title || !url) return;
|
||||
|
||||
searchResults.push({ title, year: Number(year) ?? undefined, url });
|
||||
});
|
||||
|
||||
const watchPageUrl = searchResults.find((x) => x && compareMedia(ctx.media, x.title, x.year))?.url;
|
||||
if (!watchPageUrl) throw new NotFoundError('No watchable item found');
|
||||
|
||||
const watchPage = await ctx.proxiedFetcher(watchPageUrl.replace('/movie', '/w'), { baseUrl });
|
||||
|
||||
const fileId = watchPage.match(/Movie\.setSource\('([^']*)'/)?.[1];
|
||||
if (!fileId) throw new Error('File ID not found');
|
||||
|
||||
const apiRes: { data: { file: { sources: { src: string; quality: string | number }[] } } } = await ctx.proxiedFetcher(
|
||||
`/api/file/${fileId}/source`,
|
||||
{
|
||||
baseUrl,
|
||||
query: {
|
||||
type: 'watch',
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!apiRes.data.file.sources.length) throw new Error('No sources found');
|
||||
|
||||
const qualities = apiRes.data.file.sources.reduce(
|
||||
(acc, source) => {
|
||||
const quality = typeof source.quality === 'number' ? source.quality.toString() : source.quality;
|
||||
const validQuality = getValidQualityFromString(quality);
|
||||
acc[validQuality] = {
|
||||
type: 'mp4',
|
||||
url: `${baseUrl}${source.src}`,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as FileBasedStream['qualities'],
|
||||
);
|
||||
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'file',
|
||||
flags: [],
|
||||
headers: {
|
||||
referer: 'https://fsharetv.co',
|
||||
},
|
||||
qualities,
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const fsharetvScraper = makeSourcerer({
|
||||
id: 'fsharetv',
|
||||
name: 'FshareTV',
|
||||
rank: 93,
|
||||
flags: [],
|
||||
scrapeMovie: comboScraper,
|
||||
});
|
Loading…
Reference in New Issue