dc42e97303fd1921e2e27c06641c02550da2570f
[DeezloaderRemix.git] / app / public / js / frontend.js
1 // Starting area, boot up the API and proceed to eat memory
2
3 // Variables & constants
4 const socket = io.connect(window.location.href)
5 const localStorage = window.localStorage
6 if(typeof mainApp !== "undefined"){
7         var defaultUserSettings = mainApp.defaultSettings
8         var defaultDownloadLocation = mainApp.defaultDownloadDir
9 }
10 let userSettings = []
11
12 let preview_track = document.getElementById('preview-track')
13 let preview_stopped = true
14
15 // Popup message listener
16 socket.on("message", function(desc){
17         message(desc.title, desc.msg)
18 })
19
20 // Prints object obj into console
21 // For Debug purposes
22 socket.on("printObj", function(obj){
23         console.log(obj)
24 })
25
26 //Login button
27 $('#modal_login_btn_login').click(function () {
28         $('#modal_login_btn_login').attr("disabled", true)
29         $('#modal_login_btn_login').html("Logging in...")
30         var username = $('#modal_login_input_username').val()
31         var password = $('#modal_login_input_password').val()
32         var autologin = $('#modal_login_input_autologin').prop("checked")
33         if (autologin){
34                 localStorage.setItem('autologin_email', username)
35         }
36         //Send to the software
37         socket.emit('login', username, password, autologin)
38 })
39
40 // New login system (uses cookies)
41 socket.on('getCookies', function(jar){
42         localStorage.setItem('autologin', JSON.stringify(jar))
43 })
44
45 // After Login
46 socket.on("login", function (data) {
47         if (!data.error) {
48                 $("#modal_settings_username").html(data.user.name)
49                 $("#modal_settings_picture").attr("src",data.user.picture)
50                 $("#side_user").text(data.user.name)
51                 $("#side_avatar").attr("src",data.user.picture)
52                 $("#side_email").text(data.user.email)
53                 $('#initializing').addClass('animated fadeOut').on('webkitAnimationEnd', function () {
54                         $(this).css('display', 'none')
55                         $(this).removeClass('animated fadeOut')
56                 })
57                 // Load top charts list for countries
58                 socket.emit("getChartsCountryList", {selected: userSettings.chartsCountry})
59                 socket.emit("getChartsTrackListByCountry", {country: userSettings.chartsCountry})
60                 // Load personal pubblic playlists
61                 socket.emit("getMyPlaylistList", {})
62         }else{
63                 $('#login-res-text').text(data.error)
64                 setTimeout(function(){$('#login-res-text').text("")},3000)
65         }
66         $('#modal_login_btn_login').attr("disabled", false)
67         $('#modal_login_btn_login').html("Login")
68 })
69
70 // Open downloads folder
71 $('#openDownloadsFolder').on('click', function () {
72         if(typeof shell !== "undefined"){
73                 shell.showItemInFolder(userSettings.downloadLocation + path.sep + '.')
74         }else{
75                 alert("For security reasons, this button will do nothing.")
76         }
77 })
78
79 // Alert for replayGain tag
80 $('#modal_tags_replayGain').on('click', function() {
81         if ($(this).is(':checked')) {
82                 message('Warning','Saving replay gain causes tracks to be quieter for some users.')
83         }
84 })
85
86 // Do misc stuff on page load
87 $(document).ready(function () {
88         // Page Initializing
89         M.AutoInit()
90         preview_track.volume = 0
91         var tabs = M.Tabs.getInstance(document.getElementById("tab-nav"))
92         $('.modal').modal()
93
94         // Autologin
95         socket.emit("getUserSettings")
96         if (localStorage.getItem('autologin')){
97                 socket.emit('autologin', localStorage.getItem('autologin'), localStorage.getItem('autologin_email'))
98                 $('#modal_login_input_autologin').prop('checked', true)
99                 $('#modal_login_btn_login').attr("disabled", true)
100                 $('#modal_login_btn_login').html("Logging in...")
101                 $('#modal_login_input_username').val(localStorage.getItem('autologin_email'))
102                 $('#modal_login_input_password').val("password")
103                 M.updateTextFields()
104         }
105
106         // Side Nav Stuff
107         $('.sidenav').sidenav({
108                 edge: 'right'
109         })
110
111         $('.sidenav_tab').click((e)=>{
112                 e.preventDefault
113                 $(e.currentTarget).addClass("active")
114                 tabs.select($(e.currentTarget).attr('tab-id'))
115                 tabs.updateTabIndicator()
116         })
117
118         // scrollToTop FAB
119         $(window).scroll(function () {
120                 if ($(this).scrollTop() > 100) {
121                         $('#btn_scrollToTop a').removeClass('scale-out').addClass('scale-in')
122                 } else {
123                         $('#btn_scrollToTop a').removeClass('scale-in').addClass('scale-out')
124                 }
125         })
126
127         $('#btn_scrollToTop').click(function () {
128                 $('html, body').animate({scrollTop: 0}, 800)
129                 return false
130         })
131
132         // Playlist Stuff
133         $("#button_refresh_playlist_tab").click(function(){
134                 $("table_personal_playlists").html("")
135                 socket.emit("getMyPlaylistList", {})
136         })
137
138         $('#downloadChartPlaylist').click(function(){
139                 addToQueue(`https://www.deezer.com/playlist/${$(this).data("id")}`)
140         })
141
142         // Track Preview Feature
143         $(preview_track).on('canplay', ()=>{
144                 preview_track.play()
145                 preview_stopped = false
146                 $(preview_track).animate({volume: 1}, 500)
147         })
148
149         $(preview_track).on('timeupdate', ()=>{
150                 if (preview_track.currentTime > preview_track.duration-1){
151                         $(preview_track).animate({volume: 0}, 800)
152                         preview_stopped = true
153                         $("*").removeAttr("playing")
154                         $('.preview_controls').text("play_arrow")
155                         $('.preview_playlist_controls').text("play_arrow")
156                 }
157         })
158
159         $('#modal_trackList, #modal_trackListSelective').modal({
160                 onCloseStart: ()=>{
161                         if ($('.preview_playlist_controls').filter(function(){return $(this).attr("playing")}).length > 0){
162                                 $(preview_track).animate({volume: 0}, 800)
163                                 preview_stopped = true
164                                 $(".preview_playlist_controls").removeAttr("playing")
165                                 $('.preview_playlist_controls').text("play_arrow")
166                         }
167                 }
168         })
169
170         // Night Theme Switch
171         $('#nightTimeSwitcher').change(function(){
172                 if(this.checked){
173                         document.getElementsByTagName('link')[4].disabled = false
174                         $("#nightModeSwitch2").html(`<i class="material-icons">brightness_7</i>Disable Night Mode`)
175                 }else{
176                         document.getElementsByTagName('link')[4].disabled = true
177                         $("#nightModeSwitch2").html(`<i class="material-icons">brightness_2</i>Enable Night Mode`)
178                 }
179                 localStorage.darkMode = this.checked
180         })
181
182         $('#nightModeSwitch2').click(()=>{
183                 $('#nightTimeSwitcher').prop('checked', !$('#nightTimeSwitcher').prop('checked'))
184                 $('#nightTimeSwitcher').change()
185         })
186
187         if (eval(localStorage.darkMode)){
188                 $('#nightTimeSwitcher').prop('checked', true)
189                 $('#nightTimeSwitcher').change()
190         }else{
191                 $('#nightTimeSwitcher').prop('checked', false)
192                 $('#nightTimeSwitcher').change()
193         }
194
195         // Search on tab change
196         $('input[name=searchMode][type=radio]').change(()=>{
197                 $('#tab_search_form_search').submit()
198         })
199
200         // Button download all tracks in selective modal
201         $('#download_all_tracks_selective, #download_all_tracks').click(function(){
202                 addToQueue($(this).attr("data-link"))
203         })
204 })
205
206 // Load settings
207 socket.on('getUserSettings', function (data) {
208         userSettings = data.settings
209         console.log('Settings refreshed')
210 })
211
212 /**
213  *      Modal Area START
214  */
215
216 // Prevent default behavior of closing button
217 $('.modal-close').click(function (e) {
218         e.preventDefault()
219 })
220
221 // Settings Modal START
222 const $settingsAreaParent = $('#modal_settings')
223
224 // Open settings panel
225 $('#nav_btn_openSettingsModal, #sidenav_settings').click(function () {
226         fillSettingsModal(userSettings)
227 })
228
229 // Save settings button
230 $('#modal_settings_btn_saveSettings').click(function () {
231         let settings = {}
232         // Save
233         settings.userDefined = {
234                 trackNameTemplate: $('#modal_settings_input_trackNameTemplate').val(),
235                 playlistTrackNameTemplate: $('#modal_settings_input_playlistTrackNameTemplate').val(),
236                 albumTrackNameTemplate: $('#modal_settings_input_albumTrackNameTemplate').val(),
237                 albumNameTemplate: $('#modal_settings_input_albumNameTemplate').val(),
238                 coverImageTemplate: $('#modal_settings_input_coverImageTemplate').val(),
239                 artistImageTemplate: $('#modal_settings_input_artistImageTemplate').val(),
240                 createM3UFile: $('#modal_settings_cbox_createM3UFile').is(':checked'),
241                 createArtistFolder: $('#modal_settings_cbox_createArtistFolder').is(':checked'),
242                 createAlbumFolder: $('#modal_settings_cbox_createAlbumFolder').is(':checked'),
243                 createCDFolder: $('#modal_settings_cbox_createCDFolder').is(':checked'),
244                 downloadLocation: $('#modal_settings_input_downloadTracksLocation').val(),
245                 artworkSize: parseInt($('#modal_settings_select_artworkSize').val()),
246                 hifi: $('#modal_settings_cbox_hifi').is(':checked'),
247                 padtrck: $('#modal_settings_cbox_padtrck').is(':checked'),
248                 syncedlyrics: $('#modal_settings_cbox_syncedlyrics').is(':checked'),
249                 numplaylistbyalbum: $('#modal_settings_cbox_numplaylistbyalbum').is(':checked'),
250                 chartsCountry: $('#modal_settings_select_chartsCounrty').val(),
251                 spotifyUser: $('#modal_settings_input_spotifyUser').val(),
252                 saveArtwork: $('#modal_settings_cbox_saveArtwork').is(':checked'),
253                 saveArtworkArtist: $('#modal_settings_cbox_saveArtworkArtist').is(':checked'),
254                 logErrors: $('#modal_settings_cbox_logErrors').is(':checked'),
255                 logSearched: $('#modal_settings_cbox_logSearched').is(':checked'),
256                 queueConcurrency: parseInt($('#modal_settings_number_queueConcurrency').val()),
257                 paddingSize: $('#modal_settings_number_paddingSize').val(),
258                 multitagSeparator: $('#modal_settings_select_multitagSeparator').val(),
259                 maxBitrate: $('#modal_settings_select_maxBitrate').val(),
260                 PNGcovers: $('#modal_settings_cbox_PNGcovers').is(':checked'),
261                 removeAlbumVersion : $('#modal_settings_cbox_removeAlbumVersion').is(':checked'),
262                 dateFormat: $('#modal_settings_select_dateFormat').val(),
263                 dateFormatYear: $('#modal_settings_select_dateFormatYear').val(),
264                 fallbackBitrate : $('#modal_settings_cbox_fallbackBitrate').is(':checked'),
265                 minimizeToTray : $('#modal_settings_cbox_minimizeToTray').is(':checked'),
266                 saveFullArtists : $('#modal_settings_cbox_saveFullArtists').is(':checked'),
267                 tags: {
268                         title: $('#modal_tags_title').is(':checked'),
269                         artist: $('#modal_tags_artist').is(':checked'),
270                         album: $('#modal_tags_album').is(':checked'),
271                         cover: $('#modal_tags_cover').is(':checked'),
272                         trackNumber: $('#modal_tags_trackNumber').is(':checked'),
273                         trackTotal: $('#modal_tags_trackTotal').is(':checked'),
274                         discNumber: $('#modal_tags_discNumber').is(':checked'),
275                         discTotal: $('#modal_tags_discTotal').is(':checked'),
276                         albumArtist: $('#modal_tags_albumArtist').is(':checked'),
277                         genre: $('#modal_tags_genre').is(':checked'),
278                         year: $('#modal_tags_year').is(':checked'),
279                         date: $('#modal_tags_date').is(':checked'),
280                         explicit: $('#modal_tags_explicit').is(':checked'),
281                         isrc: $('#modal_tags_isrc').is(':checked'),
282                         length: $('#modal_tags_length').is(':checked'),
283                         barcode: $('#modal_tags_barcode').is(':checked'),
284                         bpm: $('#modal_tags_bpm').is(':checked'),
285                         replayGain: $('#modal_tags_replayGain').is(':checked'),
286                         publisher: $('#modal_tags_publisher').is(':checked'),
287                         unsynchronisedLyrics: $('#modal_tags_unsynchronisedLyrics').is(':checked'),
288                         copyright: $('#modal_tags_copyright').is(':checked'),
289                         musicpublisher: $('#modal_tags_musicpublisher').is(':checked'),
290                         composer: $('#modal_tags_composer').is(':checked'),
291                         mixer: $('#modal_tags_mixer').is(':checked'),
292                         author: $('#modal_tags_author').is(':checked'),
293                         writer: $('#modal_tags_writer').is(':checked'),
294                         engineer: $('#modal_tags_engineer').is(':checked'),
295                         producer: $('#modal_tags_producer').is(':checked')
296                 }
297         }
298         // Send updated settings to be saved into config file
299         socket.emit('saveSettings', settings)
300         socket.emit('getUserSettings')
301 })
302
303 // Reset defaults button
304 $('#modal_settings_btn_defaultSettings').click(function () {
305         if(typeof defaultDownloadLocation !== 'undefined'){
306                 defaultUserSettings.downloadLocation = defaultDownloadLocation
307                 fillSettingsModal(defaultUserSettings)
308         }
309 })
310
311 // Sign Up Button
312 $('#modal_login_btn_signup').click(function(){
313         if(typeof shell != 'undefined'){
314                 shell.openExternal("https://www.deezer.com/register")
315         }else{
316                 window.open("https://www.deezer.com/register")
317         }
318 })
319
320 // Logout Button
321 $('#modal_settings_btn_logout').click(function () {
322         $('#modal_login_input_username').val("")
323         $('#modal_login_input_password').val("")
324         $('#modal_login_input_autologin').prop("checked",false)
325         $('#initializing').css('display', '')
326         $('#initializing').addClass('animated fadeIn').on('webkitAnimationEnd', function () {
327                 $(this).removeClass('animated fadeIn')
328                 $(this).css('display', '')
329         })
330         localStorage.removeItem("autologin")
331         localStorage.removeItem("autologin_email")
332         socket.emit('logout')
333 })
334
335 // Populate settings fields
336 function fillSettingsModal(settings) {
337         $('#modal_settings_input_trackNameTemplate').val(settings.trackNameTemplate)
338         $('#modal_settings_input_playlistTrackNameTemplate').val(settings.playlistTrackNameTemplate)
339         $('#modal_settings_input_albumTrackNameTemplate').val(settings.albumTrackNameTemplate)
340         $('#modal_settings_input_albumNameTemplate').val(settings.albumNameTemplate)
341         $('#modal_settings_input_coverImageTemplate').val(settings.coverImageTemplate)
342         $('#modal_settings_input_artistImageTemplate').val(settings.artistImageTemplate)
343         $('#modal_settings_cbox_createM3UFile').prop('checked', settings.createM3UFile)
344         $('#modal_settings_cbox_createArtistFolder').prop('checked', settings.createArtistFolder)
345         $('#modal_settings_cbox_createAlbumFolder').prop('checked', settings.createAlbumFolder)
346         $('#modal_settings_cbox_createCDFolder').prop('checked', settings.createCDFolder)
347         $('#modal_settings_cbox_hifi').prop('checked', settings.hifi)
348         $('#modal_settings_cbox_padtrck').prop('checked', settings.padtrck)
349         $('#modal_settings_cbox_syncedlyrics').prop('checked', settings.syncedlyrics)
350         $('#modal_settings_cbox_numplaylistbyalbum').prop('checked', settings.numplaylistbyalbum)
351         $('#modal_settings_input_downloadTracksLocation').val(settings.downloadLocation)
352         $('#modal_settings_select_artworkSize').val(settings.artworkSize).formSelect()
353         $('#modal_settings_select_chartsCounrty').val(settings.chartsCountry).formSelect()
354         $('#modal_settings_input_spotifyUser').val(settings.spotifyUser)
355         $('#modal_settings_cbox_saveArtwork').prop('checked', settings.saveArtwork)
356         $('#modal_settings_cbox_saveArtworkArtist').prop('checked', settings.saveArtworkArtist)
357         $('#modal_settings_cbox_logErrors').prop('checked', settings.logErrors)
358         $('#modal_settings_cbox_logSearched').prop('checked', settings.logSearched)
359         $('#modal_settings_number_queueConcurrency').val(settings.queueConcurrency)
360         $('#modal_settings_number_paddingSize').val(settings.paddingSize)
361         $('#modal_settings_select_multitagSeparator').val(settings.multitagSeparator).formSelect()
362         $('#modal_settings_select_maxBitrate').val(settings.maxBitrate).formSelect()
363         $('#modal_settings_cbox_PNGcovers').prop('checked', settings.PNGcovers)
364         $('#modal_settings_cbox_removeAlbumVersion').prop('checked', settings.removeAlbumVersion)
365         $('#modal_settings_select_dateFormat').val(settings.dateFormat).formSelect()
366         $('#modal_settings_select_dateFormatYear').val(settings.dateFormatYear).formSelect()
367         $('#modal_settings_cbox_fallbackBitrate').prop('checked', settings.fallbackBitrate)
368         $('#modal_settings_cbox_minimizeToTray').prop('checked', settings.minimizeToTray)
369         $('#modal_settings_cbox_saveFullArtists').prop('checked', settings.saveFullArtists)
370
371         $('#modal_tags_title').prop('checked', settings.tags.title)
372         $('#modal_tags_artist').prop('checked', settings.tags.artist)
373         $('#modal_tags_album').prop('checked', settings.tags.album)
374         $('#modal_tags_cover').prop('checked', settings.tags.cover)
375         $('#modal_tags_trackNumber').prop('checked', settings.tags.trackNumber)
376         $('#modal_tags_trackTotal').prop('checked', settings.tags.trackTotal)
377         $('#modal_tags_discNumber').prop('checked', settings.tags.discNumber)
378         $('#modal_tags_discTotal').prop('checked', settings.tags.discTotal)
379         $('#modal_tags_albumArtist').prop('checked', settings.tags.albumArtist)
380         $('#modal_tags_genre').prop('checked', settings.tags.genre)
381         $('#modal_tags_year').prop('checked', settings.tags.year)
382         $('#modal_tags_date').prop('checked', settings.tags.date)
383         $('#modal_tags_explicit').prop('checked', settings.tags.explicit)
384         $('#modal_tags_isrc').prop('checked', settings.tags.isrc)
385         $('#modal_tags_length').prop('checked', settings.tags.length)
386         $('#modal_tags_barcode').prop('checked', settings.tags.barcode)
387         $('#modal_tags_bpm').prop('checked', settings.tags.bpm)
388         $('#modal_tags_replayGain').prop('checked', settings.tags.replayGain)
389         $('#modal_tags_publisher').prop('checked', settings.tags.publisher)
390         $('#modal_tags_unsynchronisedLyrics').prop('checked', settings.tags.unsynchronisedLyrics)
391         $('#modal_tags_copyright').prop('checked', settings.tags.copyright)
392         $('#modal_tags_musicpublisher').prop('checked', settings.tags.musicpublisher)
393         $('#modal_tags_composer').prop('checked', settings.tags.composer)
394         $('#modal_tags_mixer').prop('checked', settings.tags.mixer)
395         $('#modal_tags_author').prop('checked', settings.tags.author)
396         $('#modal_tags_writer').prop('checked', settings.tags.writer)
397         $('#modal_tags_engineer').prop('checked', settings.tags.engineer)
398         $('#modal_tags_producer').prop('checked', settings.tags.producer)
399
400         M.updateTextFields()
401 }
402
403
404 //#############################################MODAL_MSG##############################################\\
405 function message(title, message) {
406         $('#modal_msg_title').html(title)
407         $('#modal_msg_message').html(message)
408         $('#modal_msg').modal('open')
409 }
410
411 //****************************************************************************************************\\
412 //************************************************TABS************************************************\\
413 //****************************************************************************************************\\
414
415 //#############################################TAB_SEARCH#############################################\\
416
417 // Submit Search Form
418 $('#tab_search_form_search').submit(function (ev) {
419         ev.preventDefault()
420
421         var searchString = $('#tab_search_form_search_input_searchString').val().trim()
422         var mode = $('#tab_search_form_search').find('input[name=searchMode]:checked').val()
423
424         if (searchString.length == 0) {return}
425
426         // Clean Table and show loading indicator
427         $('#tab_search_table_results').find('thead').find('tr').addClass('hide')
428         $('#tab_search_table_results_tbody_results').addClass('hide')
429         $('#tab_search_table_results_tbody_noResults').addClass('hide')
430         $('#tab_search_table_results_tbody_loadingIndicator').removeClass('hide')
431
432         socket.emit("search", {type: mode, text: searchString})
433 })
434
435 // Parse data from search
436 socket.on('search', function (data) {
437         // Remove loading indicator
438         $('#tab_search_table_results_tbody_loadingIndicator').addClass('hide')
439
440         // If no data, display No Results Found
441         if (data.items.length == 0) {
442                 $('#tab_search_table_results_tbody_noResults').removeClass('hide')
443                 return
444         }
445
446         // Populate table and show results
447         if (data.type == 'track') {
448                 showResults_table_track(data.items)
449         } else if (data.type == 'album') {
450                 showResults_table_album(data.items)
451         } else if (data.type == 'artist') {
452                 showResults_table_artist(data.items)
453         } else if (data.type == 'playlist') {
454                 showResults_table_playlist(data.items)
455         }
456         $('#tab_search_table_results_tbody_results').removeClass('hide')
457 })
458
459 function showResults_table_track(tracks) {
460         var tableBody = $('#tab_search_table_results_tbody_results')
461         $(tableBody).html('')
462         $('#tab_search_table_results_thead_track').removeClass('hide')
463         for (var i = 0; i < tracks.length; i++) {
464                 var currentResultTrack = tracks[i]
465                 $(tableBody).append(
466                         `<tr>
467                         <td><a href="#" class="rounded ${(currentResultTrack.preview ? `single-cover" preview="${currentResultTrack.preview}"><i class="material-icons preview_controls white-text">play_arrow</i>` : '">')}<img style="width:56px;" class="rounded" src="${(currentResultTrack.album.cover_small ? currentResultTrack.album.cover_small : "img/noCover.jpg" )}"/></a></td>
468                         <td class="hide-on-med-and-up">
469                                 <p class="remove-margin">${(currentResultTrack.explicit_lyrics ? ' <i class="material-icons valignicon tiny materialize-red-text">explicit</i>' : '')} ${currentResultTrack.title}</p>
470                                 <p class="remove-margin secondary-text">${currentResultTrack.artist.name}</p>
471                                 <p class="remove-margin secondary-text">${currentResultTrack.album.title}</p>
472                         </td>
473                         <td class="hide-on-small-only">${(currentResultTrack.explicit_lyrics ? ' <i class="material-icons valignicon tiny materialize-red-text">explicit</i>' : '')} ${currentResultTrack.title}</td>
474                         <td class="hide-on-small-only"><span class="resultArtist resultLink" data-link="${currentResultTrack.artist.link}">${currentResultTrack.artist.name}</span></td>
475                         <td class="hide-on-small-only"><span class="resultAlbum resultLink" data-link="https://www.deezer.com/album/${currentResultTrack.album.id}">${currentResultTrack.album.title}</span></td>
476                         <td>${convertDuration(currentResultTrack.duration)}</td>
477                         </tr>`)
478                 generateDownloadLink(currentResultTrack.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
479                 addPreviewControlsHover(tableBody.children('tr:last').find('.preview_controls'))
480                 addPreviewControlsClick(tableBody.children('tr:last').find('.single-cover'))
481                 tableBody.children('tr:last').find('.resultArtist').click(function (ev){
482                         ev.preventDefault()
483                         showTrackList($(this).data("link"))
484                 })
485                 tableBody.children('tr:last').find('.resultAlbum').click(function (ev){
486                         ev.preventDefault()
487                         showTrackListSelective($(this).data("link"))
488                 })
489         }
490 }
491
492 function showResults_table_album(albums) {
493         var tableBody = $('#tab_search_table_results_tbody_results')
494         $(tableBody).html('')
495         $('#tab_search_table_results_thead_album').removeClass('hide')
496         for (var i = 0; i < albums.length; i++) {
497                 var currentResultAlbum = albums[i]
498                 $(tableBody).append(
499                                 `<tr>
500                                 <td><img style="width:56px;" src="${(currentResultAlbum.cover_small ? currentResultAlbum.cover_small : "img/noCover.jpg")}" class="rounded" /></td>
501                                 <td class="hide-on-med-and-up">
502                                         <p class="remove-margin">${(currentResultAlbum.explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i>' : '')} ${currentResultAlbum.title}</p>
503                                         <p class="remove-margin secondary-text">${currentResultAlbum.artist.name}</p>
504                                         <p class="remove-margin secondary-text">${currentResultAlbum.nb_tracks == "1" ? `1 Track` : `${currentResultAlbum.nb_tracks} Tracks`} | ${currentResultAlbum.record_type[0].toUpperCase() + currentResultAlbum.record_type.substring(1)}</p>
505                                 </td>
506                                 <td class="hide-on-small-only">${(currentResultAlbum.explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i>' : '')} ${currentResultAlbum.title}</td>
507                                 <td class="hide-on-small-only"><span class="resultArtist resultLink" data-link="${currentResultAlbum.artist.link}">${currentResultAlbum.artist.name}</span></td>
508                                 <td class="hide-on-small-only">${currentResultAlbum.nb_tracks}</td>
509                                 <td class="hide-on-small-only">${currentResultAlbum.record_type[0].toUpperCase() + currentResultAlbum.record_type.substring(1)}</td>
510                                 </tr>`)
511                 generateShowTracklistSelectiveButton(currentResultAlbum.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
512                 generateDownloadLink(currentResultAlbum.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
513                 tableBody.children('tr:last').find('.resultArtist').click(function (ev){
514                         ev.preventDefault()
515                         showTrackList($(this).data("link"))
516                 })
517         }
518         $('.tooltipped').tooltip({delay: 100})
519 }
520
521 function showResults_table_artist(artists) {
522         var tableBody = $('#tab_search_table_results_tbody_results')
523         $(tableBody).html('')
524         $('#tab_search_table_results_thead_artist').removeClass('hide')
525         for (var i = 0; i < artists.length; i++) {
526                 var currentResultArtist = artists[i]
527                 $(tableBody).append(
528                                 `<tr>
529                                 <td><img style="width:56px;" src="${(currentResultArtist.picture_small ? currentResultArtist.picture_small : "img/noCover.jpg")}" class="rounded" /></td>
530                                 <td>${currentResultArtist.name}</td>
531                                 <td>${currentResultArtist.nb_album}</td>
532                                 </tr>`)
533                 generateShowTracklistButton(currentResultArtist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
534                 generateDownloadLink(currentResultArtist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
535         }
536 }
537
538 function showResults_table_playlist(playlists) {
539         var tableBody = $('#tab_search_table_results_tbody_results')
540         $(tableBody).html('')
541         $('#tab_search_table_results_thead_playlist').removeClass('hide')
542         for (var i = 0; i < playlists.length; i++) {
543                 var currentResultPlaylist = playlists[i]
544                 $(tableBody).append(
545                                 `<tr>
546                                 <td><img style="width:56px;" src="${(currentResultPlaylist.picture_small ? currentResultPlaylist.picture_small : "img/noCover.jpg")}" class="rounded" /></td>
547                                 <td>${currentResultPlaylist.title}</td>
548                                 <td>${currentResultPlaylist.nb_tracks}</td>
549                                 </tr>`)
550                 generateShowTracklistSelectiveButton(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
551                 generateDownloadLink(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
552         }
553         $('.tooltipped').tooltip({delay: 100})
554 }
555
556 function generateShowTracklistSelectiveButton(link) {
557         var btn_showTrackListSelective = $('<a href="#" class="waves-effect btn-flat"><i class="material-icons">list</i></a>')
558         $(btn_showTrackListSelective).click(function (ev){
559                 ev.preventDefault()
560                 showTrackListSelective(link)
561         })
562         return btn_showTrackListSelective
563 }
564
565 function generateShowTracklistButton(link) {
566         var btn_showTrackList = $('<a href="#" class="waves-effect btn-flat"><i class="material-icons">list</i></a>')
567         $(btn_showTrackList).click(function (ev) {
568                 ev.preventDefault()
569                 showTrackList(link)
570         })
571         return btn_showTrackList
572 }
573 // TODO: Finish Vue.js Implementation
574 var trackListSelectiveModalApp = new Vue({
575         el: '#modal_trackListSelective',
576         data: {
577                 title: null,
578                 type: null,
579                 link: null,
580                 head: null,
581                 body: []
582         }
583 })
584
585 var trackListModalApp = new Vue({
586         el: '#modal_trackList',
587         data: {
588                 title: null,
589                 type: null,
590                 link: null,
591                 head: null,
592                 body: []
593         }
594 })
595
596 function showTrackListSelective(link) {
597         $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective').addClass('hide')
598         $('#modal_trackListSelective_table_trackListSelective_tbody_loadingIndicator').removeClass('hide')
599         $('#modal_trackListSelective').modal('open')
600         socket.emit('getTrackList', {id: getIDFromLink(link), type: getTypeFromLink(link)})
601 }
602
603 $('#download_track_selection').click(function(e){
604         e.preventDefault()
605         var urls = []
606         $("input:checkbox.trackCheckbox:checked").each(function(){
607                 urls.push($(this).val())
608         })
609         if(urls.length != 0){
610                 for (var ia = 0; ia < urls.length; ia++) {
611                         addToQueue(urls[ia])
612                 }
613         }
614         $('#modal_trackListSelective').modal('close')
615 })
616
617 function showTrackList(link) {
618         $('#modal_trackList_table_trackList_tbody_trackList').addClass('hide')
619         $('#modal_trackList_table_trackList_tbody_loadingIndicator').removeClass('hide')
620         $('#modal_trackList').modal('open')
621         socket.emit("getTrackList", {id: getIDFromLink(link), type: getTypeFromLink(link)})
622 }
623
624 socket.on("getTrackList", function (data) {
625         //data.err                      -> undefined/err
626         //data.id                        -> passed id
627         //data.response -> API response
628         if (data.err){
629                 trackListSelectiveModalApp.title = "Can't get data"
630                 console.log(data.err)
631                 return
632         }
633         if (data.response){
634                 var trackList = data.response.data, content = ''
635                 var trackListSelective = data.response.data, content = ''
636                 if (typeof trackList == 'undefined') {
637                         alert('Well, there seems to be a problem with this part of the app. Please notify the developer.')
638                         return
639                 }
640
641                 // ########################################
642                 if(data.reqType == 'album' || data.reqType == 'playlist'){
643                         var tableBody = $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective')
644                 } else {
645                         var tableBody = $('#modal_trackList_table_trackList_tbody_trackList')
646                 }
647                 $(tableBody).html('')
648                 //############################################
649                 if (data.reqType == 'artist') {
650                         trackListModalApp.type = data.reqType
651                         trackListModalApp.link = `https://www.deezer.com/${data.reqType}/${data.id}`
652                         trackListModalApp.title = 'Album List'
653                         trackListModalApp.head = [
654                                 {title: '#'},
655                                 {title: ''},
656                                 {title: 'Album Title'},
657                                 {title: 'Release Date'},
658                                 {title: 'Record Type'},
659                                 {title: 'Download Album'}
660                         ]
661                         for (var i = 0; i < trackList.length; i++) {
662                                 $(tableBody).append(
663                                         `<tr>
664                                         <td>${(i + 1)}</td>
665                                         <td>${(trackList[i].explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i>' : '')}</td>
666                                         <td><a href="#" class="album_chip" data-link="${trackList[i].link}"><div class="chip"><img src="${trackList[i].cover_small}"/>${trackList[i].title}</div></a></td>
667                                         <td>${trackList[i].release_date}</td>
668                                         <td>${trackList[i].record_type[0].toUpperCase() + trackList[i].record_type.substring(1)}</td>
669                                         </tr>`
670                                 )
671                                 generateDownloadLink(trackList[i].link).appendTo(tableBody.children('tr:last')).wrap('<td>')
672                         }
673                         $('.album_chip').click(function(e){
674                                 showTrackListSelective($(this).data('link'), true)
675                         })
676                 } else if(data.reqType == 'playlist') {
677                         trackListSelectiveModalApp.type = data.reqType
678                         trackListSelectiveModalApp.link = `https://www.deezer.com/${data.reqType}/${data.id}`
679                         trackListSelectiveModalApp.title = 'Playlist'
680                         trackListSelectiveModalApp.head = [
681                                 {title: '<i class="material-icons">music_note</i>'},
682                                 {title: '#'},
683                                 {title: 'Song'},
684                                 {title: 'Artist'},
685                                 {title: '<i class="material-icons">timer</i>'},
686                                 {title: '<div class="valign-wrapper"><label><input class="selectAll" type="checkbox" id="selectAll"><span></span></label></div>'}
687                         ]
688                         $('.selectAll').prop('checked', false)
689                         for (var i = 0; i < trackList.length; i++) {
690                                 $(tableBody).append(
691                                         `<tr>
692                                         <td><i class="material-icons ${(trackList[i].preview ? `preview_playlist_controls" preview="${trackList[i].preview}"` : 'grey-text"')}>play_arrow</i></td>
693                                         <td>${(i + 1)}</td>
694                                         <td>${(trackList[i].explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i> ' : '')}${trackList[i].title}</td>
695                                         <td>${trackList[i].artist.name}</td>
696                                         <td>${convertDuration(trackList[i].duration)}</td>
697                                         <td>
698                                                 <div class="valign-wrapper">
699                                                 <label>
700                                                 <input class="trackCheckbox valign" type="checkbox" id="trackChk${i}" value="${trackList[i].link}"><span></span>
701                                                 </label>
702                                                 </div>
703                                         </td>
704                                         </tr>`
705                                 )
706                                 addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
707                         }
708                 } else if(data.reqType == 'album') {
709                         trackListSelectiveModalApp.type = data.reqType
710                         trackListSelectiveModalApp.link = `https://www.deezer.com/${data.reqType}/${data.id}`
711                         trackListSelectiveModalApp.title = 'Tracklist'
712                         trackListSelectiveModalApp.head = [
713                                 {title: '<i class="material-icons">music_note</i>'},
714                                 {title: '#'},
715                                 {title: 'Song'},
716                                 {title: 'Artist'},
717                                 {title: '<i class="material-icons">timer</i>'},
718                                 {title: '<div class="valign-wrapper"><label><input class="selectAll" type="checkbox" id="selectAll"><span></span></label></div>'}
719                         ]
720                         $('.selectAll').prop('checked', false)
721                         if (trackList[trackList.length-1].disk_number != 1){
722                                 baseDisc = 0
723                         } else {
724                                 baseDisc =1
725                         }
726                         for (var i = 0; i < trackList.length; i++) {
727                                 discNum = trackList[i].disk_number
728                                 if (discNum != baseDisc){
729                                         $(tableBody).append(`<tr><td colspan="4" style="opacity: 0.54;"><i class="material-icons valignicon tiny">album</i>${discNum}</td></tr>`)
730                                         baseDisc = discNum
731                                 }
732                                 $(tableBody).append(
733                                         `<tr>
734                                         <td><i class="material-icons ${(trackList[i].preview ? `preview_playlist_controls" preview="${trackList[i].preview}"` : 'grey-text"')}>play_arrow</i></td>
735                                         <td>${trackList[i].track_position}</td>
736                                         <td>${(trackList[i].explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i> ' : '')}${trackList[i].title}</td>
737                                         <td>${trackList[i].artist.name}</td>
738                                         <td>${convertDuration(trackList[i].duration)}</td>
739                                         <td>
740                                                 <div class="valign-wrapper">
741                                                 <label>
742                                                 <input class="trackCheckbox valign" type="checkbox" id="trackChk${i}" value="${trackList[i].link}"><span></span>
743                                                 </label>
744                                                 </div>
745                                         </td>
746                                         </tr>`
747                                 )
748                                 addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
749                         }
750                 } else if(data.reqType == 'spotifyplaylist') {
751                         trackListModalApp.type = "Spotify Playlist"
752                         trackListModalApp.link = 'spotify:playlist:'+data.id
753                         trackListModalApp.title = 'Tracklist'
754                         trackListModalApp.head = [
755                                 {title: '<i class="material-icons">music_note</i>'},
756                                 {title: '#'},
757                                 {title: 'Song'},
758                                 {title: 'Artist'},
759                                 {title: '<i class="material-icons">timer</i>'}
760                         ]
761                         for (var i = 0; i < trackList.length; i++) {
762                                 $(tableBody).append(
763                                         `<tr>
764                                         <td><i class="material-icons ${(trackList[i].preview ? `preview_playlist_controls" preview="${trackList[i].preview}"` : 'grey-text"')}>play_arrow</i></td>
765                                         <td>${(i + 1)}</td>
766                                         <td>${(trackList[i].explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i> ' : '')}${trackList[i].title}</td>
767                                         <td>${trackList[i].artist.name}</td>
768                                         <td>${convertDuration(trackList[i].duration)}</td>
769                                         </tr>`
770                                 )
771                                 addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
772                         }
773                 } else {
774                         trackListModalApp.type = null
775                         trackListModalApp.title = 'Tracklist'
776                         trackListModalApp.head = [
777                                 {title: '<i class="material-icons">music_note</i>'},
778                                 {title: '#'},
779                                 {title: 'Song'},
780                                 {title: 'Artist'},
781                                 {title: '<i class="material-icons">timer</i>'}
782                         ]
783                         for (var i = 0; i < trackList.length; i++) {
784                                 $(tableBody).append(
785                                         `<tr>
786                                         <td><i class="material-icons ${(trackList[i].preview ? `preview_playlist_controls" preview="${trackList[i].preview}"` : 'grey-text"')}>play_arrow</i></td>
787                                         <td>${(i + 1)}</td>
788                                         <td>${(trackList[i].explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i> ' : '')}${trackList[i].title}</td>
789                                         <td>${trackList[i].artist.name}</td>
790                                         <td>${convertDuration(trackList[i].duration)}</td>
791                                         </tr>`
792                                 )
793                                 addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
794                         }
795                 }
796                 if(data.reqType == 'album' || data.reqType == 'playlist'){
797                         $('#modal_trackListSelective_table_trackListSelective_tbody_loadingIndicator').addClass('hide')
798                         $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective').removeClass('hide')
799                 } else {
800                         $('#modal_trackList_table_trackList_tbody_loadingIndicator').addClass('hide')
801                         $('#modal_trackList_table_trackList_tbody_trackList').removeClass('hide')
802                 }
803                 //$('#modal_trackList_table_trackList_tbody_trackList').html(content)
804         }
805 })
806
807 //#############################################TAB_CHARTS#############################################\\
808 socket.on("getChartsCountryList", function (data) {
809         //data.countries                -> Array
810         //data.countries[0].country -> String (country name)
811         //data.countries[0].picture_small/picture_medium/picture_big -> url to cover
812         for (var i = 0; i < data.countries.length; i++) {
813                 $('#tab_charts_select_country').append('<option value="' + data.countries[i]['country'] + '" data-icon="' + data.countries[i]['picture_small'] + '" class="left rounded">' + data.countries[i]['country'] + '</option>')
814                 $('#modal_settings_select_chartsCounrty').append('<option value="' + data.countries[i]['country'] + '" data-icon="' + data.countries[i]['picture_small'] + '" class="left rounded">' + data.countries[i]['country'] + '</option>')
815         }
816         $('#tab_charts_select_country').find('option[value="' + data.selected + '"]').attr("selected", true)
817         $('#modal_settings_select_chartsCounrty').find('option[value="' + data.selected + '"]').attr("selected", true)
818         $('select').formSelect()
819 })
820
821 socket.on("setChartsCountry", function (data) {
822         $('#tab_charts_select_country').find('option[value="' + data.selected + '"]').attr("selected", true)
823         $('#modal_settings_select_chartsCounrty').find('option[value="' + data.selected + '"]').attr("selected", true)
824         $('select').formSelect()
825 })
826
827 $('#tab_charts_select_country').on('change', function () {
828         var country = $(this).find('option:selected').val()
829         $('#tab_charts_table_charts_tbody_charts').addClass('hide')
830         $('#tab_charts_table_charts_tbody_loadingIndicator').removeClass('hide')
831         socket.emit("getChartsTrackListByCountry", {country: country})
832 })
833
834 socket.on("getChartsTrackListByCountry", function (data) {
835         //data.playlist         -> Object with Playlist information
836         //data.tracks                   -> Array
837         //data.tracks[0]         -> Object of track 0
838         $("#downloadChartPlaylist").data("id", data.playlistId)
839         var chartsTableBody = $('#tab_charts_table_charts_tbody_charts'), currentChartTrack
840         chartsTableBody.html('')
841         for (var i = 0; i < data.tracks.length; i++) {
842                 currentChartTrack = data.tracks[i]
843                 $(chartsTableBody).append(
844                                 `<tr>
845                                 <td>${(i + 1)}</td>
846                                 <td><a href="#" class="rounded ${(currentChartTrack.preview ? `single-cover" preview="${currentChartTrack.preview}"><i class="material-icons preview_controls white-text">play_arrow</i>` : '">')}<img style="width:56px;" src="${(currentChartTrack.album.cover_small ? currentChartTrack.album.cover_small : "img/noCover.jpg")}" class="rounded" /></a></td>
847                                 <td class="hide-on-med-and-up">
848                                         <p class="remove-margin">${(currentChartTrack.explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i> ' : '')}${currentChartTrack.title}</p>
849                                         <p class="remove-margin secondary-text">${currentChartTrack.artist.name}</p>
850                                         <p class="remove-margin secondary-text">${currentChartTrack.album.title}</p>
851                                 </td>
852                                 <td class="hide-on-small-only">${(currentChartTrack.explicit_lyrics ? '<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="Explicit">explicit</i> ' : '')}${currentChartTrack.title}</td>
853                                 <td class="hide-on-small-only"><span class="resultArtist resultLink" data-link="${currentChartTrack.artist.link}">${currentChartTrack.artist.name}</span></td>
854                                 <td class="hide-on-small-only"><span class="resultAlbum resultLink" data-link="https://www.deezer.com/album/${currentChartTrack.album.id}">${currentChartTrack.album.title}</span></td>
855                                 <td>${convertDuration(currentChartTrack.duration)}</td>
856                                 </tr>`)
857                 generateDownloadLink(currentChartTrack.link).appendTo(chartsTableBody.children('tr:last')).wrap('<td>')
858                 addPreviewControlsHover(chartsTableBody.children('tr:last').find('.preview_controls'))
859                 addPreviewControlsClick(chartsTableBody.children('tr:last').find('.single-cover'))
860                 chartsTableBody.children('tr:last').find('.resultArtist').click(function (ev){
861                         ev.preventDefault()
862                         showTrackList($(this).data("link"))
863                 })
864                 chartsTableBody.children('tr:last').find('.resultAlbum').click(function (ev){
865                         ev.preventDefault()
866                         showTrackListSelective($(this).data("link"))
867                 })
868         }
869         $('#tab_charts_table_charts_tbody_loadingIndicator').addClass('hide')
870         chartsTableBody.removeClass('hide')
871 })
872
873 //#############################################TAB_PLAYLISTS############################################\\
874 socket.on("getMyPlaylistList", function (data) {
875         var tableBody = $('#table_personal_playlists')
876         $(tableBody).html('')
877         for (var i = 0; i < data.playlists.length; i++) {
878                 var currentResultPlaylist = data.playlists[i]
879                 $(tableBody).append(
880                                 `<tr>
881                                 <td><img src="${currentResultPlaylist.image}" class="rounded" width="56px" /></td>
882                                 <td>${currentResultPlaylist.title}</td>
883                                 <td>${currentResultPlaylist.songs}</td>
884                                 </tr>`)
885                 if (currentResultPlaylist.spotify)
886                         generateShowTracklistButton(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
887                 else
888                         generateShowTracklistSelectiveButton(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
889
890                 generateDownloadLink(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
891         }
892         $('.tooltipped').tooltip({delay: 100})
893 })
894
895 //###############################################TAB_URL##############################################\\
896 $('#tab_url_form_url').submit(function (ev) {
897
898         ev.preventDefault()
899         var urls = $("#song_url").val().split(";")
900         for(var i = 0; i < urls.length; i++){
901                 var url = urls[i]
902                 if (url.length == 0) {
903                         message('Blank URL Field', 'You need to insert an URL to download it!')
904                         return false
905                 }
906                 //Validate URL
907                 if (url.indexOf('deezer.com/') < 0 && url.indexOf('open.spotify.com/') < 0 && url.indexOf('spotify:') < 0) {
908                         message('Wrong URL', 'The URL seems to be wrong. Please check it and try it again.')
909                         return false
910                 }
911                 if (url.indexOf('?') > -1) {
912                         url = url.substring(0, url.indexOf("?"))
913                 }
914                 if (url.indexOf('open.spotify.com/') >= 0 ||  url.indexOf('spotify:') >= 0){
915                         if (url.indexOf('playlist') < 0){
916                                 message('Playlist not found', 'Deezloader for now can only download Spotify playlists.')
917                                 return false
918                         }
919                 }
920                 addToQueue(url)
921         }
922 })
923
924 //############################################TAB_DOWNLOADS###########################################\\
925 function addToQueue(url) {
926         var type = getTypeFromLink(url), id = getIDFromLink(url, type)
927         if (['track', 'playlist', 'spotifyplaylist', 'artisttop', 'album', 'artist'].indexOf(type) == -1) {
928                 M.toast({html: '<i class="material-icons left">error</i> Wrong Type!', displayLength: 5000, classes: 'rounded'})
929                 return false
930         }
931         if (alreadyInQueue(id)) {
932                 M.toast({html: '<i class="material-icons left">playlist_add_check</i> Already in download-queue!', displayLength: 5000, classes: 'rounded'})
933                 return false
934         }
935         if (id.match(/^-?[0-9]+$/) == null && type != 'spotifyplaylist') {
936                 M.toast({html: '<i class="material-icons left">error</i> Wrong ID!', displayLength: 5000, classes: 'rounded'})
937                 return false
938         }
939         socket.emit("download" + type, {id: id, settings: userSettings})
940         M.toast({html: '<i class="material-icons left">add</i>Added to download-queue', displayLength: 5000, classes: 'rounded'})
941 }
942
943 function alreadyInQueue(id) {
944         var alreadyInQueue = false
945         $('#tab_downloads_table_downloads').find('tbody').find('tr').each(function () {
946                 if ($(this).data('deezerid') == `${id}:${userSettings.maxBitrate}`) {
947                         alreadyInQueue = true
948                         return false
949                 }
950         })
951         return alreadyInQueue
952 }
953
954 socket.on('addToQueue', function (data) {
955
956         var tableBody = $('#tab_downloads_table_downloads').find('tbody')
957
958         $(tableBody).append(
959                         `<tr id="${data.queueId}" data-deezerid="${data.id}">
960                         <td class="queueTitle">${data.name}</td>
961                         <td class="queueSize">${data.size}</td>
962                         <td class="queueDownloaded">${data.downloaded}</td>
963                         <td class="queueFailed">${data.failed}</td>
964                         <td><div class="progress"><div class="indeterminate"></div></div></td>
965                         </tr>`)
966
967         var btn_remove = $('<a href="#" class="btn-flat waves-effect"><i class="material-icons">remove</i></a>')
968
969         $(btn_remove).click(function (ev) {
970                 ev.preventDefault()
971                 socket.emit("cancelDownload", {queueId: data.queueId})
972         })
973
974         btn_remove.appendTo(tableBody.children('tr:last')).wrap('<td class="eventBtn center">')
975
976 })
977
978 socket.on("downloadStarted", function (data) {
979         //data.queueId -> queueId of started download
980
981         //Switch progress type indeterminate to determinate
982         $('#' + data.queueId).find('.indeterminate').removeClass('indeterminate').addClass('determinate')
983         $('#' + data.queueId).find('.eventBtn').find('a').html('<i class="material-icons">clear</i>')
984
985 })
986
987 socket.on('updateQueue', function (data) {
988
989         if (data.cancelFlag) {
990                 return
991         }
992
993         $('#' + data.queueId).find('.queueDownloaded').html(data.downloaded)
994         $('#' + data.queueId).find('.queueFailed').html(data.failed)
995
996         if (data.failed == 0 && ((data.downloaded + data.failed) >= data.size)) {
997                 $('#' + data.queueId).find('.eventBtn').html('<i class="material-icons">done</i>')
998                 $('#' + data.queueId).addClass('finished')
999                 M.toast({html: `<i class="material-icons left">done</i>${quoteattr(data.name)} - Completed!`, displayLength: 5000, classes: 'rounded'})
1000         } else if (data.downloaded == 0 && ((data.downloaded + data.failed) >= data.size)) {
1001                 $('#' + data.queueId).find('.eventBtn').html('<i class="material-icons">error</i>')
1002                 $('#' + data.queueId).addClass('error')
1003                 M.toast({html: `<i class="material-icons left">error</i>${quoteattr(data.name)} - Failed!`, displayLength: 5000, classes: 'rounded'})
1004         } else if ((data.downloaded + data.failed) >= data.size) {
1005                 $('#' + data.queueId).find('.eventBtn').html('<i class="material-icons">warning</i>')
1006                 $('#' + data.queueId).addClass('error')
1007                 M.toast({html: `<i class="material-icons left">warning</i>${quoteattr(data.name)} - Completed with errors!`, displayLength: 5000, classes: 'rounded'})
1008         }
1009 })
1010
1011 socket.on("downloadProgress", function (data) {
1012         //data.queueId -> id (string)
1013         //data.percentage -> float/double, percentage
1014         //updated in 1% steps
1015         $('#' + data.queueId).find('.determinate').css('width', data.percentage + '%')
1016
1017 })
1018
1019 socket.on("emptyDownloadQueue", function () {
1020         M.toast({html: '<i class="material-icons left">done_all</i>All downloads completed!', displayLength: 5000, classes: 'rounded'})
1021 })
1022
1023 socket.on("cancelDownload", function (data) {
1024         //data.queueId          -> queueId of item which was canceled
1025         $('#' + data.queueId).addClass('animated fadeOutRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
1026                 $(this).remove()
1027                 if (!data.cleanAll) M.toast({html: '<i class="material-icons left">clear</i>One download removed!', displayLength: 5000, classes: 'rounded'})
1028         })
1029 })
1030
1031 $('#clearTracksTable').click(function (ev) {
1032         $('#tab_downloads_table_downloads').find('tbody').find('.finished, .error').addClass('animated fadeOutRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
1033                 $(this).remove()
1034         })
1035         return false
1036 })
1037
1038 $('#cancelAllTable').click(function (ev) {
1039         let listOfIDs = $('#tab_downloads_table_downloads').find('tbody').find('tr').map((x,i)=>{
1040                 return $(i).attr('id')
1041         }).get()
1042         socket.emit('cancelAllDownloads', {queueList: listOfIDs})
1043 })
1044
1045 socket.on("cancelAllDownloads", function () {
1046         M.toast({html: '<i class="material-icons left">clear</i>All downloads removed!', displayLength: 5000, classes: 'rounded'})
1047 })
1048
1049 //****************************************************************************************************\\
1050 //******************************************HELPER-FUNCTIONS******************************************\\
1051 //****************************************************************************************************\\
1052 /**
1053  * Replaces special characters with HTML friendly counterparts
1054  * @param s string
1055  * @param preserveCR preserves the new line character
1056  * @returns {string}
1057  */
1058 function quoteattr(s, preserveCR) {
1059   preserveCR = preserveCR ? '&#13;' : '\n'
1060   return ('' + s) /* Forces the conversion to string. */
1061         .replace(/&/g, '&amp;') /* This MUST be the 1st replacement. */
1062     .replace(/'/g, '&apos;') /* The 4 other predefined entities, required. */
1063     .replace(/"/g, '&quot;')
1064     .replace(/</g, '&lt;')
1065     .replace(/>/g, '&gt;')
1066     /*
1067     You may add other replacements here for HTML only
1068     (but it's not necessary).
1069     Or for XML, only if the named entities are defined in its DTD.
1070     */
1071     .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */
1072     .replace(/[\r\n]/g, preserveCR)
1073
1074 }
1075
1076 function getIDFromLink(link, type) {
1077         if (link.indexOf('?') > -1) {
1078                 link = link.substring(0, link.indexOf("?"))
1079         }
1080         // Spotify
1081         if ((link.startsWith("http") && link.indexOf('open.spotify.com/') >= 0)){
1082                 return link.slice(link.indexOf("/playlist/")+10)
1083         } else if (link.startsWith("spotify:")){
1084                 return link.slice(link.indexOf("playlist:")+9)
1085
1086         // Deezer
1087         } else if(type == "artisttop") {
1088                 return link.match(/\/artist\/(\d+)\/top_track/)[1];
1089         } else {
1090                 return link.substring(link.lastIndexOf("/") + 1)
1091         }
1092 }
1093
1094 function getTypeFromLink(link) {
1095         var type
1096         if (link.indexOf('spotify') > -1){
1097                 type = "spotifyplaylist"
1098         } else  if (link.indexOf('/track') > -1) {
1099                 type = "track"
1100         } else if (link.indexOf('/playlist') > -1) {
1101                 type = "playlist"
1102         } else if (link.indexOf('/album') > -1) {
1103                 type = "album"
1104         } else if (link.match(/\/artist\/(\d+)\/top_track/)) {
1105                 type = "artisttop";
1106         } else if (link.indexOf('/artist')) {
1107                 type = "artist"
1108         }
1109         return type
1110 }
1111
1112 function generateDownloadLink(url) {
1113         var btn_download = $('<a href="#" class="waves-effect btn-flat"><i class="material-icons">file_download</i></a>')
1114         $(btn_download).click(function (ev) {
1115                 ev.preventDefault()
1116                 addToQueue(url)
1117         })
1118         return btn_download
1119 }
1120
1121 function addPreviewControlsHover(el){
1122         el.hover( function () {
1123                 $(this).css({opacity: 1})
1124         }, function () {
1125                 if (($(this).parent().attr("playing") && preview_stopped) || !$(this).parent().attr("playing")){
1126                         $(this).css({opacity: 0}, 200)
1127                 }
1128         })
1129 }
1130
1131 function addPreviewControlsClick(el){
1132         el.click(function (e) {
1133                 e.preventDefault()
1134                 var icon = (this.tagName == "I" ? $(this) : $(this).children('i'))
1135                 if ($(this).attr("playing")){
1136                         if (preview_track.paused){
1137                                 preview_track.play()
1138                                 preview_stopped = false
1139                                 icon.text("pause")
1140                                 $(preview_track).animate({volume: 1}, 500)
1141                         }else{
1142                                 preview_stopped = true
1143                                 icon.text("play_arrow")
1144                                 $(preview_track).animate({volume: 0}, 250, "swing", ()=>{ preview_track.pause() })
1145                         }
1146                 }else{
1147                         $("*").removeAttr("playing")
1148                         $(this).attr("playing",true)
1149                         $('.preview_controls').text("play_arrow")
1150                         $('.preview_playlist_controls').text("play_arrow")
1151                         $('.preview_controls').css({opacity:0})
1152                         icon.text("pause")
1153                         icon.css({opacity: 1})
1154                         preview_stopped = false
1155                         $(preview_track).animate({volume: 0}, 250, "swing", ()=>{
1156                                 preview_track.pause()
1157                                 $('#preview-track_source').prop("src", $(this).attr("preview"))
1158                                 preview_track.load()
1159                         })
1160                 }
1161         })
1162 }
1163
1164 function convertDuration(duration) {
1165         //convert from seconds only to mm:ss format
1166         var mm, ss
1167         mm = Math.floor(duration / 60)
1168         ss = duration - (mm * 60)
1169         //add leading zero if ss < 0
1170         if (ss < 10) {
1171                 ss = "0" + ss
1172         }
1173         return mm + ":" + ss
1174 }
1175
1176 function sleep(milliseconds) {
1177   var start = new Date().getTime()
1178   for (var i = 0; i < 1e7; i++) {
1179     if ((new Date().getTime() - start) > milliseconds){
1180       break
1181                 }
1182   }
1183 }