Major code rework
This commit is contained in:
parent
5d29424c3a
commit
87164f0644
8
index.js
8
index.js
@ -10,13 +10,15 @@ const argv = yargs(hideBin(process.argv)).options({
|
||||
host: { type: 'string', default: '127.0.0.1' },
|
||||
dev: { type: 'boolean', default: false}
|
||||
}).argv
|
||||
const server = require('./server/dist/app.js')
|
||||
const { DeemixServer }= require('./server/dist/app.js')
|
||||
|
||||
const PORT = process.env.DEEMIX_SERVER_PORT || argv.port
|
||||
|
||||
process.env.DEEMIX_SERVER_PORT = PORT
|
||||
process.env.DEEMIX_HOST = argv.host
|
||||
|
||||
const server = new DeemixServer(argv.host, PORT)
|
||||
server.init()
|
||||
|
||||
let win
|
||||
const windowState = new WindowStateManager('mainWindow', {
|
||||
defaultWidth: 800,
|
||||
@ -91,7 +93,7 @@ app.on('window-all-closed', () => {
|
||||
})
|
||||
|
||||
ipcMain.on('openDownloadsFolder', (event)=>{
|
||||
const { downloadLocation } = server.getSettings().settings
|
||||
const { downloadLocation } = server.deemixApp.getSettings().settings
|
||||
shell.openPath(downloadLocation)
|
||||
})
|
||||
|
||||
|
@ -1,2 +1 @@
|
||||
bin/www
|
||||
dist/
|
||||
|
2
server/dist/app.js
vendored
2
server/dist/app.js
vendored
File diff suppressed because one or more lines are too long
426
server/dist/app.js.LICENSE.txt
vendored
426
server/dist/app.js.LICENSE.txt
vendored
@ -1,426 +0,0 @@
|
||||
/*!
|
||||
* Connect - session - Cookie
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Connect - session - Session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Connect - session - Store
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Copyright (c) 2015, Salesforce.com, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Salesforce.com nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Copyright (c) 2018, Salesforce.com, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Salesforce.com nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Prototype.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* accepts
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* basic-auth
|
||||
* Copyright(c) 2013 TJ Holowaychuk
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2015-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* body-parser
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* body-parser
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* bytes
|
||||
* Copyright(c) 2012-2014 TJ Holowaychuk
|
||||
* Copyright(c) 2015 Jed Watson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* content-disposition
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* content-type
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* cookie
|
||||
* Copyright(c) 2012-2014 Roman Shtylman
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* cookie-parser
|
||||
* Copyright(c) 2014 TJ Holowaychuk
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* depd
|
||||
* Copyright(c) 2014 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* depd
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* depd
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* depd
|
||||
* Copyright(c) 2014-2018 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* depd
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* destroy
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* ee-first
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* encodeurl
|
||||
* Copyright(c) 2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* escape-html
|
||||
* Copyright(c) 2012-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2015 Andreas Lubbe
|
||||
* Copyright(c) 2015 Tiancheng "Timothy" Gu
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* etag
|
||||
* Copyright(c) 2014-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* express-session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* express-session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* finalhandler
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* forwarded
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* fresh
|
||||
* Copyright(c) 2012 TJ Holowaychuk
|
||||
* Copyright(c) 2016-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* http-errors
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* media-typer
|
||||
* Copyright(c) 2014 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* memorystore
|
||||
* Copyright(c) 2020 Rocco Musolino <@roccomuso>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* merge-descriptors
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* methods
|
||||
* Copyright(c) 2013-2014 TJ Holowaychuk
|
||||
* Copyright(c) 2015-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* mime-db
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* mime-types
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* morgan
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* negotiator
|
||||
* Copyright(c) 2012 Federico Romero
|
||||
* Copyright(c) 2012-2014 Isaac Z. Schlueter
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* on-finished
|
||||
* Copyright(c) 2013 Jonathan Ong
|
||||
* Copyright(c) 2014 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* on-headers
|
||||
* Copyright(c) 2014 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* parseurl
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* proxy-addr
|
||||
* Copyright(c) 2014-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* random-bytes
|
||||
* Copyright(c) 2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* range-parser
|
||||
* Copyright(c) 2012-2014 TJ Holowaychuk
|
||||
* Copyright(c) 2015-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* raw-body
|
||||
* Copyright(c) 2013-2014 Jonathan Ong
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* send
|
||||
* Copyright(c) 2012 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* serve-static
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* statuses
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* toidentifier
|
||||
* Copyright(c) 2016 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* type-is
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* uid-safe
|
||||
* Copyright(c) 2014 Jonathan Ong
|
||||
* Copyright(c) 2015-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* unpipe
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* vary
|
||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
|
||||
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
@ -1,67 +1,374 @@
|
||||
import http from 'http'
|
||||
import express, { Application } from 'express'
|
||||
import { Server as WsServer } from 'ws'
|
||||
import yargs from 'yargs'
|
||||
import initDebug from 'debug'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
import fs from 'fs'
|
||||
import { sep } from 'path'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
// @ts-expect-error
|
||||
import deemix from 'deemix'
|
||||
import got from 'got'
|
||||
import { Settings, Listener } from './types'
|
||||
import { NotLoggedIn } from './helpers/errors'
|
||||
|
||||
import { registerMiddlewares } from './middlewares'
|
||||
import { GUI_PACKAGE } from './helpers/paths'
|
||||
|
||||
import indexRouter from './routes'
|
||||
// Types
|
||||
const Downloader = deemix.downloader.Downloader
|
||||
const { Single, Collection, Convertable } = deemix.types.downloadObjects
|
||||
|
||||
import { normalizePort } from './helpers/port'
|
||||
import { getErrorCb, getListeningCb } from './helpers/server-callbacks'
|
||||
import { registerApis } from './routes/api/register'
|
||||
import { registerWebsocket } from './websocket'
|
||||
import type { Arguments } from './types'
|
||||
import { consoleInfo } from './helpers/errors'
|
||||
// Functions
|
||||
export const getAccessToken = deemix.utils.deezer.getAccessToken
|
||||
export const getArlFromAccessToken = deemix.utils.deezer.getArlFromAccessToken
|
||||
|
||||
export { getSettings } from './main'
|
||||
// Constants
|
||||
export const configFolder: string = deemix.utils.localpaths.getConfigFolder()
|
||||
export const defaultSettings: Settings = deemix.settings.DEFAULTS
|
||||
export const deemixVersion = require('../node_modules/deemix/package.json').version
|
||||
const currentVersionTemp = JSON.parse(String(fs.readFileSync(GUI_PACKAGE))).version
|
||||
export const currentVersion = currentVersionTemp === '0.0.0' ? 'continuous' : currentVersionTemp
|
||||
|
||||
// TODO: Remove type assertion while keeping correct types
|
||||
const argv = yargs(hideBin(process.argv)).options({
|
||||
port: { type: 'string', default: '6595' },
|
||||
host: { type: 'string', default: '127.0.0.1' }
|
||||
}).argv as Arguments
|
||||
export const sessionDZ: any = {}
|
||||
|
||||
const DEEMIX_SERVER_PORT = normalizePort(process.env.DEEMIX_SERVER_PORT ?? argv.port)
|
||||
const DEEMIX_HOST = process.env.DEEMIX_HOST ?? argv.host
|
||||
export class DeemixApp {
|
||||
queueOrder: string[]
|
||||
queue: any
|
||||
currentJob: any
|
||||
|
||||
const debug = initDebug('deemix-gui:server')
|
||||
export const wss = new WsServer({ noServer: true })
|
||||
const app: Application = express()
|
||||
const server = http.createServer(app)
|
||||
deezerAvailable: boolean | null
|
||||
latestVersion: string | null
|
||||
|
||||
/* === Middlewares === */
|
||||
registerMiddlewares(app)
|
||||
plugins: any
|
||||
settings: any
|
||||
|
||||
/* === Routes === */
|
||||
app.use('/', indexRouter)
|
||||
listener: Listener
|
||||
|
||||
/* === APIs === */
|
||||
registerApis(app)
|
||||
constructor(listener: Listener) {
|
||||
this.settings = deemix.settings.load(configFolder)
|
||||
|
||||
/* === Config === */
|
||||
app.set('port', DEEMIX_SERVER_PORT)
|
||||
this.queueOrder = []
|
||||
this.queue = {}
|
||||
this.currentJob = null
|
||||
|
||||
/* === Server port === */
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
server.listen({ port: DEEMIX_SERVER_PORT, host: DEEMIX_HOST })
|
||||
this.plugins = {
|
||||
// eslint-disable-next-line new-cap
|
||||
spotify: new deemix.plugins.spotify()
|
||||
}
|
||||
this.deezerAvailable = null
|
||||
this.latestVersion = null
|
||||
this.listener = listener
|
||||
|
||||
this.plugins.spotify.setup()
|
||||
this.restoreQueueFromDisk()
|
||||
}
|
||||
|
||||
async isDeezerAvailable(): Promise<boolean> {
|
||||
if (this.deezerAvailable === null) {
|
||||
let response
|
||||
try {
|
||||
response = await got.get('https://www.deezer.com/', {
|
||||
headers: { Cookie: 'dz_lang=en; Domain=deezer.com; Path=/; Secure; hostOnly=false;' },
|
||||
https: {
|
||||
rejectUnauthorized: false
|
||||
},
|
||||
retry: 5
|
||||
})
|
||||
} catch (e) {
|
||||
console.trace(e)
|
||||
this.deezerAvailable = false
|
||||
return this.deezerAvailable
|
||||
}
|
||||
const title = (response.body.match(/<title[^>]*>([^<]+)<\/title>/)![1] || '').trim()
|
||||
this.deezerAvailable = title !== 'Deezer will soon be available in your country.'
|
||||
}
|
||||
return this.deezerAvailable
|
||||
}
|
||||
|
||||
async getLatestVersion(force = false): Promise<string | null> {
|
||||
if ((this.latestVersion === null || force) && !this.settings.disableUpdateCheck) {
|
||||
let response
|
||||
try {
|
||||
response = await got.get('https://deemix.app/gui/latest', {
|
||||
https: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.trace(e)
|
||||
this.latestVersion = 'NotFound'
|
||||
return this.latestVersion
|
||||
}
|
||||
this.latestVersion = response.body.trim()
|
||||
}
|
||||
return this.latestVersion
|
||||
}
|
||||
|
||||
parseVersion(version: string | null): any {
|
||||
if (version === null || version === 'continuous' || version === 'NotFound') return null
|
||||
try {
|
||||
const matchResult = version.match(/(\d+)\.(\d+)\.(\d+)-r(\d)+\.(.+)/) || []
|
||||
return {
|
||||
year: parseInt(matchResult[1]),
|
||||
month: parseInt(matchResult[2]),
|
||||
day: parseInt(matchResult[3]),
|
||||
revision: parseInt(matchResult[4]),
|
||||
commit: matchResult[5] || ''
|
||||
}
|
||||
} catch (e) {
|
||||
console.trace(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
isUpdateAvailable(): boolean {
|
||||
const currentVersionObj: any = this.parseVersion(currentVersion)
|
||||
const latestVersionObj: any = this.parseVersion(this.latestVersion)
|
||||
if (currentVersionObj === null || latestVersionObj === null) return false
|
||||
if (latestVersionObj.year > currentVersionObj.year) return true
|
||||
if (latestVersionObj.month > currentVersionObj.month) return true
|
||||
if (latestVersionObj.day > currentVersionObj.day) return true
|
||||
if (latestVersionObj.revision > currentVersionObj.revision) return true
|
||||
if (latestVersionObj.commit !== currentVersionObj.commit) return true
|
||||
return false
|
||||
}
|
||||
|
||||
getSettings(): any {
|
||||
return { settings: this.settings, defaultSettings, spotifySettings: this.plugins.spotify.getSettings() }
|
||||
}
|
||||
|
||||
saveSettings(newSettings: any, newSpotifySettings: any) {
|
||||
newSettings.executeCommand = this.settings.executeCommand
|
||||
deemix.settings.save(newSettings, configFolder)
|
||||
this.settings = newSettings
|
||||
this.plugins.spotify.saveSettings(newSpotifySettings)
|
||||
}
|
||||
|
||||
getQueue() {
|
||||
const result: any = {
|
||||
queue: this.queue,
|
||||
queueOrder: this.queueOrder
|
||||
}
|
||||
if (this.currentJob && this.currentJob !== true) {
|
||||
result.current = this.currentJob.downloadObject.getSlimmedDict()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async addToQueue(dz: any, url: string[], bitrate: number) {
|
||||
if (!dz.logged_in) throw new NotLoggedIn()
|
||||
|
||||
let downloadObjs: any[] = []
|
||||
const downloadErrors: any[] = []
|
||||
let link: string = ''
|
||||
const requestUUID = uuidv4()
|
||||
|
||||
if (url.length > 1) {
|
||||
this.listener.send('startGeneratingItems', { uuid: requestUUID, total: url.length })
|
||||
}
|
||||
|
||||
for (let i = 0; i < url.length; i++) {
|
||||
link = url[i]
|
||||
console.log(`Adding ${link} to queue`)
|
||||
let downloadObj
|
||||
try {
|
||||
downloadObj = await deemix.generateDownloadObject(dz, link, bitrate, this.plugins, this.listener)
|
||||
} catch (e) {
|
||||
downloadErrors.push(e)
|
||||
}
|
||||
if (Array.isArray(downloadObj)) {
|
||||
downloadObjs = downloadObjs.concat(downloadObj)
|
||||
} else if (downloadObj) downloadObjs.push(downloadObj)
|
||||
}
|
||||
|
||||
if (downloadErrors.length) {
|
||||
downloadErrors.forEach((e: any) => {
|
||||
if (!e.errid) console.trace(e)
|
||||
this.listener.send('queueError', { link: e.link, error: e.message, errid: e.errid })
|
||||
})
|
||||
}
|
||||
|
||||
if (url.length > 1) {
|
||||
this.listener.send('finishGeneratingItems', { uuid: requestUUID, total: downloadObjs.length })
|
||||
}
|
||||
|
||||
const slimmedObjects: any[] = []
|
||||
|
||||
downloadObjs.forEach((downloadObj: any, pos: number) => {
|
||||
// Check if element is already in queue
|
||||
if (Object.keys(this.queue).includes(downloadObj.uuid)) {
|
||||
this.listener.send('alreadyInQueue', downloadObj.getEssentialDict())
|
||||
delete downloadObjs[pos]
|
||||
return
|
||||
}
|
||||
|
||||
// Save queue status when adding something to the queue
|
||||
if (!fs.existsSync(configFolder + 'queue')) fs.mkdirSync(configFolder + 'queue')
|
||||
|
||||
this.queueOrder.push(downloadObj.uuid)
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(this.queueOrder))
|
||||
this.queue[downloadObj.uuid] = downloadObj.getEssentialDict()
|
||||
this.queue[downloadObj.uuid].status = 'inQueue'
|
||||
|
||||
const savedObject = downloadObj.toDict()
|
||||
savedObject.status = 'inQueue'
|
||||
fs.writeFileSync(configFolder + `queue${sep}${downloadObj.uuid}.json`, JSON.stringify(savedObject))
|
||||
|
||||
slimmedObjects.push(downloadObj.getSlimmedDict())
|
||||
})
|
||||
const isSingleObject = downloadObjs.length === 1
|
||||
if (isSingleObject) this.listener.send('addedToQueue', downloadObjs[0].getSlimmedDict())
|
||||
else this.listener.send('addedToQueue', slimmedObjects)
|
||||
|
||||
this.startQueue(dz)
|
||||
return slimmedObjects
|
||||
}
|
||||
|
||||
async startQueue(dz: any): Promise<any> {
|
||||
do {
|
||||
if (this.currentJob !== null || this.queueOrder.length === 0) {
|
||||
// Should not start another download
|
||||
return null
|
||||
}
|
||||
this.currentJob = true // lock currentJob
|
||||
|
||||
let currentUUID: string
|
||||
do {
|
||||
currentUUID = this.queueOrder.shift() || ''
|
||||
} while (this.queue[currentUUID] === undefined && this.queueOrder.length)
|
||||
this.queue[currentUUID].status = 'downloading'
|
||||
const currentItem: any = JSON.parse(fs.readFileSync(configFolder + `queue${sep}${currentUUID}.json`).toString())
|
||||
let downloadObject: any
|
||||
switch (currentItem.__type__) {
|
||||
case 'Single':
|
||||
downloadObject = new Single(currentItem)
|
||||
break
|
||||
case 'Collection':
|
||||
downloadObject = new Collection(currentItem)
|
||||
break
|
||||
case 'Convertable':
|
||||
downloadObject = new Convertable(currentItem)
|
||||
downloadObject = await this.plugins[downloadObject.plugin].convert(
|
||||
dz,
|
||||
downloadObject,
|
||||
this.settings,
|
||||
this.listener
|
||||
)
|
||||
fs.writeFileSync(
|
||||
configFolder + `queue${sep}${downloadObject.uuid}.json`,
|
||||
JSON.stringify({ ...downloadObject.toDict(), status: 'inQueue' })
|
||||
)
|
||||
break
|
||||
}
|
||||
this.currentJob = new Downloader(dz, downloadObject, this.settings, this.listener)
|
||||
this.listener.send('startDownload', currentUUID)
|
||||
await this.currentJob.start()
|
||||
|
||||
if (!downloadObject.isCanceled) {
|
||||
// Set status
|
||||
if (downloadObject.failed === downloadObject.size && downloadObject.size !== 0) {
|
||||
this.queue[currentUUID].status = 'failed'
|
||||
} else if (downloadObject.failed > 0) {
|
||||
this.queue[currentUUID].status = 'withErrors'
|
||||
} else {
|
||||
this.queue[currentUUID].status = 'completed'
|
||||
}
|
||||
|
||||
const savedObject = downloadObject.getSlimmedDict()
|
||||
savedObject.status = this.queue[currentUUID].status
|
||||
|
||||
// Save queue status
|
||||
this.queue[currentUUID] = savedObject
|
||||
fs.writeFileSync(configFolder + `queue${sep}${currentUUID}.json`, JSON.stringify(savedObject))
|
||||
}
|
||||
console.log(this.queueOrder)
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(this.queueOrder))
|
||||
|
||||
this.currentJob = null
|
||||
} while (this.queueOrder.length)
|
||||
}
|
||||
|
||||
cancelDownload(uuid: string) {
|
||||
if (Object.keys(this.queue).includes(uuid)) {
|
||||
switch (this.queue[uuid].status) {
|
||||
case 'downloading':
|
||||
this.currentJob.downloadObject.isCanceled = true
|
||||
this.listener.send('cancellingCurrentItem', uuid)
|
||||
break
|
||||
case 'inQueue':
|
||||
this.queueOrder.splice(this.queueOrder.indexOf(uuid), 1)
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(this.queueOrder))
|
||||
// break
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
default:
|
||||
// This gets called even in the 'inQueue' case. Is this the expected behaviour? If no, de-comment the break
|
||||
this.listener.send('removedFromQueue', uuid)
|
||||
break
|
||||
}
|
||||
fs.unlinkSync(configFolder + `queue${sep}${uuid}.json`)
|
||||
delete this.queue[uuid]
|
||||
}
|
||||
}
|
||||
|
||||
cancelAllDownloads() {
|
||||
this.queueOrder = []
|
||||
let currentItem: string | null = null
|
||||
Object.values(this.queue).forEach((downloadObject: any) => {
|
||||
if (downloadObject.status === 'downloading') {
|
||||
this.currentJob.downloadObject.isCanceled = true
|
||||
this.listener.send('cancellingCurrentItem', downloadObject.uuid)
|
||||
currentItem = downloadObject.uuid
|
||||
}
|
||||
fs.unlinkSync(configFolder + `queue${sep}${downloadObject.uuid}.json`)
|
||||
delete this.queue[downloadObject.uuid]
|
||||
})
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(this.queueOrder))
|
||||
this.listener.send('removedAllDownloads', currentItem)
|
||||
}
|
||||
|
||||
clearCompletedDownloads() {
|
||||
Object.values(this.queue).forEach((downloadObject: any) => {
|
||||
if (downloadObject.status === 'completed') {
|
||||
fs.unlinkSync(configFolder + `queue${sep}${downloadObject.uuid}.json`)
|
||||
delete this.queue[downloadObject.uuid]
|
||||
}
|
||||
})
|
||||
this.listener.send('removedFinishedDownloads')
|
||||
}
|
||||
|
||||
restoreQueueFromDisk() {
|
||||
if (!fs.existsSync(configFolder + 'queue')) fs.mkdirSync(configFolder + 'queue')
|
||||
const allItems: string[] = fs.readdirSync(configFolder + 'queue')
|
||||
allItems.forEach((filename: string) => {
|
||||
if (filename === 'order.json') {
|
||||
try {
|
||||
this.queueOrder = JSON.parse(fs.readFileSync(configFolder + `queue${sep}order.json`).toString())
|
||||
} catch {
|
||||
this.queueOrder = []
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(this.queueOrder))
|
||||
}
|
||||
} else {
|
||||
let currentItem: any
|
||||
try {
|
||||
currentItem = JSON.parse(fs.readFileSync(configFolder + `queue${sep}${filename}`).toString())
|
||||
} catch {
|
||||
fs.unlinkSync(configFolder + `queue${sep}${filename}`)
|
||||
return
|
||||
}
|
||||
if (currentItem.status === 'inQueue') {
|
||||
let downloadObject: any
|
||||
switch (currentItem.__type__) {
|
||||
case 'Single':
|
||||
downloadObject = new Single(currentItem)
|
||||
break
|
||||
case 'Collection':
|
||||
downloadObject = new Collection(currentItem)
|
||||
break
|
||||
case 'Convertable':
|
||||
downloadObject = new Convertable(currentItem)
|
||||
break
|
||||
}
|
||||
this.queue[downloadObject.uuid] = downloadObject.getEssentialDict()
|
||||
this.queue[downloadObject.uuid].status = 'inQueue'
|
||||
} else {
|
||||
this.queue[currentItem.uuid] = currentItem
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
registerWebsocket(wss)
|
||||
|
||||
/* === Server callbacks === */
|
||||
app.on('mount', a => {
|
||||
console.log(a)
|
||||
})
|
||||
server.on('connect', () => {
|
||||
consoleInfo('Server connected')
|
||||
})
|
||||
server.on('upgrade', (request, socket, head) => {
|
||||
wss.handleUpgrade(request, socket, head, socket => {
|
||||
wss.emit('connection', socket, request)
|
||||
})
|
||||
})
|
||||
server.on('error', getErrorCb(DEEMIX_SERVER_PORT))
|
||||
server.on('listening', getListeningCb(server, debug))
|
||||
|
23
server/src/index.ts
Normal file
23
server/src/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import yargs from 'yargs'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
import type { Arguments } from './types'
|
||||
|
||||
import { DeemixServer } from './server'
|
||||
|
||||
const isModule = process.mainModule && process.mainModule.parent
|
||||
|
||||
if (!isModule) {
|
||||
// TODO: Remove type assertion while keeping correct types
|
||||
const argv = yargs(hideBin(process.argv)).options({
|
||||
port: { type: 'string', default: '6595' },
|
||||
host: { type: 'string', default: '127.0.0.1' }
|
||||
}).argv as Arguments
|
||||
|
||||
const DEEMIX_SERVER_PORT = process.env.DEEMIX_SERVER_PORT ?? argv.port
|
||||
const DEEMIX_HOST = process.env.DEEMIX_HOST ?? argv.host
|
||||
|
||||
const server = new DeemixServer(DEEMIX_HOST, DEEMIX_SERVER_PORT)
|
||||
server.init()
|
||||
}
|
||||
|
||||
export { DeemixServer }
|
@ -1,362 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import { sep } from 'path'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
// @ts-expect-error
|
||||
import deemix from 'deemix'
|
||||
import WebSocket from 'ws'
|
||||
import got from 'got'
|
||||
import { wss } from './app'
|
||||
import { Settings } from './types'
|
||||
import { NotLoggedIn } from './helpers/errors'
|
||||
|
||||
import { GUI_PACKAGE } from './helpers/paths'
|
||||
|
||||
const Downloader = deemix.downloader.Downloader
|
||||
const { Single, Collection, Convertable } = deemix.types.downloadObjects
|
||||
export const defaultSettings: Settings = deemix.settings.DEFAULTS
|
||||
export const configFolder: string = deemix.utils.localpaths.getConfigFolder()
|
||||
export const sessionDZ: any = {}
|
||||
let settings: any = deemix.settings.load(configFolder)
|
||||
|
||||
export const getAccessToken = deemix.utils.deezer.getAccessToken
|
||||
export const getArlFromAccessToken = deemix.utils.deezer.getArlFromAccessToken
|
||||
|
||||
export const deemixVersion = require('../node_modules/deemix/package.json').version
|
||||
const currentVersionTemp = JSON.parse(String(fs.readFileSync(GUI_PACKAGE))).version
|
||||
export const currentVersion = currentVersionTemp === '0.0.0' ? 'continuous' : currentVersionTemp
|
||||
let deezerAvailable: boolean | null = null
|
||||
let latestVersion: string | null = null
|
||||
|
||||
export async function isDeezerAvailable(): Promise<boolean> {
|
||||
if (deezerAvailable === null) {
|
||||
let response
|
||||
try {
|
||||
response = await got.get('https://www.deezer.com/', {
|
||||
headers: { Cookie: 'dz_lang=en; Domain=deezer.com; Path=/; Secure; hostOnly=false;' },
|
||||
https: {
|
||||
rejectUnauthorized: false
|
||||
},
|
||||
retry: 5
|
||||
})
|
||||
} catch (e) {
|
||||
console.trace(e)
|
||||
deezerAvailable = false
|
||||
return deezerAvailable
|
||||
}
|
||||
const title = (response.body.match(/<title[^>]*>([^<]+)<\/title>/)![1] || '').trim()
|
||||
deezerAvailable = title !== 'Deezer will soon be available in your country.'
|
||||
}
|
||||
return deezerAvailable
|
||||
}
|
||||
|
||||
export async function getLatestVersion(force = false): Promise<string | null> {
|
||||
if ((latestVersion === null || force) && !settings.disableUpdateCheck) {
|
||||
let response
|
||||
try {
|
||||
response = await got.get('https://deemix.app/gui/latest', {
|
||||
https: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.trace(e)
|
||||
latestVersion = 'NotFound'
|
||||
return latestVersion
|
||||
}
|
||||
latestVersion = response.body.trim()
|
||||
}
|
||||
return latestVersion
|
||||
}
|
||||
|
||||
function parseVersion(version: string | null): any {
|
||||
if (version === null || version === 'continuous' || version === 'NotFound') return null
|
||||
try {
|
||||
const matchResult = version.match(/(\d+)\.(\d+)\.(\d+)-r(\d)+\.(.+)/) || []
|
||||
return {
|
||||
year: parseInt(matchResult[1]),
|
||||
month: parseInt(matchResult[2]),
|
||||
day: parseInt(matchResult[3]),
|
||||
revision: parseInt(matchResult[4]),
|
||||
commit: matchResult[5] || ''
|
||||
}
|
||||
} catch (e) {
|
||||
console.trace(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function isUpdateAvailable(): boolean {
|
||||
const currentVersionObj: any = parseVersion(currentVersion)
|
||||
const latestVersionObj: any = parseVersion(latestVersion)
|
||||
if (currentVersionObj === null || latestVersionObj === null) return false
|
||||
if (latestVersionObj.year > currentVersionObj.year) return true
|
||||
if (latestVersionObj.month > currentVersionObj.month) return true
|
||||
if (latestVersionObj.day > currentVersionObj.day) return true
|
||||
if (latestVersionObj.revision > currentVersionObj.revision) return true
|
||||
if (latestVersionObj.commit !== currentVersionObj.commit) return true
|
||||
return false
|
||||
}
|
||||
|
||||
export const plugins: any = {
|
||||
// eslint-disable-next-line new-cap
|
||||
spotify: new deemix.plugins.spotify()
|
||||
}
|
||||
plugins.spotify.setup()
|
||||
|
||||
export const listener = {
|
||||
send(key: string, data?: any) {
|
||||
const logLine = deemix.utils.formatListener(key, data)
|
||||
if (logLine) console.log(logLine)
|
||||
if (['downloadInfo', 'downloadWarn'].includes(key)) return
|
||||
wss.clients.forEach(client => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(JSON.stringify({ key, data }))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getSettings(): any {
|
||||
return { settings, defaultSettings, spotifySettings: plugins.spotify.getSettings() }
|
||||
}
|
||||
|
||||
export function saveSettings(newSettings: any, newSpotifySettings: any) {
|
||||
newSettings.executeCommand = settings.executeCommand
|
||||
deemix.settings.save(newSettings, configFolder)
|
||||
settings = newSettings
|
||||
plugins.spotify.saveSettings(newSpotifySettings)
|
||||
}
|
||||
|
||||
let queueOrder: string[] = []
|
||||
const queue: any = {}
|
||||
let currentJob: any = null
|
||||
|
||||
restoreQueueFromDisk()
|
||||
|
||||
export function getQueue() {
|
||||
const result: any = {
|
||||
queue,
|
||||
queueOrder
|
||||
}
|
||||
if (currentJob && currentJob !== true) {
|
||||
result.current = currentJob.downloadObject.getSlimmedDict()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function addToQueue(dz: any, url: string[], bitrate: number) {
|
||||
if (!dz.logged_in) throw new NotLoggedIn()
|
||||
|
||||
let downloadObjs: any[] = []
|
||||
const downloadErrors: any[] = []
|
||||
let link: string = ''
|
||||
const requestUUID = uuidv4()
|
||||
|
||||
if (url.length > 1) {
|
||||
listener.send('startGeneratingItems', { uuid: requestUUID, total: url.length })
|
||||
}
|
||||
|
||||
for (let i = 0; i < url.length; i++) {
|
||||
link = url[i]
|
||||
console.log(`Adding ${link} to queue`)
|
||||
let downloadObj
|
||||
try {
|
||||
downloadObj = await deemix.generateDownloadObject(dz, link, bitrate, plugins, listener)
|
||||
} catch (e) {
|
||||
downloadErrors.push(e)
|
||||
}
|
||||
if (Array.isArray(downloadObj)) {
|
||||
downloadObjs = downloadObjs.concat(downloadObj)
|
||||
} else if (downloadObj) downloadObjs.push(downloadObj)
|
||||
}
|
||||
|
||||
if (downloadErrors.length) {
|
||||
downloadErrors.forEach((e: any) => {
|
||||
if (!e.errid) console.trace(e)
|
||||
listener.send('queueError', { link: e.link, error: e.message, errid: e.errid })
|
||||
})
|
||||
}
|
||||
|
||||
if (url.length > 1) {
|
||||
listener.send('finishGeneratingItems', { uuid: requestUUID, total: downloadObjs.length })
|
||||
}
|
||||
|
||||
const slimmedObjects: any[] = []
|
||||
|
||||
downloadObjs.forEach((downloadObj: any, pos: number) => {
|
||||
// Check if element is already in queue
|
||||
if (Object.keys(queue).includes(downloadObj.uuid)) {
|
||||
listener.send('alreadyInQueue', downloadObj.getEssentialDict())
|
||||
delete downloadObjs[pos]
|
||||
return
|
||||
}
|
||||
|
||||
// Save queue status when adding something to the queue
|
||||
if (!fs.existsSync(configFolder + 'queue')) fs.mkdirSync(configFolder + 'queue')
|
||||
|
||||
queueOrder.push(downloadObj.uuid)
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(queueOrder))
|
||||
queue[downloadObj.uuid] = downloadObj.getEssentialDict()
|
||||
queue[downloadObj.uuid].status = 'inQueue'
|
||||
|
||||
const savedObject = downloadObj.toDict()
|
||||
savedObject.status = 'inQueue'
|
||||
fs.writeFileSync(configFolder + `queue${sep}${downloadObj.uuid}.json`, JSON.stringify(savedObject))
|
||||
|
||||
slimmedObjects.push(downloadObj.getSlimmedDict())
|
||||
})
|
||||
const isSingleObject = downloadObjs.length === 1
|
||||
if (isSingleObject) listener.send('addedToQueue', downloadObjs[0].getSlimmedDict())
|
||||
else listener.send('addedToQueue', slimmedObjects)
|
||||
|
||||
startQueue(dz)
|
||||
return slimmedObjects
|
||||
}
|
||||
|
||||
export async function startQueue(dz: any): Promise<any> {
|
||||
do {
|
||||
if (currentJob !== null || queueOrder.length === 0) {
|
||||
// Should not start another download
|
||||
return null
|
||||
}
|
||||
currentJob = true // lock currentJob
|
||||
|
||||
let currentUUID: string
|
||||
do {
|
||||
currentUUID = queueOrder.shift() || ''
|
||||
} while (queue[currentUUID] === undefined && queueOrder.length)
|
||||
queue[currentUUID].status = 'downloading'
|
||||
const currentItem: any = JSON.parse(fs.readFileSync(configFolder + `queue${sep}${currentUUID}.json`).toString())
|
||||
let downloadObject: any
|
||||
switch (currentItem.__type__) {
|
||||
case 'Single':
|
||||
downloadObject = new Single(currentItem)
|
||||
break
|
||||
case 'Collection':
|
||||
downloadObject = new Collection(currentItem)
|
||||
break
|
||||
case 'Convertable':
|
||||
downloadObject = new Convertable(currentItem)
|
||||
downloadObject = await plugins[downloadObject.plugin].convert(dz, downloadObject, settings, listener)
|
||||
fs.writeFileSync(
|
||||
configFolder + `queue${sep}${downloadObject.uuid}.json`,
|
||||
JSON.stringify({ ...downloadObject.toDict(), status: 'inQueue' })
|
||||
)
|
||||
break
|
||||
}
|
||||
currentJob = new Downloader(dz, downloadObject, settings, listener)
|
||||
listener.send('startDownload', currentUUID)
|
||||
await currentJob.start()
|
||||
|
||||
if (!downloadObject.isCanceled) {
|
||||
// Set status
|
||||
if (downloadObject.failed === downloadObject.size && downloadObject.size !== 0) {
|
||||
queue[currentUUID].status = 'failed'
|
||||
} else if (downloadObject.failed > 0) {
|
||||
queue[currentUUID].status = 'withErrors'
|
||||
} else {
|
||||
queue[currentUUID].status = 'completed'
|
||||
}
|
||||
|
||||
const savedObject = downloadObject.getSlimmedDict()
|
||||
savedObject.status = queue[currentUUID].status
|
||||
|
||||
// Save queue status
|
||||
queue[currentUUID] = savedObject
|
||||
fs.writeFileSync(configFolder + `queue${sep}${currentUUID}.json`, JSON.stringify(savedObject))
|
||||
}
|
||||
console.log(queueOrder)
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(queueOrder))
|
||||
|
||||
currentJob = null
|
||||
} while (queueOrder.length)
|
||||
}
|
||||
|
||||
export function cancelDownload(uuid: string) {
|
||||
if (Object.keys(queue).includes(uuid)) {
|
||||
switch (queue[uuid].status) {
|
||||
case 'downloading':
|
||||
currentJob.downloadObject.isCanceled = true
|
||||
listener.send('cancellingCurrentItem', uuid)
|
||||
break
|
||||
case 'inQueue':
|
||||
queueOrder.splice(queueOrder.indexOf(uuid), 1)
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(queueOrder))
|
||||
// break
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
default:
|
||||
// This gets called even in the 'inQueue' case. Is this the expected behaviour? If no, de-comment the break
|
||||
listener.send('removedFromQueue', uuid)
|
||||
break
|
||||
}
|
||||
fs.unlinkSync(configFolder + `queue${sep}${uuid}.json`)
|
||||
delete queue[uuid]
|
||||
}
|
||||
}
|
||||
|
||||
export function cancelAllDownloads() {
|
||||
queueOrder = []
|
||||
let currentItem: string | null = null
|
||||
Object.values(queue).forEach((downloadObject: any) => {
|
||||
if (downloadObject.status === 'downloading') {
|
||||
currentJob.downloadObject.isCanceled = true
|
||||
listener.send('cancellingCurrentItem', downloadObject.uuid)
|
||||
currentItem = downloadObject.uuid
|
||||
}
|
||||
fs.unlinkSync(configFolder + `queue${sep}${downloadObject.uuid}.json`)
|
||||
delete queue[downloadObject.uuid]
|
||||
})
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(queueOrder))
|
||||
listener.send('removedAllDownloads', currentItem)
|
||||
}
|
||||
|
||||
export function clearCompletedDownloads() {
|
||||
Object.values(queue).forEach((downloadObject: any) => {
|
||||
if (downloadObject.status === 'completed') {
|
||||
fs.unlinkSync(configFolder + `queue${sep}${downloadObject.uuid}.json`)
|
||||
delete queue[downloadObject.uuid]
|
||||
}
|
||||
})
|
||||
listener.send('removedFinishedDownloads')
|
||||
}
|
||||
|
||||
export function restoreQueueFromDisk() {
|
||||
if (!fs.existsSync(configFolder + 'queue')) fs.mkdirSync(configFolder + 'queue')
|
||||
const allItems: string[] = fs.readdirSync(configFolder + 'queue')
|
||||
allItems.forEach((filename: string) => {
|
||||
if (filename === 'order.json') {
|
||||
try {
|
||||
queueOrder = JSON.parse(fs.readFileSync(configFolder + `queue${sep}order.json`).toString())
|
||||
} catch {
|
||||
queueOrder = []
|
||||
fs.writeFileSync(configFolder + `queue${sep}order.json`, JSON.stringify(queueOrder))
|
||||
}
|
||||
} else {
|
||||
let currentItem: any
|
||||
try {
|
||||
currentItem = JSON.parse(fs.readFileSync(configFolder + `queue${sep}${filename}`).toString())
|
||||
} catch {
|
||||
fs.unlinkSync(configFolder + `queue${sep}${filename}`)
|
||||
return
|
||||
}
|
||||
if (currentItem.status === 'inQueue') {
|
||||
let downloadObject: any
|
||||
switch (currentItem.__type__) {
|
||||
case 'Single':
|
||||
downloadObject = new Single(currentItem)
|
||||
break
|
||||
case 'Collection':
|
||||
downloadObject = new Collection(currentItem)
|
||||
break
|
||||
case 'Convertable':
|
||||
downloadObject = new Convertable(currentItem)
|
||||
break
|
||||
}
|
||||
queue[downloadObject.uuid] = downloadObject.getEssentialDict()
|
||||
queue[downloadObject.uuid].status = 'inQueue'
|
||||
} else {
|
||||
queue[currentItem.uuid] = currentItem
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -3,7 +3,7 @@ import type { RequestHandler } from 'express'
|
||||
import { Deezer } from 'deezer-js'
|
||||
|
||||
import type { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
export interface RawAlbumQuery {
|
||||
term: string
|
||||
|
@ -5,7 +5,7 @@ import deemix from 'deemix'
|
||||
import { Deezer } from 'deezer-js'
|
||||
|
||||
import type { ApiHandler, GetTrackResponse, GetAlbumResponse } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
export interface AnalyzeQuery {
|
||||
term?: string
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { getLatestVersion, isUpdateAvailable } from '../../../main'
|
||||
|
||||
const path: ApiHandler['path'] = '/checkForUpdates'
|
||||
|
||||
const handler: ApiHandler['handler'] = async (_, res) => {
|
||||
const latestCommit = await getLatestVersion()
|
||||
const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
const latestCommit = await deemix.getLatestVersion()
|
||||
res.send({
|
||||
latestCommit,
|
||||
updateAvailable: isUpdateAvailable()
|
||||
updateAvailable: deemix.isUpdateAvailable()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { RequestHandler } from 'express'
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
import { isObjectEmpy } from '../../../helpers/primitive-checks'
|
||||
import { BadRequestError, isBadRequestError, consoleError } from '../../../helpers/errors'
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getCharts'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getHome'
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
// import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { getQueue } from '../../../main'
|
||||
|
||||
const path: ApiHandler['path'] = '/getQueue'
|
||||
|
||||
// let homeCache: any
|
||||
|
||||
const handler: ApiHandler['handler'] = (_, res) => {
|
||||
const result: any = getQueue()
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
const result: any = deemix.getQueue()
|
||||
res.send(result)
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { getSettings } from '../../../main'
|
||||
|
||||
const path: ApiHandler['path'] = '/getSettings'
|
||||
|
||||
const handler: ApiHandler['handler'] = (_, res) => {
|
||||
res.send(getSettings())
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
res.send(deemix.getSettings())
|
||||
}
|
||||
|
||||
const apiHandler: ApiHandler = { path, handler }
|
||||
|
@ -1,13 +1,14 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ, plugins } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getTracklist'
|
||||
|
||||
const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
if (!sessionDZ[req.session.id]) sessionDZ[req.session.id] = new Deezer()
|
||||
const dz = sessionDZ[req.session.id]
|
||||
const deemix = req.app.get('deemix')
|
||||
|
||||
const list_id = String(req.query.id)
|
||||
const list_type = String(req.query.type)
|
||||
@ -20,7 +21,7 @@ const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
}
|
||||
case 'spotifyplaylist':
|
||||
case 'spotify_playlist': {
|
||||
if (!plugins.spotify.enabled) {
|
||||
if (!deemix.plugins.spotify.enabled) {
|
||||
res.send({
|
||||
collaborative: false,
|
||||
description: '',
|
||||
@ -40,7 +41,7 @@ const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
})
|
||||
break
|
||||
}
|
||||
const sp = plugins.spotify.sp
|
||||
const sp = deemix.plugins.spotify.sp
|
||||
let playlist = await sp.getPlaylist(list_id)
|
||||
playlist = playlist.body
|
||||
let tracklist = playlist.tracks.items
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getUserAlbums'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getUserArtists'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getUserFavorites'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getUserPlaylists'
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { plugins } from '../../../main'
|
||||
|
||||
const path: ApiHandler['path'] = '/getUserSpotifyPlaylists'
|
||||
|
||||
const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
let data
|
||||
const deemix = req.app.get('deemix')
|
||||
|
||||
if (plugins.spotify.enabled) {
|
||||
const sp = plugins.spotify.sp
|
||||
if (deemix.plugins.spotify.enabled) {
|
||||
const sp = deemix.plugins.spotify.sp
|
||||
const username = req.query.spotifyUser
|
||||
data = []
|
||||
let playlists = await sp.getUserPlaylists(username)
|
||||
@ -22,7 +22,7 @@ const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
playlistList = playlistList.concat(playlists.items)
|
||||
}
|
||||
playlistList.forEach((playlist: any) => {
|
||||
data.push(plugins.spotify._convertPlaylistStructure(playlist))
|
||||
data.push(deemix.plugins.spotify._convertPlaylistStructure(playlist))
|
||||
})
|
||||
} else {
|
||||
data = { error: 'spotifyNotEnabled' }
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/getUserTracks'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/mainSearch'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
import { getAlbumDetails } from './albumSearch'
|
||||
|
||||
const path: ApiHandler['path'] = '/newReleases'
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/search'
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { plugins } from '../../../main'
|
||||
|
||||
const path: ApiHandler['path'] = '/spotifyStatus'
|
||||
|
||||
const handler: ApiHandler['handler'] = (_, res) => {
|
||||
res.send({ spotifyEnabled: plugins.spotify.enabled })
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
res.send({ spotifyEnabled: deemix.plugins.spotify.enabled })
|
||||
}
|
||||
|
||||
const apiHandler: ApiHandler = { path, handler }
|
||||
|
@ -1,26 +1,27 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ, addToQueue, getSettings, listener } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/addToQueue'
|
||||
|
||||
const handler: ApiHandler['handler'] = async (req, res) => {
|
||||
if (!sessionDZ[req.session.id]) sessionDZ[req.session.id] = new Deezer()
|
||||
const deemix = req.app.get('deemix')
|
||||
const dz = sessionDZ[req.session.id]
|
||||
|
||||
const url = req.body.url.split(/[\s;]+/)
|
||||
let bitrate = req.body.bitrate
|
||||
if (bitrate === 'null' || bitrate === null) bitrate = getSettings().settings.maxBitrate
|
||||
if (bitrate === 'null' || bitrate === null) bitrate = deemix.getSettings().settings.maxBitrate
|
||||
let obj: any
|
||||
|
||||
try {
|
||||
obj = await addToQueue(dz, url, bitrate)
|
||||
obj = await deemix.addToQueue(dz, url, bitrate)
|
||||
} catch (e) {
|
||||
switch (e.name) {
|
||||
case 'NotLoggedIn':
|
||||
res.send({ result: false, errid: e.name, data: { url, bitrate } })
|
||||
listener.send('loginNeededToDownload')
|
||||
deemix.listener.send('loginNeededToDownload')
|
||||
break
|
||||
default:
|
||||
console.error(e)
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { cancelAllDownloads } from '../../../main'
|
||||
|
||||
const path = '/cancelAllDownloads'
|
||||
|
||||
const handler: ApiHandler['handler'] = (_, res) => {
|
||||
cancelAllDownloads()
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
deemix.cancelAllDownloads()
|
||||
res.send({ result: true })
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { RequestHandler } from 'express'
|
||||
import { Deezer } from 'deezer-js'
|
||||
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/changeAccount'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { RequestHandler } from 'express'
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { sessionDZ, startQueue, isDeezerAvailable } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
import { ApiHandler } from '../../../types'
|
||||
|
||||
export interface RawLoginArlBody {
|
||||
@ -21,6 +21,7 @@ const path: ApiHandler['path'] = '/loginArl'
|
||||
|
||||
const handler: RequestHandler<{}, {}, RawLoginArlBody, {}> = async (req, res, _) => {
|
||||
if (!sessionDZ[req.session.id]) sessionDZ[req.session.id] = new Deezer()
|
||||
const deemix = req.app.get('deemix')
|
||||
const dz = sessionDZ[req.session.id]
|
||||
|
||||
if (!req.body) {
|
||||
@ -58,7 +59,7 @@ const handler: RequestHandler<{}, {}, RawLoginArlBody, {}> = async (req, res, _)
|
||||
response = await testDz.login_via_arl(...loginParams)
|
||||
}
|
||||
if (response === LoginStatus.FAILED) sessionDZ[req.session.id] = new Deezer()
|
||||
if (!(await isDeezerAvailable())) response = LoginStatus.NOT_AVAILABLE
|
||||
if (!(await deemix.isDeezerAvailable())) response = LoginStatus.NOT_AVAILABLE
|
||||
const returnValue = {
|
||||
status: response,
|
||||
arl: req.body.arl,
|
||||
@ -67,7 +68,7 @@ const handler: RequestHandler<{}, {}, RawLoginArlBody, {}> = async (req, res, _)
|
||||
currentChild: dz.selected_account
|
||||
}
|
||||
|
||||
if (response !== LoginStatus.NOT_AVAILABLE && response !== LoginStatus.FAILED) startQueue(dz)
|
||||
if (response !== LoginStatus.NOT_AVAILABLE && response !== LoginStatus.FAILED) deemix.startQueue(dz)
|
||||
return res.status(200).send(returnValue)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { getAccessToken, getArlFromAccessToken } from '../../../main'
|
||||
import { getAccessToken, getArlFromAccessToken } from '../../../app'
|
||||
|
||||
const path = '/loginEmail'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { sessionDZ } from '../../../main'
|
||||
import { sessionDZ } from '../../../app'
|
||||
|
||||
const path: ApiHandler['path'] = '/logout'
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { clearCompletedDownloads } from '../../../main'
|
||||
|
||||
const path = '/removeFinishedDownloads'
|
||||
|
||||
const handler: ApiHandler['handler'] = (_, res) => {
|
||||
clearCompletedDownloads()
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
deemix.clearCompletedDownloads()
|
||||
res.send({ result: true })
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { ApiHandler } from '../../../types'
|
||||
import { cancelDownload } from '../../../main'
|
||||
|
||||
const path = '/removeFromQueue'
|
||||
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
const { uuid } = req.query
|
||||
if (uuid) {
|
||||
cancelDownload(uuid)
|
||||
deemix.cancelDownload(uuid)
|
||||
res.send({ result: true })
|
||||
} else {
|
||||
res.send({ result: false })
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { ApiHandler, Settings, SpotifySettings } from '../../../types'
|
||||
import { saveSettings, listener } from '../../../main'
|
||||
|
||||
const path = '/saveSettings'
|
||||
|
||||
@ -9,9 +8,10 @@ export interface SaveSettingsData {
|
||||
}
|
||||
|
||||
const handler: ApiHandler['handler'] = (req, res) => {
|
||||
const deemix = req.app.get('deemix')
|
||||
const { settings, spotifySettings }: SaveSettingsData = req.query
|
||||
saveSettings(settings, spotifySettings)
|
||||
listener.send('updateSettings', { settings, spotifySettings })
|
||||
deemix.saveSettings(settings, spotifySettings)
|
||||
deemix.listener.send('updateSettings', { settings, spotifySettings })
|
||||
res.send({ result: true })
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import express from 'express'
|
||||
// @ts-expect-error
|
||||
import { Deezer } from 'deezer-js'
|
||||
import { consoleInfo } from '../helpers/errors'
|
||||
import { sessionDZ, getQueue, deemixVersion, currentVersion, isDeezerAvailable, plugins, getSettings } from '../main'
|
||||
import { sessionDZ, deemixVersion, currentVersion } from '../app'
|
||||
|
||||
const router = express.Router()
|
||||
let update: any = null
|
||||
@ -10,6 +10,7 @@ let update: any = null
|
||||
router.get('/connect', async (req, res) => {
|
||||
if (!sessionDZ[req.session.id]) sessionDZ[req.session.id] = new Deezer()
|
||||
const dz = sessionDZ[req.session.id]
|
||||
const deemix = req.app.get('deemix')
|
||||
|
||||
if (!update) {
|
||||
consoleInfo(`Currently running deemix-gui version ${currentVersion}`)
|
||||
@ -24,14 +25,14 @@ router.get('/connect', async (req, res) => {
|
||||
update,
|
||||
autologin: !dz.logged_in,
|
||||
currentUser: dz.current_user,
|
||||
deezerAvailable: await isDeezerAvailable(),
|
||||
spotifyEnabled: plugins.spotify.enabled,
|
||||
settingsData: getSettings()
|
||||
deezerAvailable: await deemix.isDeezerAvailable(),
|
||||
spotifyEnabled: deemix.plugins.spotify.enabled,
|
||||
settingsData: deemix.getSettings()
|
||||
}
|
||||
|
||||
if (result.settingsData.settings.autoCheckForUpdates) result.checkForUpdates = true
|
||||
|
||||
const queue = getQueue()
|
||||
const queue = deemix.getQueue()
|
||||
|
||||
if (Object.keys(queue.queue).length > 0) {
|
||||
result.queue = queue
|
||||
|
88
server/src/server.ts
Normal file
88
server/src/server.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import http, { Server } from 'http'
|
||||
import express, { Application } from 'express'
|
||||
import { Server as WsServer } from 'ws'
|
||||
import initDebug from 'debug'
|
||||
// @ts-expect-error
|
||||
import deemix from 'deemix'
|
||||
import { registerMiddlewares } from './middlewares'
|
||||
import indexRouter from './routes'
|
||||
import { getErrorCb, getListeningCb } from './helpers/server-callbacks'
|
||||
import { registerApis } from './routes/api/register'
|
||||
import { registerWebsocket } from './websocket'
|
||||
import { consoleInfo } from './helpers/errors'
|
||||
import { Port, Listener } from './types'
|
||||
import { DeemixApp } from './app'
|
||||
import { normalizePort } from './helpers/port'
|
||||
|
||||
export class DeemixServer {
|
||||
host: string
|
||||
port: Port
|
||||
|
||||
wss: WsServer
|
||||
app: Application
|
||||
server: Server
|
||||
deemixApp: DeemixApp
|
||||
|
||||
constructor(host: string, port: string) {
|
||||
this.host = host
|
||||
this.port = normalizePort(port)
|
||||
|
||||
this.wss = new WsServer({ noServer: true })
|
||||
this.app = express()
|
||||
this.server = http.createServer(this.app)
|
||||
|
||||
const listener: Listener = {
|
||||
send: (key: string, data?: any) => {
|
||||
const logLine = deemix.utils.formatListener(key, data)
|
||||
if (logLine) console.log(logLine)
|
||||
if (['downloadInfo', 'downloadWarn'].includes(key)) return
|
||||
this.wss.clients.forEach(client => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(JSON.stringify({ key, data }))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.deemixApp = new DeemixApp(listener)
|
||||
}
|
||||
|
||||
init() {
|
||||
const debug = initDebug('deemix-gui:server')
|
||||
this.app.set('deemix', this.deemixApp)
|
||||
|
||||
/* === Middlewares === */
|
||||
registerMiddlewares(this.app)
|
||||
|
||||
/* === Routes === */
|
||||
this.app.use('/', indexRouter)
|
||||
|
||||
/* === APIs === */
|
||||
registerApis(this.app)
|
||||
|
||||
/* === Config === */
|
||||
this.app.set('port', this.port)
|
||||
|
||||
/* === Server port === */
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
this.server.listen({ port: this.port, host: this.host })
|
||||
}
|
||||
|
||||
registerWebsocket(this.wss, this.deemixApp)
|
||||
|
||||
/* === Server callbacks === */
|
||||
this.app.on('mount', a => {
|
||||
console.log(a)
|
||||
})
|
||||
this.server.on('connect', () => {
|
||||
consoleInfo('Server connected')
|
||||
})
|
||||
this.server.on('upgrade', (request, socket, head) => {
|
||||
this.wss.handleUpgrade(request, socket, head, socket => {
|
||||
this.wss.emit('connection', socket, request)
|
||||
})
|
||||
})
|
||||
this.server.on('error', getErrorCb(this.port))
|
||||
this.server.on('listening', getListeningCb(this.server, debug))
|
||||
}
|
||||
}
|
@ -239,3 +239,7 @@ export interface Arguments {
|
||||
[x: string]: unknown
|
||||
$0: string
|
||||
}
|
||||
|
||||
export interface Listener {
|
||||
send: (key: string, data?: any) => void
|
||||
}
|
||||
|
@ -1,19 +1,17 @@
|
||||
import { Server as WsServer } from 'ws'
|
||||
|
||||
import { consoleError, consoleInfo } from '../helpers/errors'
|
||||
import { DeemixApp } from '../app'
|
||||
import wsModules from './modules'
|
||||
|
||||
// ? Is this needed?
|
||||
// ? https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
|
||||
|
||||
export const registerWebsocket = (wss: WsServer) => {
|
||||
export const registerWebsocket = (wss: WsServer, deemix: DeemixApp) => {
|
||||
wss.on('connection', ws => {
|
||||
ws.on('message', message => {
|
||||
const data = JSON.parse(message.toString())
|
||||
|
||||
wsModules.forEach(module => {
|
||||
if (data.key === module.eventName) {
|
||||
module.cb(data.data, ws, wss)
|
||||
module.cb(data.data, ws, wss, deemix)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Server as WsServer } from 'ws'
|
||||
import { consoleInfo } from '../../helpers/errors'
|
||||
import { cancelAllDownloads } from '../../main'
|
||||
import { DeemixApp } from '../../app'
|
||||
|
||||
const eventName = 'cancelAllDownloads'
|
||||
|
||||
const cb = (_: any, __: any, ___: WsServer) => {
|
||||
cancelAllDownloads()
|
||||
const cb = (_: any, __: any, ___: WsServer, deemix: DeemixApp) => {
|
||||
deemix.cancelAllDownloads()
|
||||
consoleInfo(`Queue cleared`)
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Server as WsServer } from 'ws'
|
||||
import { consoleInfo } from '../../helpers/errors'
|
||||
import { clearCompletedDownloads } from '../../main'
|
||||
import { DeemixApp } from '../../app'
|
||||
|
||||
const eventName = 'removeFinishedDownloads'
|
||||
|
||||
const cb = (_: any, __: any, ___: WsServer) => {
|
||||
clearCompletedDownloads()
|
||||
const cb = (_: any, __: any, ___: WsServer, deemix: DeemixApp) => {
|
||||
deemix.clearCompletedDownloads()
|
||||
consoleInfo('Completed downloads cleared')
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Server as WsServer } from 'ws'
|
||||
import { consoleInfo } from '../../helpers/errors'
|
||||
import { cancelDownload } from '../../main'
|
||||
import { DeemixApp } from '../../app'
|
||||
|
||||
const eventName = 'removeFromQueue'
|
||||
|
||||
const cb = (data: any, __: any, ___: WsServer) => {
|
||||
cancelDownload(data)
|
||||
const cb = (data: any, __: any, ___: WsServer, deemix: DeemixApp) => {
|
||||
deemix.cancelDownload(data)
|
||||
consoleInfo(`Cancelled ${data}`)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Server as WsServer } from 'ws'
|
||||
import { consoleInfo } from '../../helpers/errors'
|
||||
import { saveSettings, listener } from '../../main'
|
||||
import { DeemixApp } from '../../app'
|
||||
import { Settings, SpotifySettings } from '../../types'
|
||||
|
||||
const eventName = 'saveSettings'
|
||||
@ -10,11 +10,11 @@ export interface SaveSettingsData {
|
||||
spotifySettings: SpotifySettings
|
||||
}
|
||||
|
||||
const cb = (data: SaveSettingsData, _: any, __: WsServer) => {
|
||||
const cb = (data: SaveSettingsData, _: any, __: WsServer, deemix: DeemixApp) => {
|
||||
const { settings, spotifySettings } = data
|
||||
saveSettings(settings, spotifySettings)
|
||||
deemix.saveSettings(settings, spotifySettings)
|
||||
consoleInfo('Settings saved')
|
||||
listener.send('updateSettings', { settings, spotifySettings })
|
||||
deemix.listener.send('updateSettings', { settings, spotifySettings })
|
||||
}
|
||||
|
||||
export default { eventName, cb }
|
||||
|
@ -6,7 +6,7 @@ module.exports = env => {
|
||||
const isProduction = !!env.production
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
entry: './src/app.ts',
|
||||
entry: './src/index.ts',
|
||||
devtool: isProduction ? false : 'eval',
|
||||
module: {
|
||||
rules: [
|
||||
|
2
webui
2
webui
@ -1 +1 @@
|
||||
Subproject commit 5fb5ae4ace6f271bee3cd744208197549738897d
|
||||
Subproject commit c4cd5cb3b8cb49676d9e19761324a037cee3eab5
|
Loading…
Reference in New Issue
Block a user