window.savedAudiosPages = {} class playersSearcher { constructor(context_type, context_id) { this.context_type = context_type this.context_id = context_id this.searchType = "by_name" this.query = "" this.page = 1 this.successCallback = () => {} this.errorCallback = () => {} this.beforesendCallback = () => {} this.clearContainer = () => {} } execute() { $.ajax({ type: "POST", url: "/audios/context", data: { context: this.context_type, hash: u("meta[name=csrf]").attr("value"), page: this.page, query: this.query, context_entity: this.context_id, type: this.searchType, returnPlayers: 1, }, beforeSend: () => { this.beforesendCallback() }, error: () => { this.errorCallback() }, success: (response) => { this.successCallback(response, this) } }) } movePage(page) { this.page = page this.execute() } } window.player = new class { context = { object: {}, pagesCount: 0, count: 0, playedPages: [], } __linked_player_id = null current_track_id = 0 tracks = [] get timeType() { return localStorage.getItem('audio.timeType') ?? 0 } set timeType(value) { localStorage.setItem('audio.timeType', value) } get audioPlayer() { return this.__realAudioPlayer } get uiPlayer() { return u('.bigPlayer') } get currentTrack() { return this.__findTrack(this.current_track_id) } get previousTrack() { const current = this.__findTrack(this.current_track_id, true) return this.__findByIndex(current - 1) } get nextTrack() { const current = this.__findTrack(this.current_track_id, true) return this.__findByIndex(current + 1) } get linkedInlinePlayer() { if(!this.__linked_player_id) { return null; } return u('#' + this.__linked_player_id) } async init(input_context) { let context = Object.assign({ url: location.pathname }, input_context) this.context.object = !input_context ? null : context this.__realAudioPlayer = document.createElement("audio") this.dashPlayer = dashjs.MediaPlayer().create() await this.loadContext(input_context ? context.page : 0) this.initEvents() this.__setMediaSessionActions() } initEvents() { this.audioPlayer.ontimeupdate = () => { const time = this.audioPlayer.currentTime; const ps = ((time * 100) / this.currentTrack.length).toFixed(3) this.uiPlayer.find(".time").html(fmtTime(time)) this.__updateTime(time) if (ps <= 100) { this.uiPlayer.find(".track .selectableTrack .slider").attr('style', `left:${ ps}%`); if(this.linkedInlinePlayer) { this.linkedInlinePlayer.find(".subTracks .lengthTrackWrapper .slider").attr('style', `left:${ ps}%`) this.linkedInlinePlayer.find('.mini_timer .nobold').html(fmtTime(time)) } } } this.audioPlayer.onvolumechange = () => { const volume = this.audioPlayer.volume; const ps = Math.ceil((volume * 100) / 1); if (ps <= 100) { this.uiPlayer.find(".volumePanel .selectableTrack .slider").attr('style', `left:${ ps}%`); if(this.linkedInlinePlayer) { this.linkedInlinePlayer.find(".subTracks .volumeTrackWrapper .slider").attr('style', `left:${ ps}%`) } } localStorage.setItem('audio.volume', volume) } this.audioPlayer.onprogress = (e) => { u('.loaded_chunk').remove() const buffered = this.audioPlayer.buffered if (buffered.length > 0) { this.listen_coef += 1.25 const end = buffered.end(buffered.length - 1) const percentage = (end / window.player.audioPlayer.duration) * 100 if(this.uiPlayer.length > 0) { this.uiPlayer.find('.track .selectableTrackLoadProgress .load_bar').attr('style', `width:${percentage.toFixed(2)}%`) } if(this.linkedInlinePlayer) { this.linkedInlinePlayer.find('.lengthTrackWrapper .selectableTrackLoadProgress .load_bar').attr('style', `width:${percentage.toFixed(2)}%`) } } if(window.player.listen_coef > 10) { this.__countListen() window.player.listen_coef = -10 } } this.audioPlayer.onended = (e) => { e.preventDefault() if(!this.nextTrack && window.player.context.playedPages.indexOf(1) == -1) { this.loadContext(1, false) this.setTrack(this.__findByIndex(0).id) } else { this.playNextTrack() } if(this.linkedInlinePlayer && this.connectionType) { const parent = this.linkedInlinePlayer.closest(this.connectionType) const real_current = parent.find(`.audioEmbed[data-realid='${this.current_track_id}']`) u('.audioEntry .playerButton .playIcon.paused').removeClass('paused') u('.audioEntry .subTracks.shown').removeClass('shown') real_current.find('.subTracks').addClass('shown') this.linkPlayer(real_current) } } this.audioPlayer.volume = Number(localStorage.getItem('audio.volume') ?? 1) } async loadContext(page = 1, after = true) { if(!this.context.object) { return } const form_data = new FormData switch(this.context.object.name) { case 'entity_audios': form_data.append('context', this.context.object.name) form_data.append('context_entity', this.context.object.entity_id) break case 'playlist_context': form_data.append('context', this.context.object.name) form_data.append('context_entity', this.context.object.entity_id) break case 'classic_search_context': // todo rifictir form_data.append('context', this.context.object.name) form_data.append('context_entity', JSON.stringify({ 'order': this.context.object.order, 'invert': this.context.object.invert, 'genre': this.context.object.genre, 'only_performers': this.context.object.only_performers, 'with_lyrics': this.context.object.with_lyrics, 'query': this.context.object.query, })) break } form_data.append('page', page) form_data.append("hash", u("meta[name=csrf]").attr("value")) this.context.playedPages.push(page) const req = await fetch('/audios/context', { method: 'POST', body: form_data }) const res = await req.json() if(!res.success) { makeError(tr("unable_to_load_queue")) return } this.context.pagesCount = res.pagesCount this.context.count = res.count this.__appendTracks(res.items, after) } linkPlayer(node) { this.__linked_player_id = node.attr('id') u(this.audioPlayer).trigger('volumechange') } async setTrack(id) { if(!this.tracks || this.tracks.length < 1) { makeError('Context is not loaded yet', 'Red', 5000, 1489) return } this.listen_coef = 0.0 this.current_track_id = id const c_track = this.currentTrack if(!c_track) { makeError('Error playing audio: track not found') return } const protData = { "org.w3.clearkey": { "clearkeys": c_track.keys } }; u('.nowPlaying').removeClass('nowPlaying') if(this.isAtAudiosPage()) { u(`.audioEmbed[data-realid='${id}'] .audioEntry`).addClass('nowPlaying') } navigator.mediaSession.setPositionState({ duration: this.currentTrack.length }) this.__updateMediaSession() this.dashPlayer.initialize(this.audioPlayer, c_track.url, false); this.dashPlayer.setProtectionData(protData) if(!this.nextTrack && Math.max(...this.context["playedPages"]) < this.context["pagesCount"]) { await this.loadContext(Number(Math.max(...this.context["playedPages"])) + 1, true) } if(!this.previousTrack && (Math.min(...this.context["playedPages"]) > 1)) { await this.loadContext(Math.min(...this.context["playedPages"]) - 1, false) } this.__updateFace() u(this.audioPlayer).trigger('volumechange') } switchTracks(id1, id2) { const first_audio = this.__findTrack(id1) const first_audio_index = this.__findTrack(id1, true) const second_audio = this.__findTrack(id2) const second_audio_index = this.__findTrack(id2, true) this.tracks[first_audio_index] = second_audio this.tracks[second_audio_index] = first_audio this.__updateFace() } appendTrack(object, after = true) { this.__appendTracks([object], after) } hasTrackWithId(id) { return this.__findTrack(id, true) != -1 } play() { if(!this.currentTrack) { return } document.querySelectorAll('audio').forEach(el => el.pause()) this.audioPlayer.play() this.__setFavicon() this.__updateFace() navigator.mediaSession.playbackState = "playing" } pause() { if(!this.currentTrack) { return } this.audioPlayer.pause() this.__setFavicon('paused') this.__updateFace() navigator.mediaSession.playbackState = "paused" } playPreviousTrack() { if(!this.currentTrack || !this.previousTrack) { return } this.setTrack(this.previousTrack.id) if(!this.currentTrack.available || this.currentTrack.withdrawn) { if(!this.previousTrack) { return } this.playPreviousTrack() } this.play() } playNextTrack() { if(!this.currentTrack || !this.nextTrack) { return } this.setTrack(this.nextTrack.id) if(!this.currentTrack.available || this.currentTrack.withdrawn) { if(!this.nextTrack) { return } this.playNextTrack() } this.play() } // fake shuffle shuffle() { this.tracks.sort(() => Math.random() - 0.59) this.setTrack(this.tracks.at(0).id) } isAtAudiosPage() { return u('.bigPlayer').length > 0 } __setFavicon(state = 'playing') { if(state == 'playing') { document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_paused.png") } else { document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_playing.png") } } __findTrack(id, return_index = false) { if(return_index) { return this.tracks.indexOf(this.tracks.find(item => item.id == id)) } return this.tracks.find(item => item.id == id) } __findByIndex(index) { return this.tracks[index] } __setMediaSessionActions() { navigator.mediaSession.setActionHandler('play', () => { window.player.play() }); navigator.mediaSession.setActionHandler('pause', () => { window.player.pause() }); navigator.mediaSession.setActionHandler('previoustrack', () => { window.player.playPreviousTrack() }); navigator.mediaSession.setActionHandler('nexttrack', () => { window.player.playNextTrack() }); navigator.mediaSession.setActionHandler("seekto", (details) => { window.player.audioPlayer.currentTime = details.seekTime }); } __appendTracks(list, after = true) { if(after) { this.tracks = this.tracks.concat(list) } else { this.tracks = list.concat(this.tracks) } } __updateFace() { // Во второй раз перепутал next и back, но фиксить смысла уже нет. const _c = this.currentTrack const prev_button = this.uiPlayer.find('.nextButton') const next_button = this.uiPlayer.find('.backButton') if(!this.previousTrack) { prev_button.addClass('lagged') } else { prev_button.removeClass('lagged') prev_button.attr('data-title', ovk_proc_strtr(escapeHtml(this.previousTrack.name), 50)) } if(!this.nextTrack) { next_button.addClass('lagged') } else { next_button.removeClass('lagged') next_button.attr('data-title', ovk_proc_strtr(escapeHtml(this.nextTrack.name), 50)) } if(!this.audioPlayer.paused) { this.uiPlayer.find('.playButton').addClass('pause') if(this.linkedInlinePlayer) { this.linkedInlinePlayer.find('.playerButton .playIcon').addClass('paused') } } else { this.uiPlayer.find('.playButton').removeClass('pause') if(this.linkedInlinePlayer) { this.linkedInlinePlayer.find('.playerButton .playIcon').removeClass('paused') } } this.uiPlayer.find('.trackInfo .trackName span').html(escapeHtml(_c.name)) this.uiPlayer.find('.trackInfo .trackPerformers').html('') const performers = _c.performer.split(', ') const lastPerformer = performers[performers.length - 1] performers.forEach(performer => { this.uiPlayer.find('.trackInfo .trackPerformers').append( `${performer.escapeHtml()}${(performer != lastPerformer ? ', ' : '')}`) }) u(`.tip_result`).remove() } __updateTime(new_time) { this.uiPlayer.find(".trackInfo .time").html(fmtTime(new_time)) if(this.timeType == 1) { this.uiPlayer.find(".trackInfo .elapsedTime").html(fmtTime(this.currentTrack.length)) } else { this.uiPlayer.find(".trackInfo .elapsedTime").html(getRemainingTime(this.currentTrack.length, new_time)) } } __updateMediaSession() { const album = document.querySelector(".playlistBlock") const cur = this.currentTrack navigator.mediaSession.metadata = new MediaMetadata({ title: escapeHtml(cur.name), artist: escapeHtml(cur.performer), album: album == null ? "OpenVK Audios" : escapeHtml(album.querySelector(".playlistInfo h4").innerHTML), artwork: [{ src: album == null ? "/assets/packages/static/openvk/img/song.jpg" : album.querySelector(".playlistCover img").src }], }) } async __countListen() { let playlist = 0 if(!this.listen_coef) { return false } if(this.context.object && this.context.object.name == 'playlist_context') { playlist = this.context.object.entity_id } const form_data = new FormData form_data.append('hash', u("meta[name=csrf]").attr("value")) form_data.append('playlist', playlist) if(this.context.object) { form_data.append('listened_from', this.context.object.url) } const req = await fetch(`/audio${this.currentTrack.id}/listen`, { method: 'POST', body: form_data }) const res = await req.json() if(res.success) { console.log('Listen is counted') } else { console.log('Listen is not counted ! ! !') } } } document.addEventListener("DOMContentLoaded", async () => { await window.player.init(window.__current_page_audio_context) }) u(document).on('click', '.audioEntry .playerButton > .playIcon', (e) => { const audioPlayer = u(e.target).closest('.audioEmbed') const id = Number(audioPlayer.attr('data-realid')) if(!window.player) { return } if(!window.player.hasTrackWithId(id) && !window.player.isAtAudiosPage()) { let _nodes = null if(u(e.target).closest('.attachments').length > 0) { window.player.connectionType = '.attachments' _nodes = u(e.target).closest('.attachments').find('.audioEmbed').nodes } else if(u(e.target).closest('.content_list').length > 0) { window.player.connectionType = '.content_list' _nodes = u(e.target).closest('.content_list').find('.audioEmbed').nodes } _nodes.forEach(el => { const tempAudio = u(el) const name = tempAudio.attr('data-name').split(' — ') window.player.appendTrack({ 'id': Number(tempAudio.attr('data-realid')), 'available': true, // , судя по всему 'keys': JSON.parse(tempAudio.attr('data-keys')), 'length': Number(tempAudio.attr('data-length')), 'url': tempAudio.attr('data-url'), 'name': name[1], 'performer': name[0] }) }) } if(window.player.current_track_id != id) { window.player.setTrack(id) } if(window.player.audioPlayer.paused) { window.player.play() if(!window.player.isAtAudiosPage()) { u('.audioEntry .playerButton .playIcon.paused').removeClass('paused') u(e.target).addClass('paused') } } else { window.player.pause() } if(window.player.isAtAudiosPage()) { } else { window.player.linkPlayer(audioPlayer) u('.audioEntry .subTracks.shown').removeClass('shown') audioPlayer.find('.subTracks').addClass('shown') } }) u(document).on('click', '.bigPlayer .playButton', (e) => { if(window.player.audioPlayer.paused) { window.player.play() } else { window.player.pause() } }) u(document).on('click', '.bigPlayer .backButton', (e) => { window.player.playNextTrack() }) u(document).on('click', '.bigPlayer .nextButton', (e) => { window.player.playPreviousTrack() }) u(document).on("click", ".bigPlayer .elapsedTime", (e) => { if(window.player.current_track_id == 0) return const res = window.player.timeType == 0 ? 1 : 0 window.player.timeType = res window.player.__updateTime(window.player.audioPlayer.currentTime) }) u(document).on("click", ".bigPlayer .additionalButtons .repeatButton", (e) => { if(window.player.current_track_id == 0) return const targ = u(e.target) targ.toggleClass("pressed") if(targ.hasClass("pressed")) window.player.audioPlayer.loop = true else window.player.audioPlayer.loop = false }) u(document).on("click", ".bigPlayer .additionalButtons .shuffleButton", (e) => { if(window.player.current_track_id == 0) return window.player.shuffle() }) u(document).on("click", ".bigPlayer .additionalButtons .deviceButton", (e) => { if(window.player.current_track_id == 0) return e.target.classList.toggle("pressed") window.player.audioPlayer.muted = e.target.classList.contains("pressed") }) u(document).on('keydown', (e) => { if(document.activeElement.closest('.page_header')) { return } if(!window.player) { return } if([32, 37, 39, 107, 109].includes(e.keyCode)) { if(window.messagebox_stack.length > 0) return e.preventDefault() } const arrow_click_offset = 3 const volume_offset = 0.1 switch(e.keyCode) { case 32: window.player.listen_coef -= 0.1 window.player.uiPlayer.find('.playButton').trigger('click') break case 37: window.player.listen_coef -= 0.5 window.player.audioPlayer.currentTime = window.player.audioPlayer.currentTime - arrow_click_offset break case 39: window.player.listen_coef -= 0.5 window.player.audioPlayer.currentTime = window.player.audioPlayer.currentTime + arrow_click_offset break case 107: window.player.audioPlayer.volume = Math.max(Math.min(window.player.audioPlayer.volume + volume_offset, 1), 0) break case 109: window.player.audioPlayer.volume = Math.max(Math.min(window.player.audioPlayer.volume - volume_offset, 1), 0) break default: //console.log(e.keyCode) break } }) u(document).on('keyup', (e) => { if(document.activeElement.closest('.page_header')) { return } if(!window.player || !window.player.isAtAudiosPage()) { return } if([87, 65, 83, 68, 82, 77].includes(e.keyCode)) { if(window.messagebox_stack.length > 0) return e.preventDefault() } switch(e.keyCode) { case 87: case 65: window.player.playPreviousTrack() break case 83: case 68: window.player.playNextTrack() break case 82: document.querySelector(".bigPlayer .additionalButtons .repeatButton").click() break case 77: document.querySelector(".bigPlayer .additionalButtons .deviceButton").click() break } }) u(document).on("mousemove click mouseup", ".bigPlayer .trackPanel .selectableTrack, .audioEntry .subTracks .lengthTrackWrapper .selectableTrack", (e) => { if(window.player.isAtAudiosPage() && window.player.current_track_id == 0) return function __defaultAction(i_time) { window.player.listen_coef -= 0.5 window.player.audioPlayer.currentTime = i_time } const taggart = u(e.target).closest('.selectableTrack') const parent = taggart.parent() const rect = taggart.nodes[0].getBoundingClientRect() const width = e.clientX - rect.left const time = Math.ceil((width * window.player.currentTrack.length) / (rect.right - rect.left)) if(e.type == "mousemove") { let buttonsPresseed = _bsdnUnwrapBitMask(e.buttons) if(buttonsPresseed[0]) __defaultAction(time) } if(e.type == 'click' || e.type == 'mouseup') { __defaultAction(time) } if(parent.find('.tip_result').length < 1) { parent.append(`
`) } parent.find('.tip_result').html(fmtTime(time)).attr('style', `left:min(${width - 15}px, 315.5px)`) }) u(document).on("mouseout", ".bigPlayer .trackPanel .selectableTrack, .audioEntry .subTracks .lengthTrackWrapper .selectableTrack", (e) => { if(window.player.isAtAudiosPage() && window.player.current_track_id == 0) return u(e.target).closest('.selectableTrack').parent().find('.tip_result').remove() }) u(document).on("mousemove click mouseup", ".bigPlayer .volumePanelTrack .selectableTrack, .audioEntry .subTracks .volumeTrack .selectableTrack", (e) => { if(window.player.isAtAudiosPage() && window.player.current_track_id == 0) return function __defaultAction(i_volume) { window.player.audioPlayer.volume = i_volume } const rect = u(e.target).closest(".selectableTrack").nodes[0].getBoundingClientRect(); const taggart = u(e.target).closest('.selectableTrack') const parent = taggart.parent() const width = e.clientX - rect.left const volume = Math.max(0, (width * 1) / (rect.right - rect.left)) if(e.type == "mousemove") { let buttonsPresseed = _bsdnUnwrapBitMask(e.buttons) if(buttonsPresseed[0]) __defaultAction(volume) } if(e.type == 'click' || e.type == 'mouseup') { __defaultAction(volume) } if(parent.find('.tip_result').length < 1) { parent.append(``) } parent.find('.tip_result').html((volume * 100).toFixed(0) + '%').attr('style', `left:${width - 15}px`) }) u(document).on("mouseout", ".bigPlayer .volumePanelTrack .selectableTrack, .audioEntry .subTracks .volumeTrack .selectableTrack", (e) => { if(window.player.isAtAudiosPage() && window.player.current_track_id == 0) return u(e.target).closest('.selectableTrack').parent().find('.tip_result').remove() }) u(document).on('dragstart', '.audiosContainer .audioEmbed', (e) => { u(e.target).closest('.audioEmbed').addClass('currently_dragging') return }) u(document).on('dragover', '.audiosContainer .audioEmbed', (e) => { e.preventDefault() const target = u(e.target).closest('.audioEmbed') const current = u('.audioEmbed.currently_dragging') if(current.length < 1) { return } if(target.nodes[0].dataset.id != current.nodes[0].dataset.id) { target.addClass('dragged') } return }) u(document).on('dragend', '.audiosContainer .audioEmbed', (e) => { //console.log(e) u(e.target).closest('.audioEmbed').removeClass('dragged') return }) // TODO: write changes on server side (audio.reorder) u(document).on("drop", '.audiosContainer', function(e) { const current = u('.audioEmbed.currently_dragging') if(e.dataTransfer.types.includes('Files')) { e.preventDefault() e.dataTransfer.dropEffect = 'move' } else if(e.dataTransfer.types.length < 1 || e.dataTransfer.types.includes('text/uri-list')) { e.preventDefault() u('.audioEmbed.currently_dragging').removeClass('currently_dragging') const target = u(e.target).closest('.audioEmbed') const first_id = Number(current.attr('data-realid')) const second_id = Number(target.attr('data-realid')) const first_html = target.nodes[0].outerHTML const second_html = current.nodes[0].outerHTML current.nodes[0].outerHTML = first_html target.nodes[0].outerHTML = second_html window.player.switchTracks(first_id, second_id) } }) u(document).on("click", ".musicIcon.edit-icon", (e) => { const player = e.target.closest(".audioEmbed") const id = Number(player.dataset.realid) const performer = e.target.dataset.performer const name = e.target.dataset.title const genre = player.dataset.genre const lyrics = e.target.dataset.lyrics MessageBox(tr("edit_audio"), `