Implemented simple queue structure

This commit is contained in:
RemixDev 2021-05-13 18:14:38 +02:00
parent 7b16ddc91a
commit dbc1e2bcfb
7 changed files with 148 additions and 20 deletions

View File

@ -15,7 +15,7 @@
"dependencies": { "dependencies": {
"cookie-parser": "1.4.5", "cookie-parser": "1.4.5",
"debug": "2.6.9", "debug": "2.6.9",
"deemix": "0.0.2", "deemix": "0.0.3",
"deezer-js": "0.0.10", "deezer-js": "0.0.10",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"express": "4.17.1", "express": "4.17.1",

View File

@ -1,12 +1,110 @@
// @ts-expect-error // @ts-expect-error
import deemix from 'deemix' import deemix from 'deemix'
import fs from 'fs'
import {sep} from 'path'
import { wss } from './app'
import WebSocket from 'ws'
export const loadSettings = deemix.settings.load const Downloader = deemix.downloader.Downloader
const { Single, Collection, Convertable } = deemix.types.downloadObjects
export const defaultSettings: any = deemix.settings.DEFAULTS export const defaultSettings: any = deemix.settings.DEFAULTS
export let settings: any = loadSettings() export const configFolder: string = deemix.utils.localpaths.getConfigFolder()
export let settings: any = deemix.settings.load(configFolder)
export let sessionDZ: any = {} export let sessionDZ: any = {}
let deemixPlugins = {}
export const listener = {
send: function(key:string, data:any){
console.log(key, data)
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({key, data}))
}
})
}
}
export function saveSettings(newSettings: any) { export function saveSettings(newSettings: any) {
deemix.settings.save(newSettings) deemix.settings.save(newSettings, configFolder)
settings = newSettings settings = newSettings
} }
export let queueOrder: string[] = []
export let queue: any = {}
export let currentJob: any = null
export async function addToQueue(dz: any, url: string, bitrate: number){
if (!dz.logged_in) throw new NotLoggedIn
console.log(`Adding ${url} to queue`)
let downloadObj = await deemix.generateDownloadObject(dz, url, bitrate, deemixPlugins, listener)
// Check if element is already in queue
if (queueOrder.includes(downloadObj.uuid))
throw new AlreadyInQueue(downloadObj.getEssentialDict())
// 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()
fs.writeFileSync(configFolder+`queue${sep}${downloadObj.uuid}.json`, JSON.stringify(downloadObj.toDict()))
listener.send('addedToQueue', downloadObj.getSlimmedDict())
startQueue(dz)
return queue[downloadObj.uuid]
}
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 | undefined = queueOrder.shift()
let 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)
// Convert object here
break;
}
currentJob = new Downloader(dz, downloadObject, settings, listener)
listener.send('startDownload', currentUUID)
await currentJob.start()
currentJob = null
} while (queueOrder.length)
}
class QueueError extends Error {
constructor(message: string) {
super(message)
this.name = "QueueError"
}
}
class AlreadyInQueue extends QueueError {
item: any
constructor(dwObj: any) {
super(`${dwObj.artist} - ${dwObj.title} is already in queue.`)
this.name = "AlreadyInQueue"
this.item = dwObj
}
}
class NotLoggedIn extends QueueError {
constructor() {
super(`You must be logged in to start a download.`)
this.name = "NotLoggedIn"
}
}

View File

@ -0,0 +1,38 @@
// @ts-expect-error
import { Deezer } from 'deezer-js'
import { ApiHandler } from '../../../types'
import { sessionDZ, addToQueue, settings } from '../../../main'
const path: ApiHandler['path'] = '/addToQueue'
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 url = req.query.url
let bitrate = req.query.bitrate
if (bitrate === 'null') bitrate = settings.maxBitrate
let obj: any;
try {
obj = await addToQueue(dz, url, bitrate)
} catch (e){
switch (e.name) {
case 'AlreadyInQueue':
res.send({result: false, errid: e.name, data: {url, bitrate, obj: e.data}})
break
default:
console.error(e)
case 'NotLoggedIn':
res.send({result: false, errid: e.name, data: {url, bitrate}})
break
}
return
}
res.send({result: true, data: {url, bitrate, obj}})
}
const apiHandler: ApiHandler = { path, handler }
export default apiHandler

View File

@ -1,3 +1,4 @@
import loginArl from './login-arl' import loginArl from './login-arl'
import addToQueue from './addToQueue'
export default [loginArl] export default [loginArl, addToQueue]

View File

@ -6,14 +6,6 @@ import wsModules from './modules'
// ? Is this needed? // ? Is this needed?
// ? https://github.com/websockets/ws#how-to-detect-and-close-broken-connections // ? https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
export const broadcast = function(wss:WsServer, key:string, data:any) {
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({key, data}))
}
})
}
export const registerWebsocket = (wss: WsServer) => { export const registerWebsocket = (wss: WsServer) => {
wss.on('connection', ws => { wss.on('connection', ws => {
ws.on('message', (message)=>{ ws.on('message', (message)=>{

View File

@ -1,7 +1,6 @@
import { Server as WsServer } from 'ws' import { Server as WsServer } from 'ws'
import { consoleInfo } from '../../helpers/errors' import { consoleInfo } from '../../helpers/errors'
import { saveSettings } from '../../main' import { saveSettings, listener } from '../../main'
import { broadcast } from '../index'
const eventName = 'saveSettings' const eventName = 'saveSettings'
@ -9,7 +8,7 @@ const cb = (data: any, ws: any, wss: WsServer) => {
const {settings, spotifySettings} = data const {settings, spotifySettings} = data
saveSettings(settings) saveSettings(settings)
consoleInfo('Settings saved') consoleInfo('Settings saved')
broadcast(wss, 'updateSettings', {settings, spotifySettings}) listener.send('updateSettings', {settings, spotifySettings})
} }
export default { eventName, cb } export default { eventName, cb }

View File

@ -1878,10 +1878,10 @@ decompress-response@^6.0.0:
dependencies: dependencies:
mimic-response "^3.1.0" mimic-response "^3.1.0"
deemix@0.0.2: deemix@0.0.3:
version "0.0.2" version "0.0.3"
resolved "https://registry.yarnpkg.com/deemix/-/deemix-0.0.2.tgz#3b375834327d4d6bd1d1db072db976ced86746ee" resolved "https://registry.yarnpkg.com/deemix/-/deemix-0.0.3.tgz#a32b1b3c8e99fe26ea7a326658e49b4892c96dd5"
integrity sha512-NhTAM2LcoNxit6GiQYlUUO0YaGUMWtN8brGt9HO5mkvbn2cKojF1k9BulmUt/38NoiVvfkqTEtSQD8RtD4qPIA== integrity sha512-cUsfliHRXWLOYB7K+UCN0Lmx3d4ueWfaQ/Xinhg8/cHetifJC4/CxD8YDdBArI7Fl3StkJVGt45BajU/z00k6Q==
dependencies: dependencies:
async "^3.2.0" async "^3.2.0"
browser-id3-writer "^4.4.0" browser-id3-writer "^4.4.0"