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