Skip to content

Commit fe3117d

Browse files
committed
[AniLINK] add support for YFlix and Rapidshare extractor
1 parent c79b7cd commit fe3117d

1 file changed

Lines changed: 44 additions & 4 deletions

File tree

AniLINK/AniLINK_Episode-Link-Extractor.user.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ==UserScript==
22
// @name AniLINK - Episode Link Extractor
33
// @namespace https://greasyfork.org/en/users/781076-jery-js
4-
// @version 6.22.1
4+
// @version 6.23.0
55
// @description Stream or download your favorite anime series effortlessly with AniLINK! Unlock the power to play any anime series directly in your preferred video player or download entire seasons in a single click using popular download managers like IDM. AniLINK generates direct download links for all episodes, conveniently sorted by quality. Elevate your anime-watching experience now!
66
// @icon https://www.google.com/s2/favicons?domain=animepahe.ru
77
// @author Jery
@@ -46,6 +46,7 @@
4646
// @match https://animekai.ac/watch/*
4747
// @match https://animekai.cc/watch/*
4848
// @match https://anikai.to/watch/*
49+
// @match https://yflix.to/watch/*
4950
// @grant GM_registerMenuCommand
5051
// @grant GM_xmlhttpRequest
5152
// @grant GM.xmlHttpRequest
@@ -821,6 +822,43 @@ const Websites = [
821822
},
822823
_encdec: async (s, t = 'e') => await GM_fetch(`https://enc-dec.app/api/${t == 'e' ? 'enc' : 'dec'}-kai?text=` + s).then(r => r.json()).then(d => d.result),
823824
_typeSuffix: type => ({ sub: "Hard Sub", softsub: "Soft Sub", dub: "Dub & S-Sub" }[type] || type)
825+
},
826+
{
827+
name: "YFlix",
828+
url: ["yflix.to/"],
829+
_chunkSize: 12,
830+
addStartButton: function (id) {
831+
setInterval(() => {
832+
if ($('#' + id).get(0)) return; try {
833+
const button = Object.assign(document.createElement('li'), { id, className: "btn btn-primary", textContent: "Extract Stream Links", style: "height: fit-content; margin-left: 10px;" });
834+
document.querySelector('#filmServer > ul')?.appendChild(button);
835+
button.addEventListener('click', extractEpisodes);
836+
} catch (e) { /* ignore */ }
837+
}, 500);
838+
},
839+
extractEpisodes: async function* (status) {
840+
status.text = 'Fetching episode list...';
841+
const contentId = _$('div.rating[data-id]')?.dataset.id; if (!contentId) return;
842+
const encId = await this._enc(contentId);
843+
const epElms = await fetch(`/ajax/episodes/list?id=${contentId}&_=${encId}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }).then(r => r.json().then(d => d.result)).then(t => (new DOMParser()).parseFromString(t, 'text/html')).then(doc => [...doc.querySelectorAll('ul.episodes[data-season] li a')]);
844+
const filteredEps = await applyEpisodeRangeFilter(epElms); if (!filteredEps?.length) return;
845+
const srcCfg = await (async () => {
846+
const servers = await fetch(`/ajax/links/list?eid=${filteredEps[0].getAttribute('eid')}&_=${await this._enc(filteredEps[0].getAttribute('eid'))}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }).then(r => r.json().then(d => d.result)).then(t => (new DOMParser()).parseFromString(t, 'text/html')).then(doc => [...doc.querySelectorAll('li.server span')].map(s => s.textContent.trim()));
847+
return await showSourceSelector(servers, 'yflix', { mode: 'single' });
848+
})();
849+
for (let i = 0; i < filteredEps.length; i += this._chunkSize)
850+
yield* yieldEpisodesFromPromises(filteredEps.slice(i, i + this._chunkSize).map(async ep => {
851+
const epNum = ep.getAttribute('num') || ep.querySelector('.num')?.textContent || '1';
852+
status.text = `Extracting Episodes ${(epNum - Math.min(this._chunkSize, epNum) + 1)} - ${epNum}...`;
853+
const servers = await fetch(`/ajax/links/list?eid=${ep.getAttribute('eid')}&_=${await this._enc(ep.getAttribute('eid'))}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }).then(r => r.json().then(d => d.result)).then(t => (new DOMParser()).parseFromString(t, 'text/html')).then(doc => [...doc.querySelectorAll('li.server')].map(s => ({ lid: s.dataset.lid, name: s.querySelector('span')?.textContent.trim() }))).catch(() => []);
854+
const links = {}, fetchSource = async s => { try { const encUrl = await fetch(`/ajax/links/view?id=${s.lid}&_=${await this._enc(s.lid)}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }).then(r => r.json().then(d => d.result)); const iframe = await this._dec(encUrl); links[s.name] = await Extractors.use(iframe, (new URL(iframe)).origin+'/'); } catch (e) { showToast(`Failed to fetch Ep ${epNum} from ${s.name}: ${e.message || e}`); } };
855+
if (srcCfg?.mode === 'single') { for (const key of srcCfg.sources) { const s = servers.find(srv => srv.name === key); if (s) { await fetchSource(s); if (Object.keys(links).length) break; } } }
856+
else for (const key of srcCfg.sources) { const s = servers.find(srv => srv.name === key); if (s) await fetchSource(s); }
857+
return new Episode(epNum, _$('h1.title')?.textContent || '', links, _$('.poster img')?.src || '', ep.querySelector('span:not(.num)')?.textContent || '');
858+
}))
859+
},
860+
_enc: async s => await GM_fetch(`https://enc-dec.app/api/enc-movies-flix?text=${s}`).then(r => r.json()).then(d => d.result),
861+
_dec: async s => await GM_fetch(`https://enc-dec.app/api/dec-movies-flix?text=${s}`).then(r => r.json()).then(d => d.result.url),
824862
}
825863
];
826864

@@ -902,11 +940,13 @@ const Extractors = {
902940
const source = sources.reduce((best, curr) => (s => parseInt(s.label) || 0)(curr) > (s => parseInt(s.label) || 0)(best) ? curr : best, sources[0]);
903941
return { file: source.file, type: source.file.includes('.m3u8') ? 'm3u8' : 'mp4', tracks: [] };
904942
},
905-
'/^(4spromax|megaup)(\\d+)?\\.?(live|online|cc|site)$/': async function (url, referer = 'https://megaup.cc/') {
943+
'/^(4spromax|megaup|rapidairmax|rapidshare)(\\d+)?\\.?(live|online|cc|site)$/': async function (url, referer = 'https://megaup.cc/') {
906944
// workaround: use GM_xmlhttpRequest to avoid passing cookies (coudnt do that with GM_fetch)
945+
const u = new URL(url), subListUrl = u.searchParams.get('sub.list');
907946
const encToken = await new Promise((r, j) => GM_xmlhttpRequest({ method: 'GET', url: url.replace('/e/', '/media/'), headers: { 'User-Agent': USER_AGENT_HEADER }, anonymous: true, onload: res => { try { r(JSON.parse(res.responseText).result); } catch (e) { j(e); } }, onerror: j }));
908-
const src = (await GM_fetch('https://enc-dec.app/api/dec-mega', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: encToken, agent: USER_AGENT_HEADER }) }).then(r => r.json())).result;
909-
return { stream: src.sources[0].file, type: 'm3u8', tracks: src.tracks?.map(t => ({ file: t.file, label: t.label, kind: t.kind, default: !!t.default })), referer: 'https://megaup.cc/' };
947+
const src = (await GM_fetch(`https://enc-dec.app/api/dec-${url.includes('://rapid') ? 'rapid' : 'mega'}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: encToken, agent: USER_AGENT_HEADER }) }).then(r => r.json())).result;
948+
const tracks = subListUrl ? await fetch(subListUrl, { headers: { 'Accept': '*/*', 'Referer': `${u.origin}/` } }).then(r => r.json()).then(list => list.filter(t => t.kind === 'captions' && t.file && t.label).map(t => ({ file: t.file, label: t.label, kind: t.kind }))).catch(() => []) : (src.tracks || []).filter(t => t.kind === 'captions' && t.file && t.label).map(t => ({ file: t.file, label: t.label, kind: t.kind, default: !!t.default }));
949+
return { stream: src.sources[0].file, type: 'm3u8', tracks, referer };
910950
}
911951
}
912952
/**

0 commit comments

Comments
 (0)