From 2b12bd690e9bf91ca0ebf90eea4aa96df0ad0112 Mon Sep 17 00:00:00 2001 From: ThaUnknown <6506529+ThaUnknown@users.noreply.github.com> Date: Mon, 16 Jan 2023 17:15:06 +0100 Subject: [PATCH] feat: aniskip API --- package.json | 2 +- src/renderer/src/lib/Player/Player.svelte | 7 +++ src/renderer/src/modules/anilist.js | 1 + src/renderer/src/modules/anime.js | 52 +++++++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 05cf9ac..9652860 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Miru", - "version": "3.6.0", + "version": "3.7.0", "author": "ThaUnknown_ ", "description": "Stream anime torrents, real-time with no waiting for downloads.", "main": "src/index.js", diff --git a/src/renderer/src/lib/Player/Player.svelte b/src/renderer/src/lib/Player/Player.svelte index 1a4e50d..c548bcb 100644 --- a/src/renderer/src/lib/Player/Player.svelte +++ b/src/renderer/src/lib/Player/Player.svelte @@ -8,6 +8,7 @@ import Subtitles from '@/modules/subtitles.js' import { toTS, videoRx, fastPrettyBytes, throttle } from '@/modules/util.js' import { addToast } from '../Toasts.svelte' + import { getChaptersAniSkip } from '@/modules/anime.js' import { w2gEmitter } from '../WatchTogether/WatchTogether.svelte' import Keybinds, { loadWithDefaults, condition } from 'svelte-keybinds' @@ -636,6 +637,11 @@ client.on('chapters', ({ detail }) => { chapters = detail }) + async function findChapters () { + if (!chapters.length) { + chapters = await getChaptersAniSkip(current, safeduration) + } + } let hoverChapter = null let currentSkippable = null @@ -911,6 +917,7 @@ on:loadedmetadata={hideBuffering} on:ended={tryPlayNext} on:loadedmetadata={initThumbnails} + on:loadedmetadata={findChapters} on:loadedmetadata={autoPlay} on:loadedmetadata={checkAudio} on:leavepictureinpicture={() => (pip = false)} /> diff --git a/src/renderer/src/modules/anilist.js b/src/renderer/src/modules/anilist.js index a3a104c..a577a5e 100644 --- a/src/renderer/src/modules/anilist.js +++ b/src/renderer/src/modules/anilist.js @@ -190,6 +190,7 @@ export async function alRequest (opts) { const userId = (await alID)?.data?.Viewer.id const queryObjects = /* js */` id, +idMal, title { romaji, english, diff --git a/src/renderer/src/modules/anime.js b/src/renderer/src/modules/anime.js index 2d5a945..84e154e 100644 --- a/src/renderer/src/modules/anime.js +++ b/src/renderer/src/modules/anime.js @@ -71,6 +71,58 @@ export async function traceAnime (image) { // WAIT lookup logic } } +const parts = ['A', 'B', 'C', 'D', 'E'] + +export async function getChaptersAniSkip (file, duration) { + const res = await fetch(`https://api.aniskip.com/v2/skip-times/${file.media.media.idMal}/${file.media.episode}/?episodeLength=${duration}&types=op&types=ed&types=recap`) + const { found, results } = await res.json() + if (!found) return [] + const chapters = results.map(result => { + return { + start: result.interval.startTime * 1000, + end: result.interval.endTime * 1000, + text: result.skipType.toUpperCase() + } + }) + const ed = chapters.find(({ text }) => text === 'ED') + const recap = chapters.find(({ text }) => text === 'RECAP') + if (recap) recap.text = 'Recap' + + chapters.sort((a, b) => a - b) + + if ((chapters[0].start | 0) !== 0) { + chapters.unshift({ start: 0, end: chapters[0].start, text: 'Intro' }) + } + let part = 0 + if (ed) { + if ((ed.end | 0) + 5000 - duration * 1000 < 0) { + chapters.push({ start: ed.end, end: duration * 1000, text: 'Preview' }) + } + } else if ((chapters[chapters.length - 1].end | 0) + 5000 - duration * 1000 < 0) { + chapters.push({ + start: chapters[chapters.length - 1].end, + end: duration * 1000, + text: 'Part ' + parts[part++] + }) + } + + for (let i = 0, len = chapters.length - 2; i <= len; ++i) { + const current = chapters[i] + const next = chapters[i + 1] + if ((current.end | 0) !== (next.start | 0)) { + chapters.push({ + start: current.end, + end: next.start, + text: 'Part ' + parts[part++] + }) + } + } + + chapters.sort((a, b) => a - b) + + return chapters +} + export function getMediaMaxEp (media, playable) { if (playable) { return media.nextAiringEpisode?.episode - 1 || media.airingSchedule?.nodes?.[0]?.episode - 1 || media.episodes