fix: destroy background

fix: animetype keys
This commit is contained in:
ThaUnknown 2023-11-09 23:14:42 +01:00
parent 13e25131da
commit 4cadfa6b1f
6 changed files with 14 additions and 277 deletions

View file

@ -1,6 +1,6 @@
{
"name": "Miru",
"version": "4.4.17",
"version": "4.4.18",
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
"description": "Stream anime torrents, real-time with no waiting for downloads.",
"main": "build/main.js",

View file

@ -27,11 +27,11 @@ class TorrentClient extends WebTorrent {
resolve()
ports[0].onmessage = ({ data }) => {
if (data.type === 'load') this.loadLastTorrent()
if (data.type === 'destroy') this.predestroy()
if (data.type === 'destroy') this.destroy()
this.handleMessage({ data })
}
})
ipcRenderer.on('destroy', this.predestroy.bind(this))
ipcRenderer.on('destroy', this.destroy.bind(this))
})
this.settings = settings
@ -226,9 +226,10 @@ class TorrentClient extends WebTorrent {
this.message?.({ type, data }, transfer)
}
predestroy () {
this.destroy()
destroy () {
this.parser?.destroy()
this.server.close()
super.destroy()
}
}

View file

@ -1,265 +0,0 @@
// currently unused
import { EbmlIteratorDecoder, EbmlTagId } from 'ebml-iterator'
import { EventEmitter } from 'events'
import { inflate } from 'pako'
const SSA_TYPES = new Set(['ssa', 'ass'])
const SSA_KEYS = ['readOrder', 'layer', 'style', 'name', 'marginL', 'marginR', 'marginV', 'effect', 'text']
function getChild (chunk, tag) {
return chunk?.Children.find(({ id }) => id === tag)
}
function getData (chunk, tag) {
return getChild(chunk, tag)?.data
}
export class SubtitleParserBase extends EventEmitter {
constructor () {
super()
this.subtitleTracks = new Map()
this.timecodeScale = 1
this._currentClusterTimecode = null
this.destroyed = false
this._tagMap = {
// Segment Information
[EbmlTagId.TimecodeScale]: ({ data }) => {
this.timecodeScale = data / 1000000
},
// Assumption: This is a Cluster `Timecode`
[EbmlTagId.Timecode]: ({ data }) => {
this._currentClusterTimecode = data
},
// Parse attached files, mainly to allow extracting subtitle font files.
[EbmlTagId.AttachedFile]: chunk => {
this.emit('file', {
filename: getData(chunk, EbmlTagId.FileName),
mimetype: getData(chunk, EbmlTagId.FileMimeType),
data: getData(chunk, EbmlTagId.FileData)
})
},
// Duration for chapters which don't specify an end position
[EbmlTagId.Duration]: ({ data }) => {
if (this.chapters) {
this.chapters[this.chapters.length - 1].end = data
this.emit('chapters', this.chapters)
} else {
this.duration = data
}
},
[EbmlTagId.Tracks]: this.handleTracks.bind(this),
[EbmlTagId.BlockGroup]: this.handleBlockGroup.bind(this),
[EbmlTagId.Chapters]: this.handleChapters.bind(this)
}
this.decoder = new EbmlIteratorDecoder({
bufferTagIds: [
EbmlTagId.TimecodeScale,
EbmlTagId.Tracks,
EbmlTagId.BlockGroup,
EbmlTagId.AttachedFile,
EbmlTagId.Chapters,
EbmlTagId.Duration
]
})
}
decoderWrite (chunk) {
const tags = this.decoder.parseTags(chunk)
for (const tag of tags) {
this._tagMap[tag.id]?.(tag)
if (tag.id === EbmlTagId.Tracks) {
if (!tag.Children.some(({ id }) => id === EbmlTagId.TrackEntry)) return this.destroy()
}
}
}
handleTracks (chunk) {
for (const entry of chunk.Children.filter(c => c.id === EbmlTagId.TrackEntry)) {
// Skip non subtitle tracks
if (getData(entry, EbmlTagId.TrackType) !== 0x11) continue
const codecID = getData(entry, EbmlTagId.CodecID) || ''
if (codecID.startsWith('S_TEXT')) {
const track = {
number: getData(entry, EbmlTagId.TrackNumber),
language: getData(entry, EbmlTagId.Language),
type: codecID.substring(7).toLowerCase()
}
const name = getData(entry, EbmlTagId.Name)
if (name) track.name = name
const header = getData(entry, EbmlTagId.CodecPrivate)
if (header) track.header = header.toString()
// TODO: Assume zlib deflate compression
const compressed = entry.Children.find(c =>
c.id === EbmlTagId.ContentEncodings &&
c.Children.find(cc =>
cc.id === EbmlTagId.ContentEncoding &&
getChild(cc, EbmlTagId.ContentCompression)
)
)
if (compressed) track._compressed = true
this.subtitleTracks.set(track.number, track)
}
}
this.emit('tracks', [...this.subtitleTracks.values()])
}
handleBlockGroup (chunk) {
const block = getChild(chunk, EbmlTagId.Block)
if (block && this.subtitleTracks.has(block.track)) {
const blockDuration = getData(chunk, EbmlTagId.BlockDuration)
const track = this.subtitleTracks.get(block.track)
const payload = track._compressed
? inflate(Buffer.from(block.payload), { to: 'string' })
: block.payload
const subtitle = {
text: payload.toString('utf8'),
time: (block.value + this._currentClusterTimecode) * this.timecodeScale,
duration: blockDuration * this.timecodeScale
}
if (SSA_TYPES.has(track.type)) {
// extract SSA/ASS keys
const values = subtitle.text.split(',')
// ignore read-order, and skip layer if ssa
for (let i = track.type === 'ssa' ? 2 : 1; i < 8; i++) {
subtitle[SSA_KEYS[i]] = values[i]
}
subtitle.text = values.slice(8).join(',')
}
this.emit('subtitle', subtitle, block.track)
}
}
handleChapters ({ Children }) {
const editions = Children.filter(c => c.id === EbmlTagId.EditionEntry)
// https://www.matroska.org/technical/chapters.html#default-edition
// finds first default edition, or first entry
const defaultEdition = editions.find(c => {
return c.Children.some(cc => {
return cc.id === EbmlTagId.EditionFlagDefault && Boolean(cc.data)
})
}) || editions[0]
// exclude hidden atoms
const atoms = defaultEdition.Children.filter(c => c.id === EbmlTagId.ChapterAtom && !getData(c, EbmlTagId.ChapterFlagHidden))
const chapters = []
for (let i = atoms.length - 1; i >= 0; --i) {
const start = getData(atoms[i], EbmlTagId.ChapterTimeStart) / this.timecodeScale / 1000000
const end = (getData(atoms[i], EbmlTagId.ChapterTimeEnd) / this.timecodeScale / 1000000)
const disp = getChild(atoms[i], EbmlTagId.ChapterDisplay)
chapters[i] = {
start,
end,
text: getData(disp, EbmlTagId.ChapString),
language: getData(disp, EbmlTagId.ChapLanguage)
}
}
chapters.sort((a, b) => a.start - b.start)
for (let i = chapters.length - 1; i >= 0; --i) {
chapters[i].end ||= chapters[i + 1]?.start || this.duration
}
if (this.duration) {
this.emit('chapters', chapters)
} else {
this.chapters = chapters
}
}
destroy () {
this.destroyed = true
this.emit('finish')
}
}
export class SubtitleParser extends SubtitleParserBase {
constructor (stream) {
super()
;(async () => {
const iterator = stream[Symbol.asyncIterator]()
try {
// eslint-disable-next-line no-unused-vars
for await (const chunk of iterator) {
if (this.destroyed) return iterator.return()
this.decoderWrite(chunk)
}
} catch (e) {
iterator.return()
}
this.emit('finish')
})()
}
}
export class SubtitleStream extends SubtitleParserBase {
constructor (prevInstance, stream) {
super()
this._stream = stream
if (prevInstance instanceof SubtitleParserBase) {
// copy previous metadata
this.subtitleTracks = prevInstance.subtitleTracks
this.timecodeScale = prevInstance.timecodeScale
// may not be at ebml tag offset
this.unstable = true
}
}
destroy () {
this.destroyed = true
this.emit('finish')
this._stream.return()
}
async * [Symbol.asyncIterator] () {
try {
for await (const chunk of this._stream) {
if (this.destroyed) return this._stream.return()
if (this.unstable) {
// the ebml decoder expects to see a tag, so we won't use it until we find a cluster
for (let i = 0; i < chunk.length - 12; i++) {
// cluster id 0x1F43B675
// https://matroska.org/technical/elements.html#LevelCluster
if (chunk[i] === 0x1f && chunk[i + 1] === 0x43 && chunk[i + 2] === 0xb6 && chunk[i + 3] === 0x75) {
// length of cluster size tag
const len = 8 - Math.floor(Math.log2(chunk[i + 4]))
// first tag in cluster is a valid EbmlTag
if (EbmlTagId[chunk[i + 4 + len]]) {
// okay this is probably a cluster
this.unstable = false
this.decoderWrite(chunk.slice(i))
}
}
}
} else {
this.decoderWrite(chunk)
}
yield chunk
}
} finally {
this._stream.return()
}
this._stream.return()
}
}

View file

@ -1,11 +1,11 @@
<script>
import { getContext } from 'svelte'
import { TABS } from './Tabs.svelte'
import { getContext } from 'svelte'
import { TABS } from './Tabs.svelte'
const panel = {}
const { registerPanel, selectedPanel } = getContext(TABS)
const panel = {}
const { registerPanel, selectedPanel } = getContext(TABS)
registerPanel(panel)
registerPanel(panel)
</script>
{#if $selectedPanel === panel}

View file

@ -28,6 +28,7 @@ export function since (date) {
for (const key in ranges) {
if (ranges[key] < Math.abs(secondsElapsed)) {
const delta = secondsElapsed / ranges[key]
// @ts-ignore
return formatter.format(Math.round(delta), key)
}
}
@ -120,7 +121,7 @@ export function debounce (fn, time) {
clearTimeout(timeout)
timeout = setTimeout(later, time)
}
};
}
export function binarySearch (arr, el) {
let left = 0

View file

@ -126,7 +126,7 @@
return file
})
videoFiles = videoFiles.filter(file => !TYPE_EXCLUSIONS.includes(file.media.parseObject.anime_type))
videoFiles = videoFiles.filter(file => !TYPE_EXCLUSIONS.includes(file.media.parseObject.anime_type?.toUpperCase()))
if (nowPlaying?.verified && videoFiles.length === 1) {
console.info('Media was verified, skipping verification')