diff --git a/.env.sample b/.env.sample index dcb7e8b..a775549 100644 --- a/.env.sample +++ b/.env.sample @@ -1,5 +1,3 @@ # пример заполнения: https://example.com, http://0.0.0.0:80 -NEXT_PUBLIC_KODIK_PARSER_URL= # Домен парсера кодика, требуется для просмотра с данного источника -NEXT_PUBLIC_ANILIBRIA_PARSER_URL= # Домен парсера анилибрии, если не заполнено, используется официальное апи -NEXT_PUBLIC_SIBNET_PARSER_URL= # Домен парсера сибнет, требуется для просмотра с данного источника +NEXT_PUBLIC_PLAYER_PARSER_URL= # Домен сервиса player-parsers, требуется для работы встроенного плеера # --- \ No newline at end of file diff --git a/app/api/config.ts b/app/api/config.ts index ec8fffd..ad32b6f 100644 --- a/app/api/config.ts +++ b/app/api/config.ts @@ -1,9 +1,9 @@ export const CURRENT_APP_VERSION = "3.7.0"; -export const API_URL = "https://api.anixart.tv"; +export const API_URL = "https://api.anixart.app"; export const API_PREFIX = "/api/proxy"; export const USER_AGENT = - "AnixartApp/8.2.1-23121216 (Android 9; SDK 28; arm64-v8a; samsung SM-G975N; en)"; + "AnixartApp/9.0 BETA 5-25062213 (Android 9; SDK 28; arm64-v8a; samsung SM-G975N; en)"; export const ENDPOINTS = { release: { diff --git a/app/components/ReleasePlayer/PlayerParsing.ts b/app/components/ReleasePlayer/PlayerParsing.ts index bd85ff4..c5cf87b 100644 --- a/app/components/ReleasePlayer/PlayerParsing.ts +++ b/app/components/ReleasePlayer/PlayerParsing.ts @@ -62,148 +62,35 @@ export async function _fetchPlayer( return data; } -function decryptKodikLink(enc: string) { - const decryptedBase64 = enc.replace(/[a-zA-Z]/g, (e: any) => { - return String.fromCharCode( - (e <= "Z" ? 90 : 122) >= (e = e.charCodeAt(0) + 18) ? e : e - 26 - ); - }); - return atob(decryptedBase64); -} export const _fetchKodikManifest = async ( url: string, setPlayerError: (state) => void ) => { - // Fetch episode links via edge function - const NEXT_PUBLIC_KODIK_PARSER_URL = env("NEXT_PUBLIC_KODIK_PARSER_URL") - if (!NEXT_PUBLIC_KODIK_PARSER_URL) { + const NEXT_PUBLIC_PLAYER_PARSER_URL = env("NEXT_PUBLIC_PLAYER_PARSER_URL") + if (!NEXT_PUBLIC_PLAYER_PARSER_URL) { setPlayerError({ - message: "Источник не настроен", - detail: "переменная 'NEXT_PUBLIC_KODIK_PARSER_URL' не обнаружена", + message: "Плеер не настроен", + detail: "переменная 'NEXT_PUBLIC_PLAYER_PARSER_URL' не обнаружена", }); return { manifest: null, poster: null }; } const data = await _fetchPlayer( - `${NEXT_PUBLIC_KODIK_PARSER_URL}/?url=${url}&player=kodik`, + `${NEXT_PUBLIC_PLAYER_PARSER_URL}/?url=${url}&player=kodik`, setPlayerError ); + if (data) { - let lowQualityLink = data.links["360"][0].src; // we assume that 360p is always present - - if (!lowQualityLink.includes("//")) { - // check if link is encrypted, else do nothing - lowQualityLink = decryptKodikLink(lowQualityLink); + let manifest: string = data.manifest + if (!manifest.startsWith("http")) { + let file = new File([manifest], "manifest.m3u8", { + type: "application/x-mpegURL", + }); + manifest = URL.createObjectURL(file); + } + return { manifest, poster: data.poster }; } - - if (lowQualityLink.includes("https://")) { - // strip the https prefix, since we add it manually - lowQualityLink = lowQualityLink.replace("https://", "//"); - } - - let manifest = `https:${lowQualityLink.replace("360.mp4:hls:", "")}`; - let poster = `https:${lowQualityLink.replace("360.mp4:hls:manifest.m3u8", "thumb001.jpg")}`; - - if ( - lowQualityLink.includes("animetvseries") || - lowQualityLink.includes("tvseries") - ) { - // if link includes "animetvseries" or "tvseries" we need to construct manifest ourselves - let blobTxt = "#EXTM3U\n"; - - if (data.links.hasOwnProperty("240")) { - blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=427x240,BANDWIDTH=200000\n"; - let link = data.links["240"][0].src; - let dec = null; - link.includes("//") ? - link.startsWith("https:") ? - (blobTxt += `${link}\n`) - : (blobTxt += `https:${link}\n`) - : (dec = decryptKodikLink(link)); - - dec ? - dec.startsWith("https:") ? - (blobTxt += `${dec}\n`) - : (blobTxt += `https:${dec}\n`) - : null; - } - - if (data.links.hasOwnProperty("360")) { - blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=578x360,BANDWIDTH=400000\n"; - let link = data.links["360"][0].src; - let dec = null; - link.includes("//") ? - link.startsWith("https:") ? - (blobTxt += `${link}\n`) - : (blobTxt += `https:${link}\n`) - : (dec = decryptKodikLink(link)); - - dec ? - dec.startsWith("https:") ? - (blobTxt += `${dec}\n`) - : (blobTxt += `https:${dec}\n`) - : null; - } - - if (data.links.hasOwnProperty("480")) { - blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=854x480,BANDWIDTH=596000\n"; - let link = data.links["480"][0].src; - let dec = null; - link.includes("//") ? - link.startsWith("https:") ? - (blobTxt += `${link}\n`) - : (blobTxt += `https:${link}\n`) - : (dec = decryptKodikLink(link)); - - dec ? - dec.startsWith("https:") ? - (blobTxt += `${dec}\n`) - : (blobTxt += `https:${dec}\n`) - : null; - } - - if (data.links.hasOwnProperty("720")) { - blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=1280x720,BANDWIDTH=1280000\n"; - let link = data.links["720"][0].src; - let dec = null; - link.includes("//") ? - link.startsWith("https:") ? - (blobTxt += `${link}\n`) - : (blobTxt += `https:${link}\n`) - : (dec = decryptKodikLink(link)); - - dec ? - dec.startsWith("https:") ? - (blobTxt += `${dec}\n`) - : (blobTxt += `https:${dec}\n`) - : null; - } - - if (data.links.hasOwnProperty("1080")) { - blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=1920x1080,BANDWIDTH=2560000\n"; - let link = data.links["1080"][0].src; - let dec = null; - link.includes("//") ? - link.startsWith("https:") ? - (blobTxt += `${link}\n`) - : (blobTxt += `https:${link}\n`) - : (dec = decryptKodikLink(link)); - - dec ? - dec.startsWith("https:") ? - (blobTxt += `${dec}\n`) - : (blobTxt += `https:${dec}\n`) - : null; - } - - let file = new File([blobTxt], "manifest.m3u8", { - type: "application/x-mpegURL", - }); - manifest = URL.createObjectURL(file); - } - return { manifest, poster }; - } return { manifest: null, poster: null }; }; @@ -211,32 +98,26 @@ export const _fetchAnilibriaManifest = async ( url: string, setPlayerError: (state) => void ) => { - const id = url.split("?id=")[1].split("&ep=")[0]; - const epid = url.split("?id=")[1].split("&ep=")[1]; - const _url = `https://api.anilibria.tv/v3/title?id=${id}`; - let data = null; - const NEXT_PUBLIC_ANILIBRIA_PARSER_URL = env("NEXT_PUBLIC_ANILIBRIA_PARSER_URL") - if (NEXT_PUBLIC_ANILIBRIA_PARSER_URL) { - data = await _fetchPlayer( - `${NEXT_PUBLIC_ANILIBRIA_PARSER_URL}/?url=${_url}&player=libria`, - setPlayerError - ); - } else { - data = await _fetchPlayer(_url, setPlayerError); + const NEXT_PUBLIC_PLAYER_PARSER_URL = env("NEXT_PUBLIC_PLAYER_PARSER_URL") + if (!NEXT_PUBLIC_PLAYER_PARSER_URL) { + setPlayerError({ + message: "Плеер не настроен", + detail: "переменная 'NEXT_PUBLIC_PLAYER_PARSER_URL' не обнаружена", + }); + return { manifest: null, poster: null }; } - if (data) { - const host = `https://${data.player.host}`; - const ep = data.player.list[epid]; + const data = await _fetchPlayer( + `${NEXT_PUBLIC_PLAYER_PARSER_URL}/?url=${encodeURIComponent(url)}&player=libria`, + setPlayerError + ); - // we need to manually construct a manifest file for a hls player - const blobTxt = `#EXTM3U\n${ep.hls.sd && `#EXT-X-STREAM-INF:RESOLUTION=854x480,BANDWIDTH=596000\n${host}${ep.hls.sd}\n`}${ep.hls.hd && `#EXT-X-STREAM-INF:RESOLUTION=1280x720,BANDWIDTH=1280000\n${host}${ep.hls.hd}\n`}${ep.hls.fhd && `#EXT-X-STREAM-INF:RESOLUTION=1920x1080,BANDWIDTH=2560000\n${host}${ep.hls.fhd}\n`}`; - let file = new File([blobTxt], "manifest.m3u8", { + if (data) { + let file = new File([data.manifest], "manifest.m3u8", { type: "application/x-mpegURL", }); let manifest = URL.createObjectURL(file); - let poster = `https://anixart.libria.fun${ep.preview}`; - return { manifest, poster }; + return { manifest, poster: data.poster }; } return { manifest: null, poster: null }; }; @@ -245,23 +126,22 @@ export const _fetchSibnetManifest = async ( url: string, setPlayerError: (state) => void ) => { - // Fetch data via cloud endpoint - const NEXT_PUBLIC_SIBNET_PARSER_URL = env("NEXT_PUBLIC_SIBNET_PARSER_URL") - if (!NEXT_PUBLIC_SIBNET_PARSER_URL) { + const NEXT_PUBLIC_PLAYER_PARSER_URL = env("NEXT_PUBLIC_PLAYER_PARSER_URL") + if (!NEXT_PUBLIC_PLAYER_PARSER_URL) { setPlayerError({ - message: "Источник не настроен", - detail: "переменная 'NEXT_PUBLIC_SIBNET_PARSER_URL' не обнаружена", + message: "Плеер не настроен", + detail: "переменная 'NEXT_PUBLIC_PLAYER_PARSER_URL' не обнаружена", }); return { manifest: null, poster: null }; } + const data = await _fetchPlayer( - `${NEXT_PUBLIC_SIBNET_PARSER_URL}/?url=${url}&player=sibnet`, + `${NEXT_PUBLIC_PLAYER_PARSER_URL}/?url=${url}&player=sibnet`, setPlayerError ); + if (data) { - let manifest = data.video; - let poster = data.poster; - return { manifest, poster }; + return { manifest: data.manifest, poster: data.poster }; } return { manifest: null, poster: null }; }; diff --git a/app/components/SettingsModal/SettingsModal.tsx b/app/components/SettingsModal/SettingsModal.tsx index 90ee3c3..56d9291 100644 --- a/app/components/SettingsModal/SettingsModal.tsx +++ b/app/components/SettingsModal/SettingsModal.tsx @@ -16,6 +16,8 @@ import { useThemeMode, } from "flowbite-react"; import Link from "next/link"; +import { env } from "next-runtime-env"; +import { useEffect, useState } from "react"; const HomeCategory = { last: "Последние релизы", @@ -51,6 +53,14 @@ export const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => { const userStore = useUserStore(); const { computedMode, setMode } = useThemeMode(); + const [isPlayerConfigured, setIsPlayerConfigured] = useState(false); + + useEffect(() => { + const NEXT_PUBLIC_PLAYER_PARSER_URL = env("NEXT_PUBLIC_PLAYER_PARSER_URL") || null; + if (NEXT_PUBLIC_PLAYER_PARSER_URL) { + setIsPlayerConfigured(true); + } + }, []); return ( { }) } checked={preferenceStore.params.experimental.newPlayer} + disabled={!isPlayerConfigured} /> diff --git a/player-parsers/sibnet.ts b/player-parsers/sibnet.ts index 46e9dd7..ab9b752 100644 --- a/player-parsers/sibnet.ts +++ b/player-parsers/sibnet.ts @@ -46,7 +46,7 @@ export async function getSibnetURL(res, url: string) { return } - const video = actualVideoRes.headers.get("location"); + const video = `https:${actualVideoRes.headers.get("location")}`; const poster = posterMatch ? posterMatch.length > 0 ?