mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-20 04:12:06 +00:00
feat: improved batch support and handling
This commit is contained in:
parent
000f39ced0
commit
057385aed6
12 changed files with 219 additions and 151 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Miru",
|
||||
"version": "2.10.5",
|
||||
"version": "2.11.0",
|
||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||
"main": "src/index.js",
|
||||
"homepage": "https://github.com/ThaUnknown/miru#readme",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export let length = 5
|
|||
<div class='row h-full'>
|
||||
<div class='col-4 skeloader' />
|
||||
<div class='col-8 bg-very-dark px-15 py-10'>
|
||||
<h5 class='m-0 text-capitalize font-weight-bold pb-10'>{[card.parseObject.anime_title, card.parseObject.episode_number].filter(s => s).join(' - ')}</h5>
|
||||
<h5 class='m-0 text-capitalize font-weight-bold pb-10'>{[card.parseObject.anime_title, card.episode].filter(s => s).join(' - ')}</h5>
|
||||
<p class='skeloader w-150 h-10 rounded bg-dark' />
|
||||
<p class='skeloader w-150 h-10 rounded bg-dark' />
|
||||
</div>
|
||||
|
|
@ -49,7 +49,7 @@ export let length = 5
|
|||
{#if card.failed}
|
||||
<span class='badge badge-secondary'>Uncertain</span>
|
||||
{/if}
|
||||
{[card.media.title.userPreferred, card.episodeNumber].filter(s => s).join(' - ')}
|
||||
{[card.media.title.userPreferred, card.episode].filter(s => s).join(' - ')}
|
||||
</h5>
|
||||
{#if card.schedule && card.media.nextAiringEpisode}
|
||||
<span class='text-muted font-weight-bold'>
|
||||
|
|
@ -76,7 +76,7 @@ export let length = 5
|
|||
</p>
|
||||
</div>
|
||||
<div class='overflow-y-auto px-15 pb-5 bg-very-dark card-desc pre-wrap'>
|
||||
{card.media.description.replace(/<[^>]*>/g, '')}
|
||||
{card.media.description?.replace(/<[^>]*>/g, '') || ''}
|
||||
</div>
|
||||
<div class='px-15 pb-10 pt-5 genres'>
|
||||
{#each card.media.genres as genre}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ async function releasesCards (page, limit, force, val) {
|
|||
const index = (page - 1) * limit
|
||||
const items = [...doc.querySelectorAll('item')].slice(index, index + limit)
|
||||
hasNext = items.length === limit
|
||||
const media = await resolveFileMedia({ fileName: items.map(item => item.querySelector('title').textContent), isRelease: true })
|
||||
const media = await resolveFileMedia(items.map(item => item.querySelector('title').textContent))
|
||||
media.forEach((mediaInformation, index) => {
|
||||
mediaInformation.onclick = () => {
|
||||
add(items[index].querySelector('link').textContent)
|
||||
|
|
|
|||
168
src/renderer/src/lib/Player/MediaHandler.svelte
Normal file
168
src/renderer/src/lib/Player/MediaHandler.svelte
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
<script context='module'>
|
||||
import { writable, get } from 'svelte/store'
|
||||
import { resolveFileMedia } from '@/modules/anime.js'
|
||||
import { videoRx } from '@/modules/util.js'
|
||||
import { title } from '../Menubar.svelte'
|
||||
|
||||
const episodeRx = /Episode (\d+) - (.*)/
|
||||
|
||||
export const media = writable(null)
|
||||
|
||||
const nowPlaying = writable({})
|
||||
|
||||
export const files = writable([])
|
||||
|
||||
const processed = writable([])
|
||||
|
||||
const noop = () => {}
|
||||
|
||||
let playFile = noop
|
||||
|
||||
media.subscribe((media) => {
|
||||
handleMedia(media || {})
|
||||
return noop
|
||||
})
|
||||
|
||||
function handleCurrent ({ detail }) {
|
||||
media.set(detail.media)
|
||||
}
|
||||
|
||||
export function findInCurrent (obj) {
|
||||
const oldNowPlaying = get(nowPlaying)
|
||||
const fileList = get(files)
|
||||
|
||||
const targetFile = fileList.find(file => file.media.media.id === obj.media.id && file.media.episode === obj.episode)
|
||||
if (!targetFile) return false
|
||||
if (oldNowPlaying.media.id !== obj.media.id) {
|
||||
// mediachange, filelist change
|
||||
media.set({ media: obj.media, episode: obj.episode })
|
||||
handleFiles(fileList)
|
||||
} else {
|
||||
playFile(targetFile)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function handleMedia ({ media, episode, parseObject }) {
|
||||
if (media) {
|
||||
const ep = Number(episode || parseObject.episode_number) || null
|
||||
const streamingEpisode = media?.streamingEpisodes.find(episode => {
|
||||
const match = episodeRx.exec(episode.title)
|
||||
return match && Number(match[1]) === ep
|
||||
})
|
||||
|
||||
const np = {
|
||||
media,
|
||||
title: media?.title.userPreferred || parseObject.anime_title,
|
||||
episode: ep,
|
||||
episodeTitle: streamingEpisode && episodeRx.exec(streamingEpisode.title)[2],
|
||||
thumbnail: streamingEpisode?.thumbnail || media?.coverImage.extraLarge
|
||||
}
|
||||
setDiscordRPC(np)
|
||||
setMediaSession(np)
|
||||
nowPlaying.set(np)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFiles (files) {
|
||||
let videoFiles = []
|
||||
const otherFiles = []
|
||||
for (const file of files) {
|
||||
if (videoRx.test(file.name)) {
|
||||
videoFiles.push(file)
|
||||
} else {
|
||||
otherFiles.push(file)
|
||||
}
|
||||
}
|
||||
const resolved = await resolveFileMedia(videoFiles.map(file => file.name))
|
||||
|
||||
videoFiles.map(file => {
|
||||
file.media = resolved.find(({ parseObject }) => file.name.includes(parseObject.file_name))
|
||||
return file
|
||||
})
|
||||
|
||||
const nowPlaying = get(media)
|
||||
|
||||
if (nowPlaying?.media) videoFiles = videoFiles.filter(file => file.media.media.id === nowPlaying.media.id)
|
||||
|
||||
videoFiles.sort((a, b) => a.media.episode - b.media.episode)
|
||||
|
||||
if (!videoFiles.length) {
|
||||
processed.set(files)
|
||||
} else {
|
||||
processed.set([...videoFiles, ...otherFiles])
|
||||
|
||||
if (nowPlaying?.episode && nowPlaying.episode !== 1) {
|
||||
const file = videoFiles.find(({ media }) => media.episode === nowPlaying.episode)
|
||||
playFile(file || 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files.subscribe((files = []) => {
|
||||
handleFiles(files)
|
||||
return noop
|
||||
})
|
||||
|
||||
function setMediaSession (nowPlaying) {
|
||||
const name = [nowPlaying.title, nowPlaying.episode, nowPlaying.episodeTitle, 'Miru'].filter(i => i).join(' - ')
|
||||
|
||||
title.set(name)
|
||||
const metadata =
|
||||
nowPlaying.thumbnail
|
||||
? new MediaMetadata({
|
||||
title: name,
|
||||
artwork: [
|
||||
{
|
||||
src: nowPlaying.thumbnail,
|
||||
sizes: '256x256',
|
||||
type: 'image/jpg'
|
||||
}
|
||||
]
|
||||
})
|
||||
: new MediaMetadata({ title: name })
|
||||
navigator.mediaSession.metadata = metadata
|
||||
}
|
||||
|
||||
function setDiscordRPC (nowPlaying) {
|
||||
window.IPC.emit('discord', {
|
||||
activity: {
|
||||
details: [nowPlaying.title, nowPlaying.episodeTitle].filter(i => i).join(' - '),
|
||||
state: 'Watching Episode ' + ((!nowPlaying.media?.episodes && nowPlaying.episode) || ''),
|
||||
timestamps: {
|
||||
start: Date.now()
|
||||
},
|
||||
party: {
|
||||
size: (nowPlaying.episode && nowPlaying.media?.episodes && [nowPlaying.episode, nowPlaying.media.episodes]) || undefined
|
||||
},
|
||||
assets: {
|
||||
large_text: nowPlaying.title,
|
||||
large_image: nowPlaying.thumbnail,
|
||||
small_image: 'logo',
|
||||
small_text: 'https://github.com/ThaUnknown/miru'
|
||||
},
|
||||
instance: true,
|
||||
type: 3,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Download app',
|
||||
url: 'https://github.com/ThaUnknown/miru/releases/latest'
|
||||
},
|
||||
{
|
||||
label: 'Watch on Miru',
|
||||
url: `miru://anime/${nowPlaying.media?.id}`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import Player from './Player.svelte'
|
||||
|
||||
export let miniplayer = false
|
||||
export let page = 'home'
|
||||
</script>
|
||||
|
||||
<Player files={$processed} {miniplayer} media={$nowPlaying} bind:playFile bind:page on:current={handleCurrent}/>
|
||||
|
|
@ -1,77 +1,9 @@
|
|||
<script context='module'>
|
||||
<script>
|
||||
import { set } from '../Settings.svelte'
|
||||
import { playAnime } from '../RSSView.svelte'
|
||||
import { title } from '../Menubar.svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import { client } from '@/modules/torrent.js'
|
||||
export let media = null
|
||||
let fileMedia = null
|
||||
let hadImage = false
|
||||
export function updateMedia (fileMed) {
|
||||
if (!fileMedia) {
|
||||
setDiscordRPC(fileMed)
|
||||
}
|
||||
fileMedia = fileMed
|
||||
media = fileMedia.media
|
||||
const name = [fileMedia.mediaTitle, fileMedia.episodeNumber, fileMedia.episodeTitle, 'Miru'].filter(i => i).join(' - ')
|
||||
title.set(name)
|
||||
|
||||
fileMedia.episodeThumbnail = !!fileMedia.episodeThumbnail
|
||||
const metadata =
|
||||
fileMedia.episodeThumbnail || fileMedia.mediaCover
|
||||
? new MediaMetadata({
|
||||
title: name,
|
||||
artwork: [
|
||||
{
|
||||
src: fileMedia.episodeThumbnail || fileMedia.mediaCover,
|
||||
sizes: '256x256',
|
||||
type: 'image/jpg'
|
||||
}
|
||||
]
|
||||
})
|
||||
: new MediaMetadata({ title: name })
|
||||
if (fileMedia.parseObject?.release_group) metadata.artist = fileMedia.parseObject.release_group
|
||||
navigator.mediaSession.metadata = metadata
|
||||
}
|
||||
function setDiscordRPC (fileMedia) {
|
||||
if (fileMedia) {
|
||||
window.IPC.emit('discord', {
|
||||
activity: {
|
||||
details: fileMedia.media?.title.userPreferred || fileMedia.parseObject.anime_title,
|
||||
state: 'Watching Episode ' + ((!fileMedia.media?.episodes && fileMedia.episodeNumber) || ''),
|
||||
timestamps: {
|
||||
start: Date.now()
|
||||
},
|
||||
party: {
|
||||
size: (fileMedia.episodeNumber && fileMedia.media?.episodes && [fileMedia.episodeNumber, fileMedia.media.episodes]) || undefined
|
||||
},
|
||||
assets: {
|
||||
large_text: fileMedia.media?.title.userPreferred,
|
||||
large_image: fileMedia.media?.coverImage.extraLarge,
|
||||
small_image: 'logo',
|
||||
small_text: 'https://github.com/ThaUnknown/miru'
|
||||
},
|
||||
instance: true,
|
||||
type: 3,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Download app',
|
||||
url: 'https://github.com/ThaUnknown/miru/releases/latest'
|
||||
},
|
||||
{
|
||||
label: 'Watch on Miru',
|
||||
url: `miru://anime/${fileMedia.media?.id}`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { onMount, createEventDispatcher } from 'svelte'
|
||||
import { alEntry } from '@/modules/anilist.js'
|
||||
import { resolveFileMedia } from '@/modules/anime.js'
|
||||
// import Peer from '@/modules/Peer.js'
|
||||
import Subtitles from '@/modules/subtitles.js'
|
||||
import { toTS, videoRx, fastPrettyBytes } from '@/modules/util.js'
|
||||
|
|
@ -80,31 +12,29 @@ import { addToast } from '../Toasts.svelte'
|
|||
import { w2gEmitter } from '../WatchTogether/WatchTogether.svelte'
|
||||
import Keybinds, { loadWithDefaults, condition } from 'svelte-keybinds'
|
||||
|
||||
const emit = createEventDispatcher()
|
||||
|
||||
w2gEmitter.on('playerupdate', ({ detail }) => {
|
||||
currentTime = detail.time
|
||||
paused = detail.paused
|
||||
})
|
||||
|
||||
w2gEmitter.on('setindex', ({ detail }) => {
|
||||
handleCurrent(videos?.[detail])
|
||||
playFile(detail)
|
||||
})
|
||||
|
||||
export function playFile (file) {
|
||||
if (typeof value === 'number') {
|
||||
handleCurrent(videos?.[file])
|
||||
} else if (videos.includes(file)) {
|
||||
handleCurrent(file)
|
||||
}
|
||||
}
|
||||
|
||||
function updatew2g () {
|
||||
w2gEmitter.emit('player', { time: Math.floor(currentTime), paused })
|
||||
}
|
||||
|
||||
async function mediaChange (current, image) {
|
||||
if (current && 'mediaSession' in navigator) {
|
||||
if (!media || (!hadImage && image)) {
|
||||
// filename is already mapped so this *should* be fine
|
||||
const data = await resolveFileMedia({ fileName: current.name })
|
||||
if (image) data.episodeThumbnail = image
|
||||
updateMedia(data)
|
||||
checkAvail(current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export let miniplayer = false
|
||||
// eslint-disable-next-line prefer-const
|
||||
$condition = () => !miniplayer
|
||||
|
|
@ -137,6 +67,8 @@ let volume = localStorage.getItem('volume') || 1
|
|||
let playbackRate = 1
|
||||
$: localStorage.setItem('volume', volume || 0)
|
||||
|
||||
export let media
|
||||
|
||||
function checkAudio () {
|
||||
if ('audioTracks' in HTMLVideoElement.prototype && !video.audioTracks.length) {
|
||||
addToast({
|
||||
|
|
@ -230,9 +162,6 @@ function updateFiles (files) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
media = null
|
||||
fileMedia = null
|
||||
hadImage = false
|
||||
src = ''
|
||||
video?.load()
|
||||
currentTime = 0
|
||||
|
|
@ -250,11 +179,9 @@ async function handleCurrent (file) {
|
|||
})
|
||||
currentTime = 0
|
||||
targetTime = 0
|
||||
media = null
|
||||
fileMedia = null
|
||||
hadImage = false
|
||||
completed = false
|
||||
current = file
|
||||
emit('current', current)
|
||||
initSubs()
|
||||
src = file.url
|
||||
client.send('current', file)
|
||||
|
|
@ -267,14 +194,14 @@ async function handleCurrent (file) {
|
|||
let hasNext = false
|
||||
let hasLast = false
|
||||
function checkAvail (current) {
|
||||
if ((media?.nextAiringEpisode?.episode - 1 || media?.episodes) > fileMedia?.episodeNumber) {
|
||||
if ((media?.media?.nextAiringEpisode?.episode - 1 || media?.media?.episodes) > media?.episode) {
|
||||
hasNext = true
|
||||
} else if (videos.indexOf(current) !== videos.length - 1) {
|
||||
hasNext = true
|
||||
} else {
|
||||
hasNext = false
|
||||
}
|
||||
if (media && fileMedia?.episodeNumber > 1) {
|
||||
if (media?.episode > 1) {
|
||||
hasLast = true
|
||||
} else if (videos.indexOf(current) > 0) {
|
||||
hasLast = true
|
||||
|
|
@ -347,8 +274,8 @@ function playNext () {
|
|||
const target = (index + 1) % videos.length
|
||||
handleCurrent(videos[target])
|
||||
w2gEmitter.emit('index', { index: target })
|
||||
} else if (media?.nextAiringEpisode?.episode - 1 || media?.episodes > fileMedia?.episodeNumber) {
|
||||
playAnime(media, fileMedia?.episodeNumber + 1)
|
||||
} else if (media?.media?.nextAiringEpisode?.episode - 1 || media?.media?.episodes > media?.episode) {
|
||||
playAnime(media.media, media.episode + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -358,8 +285,8 @@ function playLast () {
|
|||
if (index > 1) {
|
||||
handleCurrent(videos[index - 1])
|
||||
w2gEmitter.emit('index', { index: index - 1 })
|
||||
} else if (media && fileMedia?.episodeNumber > 1) {
|
||||
playAnime(media, fileMedia?.episodeNumber - 1)
|
||||
} else if (media?.episode > 1) {
|
||||
playAnime(media.media, media.episode - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -692,7 +619,6 @@ $: navigator.mediaSession?.setPositionState({
|
|||
playbackRate: 1,
|
||||
position: Math.max(0, Math.min(duration || 0, currentTime || 0))
|
||||
})
|
||||
$: mediaChange(current)
|
||||
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.setActionHandler('play', playPause)
|
||||
|
|
@ -768,7 +694,6 @@ function createThumbnail (vid = video) {
|
|||
thumbnailData.context.fillRect(0, 0, 200, thumbnailData.canvas.height)
|
||||
thumbnailData.context.drawImage(vid, 0, 0, 200, thumbnailData.canvas.height)
|
||||
thumbnailData.thumbnails[index] = thumbnailData.canvas.toDataURL('image/jpeg')
|
||||
if (index === 5) mediaChange(current, thumbnailData.thumbnails[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -846,10 +771,10 @@ function toggleDropdown ({ target }) {
|
|||
let completed = false
|
||||
function checkCompletion () {
|
||||
if (!completed && duration && video?.readyState && duration - 180 < currentTime) {
|
||||
if (fileMedia?.media?.episodes || fileMedia?.media?.nextAiringEpisode?.episode) {
|
||||
if (fileMedia.media.episodes || fileMedia.media.nextAiringEpisode?.episode > fileMedia.episodeNumber) {
|
||||
if (media?.media?.episodes || media?.media?.nextAiringEpisode?.episode) {
|
||||
if (media.media.episodes || media.media.nextAiringEpisode?.episode > media.episode) {
|
||||
completed = true
|
||||
alEntry(fileMedia)
|
||||
alEntry(media)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script context='module'>
|
||||
import { DOMPARSER } from '@/modules/util.js'
|
||||
import { updateMedia } from './Player/Player.svelte'
|
||||
import { set } from './Settings.svelte'
|
||||
import { addToast } from './Toasts.svelte'
|
||||
import { alRequest } from '@/modules/anilist.js'
|
||||
import { episodeRx, findEdge, resolveSeason, getMediaMaxEp } from '@/modules/anime.js'
|
||||
import { findEdge, resolveSeason, getMediaMaxEp } from '@/modules/anime.js'
|
||||
import { findInCurrent } from './Player/MediaHandler.svelte'
|
||||
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
|
|
@ -26,6 +26,7 @@ video.remove()
|
|||
|
||||
export function playAnime (media, episode = 1) {
|
||||
episode = isNaN(episode) ? 1 : episode
|
||||
if (findInCurrent({ media, episode })) return
|
||||
rss.set({ media, episode })
|
||||
}
|
||||
|
||||
|
|
@ -216,13 +217,12 @@ function createTitle (media) {
|
|||
|
||||
<script>
|
||||
import { add } from '@/modules/torrent.js'
|
||||
import { media } from './Player/MediaHandler.svelte'
|
||||
|
||||
$: parseRss($rss)
|
||||
|
||||
let table = null
|
||||
|
||||
let fileMedia = null
|
||||
|
||||
export async function parseRss ({ media, episode }) {
|
||||
if (!media) return
|
||||
const entries = await getRSSEntries({ media, episode })
|
||||
|
|
@ -235,16 +235,6 @@ export async function parseRss ({ media, episode }) {
|
|||
return
|
||||
}
|
||||
entries.sort((a, b) => b.seeders - a.seeders)
|
||||
const streamingEpisode = media?.streamingEpisodes.filter(episode => episodeRx.exec(episode.title) && Number(episodeRx.exec(episode.title)[1]) === Number(episode))[0]
|
||||
fileMedia = {
|
||||
mediaTitle: media?.title.userPreferred,
|
||||
episodeNumber: Number(episode),
|
||||
episodeTitle: streamingEpisode ? episodeRx.exec(streamingEpisode.title)[2] : undefined,
|
||||
episodeThumbnail: streamingEpisode?.thumbnail,
|
||||
mediaCover: media?.coverImage.medium,
|
||||
name: 'Miru',
|
||||
media
|
||||
}
|
||||
if (settings.rssAutoplay) {
|
||||
play(entries[0])
|
||||
} else {
|
||||
|
|
@ -258,7 +248,7 @@ function checkClose ({ keyCode }) {
|
|||
if (keyCode === 27) close()
|
||||
}
|
||||
function play (entry) {
|
||||
updateMedia(fileMedia)
|
||||
$media = $rss
|
||||
if (entry.seeders !== '?' && entry.seeders <= 15) {
|
||||
addToast({
|
||||
text: 'This release is poorly seeded and likely will have playback issues such as buffering!',
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
<script context='module'>
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
export const files = writable([])
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import Home from './Home/Home.svelte'
|
||||
import Player from './Player/Player.svelte'
|
||||
import MediaHandler from './Player/MediaHandler.svelte'
|
||||
import Settings from './Settings.svelte'
|
||||
import WatchTogether from './WatchTogether/WatchTogether.svelte'
|
||||
import Miniplayer from 'svelte-miniplayer'
|
||||
|
|
@ -17,7 +11,7 @@ const current = getContext('gallery')
|
|||
|
||||
<div class='overflow-hidden content-wrapper'>
|
||||
<Miniplayer active={page !== 'player'} class='bg-dark z-10 {page === 'player' ? 'h-full' : ''}' minwidth='35rem' maxwidth='45rem' width='300px' padding='2rem'>
|
||||
<Player files={$files} miniplayer={page !== 'player'} bind:page />
|
||||
<MediaHandler miniplayer={page !== 'player'} bind:page />
|
||||
</Miniplayer>
|
||||
{#if page === 'settings'}
|
||||
<Settings />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import { alID } from '@/modules/anilist.js'
|
||||
import { media } from './Player/Player.svelte'
|
||||
import { media } from './Player/MediaHandler.svelte'
|
||||
import { platformMap } from './Settings.svelte'
|
||||
import { addToast } from './Toasts.svelte'
|
||||
const sidebar = getContext('sidebar')
|
||||
|
|
@ -35,7 +35,7 @@ const links = [
|
|||
},
|
||||
{
|
||||
click: () => {
|
||||
if (media) $view = media
|
||||
if ($media) $view = $media.media
|
||||
},
|
||||
icon: 'queue_music',
|
||||
text: 'Now Playing'
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ function checkClose ({ keyCode }) {
|
|||
<div class='col-md-9 px-20'>
|
||||
<h1 class='title font-weight-bold text-white'>Synopsis</h1>
|
||||
<div class='font-size-16 pr-15 pre-wrap'>
|
||||
{media.description.replace(/<[^>]*>/g, '')}
|
||||
{media.description?.replace(/<[^>]*>/g, '') || ''}
|
||||
</div>
|
||||
<ToggleList list={media.relations?.edges?.filter(({ node }) => node.type === 'ANIME')} let:item>
|
||||
<div class='w-150 mx-15 mb-10 rel pointer' on:click={async () => { $view = null; $view = (await alRequest({ method: 'SearchIDSingle', id: item.node.id })).data.Media }}>
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export function alEntry (filemedia) {
|
|||
if (media.status === 'FINISHED' || media.status === 'RELEASING') {
|
||||
// some anime/OVA's can have a single episode, or some movies can have multiple episodes
|
||||
const singleEpisode = (!media.episodes || (media.format === 'MOVIE' && media.episodes === 1)) && 1
|
||||
const videoEpisode = Number(filemedia.episodeNumber) || singleEpisode
|
||||
const videoEpisode = Number(filemedia.episode) || singleEpisode
|
||||
const mediaEpisode = media.nextAiringEpisode?.episode || media.episodes || singleEpisode
|
||||
// check episode range
|
||||
if (videoEpisode && mediaEpisode && mediaEpisode >= videoEpisode) {
|
||||
|
|
@ -113,7 +113,7 @@ export function alEntry (filemedia) {
|
|||
if (!media.mediaListEntry || media.mediaListEntry?.progress <= videoEpisode || singleEpisode) {
|
||||
const variables = {
|
||||
method: 'Entry',
|
||||
repeat: 0,
|
||||
repeat: media.mediaListEntry?.repeat || 0,
|
||||
id: media.id,
|
||||
status: 'CURRENT',
|
||||
episode: videoEpisode,
|
||||
|
|
@ -121,7 +121,7 @@ export function alEntry (filemedia) {
|
|||
}
|
||||
if (videoEpisode === mediaEpisode) {
|
||||
variables.status = 'COMPLETED'
|
||||
if (media.mediaListEntry?.status === 'COMPLETED' || media.mediaListEntry.status === 'REPEATING') variables.repeat = media.mediaListEntry.repeat + 1
|
||||
if (media.mediaListEntry.status === 'REPEATING') variables.repeat = media.mediaListEntry.repeat + 1
|
||||
}
|
||||
if (!lists.includes('Watched using Miru')) {
|
||||
variables.lists.push('Watched using Miru')
|
||||
|
|
|
|||
|
|
@ -83,8 +83,6 @@ export function getMediaMaxEp (media, playable) {
|
|||
}
|
||||
}
|
||||
|
||||
export const episodeRx = /Episode (\d+) - (.*)/
|
||||
|
||||
// resolve anime name based on file name and store it
|
||||
const postfix = {
|
||||
1: 'st',
|
||||
|
|
@ -160,12 +158,10 @@ function getParseObjTitle (obj) {
|
|||
return title
|
||||
}
|
||||
|
||||
export async function resolveFileMedia (opts) {
|
||||
// opts.fileName
|
||||
const parsePromises = opts.fileName.constructor === Array
|
||||
? opts.fileName.map(name => anitomyscript(name))
|
||||
: [anitomyscript(opts.fileName)]
|
||||
const parseObjs = await Promise.all(parsePromises)
|
||||
export async function resolveFileMedia (fileName) {
|
||||
let parseObjs = await anitomyscript(fileName)
|
||||
|
||||
if (parseObjs.constructor !== Array) parseObjs = [parseObjs]
|
||||
// batches promises in 10 at a time, because of CF burst protection, which still sometimes gets triggered :/
|
||||
await PromiseBatch(resolveTitle, [...new Set(parseObjs.map(obj => getParseObjTitle(obj)))].filter(title => !(title in relations)), 10)
|
||||
const fileMedias = []
|
||||
|
|
@ -218,20 +214,14 @@ export async function resolveFileMedia (opts) {
|
|||
}
|
||||
}
|
||||
}
|
||||
const streamingEpisode = media?.streamingEpisodes.filter(episode => episodeRx.exec(episode.title) && Number(episodeRx.exec(episode.title)[1]) === Number(parseObj.episode_number))[0]
|
||||
fileMedias.push({
|
||||
mediaTitle: media?.title.userPreferred || parseObj.anime_title,
|
||||
episodeNumber: episode || parseObj.episode_number,
|
||||
episodeTitle: streamingEpisode && episodeRx.exec(streamingEpisode.title)[2],
|
||||
episodeThumbnail: streamingEpisode?.thumbnail,
|
||||
mediaCover: media?.coverImage.medium,
|
||||
name: 'Miru',
|
||||
episode: episode || parseObj.episode_number,
|
||||
parseObject: parseObj,
|
||||
media,
|
||||
failed
|
||||
})
|
||||
}
|
||||
return fileMedias.length === 1 ? fileMedias[0] : fileMedias
|
||||
return fileMedias
|
||||
}
|
||||
|
||||
export function findEdge (media, type, formats = ['TV', 'TV_SHORT'], skip) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// import WebTorrent from 'webtorrent'
|
||||
import { set } from '@/lib/Settings.svelte'
|
||||
import { files } from '@/lib/Router.svelte'
|
||||
import { files } from '@/lib/Player/MediaHandler.svelte'
|
||||
import { page } from '@/App.svelte'
|
||||
import 'browser-event-target-emitter'
|
||||
|
||||
class TorrentWorker extends Worker {
|
||||
constructor (opts) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue