UNPKG

silk-gui

Version:

GUI for developers and Node OS

611 lines (516 loc) 17.1 kB
var util = require(__dirname + '/../util.js'); var lib_func = require(__dirname + '/../library_functions.js'); var async = require('async'); var AdmZip = require('adm-zip'); var os = require('os'); var opener = require('opener'); var md5 = require('md5'); var request = require('request').defaults({ encoding: null }); var fs = require('fs'); var path = require('path'); var MobileDetect = require('mobile-detect'); var similarSongs = require('similar-songs'); /* * GET home page. */ app = null; exports.createRoutes = function(app_ref) { app = app_ref; // pass the app onto the library_functions module lib_func.setApp(app); app.get('/', musicRoute); app.get('/remote/:name', musicRoute); app.get('/songs/:id', sendSong); app.get('/cover/:id', sendCover); app.get('/downloadplaylist/:id', downloadPlaylist); // remote control commands app.get('/command/:name/:command', remoteCommand); app.io.route('player_page_connected', function(req) { req.socket.join('players'); }); // remote functions app.io.route('set_comp_name', setCompName); // scanning signals app.io.route('scan_page_connected', function(req) { req.socket.join('scanners'); }); app.io.route('start_scan', function(req) { lib_func.scanLibrary(false); }); app.io.route('start_scan_hard', function(req) { lib_func.scanLibrary(true); }); app.io.route('stop_scan', function(req) { lib_func.stopScan(); }); app.io.route('hard_rescan', rescanItem); // send the songs to the client app.io.route('fetch_songs', returnSongs); // playlist modifications app.io.route('fetch_playlists', returnPlaylists); app.io.route('create_playlist', createPlaylist); app.io.route('rename_playlist', renamePlaylist); app.io.route('delete_playlist', deletePlaylist); app.io.route('add_to_playlist', addToPlaylist); app.io.route('remove_from_playlist', removeFromPlaylist); app.io.route('song_moved_in_playlist', songMovedInPlaylist); // play count app.io.route('update_play_count', updatePlayCount); // remote control routes app.io.route('get_receivers', getReceiversMinusThis); // open file manager to location app.io.route('open_dir', function(req) { opener(req.data.substring(0, req.data.lastIndexOf(path.sep))); }); // update the info of a song app.io.route('update_song_info', updateSongInfo); // rewrite tags of a track to the file app.io.route('rewrite_tags', rewriteTags); // sync routes app.io.route('sync_page_connected', syncPageConnected); app.io.route('sync_playlists', syncPlaylists); // soundcloud downloading app.io.route('soundcloud_download', soundcloudDownload); // youtube downloading app.io.route('youtube_download', youtubeDownload); // youtube downloading for bunch of songs with pre-filled info // (i.e. they were viewing from a mix) app.io.route('youtube_import', youtubeImport); // settings updating app.io.route('update_settings', updateSettings); // get similar songs app.io.route('similar_songs', getSimilarSongs); }; function musicRoute(req, res) { // test if client is mobile md = new MobileDetect(req.headers['user-agent']); // get ip (for syncing functions) util.getip(function(ip) { // the function to send the homepage only when the config is finished initalizing var sendHome = function() { // render the view res.render((md.mobile() ? 'mobile' : 'index'), { menu: !md.mobile(), music_dir: app.get('config').music_dir, music_dir_set: app.get('config').music_dir_set, country_code: app.get('config').country_code, ip: ip + ':' + app.get('port'), remote_name: req.params.name, }); }; // logic to wait for config to initialise var config = app.get('config'); if (config.initialized) { sendHome(); } else { config.initializedFn = sendHome; } }); } function sendSong(req, res) { app.db.songs.findOne({_id: req.params.id}, function(err, song) { if (err || !song) { res.status(404).send(); } else { res.sendFile(path.join(app.get('config').music_dir, song.location)); } }); } function sendCover(req, res) { // if they passed the location of the cover, fetch it if (req.params.id.length > 0) { res.sendFile(app.get('configDir') + '/dbs/covers/' + req.params.id); } else { res.sendFile(app.get('root') + '/static/images/unknown.png'); } } function downloadPlaylist(req, res) { app.db.playlists.findOne({_id: req.params.id}, function(err, playlist) { if (err) throw err; // create zip var filename = os.tmpdir() + '/download.zip'; var zip = new AdmZip(); async.forEach(playlist.songs, function(item, callback) { app.db.songs.findOne({_id: item._id}, function(err, song) { if (err) throw err; var zip_location; if (song.location.lastIndexOf(path.sep) > 0) { zip_location = song.location.substring(0, song.location.lastIndexOf(path.sep)); } else { zip_location = song.location; } zip.addLocalFile(path.join(app.get('config').music_dir, song.location), zip_location); callback(); }); }, function(err) { // convert the zip to a buffer zip.toBuffer(function(buffer) { // download the buffer res.setHeader('Content-disposition', 'attachment; filename=download.zip'); res.end(buffer, 'binary'); }); }); }); } function returnSongs(req) { get_songs(function(songs) { req.socket.emit('songs', {songs: songs}); }); } function get_songs(callback) { app.db.songs.find({}, function(err, songs) { if (!err) { callback(songs); } else { callback([]); } }); } function returnPlaylists(req) { // send the playlists back to the user get_playlists(function(playlists) { req.socket.emit('playlists', {playlists: playlists}); }); } function get_playlists(callback) { app.db.playlists.find({}).sort({ title: 1 }).exec(function(err, docs) { playlists = docs; // create a new playlist for the library getLibraryIds(function(result) { // add library playlists.unshift({ _id: 'LIBRARY', title: 'Library', songs: result, editable: false, }); // add queue playlists.unshift({ _id: 'QUEUE', title: 'Queue', songs: [], // populated by client editable: false, }); // send the playlists back to the user callback(playlists); }); }); } // get the _id's of every song in the library function getLibraryIds(callback) { app.db.songs.find({}).sort({ title: 1 }).exec(function(err, docs) { for (var i = 0; i < docs.length; i++) { docs[i] = {_id: docs[i]._id}; } callback(docs); }); } function createPlaylist(req) { plist = { title: req.data.title, songs: req.data.songs, editable: true, }; app.db.playlists.insert(plist, function(err, doc) { req.io.route('fetch_playlists'); }); } function renamePlaylist(req) { var id = req.data.id; var title = req.data.title; app.db.playlists.update({_id: id}, { $set: {title: title} }, function() { req.io.route('fetch_playlists'); }); } function deletePlaylist(req) { del = req.data.del; app.db.playlists.remove({_id: del}, {}, function(err, numRemoved) { req.io.route('fetch_playlists'); }); } function addToPlaylist(req) { addItems = req.data.add; to = req.data.playlist; // pull the playlists database app.db.playlists.findOne({ _id: to}, function(err, doc) { // used as a counter to count how many still need to be added waitingOn = 0; // prep function for use in loop var checkFinish = function() { if (--waitingOn === 0) { req.io.route('fetch_playlists'); } }; // run loop for (var i = 0; i < addItems.length; i++) { var found = false; for (var j = 0; j < doc.songs.length; j++) { if (doc.songs[j]._id == addItems[i]) { found = true; break; } } if (!found) { waitingOn++; app.db.playlists.update({_id: to}, { $push:{songs: {_id: addItems[i]}}}, checkFinish); } } }); } function removeFromPlaylist(req) { removeItems = req.data.remove; to = req.data.playlist; app.db.playlists.findOne({ _id: to}, function(err, doc) { var tmpSongs = []; for (var i = 0; i < doc.songs.length; i++) { if (removeItems.indexOf(doc.songs[i]._id) == -1) { tmpSongs.push(doc.songs[i]); } } app.db.playlists.update({_id: to}, { $set:{songs: tmpSongs}}, function() { req.io.route('fetch_playlists'); }); }); } function songMovedInPlaylist(req) { var playlist_id = req.data.playlist_id; var oldIndex = req.data.oldIndex; var newIndex = req.data.newIndex; app.db.playlists.findOne({ _id: playlist_id}, function(err, playlist) { // remove the item from it's old place var item = playlist.songs.splice(oldIndex, 1)[0]; // add the item into it's new place playlist.songs.splice(newIndex, 0, item); // update the playlist with the new order app.db.playlists.update({_id: playlist_id}, { $set:{songs: playlist.songs}}); }); } // update song play count function updatePlayCount(req) { var _id = req.data.track_id; var plays = req.data.plays; // apply the new play_count to the database app.db.songs.update({_id: _id}, { $set: { play_count: plays } }); } // force rescan of a set of items function rescanItem(req) { var items = req.data.items; var songLocArr = []; app.db.songs.find({ _id: { $in: items }}, function(err, songs) { if (!err && songs) // add the location to the list of songs to scan for (var i = 0; i < songs.length; i++) { songLocArr.push(songs[i].location); } lib_func.scanItems(songLocArr); }); } // update the details for a song function updateSongInfo(req) { // update the details about the song in the database (minus the cover) app.db.songs.update({_id: req.data._id}, { $set: { title: req.data.title, display_artist: req.data.artist, album: req.data.album, // it has been modified, update it date_modified: Date.now(), }, }); // update the cover photo var cover = req.data.cover; if (cover !== null) { // function to be called by both download file and file upload methods var process_cover = function(type, content_buffer) { var cover_filename = md5(content_buffer) + '.' + type; var location = app.get('configDir') + '/dbs/covers/' + cover_filename; fs.exists(location, function(exists) { if (!exists) { fs.writeFile(location, content_buffer, function(err) { if (err) { console.log(err); } else { console.log('Wrote cover here: ' + location); } }); } // update the songs cover even if the image already exists app.db.songs.update({display_artist: req.data.artist, album: req.data.album}, { $set: { cover_location: cover_filename, }, }, { multi: true }, function(err, numReplaced) { app.db.songs.find({display_artist: req.data.artist, album: req.data.album}, function(err, tracks) { app.io.sockets.emit('song_update', tracks); }); }); }); }; // different methods of setting cover if (req.data.cover_is_url && req.data.cover_is_lastfm) { // they fetched the cover from lastfm request(cover, function(error, response, body) { if (!error && response.statusCode == 200) { var type = response.headers['content-type'].split('/').pop(); process_cover(type, body); } }); } else { // they dropped a file to upload var image = util.decodeBase64Image(cover); process_cover(image.type, image.data); } } } // write the tags (metadata) from the database to the files for the given items function rewriteTags(req) { var items = req.data.items; app.db.songs.find({ _id: { $in: items }}, function(err, songs) { if (!err && songs) { // write the tags for all the given files for (var i in songs) { lib_func.saveID3(songs[i]); } } }); } // controller routes function remoteCommand(req, res) { // get params var command = req.params.command; var name = req.params.name; // bool to see if server was found var command_sent = false; // find destination machine and send the command var receivers = getReceiverList(); for (var index in receivers) { var client = receivers[index]; if (client.name && client.name.length > 0 && client.name == name) { client.emit('command', {command: command}); // mark it as sent command_sent = true; } } if (command_sent) { res.send('OK'); } else { res.send('NAME_NOT_FOUND'); } } function setCompName(req) { req.socket.join('receivers'); req.socket.name = req.data.name; } function getReceiverList(req) { var namespace = '/'; var receiversRoom = 'receivers'; var users = []; for (var id in app.io.of(namespace).adapter.rooms[receiversRoom]) { users.push(app.io.of(namespace).adapter.nsp.connected[id]); } return users; } function getReceiversMinusThis(req) { var receivers = getReceiverList(); var validReceivers = []; console.log(receivers); for (var index in receivers) { var client = receivers[index]; if (client.name && client.name.length > 0) { validReceivers.push({ id: client.id, name: client.name, }); } } req.socket.emit('recievers', {recievers: validReceivers}); } // sync routes function syncPageConnected(req) { // get the playlist data get_playlists(function(playlists) { // get the songs data get_songs(function(songs) { req.socket.emit('alldata', {playlists: playlists, songs: songs}); }); }); } // the playlists to sync have been selected, sync them function syncPlaylists(req) { var lists = req.data.playlists; // function for within loop var playlistResult = function(err, numReplaced, newDoc) { if (newDoc) { console.log('Inserted playlist: ' + newDoc.title); } else { console.log('Updated playlist'); } }; // loop over lists to join editable lists for (var list_cnt = 0; list_cnt < lists.length; list_cnt++) { // attempt to replace the playlist if it is editable if (lists[list_cnt].editable) { app.db.playlists.update({_id: lists[list_cnt]._id}, lists[list_cnt], {upsert: true}, playlistResult); } } var songs = req.data.songs; var remote_url = req.data.remote_url; lib_func.sync_import(songs, remote_url); } // download the soundcloud songs from the requested url function soundcloudDownload(req) { lib_func.scDownload(req.data.url); } // download the youtube song function youtubeDownload(req) { lib_func.ytDownload({url: req.data.url}); } function youtubeImport(req) { var queue = async.queue(function(result, next) { // augment the song info object with the url needed result.url = 'https://www.youtube.com/watch?v=' + result.youtube_id; lib_func.ytDownload(result, next); }, app.get('config').youtube.parallel_download); // add all the items to the queue queue.push(req.data.songs); } // update the app settings function updateSettings(req) { // emit the settings updated message to the client req.socket.emit('message', {message: 'Settings Updated'}); // update the settings if (req.data.music_dir) { // first remove all music_dir settings app.db.settings.remove({key: 'music_dir'}, {multi: true}, function() { // add a new one in app.db.settings.insert({key: 'music_dir', value: req.data.music_dir}, function() { // update config app.get('config').music_dir = req.data.music_dir; app.get('config').music_dir_set = true; }); }); } if (req.data.country_code) { // first remove all country_code settings app.db.settings.remove({key: 'country_code'}, {multi: true}, function() { // add a new one in app.db.settings.insert({key: 'country_code', value: req.data.country_code}, function() { // update config app.get('config').country_code = req.data.country_code; }); }); } } // fetch the similar songs for a given title and artist function getSimilarSongs(req) { var title = req.data.title; var artist = req.data.artist; similarSongs.find({ title: title, artist: artist, limit: req.data.limit || 50, lastfmAPIKey: '4795cbddcdec3a6b3f622235caa4b504', lastfmAPISecret: 'cbe22daa03f35df599746f590bf015a5', youtubeAPIKey: app.get('config').youtube.api, }, function(err, songs) { if (err) { console.log(err); // if it couldn't find it, just pass that through if (err.message == 'Track not found') { err = null; songs = []; } } req.socket.emit('similar_songs', {error: err, songs: songs, reqData: req.data}); }); }