mirror of
https://github.com/Myxelium/Bridge-Multi.git
synced 2026-04-11 14:19:38 +00:00
Improve chart folder download performance
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"mkdirp": "^3.0.1",
|
||||
"mv": "^2.1.1",
|
||||
"parse-sng": "^3.1.1",
|
||||
"parse-sng": "^3.1.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"rxjs": "~7.8.1",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -57,8 +57,8 @@ dependencies:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
parse-sng:
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.2
|
||||
rimraf:
|
||||
specifier: ^5.0.5
|
||||
version: 5.0.5
|
||||
@@ -8370,8 +8370,8 @@ packages:
|
||||
engines: {node: '>= 0.10'}
|
||||
dev: true
|
||||
|
||||
/parse-sng@3.1.1:
|
||||
resolution: {integrity: sha512-BOU9Y4LE5VsEc76lZonVXcbKrbGbetzdP28avnWvioNEhACzTEhtqDdtNmgsXiMO3lvkveRcM5wp6hEniSIQkA==}
|
||||
/parse-sng@3.1.2:
|
||||
resolution: {integrity: sha512-gAFsAJ6yaU2uH8UeSRC+EYFAN/urm/ICRJSLSwP1sqVBEF/VhN0XAxNNFTeLGsgdv1V2gx6pLA8sS9M6YX/FCA==}
|
||||
dependencies:
|
||||
binary-parser: 2.2.1
|
||||
events: 3.3.0
|
||||
@@ -9193,7 +9193,7 @@ packages:
|
||||
lodash: 4.17.21
|
||||
midievents: 2.0.0
|
||||
midifile: 2.0.0
|
||||
parse-sng: 3.1.1
|
||||
parse-sng: 3.1.2
|
||||
sanitize-filename: 1.6.3
|
||||
sharp: 0.32.6
|
||||
stream-audio-fingerprint: github.com/Geomitron/stream-audio-fingerprint/197e8a7ff60165b18bf1debc23dab4814996a20d
|
||||
|
||||
@@ -60,7 +60,7 @@ export class SearchService {
|
||||
this.search().subscribe()
|
||||
}
|
||||
|
||||
get areMorePages() { return this.songsResponse.page && this.groupedSongs.length === this.songsResponse.page * resultsPerPage }
|
||||
get areMorePages() { return this.songsResponse?.page && this.groupedSongs.length === this.songsResponse.page * resultsPerPage }
|
||||
|
||||
/**
|
||||
* General search, uses the `/search?q=` endpoint.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import EventEmitter from 'events'
|
||||
import { createWriteStream, WriteStream } from 'fs'
|
||||
import { createWriteStream } from 'fs'
|
||||
import { access, constants } from 'fs/promises'
|
||||
import { round, throttle } from 'lodash'
|
||||
import { mkdirp } from 'mkdirp'
|
||||
@@ -8,6 +8,8 @@ import mv from 'mv'
|
||||
import { SngStream } from 'parse-sng'
|
||||
import { join } from 'path'
|
||||
import { rimraf } from 'rimraf'
|
||||
import { Readable } from 'stream'
|
||||
import { ReadableStream } from 'stream/web'
|
||||
import { inspect } from 'util'
|
||||
|
||||
import { tempPath } from '../../../src-shared/Paths'
|
||||
@@ -150,48 +152,20 @@ export class ChartDownload {
|
||||
const fileSize = BigInt(sngResponse.headers.get('Content-Length')!)
|
||||
|
||||
if (this.isSng) {
|
||||
const writeStream = createWriteStream(join(this.tempPath, this.destinationName))
|
||||
const reader = sngResponse.body.getReader()
|
||||
const sngStream = Readable.fromWeb(sngResponse.body as ReadableStream<Uint8Array>, { highWaterMark: 2e+9 })
|
||||
|
||||
sngStream.pipe(createWriteStream(join(this.tempPath, this.destinationName), { highWaterMark: 2e+9 }))
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
let downloadedByteCount = BigInt(0)
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
let result: ReadableStreamReadResult<Uint8Array>
|
||||
try {
|
||||
result = await reader.read()
|
||||
} catch (err) {
|
||||
throw { header: 'Failed to download the chart file', body: inspect(err) }
|
||||
}
|
||||
|
||||
if (this._canceled) {
|
||||
await reader.cancel()
|
||||
writeStream.end()
|
||||
return
|
||||
}
|
||||
|
||||
if (result.done) { writeStream.end(); return }
|
||||
|
||||
downloadedByteCount += BigInt(result.value.length)
|
||||
sngStream.on('end', resolve)
|
||||
sngStream.on('error', err => reject(err))
|
||||
sngStream.on('data', data => {
|
||||
downloadedByteCount += BigInt(data.length)
|
||||
const downloadPercent = round(100 * Number(downloadedByteCount / BigInt(1000)) / Number(fileSize / BigInt(1000)), 1)
|
||||
this.showProgress(`Downloading... (${downloadPercent}%)`, downloadPercent)
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
writeStream.write(result.value, err => {
|
||||
if (err) {
|
||||
reject({ header: 'Failed to download the chart file', body: inspect(err) })
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (writeStream.writableNeedDrain) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
writeStream.once('drain', resolve)
|
||||
writeStream.once('error', err => reject({ header: 'Failed to download the chart file', body: inspect(err) }))
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const sngStream = new SngStream(() => sngResponse.body!, { generateSongIni: true })
|
||||
let downloadedByteCount = BigInt(0)
|
||||
@@ -200,68 +174,19 @@ export class ChartDownload {
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
sngStream.on('file', async (fileName, fileStream) => {
|
||||
let writeStream: WriteStream
|
||||
let reader: ReadableStreamDefaultReader<Uint8Array>
|
||||
try {
|
||||
writeStream = createWriteStream(join(this.tempPath, this.destinationName, fileName))
|
||||
writeStream.on('error', () => { /** Surpress unhandled promise rejection */ })
|
||||
reader = fileStream.getReader()
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
const nodeFileStream = Readable.fromWeb(fileStream as ReadableStream<Uint8Array>, { highWaterMark: 2e+9 })
|
||||
nodeFileStream.pipe(createWriteStream(join(this.tempPath, this.destinationName, fileName), { highWaterMark: 2e+9 }))
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
let result: ReadableStreamReadResult<Uint8Array>
|
||||
try {
|
||||
result = await reader.read()
|
||||
} catch (err) {
|
||||
throw { header: 'Failed to download the chart file', body: inspect(err) }
|
||||
}
|
||||
|
||||
if (this._canceled) {
|
||||
await reader.cancel()
|
||||
writeStream.end()
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
if (result.done) { writeStream.end(); return }
|
||||
|
||||
downloadedByteCount += BigInt(result.value.length)
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
nodeFileStream.on('end', resolve)
|
||||
nodeFileStream.on('error', err => reject(err))
|
||||
nodeFileStream.on('data', data => {
|
||||
downloadedByteCount += BigInt(data.length)
|
||||
const downloadPercent =
|
||||
round(100 * Number(downloadedByteCount / BigInt(1000)) / Number(fileSize / BigInt(1000)), 1)
|
||||
this.showProgress(`Downloading "${fileName}"... (${downloadPercent}%)`, downloadPercent)
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
writeStream.write(result.value, err => {
|
||||
if (err) {
|
||||
reject({ header: 'Failed to download the chart file', body: inspect(err) })
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (writeStream.writableNeedDrain) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
writeStream.once('drain', resolve)
|
||||
writeStream.once('error', err => reject({
|
||||
header: 'Failed to download the chart file',
|
||||
body: inspect(err),
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
await reader.cancel()
|
||||
} catch (err) { /** ignore; error already reported */ }
|
||||
writeStream.end()
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
sngStream.on('end', resolve)
|
||||
sngStream.on('error', err => reject(err))
|
||||
|
||||
Reference in New Issue
Block a user