fix: improve subtitle parsing state handling

This commit is contained in:
ThaUnknown 2023-08-18 16:27:33 +02:00
parent fb81cb9367
commit 800eba7a48
3 changed files with 109 additions and 90 deletions

View file

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

View file

@ -1,9 +1,8 @@
import WebTorrent from 'webtorrent'
import { SubtitleParser, SubtitleStream } from 'matroska-subtitles'
import { ipcRenderer } from 'electron'
import { pipeline } from 'streamx'
import HTTPTracker from 'bittorrent-tracker/lib/client/http-tracker.js'
import { hex2bin, arr2hex, text2arr } from 'uint8-util'
import Parser from './parser.js'
class TorrentClient extends WebTorrent {
constructor (settings) {
@ -19,7 +18,6 @@ class TorrentClient extends WebTorrent {
this.current = null
this.parsed = false
this.boundParse = this.parseSubtitles.bind(this)
setInterval(() => {
this.dispatch('stats', {
@ -84,25 +82,12 @@ class TorrentClient extends WebTorrent {
if (data.data) {
const found = (await this.get(data.data.infoHash))?.files.find(file => file.path === data.data.path)
if (this.current) {
this.current.removeListener('done', this.boundParse)
this.current.removeAllListeners('stream')
}
this.cancelParse()
this.parsed = false
this.parser?.destroy()
this.current = found
if (this.current?.name.endsWith('.mkv')) {
// if (this.current.done) this.parseSubtitles()
// this.current.once('done', this.boundParse)
this.parseFonts(this.current)
this.current.on('stream', (_, cb) => {
if (!this.parsed) {
this.stream = new SubtitleStream(this.stream)
this.handleSubtitleParser(this.stream, true)
cb(this.stream)
}
})
}
// TODO: findSubtitleFiles(current)
this.parser = new Parser(this, found)
// TODO: parser.findSubtitleFiles(found)
}
break
}
@ -138,79 +123,9 @@ class TorrentClient extends WebTorrent {
message({ type, data }, transfer)
}
parseSubtitles () {
if (this.current.name.endsWith('.mkv')) {
const parser = new SubtitleParser()
this.handleSubtitleParser(parser, true)
const finish = () => {
console.log('Sub parsing finished')
this.parsed = true
this.parser?.destroy()
this.parser = undefined
fileStream?.destroy()
}
parser.once('tracks', tracks => {
if (!tracks.length) finish()
})
parser.once('finish', finish)
console.log('Sub parsing started')
const fileStream = this.current.createReadStream()
this.parser = fileStream.pipe(parser)
}
}
cancelParse () {
this.parser?.destroy()
this.stream?.destroy()
this.metadata?.destroy()
this.metadata = undefined
this.parser = undefined
this.stream = undefined
}
parseFonts (file) {
this.metadata = pipeline(file.createReadStream(), new SubtitleParser())
this.handleSubtitleParser(this.metadata)
this.metadata.once('tracks', tracks => {
if (!tracks.length) {
this.parsed = true
this.metadata.destroy()
}
})
this.metadata.once('subtitle', () => {
this.metadata.destroy()
})
}
handleSubtitleParser (parser, skipFile) {
parser.once('tracks', tracks => {
if (!tracks.length) {
this.parsed = true
parser?.destroy()
} else {
this.dispatch('tracks', tracks)
}
})
parser.on('subtitle', (subtitle, trackNumber) => {
this.dispatch('subtitle', { subtitle, trackNumber })
})
if (!skipFile) {
parser.once('chapters', chapters => {
this.dispatch('chapters', chapters)
})
parser.on('file', file => {
if (file.mimetype === 'application/x-truetype-font' || file.mimetype === 'application/font-woff' || file.mimetype === 'application/vnd.ms-opentype' || file.mimetype === 'font/sfnt' || file.mimetype.startsWith('font/') || file.filename.toLowerCase().endsWith('.ttf')) {
const data = Buffer.from(file.data)
this.dispatch('file', { mimetype: file.mimetype, data }, [data.buffer])
}
})
}
}
predestroy () {
this.destroy()
this.server.close()
this.cancelParse()
}
}

104
src/background/parser.js Normal file
View file

@ -0,0 +1,104 @@
import { pipeline } from 'streamx'
import { SubtitleParser, SubtitleStream } from 'matroska-subtitles'
export default class Parser {
parsed = false
metadata = null
client = null
file = null
parser = null
stream = null
destroyed = false
constructor (client, file) {
this.client = client
this.file = file
if (this.file.name.endsWith('.mkv')) {
// if (this.file.done) this.parseSubtitles()
// this.file.once('done', this.boundParse)
this.parseFonts(this.file)
this.file.on('stream', (_, cb) => {
if (!this.parsed) {
this.stream = new SubtitleStream(this.metadata || this.stream)
this.handleSubtitleParser(this.stream, true)
cb(this.stream)
}
})
}
}
parseSubtitles () {
if (this.file.name.endsWith('.mkv')) {
const parser = new SubtitleParser()
this.handleSubtitleParser(parser, true)
const finish = () => {
console.log('Sub parsing finished')
this.parsed = true
this.parser?.destroy()
this.parser = undefined
fileStream?.destroy()
}
parser.once('tracks', tracks => {
if (!tracks.length) finish()
})
parser.once('finish', finish)
console.log('Sub parsing started')
const fileStream = this.file.createReadStream()
this.parser = fileStream.pipe(parser)
}
}
destroy () {
this.destroyed = true
this.parser?.destroy()
this.stream?.destroy()
this.metadata?.destroy()
this.metadata = undefined
this.parser = undefined
this.stream = undefined
}
parseFonts (file) {
const metadata = pipeline(file.createReadStream(), new SubtitleParser())
this.handleSubtitleParser(metadata)
metadata.once('tracks', tracks => {
if (!tracks.length) {
this.parsed = true
metadata.destroy()
}
})
metadata.once('subtitle', () => {
metadata.destroy()
if (this.destroyed) return
this.metadata = metadata
})
}
handleSubtitleParser (parser, skipFile) {
parser.once('tracks', tracks => {
if (!tracks.length) {
this.parsed = true
parser?.destroy()
} else {
if (this.destroyed) return
this.client.dispatch('tracks', tracks)
}
})
parser.on('subtitle', (subtitle, trackNumber) => {
if (this.destroyed) return
this.client.dispatch('subtitle', { subtitle, trackNumber })
})
if (!skipFile) {
parser.once('chapters', chapters => {
if (this.destroyed) return
this.client.dispatch('chapters', chapters)
})
parser.on('file', file => {
if (this.destroyed) return
if (file.mimetype.toLowerCase().includes('font') || file.filename.toLowerCase().endsWith('.ttf')) {
const data = Buffer.from(file.data)
this.client.dispatch('file', { mimetype: file.mimetype, data }, [data.buffer])
}
})
}
}
}