Files
seanime-docker/seanime-2.9.10/internal/extension_repo/goja_onlinestream_test/my-onlinestream-provider.ts
2025-09-20 14:08:38 +01:00

251 lines
8.0 KiB
TypeScript

/// <reference path="./onlinestream-provider.d.ts" />
/// <reference path="../../goja/goja_bindings/js/core.d.ts" />
class ProviderN {
api = "https://anitaku.to"
ajaxURL = "https://ajax.gogocdn.net"
getSettings(): Settings {
return {
episodeServers: ["gogocdn", "vidstreaming", "streamsb"],
supportsDub: true,
}
}
async search(opts: SearchOptions): Promise<SearchResult[]> {
const request = await fetch(`${this.api}/search.html?keyword=${encodeURIComponent(opts.query)}`)
if (!request.ok) {
return []
}
const data = await request.text()
const results: SearchResult[] = []
const $ = LoadDoc(data)
$("ul.items > li").each((_, el) => {
const title = el.find("p.name a").text().trim()
const id = el.find("div.img a").attr("href")
if (!id) {
return
}
results.push({
id: id,
title: title,
url: id,
subOrDub: "sub",
})
})
return results
}
async findEpisodes(id: string): Promise<EpisodeDetails[]> {
const episodes: EpisodeDetails[] = []
const data = await (await fetch(`${this.api}${id}`)).text()
const $ = LoadDoc(data)
const epStart = $("#episode_page > li").first().find("a").attr("ep_start")
const epEnd = $("#episode_page > li").last().find("a").attr("ep_end")
const movieId = $("#movie_id").attr("value")
const alias = $("#alias_anime").attr("value")
const req = await (await fetch(`${this.ajaxURL}/ajax/load-list-episode?ep_start=${epStart}&ep_end=${epEnd}&id=${movieId}&default_ep=${0}&alias=${alias}`)).text()
const $$ = LoadDoc(req)
$$("#episode_related > li").each((i, el) => {
episodes?.push({
id: el.find("a").attr("href")?.trim() ?? "",
url: el.find("a").attr("href")?.trim() ?? "",
number: parseFloat(el.find(`div.name`).text().replace("EP ", "")),
title: el.find(`div.name`).text(),
})
})
return episodes.reverse()
}
async findEpisodeServer(episode: EpisodeDetails, _server: string): Promise<EpisodeServer> {
let server = "gogocdn"
if (_server !== "default") {
server = _server
}
const episodeServer: EpisodeServer = {
server: server,
headers: {},
videoSources: [],
}
if (episode.id.startsWith("http")) {
const serverURL = episode.id
try {
const es = await new Extractor(serverURL, episodeServer).extract(server)
if (es) {
return es
}
}
catch (e) {
console.error(e)
return episodeServer
}
return episodeServer
}
const data = await (await fetch(`${this.api}${episode.id}`)).text()
const $ = LoadDoc(data)
let serverURL: string
switch (server) {
case "gogocdn":
serverURL = `${$("#load_anime > div > div > iframe").attr("src")}`
break
case "vidstreaming":
serverURL = `${$("div.anime_video_body > div.anime_muti_link > ul > li.vidcdn > a").attr("data-video")}`
break
case "streamsb":
serverURL = $("div.anime_video_body > div.anime_muti_link > ul > li.streamsb > a").attr("data-video")!
break
default:
serverURL = `${$("#load_anime > div > div > iframe").attr("src")}`
break
}
episode.id = serverURL
return await this.findEpisodeServer(episode, server)
}
}
class Extractor {
private url: string
private result: EpisodeServer
constructor(url: string, result: EpisodeServer) {
this.url = url
this.result = result
}
async extract(server: string): Promise<EpisodeServer | undefined> {
try {
switch (server) {
case "gogocdn":
console.log("GogoCDN extraction")
return await this.extractGogoCDN(this.url, this.result)
case "vidstreaming":
return await this.extractGogoCDN(this.url, this.result)
default:
return undefined
}
}
catch (e) {
console.error(e)
return undefined
}
}
public async extractGogoCDN(url: string, result: EpisodeServer): Promise<EpisodeServer> {
const keys = {
key: CryptoJS.enc.Utf8.parse("37911490979715163134003223491201"),
secondKey: CryptoJS.enc.Utf8.parse("54674138327930866480207815084989"),
iv: CryptoJS.enc.Utf8.parse("3134003223491201"),
}
function generateEncryptedAjaxParams(id: string) {
const encryptedKey = CryptoJS.AES.encrypt(id, keys.key, {
iv: keys.iv,
})
const scriptValue = $("script[data-name='episode']").data("value")!
const decryptedToken = CryptoJS.AES.decrypt(scriptValue, keys.key, {
iv: keys.iv,
}).toString(CryptoJS.enc.Utf8)
return `id=${encryptedKey.toString(CryptoJS.enc.Base64)}&alias=${id}&${decryptedToken}`
}
function decryptAjaxData(encryptedData: string) {
const decryptedData = CryptoJS.AES.decrypt(encryptedData, keys.secondKey, {
iv: keys.iv,
}).toString(CryptoJS.enc.Utf8)
return JSON.parse(decryptedData)
}
const req = await fetch(url)
const $ = LoadDoc(await req.text())
const encryptedParams = generateEncryptedAjaxParams(new URL(url).searchParams.get("id") ?? "")
const xmlHttpUrl = `${new URL(url).protocol}//${new URL(url).hostname}/encrypt-ajax.php?${encryptedParams}`
const encryptedData = await fetch(xmlHttpUrl, {
headers: {
"X-Requested-With": "XMLHttpRequest",
},
})
const decryptedData = await decryptAjaxData(((await encryptedData.json()) as { data: any })?.data)
if (!decryptedData.source) throw new Error("No source found. Try a different server.")
if (decryptedData.source[0].file.includes(".m3u8")) {
const resResult = await fetch(decryptedData.source[0].file.toString())
const resolutions = (await resResult.text()).match(/(RESOLUTION=)(.*)(\s*?)(\s*.*)/g)
resolutions?.forEach((res: string) => {
const index = decryptedData.source[0].file.lastIndexOf("/")
const quality = res.split("\n")[0].split("x")[1].split(",")[0]
const url = decryptedData.source[0].file.slice(0, index)
result.videoSources.push({
url: url + "/" + res.split("\n")[1],
quality: quality + "p",
subtitles: [],
type: "m3u8",
})
})
decryptedData.source.forEach((source: any) => {
result.videoSources.push({
url: source.file,
quality: "default",
subtitles: [],
type: "m3u8",
})
})
} else {
decryptedData.source.forEach((source: any) => {
result.videoSources.push({
url: source.file,
quality: source.label.split(" ")[0] + "p",
subtitles: [],
type: "m3u8",
})
})
decryptedData.source_bk.forEach((source: any) => {
result.videoSources.push({
url: source.file,
quality: "backup",
subtitles: [],
type: "m3u8",
})
})
}
return result
}
}