should be alright

This commit is contained in:
aka paul
2026-03-09 16:18:24 +01:00
parent a88ed329a5
commit 68850c048e
168 changed files with 310 additions and 1633 deletions
+229
View File
@@ -0,0 +1,229 @@
async function searchResults(keyword) {
const results = [];
const image = 'https://files.catbox.moe/9tbjtb.png';
const regex = /<tr><td><a href="([^"]+)">([^<]+)<\/a><\/td><td[^>]*>[^<]*<\/td><\/tr>/g;
const urls = [
'https://a.111477.xyz/tvs/',
'https://a.111477.xyz/movies/',
'https://a.111477.xyz/kdrama/',
'https://a.111477.xyz/asiandrama/'
];
for (const url of urls) {
const response = await fetchv2(url);
const html = await response.text();
let match;
while ((match = regex.exec(html)) !== null) {
const rawTitle = match[2].trim().replace(/\/$/, '');
const cleanedTitle = rawTitle.replace(/\.+/g, ' ').toLowerCase();
if (cleanedTitle.includes(keyword.toLowerCase())) {
results.push({
title: rawTitle.replace(/\.+/g, ' '),
image,
href: url + match[1].trim()
});
}
}
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
results.push({
description: 'None provided, but hell who cares anyway',
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const episodeRegex = /<tr><td><a href="(https:\/\/[^"]+\.mkv)">([^<]+\.mkv)<\/a><\/td>/g;
let match;
let count = 1;
while ((match = episodeRegex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: count++
});
}
if (results.length > 0) {
return JSON.stringify([{
href: url,
number: 1
}]);
}
if (results.length === 0) {
const seasonRegex = /<tr><td><a href="([^"]+\/)"[^>]*>([^<]+\/)<\/a><\/td>/g;
const seasons = [];
while ((match = seasonRegex.exec(html)) !== null) {
const seasonHref = match[1].trim();
const seasonName = match[2].trim();
if (seasonHref === '../' || seasonName === '../') continue;
const isSeasonDir = (
/season\s*\d+/i.test(seasonName) ||
/s\d+/i.test(seasonName) ||
/series\s*\d+/i.test(seasonName) ||
/specials/i.test(seasonName) ||
/extras/i.test(seasonName) ||
/bonus/i.test(seasonName) ||
(/\d+/.test(seasonName) && seasonName.endsWith('/'))
);
if (isSeasonDir) {
seasons.push({
href: seasonHref,
name: seasonName
});
}
}
seasons.sort((a, b) => {
const aNum = extractSeasonNumber(a.name);
const bNum = extractSeasonNumber(b.name);
if (aNum === null && bNum === null) return a.name.localeCompare(b.name);
if (aNum === null) return 1;
if (bNum === null) return -1;
return aNum - bNum;
});
console.log(`Found ${seasons.length} seasons:`, seasons.map(s => s.name));
console.log(`Base URL: "${url}"`);
console.log(`Base URL ends with /: ${url.endsWith('/')}`);
for (const season of seasons) {
console.log(`Processing season: ${season.name}`);
console.log(`Season href: "${season.href}"`);
let seasonUrl;
if (season.href.startsWith('http')) {
seasonUrl = season.href;
} else {
const baseUrl = url.endsWith('/') ? url : url + '/';
seasonUrl = baseUrl + season.href;
}
console.log(`Season URL: "${seasonUrl}"`);
console.log(`URL encoded version: "${encodeURI(seasonUrl)}"`);
try {
const seasonResponse = await fetchv2(decodeURIComponent(seasonUrl));
const seasonHtml = await seasonResponse.text();
console.log(`Sample HTML from ${season.name}:`, seasonHtml.substring(0, 500));
let episodeCount = 1;
const seasonEpisodeRegex = /<a href="([^"]+\.mkv)"[^>]*>([^<]+\.mkv)<\/a>/g;
let seasonMatch;
let episodesInSeason = 0;
while ((seasonMatch = seasonEpisodeRegex.exec(seasonHtml)) !== null) {
let episodeHref = seasonMatch[1].trim();
if (!episodeHref.startsWith('http')) {
episodeHref = seasonUrl.endsWith('/') ? seasonUrl + episodeHref : seasonUrl + '/' + episodeHref;
}
results.push({
href: episodeHref,
number: episodeCount++,
season: season.name.replace('/', '')
});
episodesInSeason++;
}
console.log(`Found ${episodesInSeason} episodes in ${season.name}`);
} catch (error) {
console.warn(`Failed to fetch season ${season.name}:`, error);
console.warn(`Season URL was: ${seasonUrl}`);
}
}
}
return JSON.stringify(results);
}
function extractSeasonNumber(seasonName) {
const patterns = [
/season\s*(\d+)/i,
/s(\d+)/i,
/series\s*(\d+)/i,
/(\d+)/
];
for (const pattern of patterns) {
const match = seasonName.match(pattern);
if (match) {
return parseInt(match[1], 10);
}
}
return null;
}
async function extractStreamUrl(url) {
if (url.toLowerCase().endsWith('.mkv')) {
const filename = url.split('/').pop();
const final = {
streams: [filename, url],
subtitles: ""
};
console.log("RETURN: " + JSON.stringify(final));
return JSON.stringify(final);
}
try {
const response = await fetchv2(decodeURIComponent(url));
const html = await response.text();
const mkvRegex = /<tr><td><a href="([^"]+\.mkv)"[^>]*>([^<]+\.mkv)<\/a><\/td>/g;
const streams = [];
let match;
while ((match = mkvRegex.exec(html)) !== null) {
const mkvUrl = match[1].trim();
const filename = match[2].trim();
streams.push(filename, mkvUrl);
}
const final = {
streams,
subtitles: ""
};
console.log("RETURN: " + JSON.stringify(final));
return "JSON.stringify(final)";
} catch (error) {
console.log("Error in extractStreamUrl:", error);
return JSON.stringify({
streams: [],
subtitles: ""
});
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "111477",
"iconUrl": "https://media.tenor.com/tIOhF5a8McEAAAAe/heart-emoji-love-nonchalant.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Multi Language",
"streamType": "MKV",
"quality": "1080p",
"baseUrl": "https://a.111477.xyz/",
"searchBaseUrl": "https://a.111477.xyz/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/111477/111477.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"note": "USE AN EXTERNAL PLAYER (E.G., VLC/MPV)",
"supportsSora": true,
"supportsLuna": true
}
+257
View File
@@ -0,0 +1,257 @@
async function searchResults(keyword) {
const results = [];
const headers = {
"Host": 'graphql.anilist.co',
"User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:147.0) Gecko/20100101 Firefox/147.0',
"Accept": 'application/json',
"Accept-Language": 'en-US,en;q=0.9',
"Accept-Encoding": 'gzip, deflate, br, zstd',
"Referer": 'https://anicore.tv/',
"Content-Type": 'application/json',
"Content-Length": '1380',
"Origin": 'https://anicore.tv',
"Connection": 'keep-alive',
"Sec-Fetch-Dest": 'empty',
"Sec-Fetch-Mode": 'cors',
"Sec-Fetch-Site": 'cross-site',
"Priority": 'u=4',
"TE": 'trailers'
};
const postData = {"query":"\nquery ($page: Int = 1, $perPage: Int = 50, $type: MediaType = ANIME, $search: String, $format_in: [MediaFormat], $status: MediaStatus, $countryOfOrigin: CountryCode, $season: MediaSeason, $seasonYear: Int, $genre_in: [String], $tag_in: [String], $sort:[MediaSort], $isAdult: Boolean) {\n Page(page: $page, perPage: $perPage) {\n pageInfo {\n total\n perPage\n currentPage\n lastPage\n hasNextPage\n }\n media(type: $type, sort: $sort, season: $season, seasonYear: $seasonYear, search: $search, genre_in: $genre_in, tag_in: $tag_in, format_in: $format_in, status: $status, countryOfOrigin: $countryOfOrigin, isAdult: $isAdult){\n id\n title {\n english\n romaji\n }\n coverImage {\n extraLarge\n color\n }\n startDate {\n year\n month\n day\n }\n bannerImage\n season\n seasonYear\n description\n type\n format\n status(version: 2)\n episodes\n duration\n chapters\n volumes\n genres\n isAdult\n averageScore\n popularity\n nextAiringEpisode {\n airingAt\n timeUntilAiring\n episode\n }\n mediaListEntry {\n id\n status\n }\n }\n }\n}","variables":{"search":`${keyword}`,"type":"ANIME","sort":"POPULARITY_DESC","page":1}};
try {
const response = await fetchv2("https://graphql.anilist.co/", headers, "POST", postData);
const data = await response.json();
if (data.data && data.data.Page && data.data.Page.media) {
const media = data.data.Page.media;
for (const item of media) {
results.push({
title: item.title.english || item.title.romaji || "Unknown",
image: item.coverImage.extraLarge || "",
href: `${item.id}`
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(ID) {
try {
const headers = {
"Host": 'graphql.anilist.co',
"User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:147.0) Gecko/20100101 Firefox/147.0',
"Accept": 'application/json',
"Accept-Language": 'en-US,en;q=0.9',
"Accept-Encoding": 'gzip, deflate, br, zstd',
"Referer": 'https://anicore.tv/',
"Content-Type": 'application/json',
"Content-Length": '1380',
"Origin": 'https://anicore.tv',
"Connection": 'keep-alive',
"Sec-Fetch-Dest": 'empty',
"Sec-Fetch-Mode": 'cors',
"Sec-Fetch-Site": 'cross-site',
"Priority": 'u=4',
"TE": 'trailers'
};
const postData = {"query":"query media($id:Int,$type:MediaType,$isAdult:Boolean){Media(id:$id,type:$type,isAdult:$isAdult){id title{userPreferred romaji english native}coverImage{extraLarge large}bannerImage startDate{year month day}endDate{year month day}description season seasonYear type format status(version:2)episodes duration chapters volumes genres synonyms source(version:3)isAdult isLocked meanScore averageScore popularity favourites isFavouriteBlocked hashtag countryOfOrigin isLicensed isFavourite isRecommendationBlocked isFavouriteBlocked isReviewBlocked nextAiringEpisode{airingAt timeUntilAiring episode}relations{edges{id relationType(version:2)node{id title{userPreferred}format type status(version:2)bannerImage coverImage{large}}}}characterPreview:characters(perPage:6,sort:[ROLE,RELEVANCE,ID]){edges{id role name voiceActors(language:JAPANESE,sort:[RELEVANCE,ID]){id name{userPreferred}language:languageV2 image{large}}node{id name{userPreferred}image{large}}}}staffPreview:staff(perPage:8,sort:[RELEVANCE,ID]){edges{id role node{id name{userPreferred}language:languageV2 image{large}}}}studios{edges{isMain node{id name}}}reviewPreview:reviews(perPage:2,sort:[RATING_DESC,ID]){pageInfo{total}nodes{id summary rating ratingAmount user{id name avatar{large}}}}recommendations(perPage:7,sort:[RATING_DESC,ID]){pageInfo{total}nodes{id rating userRating mediaRecommendation{id title{userPreferred}format type status(version:2)bannerImage coverImage{large}}user{id name avatar{large}}}}externalLinks{id site url type language color icon notes isDisabled}streamingEpisodes{site title thumbnail url}trailer{id site}rankings{id rank type format year season allTime context}tags{id name description rank isMediaSpoiler isGeneralSpoiler userId}mediaListEntry{id status score}stats{statusDistribution{status amount}scoreDistribution{score amount}}}}","variables":{"id":ID,"type":"ANIME"}};
const response = await fetchv2("https://graphql.anilist.co/", headers, "POST", postData);
const data = await response.json();
const media = data.data.Media;
return JSON.stringify([{
description: clean(media.description),
aliases: media.title.romaji + " / " + media.title.english + " / " + media.title.native,
airdate: media.startDate.year
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(ID) {
const results = [];
try {
const response = await fetchv2("https://anicore-api.vercel.app/api/episodes?id=" + ID);
const data = await response.json();
if (Array.isArray(data)) {
for (const episode of data) {
results.push({
href: `${ID}-${episode.number}`,
number: episode.number
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const parts = url.split('-');
const animeID = parts[0];
const episodeNumber = parseInt(parts[1], 10);
const episodesResponse = await fetchv2("https://anicore-api.vercel.app/api/episodes?id=" + animeID);
const episodesData = await episodesResponse.json();
const episode = episodesData.find(ep => ep.number === episodeNumber);
if (!episode) {
return JSON.stringify({
streams: [],
subtitle: ""
});
}
const excludedProviders = ['oni', 'yuki', 'kami'];
const subProviders = (episode.subProviders || []).filter(p => !excludedProviders.includes(p));
const dubProviders = (episode.dubProviders || []).filter(p => !excludedProviders.includes(p));
if (subProviders.length === 0 && dubProviders.length === 0) {
return JSON.stringify({
streams: [],
subtitle: ""
});
}
const streamPromises = [];
for (const provider of subProviders) {
streamPromises.push(
fetchv2(`https://anicore-api.vercel.app/api/stream?id=${animeID}&host=${provider}&ep=${episodeNumber}&type=sub`)
.then(res => res.json())
.then(data => ({
provider,
type: 'sub',
data
}))
.catch(() => ({
provider,
type: 'sub',
data: null
}))
);
}
for (const provider of dubProviders) {
streamPromises.push(
fetchv2(`https://anicore-api.vercel.app/api/stream?id=${animeID}&host=${provider}&ep=${episodeNumber}&type=dub`)
.then(res => res.json())
.then(data => ({
provider,
type: 'dub',
data
}))
.catch(() => ({
provider,
type: 'dub',
data: null
}))
);
}
const streamResults = await Promise.all(streamPromises);
const streams = [];
let englishSubtitle = "";
let zenResult = null;
for (const result of streamResults) {
if (result.data && result.data.sources && result.data.sources.length > 0) {
const source = result.data.sources[0];
const typeLabel = result.type === 'dub' ? 'DUB' : 'SUB';
streams.push({
title: `${result.provider.toUpperCase()} - ${typeLabel}`,
streamUrl: source.url,
headers: result.data.headers || {}
});
if (result.provider === 'zen' && result.type === 'sub') {
zenResult = result;
}
}
}
if (zenResult && zenResult.data && Array.isArray(zenResult.data.subtitles)) {
const engSub = zenResult.data.subtitles.find(sub =>
(sub.lang.includes('eng') || sub.lang.includes('en')) &&
!sub.label.toLowerCase().includes('signs') &&
!sub.label.toLowerCase().includes('songs')
);
if (engSub) {
englishSubtitle = engSub.url || "";
}
}
if (!englishSubtitle) {
for (const result of streamResults) {
if (result.type === 'sub' && result.data) {
if (Array.isArray(result.data.subtitles)) {
const engSub = result.data.subtitles.find(sub =>
(sub.lang.includes('eng') || sub.lang.includes('en') ||
sub.language === 'eng' || sub.language === 'en' || sub.name === 'eng' || sub.name === 'en') &&
!(sub.label && (sub.label.toLowerCase().includes('signs') || sub.label.toLowerCase().includes('songs')))
);
if (engSub) {
englishSubtitle = engSub.url || "";
break;
}
}
if (!englishSubtitle && Array.isArray(result.data.audio)) {
const engAudio = result.data.audio.find(audio =>
audio.language === 'eng' || audio.language === 'en' || audio.name === 'eng' || audio.name === 'en'
);
if (engAudio) {
englishSubtitle = engAudio.url || "";
break;
}
}
}
}
}
console.log(JSON.stringify({
streams: streams.length > 0 ? streams : [],
subtitles: englishSubtitle
}));
return JSON.stringify({
streams: streams.length > 0 ? streams : [],
subtitles: englishSubtitle
});
} catch (err) {
return JSON.stringify({
streams: [],
subtitles: ""
});
}
}
const clean = html =>
html
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<\/?i>/gi, '')
.replace(/<\/?b>/gi, '')
.replace(/<\/?[^>]+>/g, '');
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "Anicore",
"iconUrl": "https://anicore.tv/_app/immutable/assets/favicon.Bq_xX7js.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://anicore.tv/",
"searchBaseUrl": "https://anicore.tv/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anicore/anicore.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+70
View File
@@ -0,0 +1,70 @@
async function searchResults(keyword) {
try {
const encodedKeyword = encodeURIComponent(keyword);
const responseText = await fetch(`https://api.anilibria.tv/v3/title/search?search=${encodedKeyword}&filter=id,names,posters`);
const data = JSON.parse(responseText);
const transformedResults = data.list.map(anime => ({
title: anime.names.en || anime.names.ru || 'Unknown Title',
image: `https://anilibria.tv${anime.posters.original.url}`,
href: `${anime.id}`
}));
return JSON.stringify(transformedResults);
} catch (error) {
console.log('Fetch error:', error);
return JSON.stringify([{ title: 'Error', image: '', href: '' }]);
}
}
async function extractDetails(id) {
try {
const response = await fetch(`https://api.anilibria.tv/v3/title?id=${id}&filter=description`);
const data = JSON.parse(response);
const animeInfo = data;
const transformedResults = [{
description: animeInfo.description || 'No description available',
aliases: `Alias: Unknown`,
airdate: `Aired: Unknown`
}];
return JSON.stringify(transformedResults);
} catch (error) {
console.log('Details error:', error);
return JSON.stringify([{
description: 'Error loading description',
aliases: 'Duration: Unknown',
airdate: 'Aired: Unknown'
}]);
}
}
async function extractEpisodes(id) {
try {
const response = await fetch(`https://api.anilibria.tv/v3/title?id=${id}`);
const data = JSON.parse(response);
const transformedResults = Object.values(data.player.list).map(episode => ({
href: `https://cache.libria.fun${episode.hls.hd}`,
number: episode.episode
}));
return JSON.stringify(transformedResults);
} catch (error) {
console.log('Fetch error:', error);
}
}
async function extractStreamUrl(url) {
try {
return url;
} catch (error) {
console.log('Fetch error:', error);
return null;
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "AniLibria",
"iconUrl": "https://github.com/50n50/sources/blob/main/anilibria/icon.png?raw=true",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Russian",
"streamType": "HLS",
"quality": "720p",
"baseUrl": "https://api.anilibria.tv/",
"searchBaseUrl": "https://api.anilibria.tv/v3/title/search?search=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anilibria/anilibria.js",
"asyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

+228
View File
@@ -0,0 +1,228 @@
function searchResults(html) {
if (typeof html !== 'string') {
console.error('Invalid HTML input: expected a string.');
return [];
}
const results = [];
const titleRegex = /<h4[^>]*>(.*?)<\/h4>/;
const hrefRegex = /<a\s+href="([^"]+)"\s*[^>]*>/;
const imgRegex = /<img[^>]*src="([^"]+)"[^>]*>/;
const itemRegex = /<a\s+href="[^"]+"\s+class="btn btn-md btn-light simple-title-card[^"]*"[^>]*>[\s\S]*?<\/a>/g;
const items = html.match(itemRegex) || [];
items.forEach((itemHtml, index) => {
try {
if (typeof itemHtml !== 'string') {
console.error(`Item ${index} is not a string.`);
return;
}
const titleMatch = itemHtml.match(titleRegex);
const title = titleMatch?.[1]?.trim() ?? '';
const hrefMatch = itemHtml.match(hrefRegex);
const href = hrefMatch?.[1]?.trim() ?? '';
const imgMatch = itemHtml.match(imgRegex);
const imageUrl = imgMatch?.[1]?.trim() ?? '';
if (title && href) {
results.push({
title: decodeHTMLEntities(title),
image: imageUrl,
href: href
});
} else {
console.error(`Missing title or href in item ${index}`);
}
} catch (err) {
console.error(`Error processing item ${index}:`, err);
}
});
return results;
}
function extractDetails(html) {
const details = [];
const containerMatch = html.match(/<div class="py-4 flex flex-col gap-2">\s*((?:<p class="sm:text-\[1\.04rem\] leading-loose text-justify">[\s\S]*?<\/p>\s*)+)<\/div>/);
let description = "";
if (containerMatch) {
const pBlock = containerMatch[1];
const pRegex = /<p class="sm:text-\[1\.04rem\] leading-loose text-justify">([\s\S]*?)<\/p>/g;
const matches = [...pBlock.matchAll(pRegex)]
.map(m => m[1].trim())
.filter(text => text.length > 0);
description = decodeHTMLEntities(matches.join("\n\n"));
}
const airdateMatch = html.match(/<td[^>]*title="([^"]+)">[^<]+<\/td>/);
let airdate = airdateMatch ? airdateMatch[1].trim() : "";
const genres = [];
const aliasesMatch = html.match(
/<div\s+class="flex flex-wrap gap-2 lg:gap-4 text-sm sm:text-\[\.94rem\] -mt-2 mb-4">([\s\S]*?)<\/div>/
);
const inner = aliasesMatch ? aliasesMatch[1] : "";
const anchorRe = /<a[^>]*class="btn btn-md btn-plain !p-0"[^>]*>([^<]+)<\/a>/g;
let m;
while ((m = anchorRe.exec(inner)) !== null) {
genres.push(m[1].trim());
}
if (description && airdate) {
details.push({
description: description,
aliases: genres.join(", "),
airdate: airdate,
});
}
console.log(details);
return details;
}
function extractEpisodes(html) {
const episodes = [];
const htmlRegex = /<a\s+[^>]*href="([^"]*?\/episode\/[^"]*?)"[^>]*>[\s\S]*?الحلقة\s+(\d+)[\s\S]*?<\/a>/gi;
const plainTextRegex = /الحلقة\s+(\d+)/g;
let matches;
if ((matches = html.match(htmlRegex))) {
matches.forEach(link => {
const hrefMatch = link.match(/href="([^"]+)"/);
const numberMatch = link.match(/الحلقة\s+(\d+)/);
if (hrefMatch && numberMatch) {
const href = hrefMatch[1];
const number = numberMatch[1];
episodes.push({
href: href,
number: number
});
}
});
}
else if ((matches = html.match(plainTextRegex))) {
matches.forEach(match => {
const numberMatch = match.match(/\d+/);
if (numberMatch) {
episodes.push({
href: null,
number: numberMatch[0]
});
}
});
}
console.log(episodes);
return episodes;
}
async function extractStreamUrl(html) {
try {
const sourceMatch = html.match(/data-video-source="([^"]+)"/);
let embedUrl = sourceMatch?.[1]?.replace(/&amp;/g, '&');
if (!embedUrl) return null;
const cinemaMatch = html.match(/url\.searchParams\.append\(\s*['"]cinema['"]\s*,\s*(\d+)\s*\)/);
const lastMatch = html.match(/url\.searchParams\.append\(\s*['"]last['"]\s*,\s*(\d+)\s*\)/);
const cinemaNum = cinemaMatch ? cinemaMatch[1] : undefined;
const lastNum = lastMatch ? lastMatch[1] : undefined;
if (cinemaNum) embedUrl += `&cinema=${cinemaNum}`;
if (lastNum) embedUrl += `&last=${lastNum}`;
embedUrl += `&next-image=undefined`;
console.log('Full embed URL:', embedUrl);
const response = await fetchv2(embedUrl);
const data = await response.text();
console.log('Embed page HTML:', data);
const qualities = extractQualities(data);
const epMatch = html.match(/<title>[^<]*الحلقة\s*(\d+)[^<]*<\/title>/);
const currentEp = epMatch ? Number(epMatch[1]) : null;
let nextEpNum, nextDuration, nextSubtitle;
if (currentEp !== null) {
const episodeRegex = new RegExp(
`<a[^>]+href="[^"]+/episode/[^/]+/(\\d+)"[\\s\\S]*?` +
`<span[^>]*>([^<]+)<\\/span>[\\s\\S]*?` +
`<p[^>]*>([^<]+)<\\/p>`,
'g'
);
let m;
while ((m = episodeRegex.exec(html)) !== null) {
const num = Number(m[1]);
if (num > currentEp) {
nextEpNum = num;
nextDuration = m[2].trim();
nextSubtitle = m[3].trim();
break;
}
}
}
if (nextEpNum != null) {
embedUrl += `&next-title=${encodeURIComponent(nextDuration)}`;
embedUrl += `&next-sub-title=${encodeURIComponent(nextSubtitle)}`;
}
const result = {
streams: qualities,
}
console.log(JSON.stringify(result));
return JSON.stringify(result);
} catch (err) {
console.error(err);
return null;
}
}
function extractQualities(html) {
const match = html.match(/var\s+videos\s*=\s*(\[[\s\S]*?\]);/);
if (!match) return [];
const raw = match[1];
const regex = /\{\s*src:\s*'([^']+)'\s*[^}]*label:\s*'([^']*)'/g;
const list = [];
let m;
while ((m = regex.exec(raw)) !== null) {
list.push(m[2], m[1]);
}
return list;
}
function decodeHTMLEntities(text) {
text = text.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec));
const entities = {
'&quot;': '"',
'&amp;': '&',
'&apos;': "'",
'&lt;': '<',
'&gt;': '>'
};
for (const entity in entities) {
text = text.replace(new RegExp(entity, 'g'), entities[entity]);
}
return text;
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "Anime3rb",
"iconUrl": "https://anime3rb.com/favicon/apple-touch-icon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.2.0",
"language": "Arabic (SUB)",
"streamType": "MP4",
"quality": "1080p - 720p - 360p",
"baseUrl": "https://anime3rb.com/",
"searchBaseUrl": "https://anime3rb.com/search?q=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anime3rb/anime3rb.js",
"streamAsyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

+165
View File
@@ -0,0 +1,165 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2(
"https://ww.anime4up.rest/?search_param=animes&s=" + encodeURIComponent(keyword)
);
const html = await response.text();
const regex = /<div class="anime-card-container">[\s\S]*?<img[^>]+src="([^"]+)"[^>]*alt="([^"]*)"[\s\S]*?<h3><a[^>]*href="([^"]+)"[^>]*>([^<]+)<\/a><\/h3>/gi;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[4].trim(),
image: match[1].trim().replace('ww.anime4up.rest', 'www.anime4up.rest'),
href: match[3].trim().replace('ww.anime4up.rest', 'www.anime4up.rest')
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = /<p class="anime-story">([\s\S]*?)<\/p>/i.exec(html);
const description = descMatch ? descMatch[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const episodeRegex = /<div class="col-lg-3[^"]*DivEpisodeContainer">([\s\S]*?)<\/div>\s*<\/div>/gi;
let match;
let epNumber = 1;
while ((match = episodeRegex.exec(html)) !== null) {
const hrefMatch = /<h3><a href="([^"]+)"/i.exec(match[1]);
const href = hrefMatch ? hrefMatch[1].trim() : "";
results.push({
href: href,
number: epNumber
});
epNumber++;
}
const paginationRegex = /<li\s*><a\s+class="page-numbers"\s+href="[^"]*\/page\/(\d+)\/">(\d+)<\/a><\/li>/gi;
let maxPage = 1;
let paginationMatch;
while ((paginationMatch = paginationRegex.exec(html)) !== null) {
const pageNum = parseInt(paginationMatch[2]);
if (pageNum > maxPage) {
maxPage = pageNum;
}
}
if (maxPage > 1) {
const pagePromises = [];
for (let page = 2; page <= maxPage; page++) {
const pageUrl = `${url}/page/${page}/`;
pagePromises.push(fetchPageEpisodes(pageUrl, epNumber + (page - 2) * getEpisodesPerPage(results.length)));
}
const pageResults = await Promise.all(pagePromises);
pageResults.forEach(pageEpisodes => {
results.push(...pageEpisodes);
});
results.forEach((episode, index) => {
episode.number = index + 1;
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function fetchPageEpisodes(pageUrl, startingEpNumber) {
try {
const response = await fetchv2(pageUrl);
const html = await response.text();
const episodeRegex = /<div class="col-lg-3[^"]*DivEpisodeContainer">([\s\S]*?)<\/div>\s*<\/div>/gi;
const pageResults = [];
let match;
let epNumber = startingEpNumber;
while ((match = episodeRegex.exec(html)) !== null) {
const hrefMatch = /<h3><a href="([^"]+)"/i.exec(match[1]);
const href = hrefMatch ? hrefMatch[1].trim() : "";
pageResults.push({
href: href,
number: epNumber
});
epNumber++;
}
return pageResults;
} catch (err) {
return [];
}
}
function getEpisodesPerPage(firstPageCount) {
return firstPageCount || 12;
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = /<a[^>]+data-ep-url="([^"]*uqload\.cx[^"]*)"/i.exec(html);
if (match) {
console.log(match[1].trim());
const response2 = await fetchv2(match[1].trim());
const html2 = await response2.text();
const match2 = /sources:\s*\[\s*"([^"]+)"\s*\]/i.exec(html2);
const url = match2 ? match2[1] : null;
return url;
}
return "dwd";
} catch (err) {
console.error(err);
return "https://files.catbox.moe/avolvc.mp4";
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Anime4Up",
"iconUrl": "https://ww.anime4up.rest/wp-content/uploads/2019/03/Anime4up-Icon-1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Arabic",
"streamType": "HLS",
"encrypted": true,
"quality": "1080p",
"baseUrl": "https://uqload.cx/",
"searchBaseUrl": "https://uqload.cx/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anime4up/anime4up.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+364
View File
@@ -0,0 +1,364 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://www.animebum.net/search?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="search-results__item[^>]*>[\s\S]*?<a href="([^"]+)"[^>]*><img src="([^"]+)"[^>]*><\/a>[\s\S]*?<h2>([^<]+)<\/h2>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<div class="description scrollV">\s*<p>([\s\S]*?)<\/p>/i;
const match = regex.exec(html);
let description = match ? match[1].trim() : "N/A";
description = description.replace(/<[^>]+>/g, "");
description = description.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&aacute;/g, "á")
.replace(/&eacute;/g, "é")
.replace(/&iacute;/g, "í")
.replace(/&oacute;/g, "ó")
.replace(/&uacute;/g, "ú")
.replace(/&ntilde;/g, "ñ")
.replace(/&uuml;/g, "ü")
.replace(/&ldquo;/g, "“")
.replace(/&rdquo;/g, "”")
.replace(/&lsquo;/g, "")
.replace(/&rsquo;/g, "")
.replace(/&iexcl;/g, "¡")
.replace(/&iquest;/g, "¿");
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const containerMatch = html.match(
/<div class="list-episodies-content">([\s\S]*?)<\/div>/
);
if (!containerMatch) {
return JSON.stringify([{ href: "Not found", number: "N/A" }]);
}
const containerHtml = containerMatch[1];
const regex = /<a[^>]+href="([^"]+)"[^>]*>[\s\S]*?Episodio\s+(\d+)\s*<\/a>/gi;
let match;
while ((match = regex.exec(containerHtml)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{ href: "Error", number: "Error" }]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
// Try Filemoon first
const filemoonRegex = /(https:\/\/filemoon\.(?:to|sx)\/e\/[A-Za-z0-9]+\/[^\s"']+)/;
const filemoonMatch = filemoonRegex.exec(html);
if (filemoonMatch) {
const filemoon = filemoonMatch[1];
console.log("Filemoon URL found: " + filemoon);
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": "https://animebum.net/",
};
const filemoonResponse = await fetchv2(filemoon, headers);
const filemoonHtml = await filemoonResponse.text();
try {
const streamUrl = await filemoonExtractor(filemoonHtml, "https://animebum.net/");
if (streamUrl) return streamUrl;
} catch (err) {
console.log("Filemoon extraction error:" + err);
}
}
const optionRegex = /<ul class="play-list-tabs">([\s\S]*?)<\/ul>/i;
const optionMatch = optionRegex.exec(html);
if (!optionMatch) return null;
const optionsHtml = optionMatch[1];
const faireMatch = /<li[^>]*data-id="(\d+)"[^>]*>\s*<a[^>]*title="Faire"/i.exec(optionsHtml);
if (!faireMatch) return null;
const faireId = parseInt(faireMatch[1], 10);
const videoRegex = new RegExp(`video\\[${faireId}\\]\\s*=\\s*'([^']+)'`);
const videoMatch = videoRegex.exec(html);
if (!videoMatch) return null;
const iframeHtml = videoMatch[1];
const srcMatch = /src=["']([^"']+)["']/.exec(iframeHtml);
const streamUrl = srcMatch ? srcMatch[1] : null;
const responeSome = await fetchv2(streamUrl);
const htmlSome = await responeSome.text();
const shareIdMatch = htmlSome.match(/var\s+shareId\s*=\s*"([^"]+)"/);
if (!shareIdMatch) return null;
const shareId = shareIdMatch[1];
const someUrl = `https://www.amazon.com/drive/v1/shares/${shareId}?resourceVersion=V2&ContentType=JSON&asset=ALL`;
const shareJsonResp = await fetchv2(someUrl);
const shareJson = await shareJsonResp.json();
console.log(JSON.stringify(shareJson));
const nodeId = shareJson.nodeInfo?.id;
if (!nodeId) return null;
const childrenUrl = `https://www.amazon.com/drive/v1/nodes/${nodeId}/children?resourceVersion=V2&ContentType=JSON&limit=200&sort=%5B%22kind+DESC%22%2C+%22modifiedDate+DESC%22%5D&asset=ALL&tempLink=true&shareId=${shareId}`;
const childrenResp = await fetchv2(childrenUrl);
const childrenData = await childrenResp.json();
console.log(JSON.stringify(childrenData));
const file = childrenData.data.find(item => item.kind === "FILE");
if (!file) return null;
return file.tempLink;
} catch (err) {
console.log("Fetch error:" + err);
return "https://error.org/";
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AnimeBum",
"iconUrl": "https://files.catbox.moe/i96cs1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.animebum.net/",
"searchBaseUrl": "https://www.animebum.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animebum/animebum.js",
"type": "Anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
@@ -0,0 +1,139 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://animeepisodeseries.com/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<a href="([^"]+)"[^>]*>\s*<img[^>]+src="([^"]+)"[^>]*>[\s\S]*?<h2 class="entry-title"><a [^>]+>([^<]+)<\/a><\/h2>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: cleanTitle(match[3].trim()),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<p><strong[\s\S]*?>[\s\S]*?Summary:[\s\S]*?<\/strong><br\s*\/?>(.*?)<\/p>/i);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: cleanTitle(description),
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const gridMatch = html.match(/<div[^>]+class="[^"]*eael-post-grid[^"]*"[^>]*>([\s\S]*?)<\/div>\s*<div[^>]+class="clearfix">/i);
const gridHtml = gridMatch[1];
const patterns = [
/<a[^>]+href="([^"]*?-episode-(\d+)-[^"]*)"/gi,
/<a[^>]+href="([^"]*?-episode-(\d+)(?:-[^"]*)?\/?)"/gi,
/<a[^>]+href="([^"]*?episode(\d+)[^"]*)"/gi,
/<a[^>]+href="([^"]*?ep(\d+)[^"]*)"/gi
];
for (const regex of patterns) {
let match;
while ((match = regex.exec(gridHtml)) !== null) {
const episodeNumber = parseInt(match[2], 10);
if (!results.find(r => r.number === episodeNumber && r.href === match[1].trim())) {
results.push({
href: match[1].trim(),
number: episodeNumber
});
}
}
}
if (results.length === 0) {
const linkRegex = /<a[^>]+href="([^"]+)"[^>]*title="[^"]*episode[^"]*(\d+)[^"]*"/gi;
let match;
while ((match = linkRegex.exec(gridHtml)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
}
const uniqueResults = results.reduce((acc, current) => {
const existing = acc.find(item => item.number === current.number);
if (!existing) {
acc.push(current);
}
return acc;
}, []);
uniqueResults.sort((a, b) => a.number - b.number);
return JSON.stringify(uniqueResults);
} catch (err) {
console.error("Error in extractEpisodes:", err);
return JSON.stringify([{
href: "Error: " + err.message,
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<iframe[^>]+src="(https:\/\/www\.4shared\.com\/web\/embed\/file\/[^"]+)"/i);
const video = match ? match[1].trim() : null;
const response2 = await fetchv2(video);
const html2 = await response2.text();
const match2 = html2.match(/<source[^>]+src="([^"]+)"[^>]*type="video\/mp4"/i);
return match2 ? match2[1].trim() : "dwa";
} catch (err) {
return "https://files.catbox.moe/avolvc.mp4";
}
}
function cleanTitle(title) {
return title
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "");
}
@@ -0,0 +1,23 @@
{
"sourceName": "AnimeEpisodeSeries",
"iconUrl": "https://animeepisodeseries.com/wp-content/uploads/2019/10/cropped-anime-episode-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English (DUB/SUB)",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://www.4shared.com/",
"searchBaseUrl": "https://www.4shared.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeepisodeseries/animeepisodeseries.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"note": "Use external player.",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+100
View File
@@ -0,0 +1,100 @@
async function searchResults(keyword) {
const results = [];
const response = await fetchv2("https://animefhd.com/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const filmListRegex = /<div class="ultAnisContainerItem">[\s\S]*?<\/div>\s*<\/div>/g;
const items = html.match(filmListRegex) || [];
items.forEach((itemHtml) => {
const titleMatch = itemHtml.match(/<a href="([^"]+)"[^>]*title="([^"]+)"/);
const href = titleMatch ? titleMatch[1] : '';
const title = titleMatch ? titleMatch[2] : '';
const imgMatch = itemHtml.match(/<img[^>]*src="([^"]+)"[^>]*>/);
const imageUrl = imgMatch ? imgMatch[1] : '';
if (title && href) {
results.push({
title: title.trim(),
image: imageUrl.trim(),
href: href.trim(),
});
}
});
console.log(results);
return JSON.stringify(results);
}
async function extractDetails(url) {
const details = [];
const response = await fetchv2(url);
const html = await response.text();
const descriptionMatch = html.match(/<b>Sinopse:<\/b>\s*<p>\s*([\s\S]*?)\s*<\/p>/);
let description = descriptionMatch ? descriptionMatch[1].trim() : 'N/A';
const airdateMatch = html.match(/<b>Ano:<\/b>\s*(\d{4})/);
let airdate = airdateMatch ? airdateMatch[1].trim() : 'N/A';
const episodesMatch = html.match(/<b>Episódios:<\/b>\s*(\d+)/);
let aliases = episodesMatch ? episodesMatch[1].trim() : 'N/A';
details.push({
description: description,
alias: "Episódios: " + aliases,
airdate: airdate
});
console.log(details);
return JSON.stringify(details);
}
async function extractEpisodes(url) {
const episodes = [];
const response = await fetchv2(url);
const html = await response.text();
const episodeMatches = html.match(/<a href="([^"]+)"[^>]*class="list-epi"[^>]*>Episódio (\d+)<\/a>/g);
if (episodeMatches) {
episodeMatches.forEach(match => {
const hrefMatch = match.match(/href="([^"]+)"/);
const numberMatch = match.match(/Episódio (\d+)/);
if (hrefMatch && numberMatch) {
episodes.push({
href: hrefMatch[1],
number: parseInt(numberMatch[1])
});
}
});
}
console.log(JSON.stringify(episodes));
return JSON.stringify(episodes);
}
async function extractStreamUrl(url) {
const response = await fetchv2(url);
const html = await response.text();
const iframeMatch = html.match(/<iframe[^>]*src="([^"]+)"/);
if (iframeMatch) {
const streamUrl = iframeMatch[1];
console.log(streamUrl);
const response = await fetch(streamUrl);
const newHtml = await response;
const m3u8Match = newHtml.match(/file:\s*'([^']+\.m3u8)'/);
if (m3u8Match) {
const videoUrl = m3u8Match[1];
console.log(videoUrl);
return videoUrl;
} else {
console.log("No m3u8 URL found.");
return null;
}
} else {
console.log("No iframe found.");
return null;
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "AnimeFHD",
"iconUrl": "https://animefhd.net/wp-content/uploads/2024/12/270.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.2",
"language": "Portuguese (SUB)",
"streamType": "MP4",
"quality": "1080p",
"baseUrl": "https://animefhd.net/",
"searchBaseUrl": "https://animefhd.net/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animefhd/animefhd.js",
"asyncJS": true,
"type": "anime",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+237
View File
@@ -0,0 +1,237 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://animemeow.xyz/directorio/?q=" + encodeURIComponent(keyword));
const html = await response.text();
const containerMatch = html.match(/<ul class="grid-animes directorio">([\s\S]*?)<\/ul>/);
const containerHtml = containerMatch ? containerMatch[1] : "";
const regex = /<a href="([^"]+)">[\s\S]*?<div class="main-img">[\s\S]*?<img[^>]+src="([^"]+)"[^>]*>[\s\S]*?<p>([^<]+)<\/p>/g;
let match;
while ((match = regex.exec(containerHtml)) !== null) {
results.push({
href: "https://animemeow.xyz" + match[1].trim(),
image: "https://animemeow.xyz" + match[2].trim(),
title: match[3].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<p class="sinopsis" id="sinopsis">([\s\S]*?)<\/p>/);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const containerMatch = html.match(/<ul id="eps">([\s\S]*?)<\/ul>/);
const containerHtml = containerMatch ? containerMatch[1] : "";
const regex = /<a href="([^"]+)">[\s\S]*?<p>[\s\S]*?<\/i>\s*([^<]+)<\/p>/g;
let match;
while ((match = regex.exec(containerHtml)) !== null) {
const href = "https://animemeow.xyz" + match[1].trim();
let number;
if (/Episodio\s*(\d+)/i.test(match[2])) {
number = parseInt(match[2].match(/Episodio\s*(\d+)/i)[1], 10);
} else if (/Ver Pel[ií]cula/i.test(match[2])) {
number = 1;
} else {
number = "N/A";
}
results.push({ href, number });
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const buttonMatch = html.match(/<button[^>]+data-url="([^"]*voe[^"]+)"[^>]*>/i);
if (!buttonMatch) return "https://error.org/";
const voeLink = buttonMatch[1].split('id=')[1];
console.log("VOE Link: " + voeLink);
const voeResponse = await fetchv2(voeLink);
const voeHtml = await voeResponse.text();
const redirectMatch = voeHtml.match(/window\.location\.href\s*=\s*['"]([^'"]+)['"]/);
if (!redirectMatch) return null;
const finalLink = redirectMatch[1];
const streamUrlOne = finalLink;
const responseTwo = await fetchv2(streamUrlOne);
const finalHtml = await responseTwo.text();
let streamUrl = null;
try {
streamUrl = voeExtractor(finalHtml);
} catch (error) {
console.log("VOE extraction error:", error);
return null;
}
console.log("Voe Stream URL: " + streamUrl);
return streamUrl;
} catch (err) {
return "https://error.org/";
}
}
/* SCHEME START */
/**
* @name voeExtractor
* @author Cufiy
*/
function voeExtractor(html, url = null) {
// Extract the first <script type="application/json">...</script>
const jsonScriptMatch = html.match(
/<script[^>]+type=["']application\/json["'][^>]*>([\s\S]*?)<\/script>/i
);
if (!jsonScriptMatch) {
console.log("No application/json script tag found");
return null;
}
const obfuscatedJson = jsonScriptMatch[1].trim();
let data;
try {
data = JSON.parse(obfuscatedJson);
} catch (e) {
throw new Error("Invalid JSON input.");
}
if (!Array.isArray(data) || typeof data[0] !== "string") {
throw new Error("Input doesn't match expected format.");
}
let obfuscatedString = data[0];
// Step 1: ROT13
let step1 = voeRot13(obfuscatedString);
// Step 2: Remove patterns
let step2 = voeRemovePatterns(step1);
// Step 3: Base64 decode
let step3 = voeBase64Decode(step2);
// Step 4: Subtract 3 from each char code
let step4 = voeShiftChars(step3, 3);
// Step 5: Reverse string
let step5 = step4.split("").reverse().join("");
// Step 6: Base64 decode again
let step6 = voeBase64Decode(step5);
// Step 7: Parse as JSON
let result;
try {
result = JSON.parse(step6);
} catch (e) {
throw new Error("Final JSON parse error: " + e.message);
}
// console.log("Decoded JSON:", result);
// check if direct_access_url is set, not null and starts with http
if (result && typeof result === "object") {
const streamUrl =
result.direct_access_url ||
result.source
.map((source) => source.direct_access_url)
.find((url) => url && url.startsWith("http"));
if (streamUrl) {
console.log("Voe Stream URL: " + streamUrl);
return streamUrl;
} else {
console.log("No stream URL found in the decoded JSON");
}
}
return result;
}
function voeRot13(str) {
return str.replace(/[a-zA-Z]/g, function (c) {
return String.fromCharCode(
(c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13)
? c
: c - 26
);
});
}
function voeRemovePatterns(str) {
const patterns = ["@$", "^^", "~@", "%?", "*~", "!!", "#&"];
let result = str;
for (const pat of patterns) {
result = result.split(pat).join("");
}
return result;
}
function voeBase64Decode(str) {
// atob is available in browsers and Node >= 16
if (typeof atob === "function") {
return atob(str);
}
// Node.js fallback
return Buffer.from(str, "base64").toString("utf-8");
}
function voeShiftChars(str, shift) {
return str
.split("")
.map((c) => String.fromCharCode(c.charCodeAt(0) - shift))
.join("");
}
/* SCHEME END */
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AnimeMeow",
"iconUrl": "https://files.catbox.moe/5phbht.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animemeow.xyz/",
"searchBaseUrl": "https://animemeow.xyz/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animemeow/animemeow.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+138
View File
@@ -0,0 +1,138 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://animeq.blog/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="result-item">[\s\S]*?<a href="([^"]+)"[^>]*>\s*<img src="([^"]+)"[^>]*alt="([^"]+)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
image: match[2].trim(),
title: cleanHtmlSymbols(match[3].trim())
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<p>\s*Sinopse\s*:\s*([\s\S]*?)<\/p>/i;
const match = regex.exec(html);
const description = match ? match[1].trim() : "fuck off you don't need a description";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
const regex = /<div class=['"]?numerando['"]?[^>]*>\d+\s*-\s*(\d+)<\/div>[\s\S]*?<a\s+href=['"]([^'"]+)['"][^>]*>/g;
try {
const response = await fetchv2(url);
const html = await response.text();
let match;
while ((match = regex.exec(html)) !== null) {
const episodeNumber = parseInt(match[1], 10);
const href = match[2].trim();
results.push({
href: "episode: " + href,
number: episodeNumber
});
}
if (results.length === 0) {
results.push({
href: "movie: " + url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
let endpointType;
if (url.startsWith("movie: ")) {
url = url.replace("movie: ", "");
endpointType = "movie";
} else if (url.startsWith("episode: ")) {
url = url.replace("episode: ", "");
endpointType = "tv";
} else {
return "ERROR";
}
const response = await fetchv2(url);
const html = await response.text();
const idMatch = html.match(/<link rel=['"]shortlink['"] href=['"][^?]+\?p=(\d+)['"]/);
if (!idMatch) return "ID NOT FOUND";
const id = idMatch[1];
const apiUrl = `https://animeq.blog/wp-json/dooplayer/v2/${id}/${endpointType}/1`;
const apiResponse = await fetchv2(apiUrl);
const apiData = await apiResponse.json();
if (!apiData.embed_url) return { error: "embed_url not found" };
const embedResponse = await fetchv2(apiData.embed_url);
const embedHtml = await embedResponse.text();
const fileMatch = embedHtml.match(/"file":"(https?:\\\/\\\/[^"]+)"/);
if (!fileMatch) return { error: "file not found" };
const fileUrl = fileMatch[1].replace(/\\\//g, "/");
return fileUrl;
} catch (err) {
console.error("Error extracting stream URL:"+ err);
return "{ error: err.message }";
}
}
function cleanHtmlSymbols(string) {
if (!string) return "";
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "Animeq",
"iconUrl": "https://animeq.blog/wp-content/uploads/2025/06/Favicon-AnimeQ-1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://animeq.blog/",
"searchBaseUrl": "https://animeq.blog/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeq/animeq.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+138
View File
@@ -0,0 +1,138 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://animesdrive.blog/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="result-item">[\s\S]*?<a href="([^"]+)"[^>]*>\s*<img src="([^"]+)"[^>]*alt="([^"]+)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
image: match[2].trim(),
title: cleanHtmlSymbols(match[3].trim())
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<p[^>]*>\s*Sinopse\s*:\s*([\s\S]*?)<\/p>/i;
const match = regex.exec(html);
const description = match ? match[1].trim() : "fuck off you don't need a description";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
const regex = /<div class=['"]?numerando['"]?[^>]*>\d+\s*-\s*(\d+)<\/div>[\s\S]*?<a\s+href=['"]([^'"]+)['"][^>]*>/g;
try {
const response = await fetchv2(url);
const html = await response.text();
let match;
while ((match = regex.exec(html)) !== null) {
const episodeNumber = parseInt(match[1], 10);
const href = match[2].trim();
results.push({
href: "episode: " + href,
number: episodeNumber
});
}
if (results.length === 0) {
results.push({
href: "movie: " + url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
let endpointType;
if (url.startsWith("movie: ")) {
url = url.replace("movie: ", "");
endpointType = "movie";
} else if (url.startsWith("episode: ")) {
url = url.replace("episode: ", "");
endpointType = "tv";
} else {
return "ERROR";
}
const response = await fetchv2(url);
const html = await response.text();
const idMatch = html.match(/<link rel=['"]shortlink['"] href=['"][^?]+\?p=(\d+)['"]/);
if (!idMatch) return "ID NOT FOUND";
const id = idMatch[1];
const apiUrl = `https://animesdrive.blog/wp-json/dooplayer/v2/${id}/${endpointType}/1`;
const apiResponse = await fetchv2(apiUrl);
const apiData = await apiResponse.json();
console.log(JSON.stringify(apiData));
const embedResponse = await fetchv2(apiData.embed_url);
const embedHtml = await embedResponse.text();
const match = embedHtml.match(/<source\s+src="([^"]+)"\s+type="video\/mp4"/i);
const finalUrl = match ? match[1] : null;
console.log("Final URL: " + finalUrl);
return finalUrl
} catch (err) {
console.error("Error extracting stream URL:"+ err);
return "{ error: err.message }";
}
}
function cleanHtmlSymbols(string) {
if (!string) return "";
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AnimesDrive",
"iconUrl": "https://animesdrive.blog/wp-content/uploads/2025/08/cropped-ico-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://animesdrive.blog/",
"searchBaseUrl": "https://animesdrive.blog/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesdrive/animesdrive.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+144
View File
@@ -0,0 +1,144 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://animesonline.cloud/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="result-item">[\s\S]*?<a href="([^"]+)"[^>]*>\s*<img src="([^"]+)"[^>]*alt="([^"]+)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
image: match[2].trim(),
title: cleanHtmlSymbols(match[3].trim())
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<p>\s*Sinopse\s*:\s*([\s\S]*?)<\/p>/i;
const match = regex.exec(html);
const description = match ? match[1].trim() : "fuck off you don't need a description";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
const regex = /<div class=['"]?numerando['"]?[^>]*>\d+\s*-\s*(\d+)<\/div>[\s\S]*?<a\s+href=['"]([^'"]+)['"][^>]*>/g;
try {
const response = await fetchv2(url);
const html = await response.text();
let match;
while ((match = regex.exec(html)) !== null) {
const episodeNumber = parseInt(match[1], 10);
const href = match[2].trim();
results.push({
href: "episode: " + href,
number: episodeNumber
});
}
if (results.length === 0) {
results.push({
href: "movie: " + url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
let endpointType;
if (url.startsWith("movie: ")) {
url = url.replace("movie: ", "");
endpointType = "movie";
} else if (url.startsWith("episode: ")) {
url = url.replace("episode: ", "");
endpointType = "tv";
} else {
return "ERROR";
}
const response = await fetchv2(url);
const html = await response.text();
const idMatch = html.match(/<link rel=['"]shortlink['"] href=['"][^?]+\?p=(\d+)['"]/);
if (!idMatch) return "ID NOT FOUND";
const id = idMatch[1];
const apiUrl = `https://animesonline.cloud/wp-json/dooplayer/v2/${id}/${endpointType}/1`;
const apiResponse = await fetchv2(apiUrl);
const apiData = await apiResponse.json();
console.log(JSON.stringify(apiData));
if (!apiData.embed_url) return { error: "embed_url not found" };
const embedResponse = await fetchv2(apiData.embed_url);
const embedHtml = await embedResponse.text();
console.log(embedHtml);
const fileMatch = embedHtml.match(/"file":"(https?:\\\/\\\/[^"]+)"/);
if (!fileMatch) return { error: "file not found" };
const fileUrl = fileMatch[1].replace(/\\\//g, "/");
let finalUrl = encodeURI(fileUrl);
console.log(finalUrl);
return finalUrl;
} catch (err) {
console.error("Error extracting stream URL:"+ err);
return "{ error: err.message }";
}
}
function cleanHtmlSymbols(string) {
if (!string) return "";
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AnimesOnline",
"iconUrl": "https://animesonline.cloud/wp-content/uploads/2025/06/Icone.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://animesonline.cloud",
"searchBaseUrl": "https://animesonline.cloud",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesonline/animesonline.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+171
View File
@@ -0,0 +1,171 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2(
"https://api-search.anroll.net/data?q=" + encodeURIComponent(keyword)
);
const json = await response.json();
if (json.code === 200 && json.data && Array.isArray(json.data)) {
for (const item of json.data) {
if (item.generic_path && item.generic_path.startsWith("/f/")) {
continue;
}
results.push({
title: item.title,
image: `https://www.anroll.net/_next/image?url=${encodeURIComponent(
"https://static.anroll.net/images/animes/capas/" + item.slug + ".jpg"
)}&w=384&q=75`,
href: "https://www.anroll.net" + item.generic_path,
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([
{
title: "Error",
image: "Error",
href: "Error",
},
]);
}
}
async function extractDetails(url) {
try {
console.log(url);
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="sinopse">(.*?)<\/div>/s);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([
{
description: description,
aliases: "N/A",
airdate: "N/A",
},
]);
} catch (err) {
return JSON.stringify([
{
description: "Error",
aliases: "Error",
airdate: "Error",
},
]);
}
}
async function extractEpisodes(url) {
try {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const episodesMatch = html.match(/"episodes"\s*:\s*(\d+)/);
const episodes = episodesMatch ? parseInt(episodesMatch[1], 10) : null;
const idMatch = html.match(/"id_serie"\s*:\s*(\d+)/);
const serieId = idMatch ? parseInt(idMatch[1], 10) : null;
if (!episodes || !serieId) {
console.log("Failed to extract episode count or serie ID.");
return JSON.stringify([{ href: "Error", number: "Error" }]);
}
const totalPages = Math.ceil(episodes / 25);
const pagePromises = [];
for (let page = 1; page <= totalPages; page++) {
const apiUrl = `https://apiv3-prd.anroll.net/animes/${serieId}/episodes?page=${page}&order=desc`;
pagePromises.push(
fetchv2(apiUrl)
.then(response => response.json())
.then(json => {
console.log(`Fetched page ${page} of ${totalPages}...`);
if (json && json.data && Array.isArray(json.data)) {
return json.data.map(ep => ({
href: "https://www.anroll.net/watch/e/" + ep.generate_id,
number: parseInt(ep.n_episodio, 10),
}));
}
return [];
})
.catch(error => {
console.log(`Error fetching page ${page}:`, error);
return [];
})
);
}
const pageResults = await Promise.all(pagePromises);
pageResults.forEach(pageEpisodes => {
results.push(...pageEpisodes);
});
return JSON.stringify(results.reverse());
} catch (err) {
console.log("Error during fetch:", err);
return JSON.stringify([{ href: "Error", number: "Error" }]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const patterns = [
/"streamUrl"\s*:\s*"([^"]+\.m3u8)"/,
/"streamUrl\\?"\s*:\s*\\?"([^"\\]+\.m3u8)\\?"/,
/streamUrl['"]\s*:\s*['"](https?:\/\/[^'"]+\.m3u8)['"]/,
/streamUrl\s*:\s*"([^"]+\.m3u8)"/,
/\\"streamUrl\\":\\"([^"\\]+\.m3u8)\\"/
];
for (const pattern of patterns) {
const match = html.match(pattern);
if (match && match[1]) {
const streamUrl = match[1]
.replace(/\\"/g, '"')
.replace(/\\\//g, '/')
.replace(/\\\\/g, '\\');
return streamUrl;
}
}
const nextDataMatch = html.match(/self\.__next_f\.push\(\[1,"([^"]+)"\]\)/g);
if (nextDataMatch) {
for (const match of nextDataMatch) {
const dataMatch = match.match(/self\.__next_f\.push\(\[1,"([^"]+)"\]\)/);
if (dataMatch && dataMatch[1]) {
const decodedData = dataMatch[1]
.replace(/\\"/g, '"')
.replace(/\\\//g, '/')
.replace(/\\n/g, '\n');
const streamMatch = decodedData.match(/streamUrl['"]\s*:\s*['"](https?:\/\/[^'"]+\.m3u8)['"]/);
if (streamMatch && streamMatch[1]) {
return streamMatch[1];
}
}
}
}
return "https://files.catbox.moe/avolvc.mp4";
} catch (err) {
console.error('Error extracting stream URL:', err);
return "https://files.catbox.moe/avolvc.mp4";
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "AnimesRoll",
"iconUrl": "https://cdn.countryflags.com/thumbs/portugal/flag-button-round-250.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Portuguese",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.anroll.net/",
"searchBaseUrl": "https://www.anroll.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animesroll/animesroll.js",
"type": "animes",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+315
View File
@@ -0,0 +1,315 @@
function cleanTitle(title) {
return title
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "");
}
async function searchResults(keyword) {
const results = [];
const response = await fetchv2(`https://animeytx.net/?s=${keyword}`);
const html = await response.text();
const regex = /<article class="bs"[^>]*>[\s\S]*?<a href="([^"]+)"[^>]*>[\s\S]*?(?:data-src|src)="([^"]+)"[^>]*>[\s\S]*?<h2[^>]*>(.*?)<\/h2>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: cleanTitle(match[3].trim()),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="entry-content"[^>]*>([\s\S]*?)<\/div>/);
let description = "N/A";
if (match) {
description = match[1]
.replace(/<[^>]+>/g, '')
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(code))
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, "&")
.trim();
}
results.push({
description: description,
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const regex = /<li data-index="\d+">[\s\S]*?<a href="([^"]+)">/g;
let match;
let count = 1;
while ((match = regex.exec(html)) !== null) {
results.push({
href: encodeURI(match[1].trim()),
number: count
});
count++;
}
const total = results.length;
results.forEach((ep, i) => {
ep.number = total - i;
});
return JSON.stringify(results.reverse());
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(`https://passthrough-worker.simplepostrequest.workers.dev/?simple=${encodeURIComponent(url)}`);
const html = await response.text();
// Try primary method first
let iframeMatch = html.match(/<iframe[^>]+data-src="([^"]+)"/);
let filemoonUrl;
if (iframeMatch) {
const iframeUrl = iframeMatch[1];
const valueMatch = iframeUrl.match(/value=([^&]+)/);
if (valueMatch) {
const valueId = valueMatch[1];
const containerResponse = await fetchv2(`https://mytsumi.com/multiplayer/contenedor.php?id=${valueId}`);
const containerHtml = await containerResponse.text();
const filemoonMatch = containerHtml.match(/"tab_name":"Moon","url":"([^"]+)"/);
if (filemoonMatch) filemoonUrl = filemoonMatch[1].replace(/\\\//g, '/');
}
}
// Fallback if no Filemoon found
if (!filemoonUrl) {
const optionMatch = html.match(/<option[^>]+value="([^"]+)"[^>]*>\s*Moon\s*<\/option>/);
if (!optionMatch) return "https://error.org/";
const decoded = atob(optionMatch[1]);
const srcMatch = decoded.match(/src="([^"]+)"/);
if (!srcMatch) return "https://error.org/";
const fallbackUrl = srcMatch[1];
const fallbackResponse = await fetchv2(fallbackUrl);
const fallbackHtml = await fallbackResponse.text();
const filemoonMatch2 = fallbackHtml.match(/<a href="(https:\/\/filemoon\.to\/e\/[^"]+)">/);
if (!filemoonMatch2) return "https://error.org/";
filemoonUrl = filemoonMatch2[1];
}
console.log(filemoonUrl);
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url
};
const finalResponse = await fetchv2(filemoonUrl, headers);
const finalHtml = await finalResponse.text();
const streamUrl = await filemoonExtractor(finalHtml, filemoonUrl);
if (streamUrl) return streamUrl;
return "filemoonUrl";
} catch (err) {
return "https://error.org/";
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AnimeYTX",
"iconUrl": "https://i1.wp.com/animeytx.net/wp-content/uploads/2024/09/cropped-hgn584ghj45-1-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animeytx.net/",
"searchBaseUrl": "https://animeytx.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animeytx/animeytx.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+175
View File
@@ -0,0 +1,175 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://api-search.anroll.net/data?q=" + encodeURIComponent(keyword));
const data = await response.json();
data.data.forEach(item => {
const baseUrl = item.generic_path.trim().startsWith('/f/')
? "https://www.anroll.net/_next/image?url=https://static.anroll.net/images/filmes/capas/"
: "https://www.anroll.net/_next/image?url=https://static.anroll.net/images/animes/capas/";
results.push({
title: item.title.trim(),
image: baseUrl + item.slug.trim() + ".jpg&w=384&q=75",
href: item.generic_path.trim()
});
});
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(slug) {
try {
const response = await fetchv2("https://www.anroll.net/" + slug);
const html = await response.text();
const match = html.match(/<div class="sinopse">(.*?)<\/div>/s);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(slug) {
const results = [];
try {
// If slug starts with /f/ (movie), extract ID and return as single episode
if (slug.startsWith('/f/')) {
results.push({
href: slug,
number: 1
});
return JSON.stringify(results);
}
// Only process if slug starts with /a/ (anime)
if (!slug.startsWith('/a/')) {
return JSON.stringify(results);
}
const response = await fetchv2("https://www.anroll.net" + slug);
const html = await response.text();
// Extract series ID from __NEXT_DATA__
const scriptMatch = html.match(/<script id="__NEXT_DATA__" type="application\/json">({.*?})<\/script>/);
if (!scriptMatch) return JSON.stringify(results);
const jsonData = JSON.parse(scriptMatch[1]);
const seriesId = jsonData?.props?.pageProps?.data?.id_serie;
if (!seriesId) return JSON.stringify(results);
// Fetch all episodes from all pages
let page = 1;
let hasNextPage = true;
while (hasNextPage) {
const episodesResponse = await fetchv2(`https://apiv3-prd.anroll.net/animes/${seriesId}/episodes?page=${page}&order=desc`);
const episodesData = await episodesResponse.json();
if (episodesData.data && episodesData.data.length > 0) {
episodesData.data.forEach(episode => {
results.push({
href: episode.generate_id,
number: parseInt(episode.n_episodio, 10)
});
});
hasNextPage = episodesData.meta?.hasNextPage || false;
page++;
} else {
hasNextPage = false;
}
}
return JSON.stringify(results.reverse());
} catch (err) {
return JSON.stringify([]);
}
}
async function extractStreamUrl(ID) {
try {
if (!ID.startsWith('/f/')) {
const response = await fetchv2("https://www.anroll.net/watch/e/" + ID);
const html = await response.text();
const streamMatch = html.match(/streamUrl\\?":\\?"(https:\/\/[^"\\]+\.m3u8)/);
if (!streamMatch) return JSON.stringify({
streams: [{
title: "Server 1",
streamUrl: "https://error.org/no-stream-found",
headers: {}
}],
subtitle: ""
});
const streamUrl = streamMatch[1];
return JSON.stringify({
streams: [
{
title: "Server 1",
streamUrl: streamUrl,
headers: {
"Referer": "https://www.anroll.net/",
"Origin": "https://www.anroll.net"
}
}
],
subtitle: ""
});
}
const response = await fetchv2("https://www.anroll.net" + ID);
const html = await response.text();
const scriptMatch = html.match(/<script id="__NEXT_DATA__" type="application\/json">({.*?})<\/script>/);
if (!scriptMatch) return "https://error.org/no-next-data";
const jsonData = JSON.parse(scriptMatch[1]);
const movieData = jsonData?.props?.pageProps?.data?.data_movie;
if (!movieData || !movieData.slug_filme) {
return "https://error.org/no-movie-data";
}
const slug = movieData.slug_filme;
const streamUrl = `https://cdn-zenitsu-2-gamabunta.b-cdn.net/cf/hls/movies/${slug}/movie.mp4/media-1/stream.m3u8`;
return JSON.stringify({
streams: [
{
title: "Server 1",
streamUrl: streamUrl,
headers: {
"Referer": "https://www.anroll.net/",
"Origin": "https://www.anroll.net"
}
}
],
subtitle: ""
});
} catch (err) {
return "https://error.org/";
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AnimesRoll",
"iconUrl": "https://files.catbox.moe/9lx2bp.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.anroll.net/",
"searchBaseUrl": "https://www.anroll.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/anroll/anroll.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+229
View File
@@ -0,0 +1,229 @@
async function searchResults(keyword) {
const results = [];
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Referer": "https://arablionztv.cam/"
};
const postData = "";
try {
const response = await fetchv2("https://arablionztv.cam/SearchEngine/"+encodeURIComponent(keyword), headers, "POST", postData);
const html = await response.text();
const regex = /<a href="([^"]+)"[^>]*>\s*<div class="Box--Poster">\s*<img[^>]+data-image="([^"]+)"[^>]*>\s*.*?<h2>(.*?)<\/h2>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<p class="Singular--Story-P">(.*?)<\/p>/s;
const match = html.match(regex);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
try {
return JSON.stringify([{
href: url,
number: 1
}]);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const idMatch = html.match(/data-id="(\d+)"/);
if (!idMatch) return "https://error.org/";
const id = idMatch[1];
const apiUrl = "https://passthrough-worker.simplepostrequest.workers.dev/?arablionz=https://arablionztv.cam/PostServersWatch/" + id;
const apiResponse = await fetchv2(apiUrl);
const apiHtml = await apiResponse.text();
const iframeMatch = apiHtml.match(/<iframe[^>]+src="([^"]+)"/);
if (!iframeMatch) return "https://error.org/";
const iframeUrl = iframeMatch[1];
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Referer": "https://arablionztv.cam/"
};
const streamResponse = await fetchv2(iframeUrl, headers);
const streamHtml = await streamResponse.text();
const obfuscatedScript = streamHtml.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript) return "https://error.org/";
const unpackedScript = unpack(obfuscatedScript[1]);
const fileMatch = unpackedScript.match(/file:\s*"([^"]+)"/);
if (!fileMatch) return "https://error.org/";
const fileUrl = fileMatch[1];
console.log(fileUrl);
return fileUrl;
} catch (err) {
return "https://error.org/";
}
}
/***********************************************************
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
***********************************************************/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "ArabLionz",
"iconUrl": "https://arablionztv.cam/wp-content/uploads/2023/04/vRA6ewKk_400x400-280x280.jpeg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://arablionztv.cam/",
"searchBaseUrl": "https://arablionztv.cam/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/arablionz/arablionz.js",
"type": "shows/movies",
"asyncJS": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true
}
+156
View File
@@ -0,0 +1,156 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://a.asd.homes/find/?word=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<li class="box__xs__2[^>]*>[\s\S]*?<a href="([^"]+)"[^>]*>[\s\S]*?<img[^>]+src="([^"]+)"[^>]*>[\s\S]*?<h3>(.*?)<\/h3>[\s\S]*?<\/a>/g;
let match;
const tempResults = [];
while ((match = regex.exec(html)) !== null) {
const cleanedTitle = match[3].replace(/الموسم\s+\S+\s+الحلقة\s+\S+.*$/u, '').trim();
tempResults.push({
href: match[1].trim(),
image: match[2].trim(),
title: cleanedTitle
});
}
const combined = [];
const seen = new Set();
for (const item of tempResults) {
if (!seen.has(item.title)) {
seen.add(item.title);
combined.push(item);
}
}
return JSON.stringify(combined);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="post__story">\s*<p>(.*?)<\/p>\s*<\/div>/s);
let description = "N/A";
if (match) {
const rawDescription = match[1];
description = rawDescription.replace(/<\/?span[^>]*>/g, '').trim();
description = description.replace(/\s+/g, ' ');
}
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const episodesListMatch = html.match(/<ul class="episodes__list[^>]*>([\s\S]*?)<\/ul>/);
if (!episodesListMatch) {
results.push({
href: url,
number: 1
});
return JSON.stringify(results);
}
const episodesHTML = episodesListMatch[1];
const episodeItemRegex = /<li[^>]*>[\s\S]*?<a href="([^"]+)"[\s\S]*?الحلقة<b>(\d+)<\/b>[\s\S]*?<\/a>[\s\S]*?<\/li>/g;
let match;
const episodes = [];
while ((match = episodeItemRegex.exec(episodesHTML)) !== null) {
const href = match[1].trim();
const episodeNumber = parseInt(match[2]);
episodes.push({
href: href,
number: episodeNumber
});
}
episodes.sort((a, b) => a.number - b.number);
results.push(...episodes);
if (results.length === 0) {
results.push({
href: url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: url,
number: 1
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/href="([^"]+)"[^>]*class="btton watch__btn"/);
console.log("Match found: " + match);
if (match) {
const extractedUrl = match[1].replace(/&amp;/g, '&');
const headers = {
"Referer": "https://a.asd.homes/"
};
const extractedResponse = await fetchv2(extractedUrl, headers);
const extractedHtml = await extractedResponse.text();
console.log("Extracted HTML snippet:"+ extractedHtml);
const embedMatch = extractedHtml.match(/<iframe[^>]*src="([^"]+)"/);
if (embedMatch) {
const embedUrl = embedMatch[1];
const embedResponse = await fetchv2(embedUrl, headers);
const embedHtml = await embedResponse.text();
const sourceMatch = embedHtml.match(/<source src="([^"]+)"/);
if (sourceMatch) {
return sourceMatch[1];
}
}
}
} catch (err) {
console.log(err);
}
return "https://files.catbox.moe/avolvc.mp4";
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "ArabSeed",
"iconUrl": "https://a.asd.homes/wp-content/themes/Elshaikh2021/UI/images/logo333.webp",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.2",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://a.asd.homes/",
"searchBaseUrl": "https://a.asd.homes/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/arabseed/arabseed.js",
"type": "shows/movies/anime",
"asyncJS": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true
}
+295
View File
@@ -0,0 +1,295 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://asialiveaction.com/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const containerMatch = html.match(/<ul class="navegacion-grid">([\s\S]*?)<\/ul>/);
if (!containerMatch) return JSON.stringify([]);
const containerHtml = containerMatch[1];
const regex = /<a href="([^"]+)"[^>]*>[\s\S]*?<h5 class="carousel-title">([\s\S]*?)<\/h5>[\s\S]*?<img[^>]+src="([^"]+)"/g;
let match;
while ((match = regex.exec(containerHtml)) !== null) {
results.push({
title: match[2].trim(),
image: match[3].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const titleMatch = html.match(/<h2 class="Title">\s*([\s\S]*?)\s*<\/h2>/);
const descriptionMatch = html.match(/<p>([\s\S]*?)<\/p>/);
const description = descriptionMatch ? descriptionMatch[1].trim() : "N/A";
const title = titleMatch ? titleMatch[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: title,
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const listMatch = html.match(/<div class="lista-episodios">([\s\S]*?)<\/div>\s*<\/div>/);
if (!listMatch) return JSON.stringify([]);
const listHtml = listMatch[1];
const regex = /<a href="([^"]+)">[\s\S]*?<b class="numero-episodio">Episodio (\d+)<\/b>/g;
let match;
while ((match = regex.exec(listHtml)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{ href: "Error", number: "Error" }]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/\["FM","(https:\\?\/\\?\/filemoon\.to\\?\/e\\?\/[^"]+)"/);
const filemoonLink = match[1].replace(/\\\//g, "/");
console.log(filemoonLink);
const response2 = await fetchv2(filemoonLink);
const html2 = await response2.text();
let streamUrl = null;
try {
streamUrl = await filemoonExtractor(html2, filemoonLink);
} catch (error) {
console.log("filemoon HD extraction error:" + error);
}
console.log("filemoon Stream URL: " + streamUrl);
if (streamUrl && streamUrl !== false && streamUrl !== null) {
return streamUrl;
}
console.log("No stream URL found");
return null;
} catch (error) {
console.log("Fetch error:", error);
return null;
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
@@ -0,0 +1,21 @@
{
"sourceName": "AsiaLiveAction",
"iconUrl": "https://asialiveaction.com/wp-content/uploads/cropped-favicon-200x200.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://asialiveaction.com/",
"searchBaseUrl": "https://asialiveaction.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/asialiveaction/asialiveaction.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+108
View File
@@ -0,0 +1,108 @@
async function searchContent(keyword, page=0) {
const results = [];
try {
const response = await fetch(`https://atsu.moe/collections/manga/documents/search?q=${encodeURIComponent(keyword)}&limit=250&query_by=title%2CenglishTitle%2CotherNames&query_by_weights=3%2C2%2C1&include_fields=id%2Ctitle%2CenglishTitle%2Cposter%2CposterSmall%2CposterMedium%2Ctype%2CisAdult&num_typos=4%2C3%2C2&highlight_full_fields=title%2CenglishTitle%2CotherNames`);
const data = await response.json();
if (Array.isArray(data.hits)) {
for (const hit of data.hits) {
const doc = hit.document;
results.push({
id: doc.id,
imageURL: "https://atsu.moe" + doc.poster,
title: doc.title || doc.englishTitle || ""
});
}
}
return results;
} catch (err) {
return [];
}
}
async function getContentData(ID) {
try {
const response = await fetch(`https://atsu.moe/manga/${ID}`);
const html = await response.text();
const scriptRegex = /window\.mangaPage\s*=\s*(\{[\s\S]*?\});/;
const scriptMatch = scriptRegex.exec(html);
let description = "";
let tags = [];
if (scriptMatch) {
try {
const mangaPageObj = JSON.parse(scriptMatch[1]);
const page = mangaPageObj.mangaPage;
description = page.synopsis || "";
if (Array.isArray(page.tags)) {
tags = page.tags.map(tag => tag.name);
}
} catch (e) {
}
}
return {
description,
tags
};
} catch (err) {
return {
description: "Error",
tags: []
};
}
}
async function getChapters(ID) {
const results = [];
try {
const firstResponse = await fetch(`https://atsu.moe/api/manga/chapters?id=${ID}&filter=all&sort=desc&page=0`);
const firstData = await firstResponse.json();
const totalPages = firstData.pages || 1;
let allChapters = firstData.chapters || [];
const fetchPromises = [];
for (let p = 1; p < totalPages; p++) {
fetchPromises.push(
fetch(`https://atsu.moe/api/manga/chapters?id=${ID}&filter=all&sort=desc&page=${p}`).then(r => r.json())
);
}
const pageResults = await Promise.all(fetchPromises);
for (const pageData of pageResults) {
if (Array.isArray(pageData.chapters)) {
allChapters = allChapters.concat(pageData.chapters);
}
}
const total = allChapters.length;
for (let i = 0; i < total; i++) {
const chapter = allChapters[i];
results.push([
String(total - i - 1),
[{
id: "https://atsu.moe/api/read/chapter?mangaId=" + ID + "&chapterId=" + chapter.id,
title: chapter.title,
chapter: chapter.number,
scanlation_group: chapter.title
}]
]);
}
return { en: results.reverse() };
} catch (err) {
return { en: [] };
}
}
async function getChapterImages(url) {
const results = [];
try {
const response = await fetch(url);
const data = await response.json();
if (data.readChapter && Array.isArray(data.readChapter.pages)) {
for (const page of data.readChapter.pages) {
results.push("https://atsu.moe" + page.image);
}
}
return results;
} catch (err) {
return [];
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"sourceName": "Atsu",
"iconURL": "https://atsu.moe/favicon/apple-touch-icon-180x180.png",
"version": "1.0",
"language": "English",
"scriptURL": "https://git.luna-app.eu/50n50/sources/raw/branch/main/atsu/atsu.js",
"author": {
"name": "50/50",
"iconURL": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"supportsLuna": true
}
+227
View File
@@ -0,0 +1,227 @@
async function searchResults(keyword) {
const results = [];
let pageNumber = 1;
let hasMore = true;
try {
while (hasMore) {
const headers = {
"next-action": await extractNextActionIdentifier(),
"Content-Type": "text/plain"
};
console.log(JSON.stringify(headers));
const postData = `[${pageNumber},"${keyword}",""]`;
const response = await fetchv2("https://mapple.site/audiobooks?search=" + encodeURIComponent(keyword), headers, "POST", postData);
const text = await response.text();
console.log("Page " + pageNumber + " response:", text);
const lines = text.split('\n').filter(line => line.trim());
let foundBooks = false;
for (const line of lines) {
try {
const data = JSON.parse(line.substring(line.indexOf('{')));
if (data.books && Array.isArray(data.books)) {
foundBooks = true;
for (const book of data.books) {
let title = book.title || "Untitled";
title = title.replace(/\s+free\s+online\s*$/i, '')
.replace(/\s+audiobook\s+free\s+online\s*$/i, '')
.replace(/\s+audiobook\s+free\s*$/i, '')
.replace(/\s+free\s+audiobook\s*$/i, '')
.replace(/\s+free\s*$/i, '')
.replace(/\s+\(free\s*\)\s*$/i, '')
.replace(/\s+free\s+read\s+/i, ' read ')
.replace(/\s+\(.*?free.*?\)\s*$/i, '')
.replace(/\s+audiobook\s*$/i, '')
.trim();
results.push({
title: title,
image: book.image || "",
href: "https://mapple.site/listen/" + (book.$id || "") + "/" + encodeURIComponent(book.title || "untitled")
});
}
hasMore = data.hasMore === true;
console.log("hasMore: " + hasMore);
}
} catch (e) {
}
}
if (!foundBooks) {
hasMore = false;
}
pageNumber++;
}
return JSON.stringify(results);
} catch (err) {
console.log("Search error:", err);
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
return JSON.stringify([{
description: "",
aliases: "",
airdate: ""
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const fetchResponse = await fetchv2(url, {
"rsc": "1"
});
const fetchText = await fetchResponse.text();
const audioItemsMatch = fetchText.match(/"audio_items":\[([^\]]+)\]/);
if (audioItemsMatch && audioItemsMatch[1]) {
const audioItemsStr = audioItemsMatch[1];
const urlMatches = audioItemsStr.match(/"(https?:\/\/[^"]+)"/g);
if (urlMatches) {
for (let i = 0; i < urlMatches.length; i++) {
const audioUrl = urlMatches[i].replace(/"/g, '');
results.push({
href: audioUrl,
number: i + 1,
title: `Chapter ${i + 1}`
});
}
}
}
return JSON.stringify(results);
} catch (err) {
console.log("Episodes error:", err);
return JSON.stringify([{
href: "Error",
number: "Error",
title: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const headers = {
"Host": "ipaudio.club",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "cross-site",
"Priority": "u=4",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
};
const streams = [{
title: `Audio Stream 1`,
streamUrl: url,
headers: headers
}];
return JSON.stringify({
streams: streams,
subtitle: ""
});
} catch (err) {
console.log("Stream error:", err);
return JSON.stringify({
streams: [{
title: "Error",
streamUrl: "",
headers: {}
}],
subtitle: ""
});
}
}
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) {
try {
return await fetchv2(
url,
options.headers ?? {},
options.method ?? 'GET',
options.body ?? null,
true,
options.encoding ?? 'utf-8'
);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
async function extractNextActionIdentifier() {
const htmlResponse = await soraFetch(atob("aHR0cHM6Ly9tYXBwbGUuc2l0ZS9hdWRpb2Jvb2tz"));
const htmlText = await htmlResponse.text();
const pageMatch = htmlText.match(/<script[^>]*src="([^"]*app\/audiobooks\/page-[^"]*\.js)"[^>]*><\/script>/);
if (!pageMatch || !pageMatch[1]) {
throw new Error("error 1");
}
const beforePageMatch = htmlText.match(/<script[^>]*src="([^"]*\.js)"[^>]*><\/script><script[^>]*src="[^"]*app\/audiobooks\/page-[^"]*\.js"/);
if (!beforePageMatch || !beforePageMatch[1]) {
throw new Error("error 2");
}
let targetUrl = beforePageMatch[1];
if (targetUrl.startsWith('/_next/')) {
targetUrl = 'https://mapple.site' + targetUrl;
} else if (!targetUrl.startsWith('http')) {
targetUrl = 'https://mapple.site/' + targetUrl;
}
try {
const response = await soraFetch(targetUrl);
const text = await response.text();
let actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"[^"]*"getStreamUrl/);
if (!actionMatch) {
actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"/);
}
if (!actionMatch) {
actionMatch = text.match(/"([a-f0-9]{40,})"[^"]*"getStreamUrl/);
}
if (actionMatch && actionMatch[1]) {
return actionMatch[1];
}
} catch (e) {
throw new Error("error 3: " + e);
}
throw new Error("error 4");
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "Audible",
"iconUrl": "https://m.media-amazon.com/images/I/51iKw5dFQoL.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English",
"streamType": "MP3",
"quality": "Audio",
"baseUrl": "https://www.audible.co.uk/",
"searchBaseUrl": "https://www.audible.co.uk/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/audible/audible.js",
"type": "audiobooks",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+312
View File
@@ -0,0 +1,312 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://www.az-animex.com/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const ulMatch = html.match(/<ul class="columns-4 wp-block-post-template[^"]*">([\s\S]*?)<\/ul>/);
if (!ulMatch) return JSON.stringify(results);
const ulContent = ulMatch[1];
const liRegex = /<li class="wp-block-post[^"]*">([\s\S]*?)<\/li>/g;
let liMatch;
while ((liMatch = liRegex.exec(ulContent)) !== null) {
const liHtml = liMatch[1];
const itemMatch = liHtml.match(
/<a href="([^"]+)"[^>]*>\s*<img[^>]+src="([^"]+)"[^>]*>[\s\S]*?<h2[^>]*>\s*<a href="[^"]+"[^>]*>([^<]+)<\/a>/i
);
if (itemMatch) {
results.push({
href: itemMatch[1].trim(),
image: itemMatch[2].trim(),
title: itemMatch[3].replace(/\[.*?\]/g, '').trim()
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = html.match(/<div class="su-spoiler-content[^"]*">([\s\S]*?)<\/div>/i);
const description = descMatch
? descMatch[1].replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim()
: "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const initialResponse = await fetchv2(url);
const initialHtml = await initialResponse.text();
const filemoonButtonRegex = /<a\s+href="(https:\/\/filemoon\.(?:sx|to)\/[^"]+)"\s+class="su-button\s+su-button-style-glass"/i;
const filemoonMatch = filemoonButtonRegex.exec(initialHtml);
if (!filemoonMatch) {
throw new Error('Filemoon button not found');
}
const filemoonUrl = filemoonMatch[1];
const filemoonResponse = await fetchv2(filemoonUrl);
const filemoonHtml = await filemoonResponse.text();
const episodeRegex = /<li class="d-flex flex-wrap align-items-center justify-content-between mb-2">[\s\S]*?<h4>([^<]+)<\/h4>[\s\S]*?<a class="btn btn-success"[^>]*href="([^"]+)"[\s\S]*?<\/li>/g;
let match;
let episodeCounter = 1;
while ((match = episodeRegex.exec(filemoonHtml)) !== null) {
let href = match[2].trim();
href = href.replace('/d/', '/e/');
results.push({
href: href,
number: episodeCounter
});
episodeCounter++;
}
return JSON.stringify(results);
} catch (err) {
console.error('Error extracting episodes:', err);
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
let streamUrl = null;
try {
streamUrl = await filemoonExtractor(html, url);
} catch (error) {
console.log("filemoon HD extraction error:" + error);
}
console.log("filemoon Stream URL: " + streamUrl);
if (streamUrl && streamUrl !== false && streamUrl !== null) {
return streamUrl;
}
console.log("No stream URL found");
return null;
} catch (error) {
console.log("Fetch error:", error);
return null;
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "AZ-Animex",
"iconUrl": "https://www.az-animex.com/wp-content/uploads/2023/11/Logo-icono-144x144-1.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.az-animex.com/",
"searchBaseUrl": "https://www.az-animex.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/az-animex/az-animex.js",
"type": "Anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+103
View File
@@ -0,0 +1,103 @@
async function searchResults(keyword) {
const results = [];
const baseUrl = "https://www.beatz-anime.net";
try {
const response = await fetchv2("https://www.beatz-anime.net/busqueda.php");
const html = await response.text();
const cardRegex = /<div class="col-lg-2 mb-4">([\s\S]*?)<\/div>\s*<\/div>/gs;
let cardMatch;
while ((cardMatch = cardRegex.exec(html)) !== null) {
const cardHTML = cardMatch[1];
const infoRegex = /<a href="([^"]+)">.*?<span class="titulo"[^>]*>([^<]+)<\/span>.*?<img[^>]+src="([^"]+)"/s;
const infoMatch = infoRegex.exec(cardHTML);
if (infoMatch) {
const title = infoMatch[2].trim();
if (title.toLowerCase().includes(keyword.toLowerCase())) {
results.push({
href: baseUrl + infoMatch[1].trim(),
title: title,
image: baseUrl + infoMatch[3].trim()
});
}
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const pMatch = html.match(/<p class="post-text"[^>]*>([\s\S]*?)<\/p>/);
let description = "N/A";
let aliases = "N/A";
if (pMatch) {
const content = pMatch[1];
const descMatch = content.split(/<span class="mr-3"/)[0].replace(/<br\s*\/?>/gi, "\n").trim();
if (descMatch) description = descMatch;
const aliasMatch = content.match(/<b>Sinónimos:<\/b>\s*([^<]+)/);
if (aliasMatch) aliases = aliasMatch[1].trim();
}
return JSON.stringify([{
description: description,
aliases: aliases,
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const rows = html.match(/<tbody>([\s\S]*?)<\/tbody>/)?.[1].match(/<tr>[\s\S]*?<\/tr>/g) || [];
rows.forEach((row, index) => {
const hrefMatch = row.match(/<td class="text-center"><a href="([^"]+)"/);
const href = hrefMatch ? hrefMatch[1] : "N/A";
results.push({
href: href,
number: index + 1
});
});
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
return url;
} catch (err) {
return "https://error.org/";
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"sourceName": "Beatz-Anime",
"iconUrl": "https://files.catbox.moe/vij8dw.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.beatz-anime.net/",
"searchBaseUrl": "https://www.beatz-anime.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/beatz-anime/beatz-anime.js",
"type": "Anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"note": "Use external player!",
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "Tsumi (詰み) - Literally Everything 2.0",
"iconUrl": "https://files.catbox.moe/krovkt.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.7",
"language": "English",
"streamType": "HLS",
"quality": "4K",
"baseUrl": "https://google.com/",
"searchBaseUrl": "https://google.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/checkmate/checkmate.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+247
View File
@@ -0,0 +1,247 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://www.cinecalidad.ec/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const containerMatch = html.match(/<div id="archive-content"[^>]*>([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/);
if (containerMatch) {
const container = containerMatch[1];
const regex = /<article[^>]*>([\s\S]*?)<\/article>/g;
let articleMatch;
while ((articleMatch = regex.exec(container)) !== null) {
const articleContent = articleMatch[1];
const imageMatch = articleContent.match(/data-src="([^"]+)"/);
const hrefMatch = articleContent.match(/<a href="([^"]+)"/);
const titleMatch = articleContent.match(/<div class="in_title">([^<]+)<\/div>/);
if (imageMatch && hrefMatch && titleMatch) {
results.push({
title: titleMatch[1].trim(),
image: imageMatch[1].trim(),
href: hrefMatch[1].trim()
});
}
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = html.match(/<p><p>([^<]+)<\/p>/);
const description = descMatch ? descMatch[1].trim() : "N/A";
return JSON.stringify([{
description
}]);
} catch (err) {
return JSON.stringify([{
description: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const seasonRegex = /<div id="jstab"[^>]*>([\s\S]*?)<\/div>\s*<\/div>/g;
let seasonMatch;
while ((seasonMatch = seasonRegex.exec(html)) !== null) {
const seasonContent = seasonMatch[1];
const episodeRegex = /<a href="([^"]+)"[^>]*>Episodio \d+<\/a>/g;
let episodeMatch;
let episodeCount = 1;
while ((episodeMatch = episodeRegex.exec(seasonContent)) !== null) {
results.push({
href: episodeMatch[1].trim(),
number: episodeCount
});
episodeCount++;
}
}
if (results.length === 0) {
results.push({
href: url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/data-option="(https:\/\/lamovie\.link\/embed-[^"]+)"/);
if (!match) return "https://error.org/";
const embedUrl = match[1];
const embedResponse = await fetchv2(embedUrl);
const embedHtml = await embedResponse.text();
const obfuscatedScript = embedHtml.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!obfuscatedScript) return "https://error.org/";
const unpackedScript = unpack(obfuscatedScript[1]);
const fileMatch = unpackedScript.match(/file:\s*"([^"]+)"/);
if (!fileMatch) return "https://error.org/";
return fileMatch[1];
} catch (err) {
return "https://error.org/";
}
}
/***********************************************************
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
***********************************************************/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "CineCalidad",
"iconUrl": "https://www.cinecalidad.ec/wp-content/themes/Cinecalidad/assets/img/favicon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.cinecalidad.ec/",
"searchBaseUrl": "https://www.cinecalidad.ec/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cinecalidad/cinecalidad.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+105
View File
@@ -0,0 +1,105 @@
async function searchResults(keyword) {
const results = [];
const response = await fetchv2(`https://cksub.org/?s=${keyword}`);
const html = await response.text();
const regex = /<article class="bs"[^>]*>.*?<a href="([^"]+)"[^>]*>.*?<img src="([^"]+)"[^>]*>.*?<h2[^>]*>(.*?)<\/h2>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="entry-content"[^>]*>([\s\S]*?)<\/div>/);
let description = "N/A";
if (match) {
description = match[1]
.replace(/<[^>]+>/g, '')
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(code))
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, "&")
.trim();
}
results.push({
description: description,
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a href="([^"]+)">\s*<div class="epl-num">([\d.]+)<\/div>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
results.reverse();
return JSON.stringify(results);
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const iframeMatch = html.match(/dailymotion\.com\/player\/[a-zA-Z0-9]+\.html\?video=([a-zA-Z0-9]+)/); if (!iframeMatch) return "no iframe";
const videoId = iframeMatch[1];
const metaRes = await fetchv2(`https://www.dailymotion.com/player/metadata/video/${videoId}`);
const metaJson = await metaRes.json();
const hlsLink = metaJson.qualities?.auto?.[0]?.url;
if (!hlsLink) return "no hls";
async function getBestHls(hlsUrl) {
try {
const res = await fetchv2(hlsUrl);
const text = await res.text();
const regex = /#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+).*?\n(https?:\/\/[^\n]+)/g;
const streams = [];
let match;
while ((match = regex.exec(text)) !== null) {
streams.push({ width: parseInt(match[1]), height: parseInt(match[2]), url: match[3] });
}
if (streams.length === 0) return hlsUrl;
streams.sort((a, b) => b.height - a.height);
return streams[0].url;
} catch {
return hlsUrl;
}
}
const bestHls = await getBestHls(hlsLink);
return bestHls;
} catch {
const empty = "{ streams: [";
console.log("Extracted stream result:" + JSON.stringify(empty));
return JSON.stringify(empty);
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "CKSub",
"iconUrl": "https://cksub.org/wp-content/uploads/2024/12/cropped-i-am-the-fated-villain-gu-changge-192x192.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Chinese",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://cksub.org/",
"searchBaseUrl": "https://cksub.org/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cksub/cksub.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+214
View File
@@ -0,0 +1,214 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://cloudy.pk/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="image-hover-wrapper">[\s\S]*?<a href="([^"]+)"[^>]*>[\s\S]*?<img [^>]*src="([^"]+)"[^>]*>[\s\S]*?<h2 class="entry-title"><a href="[^"]+"[^>]*>([^<]+)<\/a><\/h2>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
image: match[2].trim(),
title: match[3].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<p[^>]*><strong>([\s\S]*?)<\/strong><\/p>/);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<a [^>]*href="(https:\/\/videospk[^"]+)"[^>]*>.*?<\/a>/);
if (match) {
results.push({
href: match[1].trim(),
number: 1
});
} else {
results.push({
href: url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const obfuscatedScript = html.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const streamMatch = unpackedScript.match(/["'](\/stream\/[^"']+)["']/);
const hlsLink = streamMatch ? streamMatch[1] : null;
console.log("HLS Link:" + hlsLink);
return "https://videospk.xyz" + hlsLink;
} catch (err) {
return "https://files.catbox.moe/avolvc.mp4";
}
}
/***********************************************************
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
***********************************************************/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "Cloudy",
"iconUrl": "https://cloudy.pk/wp-content/uploads/2018/02/cloudy.pk-icon-200x200.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Hindi (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://cloudy.pk/",
"searchBaseUrl": "https://cloudy.pk/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/cloudy/cloudy.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+280
View File
@@ -0,0 +1,280 @@
// Settings start
const preferedQualityOption = "Auto"; // ["Auto", "2160p", "1080p", "720p", "480p"]
const maxResultsPerResolution = 0;
const maxSize = 0;
const cachedOnly = false; // [true, false]
const removeTrash = true; // [true, false]
const resultFormat = "all"; // ["all"]
const debridService = "realdebrid"; // ["realdebrid", "alldebrid", "premiumize", "torbox", "debridlink"]
const debridApiKey = "";
const debridStreamProxyPassword = "";
const languagesExclude = "";
const languagesPreferred = "en";
const removeRanksUnder = -10000000000;
const allowEnglishInLanguages = false; // [true, false]
const removeUnknownLanguages = false; // [true, false]
// Settings end
function btoa(str) {
if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function" && globalThis.btoa !== btoa) {
return globalThis.btoa(str);
}
if (typeof Buffer !== "undefined") {
return Buffer.from(str, "utf8").toString("base64");
}
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let result = "";
let i = 0;
while (i < str.length) {
const byte1 = str.charCodeAt(i++) & 0xff;
const hasByte2 = i < str.length;
const byte2 = hasByte2 ? str.charCodeAt(i++) & 0xff : 0;
const hasByte3 = i < str.length;
const byte3 = hasByte3 ? str.charCodeAt(i++) & 0xff : 0;
const chunk = (byte1 << 16) | (byte2 << 8) | byte3;
result += chars[(chunk >> 18) & 63];
result += chars[(chunk >> 12) & 63];
result += hasByte2 ? chars[(chunk >> 6) & 63] : "=";
result += hasByte3 ? chars[chunk & 63] : "=";
}
return result;
}
async function searchResults(keyword) {
try {
const moviesresponse = await fetchv2(
"https://v3-cinemeta.strem.io/catalog/movie/top/search=" + encodeURIComponent(keyword) + ".json"
);
const moviesdata = await moviesresponse.json();
const results = moviesdata.metas.map(item => ({
title: item.name.trim(),
image: item.poster.trim(),
href: "Movie: " + (item.id.startsWith("tt") ? item.id : "")
}));
const showsresponse = await fetchv2(
"https://v3-cinemeta.strem.io/catalog/series/top/search=" + encodeURIComponent(keyword) + ".json"
);
const showsdata = await showsresponse.json();
const showResults = showsdata.metas.map(item => ({
title: item.name.trim(),
image: item.poster.trim(),
href: "TV: " + (item.id.startsWith("tt") ? item.id : "")
}));
results.push(...showResults);
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(ID) {
try {
let decodedID = decodeURIComponent(ID);
let actualID = decodedID;
let type = "movie";
if (decodedID.startsWith("Movie: ")) {
actualID = decodedID.replace("Movie: ", "");
type = "movie";
} else if (decodedID.startsWith("TV: ")) {
actualID = decodedID.replace("TV: ", "");
type = "series";
}
const url = "https://v3-cinemeta.strem.io/meta/" + type + "/" + actualID + ".json";
const response = await fetchv2(url);
const data = await response.json();
return JSON.stringify([{
description: data.meta.description || "N/A",
aliases: "N/A",
airdate: data.meta.released || "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(ID) {
let decodedID = decodeURIComponent(ID);
let actualID = decodedID;
let type = "movie";
if (decodedID.startsWith("Movie: ")) {
actualID = decodedID.replace("Movie: ", "");
type = "movie";
} else if (decodedID.startsWith("TV: ")) {
actualID = decodedID.replace("TV: ", "");
type = "series";
}
const results = [];
try {
if (type === "series") {
const response = await fetchv2("https://v3-cinemeta.strem.io/meta/series/" + actualID + ".json");
const data = await response.json();
const videos = data.meta.videos || [];
const shouldAdjust = videos.length > 0 && videos[0].season === 0;
let currentSeason = 0;
let episodeCounter = 0;
for (const video of videos) {
const adjustedSeason = shouldAdjust ? video.season + 1 : video.season;
if (adjustedSeason !== currentSeason) {
currentSeason = adjustedSeason;
episodeCounter = 0;
}
episodeCounter++;
let adjustedId = video.id || "";
if (adjustedId && shouldAdjust) {
const idParts = adjustedId.split(':');
if (idParts.length === 3) {
idParts[1] = String(adjustedSeason);
adjustedId = idParts.join(':');
}
}
results.push({
href: "TV: " + adjustedId,
number: episodeCounter
});
}
return JSON.stringify(results);
} else if (type === "movie") {
return JSON.stringify([{
href: "Movie: " + (actualID || ""),
number: 1
}]);
}
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(ID) {
let decodedID = decodeURIComponent(ID);
let actualID = decodedID;
let type = "movie";
if (decodedID.startsWith("Movie: ")) {
actualID = decodedID.replace("Movie: ", "");
type = "movie";
} else if (decodedID.startsWith("TV: ")) {
actualID = decodedID.replace("TV: ", "");
type = "series";
}
const config = {
maxResultsPerResolution: maxResultsPerResolution,
maxSize: maxSize,
cachedOnly: cachedOnly,
removeTrash: removeTrash,
resultFormat: [resultFormat],
debridService: debridService,
debridApiKey: debridApiKey,
debridStreamProxyPassword: debridStreamProxyPassword,
languages: {
exclude: languagesExclude ? languagesExclude.split(",").map(s => s.trim()) : [],
preferred: languagesPreferred ? languagesPreferred.split(",").map(s => s.trim()) : ["en"]
},
resolutions: {},
options: {
remove_ranks_under: removeRanksUnder,
allow_english_in_languages: allowEnglishInLanguages,
remove_unknown_languages: removeUnknownLanguages
}
};
const encodedConfig = btoa(JSON.stringify(config));
try {
const endpoint = type === "movie"
? "https://comet.feels.legal/" + encodedConfig + "/stream/movie/" + actualID + ".json"
: "https://comet.feels.legal/" + encodedConfig + "/stream/series/" + actualID + ".json";
const response = await fetchv2(endpoint);
const data = await response.json();
if (!data.streams || !Array.isArray(data.streams)) {
return JSON.stringify({
streams: [],
subtitle: "https://none.com"
});
}
const streamsByQuality = {
"2160p": [],
"1080p": [],
"720p": [],
"480p": []
};
for (const stream of data.streams) {
const name = stream.name || "";
let quality = null;
if (name.includes("2160p")) quality = "2160p";
else if (name.includes("1080p")) quality = "1080p";
else if (name.includes("720p")) quality = "720p";
else if (name.includes("480p")) quality = "480p";
if (quality && streamsByQuality[quality]) {
streamsByQuality[quality].push({
title: stream.name || "Unknown",
streamUrl: stream.url || "",
headers: {}
});
}
}
let results = [];
if (preferedQualityOption === "Auto") {
results.push(...streamsByQuality["2160p"].slice(0, 5));
results.push(...streamsByQuality["1080p"].slice(0, 5));
results.push(...streamsByQuality["720p"].slice(0, 5));
results.push(...streamsByQuality["480p"].slice(0, 5));
} else {
if (streamsByQuality[preferedQualityOption]) {
results = streamsByQuality[preferedQualityOption].slice(0, 10);
}
}
return JSON.stringify({
streams: results,
subtitle: "https://none.com"
});
} catch (err) {
return JSON.stringify({
streams: [],
subtitle: "https://none.com"
});
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "Comet",
"iconUrl": "https://i.imgur.com/jmVoVMu.jpeg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.2",
"language": "English",
"streamType": "MKV",
"quality": "4K",
"baseUrl": "https://www.google.com/",
"searchBaseUrl": "https://www.google.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/comet/comet.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"settings": true,
"supportsLuna": true
}
+82
View File
@@ -0,0 +1,82 @@
async function searchResults(keyword) {
const searchUrl = `https://ddys.pro/?s=${encodeURIComponent(keyword)}`;
try {
const response = await fetchv2(searchUrl);
const html = await response.text();
const results = [];
const articleRegex = /<article id="post-\d+"[^>]*>[\s\S]*?<h2 class="post-title"><a href="([^"]+)"[^>]*>([^<]+)<\/a><\/h2>/g;
let match;
while ((match = articleRegex.exec(html)) !== null) {
const href = match[1].trim();
const title = match[2].trim();
const imageUrl = "https://i.ibb.co/Y4b38sTG/Search-has-no-images.png";
results.push({
title,
image: imageUrl,
href
});
}
console.log(results);
return JSON.stringify(results);
} catch (error) {
throw error;
}
}
async function extractDetails(url) {
const response = await fetchv2(url);
const html = await response.text();
const aliasMatch = html.match(/又名:\s*([^<]+)/);
const descriptionMatch = html.match(/简介:\s*([\s\S]*?)<\/div>/);
const airdateMatch = html.match(/年份:\s*(\d{4})/);
const alias = aliasMatch ? aliasMatch[1].trim() : "N/A";
const description = descriptionMatch ? descriptionMatch[1].trim() : "No description available.";
const airdate = airdateMatch ? airdateMatch[1].trim() : "N/A";
const details = [{
alias,
description,
airdate
}];
console.log(JSON.stringify(details));
return JSON.stringify(details);
}
async function extractEpisodes(url) {
const response = await fetchv2(url);
const html = await response.text();
const episodes = [];
const scriptMatch = html.match(/<script class="wp-playlist-script" type="application\/json">(\{[\s\S]*?\})<\/script>/);
if (scriptMatch) {
const jsonData = JSON.parse(scriptMatch[1]);
jsonData.tracks.forEach(track => {
if (track.src0) {
const episodeMatch = track.src0.match(/S01E(\d+)/) || track.caption.match(/\u7b2c(\d+)\u96c6/);
const episodeNumber = episodeMatch ? parseInt(episodeMatch[1], 10) : null; // Convert to integer
episodes.push({
href: `https://v.ddys.pro${track.src0.trim()}`,
number: episodeNumber
});
}
});
}
console.log(episodes);
return JSON.stringify(episodes);
}
async function extractStreamUrl(url) {
return url;
}
+19
View File
@@ -0,0 +1,19 @@
{
"sourceName": "DDYS",
"iconUrl": "https://ddys.pro/apple-touch-icon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Chinese",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://ddys.pro/",
"searchBaseUrl": "https://ddys.pro/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/ddys/ddys.js",
"asyncJS": true,
"type": "movies/shows",
"supportsSora": true,
"supportsLuna": true
}
+152
View File
@@ -0,0 +1,152 @@
async function searchResults(keyword) {
const results = [];
const response = await fetchv2(`https://desu-online.pl/?s=${keyword}`);
const html = await response.text();
const regex = /<article class="bs"[^>]*>.*?<a href="([^"]+)"[^>]*>.*?<img src="([^"]+)"[^>]*>.*?<h2[^>]*>(.*?)<\/h2>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="entry-content"[^>]*>([\s\S]*?)<\/div>/);
let description = "N/A";
if (match) {
description = match[1]
.replace(/<[^>]+>/g, '')
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(code))
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, "&")
.trim();
}
results.push({
description: description,
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a href="([^"]+)">\s*<div class="epl-num">([\d.]+)<\/div>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
results.reverse();
return JSON.stringify(results);
}
async function extractStreamUrl(url) {
const response = await fetchv2(url);
const html = await response.text();
async function getBestHls(hlsUrl) {
try {
const res = await fetchv2(hlsUrl);
const text = await res.text();
const regex = /#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+).*?\n(https?:\/\/[^\n]+)/g;
let match;
const streams = [];
while ((match = regex.exec(text)) !== null) {
const width = parseInt(match[1]);
const height = parseInt(match[2]);
const url = match[3];
streams.push({ width, height, url });
}
if (streams.length === 0) return hlsUrl;
streams.sort((a, b) => b.height - a.height);
return streams[0].url;
} catch (err) {
return hlsUrl;
}
}
async function extractVideoIdFromIframely(iframelyUrl) {
try {
const iframeRes = await fetchv2(iframelyUrl);
const iframeHtml = await iframeRes.text();
const canonicalMatch = iframeHtml.match(/<meta name="canonical" content="https:\/\/www\.dailymotion\.com\/video\/([^"]+)"/);
return canonicalMatch ? canonicalMatch[1] : null;
} catch (err) {
return null;
}
}
const optionRegex = /<option value="([^"]+)"[^>]*>\s*DailyMotion\s*<\/option>/g;
const videoOptions = [];
let match;
while ((match = optionRegex.exec(html)) !== null) {
const base64Value = match[1];
try {
const decodedHtml = atob(base64Value);
const srcMatch = decodedHtml.match(/<iframe src="([^"]+)"/);
if (srcMatch) {
const iframeUrl = srcMatch[1];
const videoId = await extractVideoIdFromIframely("https:" + iframeUrl);
if (videoId) {
videoOptions.push({
videoId: videoId,
label: "Polish Hardsub"
});
}
}
} catch (err) {
continue;
}
}
const streams = [];
for (const option of videoOptions) {
try {
const metaRes = await fetchv2(`https://www.dailymotion.com/player/metadata/video/${option.videoId}`);
const metaJson = await metaRes.json();
const hlsLink = metaJson.qualities?.auto?.[0]?.url;
if (!hlsLink) continue;
const bestHls = await getBestHls(hlsLink);
streams.push(option.label);
streams.push(bestHls);
} catch (err) {
continue;
}
}
return JSON.stringify({
streams: streams,
subtitles: ""
});
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "Desu-Online",
"iconUrl": "https://desu-online.pl/wp-content/uploads/2021/03/53454540000-300x300.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Polish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://desu-online.pl/",
"searchBaseUrl": "https://desu-online.pl/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/desu-online/desu-online.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+246
View File
@@ -0,0 +1,246 @@
function decodeHtmlEntities(text) {
return text
.replace(/&#8217;/g, "'")
.replace(/&#8220;/g, '"')
.replace(/&#8221;/g, '"')
.replace(/&#8230;/g, '...')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&#(\d+);/g, (_, num) => String.fromCharCode(num));
}
async function searchResults(keyword) {
const results = [];
const regex = /<li class="border-radius-5 box-shadow">[\s\S]*?<img[^>]+src="([^"]+)"[^>]+title="([^"]+)"[^>]*>[\s\S]*?<a[^>]+href="([^"]+)"[^>]*>/gi;
try {
const response = await fetchv2("https://www1.divxfilmeonline.net/?s=" + encodeURIComponent(keyword));
const html = await response.text();
regex.lastIndex = 0;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
image: match[1].trim(),
title: decodeHtmlEntities(match[2].trim()),
href: match[3].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
return JSON.stringify([{
description: "Description not available",
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const filemoonMatch = /<li[^>]*class="server server-active"[^>]*data-vs="([^"]*fastvid\.co[^"]*)"[^>]*>Filemoon/i.exec(html);
if (filemoonMatch) {
return JSON.stringify([{
href: filemoonMatch[1].trim(),
number: 1
}]);
} else {
throw new Error("Filemoon episode not found");
}
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error",
message: err.message
}]);
}
}
async function extractStreamUrl(url) {
try {
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://www1.divxfilmeonline.net/"
};
const response = await fetchv2(url, headers);
const html = await response.text();
const iframeMatch = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/i.exec(html);
if (!iframeMatch) throw new Error("Iframe not found");
const iframeUrl = iframeMatch[1].trim();
const iframeResponse = await fetchv2(iframeUrl, headers);
const iframeContent = await iframeResponse.text();
console.log(`[Debug] Iframe content: ${iframeContent}`);
const scriptMatch = iframeContent.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!scriptMatch) throw new Error("Obfuscated script not found");
const evalContent = scriptMatch[1];
const unpackedScript = unpack(evalContent);
const fileMatch = /sources:\s*\[\s*\{\s*file:\s*"([^"]+\.m3u8[^"]*)"/.exec(unpackedScript);
if (!fileMatch) throw new Error("Stream URL not found");
const subtitleMatch = /tracks:\s*\[\s*\{\s*file:\s*"([^"]+\.vtt[^"]*)"/.exec(unpackedScript);
return JSON.stringify({
stream: fileMatch[1],
subtitles: subtitleMatch ? subtitleMatch[1] : null
});
} catch (err) {
console.error("Error extracting stream URL:", err);
return JSON.stringify({
stream: "https://files.catbox.moe/avolvc.mp4",
subtitles: null
});
}
}
/*
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
*/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
@@ -0,0 +1,21 @@
{
"sourceName": "DivXFilmeOnline",
"iconUrl": "https://www1.divxfilmeonline.net/wp-content/uploads/2015/11/cropped-movies-40-180x180.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Romanian (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://divxfilmeonline.net/",
"searchBaseUrl": "https://divxfilmeonline.net/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/divxfilmeonline/divxfilmeonline.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true
}
+116
View File
@@ -0,0 +1,116 @@
async function searchResults(keyword) {
const results = [];
try {
const workerUrl = "https://passthrough-worker.simplepostrequest.workers.dev/?url=" +
encodeURIComponent("https://www.dmand5.com/index.php?m=vod-search") +
"&type=multipart&body=" + encodeURIComponent(JSON.stringify({
wd: keyword
}));
const response = await fetchv2(workerUrl);
const html = await response.text();
const regex = /<a href="(\/detail\/\d+\.html)" title="(.*?)" target="_blank"><img .*?src="(.*?)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[2].trim(),
image: match[3].trim(),
href: "https://www.dmand5.com" + match[1].trim()
});
}
console.log("Search results:" + JSON.stringify(results));
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = html.match(/<div class="des">(.*?)<\/div>/s);
const description = descMatch ? descMatch[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const blockMatch = html.match(/<ul class="mn_list_li_movie"[^>]*>([\s\S]*?)<\/ul>/);
const blockHtml = blockMatch ? blockMatch[1] : "";
const regex = /<a href="(\/play\/\d+-\d+-(\d+)\.html)"[^>]*>第\d+集<\/a>/g;
let match;
while ((match = regex.exec(blockHtml)) !== null) {
results.push({
href: parseInt(match[2], 10) + " https://www.dmand5.com" + match[1].trim(),
number: parseInt(match[2], 10)
});
}
console.log("Episodes:" + JSON.stringify(results));
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const leadingNumberMatch = url.match(/^(\d+)\s+(.+)$/);
const episodeNumber = leadingNumberMatch ? parseInt(leadingNumberMatch[1], 10) : 1;
const cleanUrl = leadingNumberMatch ? leadingNumberMatch[2] : url;
const fetchUrl = cleanUrl;
console.log(fetchUrl);
const response = await fetchv2(fetchUrl);
const html = await response.text();
const macMatch = html.match(/mac_url\s*=\s*unescape\(\s*'([^']+)'\s*\)/s);
if (!macMatch) return "https://files.catbox.moe/avolvc.mp4";
let raw = macMatch[1];
raw = raw.replace(/%u([\dA-F]{4})/gi, (_, g1) =>
String.fromCharCode(parseInt(g1, 16))
);
const decoded = decodeURIComponent(raw);
const episodes = decoded.split("#");
const epEntry = episodes[episodeNumber - 1] || episodes[0];
const hlsUrl = epEntry.split("$")[1];
return hlsUrl || "https://files.catbox.moe/avolvc.mp4";
} catch (err) {
return "https://files.catbox.moe/avolvc.mp4";
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "Dmand5",
"iconUrl": "https://www.dmand5.com/template/dmd8pc/images/logo.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Chinese",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.dmand5.com/",
"searchBaseUrl": "https://www.dmand5.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/dmand5/dmand5.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+107
View File
@@ -0,0 +1,107 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2(`https://donghuanosekai.com/wp-json/site/search/?keyword=${encodeURIComponent(keyword)}&type=undefined&nonce=4c4380bfaa`);
const data = await response.json();
for (const key in data) {
if (data.hasOwnProperty(key)) {
const item = data[key];
results.push({
title: item.title,
image: item.img,
href: item.url
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<div class="context">\s*<p>([\s\S]*?)<\/p>/i;
const match = html.match(regex);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a href="([^"]+)"[^>]*>\s*.*?<span class="episode">Episódio\s+(\d+)<\/span>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
return JSON.stringify(results.reverse());
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const divRegex = /<div class="slideItem"[^>]*data-video-url="([^"]+)"[^>]*>\s*Player 2\s*<\/div>/i;
const divMatch = html.match(divRegex);
if (!divMatch) return "https://error.org/";
const playerUrl = divMatch[1].trim();
const headers = {
"Referer": url,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
};
const playerResponse = await fetchv2(playerUrl, headers);
const playerHtml = await playerResponse.text();
const iframeRegex = /<iframe[^>]+src="([^"]+)"/i;
const iframeMatch = playerHtml.match(iframeRegex);
if (!iframeMatch) return "https://error.org/";
const iframeSrc = iframeMatch[1];
const m3u8Regex = /v=(https:\/\/[^&"]+\.m3u8[^&"]*)/i;
const m3u8Match = iframeSrc.match(m3u8Regex);
return m3u8Match ? decodeURIComponent(m3u8Match[1]) : "https://error.org/";
} catch {
return "https://error.org/";
}
}
@@ -0,0 +1,22 @@
{
"sourceName": "Donghanosekai",
"iconUrl": "https://files.catbox.moe/vgb4mq.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://donghuanosekai.com/",
"searchBaseUrl": "https://donghuanosekai.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/donghuanosekai/donghuanosekai.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "DoraBash",
"iconUrl": "https://dorabash.com/wp-content/uploads/2023/06/cropped-Untitled_design-removebg-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.3",
"language": "Hindi",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://dorabash.com/",
"searchBaseUrl": "https://dorabash.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/dorabash/dorabash.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+307
View File
@@ -0,0 +1,307 @@
async function searchResults(keyword) {
const results = [];
const url = "https://www.doramasyt.com/buscar?q=" + encodeURIComponent(keyword);
try {
const response = await fetchv2(url);
const html = await response.text();
console.log(html);
const regex = /<li class="col mb-3 ficha_efecto">\s*<article>\s*<a href="([^"]+)"[\s\S]*?<img[^>]*data-src="([^"]+)"[^>]*>[\s\S]*?<h3[^>]*>(.*?)<\/h3>[\s\S]*?<\/a>\s*<\/article>\s*<\/li>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: cleanTitle(match[3].trim().replace(/<[^>]*>/g, '')),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
function cleanTitle(title) {
return title
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "");
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = html.match(/<h1[^>]*>([^<]+)<\/h1>/);
const aliasMatch = html.match(/<span>([^<]+)<\/span>/);
const description = descMatch ? descMatch[1].trim() : "N/A";
const aliases = aliasMatch ? aliasMatch[1].trim() : "N/A";
return JSON.stringify([{
description: cleanTitle(description),
aliases: cleanTitle(aliases),
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await networkFetch(url, {
timeoutSeconds: 2,
returnHTML: true
});
const html = await response.html;
const regex = /<a class="ko chapter-link" href="([^"]+)"[^>]*data-episode="(\d+)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const responseOne = await fetchv2(url);
const htmlOne = await responseOne.text();
const matchOne = htmlOne.match(/<button[^>]*data-player="([^"]+)"[^>]*>filemoon<\/button>/i);
const filemoonData = matchOne ? matchOne[1] : null;
const response = await fetchv2("https://www.doramasyt.com/reproductor?video=" + encodeURIComponent(filemoonData));
const html = await response.text();
const matchIframe = html.match(/<iframe[^>]*src="([^"]*filemoon\.sx\/e\/[^"]+)"/i);
const filemoonUrl = matchIframe ? matchIframe[1] : null;
console.log(filemoonUrl);
const responseTwo = await fetchv2(filemoonUrl);
const htmlTwo = await responseTwo.text();
let streamUrl = null;
try {
streamUrl = await filemoonExtractor(htmlTwo, filemoonUrl);
} catch (error) {
console.log("filemoon HD extraction error:" + error);
}
console.log("filemoon Stream URL: " + streamUrl);
if (streamUrl && streamUrl !== false && streamUrl !== null) {
return streamUrl;
}
console.log("No stream URL found");
return null;
} catch (error) {
console.log("Fetch error:", error);
return null;
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "DoramasYT",
"iconUrl": "https://files.catbox.moe/afzsms.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://www.doramasyt.com/",
"searchBaseUrl": "https://www.doramasyt.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/doramasyt/doramasyt.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
@@ -0,0 +1,243 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://egydead.com.co/search?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<a href="([^"]+)"[^>]*style="background-image:\s*url\(([^)]+)\)[^"]*"[^>]*>[\s\S]*?<p class="title">(.*?)<\/p>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
image: match[2].trim(),
title: match[3].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<span class="description">\s*([\s\S]*?)\s*<\/span>/i;
const match = regex.exec(html);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a href="([^"]+)" class="btn watch">/i;
const match = regex.exec(html);
if (match) {
return JSON.stringify([{
href: match[1].trim(),
number: 1
}]);
}
return JSON.stringify([]);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
console.log(html);
const jsonRegex = /let servers = JSON\.parse\('(\[.*?\])'\);/;
const jsonMatch = jsonRegex.exec(html);
if (jsonMatch) {
const serversJson = jsonMatch[1].replace(/\\/g, '');
const servers = JSON.parse(serversJson);
const fdewsdcServer = servers.find(server => server.name === "EarnVids");
if (fdewsdcServer) {
console.log("Stream URL: " + fdewsdcServer.url);
return await extractEarnVids(fdewsdcServer.url);
}
}
} catch (err) {
console.error(err);
}
}
async function extractEarnVids(url) {
const headers = {
"Referer": "https://shahed4u.day/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"
};
try {
const response = await fetchv2(url, headers);
const html = await response.text();
const obfuscatedScript = html.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const streamMatch = unpackedScript.match(/["'](\/stream\/[^"']+)["']/);
const hlsLink = streamMatch ? streamMatch[1] : null;
const baseUrl = url.match(/^(https?:\/\/[^/]+)/)[1];
console.log("HLS Link:" + baseUrl + hlsLink);
return baseUrl + hlsLink;
} catch (err) {
console.log(err);
return "https://files.catbox.moe/avolvc.mp4";
}
}
/***********************************************************
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
***********************************************************/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
@@ -0,0 +1,20 @@
{
"sourceName": "EgyDead (Fasel reuploader)",
"iconUrl": "https://tv3.egydead.live/wp-content/uploads/2019/01/cropped-yXYdE2f-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://egydead.com.co/",
"searchBaseUrl": "https://egydead.com.co/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/egydead%20(Fasel%20reuploader)/egydead.js",
"type": "shows/movies/anime",
"asyncJS": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true
}
+254
View File
@@ -0,0 +1,254 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://tv3.egydead.live/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<li class="movieItem">\s*<a href="([^"]+)" title="([^"]+)">\s*<img src="([^"]+)">/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
title: match[2].trim(),
image: match[3].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="extra-content">\s*<span>القصه<\/span>\s*<p>([\s\S]*?)<\/p>/);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a href="([^"]+)"[^>]*>\s*حلقه\s*(\d+)\s*<\/a>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
if (results.length === 0) {
results.push({
href: url,
number: 1
});
}
return JSON.stringify(results.reverse());
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/",
"Referer": url
};
const postData = "View=1";
const response = await fetchv2(url, header, "POST", postData);
const html = await response.text();
const earnvidsMatch = html.match(/<li[^>]*data-link=["']([^"']+)["'][^>]*>\s*<span>\s*<p>\s*EarnVids\s*<\/p>/i);
const embedResponse = await fetchv2(earnvidsMatch[1], header);
const embedHtml = await embedResponse.text();
if (earnvidsMatch) {
const stream = await earnvidsExtractor(embedHtml, url);
return stream;
}
return null;
} catch (err) {
console.error("Error in extractStreamUrl:", err);
return "https://files.catbox.moe/avolvc.mp4";
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name earnvidsExtractor
* @author 50/50
*/
async function earnvidsExtractor(html, url = null) {
try {
const obfuscatedScript = html.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
const unpackedScript = unpack(obfuscatedScript[1]);
const streamMatch = unpackedScript.match(/["'](\/stream\/[^"']+)["']/);
const hlsLink = streamMatch ? streamMatch[1] : null;
const baseUrl = url.match(/^(https?:\/\/[^/]+)/)[1];
console.log("HLS Link:" + baseUrl + hlsLink);
return baseUrl + hlsLink;
} catch (err) {
console.log(err);
return "https://files.catbox.moe/avolvc.mp4";
}
}
/* REMOVE_START */
/***********************************************************
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
***********************************************************/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
/* REMOVE_END */
/* SCHEME END */
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "EgyDead",
"iconUrl": "https://tv3.egydead.live/wp-content/uploads/2019/01/cropped-yXYdE2f-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://tv3.egydead.live/",
"searchBaseUrl": "https://tv3.egydead.live/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/egydead/egydead.js",
"type": "shows/movies/anime",
"asyncJS": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+102
View File
@@ -0,0 +1,102 @@
function cleanTitle(title) {
return title
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "");
}
async function searchResults(keyword) {
const results = [];
const response = await fetchv2(`https://estrenosdoramas.es/?s=${keyword}`);
const html = await response.text();
const regex = /<article[^>]*class="[^"]*bs[^"]*"[^>]*>.*?<a href="([^"]+)"[^>]*>.*?<div class="ttzz">\s*(.*?)\s*<\/div>.*?<img[^>]*src="([^"]+)"[^>]*>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: cleanTitle(match[2].trim()),
image: match[3].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="entry-content"[^>]*>([\s\S]*?)<\/div>/);
let description = "N/A";
if (match) {
description = match[1]
.replace(/<[^>]+>/g, '')
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(code))
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, "&")
.trim();
}
results.push({
description: description,
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const regex = /<li[^>]*id="epcheck"[^>]*>[\s\S]*?<a href="([^"]+)">/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push(match[1].trim());
}
const total = results.length;
const final = results.map((href, index) => ({
href: href,
number: total - index
}));
return JSON.stringify(final.reverse());
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const embedMatch = html.match(/<div class="pembed" data-embed="([^"]+)"/);
if (!embedMatch) return "error";
const embedUrl = embedMatch[1].trim();
console.log(embedUrl);
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": url
};
const embedResponse = await fetchv2(embedUrl.replace("//", "https://"), headers);
const embedHtml = await embedResponse.text();
console.log(embedHtml);
const fileMatch = embedHtml.match(/sources:\s*\[\{file:"([^"]+)"/);
if (!fileMatch) return "error";
return fileMatch[1];
} catch (err) {
console.log(err);
return "error";
}
}
@@ -0,0 +1,21 @@
{
"sourceName": "EstrenosDoramas",
"iconUrl": "https://i0.wp.com/estrenosdoramas.es/wp-content/uploads/2023/12/cropped-unnamed-7-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://vidmoly.to/",
"searchBaseUrl": "https://vidmoly.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/estrenosdoramas/estrenosdoramas.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+289
View File
@@ -0,0 +1,289 @@
function decodeHtmlEntities(text) {
return text
.replace(/&#8217;/g, "'")
.replace(/&#8220;/g, '"')
.replace(/&#8221;/g, '"')
.replace(/&#8230;/g, '...')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&#(\d+);/g, (_, num) => String.fromCharCode(num));
}
async function searchResults(keyword) {
const results = [];
const regex = /<article[^>]*class="item (movies|seasons)"[^>]*>[\s\S]*?<img\s+src="([^"]+)"[^>]*title="([^"]+)"[^>]*>[\s\S]*?<a\s+href="([^"]+)"[^>]*>/gi;
try {
const response = await fetchv2("https://filmehd.to/?s=" + encodeURIComponent(keyword));
const html = await response.text();
regex.lastIndex = 0;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
image: match[2].trim(),
title: decodeHtmlEntities(match[3].trim()),
href: match[4].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = /<div class="wp-content"[^>]*>\s*<p>(.*?)<\/p>/i.exec(html);
const description = match ? match[1].trim() : "No description found";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const episodes = [];
const iframeMatch = html.match(/<iframe[^>]+src\s*=\s*"([^"]*movies\/iframe\/[^"]*?)"/i);
if (iframeMatch) {
episodes.push({
href: iframeMatch[1].trim(),
number: 1
});
} else {
const allDataVs = [];
const dataVsRegex = /data-vs\s*=\s*"([^"]*?)"/g;
let match;
while ((match = dataVsRegex.exec(html)) !== null) {
allDataVs.push(match[1].trim());
}
const allNumbers = [];
const numberRegex = /<span[^>]*class="servers"[^>]*>(\d+)<\/span>/g;
while ((match = numberRegex.exec(html)) !== null) {
allNumbers.push(parseInt(match[1], 10));
}
const server1Start = html.indexOf('SERVER 1');
let server1End = html.indexOf('SERVER 2');
if (server1End === -1) server1End = html.length;
if (server1Start !== -1) {
const server1Section = html.substring(server1Start, server1End);
const server1Count = (server1Section.match(/<span[^>]*class="servers"/g) || []).length;
for (let i = 0; i < server1Count && i < allDataVs.length && i < allNumbers.length; i++) {
episodes.push({
href: allDataVs[i],
number: allNumbers[i]
});
}
}
}
episodes.sort((a, b) => a.number - b.number);
if (episodes.length === 0) {
throw new Error("No episodes found");
}
return JSON.stringify(episodes);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error",
message: err.message
}]);
}
}
async function extractStreamUrl(url) {
try {
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Referer": "https://filmehd.to/"
};
const response = await fetchv2(url, headers);
const html = await response.text();
const iframeMatch = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/i.exec(html);
if (!iframeMatch) throw new Error("Iframe not found");
const iframeUrl = iframeMatch[1].trim();
const iframeResponse = await fetchv2(iframeUrl, headers);
const iframeContent = await iframeResponse.text();
console.log(`[Debug] Iframe content: ${iframeContent}`);
const scriptMatch = iframeContent.match(/<script[^>]*>\s*(eval\(function\(p,a,c,k,e,d.*?\)[\s\S]*?)<\/script>/);
if (!scriptMatch) throw new Error("Obfuscated script not found");
const evalContent = scriptMatch[1];
const unpackedScript = unpack(evalContent);
const fileMatch = /sources:\s*\[\s*\{\s*file:\s*"([^"]+\.m3u8[^"]*)"/.exec(unpackedScript);
if (!fileMatch) throw new Error("Stream URL not found");
const subtitleMatch = /tracks:\s*\[\s*\{\s*file:\s*"([^"]+\.vtt[^"]*)"/.exec(unpackedScript);
return JSON.stringify({
stream: fileMatch[1],
subtitles: subtitleMatch ? subtitleMatch[1] : null
});
} catch (err) {
console.error("Error extracting stream URL:", err);
return JSON.stringify({
stream: "https://files.catbox.moe/avolvc.mp4",
subtitles: null
});
}
}
/*
* UNPACKER MODULE
* Credit to GitHub user "mnsrulz" for Unpacker Node library
* https://github.com/mnsrulz/unpacker
*/
class Unbaser {
constructor(base) {
/* Functor for a given base. Will efficiently convert
strings to natural numbers. */
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
// fill elements 37...61, if necessary
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
// If base can be handled by int() builtin, let it do it for us
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
// Build conversion dictionary cache
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
/* Decodes a value to an integer. */
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function detect(source) {
/* Detects whether `source` is P.A.C.K.E.R. coded. */
return source.replace(" ", "").startsWith("eval(function(p,a,c,k,e,");
}
function unpack(source) {
/* Unpacks P.A.C.K.E.R. packed js code. */
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
/* Look up symbols in the synthetic symtab. */
const word = match;
let word2;
if (radix == 1) {
//throw Error("symtab unknown");
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
/* Juice from a source file the four args needed by decoder. */
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
//const args = re.search(juicer, source, re.DOTALL);
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
//don't know what it is
// a = list(a);
// a[1] = 62;
// a = tuple(a);
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
/* Strip string lookup table (list) and replace values in source. */
/* Need to work on this. */
return source;
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "FilmeHD",
"iconUrl": "https://filmehd.to/wp-content/uploads/2020/01/favicon.ico",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "3.0.1",
"language": "Romanian (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://filmehd.to/",
"searchBaseUrl": "https://filmehd.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/filmehd/filmehd.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true
}
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "FlixLatam",
"iconUrl": "https://flixlatam.com/wp-content/uploads/2022/04/cropped-Series-Latinoamerica-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://flixlatam.com/",
"searchBaseUrl": "https://flixlatam.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/flixlatam/flixlatam.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+140
View File
@@ -0,0 +1,140 @@
async function searchResults(keyword) {
try {
const response = await fetchv2("https://api.franime.fr/api/animes");
const data = await response.json();
const results = data
.filter(anime => (anime.title?.toLowerCase().includes(keyword.toLowerCase())) ||
(anime.titleO?.toLowerCase().includes(keyword.toLowerCase())))
.map(anime => ({
href: anime.id.toString(),
title: (anime.title || anime.titleO),
image: anime.affiche
}));
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(id) {
try {
const response = await fetchv2("https://api.franime.fr/api/animes");
const data = await response.json();
const anime = data.find(item => item.id === Number(id));
if (!anime) {
return JSON.stringify([{
description: "Not found",
aliases: "N/A",
airdate: "N/A"
}]);
}
return JSON.stringify([{
description: anime.description || "N/A",
aliases: anime.titles ? Object.values(anime.titles).join(", ") : "N/A",
airdate: anime.startDate || "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(id) {
const results = [];
try {
const response = await fetchv2("https://api.franime.fr/api/animes");
const data = await response.json();
const anime = data.find(item => item.id === Number(id));
if (!anime || !anime.saisons) {
return JSON.stringify([{
href: "N/A",
number: "N/A"
}]);
}
anime.saisons.forEach((season, seasonIndex) => {
season.episodes.forEach((episode, episodeIndex) => {
results.push({
href: `${id}/${seasonIndex}/${episodeIndex}`,
number: episodeIndex + 1
});
});
});
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(id) {
try {
const headers = {
"Host": "api.franime.fr",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:143.0) Gecko/20100101 Firefox/143.0",
"Referer": "https://franime.fr/",
"Content-Type": "application/json",
"Origin": "https://franime.fr",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"Priority": "u=0",
"TE": "trailers"
};
async function fetchUntilMp4(type) {
let i = 0;
while (i < 10) {
const url = `https://api.franime.fr/api/anime/${id}/${type}/${i}`;
const res = await fetchv2(url, headers);
const text = await res.text();
console.log(type.toUpperCase(), i, text);
if (text.includes("Aucun lecteur disponible")) return null;
if (text.endsWith(".mp4")) return text;
i++;
}
}
const vf = await fetchUntilMp4("vf");
const vo = await fetchUntilMp4("vo");
const streams = [];
if (vf) streams.push("VF", vf);
if (vo) streams.push("VOSTFR", vo);
const final = {
streams,
subtitles: ""
};
console.log("RETURN: " + JSON.stringify(final));
return JSON.stringify(final);
} catch (error) {
console.log("Error in extractStreamUrl:", error);
return JSON.stringify({
streams: [],
subtitles: ""
});
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"sourceName": "Franime",
"iconUrl": "https://franime.fr/logos/apple-touch-icon.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.3",
"language": "French (DUB/SUB)",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://franime.fr/",
"searchBaseUrl": "https://franime.fr/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/franime/franime.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsMojuru": true,
"supportsSora": true,
"supportsLuna": true
}
+281
View File
@@ -0,0 +1,281 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://funmovieslix.com/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<a href="([^"]+)" class="poster-wrap">[\s\S]*?<img[^>]+src="([^"]+)"[^>]*>[\s\S]*?<h3 class="title">([^<]+)<\/h3>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = html.match(/<div class="entry-content entry-content-single"[^>]*>[\s\S]*?<p>([\s\S]*?)<\/p>/);
const description = descMatch ? descMatch[1].trim() : "N/A";
const dateMatch = html.match(/<time itemprop="dateCreated"[^>]*>([^<]+)<\/time>/);
const airdate = dateMatch ? dateMatch[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: airdate
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a class="button button-shadow" href="([^"]+)"[^>]*>S\d+\sEps?(\d+)<\/a>/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: parseInt(match[2], 10)
});
}
if (results.length === 0) {
results.push({
href: url,
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<iframe src="(https:\/\/filemoon\.(?:to|sx)\/e\/[^"]+)"/);
const filemoonUrl = match ? match[1] : "https://files.catbox.moe/avolvc.mp4";
console.log("Filemoon URL:"+ filemoonUrl);
const filemoonResponse = await fetchv2(filemoonUrl);
const filemoonHtml = await filemoonResponse.text();
return filemoonExtractor(filemoonHtml, filemoonUrl) || filemoonUrl;
} catch (err) {
const fallback = "https://files.catbox.moe/avolvc.mp4";
console.log("Filemoon URL:"+ fallback);
return fallback;
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "FunMoviesLix",
"iconUrl": "https://funmovieslix.com/wp-content/uploads/2023/12/cropped-download.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Hindi",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://funmovieslix.com/",
"searchBaseUrl": "https://funmovieslix.com/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/funmovieslix/funmovieslix.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+358
View File
@@ -0,0 +1,358 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://ww3.gnulahd.nu/?s=" + keyword);
const html = await response.text();
const regex = /<a href="([^"]+)"[^>]*itemprop="url"[\s\S]*?<img src="(https:\/\/i[0-9]+\.wp\.com\/[^"]+)"[^>]*>[\s\S]*?<h2[^>]*itemprop="headline">([^<]+)<\/h2>/gi;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
image: match[2].trim(),
title: match[3].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const match = html.match(/<div class="mindesc">\s*<p>([\s\S]*?)<\/p>\s*<\/div>/);
const description = match ? match[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<li[^>]*>\s*<a href="([^"]+)">/g;
let match;
let count = 1;
while ((match = regex.exec(html)) !== null) {
results.push({
href: match[1].trim(),
number: count
});
count++;
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
const dummyLink = "https://files.catbox.moe/avolvc.mp4";
try {
const response = await fetchv2(url);
const html = await response.text();
const iframeMatch = html.match(/<iframe[^>]+src="([^"]+embed\.php[^"]*)"[^>]*>/i);
if (!iframeMatch) {
return dummyLink;
}
const fjireo = await fetchv2(iframeMatch[1]);
const kitri = await fjireo.text();
const videoArrayMatches = kitri.match(/var videos(\w+) = (\[.*?\]);/g);
if (!videoArrayMatches) {
console.log("Could not find video arrays");
return dummyLink;
}
const providers = {};
function processVideoArray(videoArrayString, language) {
try {
const videoArray = JSON.parse(videoArrayString);
videoArray.forEach(([providerName, url]) => {
const idMatch = url.match(/id=([A-Za-z0-9+/=]+)/);
if (idMatch) {
const base64Id = idMatch[1];
const decodedUrl = atob(base64Id);
if (decodedUrl.includes('filemoon.to')) {
let key = decodedUrl;
let counter = 1;
while (providers[key]) {
key = `${decodedUrl}#${counter}`;
counter++;
}
providers[key] = language;
}
}
});
} catch (error) {
console.error(`Error processing ${language} video array:`, error);
}
}
videoArrayMatches.forEach(match => {
const arrayMatch = match.match(/var videos(\w+) = (\[.*?\]);/);
if (arrayMatch) {
const language = arrayMatch[1].toLowerCase();
const arrayString = arrayMatch[2];
processVideoArray(arrayString, language);
}
});
const streams = [];
for (const [filemoonUrl, language] of Object.entries(providers)) {
try {
console.log(`Processing ${filemoonUrl} for language: ${language}`);
const filemoonResponse = await fetchv2(filemoonUrl);
const filemoonHtml = await filemoonResponse.text();
const m3u8Url = await filemoonExtractor(filemoonHtml, filemoonUrl);
if (m3u8Url) {
const cleanLanguage = language.replace(/#\d+$/, '').toUpperCase();
streams.push(cleanLanguage, m3u8Url);
console.log(`Successfully extracted: ${m3u8Url} for ${cleanLanguage}`);
} else {
console.log(`Failed to extract m3u8 URL from ${filemoonUrl}`);
}
} catch (error) {
console.error(`Error processing ${filemoonUrl}:`, error);
}
}
const final = {
streams,
subtitles: ""
};
console.log("RETURN: " + JSON.stringify(final));
if (streams.length > 0) {
return JSON.stringify(final);
} else {
return JSON.stringify({
streams: [],
subtitles: ""
});
}
} catch (err) {
console.error("Error in extractStreamUrl:", err);
return dummyLink;
}
}
/* SCHEME START */
/* {REQUIRED PLUGINS: unbaser} */
/**
* @name filemoonExtractor
* @author Cufiy - Inspired by Churly
*/
async function filemoonExtractor(html, url = null) {
// check if contains iframe, if does, extract the src and get the url
const regex = /<iframe[^>]+src="([^"]+)"[^>]*><\/iframe>/;
const match = html.match(regex);
if (match) {
console.log("Iframe URL: " + match[1]);
const iframeUrl = match[1];
const iframeResponse = await soraFetch(iframeUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Referer": url,
}
});
console.log("Iframe Response: " + iframeResponse.status);
html = await iframeResponse.text();
}
// console.log("HTML: " + html);
// get /<script[^>]*>([\s\S]*?)<\/script>/gi
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
const scripts = [];
let scriptMatch;
while ((scriptMatch = scriptRegex.exec(html)) !== null) {
scripts.push(scriptMatch[1]);
}
// get the script with eval and m3u8
const evalRegex = /eval\((.*?)\)/;
const m3u8Regex = /m3u8/;
// console.log("Scripts: " + scripts);
const evalScript = scripts.find(script => evalRegex.test(script) && m3u8Regex.test(script));
if (!evalScript) {
console.log("No eval script found");
return null;
}
const unpackedScript = unpack(evalScript);
// get the m3u8 url
const m3u8Regex2 = /https?:\/\/[^\s]+master\.m3u8[^\s]*?(\?[^"]*)?/;
const m3u8Match = unpackedScript.match(m3u8Regex2);
if (m3u8Match) {
return m3u8Match[0];
} else {
console.log("No M3U8 URL found");
return null;
}
}
/* REMOVE_START */
class Unbaser {
constructor(base) {
this.ALPHABET = {
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95: "' !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'",
};
this.dictionary = {};
this.base = base;
if (36 < base && base < 62) {
this.ALPHABET[base] = this.ALPHABET[base] ||
this.ALPHABET[62].substr(0, base);
}
if (2 <= base && base <= 36) {
this.unbase = (value) => parseInt(value, base);
}
else {
try {
[...this.ALPHABET[base]].forEach((cipher, index) => {
this.dictionary[cipher] = index;
});
}
catch (er) {
throw Error("Unsupported base encoding.");
}
this.unbase = this._dictunbaser;
}
}
_dictunbaser(value) {
let ret = 0;
[...value].reverse().forEach((cipher, index) => {
ret = ret + ((Math.pow(this.base, index)) * this.dictionary[cipher]);
});
return ret;
}
}
function unpack(source) {
let { payload, symtab, radix, count } = _filterargs(source);
if (count != symtab.length) {
throw Error("Malformed p.a.c.k.e.r. symtab.");
}
let unbase;
try {
unbase = new Unbaser(radix);
}
catch (e) {
throw Error("Unknown p.a.c.k.e.r. encoding.");
}
function lookup(match) {
const word = match;
let word2;
if (radix == 1) {
word2 = symtab[parseInt(word)];
}
else {
word2 = symtab[unbase.unbase(word)];
}
return word2 || word;
}
source = payload.replace(/\b\w+\b/g, lookup);
return _replacestrings(source);
function _filterargs(source) {
const juicers = [
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)/,
/}\('(.*)', *(\d+|\[\]), *(\d+), *'(.*)'\.split\('\|'\)/,
];
for (const juicer of juicers) {
const args = juicer.exec(source);
if (args) {
let a = args;
if (a[2] == "[]") {
}
try {
return {
payload: a[1],
symtab: a[4].split("|"),
radix: parseInt(a[2]),
count: parseInt(a[3]),
};
}
catch (ValueError) {
throw Error("Corrupted p.a.c.k.e.r. data.");
}
}
}
throw Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)");
}
function _replacestrings(source) {
return source;
}
}
/**
* Uses Sora's fetchv2 on ipad, fallbacks to regular fetch on Windows
* @author ShadeOfChaos
*
* @param {string} url The URL to make the request to.
* @param {object} [options] The options to use for the request.
* @param {object} [options.headers] The headers to send with the request.
* @param {string} [options.method='GET'] The method to use for the request.
* @param {string} [options.body=null] The body of the request.
*
* @returns {Promise<Response|null>} The response from the server, or null if the
* request failed.
*/
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null }) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
/* REMOVE_END */
/* SCHEME END */
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "GnulaHD",
"iconUrl": "https://i3.wp.com/ww3.gnulahd.nu/wp-content/uploads/2025/07/cropped-faviconhd3-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Spanish (DUB/SUB)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://ww3.gnulahd.nu/",
"searchBaseUrl": "https://ww3.gnulahd.nu/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/gnulahd/gnulahd.js",
"type": "anime/shows/movies",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+158
View File
@@ -0,0 +1,158 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://hdhub4u.navy/?s=" + keyword);
const html = await response.text();
const regex = /<li class="thumb[^>]*>[\s\S]*?<img src="([^"]+)"[^>]*alt="([^"]+)"[\s\S]*?<a href="([^"]+)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
const title = cleanTitle(match[2].trim());
if (!/episode/i.test(title)) {
results.push({
title: title,
image: match[1].trim(),
href: match[3].trim()
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regexTitle = /<h1 class="page-title">[\s\S]*?<span class="material-text">([\s\S]*?)<\/span>/i;
const matchTitle = regexTitle.exec(html);
const titleText = matchTitle ? matchTitle[1].replace(/<[^>]+>/g, "").trim() : "";
let description;
if (/episodes/i.test(titleText)) {
description = "SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED - SHOWS NOT SUPPORTED ";
} else {
const regexDesc = /<div class="kno-rdesc">([\s\S]*?)<\/div>/i;
const matchDesc = regexDesc.exec(html);
description = matchDesc ? matchDesc[1].replace(/<[^>]+>/g, "").trim() : "N/A";
}
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a\s+[^>]*href="([^"]+)"[^>]*>([^<]*(?:\d+p|\d+\.\d*(?:GB|MB)|480p|720p|1080p|4K|2160p)[^<]*)<\/a>/gi;
let matches = [];
let match;
while ((match = regex.exec(html)) !== null) {
const href = match[1].trim();
const text = match[2].trim();
if (!href.includes('join-our-group') &&
!href.includes('telegram') &&
!href.includes('whatsapp') &&
!text.toLowerCase().includes('join') &&
!text.toLowerCase().includes('group')) {
matches.push({
href: href,
text: text
});
}
}
const lastMatch = matches.length > 0 ? matches[matches.length - 1] : null;
return JSON.stringify([{
href: lastMatch ? lastMatch.href : "N/A",
number: 1,
text: lastMatch ? lastMatch.text : "N/A"
}]);
} catch (err) {
console.error('Error extracting episodes:', err);
return JSON.stringify([{
href: "Error",
number: "Error",
text: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<title>HubDrive \| (.*?)<\/title>/i;
const match = regex.exec(html);
const filename = match ? match[1].trim() : "default.mp4";
const baseUrls = ["https://fsl.fastcloud.lol/", "https://fsl.fastcloud.buzz/"];
let fullUrl = null;
for (const base of baseUrls) {
const testUrl = base + filename;
try {
const passthroughUrl = "https://passthrough-worker.simplepostrequest.workers.dev/?head=" + encodeURIComponent(testUrl);
const resText = await fetchv2(passthroughUrl).then(r => r.text());
if (!/Status:404/i.test(resText)) {
fullUrl = testUrl;
break;
}
} catch {}
}
if (!fullUrl) fullUrl = "https://files.catbox.moe/avolvc.mp4";
console.log("Full URL:"+ fullUrl);
return fullUrl;
} catch (err) {
console.log("Full URL:"+ "https://files.catbox.moe/avolvc.mp4");
return "https://files.catbox.moe/avolvc.mp4";
}
}
function cleanTitle(title) {
return title
.replace(/&#8217;|&rsquo;/g, "'")
.replace(/&#8216;|&lsquo;/g, "'")
.replace(/&#8220;|&ldquo;/g, '"')
.replace(/&#8221;|&rdquo;/g, '"')
.replace(/&#8211;|&ndash;/g, "-")
.replace(/&#8212;|&mdash;/g, "--")
.replace(/&#8230;|&hellip;/g, "...")
.replace(/&amp;/g, "&")
.replace(/&quot;/g, '"')
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ")
.replace(/&#[0-9]+;/g, "")
.replace(/\s+/g, " ")
.trim();
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "hdhub4u",
"iconUrl": "https://hdhub4u.navy/wp-content/uploads/2021/05/cropped-cropped-1-1-1-2-1-180x180.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Hindi",
"streamType": "MKV",
"quality": "4K - 1080p",
"baseUrl": "https://hdhub4u.co/",
"searchBaseUrl": "https://hdhub4u.co/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/hdhub4u/hdhub4u.js",
"type": "shows/movies",
"asyncJS": true,
"softsub": false,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+232
View File
@@ -0,0 +1,232 @@
async function searchResults(keyword) {
try {
const encodedKeyword = encodeURIComponent(keyword);
const url = `https://helioscans.com/series/?q=${encodedKeyword}`;
const response = await soraFetch(url);
const html = await response.text();
const results = [];
const regex = /<button[^>]+?title="([^"]+?)"[^>]*?>[\s\S]*?<a href="([^"]+?)"[\s\S]*?background-image:url\(([^)]+)\)/g;
let match;
while ((match = regex.exec(html)) !== null) {
const title = match[1];
const href = `https://helioscans.com${match[2]}`;
const rawImage = match[3].replace(/&amp;/g, "&");
const image = rawImage.startsWith("http") ? rawImage : `https:${rawImage}`;
results.push({ title, href, image });
}
console.log(JSON.stringify(results));
return JSON.stringify(results);
} catch (error) {
console.error("Error fetching or parsing: " + error);
return JSON.stringify([{
title: "Error",
href: "",
image: ""
}]);
}
}
extractChapters('https://helioscans.com/series/63a6054296b/');
async function extractDetails(url) {
try {
const response = await soraFetch(url);
const htmlText = await response.text();
const metaMatch = htmlText.match(/<meta name="description" content="([\s\S]*?)">/i);
const description = metaMatch
? metaMatch[1].replace(/\s+/g, ' ').trim()
: "No description available";
const aliases = 'N/A';
const airdate = 'N/A';
const transformedResults = [{
description,
aliases,
airdate
}];
console.log(JSON.stringify(transformedResults));
return JSON.stringify(transformedResults);
} catch (error) {
console.log('Details error:' + error);
return JSON.stringify([{
description: 'Error loading description',
aliases: 'N/A',
airdate: 'N/A'
}]);
}
}
async function extractChapters(url) {
try {
const response = await soraFetch(url);
const htmlText = await response.text();
console.log(htmlText);
const chapters = [];
const chapterLinkRegex = /<a\s+[^>]*href="([^"]*\/chapter\/[^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
let linkMatch;
while ((linkMatch = chapterLinkRegex.exec(htmlText)) !== null) {
const fullLinkHtml = linkMatch[0];
const href = `https://helioscans.com${linkMatch[1]}`;
const titleRegex = /<span[^>]*class="[^"]*\btext-sm\b[^"]*\btruncate\b[^"]*"[^>]*>([^<]+)<\/span>/i;
const titleMatch = titleRegex.exec(fullLinkHtml);
const rawTitle = titleMatch ? titleMatch[1].trim() : "";
if (!rawTitle) continue;
const isLocked = /Coin\.svg/i.test(fullLinkHtml);
const title = isLocked ? `${rawTitle} (Locked 100 credits)` : rawTitle;
chapters.push({ title, href });
}
chapters.sort((a, b) => {
const numA = parseFloat(a.title.match(/Chapter\s+(\d+)/i)?.[1]) || 0;
const numB = parseFloat(b.title.match(/Chapter\s+(\d+)/i)?.[1]) || 0;
return numA - numB;
});
chapters.forEach((chapter, index) => {
chapter.number = index + 1;
});
console.log(JSON.stringify(chapters));
return JSON.stringify(chapters);
} catch (error) {
console.error('Fetch error in extractChapters:', error);
return JSON.stringify([{
href: url,
title: "Error fetching chapters",
number: 0
}]);
}
}
async function extractText(url) {
try {
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
};
const response = await soraFetch(url, headers);
const htmlText = await response.text();
const startMarker = '<div id="pages"';
const startIndex = htmlText.indexOf(startMarker);
if (startIndex === -1) {
throw new Error("Pages content div start (<div id=\"pages\") not found");
}
const startTagEndIndex = htmlText.indexOf('>', startIndex);
if (startTagEndIndex === -1) {
throw new Error("Could not find the end of the opening <div id=\"pages\"> tag");
}
const contentStartIndex = startTagEndIndex + 1;
let depth = 1;
let pos = contentStartIndex;
let endIndex = -1;
while (depth > 0 && pos < htmlText.length) {
const nextOpenDiv = htmlText.indexOf('<div', pos);
const nextCloseDiv = htmlText.indexOf('</div', pos);
if (nextCloseDiv === -1) {
break;
}
if (nextOpenDiv !== -1 && nextOpenDiv < nextCloseDiv) {
depth++;
pos = nextOpenDiv + 4;
} else {
depth--;
if (depth === 0) {
endIndex = nextCloseDiv;
} else {
pos = nextCloseDiv + 5;
}
}
}
if (endIndex === -1) {
throw new Error("Matching closing </div> for pages content div not found");
}
let innerContent = htmlText.substring(contentStartIndex, endIndex);
innerContent = innerContent.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
innerContent = innerContent.replace(/<div[^>]*class="[^"]*fixed[^"]*z-\[60\][^"]*top-0[^"]*left-0[^"]*w-full[^"]*h-full[^"]*bg-black\/90[^"]*flex[^"]*justify-center[^"]*items-center[\s\S]*?<\/div>/gi, '');
const paragraphRegex = /<p[^>]*>(.*?)<\/p>/gi;
let textContent = '';
let match;
while ((match = paragraphRegex.exec(innerContent)) !== null) {
const paragraphText = match[1].replace(/<[^>]*>/g, '').trim();
if (paragraphText) {
textContent += paragraphText + '\n';
}
}
innerContent = innerContent.trim();
if (!innerContent && !textContent) {
throw new Error("Chapter text not found or empty after cleaning");
}
console.log(innerContent || textContent);
return innerContent;
} catch (error) {
console.error("Fetch error in extractText: " + error.message);
return '<p>Error: This is chapter is locked as early access by the website, you will have to pay on the website or wait for the chapter to be released globally</p>';
}
}
async function soraFetch(url, options = {
headers: {},
method: 'GET',
body: null
}) {
try {
return await fetchv2(url, options.headers ?? {}, options.method ?? 'GET', options.body ?? null);
} catch (e) {
try {
return await fetch(url, options);
} catch (error) {
return null;
}
}
}
function decodeHtmlEntities(text) {
const entities = {
'&#x2014;': '—',
'&#x2013;': '',
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#x27;': "'",
'&#x2F;': '/',
'&#x60;': '`',
'&#x3D;': '=',
'&nbsp;': ' '
};
return text.replace(/&#x[\dA-Fa-f]+;|&\w+;/g, (match) => {
return entities[match] || match;
});
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "HelioScan",
"iconUrl": "https://wsrv.nl/?url=image.meowing.org/uploads/_9FxZ8P7Tik&w=44",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "English",
"streamType": "novels",
"quality": "N/A",
"baseUrl": "https://helioscans.com/",
"searchBaseUrl": "https://helioscans.com/%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/helioscans/helioscans.js",
"type": "novels",
"asyncJS": true,
"novel": true,
"downloadSupport": false,
"supportsSora": true
}
+165
View File
@@ -0,0 +1,165 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://hicartoon.to/search?keyword=" + encodeURIComponent(keyword));
const html = await response.text();
const blocks = html.split('<div class="flw-item">').slice(1);
for (const block of blocks) {
const href = block.match(/<a href="([^"]+)"/);
const image = block.match(/data-src="([^"]+)"/) || block.match(/src="([^"]+)"/);
const title = block.match(/title="([^"]+?)"/);
if (href && image && title) {
results.push({
title: title[1].trim(),
image: image[1].trim(),
href: href[1].trim()
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descMatch = html.match(/<div class="film-description m-hide">[\s\S]*?<div class="text">\s*([\s\S]*?)\s*<\/div>/);
const dateMatch = html.match(/<strong>Released:\s*<\/strong>\s*([^<\n]+)/);
const description = descMatch ? descMatch[1].trim() : "N/A";
const airdate = dateMatch ? dateMatch[1].trim() : "N/A";
return JSON.stringify([{
description: description,
aliases: "N/A",
airdate: airdate
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
let watchUrl = url;
if (!/\/watch\//.test(watchUrl)) {
watchUrl = watchUrl.replace(/\/([^\/]+)$/, '/watch/$1');
}
const watchResp = await fetchv2(watchUrl);
const watchHtml = await watchResp.text();
const idMatch = watchHtml.match(/<div[^>]+id="wrapper"[^>]+data-id="(\d+)"[^>]*>/);
if (!idMatch) throw new Error("movie_id not found");
const movieId = idMatch[1];
const epListResp = await fetchv2(`https://hicartoon.to/ajax/v2/episode/list?movie_id=${movieId}`);
const epListJson = await epListResp.json();
const epHtml = epListJson.html;
const epRegex = /<a[^>]+class="ssl-item ep-item"[^>]+data-id="(\d+)"[^>]+href="([^"]+)"[^>]*>[\s\S]*?<div class="ssli-order"[^>]*>([^<]+)<\/div>[\s\S]*?<div class="ep-name e-dynamic-name"[^>]*>([^<]+)<\/div>/g;
let match;
let idx = 1;
while ((match = epRegex.exec(epHtml)) !== null) {
results.push({
href: match[1],
number: idx
});
idx++;
}
return JSON.stringify(results);
} catch (err) {
console.error(err);
return JSON.stringify([{ id: "Error", href: "Error", number: "Error", title: "Error" }]);
}
}
async function extractStreamUrl(ID) {
try {
let response;
let json;
let iframeMatch;
// Try tserver
try {
response = await fetchv2(`https://hicartoon.to/ajax/v2/episode/sources?id=${ID}&s=tserver`);
json = await response.json();
iframeMatch = json.value ? json.value.match(/<iframe[^>]+src="([^"]+)"/) : null;
} catch (e) {
console.log("tserver failed, trying drserver");
}
if (!iframeMatch) {
try {
response = await fetchv2(`https://hicartoon.to/ajax/v2/episode/sources?id=${ID}&s=drserver`);
json = await response.json();
iframeMatch = json.value ? json.value.match(/<iframe[^>]+src="([^"]+)"/) : null;
} catch (e) {
console.log("drserver failed, trying vhserver");
}
}
if (!iframeMatch) {
try {
response = await fetchv2(`https://hicartoon.to/ajax/v2/episode/sources?id=${ID}&s=vhserver`);
json = await response.json();
iframeMatch = json.value ? json.value.match(/<iframe[^>]+src="([^"]+)"/) : null;
} catch (e) {
console.log("vhserver failed");
}
}
if (!iframeMatch) return "https://error.org/";
const iframeUrl = iframeMatch[1];
const streamHeaders = {
"Referer": "https://kem.clvd.xyz/",
"Origin": "https://kem.clvd.xyz/"
};
const iframeResp = await fetchv2(iframeUrl);
const iframeHtml = await iframeResp.text();
const hlsMatch = iframeHtml.match(/sources:\s*\[\{\s*"file":\s*"([^"]+)"/);
const streamUrl = hlsMatch ? hlsMatch[1] : "";
const subtitle = "";
return JSON.stringify({
streams: [
{
title: "Server 1",
streamUrl: streamUrl,
headers: streamHeaders
}
],
subtitle: subtitle
});
} catch (err) {
console.error(err);
return "https://error.org/";
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "HiCartoon",
"iconUrl": "https://hicartoon.to/assets/images/icons-512.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://hicartoon.to/home",
"searchBaseUrl": "https://hicartoon.to/home",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/hicartoon/hicartoon.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsSora": true,
"supportsLuna": true
}
+196
View File
@@ -0,0 +1,196 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://api.hicine.info/rpc/search/" + encodeURIComponent(keyword));
const data = await response.json();
for (const item of data) {
if (item.data) {
results.push({
title: item.data.title,
image: item.data.featured_image,
href: `https://api.hicine.info/api/${item.source_table}/${item.data.record_id}`
});
}
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const data = await response.json();
return JSON.stringify([{
description: data.categories,
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const data = await response.json();
for (let seasonNum = 1; seasonNum <= 10; seasonNum++) {
const seasonKey = `season_${seasonNum}`;
const seasonData = data[seasonKey];
if (!seasonData) continue;
const episodes = seasonData.split('\n').filter(line => line.trim().startsWith('Episode'));
let episodeNum = 1;
for (const episodeLine of episodes) {
const qualityLinks = [];
const qualityParts = episodeLine.split(' : ').slice(1);
for (const part of qualityParts) {
const match = part.match(/^(.+?),\s*([^,]*),(.+?)$/);
if (match) {
const fullUrl = match[1].trim();
const size = match[2].trim();
const quality = match[3].trim();
const vcloudMatch = fullUrl.match(/vcloud=(https:\/\/vcloud\.zip\/[^,&]+)/);
const vcloudUrl = vcloudMatch ? vcloudMatch[1] : fullUrl;
qualityLinks.push({
link: vcloudUrl,
quality: quality,
size: size
});
}
}
if (qualityLinks.length > 0) {
results.push({
season: seasonNum,
number: episodeNum,
href: JSON.stringify(qualityLinks)
});
episodeNum++;
}
}
}
if (data.links && results.length === 0) {
const links = data.links.split('\n').filter(line => line.trim());
const qualityLinks = [];
for (const line of links) {
const match = line.match(/vcloud=(https:\/\/vcloud\.zip\/[^,]+),\s*Link2,\s*Link3,\s*Link4,\s*Link5,\s*Link6,\s*Link7,\s*(.+?)(?:,\s*(\d+(?:\.\d+)?(?:MB|GB)))?$/);
if (match) {
const vcloudUrl = match[1].trim();
const quality = match[2].trim();
const size = match[3] ? match[3].trim() : '';
qualityLinks.push({
link: vcloudUrl,
quality: quality,
size: size
});
}
}
if (qualityLinks.length > 0) {
results.push({
href: JSON.stringify(qualityLinks),
number: 1
});
}
}
return JSON.stringify(results.length > 0 ? results : [{
href: "Error",
number: "Error",
season: "Error"
}]);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error",
season: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const qualityLinks = JSON.parse(url);
const vcloudPromises = qualityLinks.map(async (quality, idx) => {
try {
const vcloudUrl = quality.link;
const qualityName = quality.quality;
const vcloudResponse = await fetchv2(vcloudUrl, {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": "https://vcloud.zip/"
});
const vcloudHtml = await vcloudResponse.text();
const urlMatch = vcloudHtml.match(/var\s+url\s*=\s*'(https:\/\/[^']+)'/);
if (!urlMatch) {
return null;
}
const hubcloudUrl = urlMatch[1];
const hubcloudResponse = await fetchv2(hubcloudUrl, {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": vcloudUrl,
"X-Requested-With": "XMLHttpRequest"
});
const hubcloudHtml = await hubcloudResponse.text();
const fslMatch = hubcloudHtml.match(/<a\s+href="(https:\/\/love\.polgen\.buzz\/[^"]+)"[^>]*>[\s\S]*?Download\s*\[FSL\s*Server\]/i);
if (fslMatch) {
return {
title: qualityName,
streamUrl: fslMatch[1],
headers: {}
};
}
return null;
} catch (err) {
console.error(`Error in quality ${idx}:`, err.message);
return null;
}
});
const streams = (await Promise.all(vcloudPromises)).filter(s => s !== null);
const result = {
type: "servers",
streams: streams,
subtitle: "nonrt"
};
return JSON.stringify(result);
} catch (err) {
console.error(" Top-level error:", err.message);
return JSON.stringify({
type: "servers",
streams: [],
subtitle: "error"
});
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "Hicine",
"iconUrl": "https://files.catbox.moe/y8v199.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Hindi",
"streamType": "MKV",
"quality": "4K",
"baseUrl": "https://hicine.info/",
"searchBaseUrl": "https://hicine.info/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/hicine/hicine.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"supportsSora": false,
"supportsLuna": true
}
+367
View File
@@ -0,0 +1,367 @@
async function searchResults(keyword) {
try {
let transformedResults = [];
const keywordGroups = {
trending: ["!trending", "!hot", "!tr", "!!"],
topRatedMovie: ["!top-rated-movie", "!topmovie", "!tm", "??"],
topRatedTV: ["!top-rated-tv", "!toptv", "!tt", "::"],
popularMovie: ["!popular-movie", "!popmovie", "!pm", ";;"],
popularTV: ["!popular-tv", "!poptv", "!pt", "++"],
};
const skipTitleFilter = Object.values(keywordGroups).flat();
const shouldFilter = !matchesKeyword(keyword, skipTitleFilter);
const encodedKeyword = encodeURIComponent(keyword);
let baseUrlTemplate = null;
if (matchesKeyword(keyword, keywordGroups.trending)) {
baseUrlTemplate = (page) => `https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/trending/all/week?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=${page}`)}&simple=true`;
} else if (matchesKeyword(keyword, keywordGroups.topRatedMovie)) {
baseUrlTemplate = (page) => `https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/movie/top_rated?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=${page}`)}&simple=true`;
} else if (matchesKeyword(keyword, keywordGroups.topRatedTV)) {
baseUrlTemplate = (page) => `https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/top_rated?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=${page}`)}&simple=true`;
} else if (matchesKeyword(keyword, keywordGroups.popularMovie)) {
baseUrlTemplate = (page) => `https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/movie/popular?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=${page}`)}&simple=true`;
} else if (matchesKeyword(keyword, keywordGroups.popularTV)) {
baseUrlTemplate = (page) => `https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/popular?api_key=9801b6b0548ad57581d111ea690c85c8&include_adult=false&page=${page}`)}&simple=true`;
} else {
baseUrlTemplate = (page) => `https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/search/multi?api_key=9801b6b0548ad57581d111ea690c85c8&query=${encodedKeyword}&include_adult=false&page=${page}`)}&simple=true`;
}
let dataResults = [];
if (baseUrlTemplate) {
const pagePromises = Array.from({ length: 5 }, (_, i) =>
soraFetch(baseUrlTemplate(i + 1)).then(r => r.json())
);
const pages = await Promise.all(pagePromises);
dataResults = pages.flatMap(p => p.results || []);
}
if (dataResults.length > 0) {
transformedResults = transformedResults.concat(
dataResults
.map(result => {
if (result.media_type === "movie" || result.title) {
return {
title: result.title || result.name || result.original_title || result.original_name || "Untitled",
image: result.poster_path ? `https://image.tmdb.org/t/p/w500${result.poster_path}` : "",
href: `movie/${result.id}`,
};
} else if (result.media_type === "tv" || result.name) {
return {
title: result.name || result.title || result.original_name || result.original_title || "Untitled",
image: result.poster_path ? `https://image.tmdb.org/t/p/w500${result.poster_path}` : "",
href: `tv/${result.id}/1/1`,
};
}
})
.filter(Boolean)
.filter(r => !shouldFilter || r.title.toLowerCase().includes(keyword.toLowerCase()))
);
}
console.log("Transformed Results: " + JSON.stringify(transformedResults));
return JSON.stringify(transformedResults);
} catch (error) {
console.log("Fetch error in searchResults: " + error);
return JSON.stringify([{ title: "Error", image: "", href: "" }]);
}
}
function matchesKeyword(keyword, commands) {
const lower = keyword.toLowerCase();
return commands.some(cmd => lower.startsWith(cmd.toLowerCase()));
}
async function extractDetails(url) {
try {
if(url.includes('movie')) {
const match = url.match(/movie\/([^\/]+)/);
if (!match) throw new Error("Invalid URL format");
const movieId = match[1];
const responseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/movie/${movieId}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
const data = await responseText.json();
const transformedResults = [{
description: data.overview || 'No description available',
aliases: `Duration: ${data.runtime ? data.runtime + " minutes" : 'Unknown'}`,
airdate: `Released: ${data.release_date ? data.release_date : 'Unknown'}`
}];
return JSON.stringify(transformedResults);
} else if(url.includes('tv')) {
const match = url.match(/tv\/([^\/]+)/);
if (!match) throw new Error("Invalid URL format");
const showId = match[1];
const responseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${showId}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
const data = await responseText.json();
const transformedResults = [{
description: data.overview || 'No description available',
aliases: `Duration: ${data.episode_run_time && data.episode_run_time.length ? data.episode_run_time.join(', ') + " minutes" : 'Unknown'}`,
airdate: `Aired: ${data.first_air_date ? data.first_air_date : 'Unknown'}`
}];
console.log(JSON.stringify(transformedResults));
return JSON.stringify(transformedResults);
} else {
throw new Error("Invalid URL format");
}
} catch (error) {
console.log('Details error: ' + error);
return JSON.stringify([{
description: 'Error loading description',
aliases: 'Duration: Unknown',
airdate: 'Aired/Released: Unknown'
}]);
}
}
async function extractEpisodes(url) {
try {
if(url.includes('movie')) {
const match = url.match(/movie\/([^\/]+)/);
if (!match) throw new Error("Invalid URL format");
const movieId = match[1];
const movie = [
{ href: `/movie/${movieId}`, number: 1, title: "Full Movie" }
];
console.log(movie);
return JSON.stringify(movie);
} else if(url.includes('tv')) {
const match = url.match(/tv\/([^\/]+)\/([^\/]+)\/([^\/]+)/);
if (!match) throw new Error("Invalid URL format");
const showId = match[1];
const showResponseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${showId}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
const showData = await showResponseText.json();
let allEpisodes = [];
for (const season of showData.seasons) {
const seasonNumber = season.season_number;
if(seasonNumber === 0) continue;
const seasonResponseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${showId}/season/${seasonNumber}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
const seasonData = await seasonResponseText.json();
if (seasonData.episodes && seasonData.episodes.length) {
const episodes = seasonData.episodes.map(episode => ({
href: `/tv/${showId}/${seasonNumber}/${episode.episode_number}`,
number: episode.episode_number,
title: episode.name || ""
}));
allEpisodes = allEpisodes.concat(episodes);
}
}
console.log(allEpisodes);
return JSON.stringify(allEpisodes);
} else {
throw new Error("Invalid URL format");
}
} catch (error) {
console.log('Fetch error in extractEpisodes: ' + error);
return JSON.stringify([]);
}
}
async function extractStreamUrl(ID) {
if (ID.includes('movie')) {
const tmdbID = ID.replace('/movie/', '');
const responseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/movie/${tmdbID}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
const data = await responseText.json();
const slug = data.title.toLowerCase().replace(/\s+/g, '-') + '-' + data.release_date.split('-')[0];
const url = `https://hollymoviehd.cc/${slug}/`;
console.log(url);
const responseTwo = await soraFetch(url);
const pageText = await responseTwo.text();
const streamkeyMatch = pageText.match(/data-streamkey="([^"]*)"/);
const nonceMatch = pageText.match(/data-wpnonce="([^"]*)"/);
if (streamkeyMatch && nonceMatch) {
const streamkey = streamkeyMatch[1];
const nonce = nonceMatch[1];
const ajaxParams = `action=ajax_getlinkstream&streamkey=${streamkey}&nonce=${nonce}&imdbid=&tmdbid=${tmdbID}`;
console.log(ajaxParams);
const headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Referer": "https://hollymoviehd.cc",
"Origin": "https://hollymoviehd.cc"
};
const apiResponse = await fetchv2("https://hollymoviehd.cc/wp-admin/admin-ajax.php", headers, "POST", ajaxParams);
const apiData = await apiResponse.json();
const serverUrl = apiData.servers_iframe.streamsvr;
console.log(serverUrl);
const serverResponse = await soraFetch(serverUrl);
const serverText = await serverResponse.text();
const csrfMatch = serverText.match(/id="csrf_token" value="([^"]*)"/);
if (csrfMatch) {
const csrfToken = csrfMatch[1];
const boundary = '----geckoformboundary7183c2c77a4fd6afe70224d86261ff15';
const body = `--${boundary}\r\nContent-Disposition: form-data; name="token"\r\n\r\n\r\n--${boundary}\r\nContent-Disposition: form-data; name="csrf_token"\r\n\r\n${csrfToken}\r\n--${boundary}--`;
const headers = {
'Referer': 'https://flashstream.cc/',
'Content-Type': `multipart/form-data; boundary=${boundary}`,
'Origin': 'https://flashstream.cc'
};
const postResponse = await soraFetch(serverUrl, {
method: 'POST',
headers: headers,
body: body
});
const postText = await postResponse.text();
console.log(postText);
const data = JSON.parse(postText);
const sources = data.sources;
const hlsSource = sources.find(s => s.type === 'hls' && s.file.startsWith('https://flashstream.cc/pl/'));
if (hlsSource) {
const streamUrl = hlsSource.file;
const result = {
streams: [
{
title: "default",
streamUrl: streamUrl,
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Accept-Language": "en-US,en;q=0.5",
"Range": "bytes=0-",
"Alt-Used": "flashstream.cc",
"Connection": "keep-alive",
"Referer": "https://flashstream.cc/t: video",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-origin",
"Accept-Encoding": "identity",
"Priority": "u=4",
"TE": "trailers"
}
}
],
subtitle: ""
};
return JSON.stringify(result);
}
}
}
} else if (ID.includes('tv')) {
const parts = ID.split('/');
const tmdbID = parts[2];
const seasonNumber = parts[3];
const episodeNumber = parts[4];
const responseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${tmdbID}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
const data = await responseText.json();
const slug = data.name.toLowerCase().replace(/\s+/g, '-') + '-season-' + seasonNumber + '-episode-' + episodeNumber;
const url = `https://hollymoviehd.cc/episode/${slug}/`;
console.log(url);
const responseTwo = await soraFetch(url);
const pageText = await responseTwo.text();
const streamkeyMatch = pageText.match(/data-streamkey="([^"]*)"/);
const nonceMatch = pageText.match(/data-wpnonce="([^"]*)"/);
if (streamkeyMatch && nonceMatch) {
const streamkey = streamkeyMatch[1];
const nonce = nonceMatch[1];
const ajaxParams = `action=ajax_getlinkstream&streamkey=${streamkey}&nonce=${nonce}&imdbid=&tmdbid=${tmdbID}`;
console.log(ajaxParams);
const headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Referer": "https://hollymoviehd.cc",
"Origin": "https://hollymoviehd.cc"
};
const apiResponse = await fetchv2("https://hollymoviehd.cc/wp-admin/admin-ajax.php", headers, "POST", ajaxParams);
const apiData = await apiResponse.json();
const serverUrl = apiData.servers_iframe.streamsvr;
console.log(serverUrl);
const serverResponse = await soraFetch(serverUrl);
const serverText = await serverResponse.text();
const csrfMatch = serverText.match(/id="csrf_token" value="([^"]*)"/);
if (csrfMatch) {
const csrfToken = csrfMatch[1];
const boundary = '----geckoformboundary7183c2c77a4fd6afe70224d86261ff15';
const body = `--${boundary}\r\nContent-Disposition: form-data; name="token"\r\n\r\n\r\n--${boundary}\r\nContent-Disposition: form-data; name="csrf_token"\r\n\r\n${csrfToken}\r\n--${boundary}--`;
const headers = {
'Referer': 'https://flashstream.cc/',
'Content-Type': `multipart/form-data; boundary=${boundary}`,
'Origin': 'https://flashstream.cc'
};
const postResponse = await soraFetch(serverUrl, {
method: 'POST',
headers: headers,
body: body
});
const postText = await postResponse.text();
console.log(postText);
const data = JSON.parse(postText);
const sources = data.sources;
const hlsSource = sources.find(s => s.type === 'hls' && s.file.startsWith('https://flashstream.cc/pl/'));
if (hlsSource) {
const streamUrl = hlsSource.file;
const result = {
streams: [
{
title: "default",
streamUrl: streamUrl,
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0",
"Accept": "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Accept-Language": "en-US,en;q=0.5",
"Range": "bytes=0-",
"Alt-Used": "flashstream.cc",
"Connection": "keep-alive",
"Referer": "https://flashstream.cc/t: video",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-origin",
"Accept-Encoding": "identity",
"Priority": "u=4",
"TE": "trailers"
}
}
],
subtitle: ""
};
return JSON.stringify(result);
}
}
}
}
}
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) {
try {
return await fetchv2(
url,
options.headers ?? {},
options.method ?? 'GET',
options.body ?? null,
true,
options.encoding ?? 'utf-8'
);
} catch(e) {
try {
return await fetch(url, options);
} catch(error) {
return null;
}
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"sourceName": "HollyMovieHD",
"iconUrl": "https://files.catbox.moe/r092e6.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "English (Hardsub)",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://hollymoviehd.cc/home/",
"searchBaseUrl": "https://hollymoviehd.cc/home/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/hollymoviehd/hollymoviehd.js",
"type": "anime/movies/shows",
"asyncJS": true,
"softsub": true,
"downloadSupport": false,
"supportsLuna": true,
"note": "Requires Luna, aka Sora 2.0"
}
+124
View File
@@ -0,0 +1,124 @@
async function searchResults(keyword) {
const results = [];
try {
const response = await fetchv2("https://tn.honadrama.us/?s=" + encodeURIComponent(keyword));
const html = await response.text();
const regex = /<div class="movie"><a href="([^"]+)".*?<img src="([^"]+)"[^>]*>.*?<h3>(.*?)<\/h3>/gs;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[3].trim(),
image: match[2].trim(),
href: match[1].trim()
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
title: "Error",
image: "Error",
href: "Error"
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const descriptionMatch = (/<div class="StoryMovie">(.*?)<\/div>/s.exec(html) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? descriptionMatch.trim() : "N/A",
aliases: "N/A",
airdate: "N/A"
}]);
} catch (err) {
return JSON.stringify([{
description: "Error",
aliases: "Error",
airdate: "Error"
}]);
}
}
async function extractEpisodes(url) {
const results = [];
try {
const response = await fetchv2(url);
const html = await response.text();
const regex = /<a href="([^"]+)" class="WatchButton">/;
const match = regex.exec(html);
if (match) {
results.push({
href: match[1].trim(),
number: 1
});
}
return JSON.stringify(results);
} catch (err) {
return JSON.stringify([{
href: "Error",
number: "Error"
}]);
}
}
async function extractStreamUrl(url) {
try {
const response = await fetchv2(url);
const html = await response.text();
const serverRegex = /<li\s+([^>]*?)data-server="(\d+)"\s+data-q="(\d+)"[^>]*>/g;
const servers = [];
let match;
while ((match = serverRegex.exec(html)) !== null) {
servers.push({
server: match[2],
q: match[3]
});
}
let uqloadUrl = null;
for (const s of servers) {
const ajaxUrl = `https://tn.honadrama.us/wp-admin/admin-ajax.php?action=serverPost&server=${s.server}&q=${s.q}`;
const serverResp = await fetchv2(ajaxUrl);
const serverHtml = await serverResp.text();
const iframeMatch = /<iframe\s+[^>]*src="([^"]*uqload\.[a-z]{2,3}[^"]*)"/i.exec(serverHtml);
if (iframeMatch) {
uqloadUrl = iframeMatch[1];
console.log("[Debug] Uqload iframe found:"+ uqloadUrl);
break;
}
}
if (!uqloadUrl) {
return "https://files.catbox.moe/avolvc.mp4";
}
const uqResp = await fetchv2(uqloadUrl);
const uqHtml = await uqResp.text();
const mp4Match = /sources:\s*\["([^"]+\.mp4)"]/i.exec(uqHtml);
if (mp4Match) {
return mp4Match[1];
} else {
console.log("[Debug] No MP4 found in uqload page, returning default");
return "https://files.catbox.moe/avolvc.mp4";
}
} catch (err) {
console.log("[Error] Fetching stream failed:", err);
return "https://files.catbox.moe/avolvc.mp4";
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"sourceName": "HonaDrama",
"iconUrl": "https://tn.honadrama.us/wp-content/uploads/2023/06/cropped-logo-google-192x192.png",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.0",
"language": "Arabic",
"streamType": "mp4",
"quality": "1080p",
"baseUrl": "https://uqload.cx/",
"searchBaseUrl": "https://uqload.cx/?s=%s",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/honadrama/honadrama.js",
"type": "shows/movies",
"asyncJS": true,
"downloadSupport": false,
"supportsSora": true,
"supportsLuna": true
}
+82
View File
@@ -0,0 +1,82 @@
async function searchResults(keyword) {
const results = [];
const response = await fetchv2(`http://www.iyinghua.com/search/${keyword}/`);
const html = await response.text();
const regex = /<a href="(\/show\/\d+\.html)"><img src="(http[^"]+)" alt="([^"]+)"><\/a><h2><a href="[^"]+" title="([^"]+)"/g;
let match;
while ((match = regex.exec(html)) !== null) {
results.push({
title: match[4].trim(),
image: match[2].trim(),
href: `http://www.iyinghua.com${match[1].trim()}`
});
}
return JSON.stringify(results);
}
async function extractDetails(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const descriptionRegex = /<div class="info">([^<]+)<\/div>/;
const descriptionMatch = descriptionRegex.exec(html);
const description = descriptionMatch ? descriptionMatch[1].trim() : 'N/A';
results.push({
description: description,
aliases: 'N/A',
airdate: 'N/A'
});
return JSON.stringify(results);
}
async function extractEpisodes(url) {
const results = [];
const response = await fetchv2(url);
const html = await response.text();
const episodeRegex = /<li><a href="(\/v\/\d+-([\dpv]+)\.html)" target="_blank">([^<]+)<\/a><\/li>/g;
let match;
let episodeCount = 0;
while ((match = episodeRegex.exec(html)) !== null) {
const href = `http://www.iyinghua.com${match[1].trim()}`;
const number = match[2].match(/^\d+$/) ? parseInt(match[2], 10) : null;
if (number !== null) {
results.push({ href, number });
} else {
episodeCount++;
}
}
if (results.length === 0 && episodeCount > 0) {
episodeRegex.lastIndex = 0;
let index = 1;
while ((match = episodeRegex.exec(html)) !== null) {
results.push({
href: `http://www.iyinghua.com${match[1].trim()}`,
number: index++
});
}
}
results.reverse();
return JSON.stringify(results);
}
async function extractStreamUrl(url) {
const response = await fetchv2(url);
const html = await response.text();
console.error(html);
const streamRegex = /data-vid="([^"]+)"/;
const match = streamRegex.exec(html);
return match ? match[1] : null;
}

Some files were not shown because too many files have changed in this diff Show More