diff --git a/bun.lockb b/bun.lockb index a9fb2d13..7fdb3b5c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index d227eeea..320f3dc2 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "eruda2": "0.0.2-b8", "fb-comments-web": "^0.0.13", "group-array": "^1.0.0", - "hls.js": "1.5.10", + "hls.js": "^1.5.13", "htmlparser2": "^8.0.1", "idb-keyval": "^6.2.1", "iso-639-1": "^2.1.15", @@ -72,6 +72,7 @@ "jwplayer8": "0.0.1-b1", "lz-string": "^1.4.4", "message-port-api": "^0.0.5", + "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "pinia": "^2.0.29", "pinia-plugin-persistedstate": "^3.0.2", @@ -87,6 +88,7 @@ "supabase": "^1.183.5", "swiper": "^8.4.6", "ts-md5": "^1.3.1", + "ts-retry": "^4.2.5", "ua-parser-js": "^1.0.35", "uuid": "^9.0.0", "vn-remove-accents": "^1.0.0", diff --git a/src-capacitor/android/app/src/main/assets/capacitor.config.json b/src-capacitor/android/app/src/main/assets/capacitor.config.json index 0e31cc04..0be22654 100644 --- a/src-capacitor/android/app/src/main/assets/capacitor.config.json +++ b/src-capacitor/android/app/src/main/assets/capacitor.config.json @@ -11,5 +11,7 @@ }, "bundledWebRuntime": false, "webDir": "www", - "server": {} + "server": { + "url": "http://10.0.5.2:9500" + } } diff --git a/src-capacitor/android/capacitor.settings.gradle b/src-capacitor/android/capacitor.settings.gradle index b40227ba..c827bbea 100644 --- a/src-capacitor/android/capacitor.settings.gradle +++ b/src-capacitor/android/capacitor.settings.gradle @@ -1,33 +1,33 @@ // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN include ':capacitor-android' -project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@5.7.6_@capacitor+core@5.7.6/node_modules/@capacitor/android/capacitor') +project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') include ':capacitor-community-firebase-analytics' -project(':capacitor-community-firebase-analytics').projectDir = new File('../node_modules/.pnpm/@capacitor-community+firebase-analytics@5.0.1_@capacitor+core@5.7.6/node_modules/@capacitor-community/firebase-analytics/android') +project(':capacitor-community-firebase-analytics').projectDir = new File('../node_modules/@capacitor-community/firebase-analytics/android') include ':capacitor-app' -project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@5.0.8_@capacitor+core@5.7.6/node_modules/@capacitor/app/android') +project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') include ':capacitor-browser' -project(':capacitor-browser').projectDir = new File('../node_modules/.pnpm/@capacitor+browser@5.2.1_@capacitor+core@5.7.6/node_modules/@capacitor/browser/android') +project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/browser/android') include ':capacitor-device' -project(':capacitor-device').projectDir = new File('../node_modules/.pnpm/@capacitor+device@5.0.8_@capacitor+core@5.7.6/node_modules/@capacitor/device/android') +project(':capacitor-device').projectDir = new File('../node_modules/@capacitor/device/android') include ':capacitor-filesystem' -project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@5.2.2_@capacitor+core@5.7.6/node_modules/@capacitor/filesystem/android') +project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') include ':capacitor-haptics' -project(':capacitor-haptics').projectDir = new File('../node_modules/.pnpm/@capacitor+haptics@5.0.8_@capacitor+core@5.7.6/node_modules/@capacitor/haptics/android') +project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android') include ':capacitor-preferences' -project(':capacitor-preferences').projectDir = new File('../node_modules/.pnpm/@capacitor+preferences@5.0.8_@capacitor+core@5.7.6/node_modules/@capacitor/preferences/android') +project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android') include ':capacitor-share' -project(':capacitor-share').projectDir = new File('../node_modules/.pnpm/@capacitor+share@5.0.8_@capacitor+core@5.7.6/node_modules/@capacitor/share/android') +project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android') include ':capacitor-status-bar' -project(':capacitor-status-bar').projectDir = new File('../node_modules/.pnpm/@capacitor+status-bar@5.0.8_@capacitor+core@5.7.6/node_modules/@capacitor/status-bar/android') +project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') include ':hugotomazi-capacitor-navigation-bar' -project(':hugotomazi-capacitor-navigation-bar').projectDir = new File('../node_modules/.pnpm/@hugotomazi+capacitor-navigation-bar@3.0.0_@capacitor+core@5.7.6/node_modules/@hugotomazi/capacitor-navigation-bar/android') +project(':hugotomazi-capacitor-navigation-bar').projectDir = new File('../node_modules/@hugotomazi/capacitor-navigation-bar/android') diff --git a/src-capacitor/bun.lockb b/src-capacitor/bun.lockb old mode 100644 new mode 100755 index 87d8e09b..363d1606 Binary files a/src-capacitor/bun.lockb and b/src-capacitor/bun.lockb differ diff --git a/src-capacitor/capacitor.config.json b/src-capacitor/capacitor.config.json index 418fca7e..a53c2f68 100644 --- a/src-capacitor/capacitor.config.json +++ b/src-capacitor/capacitor.config.json @@ -11,5 +11,7 @@ }, "bundledWebRuntime": false, "webDir": "www", - "server": {} + "server": { + "url": "http://10.0.5.2:9500" + } } \ No newline at end of file diff --git a/src-capacitor/package.json b/src-capacitor/package.json index defc56ba..f7b59bf2 100644 --- a/src-capacitor/package.json +++ b/src-capacitor/package.json @@ -1,6 +1,6 @@ { "name": "git.shin.animevsub", - "version": "1.8.39", + "version": "1.8.42", "description": "Watch Anime without advertising, free, private.", "author": "Tachibana Shin <45375496+tachibana-shin@users.noreply.github.com>", "private": true, @@ -22,7 +22,6 @@ "@capacitor/share": "^5.0.8", "@capacitor/status-bar": "^5.0.8", "@hugotomazi/capacitor-navigation-bar": "^3.0.0", - "@jcesarmobile/ssl-skip": "^0.2.0", "cordova-plugin-screen-orientation": "^3.0.4" } } \ No newline at end of file diff --git a/src/apis/runs/ajax/player-link.ts b/src/apis/runs/ajax/player-link.ts index b79301e8..73ddd2c8 100644 --- a/src/apis/runs/ajax/player-link.ts +++ b/src/apis/runs/ajax/player-link.ts @@ -45,7 +45,7 @@ export function PlayerLink(config: { link, backuplinks: "1", }).then(async ({ data }) => { - + if (!data) throw new Error("unknown_error") type Writeable = { -readonly [P in keyof T]: T[P] extends object ? Writeable : T[P] @@ -59,7 +59,7 @@ export function PlayerLink(config: { // eslint-disable-next-line @typescript-eslint/no-explicit-any, promise/no-nesting ;(self as unknown as any).hn ??= await App.getInfo().then( (info) => info.id - ) + ).catch(() => 'git.shin.animevsub') await init() try { diff --git a/src/components/BrtPlayer.vue b/src/components/BrtPlayer.vue index 5f23a3f0..c541ae7d 100644 --- a/src/components/BrtPlayer.vue +++ b/src/components/BrtPlayer.vue @@ -104,7 +104,33 @@ -
+
+ + + + + {{ $t("tang-toc-mang") }} + + { video.value.currentTime = currentTime artCurrentTime.value = currentTime } - + let progressRestored: false | string = false watch( [() => props.currentChap, () => props.currentSeason, () => authStore.uid], @@ -1138,7 +1167,7 @@ const setArtPlaybackRate = (value: number) => { // value control other const artControlShow = ref(true) - + let activeTime = Date.now() const setArtControlShow = (show: boolean) => { artControlShow.value = show @@ -1282,10 +1311,8 @@ function throttle Promise>( ): T & { cancel: () => void } { - let wait = false - let timeout: NodeJS.Timeout | number | undefined // eslint-disable-next-line functional/functional-parameters, @typescript-eslint/no-explicit-any const cb = function (...args: any[]) { @@ -1337,9 +1364,9 @@ const saveCurTimeToPer = throttle( try { // get data from uid and process because processingSaveCurTimeIn === uid then load all of time current - + let cur = artCurrentTime.value - + let dur = artDuration.value if (!dur || cur <= 5) { @@ -1388,8 +1415,8 @@ const saveCurTimeToPer = throttle( { cur, dur, - name: nameCurrentChap - }, + name: nameCurrentChap, + } ) .catch((err) => console.warn("save viewing progress failed: ", err)), @@ -1466,7 +1493,6 @@ function onVideoEnded() { } } - let artPlayingOfBeforeDocumentHide: boolean watch(documentVisibility, (visibility) => { console.log("document %s", visibility) @@ -1479,9 +1505,8 @@ watch(documentVisibility, (visibility) => { }) { - let resume: (() => void) | null = null - + let pause: (() => void) | null = null const resumeDelay = debounce(() => resume?.(), 1_000) onBeforeUnmount(() => pause?.()) @@ -1561,7 +1586,6 @@ function runRemount() { }).onOk(remount) } - let currentHls: Hls onBeforeUnmount(() => currentHls?.destroy()) function remount(resetCurrentTime?: boolean, noDestroy = false) { @@ -1607,115 +1631,88 @@ function remount(resetCurrentTime?: boolean, noDestroy = false) { Hls.isSupported() ) { const offEnds = isNative ? "" : "_extra" - const hls = new Hls({ - debug: import.meta.env.isDev, - workerPath: workerHls, - progressive: true, - fragLoadingRetryDelay: 10000, - fetchSetup(context, initParams) { - // set header because this version always cors not fix by extension like desktop-web - if (isNative) initParams.headers.set("x-referer", C_URL) - else context.url += `#animevsub-vsub${offEnds}_uachrome` - - return new Request(context.url, initParams) + let セグメントs: string[] | null = null + const セグメント解決済み = new Map() + const resolvingTask = new Set() + + const hls = new HlsPatched( + { + debug: import.meta.env.DEV, + workerPath: workerHls, + progressive: true, + fragLoadingRetryDelay: 10000, + fetchSetup(context, initParams) { + // set header because this version always cors not fix by extension like desktop-web + if (isNative) initParams.headers.set("x-referer", C_URL) + else context.url += `#animevsub-vsub${offEnds}` + + return new Request(context.url, initParams) + }, }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pLoader: class CustomLoader extends (Hls.DefaultConfig.loader as any) { - loadInternal(): void { - const { config, context } = this - if (!config) { - return + settingsStore.player.preResolve !== 0 && + Number.MAX_SAFE_INTEGER !== settingsStore.player.preResolve + ? (request) => { + /// pre setup + // transforming + if (!セグメントs) + void getSegments(file).then((arr) => (セグメントs = arr)) + if (セグメントs) { + // セグメントs ready + // preload if in セグメントs + const realUrl = request.url.split("#")[0] + const インデントセグメント = セグメントs.indexOf(realUrl) + const 解決済み = セグメント解決済み.get(realUrl) + if ( + インデントセグメント > -1 && + (!解決済み || + settingsStore.player.preResolve - 解決済み[1] < + settingsStore.player.checkEndPreList) && + !findInRangeSet( + resolvingTask, + インデントセグメント, + settingsStore.player.preResolve + ) + ) { + console.log( + "Starting pre resolve segment", + インデントセグメント, + resolvingTask + ) + resolvingTask.add(インデントセグメント) + // あとで5セメント + void retryAsync( + () => + resolveMasterManifestWorker( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + セグメントs!, + セグメント解決済み, + インデントセグメント, + settingsStore.player.preResolve + + settingsStore.player.checkEndPreList + ), + { maxTry: 10, delay: 3_000 } + ).catch(() => resolvingTask.add(インデントセグメント)) + // load balance 20 + // check fn セグメントs in 20。workerせとじゃない。メモリー? + } + if (import.meta.env.DEV && 解決済み) + console.info("[Segment]: using url resolved") + if (解決済み) return fetchJava(解決済み[0], request) + } + return fetchJava(request.url, request) } - - const { stats } = this - stats.loading.first = 0 - stats.loaded = 0 - - const controller = new AbortController() - const xhr = (this.loader = { - readyState: 0, - status: 0, - responseType: context.responseType, - abort() { - controller.abort() - }, - onreadystatechange: <(() => void) | null>null, - onprogress: < - ((eventt: { loaded: number; total: number }) => void) | null - >null, - response: null, - responseText: null, - }) - const headers = new Headers() - if (this.context.headers) - for (const [key, val] of Object.entries(this.context.headers)) - headers.set(key, val as string) - const { maxTimeToFirstByteMs, maxLoadTimeMs } = config.loadPolicy - - if (context.rangeEnd) { - headers.set( - "Range", - "bytes=" + context.rangeStart + "-" + (context.rangeEnd - 1) - ) + : Number.MAX_SAFE_INTEGER === settingsStore.player.preResolve + ? (request) => { + const realUrl = request.url.split("#")[0] + const 解決済み = セグメント解決済み.get(realUrl) + if (import.meta.env.DEV && 解決済み) + console.info("[Segment]: using url resolved") + if (解決済み) return fetchJava(解決済み[0], request) + return fetchJava(request.url, request) } - - xhr.onreadystatechange = this.readystatechange.bind(this) - xhr.onprogress = this.loadprogress.bind(this) - self.clearTimeout(this.requestTimeout) - config.timeout = - maxTimeToFirstByteMs && Number.isFinite(maxTimeToFirstByteMs) - ? maxTimeToFirstByteMs - : maxLoadTimeMs - this.requestTimeout = self.setTimeout( - this.loadtimeout.bind(this), - config.timeout - ) - - // set header because this version always cors not fix by extension liek desktop-web - headers.set("referer", C_URL) - - fetchJava( - context.url + (!isNative ? "#animevsub-vsub_uachrome" : ""), - { - headers, - signal: controller.signal, - } - ) - .then(async (res) => { - - let byteLength: number - if (context.responseType !== "text") { - xhr.response = await res.arrayBuffer() - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - byteLength = xhr.response!.byteLength - } else { - xhr.responseText = await res.text() - byteLength = xhr.responseText.length - } - - xhr.readyState = 4 - xhr.status = 200 - xhr.responseType = context.responseType - - xhr.onprogress?.({ - loaded: byteLength, - total: byteLength, - }) - // eslint-disable-next-line promise/always-return - xhr.onreadystatechange?.() - }) - .catch((e) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.callbacks!.onError( - { code: xhr.status, text: e.message }, - context, - xhr - ) - }) - } - } as unknown as PlaylistLoaderConstructor, - }) - if (!offEnds) patcher(hls) + : (request) => fetchJava(request.url, request) + ) + // if (!offEnds) patcher(hls) currentHls = hls // customLoader(hls.config) hls.loadSource(file) @@ -1724,10 +1721,24 @@ function remount(resetCurrentTime?: boolean, noDestroy = false) { hls.on(Hls.Events.MANIFEST_PARSED, () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (playing) video.value!.play() + + // !NOTIFY resolve redirect urls + if (Number.MAX_SAFE_INTEGER === settingsStore.player.preResolve) + void retryAsync( + async () => + resolveMasterManifestWorker( + await getSegments(file), + セグメント解決済み + ), + { + maxTry: 10, + delay: 3_000, + } + ) }) - + let needSwapCodec = false - + let timeoutUnneedSwapCodec: NodeJS.Timeout | number | null = null hls.on(Hls.Events.ERROR, (event, data) => { if (data.fatal) { @@ -1836,7 +1847,6 @@ const watcherVideoTagReady = watch(video, (video) => { // eslint-disable-next-line promise/catch-or-return Promise.resolve().then(watcherVideoTagReady) // fix this not ready value - let currentEpStream: null | string = null watch( () => currentStream.value?.file, @@ -1882,14 +1892,14 @@ function onIndicatorMove( event: TouchEvent | MouseEvent, innerEl?: HTMLDivElement ): void - + function onIndicatorMove( event: TouchEvent | MouseEvent, innerEl: HTMLDivElement, offsetX: number, curTimeStart: number ): void - + function onIndicatorMove( event: TouchEvent | MouseEvent, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -1951,13 +1961,13 @@ function onIndicatorEnd() { } // ==== addons swipe backdrop ==== - + let timeoutHoldBD: number | NodeJS.Timeout | null = null const holdedBD = ref(false) - + let xStart: number | null = null - + let curTimeStart: number | null = null function onBDTouchStart(event: TouchEvent) { holdedBD.value = false @@ -2025,13 +2035,13 @@ function skipForward() { } const doubleClicking = ref<"left" | "right" | false>(false) - + let timeoutResetDoubleClicking: number | NodeJS.Timeout | null = null - + let lastTimeClick: number - + let lastPositionClickIsLeft: boolean | null = null - + let timeoutDbClick: number | NodeJS.Timeout | null = null const countSkip = ref(0) function onClickSkip(event: MouseEvent, orFalse: boolean) { @@ -2243,6 +2253,56 @@ watch(skiping, (skiping) => { skipOpEnd() }) + +const optionsPreResolve = computed(() => [ + { + label: t("tat"), + value: 0, + color: "secondary", + keepColor: true, + checkedIcon: "task_alt", + uncheckedIcon: "panorama_fish_eye", + }, + ...[20, 30, 40, 50, 60, 70, 80, 100].map((val, i) => ({ + label: t("val-yeu-cau", [val]), + value: val, + keepColor: true, + checkedIcon: "task_alt", + uncheckedIcon: "panorama_fish_eye", + color: `light-green-${4 + i}`, + })), + { + label: t("nong"), + value: Number.MAX_SAFE_INTEGER, + color: "red", + keepColor: true, + checkedIcon: "task_alt", + uncheckedIcon: "panorama_fish_eye", + }, +]) +function openPopupFlashNetwork() { + $q.dialog({ + title: t("giai-quyet-truoc-yeu-cau-mang"), + message: t("msg-pre-resolve"), + options: { + type: "radio", + model: settingsStore.player.preResolve as unknown as string, + // inline: true + items: optionsPreResolve.value, + }, + cancel: { + label: t("huy"), + noCaps: true, + color: "grey", + text: true, + flat: true, + rounded: true, + }, + ok: { color: "green", text: true, flat: true, rounded: true }, + }).onOk((data) => { + settingsStore.player.preResolve = data + }) +}