-
-
-
-
-
-
-
-
-
-
-
-
-
{{ names[section] }}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ names[section] }}
+
+
+
+
+
get_app
+
+
+
{{ results.allTab.TOP_RESULT[0].title }}
+
+ {{ results.allTab.TOP_RESULT[0].type == 'artist' ? numberWithDots(results.allTab.TOP_RESULT[0].nb_fan) + ' fans' : 'by '+results.allTab.TOP_RESULT[0].artist+' - '+results.allTab.TOP_RESULT[0].nb_song+' tracks'}}
+
+
{{ results.allTab.TOP_RESULT[0].type.charAt(0).toUpperCase() + results.allTab.TOP_RESULT[0].type.substring(1)}}
+
+
+
+
+
+
+ |
+ {{track.SNG_TITLE + (track.VERSION ? ' '+track.VERSION : '')}} |
+ {{artist.ART_NAME}}
+ |
+
+ {{track.ALB_TITLE}} |
+ {{convertDuration(track.DURATION)}} |
+ get_app |
+
+
+
+
+
+
+
+
get_app
+
+
{{release.ART_NAME}}
+
{{numberWithDots(release.NB_FAN) + ' fans'}}
+
+
+
+
+
+
+
get_app
+
+
{{release.ALB_TITLE}}
+
{{release.ART_NAME+' - '+release.NUMBER_TRACK+' tracks'}}
+
+
+
+
+
+
+
get_app
+
+
{{ release.TITLE }}
+
{{release.NB_SONG+' tracks'}}
+
+
+
+
+
+
No results
+
+
+
+
+
+
Loading
+
+
+
No Tracks found
+
+
+
+ |
+ Title |
+ Artists |
+ Album |
+ timer |
+ |
+
+
+ |
+ {{track.title + (track.title_version ? ' '+track.title_version : '')}} |
+ {{track.artist.name}} |
+
+ {{track.album.title}} |
+ {{convertDuration(track.duration)}} |
+ get_app
+ |
+
+
+
+
+
+
+
Loading
+
+
+
No Albums found
+
+
+
-
-
get_app
-
-
-
{{ results.allTab.TOP_RESULT[0].title }}
-
{{ results.allTab.TOP_RESULT[0].type == 'artist' ? numberWithDots(results.allTab.TOP_RESULT[0].nb_fan) + ' fans' : 'by '+results.allTab.TOP_RESULT[0].artist+' - '+results.allTab.TOP_RESULT[0].nb_song+' tracks'}}
-
{{ results.allTab.TOP_RESULT[0].type.charAt(0).toUpperCase() + results.allTab.TOP_RESULT[0].type.substring(1)}}
-
-
-
-
-
- |
- {{track.SNG_TITLE + (track.VERSION ? ' '+track.VERSION : '')}} |
- {{artist.ART_NAME}} |
- {{track.ALB_TITLE}} |
- {{convertDuration(track.DURATION)}} |
- get_app |
-
-
-
-
-
-
-
-
get_app
-
-
{{release.ART_NAME}}
-
{{numberWithDots(release.NB_FAN) + ' fans'}}
-
-
-
-
-
-
-
get_app
-
-
{{release.ALB_TITLE}}
-
{{release.ART_NAME+' - '+release.NUMBER_TRACK+' tracks'}}
-
-
-
-
-
-
-
get_app
-
-
{{ release.TITLE }}
-
{{release.NB_SONG+' tracks'}}
+
+
get_app
+
{{ release.title }}
+
{{ 'by '+release.artist.name+' - '+release.nb_tracks+' tracks' }}
+
+
+
+
+
+
+
Loading
+
+
+
No Artists found
+
+
+
+
+
+
get_app
+
+
{{ release.name }}
+
{{ release.nb_album + ' releases' }}
+
+
+
+
+
+
+
Loading
+
+
+
No Playlists found
+
+
+
+
+
+
get_app
+
+
{{ release.title }}
+
{{ 'by '+release.user.name+' - '+release.nb_tracks+' tracks' }}
-
-
-
No results
-
-
-
-
Loading
+
+
+
+
Home
+
+
+
+
Charts
+
+
+
+
Favorites
+
+
+
+
Link Analyzer
+
+
+
+
Settings
+
+
+
+
You are logged in as
+
-
-
No Tracks found
+
+
+
+
+
+
How do I
+ get my own ARL?
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
About
+
+
+
+
+ {{ title }}
+ get_app
+
+
+
+
+
+
+
+
+
+
- |
- Title |
- Artists |
- Album |
- timer |
- |
-
-
- |
- {{track.title + (track.title_version ? ' '+track.title_version : '')}} |
- {{track.artist.name}} |
- {{track.album.title}} |
- {{convertDuration(track.duration)}} |
- get_app |
-
-
-
-
-
-
-
Loading
-
-
-
No Albums found
-
-
-
-
-
-
get_app
-
-
{{ release.title }}
-
{{ 'by '+release.artist.name+' - '+release.nb_tracks+' tracks' }}
-
-
-
-
-
-
-
Loading
-
-
-
No Artists found
-
-
-
-
-
-
get_app
-
-
{{ release.name }}
-
{{ release.nb_album + ' releases' }}
-
-
-
-
-
-
-
Loading
-
-
-
No Playlists found
-
-
-
-
-
-
get_app
-
-
{{ release.title }}
-
{{ 'by '+release.user.name+' - '+release.nb_tracks+' tracks' }}
-
-
-
-
-
-
-
-
Home
-
-
- Charts
-
- Favorites
-
- Link Analyzer
-
-
-
Settings
-
-
-
-
You are logged in as
-
-
-
-
-
-
-
-
How do I get my own ARL?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- About
-
-
-
- {{ title }}
- get_app
-
-
-
-
-
-
-
-
-
-
-
-
+ |
{{data.title}}
- |
-
-
-
-
-
-
- explicit
- {{release.title}}
- fiber_new
- |
- {{release.release_date}} |
- file_download |
-
-
-
-
-
-
-
-
-
- {{ title }} explicit
- {{ metadata }}{{ release_date }}
-
-
-
+
+
+
+
+
+
+ {{ title }} explicit
+ {{ metadata }}{{ release_date }}
+
+
+
+
{{ label }}
+
+
-
-
-
{{ label }}
-
-
-
+
@@ -501,13 +586,7 @@
{{ metadata }}{{ rele
-
-
-
-
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/public/js/app.js b/public/js/app.js
new file mode 100644
index 0000000..fa7c164
--- /dev/null
+++ b/public/js/app.js
@@ -0,0 +1,905 @@
+import * as Utils from './modules/utils.js'
+import { MainSearch } from './modules/search-tab/main-search.js'
+
+const socket = io.connect(window.location.href)
+const localStorage = window.localStorage
+
+// Toasts stuff
+window.toastsWithId = {}
+
+// Settings
+window.lastSettings = {}
+window.lastCredentials = {}
+
+window.toast = function (msg, icon = null, dismiss = true, id = null) {
+ if (toastsWithId[id]) {
+ let toastObj = toastsWithId[id]
+ let toastDOM = $(`div.toastify[toast_id=${id}]`)
+ if (msg) {
+ toastDOM.find('.toast-message').html(msg)
+ }
+ if (icon) {
+ if (icon == 'loading') icon = ``
+ else icon = `${icon}`
+ toastDOM.find('.toast-icon').html(icon)
+ }
+ if (dismiss !== null && dismiss) {
+ setTimeout(function () {
+ toastObj.hideToast()
+ delete toastsWithId[id]
+ }, 3000)
+ }
+ } else {
+ if (icon == null) icon = ''
+ else if (icon == 'loading') icon = ``
+ else icon = `${icon}`
+ let toastObj = Toastify({
+ text: `${icon}${msg}`,
+ duration: dismiss ? 3000 : 0,
+ gravity: 'bottom',
+ position: 'left'
+ }).showToast()
+ if (id) {
+ toastsWithId[id] = toastObj
+ $(toastObj.toastElement).attr('toast_id', id)
+ }
+ }
+}
+
+/* ===== Socketio listeners ===== */
+socket.on('toast', data => {
+ toast(data.msg, data.icon || null, data.dismiss !== undefined ? data.dismiss : true, data.id || null)
+})
+
+// Debug messages for socketio
+socket.on('message', function (msg) {
+ console.log(msg)
+})
+
+// Login stuff
+
+window.loginButton = function () {
+ let arl = document.querySelector('#login_input_arl').value
+ if (arl != '' && arl != localStorage.getItem('arl')) {
+ socket.emit('login', arl, true)
+ }
+}
+
+socket.on('logging_in', function () {
+ toast('Logging in', 'loading', false, 'login-toast')
+})
+
+socket.on('logged_in', function (data) {
+ switch (data.status) {
+ case 1:
+ case 3:
+ toast('Logged in', 'done', true, 'login-toast')
+ if (data.arl) {
+ localStorage.setItem('arl', data.arl)
+ $('#login_input_arl').val(data.arl)
+ }
+ $('#open_login_prompt').hide()
+ if (data.user) {
+ $('#settings_username').text(data.user.name)
+ $('#settings_picture').attr(
+ 'src',
+ `https://e-cdns-images.dzcdn.net/images/user/${data.user.picture}/125x125-000000-80-0-0.jpg`
+ )
+ $('#logged_in_info').show()
+ }
+ break
+ case 2:
+ toast('Already logged in', 'done', true, 'login-toast')
+ if (data.user) {
+ $('#settings_username').text(data.user.name)
+ $('#settings_picture').attr(
+ 'src',
+ `https://e-cdns-images.dzcdn.net/images/user/${data.user.picture}/125x125-000000-80-0-0.jpg`
+ )
+ $('#logged_in_info').show()
+ }
+ break
+ case 0:
+ toast("Couldn't log in", 'close', true, 'login-toast')
+ localStorage.removeItem('arl')
+ $('#login_input_arl').val('')
+ $('#open_login_prompt').show()
+ $('#logged_in_info').hide()
+ $('#settings_username').text('Not Logged')
+ $('#settings_picture').attr('src', `https://e-cdns-images.dzcdn.net/images/user/125x125-000000-80-0-0.jpg`)
+ break
+ }
+})
+
+socket.on('logged_out', function () {
+ toast('Logged out', 'done', true, 'login-toast')
+ localStorage.removeItem('arl')
+ $('#login_input_arl').val('')
+ $('#open_login_prompt').show()
+ $('#logged_in_info').hide()
+ $('#settings_username').text('Not Logged')
+ $('#settings_picture').attr('src', `https://e-cdns-images.dzcdn.net/images/user/125x125-000000-80-0-0.jpg`)
+})
+
+/**
+ * Adds event listeners.
+ * @returns {void}
+ * @since 0.1.0 (?)
+ */
+function linkEventListeners() {
+ document.getElementById('toggle_download_tab').addEventListener('click', toggleDownloadTab)
+ document.getElementById('modal_quality').addEventListener('click', modalQualityButton)
+ document.getElementById('settings_btn_updateArl').addEventListener('click', loginButton)
+}
+
+/**
+ * App initialization.
+ * @returns {void}
+ * @since 0.1.0 (?)
+ */
+function init() {
+ linkEventListeners()
+
+ if ('true' === localStorage.darkMode) {
+ document.documentElement.classList.add('dark-theme')
+ }
+
+ if (localStorage.getItem('arl')) {
+ let arl = localStorage.getItem('arl')
+
+ socket.emit('login', arl)
+ $('#login_input_arl').val(arl)
+ }
+
+ // Check if download tab should be open
+ if ('true' === localStorage.getItem('downloadTabOpen')) {
+ document.querySelector('#download_tab_container').classList.remove('tab_hidden')
+ }
+
+ // Open default tab
+ document.getElementById('main_home_tablink').click()
+}
+
+document.addEventListener('DOMContentLoaded', init)
+
+/* tabs.js */
+window.search_selected = ''
+window.main_selected = ''
+window.windows_stack = []
+window.currentStack = {}
+
+window.changeTab = function (evt, section, tabName) {
+ windows_stack = []
+ currentStack = {}
+ var i, tabcontent, tablinks
+ tabcontent = document.getElementsByClassName(section + '_tabcontent')
+ for (i = 0; i < tabcontent.length; i++) {
+ tabcontent[i].style.display = 'none'
+ }
+ tablinks = document.getElementsByClassName(section + '_tablinks')
+ for (i = 0; i < tablinks.length; i++) {
+ tablinks[i].classList.remove('active')
+ }
+ if (tabName == 'settings_tab' && main_selected != 'settings_tab') {
+ SettingsTab.settings = { ...lastSettings }
+ }
+
+ document.getElementById(tabName).style.display = 'block'
+ window[section + '_selected'] = tabName
+
+ // Not choosing .currentTarget beacuse the event
+ // is delegated, so it refers to #sidebar
+ evt.target.classList.add('active')
+
+ // Check if you need to load more content in the search tab
+ if (
+ main_selected == 'search_tab' &&
+ ['track_search', 'album_search', 'artist_search', 'playlist_search'].indexOf(search_selected) != -1 &&
+ MainSearch.results[search_selected.split('_')[0] + 'Tab'].data.length == 0
+ ) {
+ search(search_selected.split('_')[0])
+ }
+}
+
+window.showTab = function (type, id, back = false) {
+ if (windows_stack.length == 0) windows_stack.push({ tab: main_selected })
+ else if (!back) windows_stack.push(currentStack)
+ if (type == 'artist') {
+ window.tab = 'artist_tab'
+ } else {
+ window.tab = 'tracklist_tab'
+ }
+ currentStack = { type: type, id: id }
+ let tabcontent = document.getElementsByClassName('main_tabcontent')
+ for (let i = 0; i < tabcontent.length; i++) {
+ tabcontent[i].style.display = 'none'
+ }
+ document.getElementById(tab).style.display = 'block'
+}
+
+window.backTab = function () {
+ if (windows_stack.length == 1) {
+ clickElement('main_' + main_selected + 'link')
+ } else {
+ let tabObj = windows_stack.pop()
+ if (tabObj.type == 'artist') resetArtistTab()
+ else resetTracklistTab()
+ socket.emit('getTracklist', { type: tabObj.type, id: tabObj.id })
+ showTab(tabObj.type, tabObj.id, true)
+ }
+}
+
+/**
+ * Handles click Event on the sidebar and changes tab
+ * according to clicked icon.
+ * Uses event delegation
+ * @param {Event} event
+ * @since 0.1.0
+ */
+window.handleSidebarClick = function (event) {
+ let targetID = event.target.id
+
+ switch (targetID) {
+ case 'main_search_tablink':
+ changeTab(event, 'main', 'search_tab')
+ break
+ case 'main_home_tablink':
+ changeTab(event, 'main', 'home_tab')
+ break
+ case 'main_charts_tablink':
+ changeTab(event, 'main', 'charts_tab')
+ break
+ case 'main_favorites_tablink':
+ changeTab(event, 'main', 'favorites_tab')
+ break
+ case 'main_analyzer_tablink':
+ changeTab(event, 'main', 'analyzer_tab')
+ break
+ case 'main_settings_tablink':
+ changeTab(event, 'main', 'settings_tab')
+ break
+ case 'main_about_tablink':
+ changeTab(event, 'main', 'about_tab')
+ break
+
+ default:
+ break
+ }
+}
+
+document.getElementById('sidebar').addEventListener('click', handleSidebarClick)
+
+/* stackedTabs.js */
+var artistTab = new Vue({
+ el: '#artist_tab',
+ data: {
+ currentTab: '',
+ sortKey: 'release_date',
+ sortOrder: 'desc',
+ title: '',
+ image: '',
+ type: '',
+ link: '',
+ head: null,
+ body: null
+ },
+ methods: {
+ addToQueue: function (e) {
+ e.stopPropagation()
+ sendAddToQueue(e.currentTarget.dataset.link)
+ },
+ openQualityModal: function (e) {
+ e.preventDefault()
+ openQualityModal(e.currentTarget.dataset.link)
+ },
+ moreInfo: function (url, e) {
+ if (e) e.preventDefault()
+ showTrackListSelective(url, true)
+ },
+ sortBy: function (key) {
+ if (key == this.sortKey) {
+ this.sortOrder = this.sortOrder == 'asc' ? 'desc' : 'asc'
+ } else {
+ this.sortKey = key
+ this.sortOrder = 'asc'
+ }
+ },
+ changeTab: function (tab) {
+ this.currentTab = tab
+ },
+ checkNewRelease: function (date) {
+ var g1 = new Date()
+ var g2 = new Date(date)
+ g2.setDate(g2.getDate() + 3)
+ g1.setHours(0, 0, 0, 0)
+ if (g1.getTime() <= g2.getTime()) {
+ return true
+ } else {
+ return false
+ }
+ }
+ },
+ computed: {
+ showTable() {
+ if (this.body) return _.orderBy(this.body[this.currentTab], this.sortKey, this.sortOrder)
+ else return []
+ }
+ }
+})
+
+var tracklistTab = new Vue({
+ el: '#tracklist_tab',
+ data: {
+ title: '',
+ metadata: '',
+ release_date: '',
+ label: '',
+ explicit: false,
+ image: '',
+ type: '',
+ link: '',
+ head: null,
+ body: []
+ },
+ methods: {
+ addToQueue: function (e) {
+ e.stopPropagation()
+ sendAddToQueue(e.currentTarget.dataset.link)
+ },
+ openQualityModal: function (e) {
+ e.preventDefault()
+ openQualityModal(e.currentTarget.dataset.link)
+ },
+ toggleAll: function (e) {
+ this.body.forEach(item => {
+ if (item.type == 'track') {
+ item.selected = e.currentTarget.checked
+ }
+ })
+ },
+ selectedLinks: function () {
+ var selected = []
+ if (this.body) {
+ this.body.forEach(item => {
+ if (item.type == 'track' && item.selected) selected.push(item.link)
+ })
+ }
+ return selected.join(';')
+ }
+ }
+})
+
+window.resetArtistTab = function () {
+ artistTab.title = 'Loading...'
+ artistTab.image = ''
+ artistTab.type = ''
+ artistTab.currentTab = ''
+ artistTab.sortKey = 'release_date'
+ artistTab.sortOrder = 'desc'
+ artistTab.link = ''
+ artistTab.head = []
+ artistTab.body = null
+}
+
+window.resetTracklistTab = function () {
+ tracklistTab.title = 'Loading...'
+ tracklistTab.image = ''
+ tracklistTab.metadata = ''
+ tracklistTab.label = ''
+ tracklistTab.release_date = ''
+ tracklistTab.explicit = false
+ tracklistTab.type = ''
+ tracklistTab.head = []
+ tracklistTab.body = []
+}
+
+window.artistView = function (ev) {
+ let id = ev.currentTarget.dataset.id
+ resetArtistTab()
+ socket.emit('getTracklist', { type: 'artist', id: id })
+ showTab('artist', id)
+}
+window.albumView = function (ev) {
+ let id = ev.currentTarget.dataset.id
+ resetTracklistTab()
+ socket.emit('getTracklist', { type: 'album', id: id })
+ showTab('album', id)
+}
+window.playlistView = function (ev) {
+ let id = ev.currentTarget.dataset.id
+ resetTracklistTab()
+ socket.emit('getTracklist', { type: 'playlist', id: id })
+ showTab('playlist', id)
+}
+
+socket.on('show_artist', function (data) {
+ artistTab.title = data.name
+ artistTab.image = data.picture_xl
+ artistTab.type = 'Artist'
+ artistTab.link = `https://www.deezer.com/artist/${data.id}`
+ artistTab.currentTab = Object.keys(data.releases)[0]
+ artistTab.sortKey = 'release_date'
+ artistTab.sortOrder = 'desc'
+ artistTab.head = [
+ { title: 'Title', sortKey: 'title' },
+ { title: 'Release Date', sortKey: 'release_date' },
+ { title: '', width: '56px' }
+ ]
+ if (_.isEmpty(data.releases)) {
+ artistTab.body = null
+ } else {
+ artistTab.body = data.releases
+ }
+})
+
+socket.on('show_album', function (data) {
+ tracklistTab.type = 'Album'
+ tracklistTab.link = `https://www.deezer.com/album/${data.id}`
+ tracklistTab.title = data.title
+ tracklistTab.explicit = data.explicit_lyrics
+ tracklistTab.label = data.label
+ tracklistTab.metadata = `${data.artist.name} • ${data.tracks.length} songs`
+ tracklistTab.release_date = data.release_date.substring(0, 10)
+ tracklistTab.image = data.cover_xl
+ tracklistTab.head = [
+ { title: 'music_note', width: '24px' },
+ { title: '#' },
+ { title: 'Song' },
+ { title: 'Artist' },
+ { title: 'timer', width: '40px' },
+ {
+ title: '',
+ width: '24px'
+ }
+ ]
+ if (_.isEmpty(data.tracks)) {
+ tracklistTab.body = null
+ } else {
+ tracklistTab.body = data.tracks
+ }
+})
+
+socket.on('show_playlist', function (data) {
+ tracklistTab.type = 'Playlist'
+ tracklistTab.link = `https://www.deezer.com/playlist/${data.id}`
+ tracklistTab.title = data.title
+ tracklistTab.image = data.picture_xl
+ tracklistTab.release_date = data.creation_date.substring(0, 10)
+ tracklistTab.metadata = `by ${data.creator.name} • ${data.tracks.length} songs`
+ tracklistTab.head = [
+ { title: 'music_note', width: '24px' },
+ { title: '#' },
+ { title: 'Song' },
+ { title: 'Artist' },
+ { title: 'Album' },
+ { title: 'timer', width: '40px' },
+ {
+ title: '',
+ width: '24px'
+ }
+ ]
+ if (_.isEmpty(data.tracks)) {
+ tracklistTab.body = null
+ } else {
+ tracklistTab.body = data.tracks
+ }
+})
+
+/* search.js */
+// Load more content when the search page is at the end
+$('#content').on('scroll', function () {
+ if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
+ if (
+ main_selected == 'search_tab' &&
+ ['track_search', 'album_search', 'artist_search', 'playlist_search'].indexOf(search_selected) != -1
+ ) {
+ scrolledSearch(search_selected.split('_')[0])
+ }
+ }
+})
+
+window.search = function (type) {
+ let query = MainSearch.results.query
+ socket.emit('search', {
+ term: query,
+ type: type,
+ start: MainSearch.results[type + 'Tab'].next,
+ nb: 30
+ })
+}
+
+window.scrolledSearch = function (type) {
+ query = MainSearch.results.query
+ if (MainSearch.results[type + 'Tab'].next < MainSearch.results[type + 'Tab'].total) {
+ socket.emit('search', {
+ term: query,
+ type: type,
+ start: MainSearch.results[type + 'Tab'].next,
+ nb: 30
+ })
+ }
+}
+
+window.searchUpdate = function (result) {
+ let next = 0
+ if (result.next) next = parseInt(result.next.match(/index=(\d*)/)[1])
+ else next = result.total
+ if (MainSearch.results[result.type + 'Tab'].total != result.total)
+ MainSearch.results[result.type + 'Tab'].total = result.total
+ if (MainSearch.results[result.type + 'Tab'].next != next) {
+ MainSearch.results[result.type + 'Tab'].next = next
+ MainSearch.results[result.type + 'Tab'].data = MainSearch.results[result.type + 'Tab'].data.concat(result.data)
+ }
+ MainSearch.results[result.type + 'Tab'].loaded = true
+}
+
+socket.on('search', function (result) {
+ searchUpdate(result)
+})
+
+window.clickElement = function (button) {
+ return document.getElementById(button).click()
+}
+
+window.sendAddToQueue = function (url, bitrate = null) {
+ if (url.indexOf(';') != -1) {
+ urls = url.split(';')
+ urls.forEach(url => {
+ socket.emit('addToQueue', { url: url, bitrate: bitrate })
+ })
+ } else if (url != '') {
+ socket.emit('addToQueue', { url: url, bitrate: bitrate })
+ }
+}
+
+window.handleTabClick = function (event) {
+ let targetID = event.target.id
+
+ switch (targetID) {
+ case 'search_all_tab':
+ changeTab(event, 'search', 'main_search')
+ break
+ case 'search_track_tab':
+ changeTab(event, 'search', 'track_search')
+ break
+ case 'search_album_tab':
+ changeTab(event, 'search', 'album_search')
+ break
+ case 'search_artist_tab':
+ changeTab(event, 'search', 'artist_search')
+ break
+ case 'search_playlist_tab':
+ changeTab(event, 'search', 'playlist_search')
+ break
+
+ default:
+ break
+ }
+}
+
+document.getElementById('search_tab').addEventListener('click', handleTabClick)
+
+// Search section
+$('#searchbar').keyup(function (e) {
+ if (e.keyCode == 13) {
+ let term = this.value
+ if (Utils.isValidURL(term)) {
+ if (e.ctrlKey) {
+ openQualityModal(term)
+ } else {
+ sendAddToQueue(term)
+ }
+ } else {
+ if (term != MainSearch.query || main_selected == 'search_tab') {
+ document.getElementById('search_tab_content').style.display = 'none'
+ socket.emit('mainSearch', { term: term })
+ } else {
+ document.getElementById('search_all_tab').click()
+ document.getElementById('search_tab_content').style.display = 'block'
+ document.getElementById('main_search_tablink').click()
+ }
+ }
+ }
+})
+
+window.mainSearchHandler = function (result) {
+ let resetObj = { data: [], next: 0, total: 0, loaded: false }
+ MainSearch.results.allTab = result
+ MainSearch.results.query = result.QUERY
+ MainSearch.results.trackTab = { ...resetObj }
+ MainSearch.results.albumTab = { ...resetObj }
+ MainSearch.results.artistTab = { ...resetObj }
+ MainSearch.results.playlistTab = { ...resetObj }
+ document.getElementById('search_all_tab').click()
+ document.getElementById('search_tab_content').style.display = 'block'
+ document.getElementById('main_search_tablink').click()
+}
+
+socket.on('mainSearch', function (result) {
+ mainSearchHandler(result)
+})
+
+/* settings.js */
+const SettingsTab = new Vue({
+ el: '#settings_tab',
+ data: {
+ settings: { tags: {} },
+ spotifyFeatures: {}
+ },
+ methods: {
+ addListeners() {
+ document.getElementById('settings_btn_save').addEventListener('click', window.saveSettings)
+ document.getElementById('settings_btn_copyArl').addEventListener('click', window.copyARLtoClipboard)
+ document.getElementById('settings_btn_logout').addEventListener('click', window.logout)
+ }
+ },
+ mounted() {
+ this.addListeners()
+ }
+})
+
+socket.on('init_settings', function (settings, credentials) {
+ loadSettings(settings, credentials)
+ toast('Settings loaded!', 'settings')
+})
+
+socket.on('updateSettings', function (settings, credentials) {
+ loadSettings(settings, credentials)
+ toast('Settings updated!', 'settings')
+})
+
+window.loadSettings = function (settings, spotifyCredentials) {
+ lastSettings = { ...settings }
+ lastCredentials = { ...spotifyCredentials }
+ SettingsTab.settings = settings
+ SettingsTab.spotifyFeatures = spotifyCredentials
+}
+
+window.saveSettings = function () {
+ lastSettings = { ...SettingsTab.settings }
+ lastCredentials = { ...SettingsTab.spotifyFeatures }
+ socket.emit('saveSettings', lastSettings, lastCredentials)
+}
+
+window.copyARLtoClipboard = function () {
+ $('#login_input_arl').attr('type', 'text')
+ let copyText = document.querySelector('#login_input_arl')
+ copyText.select()
+ copyText.setSelectionRange(0, 99999)
+ document.execCommand('copy')
+ $('#login_input_arl').attr('type', 'password')
+ toast('ARL copied to clipboard', 'assignment')
+}
+
+window.logout = function () {
+ socket.emit('logout')
+}
+
+/* downloadList.js */
+// Show/Hide Download Tab
+window.toggleDownloadTab = function (ev) {
+ ev.preventDefault()
+
+ let isHidden = document.querySelector('#download_tab_container').classList.toggle('tab_hidden')
+
+ localStorage.setItem('downloadTabOpen', !isHidden)
+}
+
+var queueList = {}
+var queue = []
+var queueComplete = []
+
+socket.on('init_downloadQueue', function (data) {
+ if (data.queueComplete.length) {
+ data.queueComplete.forEach(item => {
+ addToQueue(data.queueList[item])
+ })
+ }
+ if (data.currentItem) {
+ addToQueue(data['queueList'][data.currentItem], true)
+ }
+ data.queue.forEach(item => {
+ addToQueue(data.queueList[item])
+ })
+})
+
+window.addToQueue = function (queueItem, current = false) {
+ queueList[queueItem.uuid] = queueItem
+ if (queueItem.downloaded + queueItem.failed == queueItem.size) queueComplete.push(queueItem.uuid)
+ else queue.push(queueItem.uuid)
+ $('#download_list').append(
+ `
+
+
+
+ ${queueItem.title} -
+ ${queueItem.artist}
+
+
+ ${queueItem.downloaded + queueItem.failed}/${
+ queueItem.size
+ }
+
+
+
+
`
+ )
+ if (queueItem.progress > 0 || current) {
+ $('#bar_' + queueItem.uuid)
+ .removeClass('indeterminate')
+ .addClass('determinate')
+ }
+ $('#bar_' + queueItem.uuid).css('width', queueItem.progress + '%')
+ if (queueItem.failed >= 1) {
+ $('#download_' + queueItem.uuid + ' .download_info_status').append(
+ `(${queueItem.failed}error_outline)`
+ )
+ }
+ if (queueItem.downloaded + queueItem.failed == queueItem.size) {
+ let result_icon = $('#download_' + queueItem.uuid).find('.queue_icon')
+ if (queueItem.failed == 0) {
+ result_icon.text('done')
+ } else if (queueItem.failed == queueItem.size) {
+ result_icon.text('error')
+ } else {
+ result_icon.text('warning')
+ }
+ }
+}
+
+socket.on('addedToQueue', function (queueItem) {
+ addToQueue(queueItem)
+})
+
+window.downloadAction = function (evt) {
+ let icon = $(evt.currentTarget).text()
+ let uuid = $(evt.currentTarget).data('uuid')
+ switch (icon) {
+ case 'remove':
+ socket.emit('removeFromQueue', uuid)
+ break
+ default:
+ }
+}
+
+socket.on('removedFromQueue', function (uuid) {
+ let index = queue.indexOf(uuid)
+ if (index > -1) {
+ queue.splice(index, 1)
+ $(`#download_${queueList[uuid].uuid}`).remove()
+ delete queueList[uuid]
+ }
+})
+
+socket.on('startDownload', function (uuid) {
+ $('#bar_' + uuid)
+ .removeClass('indeterminate')
+ .addClass('determinate')
+})
+
+socket.on('finishDownload', function (uuid) {
+ if (queue.indexOf(uuid) > -1) {
+ toast(`${queueList[uuid].title} finished downloading.`, 'done')
+ $('#bar_' + uuid).css('width', '100%')
+ let result_icon = $('#download_' + uuid).find('.queue_icon')
+ if (queueList[uuid].failed == 0) {
+ result_icon.text('done')
+ } else if (queueList[uuid].failed >= queueList[uuid].size) {
+ result_icon.text('error')
+ } else {
+ result_icon.text('warning')
+ }
+ let index = queue.indexOf(uuid)
+ if (index > -1) {
+ queue.splice(index, 1)
+ queueComplete.push(uuid)
+ }
+ if (queue.length <= 0) {
+ toast('All downloads completed!', 'done_all')
+ }
+ }
+})
+
+socket.on('removedAllDownloads', function (currentItem) {
+ queueComplete = []
+ if (currentItem == '') {
+ queue = []
+ queueList = {}
+ $('#download_list').html('')
+ } else {
+ queue = [currentItem]
+ let tempQueueItem = queueList[currentItem]
+ queueList = {}
+ queueList[currentItem] = tempQueueItem
+ $('.download_object').each(function (index) {
+ if ($(this).attr('id') != 'download_' + currentItem) $(this).remove()
+ })
+ }
+})
+
+socket.on('removedFinishedDownloads', function () {
+ queueComplete.forEach(item => {
+ $('#download_' + item).remove()
+ })
+ queueComplete = []
+})
+
+$('#clean_queue').on('click', function () {
+ socket.emit('removeFinishedDownloads')
+})
+
+$('#cancel_queue').on('click', function () {
+ socket.emit('cancelAllDownloads')
+})
+
+socket.on('updateQueue', function (update) {
+ if (update.uuid && queue.indexOf(update.uuid) > -1) {
+ if (update.downloaded) {
+ queueList[update.uuid].downloaded++
+ $('#download_' + update.uuid + ' .queue_downloaded').text(
+ queueList[update.uuid].downloaded + queueList[update.uuid].failed
+ )
+ }
+ if (update.failed) {
+ queueList[update.uuid].failed++
+ $('#download_' + update.uuid + ' .queue_downloaded').text(
+ queueList[update.uuid].downloaded + queueList[update.uuid].failed
+ )
+ if (queueList[update.uuid].failed == 1) {
+ $('#download_' + update.uuid + ' .download_info_status').append(
+ `(1 error_outline)`
+ )
+ } else {
+ $('#download_' + update.uuid + ' .queue_failed').text(queueList[update.uuid].failed)
+ }
+ }
+ if (update.progress) {
+ queueList[update.uuid].progress = update.progress
+ $('#bar_' + update.uuid).css('width', update.progress + '%')
+ }
+ }
+})
+
+/* modals.js */
+// quality modal stuff
+var modalQuality = document.getElementById('modal_quality')
+modalQuality.open = false
+
+window.onclick = function (event) {
+ if (event.target == modalQuality && modalQuality.open) {
+ $(modalQuality).addClass('animated fadeOut')
+ }
+}
+
+$(modalQuality).on('webkitAnimationEnd', function () {
+ if (modalQuality.open) {
+ $(this).removeClass('animated fadeOut')
+ $(this).css('display', 'none')
+ modalQuality.open = false
+ } else {
+ $(this).removeClass('animated fadeIn')
+ $(this).css('display', 'block')
+ modalQuality.open = true
+ }
+})
+
+window.openQualityModal = function (link) {
+ $(modalQuality).data('url', link)
+ $(modalQuality).css('display', 'block')
+ $(modalQuality).addClass('animated fadeIn')
+}
+
+window.modalQualityButton = function (event) {
+ if (!event.target.matches('.quality-button')) {
+ return
+ }
+
+ let bitrate = event.target.dataset.qualityValue
+
+ var url = $(modalQuality).data('url')
+ sendAddToQueue(url, bitrate)
+ $(modalQuality).addClass('animated fadeOut')
+}
diff --git a/public/js/app/app.js b/public/js/app/app.js
deleted file mode 100644
index 753cdd3..0000000
--- a/public/js/app/app.js
+++ /dev/null
@@ -1,161 +0,0 @@
-// Initialization
-const socket = io.connect(window.location.href)
-var localStorage = window.localStorage
-// toasts stuff
-var toastsWithId = {}
-// settings
-var lastSettings = {}
-var lastCredentials = {}
-
-function toast(msg, icon = null, dismiss = true, id = null) {
- if (toastsWithId[id]) {
- let toastObj = toastsWithId[id]
- let toastDOM = $(`div.toastify[toast_id=${id}]`)
- if (msg) {
- toastDOM.find('.toast-message').html(msg)
- }
- if (icon) {
- if (icon == 'loading') icon = ``
- else icon = `${icon}`
- toastDOM.find('.toast-icon').html(icon)
- }
- if (dismiss !== null && dismiss) {
- setTimeout(function () {
- toastObj.hideToast()
- delete toastsWithId[id]
- }, 3000)
- }
- } else {
- if (icon == null) icon = ''
- else if (icon == 'loading') icon = ``
- else icon = `${icon}`
- let toastObj = Toastify({
- text: `${icon}${msg}`,
- duration: dismiss ? 3000 : 0,
- gravity: 'bottom',
- position: 'left'
- }).showToast()
- if (id) {
- toastsWithId[id] = toastObj
- $(toastObj.toastElement).attr('toast_id', id)
- }
- }
-}
-
-/* ===== Socketio listeners ===== */
-socket.on('toast', data => {
- toast(data.msg, data.icon || null, data.dismiss !== undefined ? data.dismiss : true, data.id || null)
-})
-
-// Debug messages for socketio
-socket.on('message', function (msg) {
- console.log(msg)
-})
-
-// Login stuff
-
-function loginButton() {
- let arl = document.querySelector('#login_input_arl').value
- if (arl != '' && arl != localStorage.getItem('arl')) {
- socket.emit('login', arl, true)
- }
-}
-
-socket.on('logging_in', function () {
- toast('Logging in', 'loading', false, 'login-toast')
-})
-
-socket.on('logged_in', function (data) {
- switch (data.status) {
- case 1:
- case 3:
- toast('Logged in', 'done', true, 'login-toast')
- if (data.arl) {
- localStorage.setItem('arl', data.arl)
- $('#login_input_arl').val(data.arl)
- }
- $('#open_login_prompt').hide()
- if (data.user) {
- $('#settings_username').text(data.user.name)
- $('#settings_picture').attr(
- 'src',
- `https://e-cdns-images.dzcdn.net/images/user/${data.user.picture}/125x125-000000-80-0-0.jpg`
- )
- $('#logged_in_info').show()
- }
- break
- case 2:
- toast('Already logged in', 'done', true, 'login-toast')
- if (data.user) {
- $('#settings_username').text(data.user.name)
- $('#settings_picture').attr(
- 'src',
- `https://e-cdns-images.dzcdn.net/images/user/${data.user.picture}/125x125-000000-80-0-0.jpg`
- )
- $('#logged_in_info').show()
- }
- break
- case 0:
- toast("Couldn't log in", 'close', true, 'login-toast')
- localStorage.removeItem('arl')
- $('#login_input_arl').val('')
- $('#open_login_prompt').show()
- $('#logged_in_info').hide()
- $('#settings_username').text('Not Logged')
- $('#settings_picture').attr('src', `https://e-cdns-images.dzcdn.net/images/user/125x125-000000-80-0-0.jpg`)
- break
- }
-})
-
-socket.on('logged_out', function () {
- toast('Logged out', 'done', true, 'login-toast')
- localStorage.removeItem('arl')
- $('#login_input_arl').val('')
- $('#open_login_prompt').show()
- $('#logged_in_info').hide()
- $('#settings_username').text('Not Logged')
- $('#settings_picture').attr('src', `https://e-cdns-images.dzcdn.net/images/user/125x125-000000-80-0-0.jpg`)
-})
-
-
-
-/**
- * Adds event listeners.
- * @returns {void}
- * @since 0.1.0 (?)
- */
-function linkEventListeners() {
- document.getElementById('toggle_download_tab').addEventListener('click', toggleDownloadTab)
- document.getElementById('modal_quality').addEventListener('click', modalQualityButton)
- document.getElementById('settings_btn_updateArl').addEventListener('click', loginButton)
-}
-
-/**
- * App initialization.
- * @returns {void}
- * @since 0.1.0 (?)
- */
-function init() {
- linkEventListeners()
-
- if ('true' === localStorage.darkMode) {
- document.documentElement.classList.add('dark-theme')
- }
-
- if (localStorage.getItem('arl')) {
- let arl = localStorage.getItem('arl')
-
- socket.emit('login', arl)
- $('#login_input_arl').val(arl)
- }
-
- // Check if download tab should be open
- if ('true' === localStorage.getItem('downloadTabOpen')) {
- document.querySelector('#download_tab_container').classList.remove('tab_hidden')
- }
-
- // Open default tab
- document.getElementById('main_home_tablink').click()
-}
-
-document.addEventListener('DOMContentLoaded', init)
\ No newline at end of file
diff --git a/public/js/app/downloadList.js b/public/js/app/downloadList.js
deleted file mode 100644
index b7112f7..0000000
--- a/public/js/app/downloadList.js
+++ /dev/null
@@ -1,183 +0,0 @@
-// Show/Hide Download Tab
-function toggleDownloadTab(ev) {
- ev.preventDefault()
-
- let isHidden = document.querySelector('#download_tab_container').classList.toggle('tab_hidden')
-
- localStorage.setItem('downloadTabOpen', !isHidden)
-}
-
-var queueList = {}
-var queue = []
-var queueComplete = []
-
-socket.on('init_downloadQueue', function (data) {
- if (data.queueComplete.length) {
- data.queueComplete.forEach(item => {
- addToQueue(data.queueList[item])
- })
- }
- if (data.currentItem){
- addToQueue(data['queueList'][data.currentItem], true)
- }
- data.queue.forEach(item => {
- addToQueue(data.queueList[item])
- })
-})
-
-function addToQueue(queueItem, current=false){
- queueList[queueItem.uuid] = queueItem
- if (queueItem.downloaded + queueItem.failed == queueItem.size) queueComplete.push(queueItem.uuid)
- else queue.push(queueItem.uuid)
- $('#download_list').append(
- `
-
-
-
- ${queueItem.title} -
- ${queueItem.artist}
-
-
- ${queueItem.downloaded + queueItem.failed}/${
- queueItem.size
- }
-
-
-
-
`)
- if (queueItem.progress>0 || current){
- $('#bar_' + queueItem.uuid).removeClass('indeterminate').addClass('determinate')
- }
- $('#bar_' + queueItem.uuid).css('width', queueItem.progress + '%')
- if (queueItem.failed >= 1) {
- $('#download_' + queueItem.uuid + ' .download_info_status').append(
- `(${queueItem.failed}error_outline)`
- )
- }
- if (queueItem.downloaded + queueItem.failed == queueItem.size) {
- let result_icon = $('#download_' + queueItem.uuid).find('.queue_icon')
- if (queueItem.failed == 0) {
- result_icon.text('done')
- } else if (queueItem.failed == queueItem.size) {
- result_icon.text('error')
- } else {
- result_icon.text('warning')
- }
- }
-}
-
-socket.on('addedToQueue', function (queueItem) {
- addToQueue(queueItem)
-})
-
-function downloadAction(evt) {
- let icon = $(evt.currentTarget).text()
- let uuid = $(evt.currentTarget).data('uuid')
- switch (icon) {
- case 'remove':
- socket.emit('removeFromQueue', uuid)
- break
- default:
- }
-}
-
-socket.on('removedFromQueue', function (uuid) {
- let index = queue.indexOf(uuid)
- if (index > -1) {
- queue.splice(index, 1)
- $(`#download_${queueList[uuid].uuid}`).remove()
- delete queueList[uuid]
- }
-})
-
-socket.on('startDownload', function (uuid) {
- $('#bar_' + uuid)
- .removeClass('indeterminate')
- .addClass('determinate')
-})
-
-socket.on('finishDownload', function (uuid) {
- if (queue.indexOf(uuid) > -1) {
- toast(`${queueList[uuid].title} finished downloading.`, 'done')
- $('#bar_' + uuid).css('width', '100%')
- let result_icon = $('#download_' + uuid).find('.queue_icon')
- if (queueList[uuid].failed == 0) {
- result_icon.text('done')
- } else if (queueList[uuid].failed >= queueList[uuid].size) {
- result_icon.text('error')
- } else {
- result_icon.text('warning')
- }
- let index = queue.indexOf(uuid)
- if (index > -1) {
- queue.splice(index, 1)
- queueComplete.push(uuid)
- }
- if (queue.length <= 0) {
- toast('All downloads completed!', 'done_all')
- }
- }
-})
-
-socket.on('removedAllDownloads', function (currentItem) {
- queueComplete = []
- if (currentItem == '') {
- queue = []
- queueList = {}
- $('#download_list').html('')
- } else {
- queue = [currentItem]
- tempQueueItem = queueList[currentItem]
- queueList = {}
- queueList[currentItem] = tempQueueItem
- $('.download_object').each(function (index) {
- if ($(this).attr('id') != 'download_' + currentItem) $(this).remove()
- })
- }
-})
-
-socket.on('removedFinishedDownloads', function () {
- queueComplete.forEach(item => {
- $('#download_' + item).remove()
- })
- queueComplete = []
-})
-
-$('#clean_queue').on('click', function () {
- socket.emit('removeFinishedDownloads')
-})
-
-$('#cancel_queue').on('click', function () {
- socket.emit('cancelAllDownloads')
-})
-
-socket.on('updateQueue', function (update) {
- if (update.uuid && queue.indexOf(update.uuid) > -1) {
- if (update.downloaded) {
- queueList[update.uuid].downloaded++
- $('#download_' + update.uuid + ' .queue_downloaded').text(
- queueList[update.uuid].downloaded + queueList[update.uuid].failed
- )
- }
- if (update.failed) {
- queueList[update.uuid].failed++
- $('#download_' + update.uuid + ' .queue_downloaded').text(
- queueList[update.uuid].downloaded + queueList[update.uuid].failed
- )
- if (queueList[update.uuid].failed == 1) {
- $('#download_' + update.uuid + ' .download_info_status').append(
- `(1 error_outline)`
- )
- } else {
- $('#download_' + update.uuid + ' .queue_failed').text(queueList[update.uuid].failed)
- }
- }
- if (update.progress) {
- queueList[update.uuid].progress = update.progress
- $('#bar_' + update.uuid).css('width', update.progress + '%')
- }
- }
-})
\ No newline at end of file
diff --git a/public/js/app/modals.js b/public/js/app/modals.js
deleted file mode 100644
index 734af2f..0000000
--- a/public/js/app/modals.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// quality modal stuff
-var modalQuality = document.getElementById('modal_quality')
-modalQuality.open = false
-
-window.onclick = function (event) {
- if (event.target == modalQuality && modalQuality.open) {
- $(modalQuality).addClass('animated fadeOut')
- }
-}
-
-$(modalQuality).on('webkitAnimationEnd', function () {
- if (modalQuality.open) {
- $(this).removeClass('animated fadeOut')
- $(this).css('display', 'none')
- modalQuality.open = false
- } else {
- $(this).removeClass('animated fadeIn')
- $(this).css('display', 'block')
- modalQuality.open = true
- }
-})
-
-function openQualityModal(link) {
- $(modalQuality).data('url', link)
- $(modalQuality).css('display', 'block')
- $(modalQuality).addClass('animated fadeIn')
-}
-
-function modalQualityButton(event) {
- if (!event.target.matches('.quality-button')) {
- return
- }
-
- let bitrate = event.target.dataset.qualityValue
-
- var url = $(modalQuality).data('url')
- sendAddToQueue(url, bitrate)
- $(modalQuality).addClass('animated fadeOut')
-}
\ No newline at end of file
diff --git a/public/js/app/search.js b/public/js/app/search.js
deleted file mode 100644
index 944f00b..0000000
--- a/public/js/app/search.js
+++ /dev/null
@@ -1,168 +0,0 @@
-// Load more content when the search page is at the end
-$('#content').on('scroll', function () {
- if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
- if (
- main_selected == 'search_tab' &&
- ['track_search', 'album_search', 'artist_search', 'playlist_search'].indexOf(search_selected) != -1
- ) {
- scrolledSearch(search_selected.split('_')[0])
- }
- }
-})
-
-function search(type) {
- query = MainSearch.results.query
- socket.emit('search', {
- term: query,
- type: type,
- start: MainSearch.results[type + 'Tab'].next,
- nb: 30
- })
-}
-
-function scrolledSearch(type) {
- query = MainSearch.results.query
- if (MainSearch.results[type + 'Tab'].next < MainSearch.results[type + 'Tab'].total) {
- socket.emit('search', {
- term: query,
- type: type,
- start: MainSearch.results[type + 'Tab'].next,
- nb: 30
- })
- }
-}
-
-function searchUpdate(result) {
- let next = 0
- if (result.next) next = parseInt(result.next.match(/index=(\d*)/)[1])
- else next = result.total
- if (MainSearch.results[result.type + 'Tab'].total != result.total)
- MainSearch.results[result.type + 'Tab'].total = result.total
- if (MainSearch.results[result.type + 'Tab'].next != next) {
- MainSearch.results[result.type + 'Tab'].next = next
- MainSearch.results[result.type + 'Tab'].data = MainSearch.results[result.type + 'Tab'].data.concat(result.data)
- }
- MainSearch.results[result.type + 'Tab'].loaded = true
-}
-
-socket.on('search', function (result) {
- searchUpdate(result)
-})
-
-function clickElement(button) {
- return document.getElementById(button).click()
-}
-
-function sendAddToQueue(url, bitrate = null) {
- if (url.indexOf(';') != -1) {
- urls = url.split(';')
- urls.forEach(url => {
- socket.emit('addToQueue', { url: url, bitrate: bitrate })
- })
- } else if (url != '') {
- socket.emit('addToQueue', { url: url, bitrate: bitrate })
- }
-}
-
-const MainSearch = new Vue({
- el: '#search_tab',
- data: {
- names: {
- TOP_RESULT: 'Top Result',
- TRACK: 'Tracks',
- ARTIST: 'Artists',
- ALBUM: 'Albums',
- PLAYLIST: 'Playlists'
- },
- results: {
- query: '',
- allTab: {
- ORDER: [],
- TOP_RESULT: [],
- ALBUM: {},
- ARTIST: {},
- TRACK: {},
- PLAYLIST: {}
- },
- trackTab: {
- data: [],
- next: 0,
- total: 0,
- loaded: false
- },
- albumTab: {
- data: [],
- next: 0,
- total: 0,
- loaded: false
- },
- artistTab: {
- data: [],
- next: 0,
- total: 0,
- loaded: false
- },
- playlistTab: {
- data: [],
- next: 0,
- total: 0,
- loaded: false
- }
- }
- },
- methods: {
- changeSearchTab(section) {
- if (section != 'TOP_RESULT') clickElement('search_' + section.toLowerCase() + '_tab')
- },
- addToQueue: function (e) {
- e.stopPropagation()
- sendAddToQueue(e.currentTarget.dataset.link)
- },
- openQualityModal: function (e) {
- e.preventDefault()
- openQualityModal(e.currentTarget.dataset.link)
- }
- }
-})
-
-console.log(MainSearch)
-
-// Search section
-$('#searchbar').keyup(function (e) {
- if (e.keyCode == 13) {
- let term = this.value
- if (isValidURL(term)) {
- if (e.ctrlKey) {
- openQualityModal(term)
- } else {
- sendAddToQueue(term)
- }
- } else {
- if (term != MainSearch.query || main_selected == 'search_tab') {
- document.getElementById('search_tab_content').style.display = 'none'
- socket.emit('mainSearch', { term: term })
- } else {
- document.getElementById('search_all_tab').click()
- document.getElementById('search_tab_content').style.display = 'block'
- document.getElementById('main_search_tablink').click()
- }
- }
- }
-})
-
-function mainSearchHandler(result) {
- let resetObj = { data: [], next: 0, total: 0, loaded: false }
- MainSearch.results.allTab = result
- MainSearch.results.query = result.QUERY
- MainSearch.results.trackTab = { ...resetObj }
- MainSearch.results.albumTab = { ...resetObj }
- MainSearch.results.artistTab = { ...resetObj }
- MainSearch.results.playlistTab = { ...resetObj }
- document.getElementById('search_all_tab').click()
- document.getElementById('search_tab_content').style.display = 'block'
- document.getElementById('main_search_tablink').click()
-}
-
-socket.on('mainSearch', function (result) {
- mainSearchHandler(result)
-})
diff --git a/public/js/app/settings.js b/public/js/app/settings.js
deleted file mode 100644
index 94101b5..0000000
--- a/public/js/app/settings.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const SettingsTab = new Vue({
- el: '#settings_tab',
- data: {
- settings: { tags: {} },
- spotifyFeatures: {}
- },
- methods: {
- addListeners() {
- document.getElementById('settings_btn_save').addEventListener('click', saveSettings)
- document.getElementById('settings_btn_copyArl').addEventListener('click', copyARLtoClipboard)
- document.getElementById('settings_btn_logout').addEventListener('click', logout)
- }
- },
- mounted() {
- this.addListeners()
- }
-})
-
-socket.on('init_settings', function (settings, credentials) {
- loadSettings(settings, credentials)
- toast('Settings loaded!', 'settings')
-})
-
-socket.on('updateSettings', function (settings, credentials) {
- loadSettings(settings, credentials)
- toast('Settings updated!', 'settings')
-})
-
-function loadSettings(settings, spotifyCredentials) {
- lastSettings = { ...settings }
- lastCredentials = { ...spotifyCredentials }
- SettingsTab.settings = settings
- SettingsTab.spotifyFeatures = spotifyCredentials
-}
-
-function saveSettings() {
- lastSettings = { ...SettingsTab.settings }
- lastCredentials = { ...SettingsTab.spotifyFeatures }
- socket.emit('saveSettings', lastSettings, lastCredentials)
-}
-
-function copyARLtoClipboard() {
- $('#login_input_arl').attr('type', 'text')
- let copyText = document.querySelector('#login_input_arl')
- copyText.select()
- copyText.setSelectionRange(0, 99999)
- document.execCommand('copy')
- $('#login_input_arl').attr('type', 'password')
- toast('ARL copied to clipboard', 'assignment')
-}
-
-function logout() {
- socket.emit('logout')
-}
diff --git a/public/js/app/stackedTabs.js b/public/js/app/stackedTabs.js
deleted file mode 100644
index 0c39f0b..0000000
--- a/public/js/app/stackedTabs.js
+++ /dev/null
@@ -1,198 +0,0 @@
-var artistTab = new Vue({
- el: '#artist_tab',
- data: {
- currentTab: '',
- sortKey: 'release_date',
- sortOrder: 'desc',
- title: "",
- image: "",
- type: "",
- link: "",
- head: null,
- body: null
- },
- methods: {
- addToQueue: function(e){e.stopPropagation(); sendAddToQueue(e.currentTarget.dataset.link)},
- openQualityModal: function(e){e.preventDefault(); openQualityModal(e.currentTarget.dataset.link)},
- moreInfo: function(url, e){
- if (e) e.preventDefault();
- showTrackListSelective(url, true)
- },
- sortBy: function(key) {
- if (key == this.sortKey) {
- this.sortOrder = (this.sortOrder == 'asc') ? 'desc' : 'asc';
- } else {
- this.sortKey = key;
- this.sortOrder = 'asc';
- }
- },
- changeTab: function(tab){
- this.currentTab = tab
- },
- checkNewRelease: function(date){
- var g1 = new Date();
- var g2 = new Date(date);
- g2.setDate(g2.getDate()+3)
- g1.setHours(0,0,0,0)
- if (g1.getTime() <= g2.getTime()){
- return true;
- }else {
- return false;
- }
- }
- },
- computed: {
- showTable() {
- if (this.body)
- return _.orderBy(this.body[this.currentTab], this.sortKey, this.sortOrder)
- else
- return []
- }
- }
-})
-
-var tracklistTab = new Vue({
- el: '#tracklist_tab',
- data: {
- title: "",
- metadata : "",
- release_date: "",
- label: "",
- explicit: false,
- image: "",
- type: "",
- link: "",
- head: null,
- body: []
- },
- methods: {
- addToQueue: function(e){e.stopPropagation(); sendAddToQueue(e.currentTarget.dataset.link)},
- openQualityModal: function(e){e.preventDefault(); openQualityModal(e.currentTarget.dataset.link)},
- toggleAll: function(e){
- this.body.forEach((item) => {
- if (item.type == 'track'){
- item.selected = e.currentTarget.checked
- }
- });
- },
- selectedLinks: function(){
- selected = []
- if (this.body){
- this.body.forEach((item) => {
- if (item.type == 'track' && item.selected) selected.push(item.link)
- })
- }
- return selected.join(";")
- }
- }
-})
-
-function resetArtistTab(){
- artistTab.title = "Loading..."
- artistTab.image = ""
- artistTab.type = ""
- artistTab.currentTab = ''
- artistTab.sortKey = 'release_date'
- artistTab.sortOrder = 'desc'
- artistTab.link = ''
- artistTab.head = []
- artistTab.body = null
-}
-
-function resetTracklistTab(){
- tracklistTab.title = "Loading..."
- tracklistTab.image = ""
- tracklistTab.metadata = ""
- tracklistTab.label = ""
- tracklistTab.release_date = ""
- tracklistTab.explicit = false
- tracklistTab.type = ""
- tracklistTab.head = []
- tracklistTab.body = []
-}
-
-function artistView(ev){
- let id = ev.currentTarget.dataset.id
- resetArtistTab()
- socket.emit('getTracklist', {type: 'artist', id: id})
- showTab('artist', id)
-}
-function albumView(ev){
- let id = ev.currentTarget.dataset.id
- resetTracklistTab()
- socket.emit('getTracklist', {type: 'album', id: id})
- showTab('album', id)
-}
-function playlistView(ev){
- let id = ev.currentTarget.dataset.id
- resetTracklistTab()
- socket.emit('getTracklist', {type: 'playlist', id: id})
- showTab('playlist', id)
-}
-
-socket.on('show_artist', function(data){
- artistTab.title = data.name
- artistTab.image = data.picture_xl
- artistTab.type = "Artist"
- artistTab.link = `https://www.deezer.com/artist/${data.id}`
- artistTab.currentTab = Object.keys(data.releases)[0]
- artistTab.sortKey = 'release_date'
- artistTab.sortOrder = 'desc'
- artistTab.head = [
- {title: 'Title', sortKey: "title"},
- {title: 'Release Date', sortKey: "release_date"},
- {title: '', width: "56px"}
- ]
- if (_.isEmpty(data.releases)){
- artistTab.body = null
- }else{
- artistTab.body = data.releases
- }
-})
-
-socket.on('show_album', function(data){
- tracklistTab.type = 'Album'
- tracklistTab.link = `https://www.deezer.com/album/${data.id}`
- tracklistTab.title = data.title
- tracklistTab.explicit = data.explicit_lyrics
- tracklistTab.label = data.label
- tracklistTab.metadata = `${data.artist.name} • ${data.tracks.length} songs`
- tracklistTab.release_date = data.release_date.substring(0,10)
- tracklistTab.image = data.cover_xl
- tracklistTab.head = [
- {title: 'music_note', width: "24px"},
- {title: '#'},
- {title: 'Song'},
- {title: 'Artist'},
- {title: 'timer', width: "40px"},
- {title: '', width: "24px"}
- ]
- if (_.isEmpty(data.tracks)){
- tracklistTab.body = null
- }else{
- tracklistTab.body = data.tracks
- }
-})
-
-socket.on('show_playlist', function(data){
- tracklistTab.type = "Playlist"
- tracklistTab.link = `https://www.deezer.com/playlist/${data.id}`
- tracklistTab.title = data.title
- tracklistTab.image = data.picture_xl
- tracklistTab.release_date = data.creation_date.substring(0,10)
- tracklistTab.metadata = `by ${data.creator.name} • ${data.tracks.length} songs`
- tracklistTab.head = [
- {title: 'music_note', width: "24px"},
- {title: '#'},
- {title: 'Song'},
- {title: 'Artist'},
- {title: 'Album'},
- {title: 'timer', width: "40px"},
- {title: '', width: "24px"}
- ]
- if (_.isEmpty(data.tracks)){
- tracklistTab.body = null
- }else{
- tracklistTab.body = data.tracks
- }
-})
diff --git a/public/js/app/tabs.js b/public/js/app/tabs.js
deleted file mode 100644
index ce19b13..0000000
--- a/public/js/app/tabs.js
+++ /dev/null
@@ -1,102 +0,0 @@
-var search_selected = ''
-var main_selected = ''
-var windows_stack = []
-var currentStack = {}
-
-function changeTab(evt, section, tabName) {
- windows_stack = []
- currentStack = {}
- var i, tabcontent, tablinks
- tabcontent = document.getElementsByClassName(section + '_tabcontent')
- for (i = 0; i < tabcontent.length; i++) {
- tabcontent[i].style.display = 'none'
- }
- tablinks = document.getElementsByClassName(section + '_tablinks')
- for (i = 0; i < tablinks.length; i++) {
- tablinks[i].classList.remove('active')
- }
- if (tabName == 'settings_tab' && main_selected != 'settings_tab') {
- SettingsTab.settings = { ...lastSettings }
- }
-
- document.getElementById(tabName).style.display = 'block'
- window[section + '_selected'] = tabName
-
- // Not choosing .currentTarget beacuse the event
- // is delegated, so it refers to #sidebar
- evt.target.classList.add('active')
-
- // Check if you need to load more content in the search tab
- if (
- main_selected == 'search_tab' &&
- ['track_search', 'album_search', 'artist_search', 'playlist_search'].indexOf(search_selected) != -1 &&
- MainSearch.results[search_selected.split('_')[0]+"Tab"].data.length == 0
- ) {
- search(search_selected.split('_')[0])
- }
-}
-
-function showTab(type, id, back=false) {
- if (windows_stack.length == 0) windows_stack.push({ tab: main_selected })
- else if (!back) windows_stack.push(currentStack)
- if (type == 'artist') tab = 'artist_tab'
- else tab = 'tracklist_tab'
- currentStack = { type: type, id: id }
- let tabcontent = document.getElementsByClassName('main_tabcontent')
- for (let i = 0; i < tabcontent.length; i++) {
- tabcontent[i].style.display = 'none'
- }
- document.getElementById(tab).style.display = 'block'
-}
-
-function backTab() {
- if (windows_stack.length == 1) {
- clickElement('main_' + main_selected + 'link')
- } else {
- let tabObj = windows_stack.pop()
- if (tabObj.type == 'artist') resetArtistTab()
- else resetTracklistTab()
- socket.emit('getTracklist', {type: tabObj.type, id: tabObj.id})
- showTab(tabObj.type, tabObj.id, true)
- }
-}
-
-/**
- * Handles click Event on the sidebar and changes tab
- * according to clicked icon.
- * Uses event delegation
- * @param {Event} event
- * @since 0.1.0
- */
-function handleSidebarClick(event) {
- let targetID = event.target.id
-
- switch (targetID) {
- case 'main_search_tablink':
- changeTab(event, 'main', 'search_tab')
- break
- case 'main_home_tablink':
- changeTab(event, 'main', 'home_tab')
- break
- case 'main_charts_tablink':
- changeTab(event, 'main', 'charts_tab')
- break
- case 'main_favorites_tablink':
- changeTab(event, 'main', 'favorites_tab')
- break
- case 'main_analyzer_tablink':
- changeTab(event, 'main', 'analyzer_tab')
- break
- case 'main_settings_tablink':
- changeTab(event, 'main', 'settings_tab')
- break
- case 'main_about_tablink':
- changeTab(event, 'main', 'about_tab')
- break
-
- default:
- break
- }
-}
-
-document.getElementById('sidebar').addEventListener('click', handleSidebarClick)
diff --git a/public/js/modules/search-tab/main-search.js b/public/js/modules/search-tab/main-search.js
new file mode 100644
index 0000000..73fcc44
--- /dev/null
+++ b/public/js/modules/search-tab/main-search.js
@@ -0,0 +1,74 @@
+export const MainSearch = new Vue({
+ el: '#search_tab',
+ data: {
+ names: {
+ TOP_RESULT: 'Top Result',
+ TRACK: 'Tracks',
+ ARTIST: 'Artists',
+ ALBUM: 'Albums',
+ PLAYLIST: 'Playlists'
+ },
+ results: {
+ query: '',
+ allTab: {
+ ORDER: [],
+ TOP_RESULT: [],
+ ALBUM: {},
+ ARTIST: {},
+ TRACK: {},
+ PLAYLIST: {}
+ },
+ trackTab: {
+ data: [],
+ next: 0,
+ total: 0,
+ loaded: false
+ },
+ albumTab: {
+ data: [],
+ next: 0,
+ total: 0,
+ loaded: false
+ },
+ artistTab: {
+ data: [],
+ next: 0,
+ total: 0,
+ loaded: false
+ },
+ playlistTab: {
+ data: [],
+ next: 0,
+ total: 0,
+ loaded: false
+ }
+ }
+ },
+ methods: {
+ changeSearchTab(section) {
+ if (section != 'TOP_RESULT') clickElement('search_' + section.toLowerCase() + '_tab')
+ },
+ addToQueue: function (e) {
+ e.stopPropagation()
+ sendAddToQueue(e.currentTarget.dataset.link)
+ },
+ openQualityModal: function (e) {
+ e.preventDefault()
+ openQualityModal(e.currentTarget.dataset.link)
+ },
+ numberWithDots(x) {
+ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
+ },
+ convertDuration(duration) {
+ //convert from seconds only to mm:ss format
+ let mm, ss
+ mm = Math.floor(duration / 60)
+ ss = duration - mm * 60
+ //add leading zero if ss < 0
+ if (ss < 10) {
+ ss = '0' + ss
+ }
+ return mm + ':' + ss
+ }
+ }
+})
\ No newline at end of file
diff --git a/public/js/app/utils.js b/public/js/modules/utils.js
similarity index 50%
rename from public/js/app/utils.js
rename to public/js/modules/utils.js
index 191d856..4a5556d 100644
--- a/public/js/app/utils.js
+++ b/public/js/modules/utils.js
@@ -1,4 +1,4 @@
-function isValidURL(text) {
+export function isValidURL(text) {
if (text.toLowerCase().startsWith('http')) {
if (text.toLowerCase().indexOf('deezer.com') >= 0 || text.toLowerCase().indexOf('open.spotify.com') >= 0)
return true
@@ -6,7 +6,7 @@ function isValidURL(text) {
return false
}
-function convertDuration(duration) {
+export function convertDuration(duration) {
//convert from seconds only to mm:ss format
let mm, ss
mm = Math.floor(duration / 60)
@@ -18,7 +18,7 @@ function convertDuration(duration) {
return mm + ':' + ss
}
-function convertDurationSeparated(duration) {
+export function convertDurationSeparated(duration) {
let hh, mm, ss
mm = Math.floor(duration / 60)
hh = Math.floor(mm / 60)
@@ -27,22 +27,18 @@ function convertDurationSeparated(duration) {
return [hh, mm, ss]
}
-function numberWithDots(x) {
- return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
+export function debounce(func, wait, immediate) {
+ var timeout
+ return function () {
+ var context = this
+ var args = arguments
+ var later = function () {
+ timeout = null
+ if (!immediate) func.apply(context, args)
+ }
+ var callNow = immediate && !timeout
+ clearTimeout(timeout)
+ timeout = setTimeout(later, wait)
+ if (callNow) func.apply(context, args)
+ }
}
-
-function debounce(func, wait, immediate) {
- var timeout
- return function() {
- var context = this
- var args = arguments
- var later = function() {
- timeout = null
- if (!immediate) func.apply(context, args)
- }
- var callNow = immediate && !timeout
- clearTimeout(timeout)
- timeout = setTimeout(later, wait)
- if (callNow) func.apply(context, args)
- }
-}
\ No newline at end of file