Spaces:
Paused
Paused
import { searchTorrents, } from "../torrent/search.js"; | |
import { getTorrentInfo } from "../torrent/webtorrent.js"; | |
import { getReadableSize, isSubtitleFile, isVideoFile } from "../utils/file.js"; | |
import { getTitles } from "../utils/imdb.js"; | |
import { guessLanguage } from "../utils/language.js"; | |
import { guessQuality } from "../utils/quality.js"; | |
import { isFileNameMatch, isTorrentNameMatch } from "../utils/shows.js"; | |
export const streamHandler = async ({ type, id, config, req }) => { | |
let torrents = []; | |
const categories = []; | |
if (type === "movie") | |
categories.push("movie"); | |
if (type === "series") | |
categories.push("show"); | |
const sources = []; | |
if (config.enableJackett === "on") | |
sources.push("jackett"); | |
if (config.enableNcore === "on") | |
sources.push("ncore"); | |
if (config.enableInsane === "on") | |
sources.push("insane"); | |
if (config.enableItorrent === "on") | |
sources.push("itorrent"); | |
if (config.enableYts === "on") | |
sources.push("yts"); | |
if (config.enableEztv === "on") | |
sources.push("eztv"); | |
const [imdbId, season, episode] = id.split(":"); | |
const queries = [imdbId]; | |
if (config.searchByTitle === "on") | |
queries.push(...(await getTitles(imdbId))); | |
torrents = (await Promise.all(queries.map((query) => searchTorrents(query, { | |
categories, | |
sources, | |
jackett: { | |
url: config.jackettUrl, | |
apiKey: config.jackettKey, | |
}, | |
ncore: { | |
user: config.nCoreUser, | |
password: config.nCorePassword, | |
}, | |
insane: { | |
user: config.insaneUser, | |
password: config.insanePassword, | |
}, | |
})))).flat(); | |
torrents = dedupeTorrents(torrents); | |
torrents = torrents.filter((torrent) => { | |
if (!torrent.seeds) | |
return false; | |
if (torrent.category?.includes("DVD")) | |
return false; | |
if (!isAllowedFormat(config, torrent.name)) | |
return false; | |
if (!isAllowedQuality(config, guessQuality(torrent.name).quality)) | |
return false; | |
if (season && | |
episode && | |
!isTorrentNameMatch(torrent.name, Number(season), Number(episode))) | |
return false; | |
return true; | |
}); | |
let streams = (await Promise.all(torrents.map((torrent) => getStreamsFromTorrent(req, torrent, season, episode)))).flat(); | |
streams = streams.filter((stream) => { | |
if (!isAllowedFormat(config, stream.fileName)) | |
return false; | |
if (!isAllowedQuality(config, stream.quality)) | |
return false; | |
return true; | |
}); | |
streams.sort((a, b) => b.score - a.score); | |
return { streams: streams.map((stream) => stream.stream) }; | |
}; | |
const dedupeTorrents = (torrents) => { | |
const map = new Map(torrents.map((torrent) => [`${torrent.tracker}:${torrent.name}`, torrent])); | |
return [...map.values()]; | |
}; | |
export const getStreamsFromTorrent = async (req, torrent, season, episode) => { | |
const uri = torrent.torrent || torrent.magnet; | |
if (!uri) | |
return []; | |
const torrentInfo = await getTorrentInfo(uri); | |
if (!torrentInfo) | |
return []; | |
let videos = torrentInfo.files.filter((file) => isVideoFile(file.name)); | |
if (season && episode) { | |
videos = videos.filter((file) => isFileNameMatch(file.name, Number(season), Number(episode))); | |
} | |
const videosSize = videos.reduce((acc, file) => acc + file.size, 0); | |
videos = videos.filter((file) => file.size > videosSize / (videos.length + 1)); | |
const subs = torrentInfo.files.filter((file) => isSubtitleFile(file.name)); | |
const torrentQuality = guessQuality(torrent.name); | |
const language = guessLanguage(torrent.name, torrent.category); | |
// @ts-ignore | |
return videos.map((file) => { | |
const fileQuality = guessQuality(file.name); | |
const { quality, score } = fileQuality.score > torrentQuality.score ? fileQuality : torrentQuality; | |
const description = [ | |
...(season && episode ? [torrent.name, file.name] : [torrent.name]), | |
[ | |
`💾 ${getReadableSize(file.size)}`, | |
`⬆️ ${torrent.seeds}`, | |
`⬇️ ${torrent.peers}`, | |
].join(" "), | |
[`🔊 ${language}`, `⚙️ ${torrent.tracker}`].join(" "), | |
].join("\n"); | |
const streamEndpoint = `${req.protocol}://${req.get("host")}/stream`; | |
const url = [ | |
streamEndpoint, | |
encodeURIComponent(uri), | |
encodeURIComponent(file.path), | |
].join("/"); | |
const subtitles = subs.map((sub, index) => ({ | |
id: index.toString(), | |
url: [ | |
streamEndpoint, | |
encodeURIComponent(uri), | |
encodeURIComponent(sub.path), | |
].join("/"), | |
lang: sub.name, | |
})); | |
return { | |
stream: { | |
name: quality, | |
description, | |
url, | |
subtitles, | |
behaviorHints: { | |
bingeGroup: torrent.name, | |
}, | |
}, | |
torrentName: torrent.name, | |
fileName: file.name, | |
quality, | |
score, | |
}; | |
}); | |
}; | |
const isAllowedQuality = (config, quality) => { | |
if (config?.disable4k === "on" && quality.includes("4K")) | |
return false; | |
if (config?.disableCam === "on" && quality.includes("CAM")) | |
return false; | |
if (config?.disableHdr === "on" && | |
(quality.includes("HDR") || quality.includes("Dolby Vision"))) | |
return false; | |
if (config?.disable3d === "on" && quality.includes("3D")) | |
return false; | |
return true; | |
}; | |
const isAllowedFormat = (config, name) => { | |
if (config?.disableHevc === "on") { | |
const str = name.replace(/\W/g, "").toLowerCase(); | |
if (str.includes("x265") || str.includes("h265") || str.includes("hevc")) | |
return false; | |
} | |
return true; | |
}; | |
//# sourceMappingURL=streams.js.map |