diff --git a/javascript/modules/TunimeApi.js b/javascript/modules/TunimeApi.js index 1ad5207..319fc58 100644 --- a/javascript/modules/TunimeApi.js +++ b/javascript/modules/TunimeApi.js @@ -1,244 +1,346 @@ import { Sleep } from "./funcitons.js"; -class Tunime { - static get standart() { - return { id: 'shadow', key: 'tunime-register', date: Date.now() - 10000 } - } +const LIFETIME = 600000; + +class Server { + #base = 'https://tunime.onrender.com'; + #list = [ + 'https://tunime-hujg.onrender.com', + ]; + #url = undefined; + #key = 'shadow-url'; + #id = -1; + get url() { + if (this.#url !== undefined) + return this.#url; + + this.#url = this.#base; + const data = sessionStorage.getItem(this.#key); - constructor() { - this.lk = 'shadow-api'; - this.life = 350000; - this.time = 299000; - this.interval = undefined; - this.url = 'tunime.onrender.com'; - this.link = { - save: 'hujg', - domain: 'onrender.com', - name: 'tunime' - }; - this.tunime = { - url: 'tunime.onrender.com', - save: 'hujg', - name: 'tunime' - }; - this.m3u8 = { - url: 'anime-m3u8.onrender.com', - save: 'qmyf', - name: 'anime-m3u8' - }; - if (window.location.pathname != '/player.html') { - this.Update(); + if (data !== null) { + /**@type {{id:number, url:string}} */ + const val = JSON.parse(data); + this.#id = val.id; + this.#url = val.url; } + + return this.#url; } - set access(val) { - if (val && val?.id && val?.key && val?.date) { - localStorage.setItem(this.lk, JSON.stringify(val)); + Next(id = this.#id) { + let url = this.#base; + if ((id + 1) < this.#list.length) { + id = id + 1; + url = this.#list[id]; + } else { + id = -1; } + this.#id = id; + this.#url = url; + sessionStorage.setItem(this.#key, JSON.stringify({id: this.#id, url: this.#url})); } +} + +class Storage { + #key = 'shadow-api'; + #val = undefined; + #loaded = false; /** - * @returns {{id: string, key: string, date: number}} + * @type {{date:number, id:string, key:string, token:string, access: boolean} | undefined} */ get access() { - let inl = localStorage.getItem(this.lk); - inl = inl || JSON.stringify(Tunime.standart); - let val = JSON.parse(inl); - if ((new Date() - val.date) > this.life) { - val = Tunime.standart; + if (this.#val === undefined && this.#loaded === true || this.#val !== undefined) { + return this.#val; + } + + const data = sessionStorage.getItem(this.#key); + this.#val = JSON.parse(data) || undefined; + + if (this.#val !== undefined) { + if (!this.Live(this.#val)) { + this.#val = undefined; + } } - this.access = val; - return val; + + return this.#val; } - online() { - return new Promise((resolve) => { - let code = 503; - const data = this.access; - fetch(`https://${this.tunime.url}/online/`, { - method: 'POST', - body: new URLSearchParams({ id: data.id, key: data.key }) - }).then((response) => { - code = response.status; - return response.json() - }).then((val) => { - this.access = { id: val.id, key: val.key, date: Date.now() } - this.Update(); - resolve(true); - }).catch(async (reas) => { - console.log(`Error: ${this.tunime.url} Code: ${code}`); - if (code == 503) { - this.tunime.url = `${this.tunime.name}-${this.link.save}.onrender.com`; - return resolve(this.online()); - } - this.access = Tunime.standart; - await Sleep(1000); - this.Update(); - return resolve(false); - }); - }); + set access(value) { + this.#val = value; + if (value === undefined) { + return sessionStorage.removeItem(this.#key); + } + sessionStorage.setItem(this.#key, JSON.stringify(this.#val)); } - Update() { - if (this.access.id == 'shadow') { - this.online(); + /** + * @param {{date:number, id:string, key:string, token:string, access: boolean} | undefined} access + * @returns {boolean} + */ + Live(access = this.#val) { + if (typeof access === 'undefined') { + return false; + } + if ((new Date() - new Date(access.date)) > LIFETIME) { + return false; } - clearInterval(this.interval); - this.interval = setInterval(() => { - this.online(); - }, this.time - (Date.now() - this.access.date)); + return true; } +} + +class Device { + #key = 'tunime-id'; + #id = undefined; + #loaded = false; + get id() { + if (this.#id === undefined && this.#loaded === false) { + this.#id = localStorage.getItem(this.#key) || undefined; + } + return this.#id; + } + + set id(value) { + this.#id = value; + localStorage.setItem(this.#key, this.#id); + } +} - anime(id) { - let code = 503; - const data = this.access; - fetch(`https://${this.tunime.url}/online/${id}/o`, { - method: 'POST', - body: new URLSearchParams({ id: data.id, key: data.key }) - }).then((response) => { - code = response.status; - return response.json(); - }).then((val) => { - this.access = { id: val.id, key: val.key, date: Date.now() } - }).catch(async (err) => { - console.log(`Error: ${this.tunime.url} Code: ${code}`); - if (code == 503) { - this.tunime.url = `${this.tunime.name}-${this.link.save}.onrender.com`; - return this.anime(id); +class Message { + #Task = { + 'genId': async function () { + const id = await Tunime.Device(); + Tunime.device.id = id; + } + } + + constructor(list = []) { + if (list !== undefined) { + for (let i = 0; i < list.length; i++) { + const element = list[i]; + const task = this.#Task[element]; + if (task) { + task(); + } } - await Sleep(1000); - this.access = Tunime.standart; - await this.online(); - this.anime(id); - }); + } } +} - //https://anime-m3u8-qmyf.onrender.com - save url - stream(url) { - return new Promise(async (resolve) => { - let code = 503; - const access = this.access; - this.m3u8.url - fetch(`https://${this.m3u8.url}/link-anime`, { - body: new URLSearchParams({ - 'link': url, - 'id': access.id, - 'key': access.key - }), method: 'post' - }).then((response) => { - code = response.status; - return response.json(); - }).then((data) => { - this.access = { id: access.id, key: data.key, date: Date.now() }; - return resolve(data); - }).catch(async (res) => { - console.log(`Error: ${this.m3u8.url} Code: ${code}`); - if (code == 503) { - this.m3u8.url = `${this.m3u8.name}-${this.m3u8.save}.onrender.com`; - return resolve(this.stream(url)); +export const Tunime = { + storage: new Storage(), + device: new Device(), + server: new Server(), + Online: function (access = this.storage.access) { + return new Promise((resolve) => { + const cods = [666, 403] + if (!this.storage.Live(access)) { + access = undefined; + } + let body = { id: 'shadow', key: 'register' }; + let headers = undefined; + if (access !== undefined) { + body = { id: access.id, key: access.key }; + headers = { 'Authorization': `Bearer ${access.token}` } + } + if (this.device.id !== undefined) { + body['did'] = this.device.id; + } + let responseCode = 503; + Fetch('/online', { method: 'POST', headers: headers, body: body }).then((response) => { + responseCode = response.status; + if (cods.includes(responseCode)) { + Tunime.storage.access = undefined; + return resolve(false); + } + response.json().then((value) => { + let storage = this.storage.access; + if (storage === undefined) + storage = {} + this.storage.access = Object.assign(storage, value.data); + if (value?.message) { + new Message(JSON.parse(value.message)); + } + return resolve(true); + }) + }).catch(async (reason) => { + console.log(`[api] - Error: ${Tunime.server.url} Code: ${responseCode}\n${reason}`); + if (responseCode == 503) { + Tunime.server.Next(); + return resolve(this.Online(access)); } - await Sleep(3000); - return resolve(this.stream(url)); + this.storage.access = undefined; + await Sleep(1000); + return resolve(true); }); - }); - } + }) + }, - link({ q720 = undefined, q480 = undefined, q360 = undefined } = {}) { + Link: function ({ q720 = undefined, q480 = undefined, q360 = undefined } = {}) { const params = { q720, q480, q360 }; const queryParams = Object.entries(params) .filter(([key, value]) => value !== undefined) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .join('&'); - return `https://${this.m3u8.url}/m3u8?${queryParams}`; - } + return `${Tunime.server.url}/video/stream.m3u8?${queryParams}`; + }, - tsget(id) { + Source: function (kodik, access = this.storage.access) { return new Promise((resolve) => { - let code = 503; - const access = this.access; - if (access.id == Tunime.standart.id) { - return resolve({ translations: [] }); - } - fetch(`https://${this.tunime.url}/ts/${id}`, { - method: 'POST', - body: new URLSearchParams({ - 'id': access.id, - 'key': access.key - }) - }).then((res) => { - code = res.status; - return res.json(); - }).then((val) => { - return resolve(val); - }).catch(() => { - console.log(`Error: ${this.tunime.url} Code: ${code}`); - if (code == 503) { - this.tunime.url = `${this.tunime.name}-${this.link.save}.onrender.com`; - return resolve(this.tsget(id)); + if (access === undefined) + return; + const body = { key: access.key, id: access.id, link: kodik }; + const headers = { 'Authorization': `Bearer ${access.token}` }; + let responseCode = 503; + Fetch('/video/source', { method: 'POST', body, headers }).then((response) => { + responseCode = response.status; + response.json().then((value) => { + return resolve(value.data); + }); + }).catch(async (reason) => { + console.log(`[api] - Error: ${Tunime.server.url} Code: ${responseCode}\n${reason}`); + if (responseCode == 503) { + return; + // URL = `onrender.com`; + return resolve(this.Source(kodik, access)); } - return resolve({ translations: [] }); - }); + await Sleep(1000); + return resolve(this.Source(kodik, access)); + }) }); - } + }, - tsset(aid, tid) { + Anime: function (aid, access = this.storage.access) { return new Promise((resolve) => { - let code = 503; - const access = this.access; - if (access.id === Tunime.standart.id) { - return resolve({ completed: false }); - } - fetch(`https://${this.tunime.url}/ts/${aid}/${tid}`, { - method: 'POST', - body: new URLSearchParams({ - 'id': access.id, - 'key': access.key - }) - }).then((res) => { - code = res.status; - return res.json(); - }).then((val) => { - return resolve(val); - }).catch(() => { - console.log(`Error: ${this.tunime.url} Code: ${code}`); - if (code == 503) { - this.tunime.url = `${this.tunime.name}-${this.link.save}.onrender.com`; - return resolve(this.tsset(aid, tid)); + if (access === undefined) + return; + const body = { key: access.key, id: access.id }; + const headers = { 'Authorization': `Bearer ${access.token}` }; + let responseCode = 503; + Fetch(`/anime/${aid}`, { method: 'POST', body, headers }).then((response) => { + responseCode = response.status; + response.json().then((value) => { + return resolve(value); + }); + }).catch(async (reason) => { + console.log(`[api] - Error: ${Tunime.server.url} Code: ${responseCode}\n${reason}`); + if (responseCode == 503) { + return; + // URL = `onrender.com`; + // return resolve(this.Anime(aid, access)); } - return resolve({ completed: false }); - }); + await Sleep(1000); + return resolve(this.Anime(aid, access)); + }) }); - } + }, - loadpage(data, id) { - return new Promise((resolve) => { - let code = 503; - const access = this.access; - if (access.id === Tunime.standart.id) { - return resolve({ completed: false }); + Voices: function (aid, access = this.storage.access) { + if (access === undefined) + return { GET: () => { }, SET: () => { } }; + const headers = { 'Authorization': `Bearer ${access.token}` }; + return { + GET: () => { + let responseCode = 503; + Fetch(`/voices/${aid}`, { method: 'GET', headers }).then((response) => { + responseCode = response.status; + response.json().then((value) => { + return resolve(value.data); + }); + }).catch(async (reason) => { + console.log(`[api] - Error: ${Tunime.server.url} Code: ${responseCode}\n${reason}`); + if (responseCode == 503) { + return; + // URL = `onrender.com`; + // return resolve(this.Voices(aid, access).GET()); + } + await Sleep(1000); + return resolve(this.Voices(aid, access).GET()); + }); + }, + + SET: (tid) => { + let responseCode = 503; + const body = { key: access.key, id: access.id, tid }; + Fetch(`/voices/${aid}`, { method: 'POST', headers, body }).then((response) => { + responseCode = response.status; + response.json().then((value) => { + return resolve(value); + }); + }).catch(async (reason) => { + console.log(`[api] - Error: ${Tunime.server.url} Code: ${responseCode}\n${reason}`); + if (responseCode == 503) { + return; + // URL = `onrender.com`; + // return resolve(this.Voices(aid, access).SET(tid)); + } + await Sleep(1000); + return resolve(this.Voices(aid, access).SET(tid)); + }); } - fetch(`https://${this.tunime.url}/page/load/${id}`, { - method: 'POST', - body: new URLSearchParams({ - 'id': access.id, - 'key': access.key, - 'data': JSON.stringify(data) - }) - }).then((res) => { - code = res.status; - return res.json(); - }).then((val) => { - return resolve(val); - }).catch(() => { - console.log(`Error: ${this.tunime.url} Code: ${code}`); - if (code == 503) { - this.tunime.url = `${this.tunime.name}-${this.link.save}.onrender.com`; - return resolve(this.loadpage(data, id)); + } + }, + + Device: function (access = this.storage.access) { + return new Promise((resolve) => { + if (access === undefined) + return; + const body = { key: access.key, id: access.id }; + const headers = { 'Authorization': `Bearer ${access.token}` }; + let responseCode = 503; + Fetch(`/device`, { method: 'POST', body, headers }).then((response) => { + responseCode = response.status; + response.json().then((value) => { + return resolve(value.id); + }); + }).catch(async (reason) => { + console.log(`[api] - Error: ${Tunime.server.url} Code: ${responseCode}\n${reason}`); + if (responseCode == 503) { + return; + // URL = `onrender.com`; + // return resolve(this.Device(access)); } - return resolve({ completed: false }); + await Sleep(1000); + return resolve(this.Device(access)); }); }) } -} +}; + +(async () => { + const exception = ['/player.html']; + let minute = 60000; + if (exception.includes(window.location.pathname)) + return; + if (Tunime.storage.access === undefined || !Tunime.storage.Live()) { + const access = await Tunime.Online(); + if (access) { + const interval = setInterval(async () => { + const access = await Tunime.Online(); + if (!access) + clearInterval(interval); + }, (LIFETIME - minute) - (Date.now() - Tunime.storage.access.date)); + } + } else { + const interval = setInterval(async () => { + const access = await Tunime.Online(); + if (!access) + clearInterval(interval); + }, (LIFETIME - minute) - (Date.now() - Tunime.storage.access.date)); + } + + console.log(`[api] - Interval Started: Next ${((LIFETIME - minute) - (Date.now() - Tunime.storage.access?.date))} ms`); +})(); -export const ApiTunime = new Tunime(); \ No newline at end of file +function Fetch(path, { method = 'GET', body = undefined, headers = undefined } = {}) { + const request = { + method + }; + if (body != undefined) { + request['body'] = new URLSearchParams(body); + } + if (headers != undefined) { + request['headers'] = headers; + } + return fetch(`${Tunime.server.url}${path}`, request); +} \ No newline at end of file diff --git a/javascript/pages/index/mod_trailers.js b/javascript/pages/index/mod_trailers.js index 6198046..e67932f 100644 --- a/javascript/pages/index/mod_trailers.js +++ b/javascript/pages/index/mod_trailers.js @@ -4,7 +4,7 @@ import { User } from "../../modules/ShikiUSR.js"; import { Sleep } from "../../modules/funcitons.js"; import { AnimeHidePreview, AnimeLoadingPlayer, AnimePausePlayer, AnimePlayPlayer, AnimeShowPreview } from "./mod_trailers_animation.js"; -const TrailersUrl = 'https://raw.githubusercontent.com/AN0NCER/anime-data/main/data.json'; +const TrailersUrl = 'https://raw.githubusercontent.com/AN0NCER/anime-data/main/data-v2.json'; const TrilersSwiper = new Swiper('.swiper-treilers', { // Parametrs @@ -75,11 +75,9 @@ function Main() { } // return; TrailersData = await response.json(); - //Проходимся по трейлерам и добавляем значения - for (const key in TrailersData) { - const element = TrailersData[key]; - //Добавляем новые слайды - TrilersSwiper.appendSlide(GenSlide(element, key)); + for (let i = 0; i < TrailersData.length; i++) { + const element = TrailersData[i]; + TrilersSwiper.appendSlide(GenSlide(element, element.id)); } //Удаляем загрузачный слайд TrilersSwiper.removeSlide(0); @@ -163,7 +161,8 @@ function PlayPLayer() { //Анимация загрузки плеера AnimeLoadingPlayer(key); //Присваеваем значения audio и video - player.video.src = TrailersData[key].audio; + const i = TrailersData.findIndex(x => x.id === key); + player.video.src = TrailersData[i].youtube.video; //Подписываемся на события PlayerFunctions(player, key); //Прогружаем audio & video @@ -246,12 +245,13 @@ function GetPlayer(key) { function LoadYTPlayer(key) { $(`.player[data-key="${key}"]`).append(`
`); + const i = TrailersData.findIndex(x => x.id === key); //Добавляем плеер const YTPlayer = new YT.Player('ytplayer', { width: '100%', height: 'auto', - videoId: key, + videoId: YouTubeGetID(TrailersData[i].youtube.link), playerVars: { controls: 0, modestbranding: 1, @@ -300,12 +300,25 @@ function LoadYTPlayer(key) { } } +function YouTubeGetID(url){ + var ID = ''; + url = url.replace(/(>|<)/gi,'').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/); + if(url[2] !== undefined) { + ID = url[2].split(/[^0-9a-z_\-]/i); + ID = ID[0]; + } + else { + ID = url; + } + return ID; + } + function GenSlide(res, key) { const type = res.anime.kind != "movie" ? "Сериал" : "Фильм"; return `
- - + +
@@ -322,7 +335,7 @@ function GenSlide(res, key) {
-
${res.anime.name ? res.anime.name : res.anime.eng}
+
${res.anime.rus ? res.anime.rus : res.anime.eng}
${type} @@ -331,7 +344,7 @@ function GenSlide(res, key) {
-
+
diff --git a/javascript/pages/player.js b/javascript/pages/player.js index 4dfc929..6876628 100644 --- a/javascript/pages/player.js +++ b/javascript/pages/player.js @@ -5,6 +5,7 @@ import { InitAPI, ParentWindow, SendAPI } from "./player/mod_api.js"; import { FULL_PLAYER, InitSettings, QUALITY } from "./player/mod_settings.js"; import { AnimLoadPlayer } from "./player/mod_animation.js"; import { LoadM3U8, LoadM3U8Episode } from "./player/mod_stream.js"; +import { Sleep } from "../modules/funcitons.js"; /**@type {HTMLVideoElement}*/ export const Player = document.getElementById('player'); @@ -129,8 +130,9 @@ function LoadPlayer(stream_file) { } }); let f = onPlay$.subscribe({ - next: () => { + next: async () => { if (FULL_PLAYER) { + await Sleep(700); toggleFullScreen(); } f.unsubscribe(); diff --git a/javascript/pages/player/mod_stream.js b/javascript/pages/player/mod_stream.js index ba7d883..a632637 100644 --- a/javascript/pages/player/mod_stream.js +++ b/javascript/pages/player/mod_stream.js @@ -5,7 +5,7 @@ * Возвращает: LoadM3U8, LoadM3U8Episode, Skips */ -import { ApiTunime } from "../../modules/TunimeApi.js"; +import { Tunime } from "../../modules/TunimeApi.js"; import { Player } from "../player.js"; import { InitMediaSession } from "./mod_mediasession.js"; import { AUTOQUALITY, QUALITY } from "./mod_settings.js"; @@ -88,11 +88,11 @@ function GenLink(streams) { return blobUrl; } else { if (QUALITY == '720') { - return ApiTunime.link({ q720: STREAMS[720][0].src, q480: STREAMS[480][0].src, q360: STREAMS[360][0].src }); + return Tunime.Link({ q720: STREAMS[720][0].src, q480: STREAMS[480][0].src, q360: STREAMS[360][0].src }); } else if (QUALITY == '480') { - return ApiTunime.link({ q480: STREAMS[480][0].src, q360: STREAMS[360][0].src }); + return Tunime.Link({ q480: STREAMS[480][0].src, q360: STREAMS[360][0].src }); } else { - return ApiTunime.link({ q360: STREAMS[360][0].src }); + return Tunime.Link({ q360: STREAMS[360][0].src }); } } } else { @@ -104,9 +104,9 @@ function loadStreamTunime(id, e, kodik_link = undefined) { return new Promise(async (resolve) => { if (!kodik_link) kodik_link = await loadKodikLink(id, e); - let tunime_data = await ApiTunime.stream(kodik_link); + let tunime_data = await Tunime.Source(kodik_link); resolve(tunime_data); - if($PARAMETERS.player.skipmoments){ + if ($PARAMETERS.player.skipmoments) { new Skips(tunime_data.skips); } loadFirstSuccessfulImage(tunime_data.thumbinals) diff --git a/javascript/pages/watch.js b/javascript/pages/watch.js index 17aaebd..52aab7b 100644 --- a/javascript/pages/watch.js +++ b/javascript/pages/watch.js @@ -7,7 +7,7 @@ import { AutoScrollEpisodes } from "./watch/mod_scrolling.js"; import { Functional } from "./watch/mod_ui.js"; import { DifferenceInData, SaveLData, SetDifferenceData, Synch, SynchLData } from "./watch/mod_sdata.js"; import { UserRate } from "./watch/mod_urate.js"; -import { ApiTunime } from "../modules/TunimeApi.js"; +import { Tunime } from "../modules/TunimeApi.js"; import { History } from "./watch/mod_history.js"; //ID ресурса из Shikimori @@ -73,15 +73,14 @@ Main(async (e) => { //Событие отправки выбора озвучки первого просмотра Player().events.onplayed((e) => { const data = DifferenceInData(); - console.log(data); if (!data[0] && !data[1]) return; if (data[0] && !data[1]) { - ApiTunime.tsset($ID, data[0].kodik_dub); + Tunime.Voices($ID).SET(data[0].kodik_dub); SetDifferenceData(data[0]); } else if (data[0] && data[1]) { if (data[0].kodik_dub != data[1].kodik_dub) { - ApiTunime.tsset($ID, data[0].kodik_dub); + Tunime.Voices($ID).SET(data[0].kodik_dub); SetDifferenceData(data[0]); } } @@ -164,7 +163,7 @@ Main(async (e) => { inline: "nearest", }); } - ApiTunime.anime($ID); + Tunime.Anime($ID); }); History().shikiData = e; History().custom.init(); diff --git a/javascript/pages/watch/mod_download.js b/javascript/pages/watch/mod_download.js index b31a645..db094a5 100644 --- a/javascript/pages/watch/mod_download.js +++ b/javascript/pages/watch/mod_download.js @@ -1,4 +1,4 @@ -import { ApiTunime } from "../../modules/TunimeApi.js"; +import { Tunime } from "../../modules/TunimeApi.js"; import { WindowManagement } from "../../modules/Windows.js"; import { ScrollElementWithMouse } from "../../modules/funcitons.js"; import { Player } from "./mod_player.js"; @@ -143,7 +143,7 @@ async function GetM3U8Links() { if (!link.includes("http")) { link = `https:${link}`; } - const data = await ApiTunime.stream(link); + const data = await Tunime.Source(link); if (data) { _localDownload(data); } diff --git a/sw.js b/sw.js index d57197c..8353a6d 100644 --- a/sw.js +++ b/sw.js @@ -1,4 +1,4 @@ -var version = '160'; +var version = '161'; var cacheName = 'pwa-tunime-v' + version; var appShellFilesToCache = [ // Директория: /images/icons