fix: memory leaks

This commit is contained in:
ThaUnknown 2023-06-14 20:35:21 +02:00
parent 68c05a71f9
commit 5e5aa7b6ab
4 changed files with 393 additions and 329 deletions

View file

@ -1,6 +1,6 @@
{
"name": "Miru",
"version": "4.0.8",
"version": "4.0.9",
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
"description": "Stream anime torrents, real-time with no waiting for downloads.",
"main": "build/main.js",
@ -43,7 +43,7 @@
"webpack": "^5.85.0",
"webpack-cli": "^5.1.3",
"webpack-dev-server": "^4.15.0",
"webtorrent": "^2.0.37"
"webtorrent": "^2.1.0"
},
"standard": {
"ignore": [

File diff suppressed because it is too large Load diff

View file

@ -51,13 +51,19 @@ class TorrentClient extends WebTorrent {
async handleMessage ({ data }) {
switch (data.type) {
case 'current': {
this.current?.removeListener('done', this.boundParse)
this.cancelParse()
this.current = null
this.metadata = null
this.parsed = false
if (data.data) {
this.current = (await this.get(data.data.infoHash))?.files.find(file => file.path === data.data.path)
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('iterator')
// this is a patch, idfk why these leak
for (const iterator of this.current._iterators) {
iterator.destroy()
}
}
this.cancelParse()
this.parsed = false
this.current = found
if (this.current?.name.endsWith('.mkv')) {
// if (this.current.done) this.parseSubtitles()
// this.current.once('done', this.boundParse)
@ -125,22 +131,23 @@ class TorrentClient extends WebTorrent {
cancelParse () {
this.parser?.destroy()
this.metadata?.destroy()
this.metadata = undefined
this.parser = undefined
}
parseFonts (file) {
const stream = new SubtitleParser(file)
this.handleSubtitleParser(stream)
stream.once('tracks', tracks => {
this.metadata = new SubtitleParser(file)
this.handleSubtitleParser(this.metadata)
this.metadata.once('tracks', tracks => {
if (!tracks.length) {
this.parsed = true
stream.destroy()
this.metadata.destroy()
}
})
stream.once('subtitle', () => {
stream.destroy()
this.metadata.once('subtitle', () => {
this.metadata.destroy()
})
this.metadata = stream
}
handleSubtitleParser (parser, skipFile) {

View file

@ -1,6 +1,5 @@
import { EbmlIteratorDecoder, EbmlTagId } from 'ebml-iterator'
import { EventEmitter } from 'events'
import join from 'join-async-iterator'
import { inflate } from 'pako'
const SSA_TYPES = new Set(['ssa', 'ass'])
@ -53,10 +52,7 @@ export class SubtitleParserBase extends EventEmitter {
[EbmlTagId.BlockGroup]: this.handleBlockGroup.bind(this),
[EbmlTagId.Chapters]: this.handleChapters.bind(this)
}
}
async * [Symbol.asyncIterator] (stream) {
const decoder = new EbmlIteratorDecoder({
this.decoder = new EbmlIteratorDecoder({
bufferTagIds: [
EbmlTagId.TimecodeScale,
EbmlTagId.Tracks,
@ -64,20 +60,17 @@ export class SubtitleParserBase extends EventEmitter {
EbmlTagId.AttachedFile,
EbmlTagId.Chapters,
EbmlTagId.Duration
],
stream
]
})
}
for await (const chunk of stream) {
if (this.destroyed) return null
const tags = 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()
}
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()
}
yield chunk
}
}
@ -200,12 +193,16 @@ export class SubtitleParser extends SubtitleParserBase {
super()
;(async () => {
const iterator = stream[Symbol.asyncIterator]()
try {
// eslint-disable-next-line no-unused-vars
for await (const _ of super[Symbol.asyncIterator](stream)) {
if (this.destroyed) break
// eslint-disable-next-line no-unused-vars
for await (const chunk of iterator) {
if (this.destroyed) return iterator.return()
this.decoderWrite(chunk)
}
} catch (e) {}
} catch (e) {
iterator.return()
}
this.emit('finish')
})()
}
@ -227,35 +224,40 @@ export class SubtitleStream extends SubtitleParserBase {
}
}
async * [Symbol.asyncIterator] (stream = this._stream) {
while (true) {
if (this.destroyed) return
if (this.unstable) {
const iterator = stream[Symbol.asyncIterator]()
const { value: chunk } = await iterator.next()
if (!chunk) return
// 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
yield chunk.slice(0, i)
yield * super[Symbol.asyncIterator](join([[chunk.slice(i)], iterator]))
return
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
} else {
yield * super[Symbol.asyncIterator](stream)
return
}
} finally {
this._stream.return()
}
this._stream.return()
}
}