Skip to content

Commit

Permalink
Vidplay & Mycloud working again on Aniwave (0.6.6)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nixuge committed Jul 23, 2024
1 parent 6e1da81 commit 246db5f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 77 deletions.
2 changes: 1 addition & 1 deletion src/aniwave/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class Source extends SourceModule implements VideoContent {
metadata = {
id: 'aniwave',
name: 'Aniwave',
version: '0.6.5',
version: '0.6.6',
icon: "https://s2.bunnycdn.ru/assets/sites/aniwave/favicon1.png"
}

Expand Down
143 changes: 67 additions & 76 deletions src/shared/extractors/mycloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { ISource } from '../models/types';
import { getM3u8Qualities } from '../utils/m3u8';
import { IRawTrackMedia, parseSubtitles } from '../utils/subtitles';
import { rc4Cypher } from '../utils/aniwave/rc4';
import { b64encode } from '../utils/aniwave/b64encode';
import { PlaylistEpisodeServerSubtitleFormat } from '@mochiapp/js/dist';
import { b64decode } from '../utils/b64decode';
import { b64encode, b64decode } from '../utils/b64';

interface IMediaInfo {
status: number,
Expand All @@ -15,90 +14,73 @@ interface IMediaInfo {
}
}

// KEYS DECRYPTION //
interface ParsedKeys {
encrypt: string[],
decrypt: string[]
}

async function grabKeysFromGithub(e) {
const resp = await request.get(e).then(s => s.text())!
return resp.match(/"rawLines":\["\[\\"(.+)\\"]"\]/)![1].split('\\",\\"')
const rawKeysHtml = resp.match(/"rawLines":\["(.+?)"],"styling/)![1];

return JSON.parse(rawKeysHtml.replaceAll("\\", ""));
}

async function getToken(domain: string, useCached: boolean) {
// Note: it doesn't seem like grabbing a new token is necessary for some reason?
if (useCached)
return 'ViFRsqNPsIHKpYB0WLBjGjDGLa4flllPaeQmJ2GWwnXjR6wupwiKOdg92mKTSXrGkg==';

const futokenText = await request.get(`https://vidplay.online/futoken`, {
headers: {
referer: domain
}
}).then(u => u.text())
return futokenText.match(/var\s+k\s*=\s*'([^']+)'/)?.[1] ?? "";
function encodeVideoId(input: string, key: string) {
input = encodeURIComponent(input);
const e = rc4Cypher(key, input);
const out = b64encode(e).replace(/\//g, "_").replace(/\+/g, '-');
return out;
}

function futoken(v: string, location: string, domain: string, token: string): string {
const a: (string | number)[] = [token];

for (let i = 0; i < v.length; i++) {
a.push(token.charCodeAt(i % token.length) + v.charCodeAt(i));
}
function encodeH(input: string, key: string) {
input = encodeURIComponent(input);
const e = rc4Cypher(key, input);
const out = b64encode(e).replace(/\//g, "_").replace(/\+/g, '-');
return out;
}

return `https://${domain}/mediainfo/${a.join(',')}${location}`;
function decodeResult(input: string, key: string) {
const i = b64decode((input).replace(/_/g, "/").replace(/-/g, "+"));
let e = rc4Cypher(key, i);
e = decodeURIComponent(e);
return e;
}

async function getUrl(fullUrl: string, keyUrl: string, cachedFutoken: boolean) {
async function getUrl(fullUrl: string, keys: ParsedKeys) {
let videoId = fullUrl.split("/e/")[1].split("?")[0];
let keys = await grabKeysFromGithub(keyUrl);

const firstPass = rc4Cypher(keys[0], videoId);
const secondPass = rc4Cypher(keys[1], firstPass);
const secondPassEncoded = b64encode(secondPass).replace("/", "_");


const urlEnd = "?" + fullUrl.split("?").pop();
const domain = fullUrl.split('/')[2];

const token = await getToken(fullUrl, cachedFutoken);

return futoken(secondPassEncoded, urlEnd, domain, token)
let encodedVideoId = encodeVideoId(videoId, keys.encrypt[1]);
let h = encodeH(videoId, keys.encrypt[2]);
let mediainfo_url = `https://vid2v11.site/mediainfo/${encodedVideoId}${urlEnd}&ads=0&h=${encodeURIComponent(h)}`;

return mediainfo_url;
}

async function attemptDecodingKeys(url: string, cachedToken: boolean) {
const keyUrls = ["https://github.com/KillerDogeEmpire/vidplay-keys/blob/keys/keys.json", "https://github.com/Ciarands/vidsrc-keys/blob/main/keys.json"];
for (let keyUrl of keyUrls) {
const sourcesUrl = await getUrl(`${url}&autostart=true`, keyUrl, cachedToken);

let sourcesRes: IMediaInfo = await request.get(sourcesUrl, {
headers: {
Referer: url
}
}).then(f => f.json());

// @ts-ignore
// Basically, when the url is invalid, result is set to "404" instead of the normal object
// with sources & tracks.
if (sourcesRes.result != 404) {
return sourcesRes;
}
async function attemptDecodingKeys(url: string, keys: ParsedKeys) {
const sourcesUrl = await getUrl(`${url}&autostart=true`, keys);

let sourcesRes: IMediaInfo = await request.get(sourcesUrl, {headers: {Referer: url}}).then(f => f.json());

// Basically, when the url is invalid, result is set to "404" instead of the normal object
// with sources & tracks.
if (sourcesRes.result as unknown as number != 404) {
return sourcesRes;
}

throw Error("Couldn't get source.")
}
// KEYS DECRYPTION END //

// SELENIUM DECRYPTION //
async function attemptDecodingSelenium(url: string) {
const mediaInfo: IMediaInfo = await request.post("https://anithunder.vercel.app/api/mcloud", {
headers: {"Content-Type": "application/json"},
body: JSON.stringify({"url": url})}
).then(req => req.json())
).then(req => req.json());

return mediaInfo;
}
// SELENIUM DECRYPTION END //

function embed_dec(inp) {
const i = b64decode((inp).replace(/_/g, "/").replace(/-/g, "+"));
let e = rc4Cypher('eO74cTKZayUWH8x5', i);
e = decodeURIComponent(e);
return e;
}

// Note: this is named mycloud but works for both mycloud & vidplay (now named vidstream & megaf)
export class MyCloudE extends VideoExtractor {
Expand All @@ -111,32 +93,41 @@ export class MyCloudE extends VideoExtractor {
// There are 3 major iterations of this extractor:
// - before (including) commit https://github.com/Nixuge/mochis/commit/ce615f9ff486ec82b01dcdcb8e6d08a987871d8d, where things are handled in a good part server side but using keys extracted myself.
// - before (including) commit https://github.com/Nixuge/mochis/commit/e30e87e5e9c56bd767bc1e3af3454903fcad2295, where things were only almost all handled using a browser on the server side (basically a hotfix done in haste because I didn't have a lot of time).
// - after (including) commit https://github.com/Nixuge/mochis/commit/50d28ceb78e960baadf8c9aa0ece2a8a9fef22a9, where third party keys are used for faster loading, still with the browser fallback from the previous commits.
//
// - before (including) commit https://github.com/Nixuge/mochis/commit/c7f49451da720ee35925b502bfb3e1fa6750746d, where third party keys are used for faster loading, still with the browser fallback from the previous commits.
// - after (not including, the one right after that bumps to 0.6.6!) commit https://github.com/Nixuge/mochis/commit/33492fe2c62b0d9d1164e11230887460b52b51f9, (the first one) where they changed how the mediaInfo response worked, having its response's result encrypted.
// If possible in the future should setup my own key extractor for better stability
let keys: ParsedKeys;
try {
keys = await grabKeysFromGithub("https://github.com/Ciarands/vidsrc-keys/blob/main/keys.json");
} catch(e) {
throw Error("Couldn't get keys to decrypt url " + this.referer);
}


let mediaInfo: IMediaInfo;
try {
console.log("attempting extraction using a cached token");
mediaInfo = await attemptDecodingKeys(url, true);
console.log("attempting extraction using keys");
mediaInfo = await attemptDecodingKeys(url, keys);
} catch(e) {
try {
console.log("attempting extraction using a fresh token");
mediaInfo = await attemptDecodingKeys(url, false);
} catch(e) {
console.log("attempting extraction using a browser");
mediaInfo = await attemptDecodingSelenium(url);
// If this fails again, just let it throw an error...
}
// Note: this isn't really that useful anymore since we rely on keys to decrypt
console.log("attempting extraction using a browser");
mediaInfo = await attemptDecodingSelenium(url);
// If this fails again, just let it throw an error...
}

mediaInfo.result = JSON.parse(embed_dec(mediaInfo.result));
console.log("Successfully got mediaInfo. Now attempting to decrypt result.");
try {
mediaInfo.result = JSON.parse(decodeResult(mediaInfo.result as unknown as string, keys.decrypt[1]));
} catch(e) {
throw Error("Couldn't get mediaInfo. This is usually due to outdated keys. Please try another server for now.");
}
console.log("Successfully decrypted result !");

const sourcesJson = mediaInfo.result.sources;

const videos = await getM3u8Qualities(sourcesJson[0]["file"])
const videos = await getM3u8Qualities(sourcesJson[0]["file"]);

const subtitles = parseSubtitles(mediaInfo.result.tracks, PlaylistEpisodeServerSubtitleFormat.vtt, true)
const subtitles = parseSubtitles(mediaInfo.result.tracks, PlaylistEpisodeServerSubtitleFormat.vtt, true);

return {
videos: videos,
Expand Down

0 comments on commit 246db5f

Please sign in to comment.