frontend.js 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905
  1. // Starting area, boot up the API and proceed to eat memory
  2. // Variables & constants
  3. const socket = io.connect(window.location.href)
  4. const serverMode = typeof require === "undefined"
  5. var defaultUserSettings = {}
  6. const localStorage = window.localStorage
  7. var modalQuality = document.getElementById('modal_quality');
  8. modalQuality.open = false
  9. let userSettings = {}
  10. let spotifySettings = {}
  11. var currentSearch = ""
  12. var searchData = {
  13. track: {
  14. next: 0,
  15. total: 0
  16. },
  17. album: {
  18. next: 0,
  19. total: 0
  20. },
  21. artist: {
  22. next: 0,
  23. total: 0
  24. },
  25. playlist: {
  26. next: 0,
  27. total: 0
  28. }
  29. }
  30. var queue = []
  31. var queueList = {}
  32. var queueComplete = []
  33. var loggedIn = false
  34. var deezerNotAvailable = false
  35. let preview_track = document.getElementById('preview-track')
  36. let preview_stopped = true
  37. let preview_max_volume;
  38. const COUNTRIES = {"AF": "Afghanistan","AX": "\u00c5land Islands","AL": "Albania","DZ": "Algeria","AS": "American Samoa","AD": "Andorra","AO": "Angola","AI": "Anguilla","AQ": "Antarctica","AG": "Antigua and Barbuda","AR": "Argentina","AM": "Armenia","AW": "Aruba","AU": "Australia","AT": "Austria","AZ": "Azerbaijan","BS": "Bahamas","BH": "Bahrain","BD": "Bangladesh","BB": "Barbados","BY": "Belarus","BE": "Belgium","BZ": "Belize","BJ": "Benin","BM": "Bermuda","BT": "Bhutan","BO": "Bolivia, Plurinational State of","BQ": "Bonaire, Sint Eustatius and Saba","BA": "Bosnia and Herzegovina","BW": "Botswana","BV": "Bouvet Island","BR": "Brazil","IO": "British Indian Ocean Territory","BN": "Brunei Darussalam","BG": "Bulgaria","BF": "Burkina Faso","BI": "Burundi","KH": "Cambodia","CM": "Cameroon","CA": "Canada","CV": "Cape Verde","KY": "Cayman Islands","CF": "Central African Republic","TD": "Chad","CL": "Chile","CN": "China","CX": "Christmas Island","CC": "Cocos (Keeling) Islands","CO": "Colombia","KM": "Comoros","CG": "Congo","CD": "Congo, the Democratic Republic of the","CK": "Cook Islands","CR": "Costa Rica","CI": "C\u00f4te d'Ivoire","HR": "Croatia","CU": "Cuba","CW": "Cura\u00e7ao","CY": "Cyprus","CZ": "Czech Republic","DK": "Denmark","DJ": "Djibouti","DM": "Dominica","DO": "Dominican Republic","EC": "Ecuador","EG": "Egypt","SV": "El Salvador","GQ": "Equatorial Guinea","ER": "Eritrea","EE": "Estonia","ET": "Ethiopia","FK": "Falkland Islands (Malvinas)","FO": "Faroe Islands","FJ": "Fiji","FI": "Finland","FR": "France","GF": "French Guiana","PF": "French Polynesia","TF": "French Southern Territories","GA": "Gabon","GM": "Gambia","GE": "Georgia","DE": "Germany","GH": "Ghana","GI": "Gibraltar","GR": "Greece","GL": "Greenland","GD": "Grenada","GP": "Guadeloupe","GU": "Guam","GT": "Guatemala","GG": "Guernsey","GN": "Guinea","GW": "Guinea-Bissau","GY": "Guyana","HT": "Haiti","HM": "Heard Island and McDonald Islands","VA": "Holy See (Vatican City State)","HN": "Honduras","HK": "Hong Kong","HU": "Hungary","IS": "Iceland","IN": "India","ID": "Indonesia","IR": "Iran, Islamic Republic of","IQ": "Iraq","IE": "Ireland","IM": "Isle of Man","IL": "Israel","IT": "Italy","JM": "Jamaica","JP": "Japan","JE": "Jersey","JO": "Jordan","KZ": "Kazakhstan","KE": "Kenya","KI": "Kiribati","KP": "Korea, Democratic People's Republic of","KR": "Korea, Republic of","KW": "Kuwait","KG": "Kyrgyzstan","LA": "Lao People's Democratic Republic","LV": "Latvia","LB": "Lebanon","LS": "Lesotho","LR": "Liberia","LY": "Libya","LI": "Liechtenstein","LT": "Lithuania","LU": "Luxembourg","MO": "Macao","MK": "Macedonia, the Former Yugoslav Republic of","MG": "Madagascar","MW": "Malawi","MY": "Malaysia","MV": "Maldives","ML": "Mali","MT": "Malta","MH": "Marshall Islands","MQ": "Martinique","MR": "Mauritania","MU": "Mauritius","YT": "Mayotte","MX": "Mexico","FM": "Micronesia, Federated States of","MD": "Moldova, Republic of","MC": "Monaco","MN": "Mongolia","ME": "Montenegro","MS": "Montserrat","MA": "Morocco","MZ": "Mozambique","MM": "Myanmar","NA": "Namibia","NR": "Nauru","NP": "Nepal","NL": "Netherlands","NC": "New Caledonia","NZ": "New Zealand","NI": "Nicaragua","NE": "Niger","NG": "Nigeria","NU": "Niue","NF": "Norfolk Island","MP": "Northern Mariana Islands","NO": "Norway","OM": "Oman","PK": "Pakistan","PW": "Palau","PS": "Palestine, State of","PA": "Panama","PG": "Papua New Guinea","PY": "Paraguay","PE": "Peru","PH": "Philippines","PN": "Pitcairn","PL": "Poland","PT": "Portugal","PR": "Puerto Rico","QA": "Qatar","RE": "R\u00e9union","RO": "Romania","RU": "Russian Federation","RW": "Rwanda","BL": "Saint Barth\u00e9lemy","SH": "Saint Helena, Ascension and Tristan da Cunha","KN": "Saint Kitts and Nevis","LC": "Saint Lucia","MF": "Saint Martin (French part)","PM": "Saint Pierre and Miquelon","VC": "Saint Vincent and the Grenadines","WS": "Samoa","SM": "San Marino","ST": "Sao Tome and Principe","SA": "Saudi Arabia","SN": "Senegal","RS": "Serbia","SC": "Seychelles","SL": "Sierra Leone","SG": "Singapore","SX": "Sint Maarten (Dutch part)","SK": "Slovakia","SI": "Slovenia","SB": "Solomon Islands","SO": "Somalia","ZA": "South Africa","GS": "South Georgia and the South Sandwich Islands","SS": "South Sudan","ES": "Spain","LK": "Sri Lanka","SD": "Sudan","SR": "Suriname","SJ": "Svalbard and Jan Mayen","SZ": "Swaziland","SE": "Sweden","CH": "Switzerland","SY": "Syrian Arab Republic","TW": "Taiwan, Province of China","TJ": "Tajikistan","TZ": "Tanzania, United Republic of","TH": "Thailand","TL": "Timor-Leste","TG": "Togo","TK": "Tokelau","TO": "Tonga","TT": "Trinidad and Tobago","TN": "Tunisia","TR": "Turkey","TM": "Turkmenistan","TC": "Turks and Caicos Islands","TV": "Tuvalu","UG": "Uganda","UA": "Ukraine","AE": "United Arab Emirates","GB": "United Kingdom","US": "United States","UM": "United States Minor Outlying Islands","UY": "Uruguay","UZ": "Uzbekistan","VU": "Vanuatu","VE": "Venezuela, Bolivarian Republic of","VN": "Viet Nam","VG": "Virgin Islands, British","VI": "Virgin Islands, U.S.","WF": "Wallis and Futuna","EH": "Western Sahara","YE": "Yemen","ZM": "Zambia","ZW": "Zimbabwe"}
  39. function toast(message, icon){
  40. if (icon){
  41. message = '<i class="material-icons left">'+icon+'</i>'+message
  42. }
  43. M.toast({html: message, displayLength: 5000, classes: 'rounded'})
  44. }
  45. window.addEventListener('offline', function(e) {
  46. toast("You are offline!", 'warning')
  47. });
  48. window.addEventListener('online', function(e) {
  49. toast("Back online!", 'check')
  50. if (!loggedIn) checkAutologin();
  51. });
  52. socket.on("toast", function(data){
  53. toast(data['msg'], 'error')
  54. })
  55. /*socket.on("deezerNotAvailable", function(){
  56. $("#deezerNotAvailable").slideDown()
  57. deezerNotAvailable = true
  58. })*/
  59. // Prints object obj into console
  60. // For Debug purposes
  61. socket.on("printObj", function(obj){
  62. console.log(obj)
  63. })
  64. socket.on("init_update", function(data){
  65. if (data.currentCommit)
  66. $('#application_version_about').text(data.currentCommit)
  67. else
  68. $('#application_version_about').text("N/A")
  69. })
  70. // Update ARL button
  71. $('#modal_settings_btn_updateArl').click(function () {
  72. $('#modal_settings_btn_updateArl').attr("disabled", true)
  73. var savedArl = localStorage.getItem('arl')
  74. var currentArl = $('#modal_login_input_arl').val()
  75. if (savedArl != currentArl){
  76. if (navigator.onLine){
  77. socket.emit('login', currentArl, true)
  78. }else{
  79. toast("You are offline!", 'warning')
  80. }
  81. }
  82. $('#modal_settings_btn_updateArl').attr("disabled", false)
  83. })
  84. $("#modal_settings_btn_copyArl").click(function(){
  85. $("#modal_login_input_arl").attr("type", "text");
  86. document.querySelector("#modal_login_input_arl").select();
  87. document.execCommand("copy");
  88. $("#modal_login_input_arl").attr("type", "password");
  89. toast("ARL copied to clipboard", 'assignment')
  90. })
  91. // After Login
  92. socket.on("logged_in", function (data) {
  93. if (data.status != 0) {
  94. $("#modal_settings_username").text(data.user.name)
  95. $("#modal_settings_picture").attr("src", `https://e-cdns-images.dzcdn.net/images/user/${data.user.picture}/128x128-000000-80-0-0.jpg`)
  96. $("#side_user").text(data.user.name)
  97. $("#side_avatar").attr("src", `https://e-cdns-images.dzcdn.net/images/user/${data.user.picture}/128x128-000000-80-0-0.jpg`)
  98. $("#side_email").text("id:"+data.user.id)
  99. if (data.user.id != 0){
  100. localStorage.setItem('arl', data.arl)
  101. $("#modal_login_input_arl").val(data.arl)
  102. // Load personal public playlists
  103. //socket.emit("getMyPlaylistList", {spotifyUser: localStorage.getItem('spotifyUser')})
  104. $('#logged_in_info').removeClass('hide')
  105. $('#login_email_btn_container').addClass('hide')
  106. //$('#modal_login').modal("close")
  107. //$('#modal_login_input_password').val("")
  108. toast("Logged in successfully", 'check')
  109. loggedIn = true;
  110. }
  111. }else{
  112. if (deezerNotAvailable) data.error = "Error: "+"Deezer is not available in your country"
  113. $('#login-res-text').text(data.error)
  114. setTimeout(function(){$('#login-res-text').text("")},3000)
  115. toast(data.error, 'error')
  116. $('#modal_login_input_arl').val("")
  117. if (localStorage.getItem("arl")){
  118. localStorage.removeItem("arl")
  119. }
  120. loggedIn = false;
  121. }
  122. M.updateTextFields()
  123. })
  124. // Autologin
  125. function checkAutologin(){
  126. if (navigator.onLine){
  127. if (localStorage.getItem('arl')){
  128. toast("Attempting Autologin...", 'info')
  129. socket.emit('login', localStorage.getItem('arl'))
  130. $('#modal_login_input_arl').val(localStorage.getItem('arl'))
  131. M.updateTextFields()
  132. }
  133. }else{
  134. if (localStorage.getItem('arl'))
  135. $('#modal_login_input_arl').val(localStorage.getItem('arl'))
  136. toast("You are offline!", 'warning')
  137. loggedIn = false;
  138. }
  139. }
  140. socket.on('init_autologin', function(){checkAutologin()})
  141. // Logout Button
  142. $('#modal_settings_btn_logout').click(function () {
  143. $('#modal_login_input_arl').val("")
  144. $('#logged_in_info').addClass('hide')
  145. localStorage.removeItem("arl")
  146. socket.emit('logout')
  147. loggedIn = false;
  148. M.updateTextFields()
  149. })
  150. // Open downloads folder
  151. $('#openDownloadsFolder').on('click', function () {
  152. socket.emit('openDownloadsFolder')
  153. })
  154. // Alert for replayGain tag
  155. $('#modal_tags_replayGain').on('click', function() {
  156. if ($(this).is(':checked')) {
  157. message("Warning", "Saving replay gain causes tracks to be quieter for some users.")
  158. }
  159. })
  160. // Do misc stuff on page load
  161. $(document).ready(function () {
  162. // Page Initializing
  163. console.log("Document ready")
  164. $("main.container").css('display', 'block')
  165. M.AutoInit()
  166. // Track Preview stuff
  167. preview_track.volume = 0
  168. preview_max_volume = parseInt(localStorage.getItem("previewVolume"))
  169. if (localStorage.getItem("previewVolume") === null){
  170. preview_max_volume = 80
  171. localStorage.setItem("previewVolume", preview_max_volume)
  172. }
  173. $('#modal_settings_range_previewVolume').val(preview_max_volume)
  174. // Init tabs and modals
  175. var tabs = M.Tabs.getInstance(document.getElementById("tab-nav"))
  176. $('.modal').modal()
  177. $("main.container").addClass('animated fadeIn').on('webkitAnimationEnd', function () {
  178. $(this).removeClass('animated fadeOut')
  179. })
  180. // Continuous scrolling search
  181. $(window).scroll(function () {
  182. if ($(document).height() <= $(window).scrollTop() + $(window).height()) {
  183. if (tabs.index == 0){
  184. var mode = $('#tab_search_form_search').find('input[name=searchMode]:checked').val()
  185. if (searchData[mode].next != searchData[mode].total){
  186. searchString = currentSearch
  187. if (searchString.length == 0) {return}
  188. $('#tab_search_table_results_tbody_loadingIndicator').removeClass('hide')
  189. socket.emit("search", {type: mode, term: searchString, start: searchData[mode].next, nb: 30})
  190. }
  191. }
  192. }
  193. });
  194. // Change Preview Volume
  195. $('#modal_settings_range_previewVolume').on('change',function(event){
  196. preview_max_volume = $(this).val()
  197. localStorage.setItem("previewVolume", preview_max_volume)
  198. })
  199. // Illegal character settings
  200. $("#modal_settings_input_illegalCharacterReplacer").keypress( function(e) {
  201. const regex = RegExp('[\0\/\\:*?"<>|]');
  202. if (regex.test(e.key) && e.key != 'backspace') {
  203. e.preventDefault();
  204. }
  205. });
  206. // Load top charts list for countries
  207. if (localStorage.getItem('chart') == null)
  208. localStorage.setItem('chart', "Worldwide")
  209. // Side Nav Stuff
  210. $('.sidenav').sidenav({
  211. edge: 'right',
  212. draggable: true
  213. })
  214. $('.sidenav_tab').click((e)=>{
  215. e.preventDefault()
  216. $(e.currentTarget).addClass("active")
  217. tabs.select($(e.currentTarget).attr('tab-id'))
  218. tabs.updateTabIndicator()
  219. })
  220. // scrollToTop FAB
  221. $(window).scroll(function () {
  222. if ($(this).scrollTop() > 100) {
  223. $('#btn_scrollToTop a').removeClass('scale-out').addClass('scale-in')
  224. } else {
  225. $('#btn_scrollToTop a').removeClass('scale-in').addClass('scale-out')
  226. }
  227. })
  228. $('#btn_scrollToTop').click(function () {
  229. $('html, body').animate({scrollTop: 0}, 800)
  230. return false
  231. })
  232. // Playlist Stuff
  233. $("#button_refresh_playlist_tab").click(function(){
  234. $("table_personal_playlists").html("")
  235. socket.emit("update_userFavorites")
  236. if (localStorage.getItem('spotifyUser'))
  237. socket.emit('update_userSpotifyPlaylists', localStorage.getItem('spotifyUser'))
  238. })
  239. $('#downloadChartPlaylist').on('contextmenu', function(e){
  240. e.preventDefault();
  241. $(modalQuality).data("url", `https://www.deezer.com/playlist/${$(this).data("id")}`)
  242. $(modalQuality).css('display', 'block')
  243. $(modalQuality).addClass('animated fadeIn')
  244. return false;
  245. }).on('click', function(e){
  246. e.preventDefault();
  247. sendAddToQueue(`https://www.deezer.com/playlist/${$(this).data("id")}`)
  248. })
  249. // Track Preview Feature
  250. $(preview_track).on('canplay', ()=>{
  251. preview_track.play()
  252. preview_stopped = false
  253. $(preview_track).animate({volume: preview_max_volume/100}, 500)
  254. })
  255. $(preview_track).on('timeupdate', ()=>{
  256. if (preview_track.currentTime > preview_track.duration-1){
  257. $(preview_track).animate({volume: 0}, 800)
  258. preview_stopped = true
  259. $('a[playing] > .preview_controls').css({opacity:0})
  260. $("*").removeAttr("playing")
  261. $('.preview_controls').text("play_arrow")
  262. $('.preview_playlist_controls').text("play_arrow")
  263. }
  264. })
  265. $('#modal_artist, #modal_trackListSelective').modal({
  266. onCloseStart: ()=>{
  267. if ($('.preview_playlist_controls').filter(function(){return $(this).attr("playing")}).length > 0){
  268. $(preview_track).animate({volume: 0}, 800)
  269. preview_stopped = true
  270. $(".preview_playlist_controls").removeAttr("playing")
  271. $('.preview_playlist_controls').text("play_arrow")
  272. }
  273. }
  274. })
  275. // Night Theme Switch
  276. $('#nightTimeSwitcher').change(function(){
  277. if(this.checked){
  278. document.getElementsByTagName('link')[4].disabled = false
  279. $("#nightModeSwitch2").html(`<i class="material-icons">brightness_7</i>${"Disable Night Mode"}`)
  280. localStorage.selectedTheme = "dark"
  281. }else{
  282. document.getElementsByTagName('link')[4].disabled = true
  283. $("#nightModeSwitch2").html(`<i class="material-icons">brightness_2</i>${"Enable Night Mode"}`)
  284. localStorage.selectedTheme = "light"
  285. }
  286. })
  287. $('#nightModeSwitch2').click((ev)=>{
  288. ev.preventDefault()
  289. $('#nightTimeSwitcher').prop('checked', !$('#nightTimeSwitcher').prop('checked'))
  290. $('#nightTimeSwitcher').change()
  291. })
  292. if (eval(localStorage.selectedTheme == "dark")){
  293. $('#nightTimeSwitcher').prop('checked', true)
  294. $('#nightTimeSwitcher').change()
  295. }else{
  296. $('#nightTimeSwitcher').prop('checked', false)
  297. $('#nightTimeSwitcher').change()
  298. }
  299. // Search on tab change
  300. $('input[name=searchMode][type=radio]').change(()=>{
  301. let url = $('#tab_search_form_search_input_searchString').val()
  302. if (url.indexOf('deezer.com/') < 0 && url.indexOf('open.spotify.com/') < 0 && url.indexOf('spotify:') < 0)
  303. $('#tab_search_form_search').submit()
  304. })
  305. $('#tab_search_form_search_input_searchString').on('input', function() {
  306. let url = $('#tab_search_form_search_input_searchString').val()
  307. if (url.indexOf('deezer.com/') < 0 && url.indexOf('open.spotify.com/') < 0 && url.indexOf('spotify:') < 0)
  308. $("#tab_search_button i").text("search")
  309. else
  310. $("#tab_search_button i").text("get_app")
  311. })
  312. // Enter on Link Analyzer and Link Download
  313. $('#link_analyzer_url').on("keyup", function(e) {
  314. if (e.keyCode == 13) {
  315. parseLinkAnalyzer($("#link_analyzer_url").val())
  316. }
  317. });
  318. // Button download all tracks in selective modal
  319. $('#download_all_tracks_selective, #download_all_tracks').on('contextmenu', function(e){
  320. e.preventDefault();
  321. $(modalQuality).data("url", $(this).attr("data-link"))
  322. $(modalQuality).css('display', 'block')
  323. $(modalQuality).addClass('animated fadeIn')
  324. return false;
  325. }).on('click', function(e){
  326. e.preventDefault();
  327. sendAddToQueue($(this).attr("data-link"))
  328. $(this).parent().parent().modal("close")
  329. })
  330. // Quality Modal
  331. window.onclick = function(event) {
  332. if (event.target == modalQuality && modalQuality.open) {
  333. $(modalQuality).addClass('animated fadeOut')
  334. }
  335. }
  336. $(modalQuality).on('webkitAnimationEnd', function () {
  337. if (modalQuality.open){
  338. $(this).removeClass('animated fadeOut')
  339. $(this).css('display', 'none')
  340. modalQuality.open = false
  341. }else{
  342. $(this).removeClass('animated fadeIn')
  343. $(this).css('display', 'block')
  344. modalQuality.open = true
  345. }
  346. })
  347. // Link Analyzer
  348. $("#link_analyzer_go").click(function(){
  349. parseLinkAnalyzer($("#link_analyzer_url").val())
  350. })
  351. // Settings cleanup
  352. $('#modal_settings_cbox_createPlaylistFolder').change(function(){
  353. $('#modal_settings_input_playlistNameTemplate').parent().slideToggle()
  354. })
  355. $('#modal_settings_cbox_createArtistFolder').change(function(){
  356. $('#modal_settings_input_artistNameTemplate').parent().slideToggle()
  357. })
  358. $('#modal_settings_cbox_createAlbumFolder').change(function(){
  359. $('#modal_settings_input_albumNameTemplate').parent().slideToggle()
  360. })
  361. $('#modal_settings_cbox_saveArtwork').change(function(){
  362. $('#modal_settings_input_coverImageTemplate').parent().slideToggle()
  363. })
  364. $('#modal_settings_cbox_saveArtworkArtist').change(function(){
  365. $('#modal_settings_input_artistImageTemplate').parent().slideToggle()
  366. })
  367. $('#modal_settings_cbox_createM3U8File').change(function(){
  368. $('#modal_settings_input_playlistFilenameTemplate').parent().slideToggle()
  369. })
  370. // Close Banner
  371. $(".close-banner").click(function(e){
  372. e.preventDefault();
  373. $(this).parent().slideUp()
  374. })
  375. })
  376. // Load settings
  377. socket.on('init_settings', function(settings, spotify, defaultSettings){
  378. defaultUserSettings = defaultSettings
  379. userSettings = settings
  380. spotifySettings = spotify
  381. let spotifyUser = localStorage.getItem("spotifyUser")
  382. if (spotifyUser) socket.emit('update_userSpotifyPlaylists', spotifyUser)
  383. console.log('Settings initialized')
  384. })
  385. socket.on('updateSettings', function(settings, spotifyCredentials){
  386. userSettings = settings
  387. spotifySettings = spotifyCredentials
  388. })
  389. /**
  390. * Modal Area START
  391. */
  392. // Prevent default behavior of closing button
  393. $('.modal-close').click(function (e) {
  394. e.preventDefault()
  395. })
  396. // Settings Modal START
  397. const $settingsAreaParent = $('#modal_settings')
  398. // Open settings panel
  399. $('#nav_btn_openSettingsModal, #sidenav_settings').click(function () {
  400. fillSettingsModal(userSettings, spotifySettings)
  401. })
  402. // Save settings button
  403. $('#modal_settings_btn_saveSettings').click(function () {
  404. let settings = {}
  405. // Save
  406. settings.userDefined = {
  407. downloadLocation: $('#modal_settings_input_downloadLocation').val(),
  408. tracknameTemplate: $('#modal_settings_input_tracknameTemplate').val(),
  409. albumTracknameTemplate: $('#modal_settings_input_albumTracknameTemplate').val(),
  410. playlistTracknameTemplate: $('#modal_settings_input_playlistTracknameTemplate').val(),
  411. createPlaylistFolder: $('#modal_settings_cbox_createPlaylistFolder').is(':checked'),
  412. playlistNameTemplate: $('#modal_settings_input_playlistNameTemplate').val(),
  413. createArtistFolder: $('#modal_settings_cbox_createArtistFolder').is(':checked'),
  414. artistNameTemplate: $('#modal_settings_input_artistNameTemplate').val(),
  415. createAlbumFolder: $('#modal_settings_cbox_createAlbumFolder').is(':checked'),
  416. albumNameTemplate: $('#modal_settings_input_albumNameTemplate').val(),
  417. createCDFolder: $('#modal_settings_cbox_createCDFolder').is(':checked'),
  418. createStructurePlaylist: $('#modal_settings_cbox_createStructurePlaylist').is(':checked'),
  419. createSingleFolder: $('#modal_settings_cbox_createSingleFolder').is(':checked'),
  420. padTracks: $('#modal_settings_cbox_padTracks').is(':checked'),
  421. paddingSize: $('#modal_settings_number_paddingSize').val(),
  422. illegalCharacterReplacer: $('#modal_settings_input_illegalCharacterReplacer').val(),
  423. queueConcurrency: parseInt($('#modal_settings_number_queueConcurrency').val()),
  424. maxBitrate: $('#modal_settings_select_maxBitrate').val(),
  425. fallbackBitrate : $('#modal_settings_cbox_fallbackBitrate').is(':checked'),
  426. fallbackSearch : $('#modal_settings_cbox_fallbackSearch').is(':checked'),
  427. logErrors: $('#modal_settings_cbox_logErrors').is(':checked'),
  428. logSearched: $('#modal_settings_cbox_logSearched').is(':checked'),
  429. saveDownloadQueue: $('#modal_settings_cbox_saveDownloadQueue').is(':checked'),
  430. overwriteFile: $('#modal_settings_select_overwriteFile').val(),
  431. createM3U8File: $('#modal_settings_cbox_createM3U8File').is(':checked'),
  432. playlistFilenameTemplate: $('#modal_settings_input_playlistFilenameTemplate').val(),
  433. syncedlyrics: $('#modal_settings_cbox_syncedlyrics').is(':checked'),
  434. embeddedArtworkSize: parseInt($('#modal_settings_select_embeddedArtworkSize').val()),
  435. embeddedArtworkPNG: $('#modal_settings_cbox_embeddedArtworkPNG').is(':checked'),
  436. localArtworkSize: parseInt($('#modal_settings_select_localArtworkSize').val()),
  437. localArtworkFormat: $('#modal_settings_select_localArtworkFormat').val(),
  438. saveArtwork: $('#modal_settings_cbox_saveArtwork').is(':checked'),
  439. coverImageTemplate: $('#modal_settings_input_coverImageTemplate').val(),
  440. saveArtworkArtist: $('#modal_settings_cbox_saveArtworkArtist').is(':checked'),
  441. artistImageTemplate: $('#modal_settings_input_artistImageTemplate').val(),
  442. jpegImageQuality: $('modal_settings_number_jpegImageQuality').val(),
  443. dateFormat: $('#modal_settings_select_dateFormat').val(),
  444. albumVariousArtists : $('#modal_settings_cbox_albumVariousArtists').is(':checked'),
  445. removeAlbumVersion : $('#modal_settings_cbox_removeAlbumVersion').is(':checked'),
  446. removeDuplicateArtists : $('#modal_settings_cbox_removeDuplicateArtists').is(':checked'),
  447. featuredToTitle: $('#modal_settings_select_featuredToTitle').val(),
  448. titleCasing : $('#modal_settings_select_titleCasing').val(),
  449. artistCasing : $('#modal_settings_select_artistCasing').val(),
  450. executeCommand: $('#modal_settings_input_executeCommand').val(),
  451. tags: {
  452. title: $('#modal_tags_title').is(':checked'),
  453. artist: $('#modal_tags_artist').is(':checked'),
  454. album: $('#modal_tags_album').is(':checked'),
  455. cover: $('#modal_tags_cover').is(':checked'),
  456. trackNumber: $('#modal_tags_trackNumber').is(':checked'),
  457. trackTotal: $('#modal_tags_trackTotal').is(':checked'),
  458. discNumber: $('#modal_tags_discNumber').is(':checked'),
  459. discTotal: $('#modal_tags_discTotal').is(':checked'),
  460. albumArtist: $('#modal_tags_albumArtist').is(':checked'),
  461. genre: $('#modal_tags_genre').is(':checked'),
  462. year: $('#modal_tags_year').is(':checked'),
  463. date: $('#modal_tags_date').is(':checked'),
  464. explicit: $('#modal_tags_explicit').is(':checked'),
  465. isrc: $('#modal_tags_isrc').is(':checked'),
  466. length: $('#modal_tags_length').is(':checked'),
  467. barcode: $('#modal_tags_barcode').is(':checked'),
  468. bpm: $('#modal_tags_bpm').is(':checked'),
  469. replayGain: $('#modal_tags_replayGain').is(':checked'),
  470. label: $('#modal_tags_label').is(':checked'),
  471. lyrics: $('#modal_tags_lyrics').is(':checked'),
  472. syncedLyrics: $('#modal_tags_syncedLyrics').is(':checked'),
  473. copyright: $('#modal_tags_copyright').is(':checked'),
  474. composer: $('#modal_tags_composer').is(':checked'),
  475. involvedPeople: $('#modal_tags_involvedPeople').is(':checked'),
  476. savePlaylistAsCompilation: $('#modal_settings_cbox_savePlaylistAsCompilation').is(':checked'),
  477. useNullSeparator : $('#modal_settings_cbox_useNullSeparator').is(':checked'),
  478. saveID3v1 : $('#modal_settings_cbox_saveID3v1').is(':checked'),
  479. multiArtistSeparator: $('#modal_settings_select_multiArtistSeparator').val(),
  480. singleAlbumArtist : $('#modal_settings_cbox_singleAlbumArtist').is(':checked'),
  481. }
  482. }
  483. let spotifyUser = $('#modal_settings_input_spotifyUser').val()
  484. let spotifyFeatures = {
  485. clientId: $('#modal_settings_input_spotifyClientID').val(),
  486. clientSecret: $('#modal_settings_input_spotifyClientSecret').val()
  487. }
  488. localStorage.setItem('spotifyUser', spotifyUser)
  489. // Send updated settings to be saved into config file
  490. //socket.emit('saveSettings', settings, spotifyFeatures, spotifyUser)
  491. })
  492. // Reset defaults button
  493. $('#modal_settings_btn_defaultSettings').click(function () {
  494. fillSettingsModal(defaultUserSettings, spotifySettings)
  495. })
  496. // Populate settings fields
  497. function fillSettingsModal(settings, spotifySettings = {clientId: "", clientSecret: ""}) {
  498. $('#modal_settings_input_downloadLocation').val(settings.downloadLocation)
  499. $('#modal_settings_input_tracknameTemplate').val(settings.tracknameTemplate)
  500. $('#modal_settings_input_albumTracknameTemplate').val(settings.albumTracknameTemplate)
  501. $('#modal_settings_input_playlistTracknameTemplate').val(settings.playlistTracknameTemplate)
  502. $('#modal_settings_cbox_createPlaylistFolder').prop('checked', settings.createPlaylistFolder)
  503. $('#modal_settings_input_playlistNameTemplate').val(settings.playlistNameTemplate)
  504. if (settings.createPlaylistFolder)
  505. $('#modal_settings_input_playlistNameTemplate').parent().slideDown()
  506. else
  507. $('#modal_settings_input_playlistNameTemplate').parent().slideUp()
  508. $('#modal_settings_cbox_createArtistFolder').prop('checked', settings.createArtistFolder)
  509. $('#modal_settings_input_artistNameTemplate').val(settings.artistNameTemplate)
  510. if (settings.createArtistFolder)
  511. $('#modal_settings_input_artistNameTemplate').parent().slideDown()
  512. else
  513. $('#modal_settings_input_artistNameTemplate').parent().slideUp()
  514. $('#modal_settings_cbox_createAlbumFolder').prop('checked', settings.createAlbumFolder)
  515. $('#modal_settings_input_albumNameTemplate').val(settings.albumNameTemplate)
  516. if (settings.createAlbumFolder)
  517. $('#modal_settings_input_albumNameTemplate').parent().slideDown()
  518. else
  519. $('#modal_settings_input_albumNameTemplate').parent().slideUp()
  520. $('#modal_settings_cbox_createCDFolder').prop('checked', settings.createCDFolder)
  521. $('#modal_settings_cbox_createStructurePlaylist').prop('checked', settings.createStructurePlaylist)
  522. $('#modal_settings_cbox_createSingleFolder').prop('checked', settings.createSingleFolder)
  523. $('#modal_settings_cbox_saveFullArtists').prop('checked', settings.saveFullArtists)
  524. $('#modal_settings_cbox_padTracks').prop('checked', settings.padTracks)
  525. $('#modal_settings_number_paddingSize').val(settings.paddingSize)
  526. $('#modal_settings_input_illegalCharacterReplacer').val(settings.illegalCharacterReplacer)
  527. $('#modal_settings_number_queueConcurrency').val(settings.queueConcurrency)
  528. $('#modal_settings_select_maxBitrate').val(settings.maxBitrate).formSelect()
  529. $('#modal_settings_cbox_fallbackBitrate').prop('checked', settings.fallbackBitrate)
  530. $('#modal_settings_cbox_fallbackSearch').prop('checked', settings.fallbackSearch)
  531. $('#modal_settings_cbox_logErrors').prop('checked', settings.logErrors)
  532. $('#modal_settings_cbox_logSearched').prop('checked', settings.logSearched)
  533. $('#modal_settings_cbox_saveDownloadQueue').prop('checked', settings.saveDownloadQueue)
  534. $('#modal_settings_select_overwriteFile').val(settings.overwriteFile).formSelect()
  535. $('#modal_settings_cbox_createM3U8File').prop('checked', settings.createM3U8File)
  536. $('#modal_settings_input_playlistFilenameTemplate').val(settings.playlistFilenameTemplate)
  537. if (settings.createM3U8File)
  538. $('#modal_settings_input_playlistFilenameTemplate').parent().slideDown()
  539. else
  540. $('#modal_settings_input_playlistFilenameTemplate').parent().slideUp()
  541. $('#modal_settings_cbox_syncedlyrics').prop('checked', settings.syncedlyrics)
  542. $('#modal_settings_select_embeddedArtworkSize').val(settings.embeddedArtworkSize).formSelect()
  543. $('#modal_settings_cbox_embeddedArtworkPNG').prop('checked', settings.embeddedArtworkPNG)
  544. $('#modal_settings_select_localArtworkSize').val(settings.localArtworkSize).formSelect()
  545. $('#modal_settings_select_localArtworkFormat').val(settings.localArtworkFormat).formSelect()
  546. $('#modal_settings_cbox_saveArtwork').prop('checked', settings.saveArtwork)
  547. $('#modal_settings_input_coverImageTemplate').val(settings.coverImageTemplate)
  548. if (settings.saveArtwork)
  549. $('#modal_settings_input_coverImageTemplate').parent().slideDown()
  550. else
  551. $('#modal_settings_input_coverImageTemplate').parent().slideUp()
  552. $('#modal_settings_cbox_saveArtworkArtist').prop('checked', settings.saveArtworkArtist)
  553. $('#modal_settings_input_artistImageTemplate').val(settings.artistImageTemplate)
  554. if (settings.saveArtworkArtist)
  555. $('#modal_settings_input_artistImageTemplate').parent().slideDown()
  556. else
  557. $('#modal_settings_input_artistImageTemplate').parent().slideUp()
  558. $('#modal_settings_number_jpegImageQuality').val(settings.jpegImageQuality)
  559. $('#modal_settings_select_dateFormat').val(settings.dateFormat).formSelect()
  560. $('#modal_settings_cbox_albumVariousArtists').prop('checked', settings.albumVariousArtists)
  561. $('#modal_settings_cbox_removeAlbumVersion').prop('checked', settings.removeAlbumVersion)
  562. $('#modal_settings_cbox_removeDuplicateArtists').prop('checked', settings.removeDuplicateArtists)
  563. $('#modal_settings_select_featuredToTitle').val(settings.featuredToTitle).formSelect()
  564. $('#modal_settings_select_titleCasing').val(settings.titleCasing).formSelect()
  565. $('#modal_settings_select_artistCasing').val(settings.artistCasing).formSelect()
  566. $('#modal_settings_input_executeCommand').val(settings.executeCommand)
  567. $('#modal_settings_input_spotifyUser').val(localStorage.getItem('spotifyUser'))
  568. $('#modal_settings_input_spotifyClientID').val(spotifySettings.clientId)
  569. $('#modal_settings_input_spotifyClientSecret').val(spotifySettings.clientSecret)
  570. $('#modal_tags_title').prop('checked', settings.tags.title)
  571. $('#modal_tags_artist').prop('checked', settings.tags.artist)
  572. $('#modal_tags_album').prop('checked', settings.tags.album)
  573. $('#modal_tags_cover').prop('checked', settings.tags.cover)
  574. $('#modal_tags_trackNumber').prop('checked', settings.tags.trackNumber)
  575. $('#modal_tags_trackTotal').prop('checked', settings.tags.trackTotal)
  576. $('#modal_tags_discNumber').prop('checked', settings.tags.discNumber)
  577. $('#modal_tags_discTotal').prop('checked', settings.tags.discTotal)
  578. $('#modal_tags_albumArtist').prop('checked', settings.tags.albumArtist)
  579. $('#modal_tags_genre').prop('checked', settings.tags.genre)
  580. $('#modal_tags_year').prop('checked', settings.tags.year)
  581. $('#modal_tags_date').prop('checked', settings.tags.date)
  582. $('#modal_tags_explicit').prop('checked', settings.tags.explicit)
  583. $('#modal_tags_isrc').prop('checked', settings.tags.isrc)
  584. $('#modal_tags_length').prop('checked', settings.tags.length)
  585. $('#modal_tags_barcode').prop('checked', settings.tags.barcode)
  586. $('#modal_tags_bpm').prop('checked', settings.tags.bpm)
  587. $('#modal_tags_replayGain').prop('checked', settings.tags.replayGain)
  588. $('#modal_tags_label').prop('checked', settings.tags.label)
  589. $('#modal_tags_lyrics').prop('checked', settings.tags.lyrics)
  590. $('#modal_tags_syncedLyrics').prop('checked', settings.tags.syncedLyrics)
  591. $('#modal_tags_copyright').prop('checked', settings.tags.copyright)
  592. $('#modal_tags_composer').prop('checked', settings.tags.composer)
  593. $('#modal_tags_involvedPeople').prop('checked', settings.tags.involvedPeople)
  594. $('#modal_settings_cbox_savePlaylistAsCompilation').prop('checked', settings.tags.savePlaylistAsCompilation)
  595. $('#modal_settings_cbox_useNullSeparator').prop('checked', settings.tags.useNullSeparator)
  596. $('#modal_settings_cbox_saveID3v1').prop('checked', settings.tags.saveID3v1)
  597. $('#modal_settings_select_multiArtistSeparator').val(settings.tags.multiArtistSeparator).formSelect()
  598. $('#modal_settings_cbox_singleAlbumArtist').prop('checked', settings.tags.singleAlbumArtist)
  599. M.updateTextFields()
  600. }
  601. //#############################################MODAL_MSG##############################################\\
  602. function message(title, message) {
  603. $('#modal_msg_title').text(title)
  604. $('#modal_msg_message').html(message)
  605. $('#modal_msg').modal('open')
  606. }
  607. //****************************************************************************************************\\
  608. //************************************************TABS************************************************\\
  609. //****************************************************************************************************\\
  610. //#############################################TAB_SEARCH#############################################\\
  611. // Submit Search Form
  612. $('#tab_search_form_search').submit(function (ev) {
  613. ev.preventDefault()
  614. var searchString = $('#tab_search_form_search_input_searchString').val().trim()
  615. if (searchString.indexOf('deezer.com/') < 0 && searchString.indexOf('open.spotify.com/') < 0 && searchString.indexOf('spotify:') < 0) {
  616. var mode = $('#tab_search_form_search').find('input[name=searchMode]:checked').val()
  617. searchData[mode].next = 0
  618. searchData[mode].total = 0
  619. currentSearch = searchString
  620. if (searchString.length == 0) {return}
  621. // Clean Table and show loading indicator
  622. $('#tab_search_table_results').find('thead').find('tr').addClass('hide')
  623. $('#tab_search_table_results_tbody_results').addClass('hide')
  624. $('#tab_search_table_results_tbody_noResults').addClass('hide')
  625. $('#tab_search_table_results_tbody_loadingIndicator').removeClass('hide')
  626. socket.emit("search", {type: mode, term: searchString, start: searchData[mode].next, nb: 30})
  627. }else{
  628. currentSearch = ""
  629. parseDownloadFromURL($('#tab_search_form_search_input_searchString').val().trim())
  630. }
  631. })
  632. $("#tab_search_button").on('contextmenu', function(e){
  633. e.preventDefault()
  634. var urls = $("#tab_search_form_search_input_searchString").val()
  635. if (urls.indexOf('deezer.com/') < 0 && urls.indexOf('open.spotify.com/') < 0 && urls.indexOf('spotify:') < 0) {
  636. return false;
  637. }
  638. let urlsArray = urls.split(";")
  639. if(urlsArray.length != 0){
  640. $(modalQuality).data("url", urls)
  641. $(modalQuality).css('display', 'block')
  642. $(modalQuality).addClass('animated fadeIn')
  643. }
  644. return false;
  645. })
  646. function parseDownloadFromURL(urlsString){
  647. urls = urlsString.split(";")
  648. newUrls = ""
  649. for(var i = 0; i < urls.length; i++){
  650. var url = urls[i]
  651. //Validate URL
  652. if (url.indexOf('deezer.com/') < 0 && url.indexOf('open.spotify.com/') < 0 && url.indexOf('spotify:') < 0) {
  653. continue
  654. }
  655. if (urls.length-1 != i)
  656. newUrls += url+";"
  657. else
  658. newUrls += url
  659. }
  660. sendAddToQueue(newUrls)
  661. }
  662. // Parse data from search
  663. socket.on('search', function (data) {
  664. // Remove loading indicator
  665. $('#tab_search_table_results_tbody_loadingIndicator').addClass('hide')
  666. if (data.next) {
  667. next = parseInt(data.next.match(/index=(\d*)/)[1])
  668. } else {
  669. next = data.total
  670. }
  671. data.update = searchData[data.type].next != 0
  672. searchData[data.type].next = next
  673. if (!searchData[data.type].total) searchData[data.type].total = data.total
  674. // If no data, display No Results Found
  675. if (data.data.length == 0) {
  676. if (!data.update) $('#tab_search_table_results_tbody_noResults').removeClass('hide')
  677. return
  678. }
  679. // Populate table and show results
  680. if (data.type == 'track') {
  681. showResults_table_track(data.data, data.update)
  682. } else if (data.type == 'album') {
  683. showResults_table_album(data.data, data.update)
  684. } else if (data.type == 'artist') {
  685. showResults_table_artist(data.data, data.update)
  686. } else if (data.type == 'playlist') {
  687. showResults_table_playlist(data.data, data.update)
  688. }
  689. $('#tab_search_table_results_tbody_results').removeClass('hide')
  690. })
  691. function showResults_table_track(tracks, update) {
  692. var tableBody = $('#tab_search_table_results_tbody_results')
  693. if (!update) $(tableBody).html('')
  694. $('#tab_search_table_results_thead_track').removeClass('hide')
  695. for (var i = 0; i < tracks.length; i++) {
  696. var currentResultTrack = tracks[i]
  697. $(tableBody).append(
  698. `<tr>
  699. <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>
  700. <td class="hide-on-med-and-up">
  701. <p class="remove-margin">${(currentResultTrack.explicit_lyrics ? ' <i class="material-icons valignicon tiny materialize-red-text">explicit</i>' : '')} ${currentResultTrack.title}</p>
  702. <p class="remove-margin secondary-text">${currentResultTrack.artist.name}</p>
  703. <p class="remove-margin secondary-text">${currentResultTrack.album.title}</p>
  704. </td>
  705. <td class="hide-on-small-only breakline">${(currentResultTrack.explicit_lyrics ? ' <i class="material-icons valignicon tiny materialize-red-text">explicit</i>' : '')} ${currentResultTrack.title}</td>
  706. <td class="hide-on-small-only breakline"><span class="resultArtist resultLink" data-link="${currentResultTrack.artist.link}">${currentResultTrack.artist.name}</span></td>
  707. <td class="hide-on-small-only breakline"><span class="resultAlbum resultLink" data-link="https://www.deezer.com/album/${currentResultTrack.album.id}">${currentResultTrack.album.title}</span></td>
  708. <td>${convertDuration(currentResultTrack.duration)}</td>
  709. </tr>`)
  710. generateDownloadLink(currentResultTrack.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  711. addPreviewControlsHover(tableBody.children('tr:last').find('.preview_controls'))
  712. addPreviewControlsClick(tableBody.children('tr:last').find('.single-cover'))
  713. tableBody.children('tr:last').find('.resultArtist').click(function (ev){
  714. ev.preventDefault()
  715. showArtistModal($(this).data("link"))
  716. })
  717. tableBody.children('tr:last').find('.resultAlbum').click(function (ev){
  718. ev.preventDefault()
  719. showTrackListSelective($(this).data("link"))
  720. })
  721. }
  722. }
  723. function showResults_table_album(albums, update) {
  724. var tableBody = $('#tab_search_table_results_tbody_results')
  725. if (!update) $(tableBody).html('')
  726. $('#tab_search_table_results_thead_album').removeClass('hide')
  727. for (var i = 0; i < albums.length; i++) {
  728. var currentResultAlbum = albums[i]
  729. $(tableBody).append(
  730. `<tr>
  731. <td><img style="width:56px;" src="${(currentResultAlbum.cover_small ? currentResultAlbum.cover_small : "img/noCover.jpg")}" class="rounded" /></td>
  732. <td class="hide-on-med-and-up">
  733. <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>
  734. <p class="remove-margin secondary-text">${currentResultAlbum.artist.name}</p>
  735. <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>
  736. </td>
  737. <td class="hide-on-small-only breakline">${(currentResultAlbum.explicit_lyrics ? `<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="${"Explicit"}">explicit</i> ` : '')} ${currentResultAlbum.title}</td>
  738. <td class="hide-on-small-only breakline"><span class="resultArtist resultLink" data-link="${currentResultAlbum.artist.link}">${currentResultAlbum.artist.name}</span></td>
  739. <td class="hide-on-small-only">${currentResultAlbum.nb_tracks}</td>
  740. <td class="hide-on-small-only">${currentResultAlbum.record_type[0].toUpperCase() + currentResultAlbum.record_type.substring(1)}</td>
  741. </tr>`)
  742. generateShowTracklistSelectiveButton(currentResultAlbum.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  743. generateDownloadLink(currentResultAlbum.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  744. tableBody.children('tr:last').find('.resultArtist').click(function (ev){
  745. ev.preventDefault()
  746. showArtistModal($(this).data("link"))
  747. })
  748. }
  749. $('.tooltipped').tooltip({delay: 100})
  750. }
  751. function showResults_table_artist(artists, update) {
  752. var tableBody = $('#tab_search_table_results_tbody_results')
  753. if (!update) $(tableBody).html('')
  754. $('#tab_search_table_results_thead_artist').removeClass('hide')
  755. for (var i = 0; i < artists.length; i++) {
  756. var currentResultArtist = artists[i]
  757. $(tableBody).append(
  758. `<tr>
  759. <td><img style="width:56px;" src="${(currentResultArtist.picture_small ? currentResultArtist.picture_small : "img/noCover.jpg")}" class="rounded" /></td>
  760. <td class="breakline">${currentResultArtist.name}</td>
  761. <td>${currentResultArtist.nb_album}</td>
  762. </tr>`)
  763. generateShowArtistButton(currentResultArtist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  764. generateDownloadLink(currentResultArtist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  765. }
  766. }
  767. function showResults_table_playlist(playlists, update) {
  768. var tableBody = $('#tab_search_table_results_tbody_results')
  769. if (!update) $(tableBody).html('')
  770. $('#tab_search_table_results_thead_playlist').removeClass('hide')
  771. for (var i = 0; i < playlists.length; i++) {
  772. var currentResultPlaylist = playlists[i]
  773. $(tableBody).append(
  774. `<tr>
  775. <td><img style="width:56px;" src="${(currentResultPlaylist.picture_small ? currentResultPlaylist.picture_small : "img/noCover.jpg")}" class="rounded" /></td>
  776. <td class="breakline">${currentResultPlaylist.title}</td>
  777. <td>${currentResultPlaylist.nb_tracks}</td>
  778. </tr>`)
  779. generateShowTracklistSelectiveButton(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  780. generateDownloadLink(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  781. }
  782. $('.tooltipped').tooltip({delay: 100})
  783. }
  784. // TODO: Finish Vue.js Implementation
  785. var trackListSelectiveModalApp = new Vue({
  786. el: '#modal_trackListSelective',
  787. data: {
  788. title: "",
  789. metadata : "",
  790. release_date: "",
  791. label: "",
  792. explicit: false,
  793. image: "",
  794. type: "",
  795. link: "",
  796. head: null,
  797. body: []
  798. }
  799. })
  800. var artistModalApp = new Vue({
  801. el: '#modal_artist',
  802. data: {
  803. currentTab: '',
  804. sortKey: 'release_date',
  805. sortOrder: 'desc',
  806. title: "",
  807. image: "",
  808. type: "",
  809. link: "",
  810. head: null,
  811. body: null
  812. },
  813. methods: {
  814. downloadClick : function(url, e){
  815. if (e) e.preventDefault();
  816. sendAddToQueue(url)
  817. },
  818. downloadRClick: function(url, e){
  819. if (e) e.preventDefault();
  820. $(modalQuality).data("url", url)
  821. $(modalQuality).css('display', 'block')
  822. $(modalQuality).addClass('animated fadeIn')
  823. return false;
  824. },
  825. moreInfo: function(url, e){
  826. if (e) e.preventDefault();
  827. showTrackListSelective(url, true)
  828. },
  829. sortBy: function(key) {
  830. if (key == this.sortKey) {
  831. this.sortOrder = (this.sortOrder == 'asc') ? 'desc' : 'asc';
  832. } else {
  833. this.sortKey = key;
  834. this.sortOrder = 'asc';
  835. }
  836. },
  837. changeTab: function(tab){
  838. this.currentTab = tab
  839. },
  840. checkNewRelease: function(date){
  841. var g1 = new Date();
  842. var g2 = new Date(date);
  843. g2.setDate(g2.getDate()+3)
  844. g1.setHours(0,0,0,0)
  845. if (g1.getTime() <= g2.getTime()){
  846. return true;
  847. }else {
  848. return false;
  849. }
  850. }
  851. },
  852. updated: function(){
  853. this.$nextTick(function () {
  854. if (this.body != {}){
  855. M.Tabs.init(document.getElementById("artist-tabs"));
  856. }
  857. })
  858. },
  859. computed: {
  860. showTable() {
  861. return _.orderBy(this.body[this.currentTab], this.sortKey, this.sortOrder)
  862. }
  863. }
  864. })
  865. // Generate Button for tracklist with selection
  866. function generateShowTracklistSelectiveButton(link) {
  867. var btn_showTrackListSelective = $('<button class="waves-effect btn-flat"><i class="material-icons">list</i></button>')
  868. $(btn_showTrackListSelective).click(function (ev){
  869. ev.preventDefault()
  870. showTrackListSelective(link)
  871. })
  872. return btn_showTrackListSelective
  873. }
  874. function showTrackListSelective(link) {
  875. $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective').addClass('hide')
  876. $('#modal_trackListSelective_table_trackListSelective_tbody_loadingIndicator').removeClass('hide')
  877. trackListSelectiveModalApp.title = "Loading..."
  878. trackListSelectiveModalApp.image = ""
  879. trackListSelectiveModalApp.metadata = ""
  880. trackListSelectiveModalApp.label = ""
  881. trackListSelectiveModalApp.release_date = ""
  882. trackListSelectiveModalApp.explicit = false
  883. trackListSelectiveModalApp.type = ""
  884. trackListSelectiveModalApp.head = []
  885. trackListSelectiveModalApp.body = []
  886. $('#modal_trackListSelective').modal('open')
  887. let type = getTypeFromLink(link)
  888. let id = getIDFromLink(link, type)
  889. socket.emit('getTracklist', {id: id, type: type})
  890. }
  891. $('#download_track_selection').on('contextmenu', function(e){
  892. e.preventDefault();
  893. var urls = []
  894. $("input:checkbox.trackCheckbox:checked").each(function(){
  895. urls.push($(this).val())
  896. })
  897. if(urls.length != 0){
  898. urls = urls.join(";")
  899. $(modalQuality).data("url", urls)
  900. $(modalQuality).css('display', 'block')
  901. $(modalQuality).addClass('animated fadeIn')
  902. }
  903. return false;
  904. }).on('click', function(e){
  905. e.preventDefault()
  906. var urls = []
  907. $("input:checkbox.trackCheckbox:checked").each(function(){
  908. urls.push($(this).val())
  909. })
  910. if(urls.length != 0){
  911. sendAddToQueue(urls.join(";"))
  912. $('#modal_trackListSelective').modal('close')
  913. }
  914. })
  915. // Generate Button for tracklist without selection
  916. function generateShowArtistButton(link) {
  917. var btn_showTrackList = $('<button class="waves-effect btn-flat"><i class="material-icons">list</i></button>')
  918. $(btn_showTrackList).click(function (ev) {
  919. ev.preventDefault()
  920. showArtistModal(link)
  921. })
  922. return btn_showTrackList
  923. }
  924. function showArtistModal(link) {
  925. $('#modal_artist_table_trackList_tbody_trackList').addClass('hide')
  926. $('#modal_artist_table_trackList_tbody_noResults').addClass('hide')
  927. $('#modal_artist_table_trackList_tbody_loadingIndicator').removeClass('hide')
  928. artistModalApp.title = "Loading..."
  929. artistModalApp.image = ""
  930. artistModalApp.type = ""
  931. artistModalApp.currentTab = ''
  932. artistModalApp.sortKey = 'release_date'
  933. artistModalApp.sortOrder = 'desc'
  934. artistModalApp.link = link
  935. artistModalApp.head = []
  936. artistModalApp.body = null
  937. $('#modal_artist').modal('open')
  938. let type = "artist"
  939. let id = getIDFromLink(link, type)
  940. socket.emit('getTracklist', {id: id, type: type})
  941. }
  942. function parseAlbum(data){
  943. let trackList = data.tracks
  944. tableBody = $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective')
  945. $(tableBody).html('')
  946. trackListSelectiveModalApp.type = "Album"
  947. trackListSelectiveModalApp.link = `https://www.deezer.com/album/${data.id}`
  948. trackListSelectiveModalApp.title = data.title
  949. trackListSelectiveModalApp.explicit = data.explicit_lyrics
  950. trackListSelectiveModalApp.label = data.label
  951. trackListSelectiveModalApp.metadata = `${data.artist.name} • ${trackList.length} songs`
  952. trackListSelectiveModalApp.release_date = data.release_date.substring(0,10)
  953. trackListSelectiveModalApp.image = data.cover_xl
  954. trackListSelectiveModalApp.head = [
  955. {title: '<i class="material-icons">music_note</i>', width: "24px"},
  956. {title: '#'},
  957. {title: 'Song'},
  958. {title: 'Artist', hideonsmall:true},
  959. {title: '<i class="material-icons">timer</i>', width: "40px"},
  960. {title: '<div class="valign-wrapper"><label><input class="selectAll" type="checkbox" id="selectAll"><span></span></label></div>', width: "24px"}
  961. ]
  962. $('.selectAll').prop('checked', false)
  963. if (trackList[trackList.length-1].disk_number != 1){
  964. baseDisc = 0
  965. } else {
  966. baseDisc =1
  967. }
  968. let totalDuration = 0
  969. for (var i = 0; i < trackList.length; i++) {
  970. totalDuration += trackList[i].duration
  971. discNum = trackList[i].disk_number
  972. if (discNum != baseDisc){
  973. $(tableBody).append(`<tr><td colspan="4" style="opacity: 0.54;"><i class="material-icons valignicon tiny">album</i>${discNum}</td></tr>`)
  974. baseDisc = discNum
  975. }
  976. $(tableBody).append(
  977. `<tr>
  978. <td><i class="material-icons ${(trackList[i].preview ? `preview_playlist_controls" preview="${trackList[i].preview}"` : 'grey-text"')}>play_arrow</i></td>
  979. <td>${trackList[i].track_position}</td>
  980. <td class="hide-on-med-and-up">
  981. <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>
  982. <p class="remove-margin secondary-text">${trackList[i].artist.name}</p>
  983. </td>
  984. <td class="hide-on-small-only breakline">${(trackList[i].explicit_lyrics ? `<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="${"Explicit"}">explicit</i> ` : '')}${trackList[i].title}</td>
  985. <td class="hide-on-small-only breakline">${trackList[i].artist.name}</td>
  986. <td>${convertDuration(trackList[i].duration)}</td>
  987. <td>
  988. <div class="valign-wrapper">
  989. <label>
  990. <input class="trackCheckbox valign" type="checkbox" id="trackChk${i}" value="${trackList[i].link}"><span></span>
  991. </label>
  992. </div>
  993. </td>
  994. </tr>`
  995. )
  996. addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
  997. }
  998. var [hh,mm,ss] = convertDurationSeparated(totalDuration)
  999. trackListSelectiveModalApp.metadata += `, ${hh>0 ? `${hh} hr` : ""} ${mm} min`
  1000. $('#modal_trackListSelective_table_trackListSelective_tbody_loadingIndicator').addClass('hide')
  1001. $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective').removeClass('hide')
  1002. }
  1003. socket.on("show_album", parseAlbum)
  1004. function parsePlaylist(data){
  1005. let trackList = data.tracks
  1006. tableBody = $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective')
  1007. $(tableBody).html('')
  1008. trackListSelectiveModalApp.type = "Playlist"
  1009. trackListSelectiveModalApp.link = `https://www.deezer.com/playlist/${data.id}`
  1010. trackListSelectiveModalApp.title = data.title
  1011. trackListSelectiveModalApp.image = data.picture_xl
  1012. trackListSelectiveModalApp.release_date = data.creation_date.substring(0,10)
  1013. trackListSelectiveModalApp.metadata = `by ${data.creator.name} • ${trackList.length} songs`
  1014. trackListSelectiveModalApp.head = [
  1015. {title: '<i class="material-icons">music_note</i>', width: "24px"},
  1016. {title: '#'},
  1017. {title: 'Song'},
  1018. {title: 'Artist', hideonsmall:true},
  1019. {title: 'Album', hideonsmall:true},
  1020. {title: '<i class="material-icons">timer</i>', width: "40px"},
  1021. {title: '<div class="valign-wrapper"><label><input class="selectAll" type="checkbox" id="selectAll"><span></span></label></div>', width: "24px"}
  1022. ]
  1023. $('.selectAll').prop('checked', false)
  1024. let totalDuration = 0
  1025. for (var i = 0; i < trackList.length; i++) {
  1026. totalDuration += trackList[i].duration
  1027. $(tableBody).append(
  1028. `<tr>
  1029. <td><i class="material-icons ${(trackList[i].preview ? `preview_playlist_controls" preview="${trackList[i].preview}"` : 'grey-text"')}>play_arrow</i></td>
  1030. <td>${(i + 1)}</td>
  1031. <td class="hide-on-med-and-up">
  1032. <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>
  1033. <p class="remove-margin secondary-text">${trackList[i].artist.name}</p>
  1034. </td>
  1035. <td class="hide-on-small-only breakline">${(trackList[i].explicit_lyrics ? `<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="${"Explicit"}">explicit</i> ` : '')}${trackList[i].title}</td>
  1036. <td class="hide-on-small-only breakline"><span class="resultArtist resultLink" data-link="${trackList[i].artist.link}">${trackList[i].artist.name}</span></td>
  1037. <td class="hide-on-small-only breakline"><span class="resultAlbum resultLink" data-link="https://www.deezer.com/album/${trackList[i].album.id}">${trackList[i].album.title}</span></td>
  1038. <td>${convertDuration(trackList[i].duration)}</td>
  1039. <td>
  1040. <div class="valign-wrapper">
  1041. <label>
  1042. <input class="trackCheckbox valign" type="checkbox" id="trackChk${i}" value="${trackList[i].link}"><span></span>
  1043. </label>
  1044. </div>
  1045. </td>
  1046. </tr>`
  1047. )
  1048. addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
  1049. tableBody.children('tr:last').find('.resultArtist').click(function (ev){
  1050. ev.preventDefault()
  1051. showArtistModal($(this).data("link"))
  1052. })
  1053. tableBody.children('tr:last').find('.resultAlbum').click(function (ev){
  1054. ev.preventDefault()
  1055. showTrackListSelective($(this).data("link"))
  1056. })
  1057. }
  1058. var [hh,mm,ss] = convertDurationSeparated(totalDuration)
  1059. trackListSelectiveModalApp.metadata += `, ${hh>0 ? `${hh} hr` : ""} ${mm} min`
  1060. $('#modal_trackListSelective_table_trackListSelective_tbody_loadingIndicator').addClass('hide')
  1061. $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective').removeClass('hide')
  1062. }
  1063. socket.on("show_playlist", parsePlaylist)
  1064. function parseSpotifyPlaylist(data){
  1065. console.log(data)
  1066. let trackList = data.tracks
  1067. tableBody = $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective')
  1068. $(tableBody).html('')
  1069. trackListSelectiveModalApp.type = "Spotify Playlist"
  1070. trackListSelectiveModalApp.link = 'spotify:playlist:'+data.id
  1071. trackListSelectiveModalApp.title = data.name
  1072. trackListSelectiveModalApp.image = data.images[0].url
  1073. trackListSelectiveModalApp.metadata = `by ${data.owner.display_name} • ${trackList.length} songs`
  1074. trackListSelectiveModalApp.head = [
  1075. {title: '<i class="material-icons">music_note</i>', width: "24px"},
  1076. {title: '#'},
  1077. {title: 'Song'},
  1078. {title: 'Artist', hideonsmall:true},
  1079. {title: '<i class="material-icons">timer</i>', width: "40px"},
  1080. {title: '<div class="valign-wrapper"><label><input class="selectAll" type="checkbox" id="selectAll"><span></span></label></div>', width: "24px"}
  1081. ]
  1082. let totalDuration = 0
  1083. for (var i = 0; i < trackList.length; i++) {
  1084. totalDuration += Math.round(trackList[i].duration_ms/1000)
  1085. $(tableBody).append(
  1086. `<tr>
  1087. <td><i class="material-icons ${(trackList[i].preview_url ? `preview_playlist_controls" preview="${trackList[i].preview_url}"` : 'grey-text"')}>play_arrow</i></td>
  1088. <td>${(i + 1)}</td>
  1089. <td class="hide-on-med-and-up">
  1090. <p class="remove-margin">${(trackList[i].explicit ? `<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="${"Explicit"}">explicit</i> ` : '')}${trackList[i].name}</p>
  1091. <p class="remove-margin secondary-text">${trackList[i].artists[0].name}</p>
  1092. </td>
  1093. <td class="hide-on-small-only breakline">${(trackList[i].explicit ? `<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="${"Explicit"}">explicit</i> ` : '')}${trackList[i].name}</td>
  1094. <td class="hide-on-small-only breakline">${trackList[i].artists[0].name}</td>
  1095. <td>${convertDuration(Math.round(trackList[i].duration_ms/1000))}</td>
  1096. <td>
  1097. <div class="valign-wrapper">
  1098. <label>
  1099. <input class="trackCheckbox valign" type="checkbox" id="trackChk${i}" value="${trackList[i].uri}"><span></span>
  1100. </label>
  1101. </div>
  1102. </td>
  1103. </tr>`
  1104. )
  1105. addPreviewControlsClick(tableBody.children('tr:last').find('.preview_playlist_controls'))
  1106. }
  1107. var [hh,mm,ss] = convertDurationSeparated(totalDuration)
  1108. trackListSelectiveModalApp.metadata += `, ${hh>0 ? `${hh} hr` : ""} ${mm} min`
  1109. $('#modal_trackListSelective_table_trackListSelective_tbody_loadingIndicator').addClass('hide')
  1110. $('#modal_trackListSelective_table_trackListSelective_tbody_trackListSelective').removeClass('hide')
  1111. }
  1112. socket.on("show_spotifyplaylist", parseSpotifyPlaylist)
  1113. function parseArtist(data){
  1114. let trackList = data.releases
  1115. artistModalApp.title = data.name
  1116. artistModalApp.image = data.picture_xl
  1117. artistModalApp.type = "Artist"
  1118. artistModalApp.link = `https://www.deezer.com/artist/${data.id}`
  1119. artistModalApp.currentTab = Object.keys(trackList)[0]
  1120. artistModalApp.sortKey = 'release_date'
  1121. artistModalApp.sortOrder = 'desc'
  1122. artistModalApp.head = [
  1123. {title: '', smallonly:true},
  1124. {title: 'Title', hideonsmall:true, sortKey: "title"},
  1125. {title: 'Release Date', hideonsmall:true, sortKey: "release_date"},
  1126. {title: '', width: "56px"}
  1127. ]
  1128. if (_.isEmpty(trackList)){
  1129. artistModalApp.body = null
  1130. $('#modal_artist_table_trackList_tbody_noResults').removeClass('hide')
  1131. }else{
  1132. artistModalApp.body = trackList
  1133. }
  1134. $('#modal_artist_table_trackList_tbody_loadingIndicator').addClass('hide')
  1135. }
  1136. socket.on("show_artist", parseArtist)
  1137. //#############################################TAB_CHARTS#############################################\\
  1138. socket.on("init_charts", function(charts){
  1139. let selectedChart = localStorage.getItem("chart")
  1140. for (var i = 0; i < charts.length; i++) {
  1141. $('#tab_charts_select_country').append('<option value="' + charts[i].title + '" data-icon="' + charts[i].picture_small + '" data-id="' + charts[i].id + '" class="left rounded">' + charts[i].title + '</option>')
  1142. }
  1143. $('#tab_charts_select_country').find('option[value="' + selectedChart + '"]').attr("selected", true)
  1144. $('select').formSelect()
  1145. var country = $('#tab_charts_select_country').find('option:selected').data('id')
  1146. var chart_id = $('#tab_charts_select_country').find('option:selected').data('id')
  1147. socket.emit("getChartTracks", country)
  1148. $("#downloadChartPlaylist").data("id", chart_id)
  1149. })
  1150. $('#tab_charts_select_country').on('change', function () {
  1151. var chart_id = $(this).find('option:selected').data('id')
  1152. var country = $(this).find('option:selected').val()
  1153. localStorage.setItem('chart', country)
  1154. $('#tab_charts_table_charts_tbody_charts').addClass('hide')
  1155. $('#tab_charts_table_charts_tbody_loadingIndicator').removeClass('hide')
  1156. socket.emit("getChartTracks", country)
  1157. $("#downloadChartPlaylist").data("id", chart_id)
  1158. })
  1159. socket.on("setChartTracks", function (data) {
  1160. var chartsTableBody = $('#tab_charts_table_charts_tbody_charts'), currentChartTrack
  1161. chartsTableBody.html('')
  1162. for (var i = 0; i < data.length; i++) {
  1163. currentChartTrack = data[i]
  1164. $(chartsTableBody).append(
  1165. `<tr>
  1166. <td>${(i + 1)}</td>
  1167. <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>
  1168. <td class="hide-on-med-and-up">
  1169. <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>
  1170. <p class="remove-margin secondary-text">${currentChartTrack.artist.name}</p>
  1171. <p class="remove-margin secondary-text">${currentChartTrack.album.title}</p>
  1172. </td>
  1173. <td class="hide-on-small-only breakline">${(currentChartTrack.explicit_lyrics ? `<i class="material-icons valignicon tiny materialize-red-text tooltipped" data-tooltip="${"Explicit"}">explicit</i> ` : '')}${currentChartTrack.title}</td>
  1174. <td class="hide-on-small-only breakline"><span class="resultArtist resultLink" data-link="${currentChartTrack.artist.link}">${currentChartTrack.artist.name}</span></td>
  1175. <td class="hide-on-small-only breakline"><span class="resultAlbum resultLink" data-link="https://www.deezer.com/album/${currentChartTrack.album.id}">${currentChartTrack.album.title}</span></td>
  1176. <td>${convertDuration(currentChartTrack.duration)}</td>
  1177. </tr>`)
  1178. generateDownloadLink(currentChartTrack.link).appendTo(chartsTableBody.children('tr:last')).wrap('<td>')
  1179. addPreviewControlsHover(chartsTableBody.children('tr:last').find('.preview_controls'))
  1180. addPreviewControlsClick(chartsTableBody.children('tr:last').find('.single-cover'))
  1181. chartsTableBody.children('tr:last').find('.resultArtist').click(function (ev){
  1182. ev.preventDefault()
  1183. showArtistModal($(this).data("link"))
  1184. })
  1185. chartsTableBody.children('tr:last').find('.resultAlbum').click(function (ev){
  1186. ev.preventDefault()
  1187. showTrackListSelective($(this).data("link"))
  1188. })
  1189. }
  1190. $('#tab_charts_table_charts_tbody_loadingIndicator').addClass('hide')
  1191. chartsTableBody.removeClass('hide')
  1192. })
  1193. //#############################################TAB_PLAYLISTS############################################\\
  1194. function loadUserPlaylists(data, table){
  1195. var tableBody = $(table)
  1196. $(tableBody).html('')
  1197. for (var i = 0; i < data.length; i++) {
  1198. var currentResultPlaylist = data[i]
  1199. $(tableBody).append(
  1200. `<tr>
  1201. <td><img src="${currentResultPlaylist.picture_medium || "/img/noCover.jpg"}" class="rounded" width="56px" /></td>
  1202. <td>${currentResultPlaylist.title}</td>
  1203. <td>${currentResultPlaylist.nb_tracks}</td>
  1204. </tr>`)
  1205. generateShowTracklistSelectiveButton(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  1206. generateDownloadLink(currentResultPlaylist.link).appendTo(tableBody.children('tr:last')).wrap('<td>')
  1207. }
  1208. $('.tooltipped').tooltip({delay: 100})
  1209. }
  1210. socket.on("init_favorites", function(favorites) {
  1211. loadUserPlaylists(favorites.playlists, '#table_personal_playlists')
  1212. })
  1213. socket.on("updated_userFavorites", function (favorites) {
  1214. loadUserPlaylists(favorites.playlists, '#table_personal_playlists')
  1215. })
  1216. socket.on("updated_userSpotifyPlaylists", function (data) {
  1217. loadUserPlaylists(data, '#table_personal_spotify_playlists')
  1218. })
  1219. //###############################################TAB_LINK#############################################\\
  1220. var linkAnalyzerSong = new Vue({
  1221. el: '#link_analyzer_song',
  1222. data: {
  1223. d:{}
  1224. },
  1225. methods:{
  1226. showArtist: function(){
  1227. showArtistModal(this.d.artist.link)
  1228. },
  1229. showAlbum: function(){
  1230. showTrackListSelective(`https://www.deezer.com/album/${this.d.album.id}`)
  1231. }
  1232. }
  1233. })
  1234. var linkAnalyzerAlbum = new Vue({
  1235. el: '#link_analyzer_album',
  1236. data: {
  1237. d:{}
  1238. },
  1239. methods:{
  1240. showArtist: function(){
  1241. showArtistModal(`https://www.deezer.com/artist/${this.d.artist.id}`)
  1242. }
  1243. }
  1244. })
  1245. var linkAnalyzerCountryModal = new Vue({
  1246. el: '#modal_link_analyzer_country',
  1247. data: {
  1248. title: "",
  1249. countries: []
  1250. }
  1251. })
  1252. function parseLinkAnalyzer(link){
  1253. $("#link_analyzer_start").hide()
  1254. $("#link_analyzer_notSupported").hide()
  1255. $("#link_analyzer_album").hide()
  1256. $("#link_analyzer_song").hide()
  1257. $("#link_analyzer_loading").show()
  1258. socket.emit('analyzeLink', link)
  1259. }
  1260. socket.on("analyze_track", (data)=>{
  1261. data.countries_string = ""
  1262. let countries = []
  1263. data.available_countries.forEach((cc)=>{
  1264. let temp = []
  1265. let chars = [...cc].map(c => c.charCodeAt() + 127397)
  1266. temp.push(String.fromCodePoint(...chars))
  1267. temp.push(COUNTRIES[cc])
  1268. countries.push(temp)
  1269. })
  1270. data.duration_string = convertDuration(data.duration)
  1271. linkAnalyzerCountryModal.title = `${data.title}${data.title_version ? ` ${data.title_version}`: ""}`
  1272. linkAnalyzerCountryModal.countries = countries
  1273. linkAnalyzerSong.d = data
  1274. $("#link_analyzer_loading").hide()
  1275. $("#link_analyzer_song").show()
  1276. })
  1277. socket.on("analyze_album", (data)=>{
  1278. let genres = []
  1279. data.genres.data.forEach((genre)=>{
  1280. genres.push(genre.name)
  1281. })
  1282. data.genres_string = genres.join(", ")
  1283. data.duration_string = convertDuration(data.duration)
  1284. data.tracks_string = data.nb_tracks+" songs"
  1285. linkAnalyzerAlbum.d = data
  1286. $("#link_analyzer_loading").hide()
  1287. $("#link_analyzer_album").show()
  1288. })
  1289. socket.on("analyze_notSupported", function(){
  1290. $("#link_analyzer_loading").hide()
  1291. $("#link_analyzer_notSupported").show()
  1292. })
  1293. //############################################TAB_DOWNLOADS###########################################\\
  1294. function sendAddToQueue(url, bitrate=null) {
  1295. if (!url) return
  1296. socket.emit('addToQueue', { url, bitrate }, () => {})
  1297. }
  1298. function initQueue(data) {
  1299. const { queue: initQueue, queueComplete: initQueueComplete, currentItem, queueList: initQueueList, restored } = data
  1300. if (initQueueComplete.length) {
  1301. initQueueComplete.forEach(item => {
  1302. initQueueList[item].silent = true
  1303. addToQueue(initQueueList[item])
  1304. })
  1305. }
  1306. if (currentItem) {
  1307. initQueueList[currentItem].silent = true
  1308. addToQueue(initQueueList[currentItem], true)
  1309. }
  1310. initQueue.forEach(item => {
  1311. initQueueList[item].silent = true
  1312. addToQueue(initQueueList[item])
  1313. })
  1314. if (restored){
  1315. toast("Queue Restored", 'done')
  1316. socket.emit('queueRestored')
  1317. }
  1318. }
  1319. function addToQueue(queueItem, current = false) {
  1320. if (Array.isArray(queueItem)){
  1321. if (queueItem.length > 1){
  1322. queueItem.forEach((item, i) => {
  1323. item.silent = true
  1324. addToQueue(item)
  1325. });
  1326. toast(`Added ${queueItem.length} items to queue`, 'playlist_add_check')
  1327. return
  1328. }else{
  1329. queueItem = queueItem[0]
  1330. }
  1331. }
  1332. var tableBody = $('#tab_downloads_table_downloads').find('tbody')
  1333. queueList[queueItem.uuid] = queueItem
  1334. if (queueItem.downloaded + queueItem.failed == queueItem.size) {
  1335. if (queueComplete.indexOf(queueItem.uuid) == -1) {
  1336. queueComplete.push(queueItem.uuid)
  1337. }
  1338. } else {
  1339. if (queue.indexOf(queueItem.uuid) == -1) {
  1340. queue.push(queueItem.uuid)
  1341. }
  1342. }
  1343. let queueDOM = document.getElementById('download_' + queueItem.uuid)
  1344. if (typeof queueDOM == 'undefined' || queueDOM == null) {
  1345. $(tableBody).append(
  1346. `<tr class="download_object" id="download_${queueItem.uuid}" data-deezerid="${queueItem.id}" >
  1347. <td class="download_cover">
  1348. <img width="75px" src="${queueItem.cover}" alt="Cover ${queueItem.title}"/>
  1349. </td>
  1350. <td class="download_info_data">
  1351. ${queueItem.title}<br>
  1352. <span class="secondary-text">${queueItem.artist}</span>
  1353. </td>
  1354. <td class="download_info_status">
  1355. <span class="queue_downloaded">${queueItem.downloaded + queueItem.failed}</span>/${queueItem.size}<br>
  1356. </td>
  1357. </tr>
  1358. <tr class="download_bar" id="download_bar_${queueItem.uuid}">
  1359. <td colspan="4" class="progress"><div id="bar_${queueItem.uuid}" class="indeterminate"></div></td>
  1360. </tr>`)
  1361. }
  1362. var btn_remove = $(`<button data-uuid="${queueItem.uuid}" class="btn-flat waves-effect"><i class="material-icons queue_icon">remove</i></button>`)
  1363. $(btn_remove).click(function (ev) {
  1364. ev.preventDefault()
  1365. let resultIcon = $(this).find('.queue_icon')[0].innerText
  1366. switch (resultIcon) {
  1367. case 'remove':
  1368. socket.emit("removeFromQueue", queueItem.uuid)
  1369. break;
  1370. case 'error':
  1371. case 'warning':
  1372. message(`Errors for ${quoteattr(queueItem.title)}`, queueItem.errorLog)
  1373. break;
  1374. }
  1375. })
  1376. btn_remove.appendTo(tableBody.children('tr.download_object:last')).wrap('<td class="eventBtn center">')
  1377. if (queueItem.progress > 0 || current) {
  1378. startDownload(queueItem.uuid)
  1379. }
  1380. $('#bar_' + queueItem.uuid).css('width', queueItem.progress + '%')
  1381. if (queueItem.failed >= 1 && $('#download_' + queueItem.uuid + ' .queue_failed').length == 0) {
  1382. $('#download_' + queueItem.uuid + ' .download_info_status').append(
  1383. `<span class="secondary-text queue_failed_button"><span class="queue_failed">${queueItem.failed}</span> Failed</span>`
  1384. )
  1385. }
  1386. if (queueItem.downloaded + queueItem.failed == queueItem.size) {
  1387. let resultIcon = $('#download_' + queueItem.uuid).find('.queue_icon')
  1388. if (queueItem.failed == 0) {
  1389. resultIcon.text('done')
  1390. } else {
  1391. let failedButton = $('#download_' + queueItem.uuid).find('.queue_failed_button')
  1392. resultIcon.addClass('clickable')
  1393. failedButton.addClass('clickable')
  1394. queueItem.errorLog = `<table><tr><th>ID</th><th>Song</th><th>Error</th></tr><tr><td>`
  1395. queueItem.errors.forEach((error) => {
  1396. queueItem.errorLog += '<tr>'
  1397. queueItem.errorLog += `<td>${error.data.id}</td>`
  1398. queueItem.errorLog += `<td>${error.data.artist} - ${error.data.title}</td>`
  1399. if (error.errid)
  1400. queueItem.errorLog += `<td>${error.errid}</td>`
  1401. else
  1402. queueItem.errorLog += `<td>${error.message}</td>`
  1403. queueItem.errorLog += '</tr>'
  1404. });
  1405. queueItem.errorLog += '</table>'
  1406. if (queueItem.failed >= queueItem.size) {
  1407. resultIcon.text('error')
  1408. } else {
  1409. resultIcon.text('warning')
  1410. }
  1411. }
  1412. }
  1413. if (!queueItem.silent) {
  1414. toast(`${queueItem.title} added to queue!`, 'playlist_add_check')
  1415. }
  1416. }
  1417. function updateQueue(update) {
  1418. // downloaded and failed default to false?
  1419. const { uuid, downloaded, failed, progress, conversion, error, data, errid } = update
  1420. if (uuid && queue.indexOf(uuid) > -1) {
  1421. if (downloaded) {
  1422. queueList[uuid].downloaded++
  1423. $('#download_' + uuid + ' .queue_downloaded').text(
  1424. queueList[uuid].downloaded + queueList[uuid].failed
  1425. )
  1426. }
  1427. if (failed) {
  1428. queueList[uuid].failed++
  1429. $('#download_' + uuid + ' .queue_downloaded').text(
  1430. queueList[uuid].downloaded + queueList[uuid].failed
  1431. )
  1432. if (queueList[uuid].failed == 1 && $('#download_' + uuid + ' .queue_failed').length == 0) {
  1433. $('#download_' + uuid + ' .download_info_status').append(
  1434. `<span class="secondary-text queue_failed_button"><span class="queue_failed">${queueList[uuid].failed}</span> Failed</span>`
  1435. )
  1436. } else {
  1437. $('#download_' + uuid + ' .queue_failed').text(queueList[uuid].failed)
  1438. }
  1439. queueList[uuid].errors.push({ message: error, data: data, errid: errid })
  1440. }
  1441. if (progress) {
  1442. queueList[uuid].progress = progress
  1443. $('#bar_' + uuid).css('width', progress + '%')
  1444. }
  1445. if (conversion) {
  1446. $('#bar_' + uuid).css('width', (100-conversion) + '%')
  1447. }
  1448. }
  1449. }
  1450. function removeFromQueue(uuid) {
  1451. let index = queue.indexOf(uuid)
  1452. if (index > -1) {
  1453. queue.splice(index, 1)
  1454. $('#download_' + uuid+',#download_bar_' + uuid).addClass('animated fadeOutRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
  1455. $(this).remove()
  1456. delete queueList[uuid]
  1457. })
  1458. }
  1459. }
  1460. function removeAllDownloads(currentItem) {
  1461. queueComplete = []
  1462. if (currentItem == '') {
  1463. queue = []
  1464. queueList = {}
  1465. $(listEl).html('')
  1466. } else {
  1467. queue = [currentItem]
  1468. let tempQueueItem = queueList[currentItem]
  1469. queueList = {}
  1470. queueList[currentItem] = tempQueueItem
  1471. $('.download_object,.download_bar').addClass('animated fadeOutRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
  1472. if ($(this).attr('id') != 'download_' + currentItem) $(this).remove()
  1473. })
  1474. }
  1475. }
  1476. function removedFinishedDownloads() {
  1477. queueComplete.forEach(item => {
  1478. $('#download_' + item+',#download_bar_' + item).addClass('animated fadeOutRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
  1479. $(this).remove()
  1480. })
  1481. })
  1482. queueComplete = []
  1483. }
  1484. function finishDownload(uuid) {
  1485. if (queue.indexOf(uuid) > -1) {
  1486. toast(`${queueList[uuid].title} finished downloading`, 'done')
  1487. $('#bar_' + uuid).css('width', '100%')
  1488. let resultIcon = $('#download_' + uuid).find('.queue_icon')
  1489. if (queueList[uuid].failed == 0) {
  1490. resultIcon.text('done')
  1491. } else {
  1492. let failedButton = $('#download_' + uuid).find('.queue_failed_button')
  1493. resultIcon.addClass('clickable')
  1494. failedButton.addClass('clickable')
  1495. queueList[uuid].errorLog = `<table><tr><th>ID</th><th>Song</th><th>Error</th></tr><tr><td>`
  1496. queueList[uuid].errors.forEach((error) => {
  1497. queueList[uuid].errorLog += '<tr>'
  1498. queueList[uuid].errorLog += `<td>${error.data.id}</td>`
  1499. queueList[uuid].errorLog += `<td>${error.data.artist} - ${error.data.title}</td>`
  1500. if (error.errid)
  1501. queueList[uuid].errorLog += `<td>${error.errid}</td>`
  1502. else
  1503. queueList[uuid].errorLog += `<td>${error.message}</td>`
  1504. queueList[uuid].errorLog += '</tr>'
  1505. });
  1506. queueList[uuid].errorLog += '</table>'
  1507. if (queueList[uuid].failed >= queueList[uuid].size) {
  1508. resultIcon.text('error')
  1509. } else {
  1510. resultIcon.text('warning')
  1511. }
  1512. }
  1513. let index = queue.indexOf(uuid)
  1514. if (index > -1) {
  1515. queue.splice(index, 1)
  1516. queueComplete.push(uuid)
  1517. }
  1518. if (queue.length <= 0) {
  1519. toast("All downloads completed!", 'done_all')
  1520. }
  1521. }
  1522. }
  1523. function startDownload(uuid) {
  1524. $('#bar_' + uuid)
  1525. .removeClass('converting')
  1526. .removeClass('indeterminate')
  1527. .addClass('determinate')
  1528. }
  1529. function startConversion(uuid) {
  1530. $('#bar_' + uuid)
  1531. .addClass('converting')
  1532. .removeClass('indeterminate')
  1533. .addClass('determinate')
  1534. .css('width', '100%')
  1535. }
  1536. socket.on('init_downloadQueue', initQueue)
  1537. socket.on('addedToQueue', addToQueue)
  1538. socket.on('updateQueue', updateQueue)
  1539. socket.on('removedFromQueue', removeFromQueue)
  1540. socket.on('removedAllDownloads', removeAllDownloads)
  1541. socket.on('removedFinishedDownloads', removedFinishedDownloads)
  1542. socket.on('finishDownload', finishDownload)
  1543. socket.on('startDownload', startDownload)
  1544. socket.on('startConversion', startConversion)
  1545. socket.on('errorMessage', function(error) {
  1546. toast(error, 'error')
  1547. })
  1548. socket.on('queueError', function(queueItem) {
  1549. if (queueItem.errid) toast(queueItem.errid, 'error')
  1550. else toast(queueItem.error, 'error')
  1551. })
  1552. socket.on('alreadyInQueue', function(data) {
  1553. toast(`${data.title} is already in queue`, 'playlist_add_check')
  1554. })
  1555. socket.on('loginNeededToDownload', function(data) {
  1556. toast("You need to login first before downloading!", 'report')
  1557. })
  1558. $('#cancelAllTable').click(function (ev) {
  1559. socket.emit('cancelAllDownloads')
  1560. })
  1561. $('#clearTracksTable').click(function (ev) {
  1562. socket.emit('removeFinishedDownloads')
  1563. })
  1564. //****************************************************************************************************\\
  1565. //******************************************HELPER-FUNCTIONS******************************************\\
  1566. //****************************************************************************************************\\
  1567. /**
  1568. * Replaces special characters with HTML friendly counterparts
  1569. * @param s string
  1570. * @param preserveCR preserves the new line character
  1571. * @returns {string}
  1572. */
  1573. function quoteattr(s, preserveCR) {
  1574. preserveCR = preserveCR ? '&#13;' : '\n'
  1575. return ('' + s) /* Forces the conversion to string. */
  1576. .replace(/&/g, '&amp;') /* This MUST be the 1st replacement. */
  1577. .replace(/'/g, '&apos;') /* The 4 other predefined entities, required. */
  1578. .replace(/"/g, '&quot;')
  1579. .replace(/</g, '&lt;')
  1580. .replace(/>/g, '&gt;')
  1581. /*
  1582. You may add other replacements here for HTML only
  1583. (but it's not necessary).
  1584. Or for XML, only if the named entities are defined in its DTD.
  1585. */
  1586. .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */
  1587. .replace(/[\r\n]/g, preserveCR)
  1588. }
  1589. function getIDFromLink(link, type) {
  1590. if (link.indexOf('?') > -1) {
  1591. link = link.substring(0, link.indexOf("?"))
  1592. }
  1593. // Spotify
  1594. if ((link.startsWith("http") && link.indexOf('open.spotify.com/') >= 0)){
  1595. switch (type){
  1596. case "spotifyplaylist":
  1597. return link.slice(link.indexOf("/playlist/")+10)
  1598. break
  1599. case "spotifytrack":
  1600. return link.slice(link.indexOf("/track/")+7)
  1601. break
  1602. case "spotifyalbum":
  1603. return link.slice(link.indexOf("/album/")+7)
  1604. break
  1605. }
  1606. } else if (link.startsWith("spotify:")){
  1607. switch (type){
  1608. case "spotifyplaylist":
  1609. return link.slice(link.indexOf("playlist:")+9)
  1610. break
  1611. case "spotifytrack":
  1612. return link.slice(link.indexOf("track:")+6)
  1613. break
  1614. case "spotifyalbum":
  1615. return link.slice(link.indexOf("album:")+6)
  1616. break
  1617. }
  1618. // Deezer
  1619. } else if(type == "artisttop") {
  1620. return link.match(/\/artist\/(\d+)\/top_track/)[1];
  1621. } else {
  1622. return link.substring(link.lastIndexOf("/") + 1)
  1623. }
  1624. }
  1625. function getTypeFromLink(link) {
  1626. var type
  1627. if (link.indexOf('spotify') > -1){
  1628. type = "spotify"
  1629. if (link.indexOf('playlist') > -1) type += "playlist"
  1630. else if (link.indexOf('track') > -1) type += "track"
  1631. else if (link.indexOf('album') > -1) type += "album"
  1632. } else if (link.indexOf('/track') > -1) {
  1633. type = "track"
  1634. } else if (link.indexOf('/playlist') > -1) {
  1635. type = "playlist"
  1636. } else if (link.indexOf('/album') > -1) {
  1637. type = "album"
  1638. } else if (link.match(/\/artist\/(\d+)\/top_track/)) {
  1639. type = "artisttop";
  1640. } else if (link.indexOf('/artist')) {
  1641. type = "artist"
  1642. }
  1643. return type
  1644. }
  1645. function generateDownloadLink(url) {
  1646. var btn_download = $('<button class="waves-effect btn-flat" oncontextmenu="return false;"><i class="material-icons">file_download</i></button>')
  1647. $(btn_download).on('contextmenu', function(e){
  1648. e.preventDefault();
  1649. $(modalQuality).data("url", url)
  1650. $(modalQuality).css('display', 'block')
  1651. $(modalQuality).addClass('animated fadeIn')
  1652. return false;
  1653. }).on('click', function(e){
  1654. e.preventDefault();
  1655. sendAddToQueue(url)
  1656. })
  1657. return btn_download
  1658. }
  1659. function modalQualityButton(bitrate){
  1660. var url=$(modalQuality).data("url")
  1661. sendAddToQueue(url, bitrate)
  1662. $('#modal_trackListSelective').modal('close')
  1663. $(modalQuality).addClass('animated fadeOut')
  1664. }
  1665. function addPreviewControlsHover(el){
  1666. el.hover( function () {
  1667. $(this).css({opacity: 1})
  1668. }, function () {
  1669. if (($(this).parent().attr("playing") && preview_stopped) || !$(this).parent().attr("playing")){
  1670. $(this).css({opacity: 0}, 200)
  1671. }
  1672. })
  1673. }
  1674. function addPreviewControlsClick(el){
  1675. el.click(function (e) {
  1676. e.preventDefault()
  1677. var icon = (this.tagName == "I" ? $(this) : $(this).children('i'))
  1678. if ($(this).attr("playing")){
  1679. if (preview_track.paused){
  1680. preview_track.play()
  1681. preview_stopped = false
  1682. icon.text("pause")
  1683. $(preview_track).animate({volume: preview_max_volume/100}, 500)
  1684. }else{
  1685. preview_stopped = true
  1686. icon.text("play_arrow")
  1687. $(preview_track).animate({volume: 0}, 250, "swing", ()=>{ preview_track.pause() })
  1688. }
  1689. }else{
  1690. $("*").removeAttr("playing")
  1691. $(this).attr("playing",true)
  1692. $('.preview_controls').text("play_arrow")
  1693. $('.preview_playlist_controls').text("play_arrow")
  1694. $('.preview_controls').css({opacity:0})
  1695. icon.text("pause")
  1696. icon.css({opacity: 1})
  1697. preview_stopped = false
  1698. $(preview_track).animate({volume: 0}, 250, "swing", ()=>{
  1699. preview_track.pause()
  1700. $('#preview-track_source').prop("src", $(this).attr("preview"))
  1701. preview_track.load()
  1702. })
  1703. }
  1704. })
  1705. }
  1706. function convertDuration(duration) {
  1707. //convert from seconds only to mm:ss format
  1708. var mm, ss
  1709. mm = Math.floor(duration / 60)
  1710. ss = duration - (mm * 60)
  1711. //add leading zero if ss < 0
  1712. if (ss < 10) {
  1713. ss = "0" + ss
  1714. }
  1715. return mm + ":" + ss
  1716. }
  1717. function convertDurationSeparated(duration){
  1718. var hh, mm, ss
  1719. mm = Math.floor(duration / 60)
  1720. hh = Math.floor(mm / 60)
  1721. ss = duration - (mm * 60)
  1722. mm -= hh*60
  1723. return [hh, mm, ss]
  1724. }
  1725. function sleep(milliseconds) {
  1726. var start = new Date().getTime()
  1727. for (var i = 0; i < 1e7; i++) {
  1728. if ((new Date().getTime() - start) > milliseconds){
  1729. break
  1730. }
  1731. }
  1732. }