Various refactoring

This commit is contained in:
Geomitron
2020-03-03 21:48:41 -05:00
parent 442736205e
commit 4ebf2db650
34 changed files with 503 additions and 329 deletions

View File

@@ -2,16 +2,24 @@ import { IPCInvokeHandler } from '../shared/IPCHandler'
import Database from '../shared/Database'
import { AlbumArtResult } from '../shared/interfaces/songDetails.interface'
/**
* Handles the 'album-art' event.
*/
export default class AlbumArtHandler implements IPCInvokeHandler<'album-art'> {
event: 'album-art' = 'album-art'
// TODO: add method documentation
/**
* @returns an `AlbumArtResult` object containing the album art for the song with `songID`.
*/
async handler(songID: number) {
const db = await Database.getInstance()
return db.sendQuery(this.getAlbumArtQuery(songID), 1) as Promise<AlbumArtResult>
}
/**
* @returns a database query that returns the album art for the song with `songID`.
*/
private getAlbumArtQuery(songID: number) {
return `
SELECT art

View File

@@ -2,16 +2,24 @@ import { IPCInvokeHandler } from '../shared/IPCHandler'
import Database from '../shared/Database'
import { VersionResult } from '../shared/interfaces/songDetails.interface'
/**
* Handles the 'batch-song-details' event.
*/
export default class BatchSongDetailsHandler implements IPCInvokeHandler<'batch-song-details'> {
event: 'batch-song-details' = 'batch-song-details'
// TODO: add method documentation
/**
* @returns an array of all the chart versions with a songID found in `songIDs`.
*/
async handler(songIDs: number[]) {
const db = await Database.getInstance()
return db.sendQuery(this.getVersionQuery(songIDs)) as Promise<VersionResult[]>
}
/**
* @returns a database query that returns all the chart versions with a songID found in `songIDs`.
*/
private getVersionQuery(songIDs: number[]) {
return `
SELECT *

View File

@@ -1,51 +0,0 @@
import { exists as _exists, mkdir as _mkdir, readFile as _readFile } from 'fs'
import { dataPath, tempPath, themesPath, settingsPath } from '../shared/Paths'
import { promisify } from 'util'
import { IPCInvokeHandler } from '../shared/IPCHandler'
import { defaultSettings, Settings } from '../shared/Settings'
import SaveSettingsHandler from './SaveSettingsHandler.ipc'
const exists = promisify(_exists)
const mkdir = promisify(_mkdir)
const readFile = promisify(_readFile)
export default class InitSettingsHandler implements IPCInvokeHandler<'init-settings'> {
event: 'init-settings' = 'init-settings'
private static settings: Settings
static async getSettings() {
if (this.settings == undefined) {
this.settings = await InitSettingsHandler.initSettings()
}
return this.settings
}
async handler() {
return InitSettingsHandler.getSettings()
}
private static async initSettings(): Promise<Settings> {
try {
// Create data directories if they don't exists
for (const path of [dataPath, tempPath, themesPath]) {
if (!await exists(path)) {
await mkdir(path)
}
}
// Read/create settings
if (await exists(settingsPath)) {
return JSON.parse(await readFile(settingsPath, 'utf8'))
} else {
await SaveSettingsHandler.saveSettings(defaultSettings)
return defaultSettings
}
} catch (e) {
console.error('Failed to initialize settings!')
console.error('Several actions (including downloading) will unexpectedly fail')
console.error(e)
return defaultSettings
}
}
}

View File

@@ -1,20 +0,0 @@
import { writeFile as _writeFile } from 'fs'
import { IPCEmitHandler } from '../shared/IPCHandler'
import { Settings } from '../shared/Settings'
import { promisify } from 'util'
import { settingsPath } from '../shared/Paths'
const writeFile = promisify(_writeFile)
export default class SaveSettingsHandler implements IPCEmitHandler<'update-settings'> {
event: 'update-settings' = 'update-settings'
handler(settings: Settings) {
SaveSettingsHandler.saveSettings(settings)
}
static async saveSettings(settings: Settings) {
const settingsJSON = JSON.stringify(settings, undefined, 2)
await writeFile(settingsPath, settingsJSON, 'utf8')
}
}

View File

@@ -3,23 +3,34 @@ import Database from '../shared/Database'
import { SongSearch, SearchType, SongResult } from '../shared/interfaces/search.interface'
import { escape } from 'mysql'
/**
* Handles the 'song-search' event.
*/
export default class SearchHandler implements IPCInvokeHandler<'song-search'> {
event: 'song-search' = 'song-search'
// TODO: add method documentation
/**
* @returns the top 20 songs that match `search`.
*/
async handler(search: SongSearch) {
const db = await Database.getInstance()
return db.sendQuery(this.getSearchQuery(search)) as Promise<SongResult[]>
}
/**
* @returns a database query that returns the type of results expected by `search.type`.
*/
private getSearchQuery(search: SongSearch) {
switch(search.type) {
switch (search.type) {
case SearchType.Any: return this.getGeneralSearchQuery(search.query)
default: return '<<<ERROR>>>' // TODO: add more search types
}
}
/**
* @returns a database query that returns the top 20 songs that match `search`.
*/
private getGeneralSearchQuery(searchString: string) {
return `
SELECT id, name, artist, album, genre, year

View File

@@ -0,0 +1,88 @@
import { exists as _exists, mkdir as _mkdir, readFile as _readFile, writeFile as _writeFile } from 'fs'
import { dataPath, tempPath, themesPath, settingsPath } from '../shared/Paths'
import { promisify } from 'util'
import { IPCInvokeHandler, IPCEmitHandler } from '../shared/IPCHandler'
import { defaultSettings, Settings } from '../shared/Settings'
const exists = promisify(_exists)
const mkdir = promisify(_mkdir)
const readFile = promisify(_readFile)
const writeFile = promisify(_writeFile)
let settings: Settings
/**
* Handles the 'get-settings' event.
*/
export class GetSettingsHandler implements IPCInvokeHandler<'get-settings'> {
event: 'get-settings' = 'get-settings'
/**
* @returns the current settings oject, or default settings if they couldn't be loaded.
*/
handler() {
return GetSettingsHandler.getSettings()
}
/**
* @returns the current settings oject, or default settings if they couldn't be loaded.
*/
static getSettings() {
if (settings == undefined) {
return defaultSettings
} else {
return settings
}
}
/**
* If data directories don't exist, creates them and saves the default settings.
* Otherwise, loads user settings from data directories.
* If this process fails, default settings are used.
*/
static async initSettings() {
try {
// Create data directories if they don't exists
for (const path of [dataPath, tempPath, themesPath]) {
if (!await exists(path)) {
await mkdir(path)
}
}
// Read/create settings
if (await exists(settingsPath)) {
settings = JSON.parse(await readFile(settingsPath, 'utf8'))
} else {
await SetSettingsHandler.saveSettings(defaultSettings)
settings = defaultSettings
}
} catch (e) {
console.error('Failed to initialize settings! Default settings will be used.')
console.error(e)
settings = defaultSettings
}
}
}
/**
* Handles the 'set-settings' event.
*/
export class SetSettingsHandler implements IPCEmitHandler<'set-settings'> {
event: 'set-settings' = 'set-settings'
/**
* Updates Bridge's settings object to `newSettings` and saves them to Bridge's data directories.
*/
handler(newSettings: Settings) {
settings = newSettings
SetSettingsHandler.saveSettings(settings)
}
/**
* Saves `settings` to Bridge's data directories.
*/
static async saveSettings(settings: Settings) {
const settingsJSON = JSON.stringify(settings, undefined, 2)
await writeFile(settingsPath, settingsJSON, 'utf8')
}
}

View File

@@ -2,16 +2,24 @@ import { IPCInvokeHandler } from '../shared/IPCHandler'
import Database from '../shared/Database'
import { VersionResult } from '../shared/interfaces/songDetails.interface'
/**
* Handles the 'song-details' event.
*/
export default class SongDetailsHandler implements IPCInvokeHandler<'song-details'> {
event: 'song-details' = 'song-details'
// TODO: add method documentation
/**
* @returns the chart versions with `songID`.
*/
async handler(songID: number) {
const db = await Database.getInstance()
return db.sendQuery(this.getVersionQuery(songID)) as Promise<VersionResult[]>
}
/**
* @returns a database query that returns the chart versions with `songID`.
*/
private getVersionQuery(songID: number) {
return `
SELECT *

View File

@@ -4,11 +4,12 @@ import { createHash, randomBytes as _randomBytes } from 'crypto'
import { tempPath } from '../../shared/Paths'
import { promisify } from 'util'
import { join } from 'path'
import { Download, NewDownload, DownloadProgress } from '../../shared/interfaces/download.interface'
import { Download, DownloadProgress } from '../../shared/interfaces/download.interface'
import { emitIPCEvent } from '../../main'
import { mkdir as _mkdir } from 'fs'
import { FileExtractor } from './FileExtractor'
import { sanitizeFilename, interpolate } from '../../shared/UtilFunctions'
import { GetSettingsHandler } from '../SettingsHandler.ipc'
const randomBytes = promisify(_randomBytes)
const mkdir = promisify(_mkdir)
@@ -19,6 +20,7 @@ export class DownloadHandler implements IPCEmitHandler<'download'> {
// TODO: replace needle with got (for cancel() method) (if before-headers event is possible?)
downloadCallbacks: { [versionID: number]: { cancel: () => void, retry: () => void, continue: () => void } } = {}
private allFilesProgress = 0
async handler(data: Download) {
switch (data.action) {
@@ -27,8 +29,9 @@ export class DownloadHandler implements IPCEmitHandler<'download'> {
case 'continue': this.downloadCallbacks[data.versionID].continue(); return
case 'add': this.downloadCallbacks[data.versionID] = { cancel: () => { }, retry: () => { }, continue: () => { } }
}
// after this point, (data.action == add), so data.data should be defined
// data.action == add; data.data should be defined
// Initialize download object
const download: DownloadProgress = {
versionID: data.versionID,
title: `${data.data.avTagName} - ${data.data.artist}`,
@@ -37,102 +40,146 @@ export class DownloadHandler implements IPCEmitHandler<'download'> {
percent: 0,
type: 'good'
}
const randomString = (await randomBytes(5)).toString('hex')
const chartPath = join(tempPath, `chart_${randomString}`)
await mkdir(chartPath)
let allFilesProgress = 0
// Only iterate over the keys in data.links that have link values (not hashes)
const fileKeys = Object.keys(data.data.links).filter(link => data.data.links[link].includes('.'))
const individualFileProgressPortion = 80 / fileKeys.length
for (let i = 0; i < fileKeys.length; i++) {
const typeHash = createHash('md5').update(data.data.links[fileKeys[i]]).digest('hex')
const downloader = await FileDownloader.asyncConstructor(data.data.links[fileKeys[i]], chartPath, fileKeys.length, data.data.links[typeHash])
this.downloadCallbacks[data.versionID].cancel = () => downloader.cancelDownload() // Make cancel button cancel this download
let fileProgress = 0
let initialWaitTime: number
downloader.on('wait', (waitTime) => {
download.header = `[${fileKeys[i]}] (file ${i + 1}/${fileKeys.length})`
download.description = `Waiting for Google rate limit... (${waitTime}s)`
download.type = 'wait'
initialWaitTime = waitTime
})
downloader.on('waitProgress', (secondsRemaining) => {
download.description = `Waiting for Google rate limit... (${secondsRemaining}s)`
fileProgress = interpolate(secondsRemaining, initialWaitTime, 0, 0, individualFileProgressPortion / 2)
download.percent = allFilesProgress + fileProgress
download.type = 'wait'
emitIPCEvent('download-updated', download)
})
downloader.on('request', () => {
download.description = `Sending request...`
fileProgress = individualFileProgressPortion / 2
download.percent = allFilesProgress + fileProgress
download.type = 'good'
emitIPCEvent('download-updated', download)
})
downloader.on('warning', (continueAnyway) => {
download.description = 'WARNING'
this.downloadCallbacks[data.versionID].continue = continueAnyway
download.type = 'warning'
emitIPCEvent('download-updated', download)
})
let filesize = -1
downloader.on('download', (filename, _filesize) => {
download.header = `[${filename}] (file ${i + 1}/${fileKeys.length})`
if (_filesize != undefined) {
filesize = _filesize
download.description = `Downloading... (0%)`
} else {
download.description = `Downloading... (0 MB)`
}
download.type = 'good'
emitIPCEvent('download-updated', download)
})
downloader.on('downloadProgress', (bytesDownloaded) => {
if (filesize != -1) {
download.description = `Downloading... (${Math.round(1000 * bytesDownloaded / filesize) / 10}%)`
fileProgress = interpolate(bytesDownloaded, 0, filesize, individualFileProgressPortion / 2, individualFileProgressPortion)
download.percent = allFilesProgress + fileProgress
} else {
download.description = `Downloading... (${Math.round(bytesDownloaded / 1e+5) / 10} MB)`
download.percent = allFilesProgress + fileProgress
}
download.type = 'good'
emitIPCEvent('download-updated', download)
})
downloader.on('error', (error, retry) => {
download.header = error.header
download.description = error.body
download.type = 'error'
this.downloadCallbacks[data.versionID].retry = retry
emitIPCEvent('download-updated', download)
})
// Wait for the 'complete' event before moving on to another file download
await new Promise<void>(resolve => {
downloader.on('complete', () => {
emitIPCEvent('download-updated', download)
allFilesProgress += individualFileProgressPortion
resolve()
})
downloader.beginDownload()
})
// Create a temporary folder to store the downloaded files
let chartPath: string
try {
chartPath = await this.createDownloadFolder()
} catch (e) {
download.header = 'Access Error'
download.description = e.message
download.type = 'error'
this.downloadCallbacks[data.versionID].retry = () => { this.handler(data) }
emitIPCEvent('download-updated', download)
return
}
// For each actual download link in <data.data.links>, download the file to <chartPath>
// Only iterate over the keys in data.links that have link values (not hashes)
const fileKeys = Object.keys(data.data.links).filter(link => data.data.links[link].includes('.'))
for (let i = 0; i < fileKeys.length; i++) {
// INITIALIZE DOWNLOADER
// <data.data.links[typeHash]> stores the expected hash value found in the download header
const typeHash = createHash('md5').update(data.data.links[fileKeys[i]]).digest('hex')
const downloader = new FileDownloader(data.data.links[fileKeys[i]], chartPath, fileKeys.length, data.data.links[typeHash])
this.downloadCallbacks[data.versionID].cancel = () => downloader.cancelDownload() // Make cancel button cancel this download
const downloadComplete = this.addDownloadEventListeners(downloader, download, fileKeys, i)
// DOWNLOAD THE NEXT FILE
downloader.beginDownload()
await downloadComplete // Wait for this download to finish before downloading the next file
}
// INITIALIZE FILE EXTRACTOR
const destinationFolderName = sanitizeFilename(`${data.data.artist} - ${data.data.avTagName} (${data.data.charter})`)
const extractor = new FileExtractor(chartPath, fileKeys.includes('archive'), destinationFolderName)
this.downloadCallbacks[data.versionID].cancel = () => extractor.cancelExtract()
this.downloadCallbacks[data.versionID].cancel = () => extractor.cancelExtract() // Make cancel button cancel the file extraction
this.addExtractorEventListeners(extractor, download)
// EXTRACT THE DOWNLOADED ARCHIVE
extractor.beginExtract()
}
private async createDownloadFolder() {
let retryCount = 0
while (true) {
const randomString = (await randomBytes(5)).toString('hex')
const chartPath = join(tempPath, `chart_${randomString}`)
try {
await mkdir(chartPath)
return chartPath
} catch (e) {
if (retryCount > 5) {
throw new Error(`Bridge was unable to create a directory at [${chartPath}]`)
} else {
console.log(`Error creating folder [${chartPath}], retrying with a different folder...`)
retryCount++
}
}
}
}
private addDownloadEventListeners(downloader: FileDownloader, download: DownloadProgress, fileKeys: string[], i: number) {
const individualFileProgressPortion = 80 / fileKeys.length
let fileProgress = 0
downloader.on('wait', (waitTime) => {
download.header = `[${fileKeys[i]}] (file ${i + 1}/${fileKeys.length})`
download.description = `Waiting for Google rate limit... (${waitTime}s)`
download.type = 'wait'
})
downloader.on('waitProgress', (secondsRemaining, initialWaitTime) => {
download.description = `Waiting for Google rate limit... (${secondsRemaining}s)`
fileProgress = interpolate(secondsRemaining, initialWaitTime, 0, 0, individualFileProgressPortion / 2)
console.log(`${initialWaitTime} ... ${secondsRemaining} ... 0`)
download.percent = this.allFilesProgress + fileProgress
download.type = 'wait'
emitIPCEvent('download-updated', download)
})
downloader.on('request', () => {
download.description = `Sending request...`
fileProgress = individualFileProgressPortion / 2
download.percent = this.allFilesProgress + fileProgress
download.type = 'good'
emitIPCEvent('download-updated', download)
})
downloader.on('warning', (continueAnyway) => {
download.description = 'WARNING'
this.downloadCallbacks[download.versionID].continue = continueAnyway
download.type = 'warning'
emitIPCEvent('download-updated', download)
})
let filesize = -1
downloader.on('download', (filename, _filesize) => {
download.header = `[${filename}] (file ${i + 1}/${fileKeys.length})`
if (_filesize != undefined) {
filesize = _filesize
download.description = `Downloading... (0%)`
} else {
download.description = `Downloading... (0 MB)`
}
download.type = 'good'
emitIPCEvent('download-updated', download)
})
downloader.on('downloadProgress', (bytesDownloaded) => {
if (filesize != -1) {
download.description = `Downloading... (${Math.round(1000 * bytesDownloaded / filesize) / 10}%)`
fileProgress = interpolate(bytesDownloaded, 0, filesize, individualFileProgressPortion / 2, individualFileProgressPortion)
download.percent = this.allFilesProgress + fileProgress
} else {
download.description = `Downloading... (${Math.round(bytesDownloaded / 1e+5) / 10} MB)`
download.percent = this.allFilesProgress + fileProgress
}
download.type = 'good'
emitIPCEvent('download-updated', download)
})
downloader.on('error', (error, retry) => {
download.header = error.header
download.description = error.body
download.type = 'error'
this.downloadCallbacks[download.versionID].retry = retry
emitIPCEvent('download-updated', download)
})
// Returns a promise that resolves when the download is finished
return new Promise<void>(resolve => {
downloader.on('complete', () => {
emitIPCEvent('download-updated', download)
this.allFilesProgress += individualFileProgressPortion
resolve()
})
})
}
private addExtractorEventListeners(extractor: FileExtractor, download: DownloadProgress) {
let archive = ''
extractor.on('extract', (filename) => {
archive = filename
download.header = `[${archive}]`
@@ -169,10 +216,8 @@ export class DownloadHandler implements IPCEmitHandler<'download'> {
download.header = error.header
download.description = error.body
download.type = 'error'
this.downloadCallbacks[data.versionID].retry = retry
this.downloadCallbacks[download.versionID].retry = retry
emitIPCEvent('download-updated', download)
})
extractor.beginExtract()
}
}

View File

@@ -2,12 +2,11 @@ import { generateUUID, sanitizeFilename } from '../../shared/UtilFunctions'
import * as fs from 'fs'
import * as path from 'path'
import * as needle from 'needle'
import InitSettingsHandler from '../InitSettingsHandler.ipc'
import { Settings } from 'src/electron/shared/Settings'
import { GetSettingsHandler } from '../SettingsHandler.ipc'
type EventCallback = {
'wait': (waitTime: number) => void
'waitProgress': (secondsRemaining: number) => void
'waitProgress': (secondsRemaining: number, initialWaitTime: number) => void
'request': () => void
'warning': (continueAnyway: () => void) => void
'download': (filename: string, filesize?: number) => void
@@ -20,43 +19,41 @@ type Callbacks = { [E in keyof EventCallback]: EventCallback[E] }
export type DownloadError = { header: string, body: string }
export class FileDownloader {
private RATE_LIMIT_DELAY: number
private readonly RETRY_MAX = 2
private static fileQueue: { // Stores the overall order that files should be downloaded
destinationFolder: string
fileCount: number
clock?: () => void
}[] = []
}[]
private static waitTime: number
private static settings: Settings
private callbacks = {} as Callbacks
private retryCount: number
private wasCanceled = false
private constructor(private url: string, private destinationFolder: string, private numFiles: number, private expectedHash?: string) { }
static async asyncConstructor(url: string, destinationFolder: string, numFiles: number, expectedHash?: string) {
const downloader = new FileDownloader(url, destinationFolder, numFiles, expectedHash)
if (FileDownloader.settings == undefined) {
await downloader.firstInit()
constructor(private url: string, private destinationFolder: string, private numFiles: number, private expectedHash?: string) {
if (FileDownloader.fileQueue == undefined) {
// First initialization
FileDownloader.fileQueue = []
let lastRateLimitDelay = GetSettingsHandler.getSettings().rateLimitDelay
FileDownloader.waitTime = 0
setInterval(() => {
if (FileDownloader.waitTime > 0) { // Update current countdown if this setting changes
let newRateLimitDelay = GetSettingsHandler.getSettings().rateLimitDelay
if (newRateLimitDelay != lastRateLimitDelay) {
FileDownloader.waitTime -= Math.min(lastRateLimitDelay - newRateLimitDelay, FileDownloader.waitTime - 1)
lastRateLimitDelay = newRateLimitDelay
}
FileDownloader.waitTime--
}
FileDownloader.fileQueue.forEach(download => { if (download.clock != undefined) download.clock() })
if (FileDownloader.waitTime <= 0 && FileDownloader.fileQueue.length != 0) {
FileDownloader.waitTime = GetSettingsHandler.getSettings().rateLimitDelay
}
}, 1000)
}
return downloader
}
async firstInit() {
FileDownloader.settings = await InitSettingsHandler.getSettings()
FileDownloader.waitTime = 0
setInterval(() => {
if (FileDownloader.waitTime > 0) {
FileDownloader.waitTime--
}
FileDownloader.fileQueue.forEach(download => { if (download.clock != undefined) download.clock() })
if (FileDownloader.waitTime == 0 && FileDownloader.fileQueue.length != 0) {
FileDownloader.waitTime = this.RATE_LIMIT_DELAY
}
}, 1000)
}
/**
* Calls <callback> when <event> fires.
* @param event The event to listen for.
@@ -72,7 +69,7 @@ export class FileDownloader {
*/
beginDownload() {
// Check that the library folder has been specified
if (FileDownloader.settings.libraryPath == undefined) {
if (GetSettingsHandler.getSettings().libraryPath == undefined) {
this.callbacks.error({ header: 'Library folder not specified', body: 'Please go to the settings to set your library folder.' }, () => this.beginDownload())
return
}
@@ -82,12 +79,16 @@ export class FileDownloader {
this.requestDownload()
return
}
// The starting point of a progress bar should be recalculated each clock cycle
// It will be what it would have been if rateLimitDelay was that value the entire time
this.initWaitTime()
let queueWaitTime = this.getQueueWaitTime()
this.callbacks.wait(queueWaitTime + FileDownloader.waitTime)
if (queueWaitTime + FileDownloader.waitTime == 0) {
FileDownloader.waitTime = this.RATE_LIMIT_DELAY
// This is the number of seconds that had elapsed since the last file download (at the time of starting this download)
const initialTimeSinceLastDownload = GetSettingsHandler.getSettings().rateLimitDelay - FileDownloader.waitTime
const initialQueueCount = this.getQueueCount()
let waitTime = this.getWaitTime(initialTimeSinceLastDownload, initialQueueCount)
this.callbacks.wait(waitTime)
if (waitTime == 0) {
FileDownloader.waitTime = GetSettingsHandler.getSettings().rateLimitDelay
this.requestDownload()
return
}
@@ -95,17 +96,21 @@ export class FileDownloader {
const fileQueue = FileDownloader.fileQueue.find(queue => queue.destinationFolder == this.destinationFolder)
fileQueue.clock = () => {
if (this.wasCanceled) { this.removeFromQueue(); return } // CANCEL POINT
queueWaitTime = this.getQueueWaitTime()
if (queueWaitTime + FileDownloader.waitTime == 0) {
waitTime = this.getWaitTime(GetSettingsHandler.getSettings().rateLimitDelay - FileDownloader.waitTime, this.getQueueCount())
if (waitTime == 0) {
this.requestDownload()
fileQueue.clock = undefined
}
this.callbacks.waitProgress(queueWaitTime + FileDownloader.waitTime)
this.callbacks.waitProgress(waitTime, this.getWaitTime(initialTimeSinceLastDownload, this.getQueueCount()))
}
}
private getWaitTime(timeSinceLastDownload: number, queueCount: number) {
const rateLimitDelay = GetSettingsHandler.getSettings().rateLimitDelay
return (queueCount * rateLimitDelay) + Math.max(0, rateLimitDelay - timeSinceLastDownload)
}
private initWaitTime() {
this.RATE_LIMIT_DELAY = FileDownloader.settings.rateLimitDelay
this.retryCount = 0
const entry = FileDownloader.fileQueue.find(entry => entry.destinationFolder == this.destinationFolder)
if (entry == undefined) {
@@ -117,7 +122,7 @@ export class FileDownloader {
/**
* Returns the number of files in front of this file in the fileQueue
*/
private getQueueWaitTime() {
private getQueueCount() {
let fileCount = 0
for (let entry of FileDownloader.fileQueue) {
if (entry.destinationFolder != this.destinationFolder) {
@@ -127,7 +132,7 @@ export class FileDownloader {
}
}
return fileCount * this.RATE_LIMIT_DELAY
return fileCount
}
private removeFromQueue() {
@@ -207,9 +212,9 @@ export class FileDownloader {
req.on('data', data => virusScanHTML += data)
req.on('done', (err: Error) => {
if (!err) {
const confirmTokenRegex = /confirm=([0-9A-Za-z\-_]+)&/g
const confirmTokenResults = confirmTokenRegex.exec(virusScanHTML)
if (confirmTokenResults != null) {
try {
const confirmTokenRegex = /confirm=([0-9A-Za-z\-_]+)&/g
const confirmTokenResults = confirmTokenRegex.exec(virusScanHTML)
const confirmToken = confirmTokenResults[1]
const downloadID = this.url.substr(this.url.indexOf('id=') + 'id='.length)
this.url = `https://drive.google.com/uc?confirm=${confirmToken}&id=${downloadID}`
@@ -217,7 +222,7 @@ export class FileDownloader {
const NID = /NID=([^;]*);/.exec(cookieHeader)[1].replace('=', '%')
const newHeader = `download_warning_${warningCode}=${confirmToken}; NID=${NID}`
this.requestDownload(newHeader)
} else {
} catch(e) {
this.callbacks.error({ header: 'Invalid response', body: 'Download server returned HTML instead of a file.' }, () => this.beginDownload())
}
} else {

View File

@@ -6,7 +6,7 @@ import { join, extname } from 'path'
import * as node7z from 'node-7z'
import * as zipBin from '7zip-bin'
import * as unrarjs from 'node-unrar-js'
import InitSettingsHandler from '../InitSettingsHandler.ipc'
import { GetSettingsHandler } from '../SettingsHandler.ipc'
const readdir = promisify(_readdir)
const unlink = promisify(_unlink)
@@ -45,7 +45,7 @@ export class FileExtractor {
* Starts the chart extraction process.
*/
async beginExtract() {
this.libraryFolder = (await InitSettingsHandler.getSettings()).libraryPath
this.libraryFolder = (await GetSettingsHandler.getSettings()).libraryPath
const files = await readdir(this.sourceFolder)
if (this.isArchive) {
this.extract(files[0])