// cineby - bundled from src/cineby/ var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); // src/cineby/http.js var SITE_URL = "https://www.cineby.at"; var METADATA_API_URL = "https://db.speedracelight.com/3"; var SOURCE_API_URL = "https://api.speedracelight.com"; var HEADERS = { "Accept": "*/*", "Origin": SITE_URL, "Referer": `${SITE_URL}/`, "User-Agent": "Mozilla/5.0 (Linux; Android 15) AppleWebKit/537.36 Chrome/140.0.0.0 Mobile Safari/537.36" }; function mergeHeaders(headers) { return __spreadValues(__spreadValues({}, HEADERS), headers || {}); } async function fetchText(url, options = {}) { const response = await fetch(url, __spreadProps(__spreadValues({}, options), { headers: mergeHeaders(options.headers) })); if (!response.ok) { const error = new Error(`HTTP ${response.status} for ${url}`); error.status = response.status; throw error; } return await response.text(); } async function fetchJson(url, options = {}) { return JSON.parse(await fetchText(url, options)); } function buildQuery(params) { return Object.keys(params).filter((key) => params[key] != null && params[key] !== "").map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(String(params[key]))}`).join("&"); } // src/cineby/extractor.js var STREAM_MAGIC = [109, 118, 109, 49]; var MIX_CONSTANTS = [ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580 ]; var CINEMETA_URL = "https://v3-cinemeta.strem.io/meta"; var TMDB_ID_CACHE = {}; function mix(value) { value >>>= 0; value ^= value >>> 16; value = Math.imul(value, 2246822507) >>> 0; value ^= value >>> 13; value = Math.imul(value, 3266489909) >>> 0; return (value ^= value >>> 16) >>> 0; } function rotateLeft(value, amount) { value >>>= 0; amount &= 31; return amount === 0 ? value : (value << amount | value >>> 32 - amount) >>> 0; } function isEven(value) { return (value * (value + 1) & 1) === 0; } function isOdd(value) { return (value * (value + 1) & 1) === 1; } function decodeBase64Url(value) { const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const normalized = String(value || "").replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""); const bytes = []; let buffer = 0; let bits = 0; for (let index = 0; index < normalized.length; index++) { const code = alphabet.indexOf(normalized[index]); if (code < 0) { continue; } buffer = buffer << 6 | code; bits += 6; if (bits >= 8) { bits -= 8; bytes.push(buffer >>> bits & 255); } } return new Uint8Array(bytes); } function decodeUtf8(bytes) { if (typeof TextDecoder !== "undefined") { return new TextDecoder("utf-8").decode(bytes); } let encoded = ""; for (let index = 0; index < bytes.length; index++) { encoded += `%${`0${bytes[index].toString(16)}`.slice(-2)}`; } return decodeURIComponent(encoded); } function createKeyStream(seed, mediaId, length) { const state = function createState(seedValue, mediaIdentifier) { if (isOdd(seedValue.length)) { const values2 = Array(256); for (let index = 0; index < 256; index++) values2[index] = index; let cursor = 0; for (let index = 0; index < 256; index++) { cursor = cursor + values2[index] + seedValue.charCodeAt(index % seedValue.length) & 255; const current = values2[index]; values2[index] = values2[cursor]; values2[cursor] = current; } let accumulator = 1732584193; for (let index = 0; index < seedValue.length; index++) { accumulator = rotateLeft( (accumulator ^ Math.imul(seedValue.charCodeAt(index), MIX_CONSTANTS[15 & index])) >>> 0, 5 ); } return { S: values2, acc: mix(accumulator) }; } const values = Array(61); let seedHash = 2166136261; for (let index = 0; index < seedValue.length; index++) { seedHash = Math.imul(seedHash ^ seedValue.charCodeAt(index), 16777619) >>> 0; } let value = mix(mix(seedHash) ^ mix(mediaIdentifier >>> 0 ^ 2654435769)) >>> 0; for (let index = 0; index < 8; index++) { if (isEven(index)) { const position = value % 61; value = rotateLeft(value + 2654435769 >>> 0, 7 + (7 & index)); values[position] = (value ^ mix(value)) >>> 0; value = mix(value + position >>> 0); } else { values[index] = MIX_CONSTANTS[15 & index]; } } return { S: values, acc: mix(2779096485 ^ value) >>> 0 }; }(seed, mediaId); const stream = new Uint8Array(length); let streamIndex = 0; let wordCounter = 0; while (streamIndex < length) { const word = function nextWord(currentState, counter) { let previous; let mixed; const values = currentState.S; let accumulator = currentState.acc; const position = accumulator % 61; const present = 0 - Number(position in values); const stored = values[position] >>> 0; let selected = (((previous = accumulator) ^ (mixed = (stored ^ Math.imul(2654435769, counter + 1) >>> 0) >>> 0)) >>> 0 | (previous & mixed & present) >>> 0) >>> 0; accumulator = mix((selected = (rotateLeft(selected + accumulator >>> 0, 31 & position) ^ rotateLeft(accumulator, 31 & Math.imul(position, 7))) >>> 0) + 2654435769 >>> 0); values[position] = accumulator >>> 0; currentState.acc = accumulator; return accumulator >>> 0; }(state, wordCounter++); stream[streamIndex++] = word & 255; if (streamIndex < length) stream[streamIndex++] = word >>> 8 & 255; if (streamIndex < length) stream[streamIndex++] = word >>> 16 & 255; if (streamIndex < length) stream[streamIndex++] = word >>> 24 & 255; } return stream; } function decryptPayload(raw, seed, mediaId) { const encrypted = decodeBase64Url(raw); const keyStream = createKeyStream(seed, mediaId, encrypted.length); for (let index = 0; index < encrypted.length; index++) { encrypted[index] ^= keyStream[index]; } for (let index = 0; index < STREAM_MAGIC.length; index++) { if (encrypted[index] !== STREAM_MAGIC[index]) { throw new Error("Cineby source payload could not be decrypted"); } } return decodeUtf8(encrypted.slice(STREAM_MAGIC.length)); } function isPositiveInteger(value) { return Number.isInteger(Number(value)) && Number(value) > 0; } async function resolveTmdbId(value, mediaType) { const id = String(value || "").trim(); if (/^\d+$/.test(id)) return id; if (!/^tt\d+$/i.test(id)) return null; const cacheKey = `${mediaType}:${id.toLowerCase()}`; if (TMDB_ID_CACHE[cacheKey]) return TMDB_ID_CACHE[cacheKey]; const catalogType = mediaType === "movie" ? "movie" : "series"; const metadata = await fetchJson( `${CINEMETA_URL}/${catalogType}/${encodeURIComponent(id)}.json`, { headers: { "Accept": "application/json" } } ); const resolved = metadata && metadata.meta && metadata.meta.moviedb_id; if (!resolved || !/^\d+$/.test(String(resolved))) { return null; } TMDB_ID_CACHE[cacheKey] = String(resolved); return TMDB_ID_CACHE[cacheKey]; } async function getMetadata(tmdbId, mediaType) { const endpoint = mediaType === "movie" ? "movie" : "tv"; const data = await fetchJson( `${METADATA_API_URL}/${endpoint}/${encodeURIComponent(tmdbId)}?append_to_response=external_ids` ); const title = mediaType === "movie" ? data.title || data.original_title : data.name || data.original_name; const date = mediaType === "movie" ? data.release_date : data.first_air_date; const imdbId = data.imdb_id || data.external_ids && data.external_ids.imdb_id || ""; if (!title || !date) { throw new Error("Cineby metadata response is incomplete"); } return { title, year: String(date).slice(0, 4), imdbId }; } async function getSeed(mediaId) { const response = await fetchJson( `${SOURCE_API_URL}/seed?${buildQuery({ mediaId })}` ); if (!response || !response.seed) { throw new Error("Cineby did not return a source seed"); } return response.seed; } async function getSources(tmdbId, mediaType, season, episode, metadata) { const request = { // Cineby's player pre-encodes the title, then its HTTP client serializes // it again. Preserve that double encoding to match the live API. title: encodeURIComponent(metadata.title), mediaType, year: metadata.year, episodeId: mediaType === "movie" ? 1 : episode, seasonId: mediaType === "movie" ? 1 : season, tmdbId, imdbId: metadata.imdbId, enc: 2 }; for (let attempt = 0; attempt < 2; attempt++) { const seed = await getSeed(tmdbId); const url = `${SOURCE_API_URL}/cdn/sources-with-title?${buildQuery(__spreadProps(__spreadValues({}, request), { seed }))}`; try { const raw = (await fetchText(url)).trim(); if (raw.startsWith("{")) { return JSON.parse(raw); } return JSON.parse(decryptPayload(raw, seed, Number(tmdbId))); } catch (error) { if (attempt === 0 && error && error.status === 401) { continue; } throw error; } } return { sources: [] }; } function getQuality(source) { const value = String(source && source.quality || ""); if (/^4k$/i.test(value)) return "4K"; const match = value.match(/(2160|1440|1080|720|480|360)p?/i); return match ? `${match[1]}p` : "Auto"; } function normalizeStreams(payload) { const streams = []; const seenUrls = {}; const sources = payload && Array.isArray(payload.sources) ? payload.sources : []; for (const source of sources) { const url = source && typeof source.url === "string" ? source.url.trim() : ""; if (!url || seenUrls[url]) continue; seenUrls[url] = true; const quality = getQuality(source); streams.push({ name: "Cineby", title: `Cineby \xB7 ${quality}`, url, quality, headers: { "Referer": `${SITE_URL}/`, "Origin": SITE_URL, "User-Agent": HEADERS["User-Agent"] } }); } return streams; } async function extractStreams(tmdbId, mediaType, season, episode) { if (mediaType !== "movie" && mediaType !== "tv") return []; if (mediaType === "tv" && (!isPositiveInteger(season) || !isPositiveInteger(episode))) return []; const resolvedTmdbId = await resolveTmdbId(tmdbId, mediaType); if (!resolvedTmdbId) return []; const metadata = await getMetadata(resolvedTmdbId, mediaType); const payload = await getSources( resolvedTmdbId, mediaType, Number(season), Number(episode), metadata ); return normalizeStreams(payload); } // src/cineby/index.js async function getStreams(tmdbId, mediaType, season, episode) { try { const episodeLabel = mediaType === "tv" ? ` S${season}E${episode}` : ""; console.log(`[Cineby] Request: ${mediaType} ${tmdbId}${episodeLabel}`); const streams = await extractStreams(tmdbId, mediaType, season, episode); console.log(`[Cineby] Found ${streams.length} stream(s)`); return streams; } catch (error) { console.error(`[Cineby] Error: ${error.message}`); return []; } } module.exports = { getStreams };