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