2020-08-28 23:06:19 +02:00
|
|
|
const express = require('express');
|
|
|
|
const path = require('path');
|
2020-11-03 10:11:37 +01:00
|
|
|
const packageJson = require('../package.json');
|
2020-08-28 23:06:19 +02:00
|
|
|
const fs = require('fs');
|
|
|
|
const axios = require('axios').default;
|
2020-09-28 12:04:19 +02:00
|
|
|
const logger = require('./winston');
|
2020-10-31 16:54:28 +01:00
|
|
|
const {DeezerAPI, DeezerStream} = require('./deezer');
|
2020-08-28 23:06:19 +02:00
|
|
|
const {Settings} = require('./settings');
|
|
|
|
const {Track, Album, Artist, Playlist, DeezerProfile, SearchResults, DeezerLibrary, DeezerPage, Lyrics} = require('./definitions');
|
2020-10-31 16:54:28 +01:00
|
|
|
const {DownloadManager} = require('./downloads');
|
2020-09-28 12:04:19 +02:00
|
|
|
const {Integrations} = require('./integrations');
|
2020-08-28 23:06:19 +02:00
|
|
|
|
|
|
|
let settings;
|
|
|
|
let deezer;
|
2020-10-31 16:54:28 +01:00
|
|
|
let downloadManager;
|
2020-09-28 12:04:19 +02:00
|
|
|
let integrations;
|
2020-08-28 23:06:19 +02:00
|
|
|
|
|
|
|
let sockets = [];
|
|
|
|
|
|
|
|
//Express
|
|
|
|
const app = express();
|
|
|
|
app.use(express.json({limit: '50mb'}));
|
|
|
|
app.use(express.static(path.join(__dirname, '../client', 'dist')));
|
|
|
|
//Server
|
|
|
|
const server = require('http').createServer(app);
|
2020-10-31 16:54:28 +01:00
|
|
|
const io = require('socket.io').listen(server, {
|
|
|
|
path: '/socket',
|
|
|
|
});
|
2020-08-28 23:06:19 +02:00
|
|
|
|
|
|
|
//Get playback info
|
|
|
|
app.get('/playback', async (req, res) => {
|
|
|
|
try {
|
|
|
|
let data = await fs.promises.readFile(Settings.getPlaybackInfoPath(), 'utf-8');
|
|
|
|
return res.json(data);
|
2020-10-31 16:54:28 +01:00
|
|
|
// eslint-disable-next-line no-empty
|
2020-08-28 23:06:19 +02:00
|
|
|
} catch (e) {}
|
|
|
|
|
|
|
|
return res.json({});
|
|
|
|
});
|
|
|
|
|
|
|
|
//Save playback info
|
|
|
|
app.post('/playback', async (req, res) => {
|
|
|
|
if (req.body) {
|
|
|
|
let data = JSON.stringify(req.body);
|
|
|
|
await fs.promises.writeFile(Settings.getPlaybackInfoPath(), data, 'utf-8');
|
|
|
|
}
|
|
|
|
res.status(200).send('');
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get settings
|
|
|
|
app.get('/settings', (req, res) => {
|
|
|
|
res.json(settings);
|
|
|
|
});
|
|
|
|
|
|
|
|
//Save settings
|
|
|
|
app.post('/settings', async (req, res) => {
|
|
|
|
if (req.body) {
|
|
|
|
Object.assign(settings, req.body);
|
2020-10-31 16:54:28 +01:00
|
|
|
downloadManager.settings = settings;
|
2020-09-28 12:04:19 +02:00
|
|
|
integrations.updateSettings(settings);
|
2020-08-28 23:06:19 +02:00
|
|
|
await settings.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
res.status(200).send('');
|
|
|
|
});
|
|
|
|
|
|
|
|
//Post with body {"arl": ARL}
|
|
|
|
app.post('/authorize', async (req, res) => {
|
|
|
|
if (!req.body.arl || req.body.arl.length < 100) return res.status(500).send('Invalid ARL');
|
|
|
|
|
|
|
|
//Check if arl valid
|
|
|
|
deezer.arl = req.body.arl;
|
|
|
|
settings.arl = req.body.arl;
|
|
|
|
|
|
|
|
if (await (deezer.authorize())) {
|
2020-10-31 16:54:28 +01:00
|
|
|
//Update download manager
|
|
|
|
downloadManager.setDeezer(deezer);
|
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
res.status(200).send('OK');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
res.status(500).send('Authorization error / Invalid ARL.');
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get track by id
|
|
|
|
app.get('/track/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('deezer.pageTrack', {sng_id: req.params.id.toString()});
|
|
|
|
res.send(new Track(data.results.DATA));
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get album by id
|
|
|
|
app.get('/album/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('deezer.pageAlbum', {alb_id: req.params.id.toString(), lang: 'us'});
|
|
|
|
res.send(new Album(data.results.DATA, data.results.SONGS));
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get artist by id
|
|
|
|
app.get('/artist/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('deezer.pageArtist', {art_id: req.params.id.toString(), lang: 'us'});
|
|
|
|
res.send(new Artist(data.results.DATA, data.results.ALBUMS, data.results.TOP));
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get playlist by id
|
|
|
|
//start & full query parameters
|
|
|
|
app.get('/playlist/:id', async (req, res) => {
|
|
|
|
//Set anything to `full` query parameter to get entire playlist
|
2020-09-20 19:17:28 +02:00
|
|
|
let nb = req.query.full ? 100000 : 50;
|
2020-08-28 23:06:19 +02:00
|
|
|
let data = await deezer.callApi('deezer.pagePlaylist', {
|
2020-09-20 19:17:28 +02:00
|
|
|
playlist_id: req.params.id.toString(),
|
|
|
|
lang: 'us',
|
|
|
|
nb: nb,
|
|
|
|
start: req.query.start ? parseInt(req.query.start, 10) : 0,
|
2020-08-28 23:06:19 +02:00
|
|
|
tags: true
|
|
|
|
});
|
2020-09-20 19:17:28 +02:00
|
|
|
return res.send(new Playlist(data.results.DATA, data.results.SONGS));
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
//DELETE playlist
|
|
|
|
app.delete('/playlist/:id', async (req, res) => {
|
|
|
|
await deezer.callApi('playlist.delete', {playlist_id: req.params.id.toString()});
|
|
|
|
res.sendStatus(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
//POST create playlist
|
|
|
|
// {
|
|
|
|
// desciption,
|
|
|
|
// title,
|
|
|
|
// type: 'public' || 'private',
|
|
|
|
// track: trackID
|
|
|
|
// }
|
|
|
|
app.post('/playlist', async (req, res) => {
|
|
|
|
await deezer.callApi('playlist.create', {
|
|
|
|
description: req.body.description,
|
|
|
|
title: req.body.title,
|
|
|
|
status: req.body.type == 'public' ? 2 : 1,
|
|
|
|
songs: req.body.track ? [[req.body.track, 0]] : []
|
|
|
|
});
|
|
|
|
|
|
|
|
res.sendStatus(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
//POST track to playlist
|
|
|
|
//Body {"track": "trackId"}
|
|
|
|
app.post('/playlist/:id/tracks', async (req, res) => {
|
|
|
|
await deezer.callApi('playlist.addSongs', {
|
|
|
|
offset: -1,
|
|
|
|
playlist_id: req.params.id,
|
|
|
|
songs: [[req.body.track, 0]]
|
|
|
|
});
|
|
|
|
|
|
|
|
res.sendStatus(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
//DELETE track from playlist
|
|
|
|
//Body {"track": "trackId"}
|
|
|
|
app.delete('/playlist/:id/tracks', async (req, res) => {
|
|
|
|
await deezer.callApi('playlist.deleteSongs', {
|
|
|
|
playlist_id: req.params.id,
|
|
|
|
songs: [[req.body.track, 0]]
|
|
|
|
});
|
|
|
|
|
|
|
|
res.sendStatus(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get more albums
|
|
|
|
//ID = artist id, QP start = offset
|
|
|
|
app.get('/albums/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('album.getDiscography', {
|
|
|
|
art_id: parseInt(req.params.id.toString(), 10),
|
|
|
|
discography_mode: "all",
|
|
|
|
nb: 25,
|
|
|
|
nb_songs: 200,
|
|
|
|
start: req.query.start ? parseInt(req.query.start, 10) : 0
|
|
|
|
});
|
|
|
|
|
|
|
|
let albums = data.results.data.map((a) => new Album(a));
|
|
|
|
res.send(albums);
|
|
|
|
})
|
|
|
|
|
2020-09-02 14:39:43 +02:00
|
|
|
//Get tracks from listening history
|
|
|
|
app.get('/history', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('deezer.pageProfile', {
|
|
|
|
nb: 200,
|
|
|
|
tab: "history",
|
|
|
|
user_id: deezer.userId.toString()
|
|
|
|
});
|
|
|
|
let tracks = data.results.TAB.history.data.map((t) => new Track(t));
|
|
|
|
res.send(tracks);
|
|
|
|
});
|
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
//Search, q as query parameter
|
|
|
|
app.get('/search', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('deezer.pageSearch', {query: req.query.q, nb: 100});
|
|
|
|
res.send(new SearchResults(data.results));
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get user profile data
|
|
|
|
app.get('/profile', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('deezer.getUserData');
|
|
|
|
let profile = new DeezerProfile(data.results);
|
|
|
|
res.send(profile);
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get list of `type` from library
|
|
|
|
app.get('/library/:type', async (req, res) => {
|
|
|
|
let type = req.params.type;
|
|
|
|
let data = await deezer.callApi('deezer.pageProfile', {
|
|
|
|
nb: 50,
|
|
|
|
tab: (type == 'tracks') ? 'loved' : type,
|
|
|
|
user_id: deezer.userId
|
|
|
|
});
|
|
|
|
res.send(new DeezerLibrary(data.results.TAB, type));
|
|
|
|
});
|
|
|
|
|
|
|
|
//DELETE from library
|
|
|
|
app.delete('/library/:type', async (req, res) => {
|
|
|
|
let type = req.params.type;
|
|
|
|
let id = req.query.id;
|
|
|
|
|
|
|
|
if (type == 'track') await deezer.callApi('favorite_song.remove', {SNG_ID: id});
|
|
|
|
if (type == 'album') await deezer.callApi('album.deleteFavorite', {ALB_ID: id});
|
|
|
|
if (type == 'playlist') await deezer.callApi('playlist.deleteFavorite', {playlist_id: parseInt(id, 10)});
|
|
|
|
if (type == 'artist') await deezer.callApi('artist.deleteFavorite', {ART_ID: id});
|
|
|
|
|
|
|
|
res.sendStatus(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
//PUT (add) to library
|
|
|
|
app.put('/library/:type', async (req, res) => {
|
|
|
|
let type = req.params.type;
|
|
|
|
let id = req.query.id;
|
|
|
|
|
|
|
|
if (type == 'track') await deezer.callApi('favorite_song.add', {SNG_ID: id});
|
|
|
|
if (type == 'album') await deezer.callApi('album.addFavorite', {ALB_ID: id});
|
|
|
|
if (type == 'artist') await deezer.callApi('artist.addFavorite', {ART_ID: id});
|
|
|
|
if (type == 'playlist') await deezer.callApi('playlist.addFavorite', {parent_playlist_id: parseInt(id)});
|
|
|
|
|
|
|
|
res.sendStatus(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//Get streaming metadata, quality fallback
|
|
|
|
app.get('/streaminfo/:info', async (req, res) => {
|
|
|
|
let info = req.params.info;
|
|
|
|
let quality = req.query.q ? req.query.q : 3;
|
2020-10-31 16:54:28 +01:00
|
|
|
let qualityInfo = await deezer.fallback(info, quality);
|
|
|
|
|
|
|
|
if (qualityInfo == null)
|
|
|
|
return res.sendStatus(404).end();
|
|
|
|
|
|
|
|
//Generate stream URL before sending
|
|
|
|
qualityInfo.generateUrl();
|
|
|
|
return res.json(qualityInfo);
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// S T R E A M I N G
|
2020-10-31 16:54:28 +01:00
|
|
|
app.get('/stream/:info', async (req, res) => {
|
2020-08-28 23:06:19 +02:00
|
|
|
//Parse stream info
|
|
|
|
let quality = req.query.q ? req.query.q : 3;
|
2020-09-28 12:04:19 +02:00
|
|
|
let streamInfo = Track.getUrlInfo(req.params.info);
|
2020-10-31 16:54:28 +01:00
|
|
|
streamInfo.quality = quality;
|
2020-08-28 23:06:19 +02:00
|
|
|
|
|
|
|
//MIME type of audio
|
|
|
|
let mime = 'audio/mp3';
|
|
|
|
if (quality == 9) mime = 'audio/flac';
|
|
|
|
|
|
|
|
//Parse range header
|
|
|
|
let range = 'bytes=0-';
|
|
|
|
if (req.headers.range) range = req.headers.range;
|
|
|
|
let rangeParts = range.replace(/bytes=/, '').split('-');
|
|
|
|
let start = parseInt(rangeParts[0], 10);
|
2020-10-31 16:54:28 +01:00
|
|
|
let end = -1;
|
2020-08-28 23:06:19 +02:00
|
|
|
if (rangeParts.length >= 2) end = rangeParts[1];
|
2020-10-31 16:54:28 +01:00
|
|
|
if (end == '' || end == ' ') end = -1;
|
|
|
|
|
|
|
|
//Create Stream
|
|
|
|
let stream = new DeezerStream(streamInfo, {});
|
|
|
|
await stream.open(start, end);
|
|
|
|
|
|
|
|
//Range header
|
|
|
|
if (req.headers.range) {
|
|
|
|
end = (end == -1) ? stream.size - 1 : end;
|
|
|
|
res.writeHead(206, {
|
|
|
|
'Content-Range': `bytes ${start}-${end}/${stream.size}`,
|
|
|
|
'Accept-Ranges': 'bytes',
|
|
|
|
'Content-Length': stream.size - start,
|
|
|
|
'Content-Type': mime
|
|
|
|
});
|
|
|
|
|
|
|
|
//Normal (non range) request
|
|
|
|
} else {
|
|
|
|
res.writeHead(200, {
|
|
|
|
'Content-Length': stream.size,
|
|
|
|
'Content-Type': mime
|
|
|
|
});
|
|
|
|
}
|
2020-08-28 23:06:19 +02:00
|
|
|
|
2020-10-31 16:54:28 +01:00
|
|
|
//Should force HTML5 to retry
|
|
|
|
stream.on('error', () => {
|
2020-09-01 20:37:02 +02:00
|
|
|
res.destroy();
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
|
2020-10-31 16:54:28 +01:00
|
|
|
stream.pipe(res);
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
//Get deezer page
|
|
|
|
app.get('/page', async (req, res) => {
|
|
|
|
let target = req.query.target.replace(/"/g, '');
|
|
|
|
|
|
|
|
let st = ['album', 'artist', 'channel', 'flow', 'playlist', 'smarttracklist', 'track', 'user'];
|
|
|
|
let data = await deezer.callApi('page.get', {}, {
|
|
|
|
'PAGE': target,
|
|
|
|
'VERSION': '2.3',
|
|
|
|
'SUPPORT': {
|
|
|
|
'grid': st,
|
|
|
|
'horizontal-grid': st,
|
|
|
|
'item-highlight': ['radio'],
|
|
|
|
'large-card': ['album', 'playlist', 'show', 'video-link'],
|
|
|
|
'ads': [] //None
|
|
|
|
},
|
|
|
|
'LANG': 'us',
|
|
|
|
'OPTIONS': []
|
|
|
|
});
|
|
|
|
res.send(new DeezerPage(data.results));
|
|
|
|
});
|
|
|
|
|
|
|
|
//Get smart track list or flow tracks
|
|
|
|
app.get('/smarttracklist/:id', async (req, res) => {
|
|
|
|
let id = req.params.id;
|
|
|
|
|
|
|
|
//Flow not normal STL
|
|
|
|
if (id == 'flow') {
|
|
|
|
let data = await deezer.callApi('radio.getUserRadio', {
|
|
|
|
user_id: deezer.userId
|
|
|
|
});
|
|
|
|
let tracks = data.results.data.map((t) => new Track(t));
|
|
|
|
return res.send(tracks);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Normal STL
|
|
|
|
let data = await deezer.callApi('smartTracklist.getSongs', {
|
|
|
|
smartTracklist_id: id
|
|
|
|
});
|
2020-10-01 14:30:00 +02:00
|
|
|
//No more tracks
|
|
|
|
if (!data.results.data) {
|
|
|
|
logger.warn('No more STL tracks: ' + JSON.stringify(data.error));
|
|
|
|
return res.send([]);
|
|
|
|
}
|
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
let tracks = data.results.data.map((t) => new Track(t));
|
|
|
|
return res.send(tracks);
|
|
|
|
});
|
|
|
|
|
2020-10-31 16:54:28 +01:00
|
|
|
//Artist smart radio
|
|
|
|
app.get('/smartradio/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('smart.getSmartRadio', {art_id: req.params.id});
|
|
|
|
res.send(data.results.data.map(t => new Track(t)));
|
|
|
|
});
|
|
|
|
|
|
|
|
//Track Mix
|
|
|
|
app.get('/trackmix/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('song.getContextualTrackMix', {sng_ids: [req.params.id]});
|
|
|
|
res.send(data.results.data.map(t => new Track(t)));
|
|
|
|
});
|
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
//Load lyrics, ID = SONG ID
|
|
|
|
app.get('/lyrics/:id', async (req, res) => {
|
|
|
|
let data = await deezer.callApi('song.getLyrics', {
|
|
|
|
sng_id: parseInt(req.params.id, 10)
|
|
|
|
});
|
|
|
|
if (!data.results || data.error.length > 0) return res.status(502).send('Lyrics not found!');
|
|
|
|
|
|
|
|
res.send(new Lyrics(data.results));
|
|
|
|
});
|
|
|
|
|
2020-09-28 12:04:19 +02:00
|
|
|
//Search Suggestions
|
|
|
|
app.get('/suggestions/:query', async (req, res) => {
|
|
|
|
let query = req.params.query;
|
|
|
|
try {
|
|
|
|
let data = await deezer.callApi('search_getSuggestedQueries', {
|
|
|
|
QUERY: query
|
|
|
|
});
|
|
|
|
let out = data.results.SUGGESTION.map((s) => s.QUERY);
|
|
|
|
res.json(out);
|
|
|
|
} catch (e) {
|
|
|
|
res.json([]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
//Post list of tracks to download
|
|
|
|
app.post('/downloads', async (req, res) => {
|
2020-11-03 10:11:37 +01:00
|
|
|
downloadManager.addBatch(req.body);
|
2020-08-28 23:06:19 +02:00
|
|
|
|
|
|
|
res.status(200).send('OK');
|
|
|
|
});
|
|
|
|
|
|
|
|
//PUT to /download to start
|
|
|
|
app.put('/download', async (req, res) => {
|
2020-10-31 16:54:28 +01:00
|
|
|
await downloadManager.start();
|
2020-08-28 23:06:19 +02:00
|
|
|
res.status(200).send('OK');
|
|
|
|
});
|
|
|
|
|
|
|
|
//DELETE to /download to stop/pause
|
|
|
|
app.delete('/download', async (req, res) => {
|
2020-10-31 16:54:28 +01:00
|
|
|
await downloadManager.stop();
|
2020-08-28 23:06:19 +02:00
|
|
|
res.status(200).send('OK');
|
|
|
|
})
|
|
|
|
|
|
|
|
//Get all downloads
|
|
|
|
app.get('/downloads', async (req, res) => {
|
|
|
|
res.json({
|
2020-10-31 16:54:28 +01:00
|
|
|
downloading: downloadManager.downloading,
|
|
|
|
queue: downloadManager.queue,
|
|
|
|
threads: downloadManager.threads.map(t => t.download)
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-10-31 16:54:28 +01:00
|
|
|
//Delete single download
|
2020-09-01 20:37:02 +02:00
|
|
|
app.delete('/downloads/:index', async (req, res) => {
|
|
|
|
let index = parseInt(req.params.index, 10);
|
2020-10-31 16:54:28 +01:00
|
|
|
await downloadManager.delete(index);
|
2020-09-01 20:37:02 +02:00
|
|
|
res.status(200).end();
|
|
|
|
});
|
|
|
|
|
2020-09-07 19:12:45 +02:00
|
|
|
//Log listen to deezer & lastfm
|
|
|
|
app.post('/log', async (req, res) => {
|
|
|
|
//LastFM
|
2020-09-28 12:04:19 +02:00
|
|
|
integrations.scrobbleLastFM(req.body.title, req.body.artists[0].name);
|
2020-09-07 19:12:45 +02:00
|
|
|
|
|
|
|
//Deezer
|
|
|
|
if (settings.logListen)
|
|
|
|
await deezer.callApi('log.listen', {
|
|
|
|
params: {
|
|
|
|
timestamp: Math.floor(new Date() / 1000),
|
|
|
|
ts_listen: Math.floor(new Date() / 1000),
|
|
|
|
type: 1,
|
|
|
|
stat: {seek: 0, pause: 0, sync: 0},
|
|
|
|
media: {id: req.body.id, type: 'song', format: 'MP3_128'}
|
|
|
|
}
|
|
|
|
});
|
2020-09-01 20:37:02 +02:00
|
|
|
res.status(200).end();
|
|
|
|
});
|
|
|
|
|
2020-09-07 19:12:45 +02:00
|
|
|
//Last.FM authorization callback
|
|
|
|
app.get('/lastfm', async (req, res) => {
|
|
|
|
//Got token
|
|
|
|
if (req.query.token) {
|
|
|
|
let token = req.query.token;
|
2020-09-28 12:04:19 +02:00
|
|
|
//Authorize
|
|
|
|
let authinfo = await integrations.loginLastFM(token);
|
|
|
|
if (authinfo) {
|
|
|
|
settings.lastFM = authinfo;
|
|
|
|
settings.save();
|
|
|
|
}
|
2020-09-07 19:12:45 +02:00
|
|
|
//Redirect to homepage
|
|
|
|
return res.redirect('/');
|
|
|
|
}
|
|
|
|
|
|
|
|
//Get auth url
|
|
|
|
res.json({
|
2020-09-28 12:04:19 +02:00
|
|
|
url: integrations.lastfm.getAuthenticationUrl({cb: `http://${req.socket.remoteAddress}:${settings.port}/lastfm`})
|
2020-09-07 19:12:45 +02:00
|
|
|
}).end();
|
|
|
|
});
|
|
|
|
|
2020-09-20 19:17:28 +02:00
|
|
|
//Get URL from deezer.page.link
|
|
|
|
app.get('/fullurl', async (req, res) => {
|
|
|
|
let url = req.query.url;
|
|
|
|
let r = await axios.get(url, {validateStatus: null});
|
|
|
|
res.json({url: r.request.res.responseUrl});
|
|
|
|
});
|
|
|
|
|
2020-11-03 10:11:37 +01:00
|
|
|
//About page
|
|
|
|
app.get('/about', async (req, res) => {
|
|
|
|
res.json({
|
|
|
|
version: packageJson.version
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
//Redirect to index on unknown path
|
|
|
|
app.all('*', (req, res) => {
|
|
|
|
res.redirect('/');
|
|
|
|
});
|
|
|
|
|
|
|
|
// S O C K E T S
|
|
|
|
io.on('connection', (socket) => {
|
|
|
|
sockets.push(socket);
|
|
|
|
//Remove on disconnect
|
|
|
|
socket.on('disconnect', () => {
|
|
|
|
sockets.splice(sockets.indexOf(socket), 1);
|
|
|
|
});
|
2020-09-28 12:04:19 +02:00
|
|
|
//Send to integrations
|
|
|
|
socket.on('stateChange', (data) => {
|
|
|
|
integrations.updateState(data);
|
|
|
|
});
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
//ecb = Error callback
|
|
|
|
async function createServer(electron = false, ecb) {
|
|
|
|
//Prepare globals
|
|
|
|
settings = new Settings(electron);
|
|
|
|
settings.load();
|
|
|
|
|
|
|
|
deezer = new DeezerAPI(settings.arl, electron);
|
|
|
|
|
|
|
|
//Prepare downloads
|
2020-10-31 16:54:28 +01:00
|
|
|
downloadManager = new DownloadManager(settings, () => {
|
2020-08-28 23:06:19 +02:00
|
|
|
//Emit queue change to socket
|
|
|
|
sockets.forEach((s) => {
|
|
|
|
s.emit('downloads', {
|
2020-10-31 16:54:28 +01:00
|
|
|
downloading: downloadManager.downloading,
|
|
|
|
queue: downloadManager.queue,
|
|
|
|
threads: downloadManager.threads.map(t => t.download)
|
2020-08-28 23:06:19 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2020-10-31 16:54:28 +01:00
|
|
|
await downloadManager.load();
|
|
|
|
downloadManager.setDeezer(deezer);
|
2020-09-02 14:39:43 +02:00
|
|
|
//Emit download progress updates
|
|
|
|
setInterval(() => {
|
|
|
|
sockets.forEach((s) => {
|
2020-10-31 16:54:28 +01:00
|
|
|
if (!downloadManager.downloading && downloadManager.threads.length == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
s.emit('currentlyDownloading', downloadManager.threads.map(t => t.download));
|
2020-09-02 14:39:43 +02:00
|
|
|
});
|
2020-10-31 16:54:28 +01:00
|
|
|
}, 400);
|
2020-08-28 23:06:19 +02:00
|
|
|
|
2020-09-28 12:04:19 +02:00
|
|
|
//Integrations (lastfm, discord)
|
|
|
|
integrations = new Integrations(settings);
|
|
|
|
//Discord Join = Sync tracks
|
|
|
|
integrations.on('discordJoin', async (data) => {
|
|
|
|
let trackData = await deezer.callApi('deezer.pageTrack', {sng_id: data.id});
|
|
|
|
let track = new Track(trackData.results.DATA);
|
|
|
|
let out = {
|
|
|
|
track: track,
|
|
|
|
position: (Date.now() - data.ts) + data.pos
|
|
|
|
}
|
|
|
|
//Emit to sockets
|
|
|
|
sockets.forEach((s) => {
|
|
|
|
s.emit('playOffset', out);
|
|
|
|
});
|
|
|
|
});
|
2020-09-07 19:12:45 +02:00
|
|
|
|
2020-08-28 23:06:19 +02:00
|
|
|
//Start server
|
|
|
|
server.on('error', (e) => {
|
|
|
|
ecb(e);
|
|
|
|
});
|
|
|
|
server.listen(settings.port, settings.serverIp);
|
|
|
|
console.log(`Running on: http://${settings.serverIp}:${settings.port}`);
|
|
|
|
|
|
|
|
return settings;
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {createServer};
|