Repair playlists

This commit is contained in:
mrilyew 2024-12-01 18:12:50 +03:00
parent 5bc306ebf9
commit 187250b22f
12 changed files with 326 additions and 307 deletions

View file

@ -343,17 +343,15 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->club = $club; $this->template->club = $club;
} }
$this->template->owner = $owner;
if ($_SERVER["REQUEST_METHOD"] === "POST") { if ($_SERVER["REQUEST_METHOD"] === "POST") {
$title = $this->postParam("title"); $title = $this->postParam("title");
$description = $this->postParam("description"); $description = $this->postParam("description");
$is_unlisted = (int)$this->postParam('is_unlisted'); $is_unlisted = (int)$this->postParam('is_unlisted');
$is_ajax = (int)$this->postParam('ajax') == 1;
$audios = !empty($this->postParam("audios")) ? array_slice(explode(",", $this->postParam("audios")), 0, 1000) : []; $audios = array_slice(explode(",", $this->postParam("audios")), 0, 1000);
if(empty($title) || iconv_strlen($title) < 1) if(empty($title) || iconv_strlen($title) < 1)
$this->flashFail("err", tr("error"), tr("set_playlist_name")); $this->flashFail("err", tr("error"), tr("set_playlist_name"), NULL, $is_ajax);
$playlist = new Playlist; $playlist = new Playlist;
$playlist->setOwner($owner); $playlist->setOwner($owner);
@ -364,12 +362,12 @@ final class AudioPresenter extends OpenVKPresenter
if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) { if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["cover"]["type"], "image")) if(!str_starts_with($_FILES["cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo")); $this->flashFail("err", tr("error"), tr("not_a_photo"), NULL, $is_ajax);
try { try {
$playlist->fastMakeCover($this->user->id, $_FILES["cover"]); $playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
} catch(\Throwable $e) { } catch(\Throwable $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo")); $this->flashFail("err", tr("error"), tr("invalid_cover_photo"), NULL, $is_ajax);
} }
} }
@ -377,25 +375,22 @@ final class AudioPresenter extends OpenVKPresenter
foreach($audios as $audio) { foreach($audios as $audio) {
$audio = $this->audios->get((int)$audio); $audio = $this->audios->get((int)$audio);
if(!$audio || $audio->isDeleted())
if(!$audio || $audio->isDeleted() || !$audio->canBeViewedBy($this->user->identity))
continue; continue;
$playlist->add($audio); $playlist->add($audio);
} }
$playlist->bookmark(isset($club) ? $club : $this->user->identity); $playlist->bookmark(isset($club) ? $club : $this->user->identity);
if($is_ajax) {
$this->returnJson([
'success' => true,
'redirect' => '/playlist' . $owner . "_" . $playlist->getId()
]);
}
$this->redirect("/playlist" . $owner . "_" . $playlist->getId()); $this->redirect("/playlist" . $owner . "_" . $playlist->getId());
} else { } else {
if(isset($club)) { $this->template->owner = $owner;
$this->template->audios = iterator_to_array($this->audios->getByClub($club, 1, 10));
$count = (new Audios)->getClubCollectionSize($club);
} else {
$this->template->audios = iterator_to_array($this->audios->getByUser($this->user->identity, 1, 10));
$count = (new Audios)->getUserCollectionSize($this->user->identity);
}
$this->template->pagesCount = ceil($count / 10);
} }
} }
@ -451,28 +446,20 @@ final class AudioPresenter extends OpenVKPresenter
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id); $playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id);
$page = (int)($this->queryParam("p") ?? 1);
if (!$playlist || $playlist->isDeleted() || !$playlist->canBeModifiedBy($this->user->identity)) if (!$playlist || $playlist->isDeleted() || !$playlist->canBeModifiedBy($this->user->identity))
$this->notFound(); $this->notFound();
$this->template->playlist = $playlist; $this->template->playlist = $playlist;
$this->template->page = $page;
$audios = iterator_to_array($playlist->fetch(1, $playlist->size())); $audios = iterator_to_array($playlist->fetch(1, $playlist->size()));
$this->template->audios = array_slice($audios, 0, 10); $this->template->audios = array_slice($audios, 0, 1000);
$audiosIds = [];
foreach($audios as $aud)
$audiosIds[] = $aud->getId();
$this->template->audiosIds = implode(",", array_unique($audiosIds)) . ",";
$this->template->ownerId = $owner_id; $this->template->ownerId = $owner_id;
$this->template->owner = $playlist->getOwner(); $this->template->owner = $playlist->getOwner();
$this->template->pagesCount = $pagesCount = ceil($playlist->size() / 10);
if($_SERVER["REQUEST_METHOD"] !== "POST") if($_SERVER["REQUEST_METHOD"] !== "POST")
return; return;
$is_ajax = (int)$this->postParam('ajax') == 1;
$title = $this->postParam("title"); $title = $this->postParam("title");
$description = $this->postParam("description"); $description = $this->postParam("description");
$is_unlisted = (int)$this->postParam('is_unlisted'); $is_unlisted = (int)$this->postParam('is_unlisted');
@ -487,12 +474,12 @@ final class AudioPresenter extends OpenVKPresenter
$playlist->resetLength(); $playlist->resetLength();
$playlist->setUnlisted((bool)$is_unlisted); $playlist->setUnlisted((bool)$is_unlisted);
if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) { if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["new_cover"]["type"], "image")) if(!str_starts_with($_FILES["cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo")); $this->flashFail("err", tr("error"), tr("not_a_photo"));
try { try {
$playlist->fastMakeCover($this->user->id, $_FILES["new_cover"]); $playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
} catch(\Throwable $e) { } catch(\Throwable $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo")); $this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
} }
@ -506,13 +493,18 @@ final class AudioPresenter extends OpenVKPresenter
foreach ($new_audios as $new_audio) { foreach ($new_audios as $new_audio) {
$audio = (new Audios)->get((int)$new_audio); $audio = (new Audios)->get((int)$new_audio);
if(!$audio || $audio->isDeleted()) if(!$audio || $audio->isDeleted())
continue; continue;
$playlist->add($audio); $playlist->add($audio);
} }
if($is_ajax) {
$this->returnJson([
'success' => true,
'redirect' => '/playlist' . $playlist->getPrettyId()
]);
}
$this->redirect("/playlist".$playlist->getPrettyId()); $this->redirect("/playlist".$playlist->getPrettyId());
} }

View file

@ -13,61 +13,44 @@
{/block} {/block}
{block content} {block content}
<div class="playlistBlock" style="display: flex;margin-top: 0px;"> <div class='PE_wrapper'>
<div class="playlistCover"> <div class='PE_playlistEditPage'>
<a> <div class="playlistCover">
<img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}"> <a onclick="document.querySelector(`input[name='cover']`).click()">
</a> <input type='file' name='cover' style='display:none;' accept=".jpg,.png">
<img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}">
<div class="profile_links" style="width: 139px;"> </a>
<a class="profile_link" style="width: 98%;" id="_deletePlaylist" data-id="{$playlist->getId()}">{_delete_playlist}</a> <div class="profile_links" style="width: 139px;">
</div> <a class="profile_link" style="width: 98%;" id="_deletePlaylist" data-id="{$playlist->getId()}">{_delete_playlist}</a>
</div>
<div style="padding-left: 13px;width:75%">
<div class="playlistInfo">
<input value="{$playlist->getName()}" type="text" name="title" maxlength="125">
</div>
<div class="moreInfo">
<textarea placeholder="{_description}" name="description" maxlength="2045" style="margin-top: 11px;">{$playlist->getDescription()}</textarea>
</div>
<label>
<input type='checkbox' name='is_unlisted' n:attr='checked => $playlist->isUnlisted()'>
{_playlist_hide_from_search}
</label>
</div>
</div>
<div style="margin-top: 19px;">
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}">
<div class="playlistAudiosContainer editContainer">
<div id="newPlaylistAudios" n:foreach="$audios as $audio">
<div class="playerContainer">
{include "player.xml", audio => $audio, hideButtons => true}
</div> </div>
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}"> </div>
<span>{_remove_from_playlist}</span>
<div class="PE_playlistInfo">
<div>
<input value='{$playlist->getName()}' type="text" name="title" placeholder="{_title}" maxlength="125" />
</div>
<div class="moreInfo">
<textarea placeholder="{_description}" name="description" maxlength="2045">{$playlist->getDescription()}</textarea>
</div>
<label>
<input type='checkbox' name='is_unlisted' value='1' n:attr='checked => $playlist->isUnlisted()'>
{_playlist_hide_from_search}
</label>
<a id='_playlistAppendTracks'>{_add_audio_verb}</a>
</div>
</div>
<div class='PE_audios generic_audio_list'>
<div n:foreach='$audios as $audio' class='vertical-attachment upload-item' data-id='{$audio->getId()}'>
<div class='vertical-attachment-content'>
{include 'player.xml', audio => $audio, hideButtons => true}
</div>
<div class="vertical-attachment-remove">
<div id="small_remove_button"></div>
</div> </div>
</div> </div>
</div> </div>
<div class='PE_end'>
<div class="showMoreAudiosPlaylist" data-page="2" data-playlist="{$playlist->getId()}" n:if="$pagesCount > 1"> <input class="button" type="button" id='playlist_edit' value="{_save}">
{_show_more_audios}
</div> </div>
</div> </div>
<form method="post" id="editPlaylistForm" data-id="{$playlist->getId()}" enctype="multipart/form-data">
<input type="hidden" name="title" maxlength="128" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="hidden" name="is_unlisted" value="0" />
<textarea style="display:none;" name="description" maxlength="2048" />
<input type="hidden" name="audios">
<input type="file" style="display:none;" name="new_cover" accept=".jpg,.png">
<div style="float:right;margin-top: 8px;">
<button class="button" type="submit">{_save}</button>
</div>
</form>
{/block} {/block}

View file

@ -19,48 +19,32 @@
{/block} {/block}
{block content} {block content}
<style> <div class='PE_wrapper'>
textarea[name='description'] { <div class='PE_playlistEditPage'>
padding: 4px; <div class="playlistCover" onclick="document.querySelector(`input[name='cover']`).click()">
} <a>
<input type='file' name='cover' style='display:none;' accept=".jpg,.png">
<img src="/assets/packages/static/openvk/img/song.jpg" alt="{_playlist_cover}">
</a>
</div>
.playlistInfo { <div class="PE_playlistInfo">
width: 76%; <div>
margin-left: 8px; <input type="text" name="title" placeholder="{_title}" maxlength="125" />
} </div>
</style> <div class="moreInfo">
<textarea placeholder="{_description}" name="description" maxlength="2045" />
<div style="display:flex;"> </div>
<div class="playlistCover" onclick="document.querySelector(`#newPlaylistForm input[name='cover']`).click()"> <label>
<a> <input type='checkbox' name='is_unlisted' value='1'>
<img src="/assets/packages/static/openvk/img/song.jpg" alt="{_playlist_cover}"> {_playlist_hide_from_search}
</a> </label>
<a id='_playlistAppendTracks'>{_add_audio_verb}</a>
</div>
</div> </div>
<div class='PE_audios generic_audio_list'></div>
<div style="padding-left: 17px;width: 75%;" class="plinfo"> <div class='PE_end'>
<div> <input class="button" type="button" id='playlist_create' value="{_create}">
<input type="text" name="title" placeholder="{_title}" maxlength="125" />
</div>
<div class="moreInfo" style="margin-top: 11px;">
<textarea placeholder="{_description}" name="description" maxlength="2045" />
</div>
<label>
<input type='checkbox' name='is_unlisted'>
{_playlist_hide_from_search}
</label>
</div> </div>
</div> </div>
<form method="post" id="newPlaylistForm" enctype="multipart/form-data">
<input type="hidden" name="title" maxlength="125" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="hidden" name="is_unlisted" value="0" />
<textarea style="display:none;" name="description" maxlength="2045" />
<input type="hidden" name="audios">
<input type="file" style="display:none;" name="cover" accept=".jpg,.png">
<div style="float: right;margin-top: 9px;">
<button class="button" type="submit">{_create}</button>
</div>
</form>
{/block} {/block}

View file

@ -605,6 +605,28 @@
width: 100%; width: 100%;
} }
.PE_playlistEditPage {
display: flex;
gap: 10px;
}
.PE_playlistEditPage .PE_playlistInfo {
width: 76%;
display: flex;
flex-direction: column;
gap: 10px;
}
.PE_playlistEditPage textarea[name='description'] {
padding: 4px;
font-size: 11px;
}
.PE_end {
text-align: right;
margin-top: 10px;
}
/* playlist listview */ /* playlist listview */
.playlistListView { .playlistListView {

View file

@ -2573,55 +2573,55 @@ a.poll-retract-vote {
border-radius: 3px; border-radius: 3px;
} }
.post-vertical .vertical-attachment { .vertical-attachment {
display: grid; display: grid;
grid-template-columns: 1fr 0fr; grid-template-columns: 1fr 0fr;
} }
.post-vertical .vertical-attachment:first-of-type { .vertical-attachment:first-of-type {
margin-top: 7px; margin-top: 7px;
} }
.post-vertical .vertical-attachment .audioEntry { .vertical-attachment .audioEntry {
max-height: 28px; max-height: 28px;
min-height: 18px; min-height: 18px;
} }
.post-vertical .vertical-attachment .audioEntry:hover { .vertical-attachment .audioEntry:hover {
background: unset !important; background: unset !important;
} }
.post-vertical .vertical-attachment .audioEntry .buttons { .vertical-attachment .audioEntry .buttons {
display: none; display: none;
} }
.post-vertical .vertical-attachment .audioEntry .status { .vertical-attachment .audioEntry .status {
margin-top: 1px; margin-top: 1px;
margin-left: 2px; margin-left: 2px;
height: 15px; height: 15px;
} }
.post-vertical .vertical-attachment .audioEntry .nobold { .vertical-attachment .audioEntry .nobold {
margin-top: 1px; margin-top: 1px;
} }
.post-vertical .vertical-attachment .audioEntry .subTracks { .vertical-attachment .audioEntry .subTracks {
padding-bottom: 2px; padding-left: 0px;
padding-left: 3px;
padding-right: 0px; padding-right: 0px;
margin-top: 2px;
} }
.post-vertical .vertical-attachment .audioEntry .audioEntryWrapper { .vertical-attachment .audioEntry .audioEntryWrapper {
height: 14px; height: 14px;
padding: 0px 4px 0px 0px; padding: 0px 4px 0px 0px;
gap: 2px; gap: 2px;
} }
.post-vertical .vertical-attachment .vertical-attachment-content { .vertical-attachment .vertical-attachment-content {
max-height: 27px; max-height: 27px;
} }
.post-vertical .vertical-attachment .vertical-attachment-content .overflowedName { .vertical-attachment .vertical-attachment-content .overflowedName {
position: initial; position: initial;
width: 100% !important; width: 100% !important;
} }

View file

@ -336,14 +336,14 @@ window.player = new class {
return this.__findTrack(id, true) != -1 return this.__findTrack(id, true) != -1
} }
play() { async play() {
if(!this.currentTrack) { if(!this.currentTrack) {
return return
} }
document.querySelectorAll('audio').forEach(el => el.pause()) document.querySelectorAll('audio').forEach(el => el.pause())
this.audioPlayer.play() await this.audioPlayer.play()
this.__setFavicon() this.__setFavicon()
this.__updateFace() this.__updateFace()
navigator.mediaSession.playbackState = "playing" navigator.mediaSession.playbackState = "playing"
@ -374,7 +374,7 @@ window.player = new class {
this.playPreviousTrack() this.playPreviousTrack()
} }
this.play() await this.play()
} }
async playNextTrack() { async playNextTrack() {
@ -391,14 +391,14 @@ window.player = new class {
this.playNextTrack() this.playNextTrack()
} }
this.play() await this.play()
} }
// fake shuffle // fake shuffle
async shuffle() { async shuffle() {
this.tracks.sort(() => Math.random() - 0.59) this.tracks.sort(() => Math.random() - 0.59)
await this.setTrack(this.tracks.at(0).id) await this.setTrack(this.tracks.at(0).id)
this.play() await this.play()
} }
isAtAudiosPage() { isAtAudiosPage() {
@ -489,15 +489,15 @@ window.player = new class {
} }
__setMediaSessionActions() { __setMediaSessionActions() {
navigator.mediaSession.setActionHandler('play', () => { navigator.mediaSession.setActionHandler('play', async () => {
window.player.play() await window.player.play()
}); });
navigator.mediaSession.setActionHandler('pause', () => { navigator.mediaSession.setActionHandler('pause', () => {
window.player.pause() window.player.pause()
}); });
navigator.mediaSession.setActionHandler('previoustrack', () => { window.player.playPreviousTrack() }); navigator.mediaSession.setActionHandler('previoustrack', async () => { await window.player.playPreviousTrack() });
navigator.mediaSession.setActionHandler('nexttrack', () => { window.player.playNextTrack() }); navigator.mediaSession.setActionHandler('nexttrack', async () => { await window.player.playNextTrack() });
navigator.mediaSession.setActionHandler("seekto", (details) => { navigator.mediaSession.setActionHandler("seekto", async (details) => {
window.player.audioPlayer.currentTime = details.seekTime window.player.audioPlayer.currentTime = details.seekTime
}); });
} }
@ -560,14 +560,19 @@ window.player = new class {
} }
} }
this.uiPlayer.find('.trackInfo .trackName span').html(escapeHtml(_c.name)) if(_c) {
this.uiPlayer.find('.trackInfo .trackPerformers').html('') this.uiPlayer.find('.trackInfo .trackName span').html(escapeHtml(_c.name))
const performers = _c.performer.split(', ') this.uiPlayer.find('.trackInfo .trackPerformers').html('')
const lastPerformer = performers[performers.length - 1] const performers = _c.performer.split(', ')
performers.forEach(performer => { const lastPerformer = performers[performers.length - 1]
this.uiPlayer.find('.trackInfo .trackPerformers').append( performers.forEach(performer => {
`<a href='/search?section=audios&order=listens&only_performers=on&q=${encodeURIComponent(performer.escapeHtml())}'>${performer.escapeHtml()}${(performer != lastPerformer ? ', ' : '')}</a>`) this.uiPlayer.find('.trackInfo .trackPerformers').append(
}) `<a href='/search?section=audios&order=listens&only_performers=on&q=${encodeURIComponent(performer.escapeHtml())}'>${performer.escapeHtml()}${(performer != lastPerformer ? ', ' : '')}</a>`)
})
} else {
this.uiPlayer.find('.trackInfo .trackName span').html(tr('track_noname'))
this.uiPlayer.find('.trackInfo .trackPerformers').html(`<a>${tr('track_unknown')}</a>`)
}
if(this.ajaxPlayer.length > 0) { if(this.ajaxPlayer.length > 0) {
this.ajaxPlayer.find('#aj_player_track_title b').html(escapeHtml(_c.performer)) this.ajaxPlayer.find('#aj_player_track_title b').html(escapeHtml(_c.performer))
@ -637,6 +642,9 @@ window.player = new class {
ajReveal() { ajReveal() {
this.is_closed = false this.is_closed = false
if(u('#ajax_audio_player').length == 0) {
this.ajCreate()
}
u('#ajax_audio_player').removeClass('hidden') u('#ajax_audio_player').removeClass('hidden')
} }
@ -731,8 +739,15 @@ u(document).on('click', '.audioEntry .playerButton > .playIcon', async (e) => {
} else if(u(e.target).closest('.content_list').length > 0) { } else if(u(e.target).closest('.content_list').length > 0) {
window.player.connectionType = '.content_list' window.player.connectionType = '.content_list'
_nodes = u(e.target).closest('.content_list').find('.audioEmbed').nodes _nodes = u(e.target).closest('.content_list').find('.audioEmbed').nodes
} else if(u(e.target).closest('.generic_audio_list').length > 0) {
window.player.connectionType = '.generic_audio_list'
_nodes = u(e.target).closest('.generic_audio_list').find('.audioEmbed').nodes
} else if(u(e.target).closest('.audiosInsert').length > 0) {
window.player.connectionType = '.audiosInsert'
_nodes = u(e.target).closest('.audiosInsert').find('.audioEmbed').nodes
} }
window.player.tracks = []
_nodes.forEach(el => { _nodes.forEach(el => {
const tempAudio = u(el) const tempAudio = u(el)
const name = tempAudio.attr('data-name').split(' — ') const name = tempAudio.attr('data-name').split(' — ')
@ -753,7 +768,7 @@ u(document).on('click', '.audioEntry .playerButton > .playIcon', async (e) => {
} }
if(window.player.audioPlayer.paused) { if(window.player.audioPlayer.paused) {
window.player.play() await window.player.play()
if(!window.player.isAtAudiosPage()) { if(!window.player.isAtAudiosPage()) {
u('.audioEntry .playerButton .playIcon.paused').removeClass('paused') u('.audioEntry .playerButton .playIcon.paused').removeClass('paused')
@ -772,9 +787,9 @@ u(document).on('click', '.audioEntry .playerButton > .playIcon', async (e) => {
} }
}) })
u(document).on('click', '.bigPlayer .playButton, #ajax_audio_player #aj_player_play_btn', (e) => { u(document).on('click', '.bigPlayer .playButton, #ajax_audio_player #aj_player_play_btn', async (e) => {
if(window.player.audioPlayer.paused) { if(window.player.audioPlayer.paused) {
window.player.play() await window.player.play()
} else { } else {
window.player.pause() window.player.pause()
} }
@ -1066,6 +1081,8 @@ u(document).on('contextmenu', '.bigPlayer, .audioEmbed, #ajax_audio_player', (e)
` : ''} ` : ''}
<a id='audio_ctx_add_to_group'>${tr('audio_ctx_add_to_group')}</a> <a id='audio_ctx_add_to_group'>${tr('audio_ctx_add_to_group')}</a>
<a id='audio_ctx_add_to_playlist'>${tr('audio_ctx_add_to_playlist')}</a> <a id='audio_ctx_add_to_playlist'>${tr('audio_ctx_add_to_playlist')}</a>
${ctx_type == 'main_player' ? `
<a id='audio_ctx_clear_context'>${tr('audio_ctx_clear_context')}</a>` : ''}
${ctx_type == 'main_player' ? `<a href='https://github.com/mrilyew' target='_blank'>BigPlayer v1.1 by MrIlyew</a>` : ''} ${ctx_type == 'main_player' ? `<a href='https://github.com/mrilyew' target='_blank'>BigPlayer v1.1 by MrIlyew</a>` : ''}
</div> </div>
`) `)
@ -1156,6 +1173,13 @@ u(document).on('contextmenu', '.bigPlayer, .audioEmbed, #ajax_audio_player', (e)
moving_track_player.remove() moving_track_player.remove()
} }
}) })
ctx_u.find('#audio_ctx_clear_context').on('click', (ev) => {
const old_url = window.player.context.object.url
window.player.pause()
window.player.__resetContext()
window.player.__updateFace()
window.router.route(old_url)
})
}) })
u(document).on("click", ".musicIcon.edit-icon", (e) => { u(document).on("click", ".musicIcon.edit-icon", (e) => {
@ -1641,9 +1665,11 @@ $(document).on("click", "#_deletePlaylist", (e) => {
}, Function.noop]) }, Function.noop])
}) })
$(document).on("click", "#__audioAttachment", (e) => { function showAudioAttachment(type = 'form', form = null)
const form = e.target.closest("form") {
let body = ` const msg = new CMessageBox({
title: tr("select_audio"),
body: `
<div class="searchBox"> <div class="searchBox">
<input name="query" type="text" maxlength="50" placeholder="${tr("header_search")}"> <input name="query" type="text" maxlength="50" placeholder="${tr("header_search")}">
<select name="perf"> <select name="perf">
@ -1653,13 +1679,12 @@ $(document).on("click", "#__audioAttachment", (e) => {
</div> </div>
<div class="audiosInsert"></div> <div class="audiosInsert"></div>
` `,
MessageBox(tr("select_audio"), body, [tr("close")], [Function.noop]) buttons: [tr('close')],
callbacks: [Function.noop],
document.querySelector(".ovk-diag-body").style.padding = "0" })
document.querySelector(".ovk-diag-cont").style.width = "580px" msg.getNode().find('.ovk-diag-body').attr('style', 'padding:0px;height:335px')
document.querySelector(".ovk-diag-body").style.height = "335px" msg.getNode().attr('style', 'width:580px')
let searcher = new playersSearcher("entity_audios", 0) let searcher = new playersSearcher("entity_audios", 0)
searcher.successCallback = (response, thisc) => { searcher.successCallback = (response, thisc) => {
let domparser = new DOMParser() let domparser = new DOMParser()
@ -1674,8 +1699,18 @@ $(document).on("click", "#__audioAttachment", (e) => {
} }
result.querySelectorAll(".audioEmbed").forEach(el => { result.querySelectorAll(".audioEmbed").forEach(el => {
let id = el.dataset.prettyid let id = 0
const is_attached = (u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`)).length > 0 if(type == 'form') {
id = el.dataset.prettyid
} else {
id = el.dataset.realid
}
let is_attached = false
if(type == 'form') {
is_attached = (u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`)).length > 0
} else {
is_attached = (u(form).find(`.PE_audios .vertical-attachment[data-id='${id}']`)).length > 0
}
document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", ` document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", `
<div class='audio_attachment_header' style="display: flex;width: 100%;"> <div class='audio_attachment_header' style="display: flex;width: 100%;">
@ -1717,7 +1752,7 @@ $(document).on("click", "#__audioAttachment", (e) => {
searcher.movePage(Number(e.currentTarget.dataset.page)) searcher.movePage(Number(e.currentTarget.dataset.page))
}) })
$(".searchBox input").on("change", async (e) => { u(".searchBox input").on("change", async (e) => {
if(e.currentTarget.value === document.querySelector(".searchBox input").value) { if(e.currentTarget.value === document.querySelector(".searchBox input").value) {
searcher.clearContainer() searcher.clearContainer()
@ -1740,7 +1775,7 @@ $(document).on("click", "#__audioAttachment", (e) => {
} }
}) })
$(".searchBox select").on("change", async (e) => { u(".searchBox select").on("change", async (e) => {
searcher.clearContainer() searcher.clearContainer()
searcher.searchType = e.currentTarget.value searcher.searchType = e.currentTarget.value
@ -1750,14 +1785,24 @@ $(document).on("click", "#__audioAttachment", (e) => {
u(".audiosInsert").on("click", ".attachAudio", (ev) => { u(".audiosInsert").on("click", ".attachAudio", (ev) => {
const id = ev.currentTarget.dataset.attachmentdata const id = ev.currentTarget.dataset.attachmentdata
const is_attached = u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`).length > 0 let is_attached = false
if(type == 'form') {
is_attached = u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`).length > 0
} else {
is_attached = u(form).find(`.PE_audios .vertical-attachment[data-id='${id}']`).length > 0
}
// 04.11.2024 19:03 // 04.11.2024 19:03
// 30.11.2024 19:03
if(is_attached) { if(is_attached) {
u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`).remove() if(type == 'form') {
u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`).remove()
} else {
u(form).find(`.PE_audios .vertical-attachment[data-id='${id}']`).remove()
}
u(ev.currentTarget).find("span").html(tr("attach_audio")) u(ev.currentTarget).find("span").html(tr("attach_audio"))
} else { } else {
if(u(form).find(`.upload-item`).length > window.openvk.max_attachments) { if(type == 'form' && u(form).find(`.upload-item`).length > window.openvk.max_attachments) {
makeError(tr('too_many_attachments'), 'Red', 10000, 1) makeError(tr('too_many_attachments'), 'Red', 10000, 1)
return return
} }
@ -1766,7 +1811,7 @@ $(document).on("click", "#__audioAttachment", (e) => {
const header = u(ev.currentTarget).closest('.audio_attachment_header') const header = u(ev.currentTarget).closest('.audio_attachment_header')
const player = header.find('.player_part') const player = header.find('.player_part')
u(form).find(".post-vertical").append(` u(form).find(type == 'form' ? ".post-vertical" : '.PE_audios').append(`
<div class="vertical-attachment upload-item" data-type='audio' data-id="${ev.currentTarget.dataset.attachmentdata}"> <div class="vertical-attachment upload-item" data-type='audio' data-id="${ev.currentTarget.dataset.attachmentdata}">
<div class='vertical-attachment-content'> <div class='vertical-attachment-content'>
${player.html()} ${player.html()}
@ -1778,6 +1823,11 @@ $(document).on("click", "#__audioAttachment", (e) => {
`) `)
} }
}) })
}
$(document).on("click", "#__audioAttachment", (e) => {
const form = e.target.closest("form")
showAudioAttachment('form', form)
}) })
$(document).on("click", ".audioEmbed.processed .playerButton", (e) => { $(document).on("click", ".audioEmbed.processed .playerButton", (e) => {
@ -1876,6 +1926,7 @@ u(document).on('click', '.upload_container_element #small_remove_button', (e) =>
u(document).on('click', `#upload_container #uploadMusic`, async (e) => { u(document).on('click', `#upload_container #uploadMusic`, async (e) => {
const current_upload_page = location.href const current_upload_page = location.href
let error = null
let end_redir = '' let end_redir = ''
u('#lastStepButtons').addClass('lagged') u('#lastStepButtons').addClass('lagged')
for(const elem of u('#lastStepContainers .upload_container_element').nodes) { for(const elem of u('#lastStepContainers .upload_container_element').nodes) {
@ -1901,13 +1952,21 @@ u(document).on('click', `#upload_container #uploadMusic`, async (e) => {
body: fd, body: fd,
}) })
const result_text = await result.json() const result_text = await result.json()
if(result_text.success && result_text.redirect_link) { if(result_text.success) {
end_redir = result_text.redirect_link end_redir = result_text.redirect_link
} else {
makeError(escapeHtml(result_text.flash.message))
} }
await sleep(6000) await sleep(6000)
elem_u.remove() elem_u.remove()
} }
if(!end_redir) {
u('#lastStepButtons').removeClass('lagged')
window.__audio_upload_page.showFirstPage()
return
}
if(current_upload_page == location.href) { if(current_upload_page == location.href) {
window.router.route(end_redir) window.router.route(end_redir)
} }
@ -1920,3 +1979,77 @@ u(document).on("drop", "#upload_container", function (e) {
document.getElementById("audio_input").files = e.dataTransfer.files document.getElementById("audio_input").files = e.dataTransfer.files
u("#audio_input").trigger("change") u("#audio_input").trigger("change")
}) })
u(document).on('click', '#_playlistAppendTracks', (e) => {
// 1984
showAudioAttachment('playlist', u('.PE_wrapper').nodes[0])
})
u(document).on('drop', '.PE_audios .vertical-attachment', (e) => {
const current = u('.upload-item.currently_dragging')
if(e.dataTransfer.types.length < 1 || e.dataTransfer.types.includes('text/uri-list')) {
e.preventDefault()
const target = u(e.target).closest('.upload-item')
u('.dragged').removeClass('dragged')
current.removeClass('currently_dragging')
if(!current.closest('.vertical-attachment').length < 1 && target.closest('.vertical-attachment').length < 1
|| current.closest('.vertical-attachment').length < 1 && !target.closest('.vertical-attachment').length < 1) {
return
}
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
}
})
u(document).on("change", `input[name='cover']`, (e) => {
const file = e.target.files[0]
if(!file.type.startsWith("image/")) {
makeError(tr("not_a_photo"))
return
}
const image = URL.createObjectURL(file)
u(".playlistCover img").attr('src', image).attr('style', 'display:block')
})
u(document).on("drop", `.playlistCover`, (e) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move';
document.querySelector(`input[name='cover']`).files = e.dataTransfer.files
u(`input[name='cover']`).trigger("change")
})
u(document).on('click', '.PE_end #playlist_create, .PE_end #playlist_edit', async (e) => {
const ids = []
u('.PE_audios .vertical-attachment').nodes.forEach(vatch => {
ids.push(vatch.dataset.id)
})
if(!ids || ids.length < 1) {
makeError(tr('error_playlist_creating_too_small'), 'Red', 5000, 77)
return
}
u(e.target).addClass('lagged')
const fd = serializeForm(u('.PE_playlistEditPage').nodes[0])
fd.append('hash', window.router.csrf)
fd.append('ajax', 1)
fd.append('audios', ids)
const req = await fetch(location.href, {
method: 'POST',
body: fd,
})
const req_json = await req.json()
if(req_json.success) {
window.router.route(req_json.redirect)
} else {
makeError(req_json.flash.message)
}
u(e.target).removeClass('lagged')
})

View file

@ -1,113 +0,0 @@
let context_type = "entity_audios"
let context_id = 0
if(document.querySelector("#editPlaylistForm")) {
context_type = "playlist_context"
context_id = document.querySelector("#editPlaylistForm").dataset.id
}
if(document.querySelector(".showMoreAudiosPlaylist") && document.querySelector(".showMoreAudiosPlaylist").dataset.club != null) {
context_type = "entity_audios"
context_id = Number(document.querySelector(".showMoreAudiosPlaylist").dataset.club) * -1
}
let searcher = new playersSearcher(context_type, context_id)
searcher.successCallback = (response, thisc) => {
let domparser = new DOMParser()
let result = domparser.parseFromString(response, "text/html")
let pagesCount = Number(result.querySelector("input[name='pagesCount']").value)
let count = Number(result.querySelector("input[name='count']").value)
result.querySelectorAll(".audioEmbed").forEach(el => {
let id = Number(el.dataset.realid)
let isAttached = (document.querySelector("input[name='audios']").value.includes(`${id},`))
document.querySelector(".playlistAudiosContainer").insertAdjacentHTML("beforeend", `
<div id="newPlaylistAudios">
<div class="playerContainer">
${el.outerHTML}
</div>
<div class="attachAudio addToPlaylist" data-id="${id}">
<span>${isAttached ? tr("remove_from_playlist") : tr("add_to_playlist")}</span>
</div>
</div>
`)
})
if(count < 1)
document.querySelector(".playlistAudiosContainer").insertAdjacentHTML("beforeend", `
${tr("no_results")}
`)
if(Number(thisc.page) >= pagesCount)
u(".showMoreAudiosPlaylist").remove()
else {
if(document.querySelector(".showMoreAudiosPlaylist") != null) {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-page", thisc.page + 1)
if(thisc.query != "") {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-query", thisc.query)
}
document.querySelector(".showMoreAudiosPlaylist").style.display = "block"
} else {
document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `
<div class="showMoreAudiosPlaylist" data-page="2"
${thisc.query != "" ? `"data-query="${thisc.query}"` : ""}
${thisc.context_type == "entity_audios" ? `"data-playlist="${thisc.context_id}"` : ""}
${thisc.context_id < 0 ? `"data-club="${thisc.context_id}"` : ""}>
${tr("show_more_audios")}
</div>
`)
}
}
u("#loader").remove()
}
searcher.beforesendCallback = () => {
document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
if(document.querySelector(".showMoreAudiosPlaylist") != null)
document.querySelector(".showMoreAudiosPlaylist").style.display = "none"
}
searcher.errorCallback = () => {
fastError("Error when loading players")
}
searcher.clearContainer = () => {
document.querySelector(".playlistAudiosContainer").innerHTML = ""
}
$(document).on("click", ".showMoreAudiosPlaylist", (e) => {
searcher.movePage(Number(e.currentTarget.dataset.page))
})
$(document).on("change", "input#playlist_query", async (e) => {
e.preventDefault()
await new Promise(r => setTimeout(r, 500));
if(e.currentTarget.value === document.querySelector("input#playlist_query").value) {
searcher.clearContainer()
if(e.currentTarget.value == "") {
searcher.context_type = "entity_audios"
searcher.context_id = 0
searcher.query = ""
searcher.movePage(1)
return
}
searcher.context_type = "search_context"
searcher.context_id = 0
searcher.query = e.currentTarget.value
searcher.movePage(1)
return;
}
})

View file

@ -1101,14 +1101,14 @@ u(document).on('paste', '#write .small-textarea', (e) => {
} }
}) })
u(document).on('dragstart', '#write .post-horizontal .upload-item, .post-vertical .upload-item', (e) => { u(document).on('dragstart', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
//e.preventDefault() //e.preventDefault()
//console.log(e) //console.log(e)
u(e.target).closest('.upload-item').addClass('currently_dragging') u(e.target).closest('.upload-item').addClass('currently_dragging')
return return
}) })
u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical .upload-item', (e) => { u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
e.preventDefault() e.preventDefault()
const target = u(e.target).closest('.upload-item') const target = u(e.target).closest('.upload-item')
@ -1130,7 +1130,7 @@ u(document).on("dragover drop", async (e) => {
return false; return false;
}) })
u(document).on('dragleave dragend', '#write .post-horizontal .upload-item, .post-vertical .upload-item', (e) => { u(document).on('dragleave dragend', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
//console.log(e) //console.log(e)
u(e.target).closest('.upload-item').removeClass('dragged') u(e.target).closest('.upload-item').removeClass('dragged')
return return
@ -1792,7 +1792,7 @@ u(document).on('click', `.post-horizontal .upload-item .upload-delete`, (e) => {
u(e.target).closest('.upload-item').remove() u(e.target).closest('.upload-item').remove()
}) })
u(document).on('click', `.post-vertical .vertical-attachment #small_remove_button`, (e) => { u(document).on('click', `.vertical-attachment #small_remove_button`, (e) => {
e.preventDefault() e.preventDefault()
u(e.target).closest('.vertical-attachment').remove() u(e.target).closest('.vertical-attachment').remove()
}) })

View file

@ -7,7 +7,7 @@ window.router = new class {
if(script.src) { if(script.src) {
const script_url = new URL(script.src) const script_url = new URL(script.src)
const script_main_part = script_url.pathname const script_main_part = script_url.pathname
console.log(script_main_part)
return u(`script[src^='${script_main_part}']`).length > 0 return u(`script[src^='${script_main_part}']`).length > 0
} }
@ -62,6 +62,7 @@ window.router = new class {
const page_body = u(parsed_content.querySelector('.page_body')) const page_body = u(parsed_content.querySelector('.page_body'))
const sidebar = u(parsed_content.querySelector('.sidebar')) const sidebar = u(parsed_content.querySelector('.sidebar'))
const page_header = u(parsed_content.querySelector('.page_header')) const page_header = u(parsed_content.querySelector('.page_header'))
const backdrop = u(parsed_content.querySelector('#backdrop'))
if(page_body.length < 1) { if(page_body.length < 1) {
throw new Error('Invalid page has been loaded') throw new Error('Invalid page has been loaded')
return return
@ -77,6 +78,15 @@ window.router = new class {
}) })
u('.page_body').html(page_body.html()) u('.page_body').html(page_body.html())
u('.sidebar').html(sidebar.html()) u('.sidebar').html(sidebar.html())
if(backdrop.length > 0) {
if(u('#backdrop').length == 0) {
u('body').append(`<div id="backdrop"></div>`)
}
u('#backdrop').nodes[0].outerHTML = (backdrop.nodes[0].outerHTML)
} else {
u('#backdrop').remove()
}
if(u('.page_header #search_box select').length > 0 && page_header.find('#search_box select').length > 0) { if(u('.page_header #search_box select').length > 0 && page_header.find('#search_box select').length > 0) {
u('.page_header #search_box select').nodes[0].value = page_header.find('#search_box select').nodes[0].value u('.page_header #search_box select').nodes[0].value = page_header.find('#search_box select').nodes[0].value
} }
@ -285,7 +295,7 @@ u(document).on('submit', 'form', async (e) => {
return return
} }
const form_data = serializeForm(form) const form_data = serializeForm(form, e.submitter)
const request_object = { const request_object = {
method: method, method: method,
headers: { headers: {

View file

@ -182,11 +182,10 @@ function getRemainingTime(fullTime, time) {
return "-" + fmtTime(timer) return "-" + fmtTime(timer)
} }
function serializeForm(form) function serializeForm(form, submitter = null)
{ {
const u_ = u(form) const u_ = u(form)
const inputs = u_.find('input, textarea') const inputs = u_.find('input, textarea, button')
let fd = new FormData() let fd = new FormData()
inputs.nodes.forEach(inp => { inputs.nodes.forEach(inp => {
if(!inp || !inp.name) { if(!inp || !inp.name) {
@ -194,16 +193,17 @@ function serializeForm(form)
} }
if(inp.type == 'submit') { if(inp.type == 'submit') {
return if(inp !== submitter) {
return
}
} }
switch(inp.type) { switch(inp.type) {
case 'submit':
return
case 'hidden': case 'hidden':
case 'text': case 'text':
case 'textarea': case 'textarea':
case 'select': case 'select':
case 'submit':
fd.append(inp.name, inp.value) fd.append(inp.name, inp.value)
break break
case 'checkbox': case 'checkbox':
@ -213,8 +213,13 @@ function serializeForm(form)
break break
case 'file': case 'file':
for(const __file of inp.files) { if(!inp.multiple) {
fd.append(inp.name, __file) if(inp.files[0]) {
fd.append(inp.name, inp.files[0])
} else {
const emptyFile = new Blob([], { type: 'application/octet-stream' })
fd.append(inp.name, emptyFile, '')
}
} }
break break
} }

View file

@ -949,12 +949,11 @@
"add_to_playlist" = "Add to playlist"; "add_to_playlist" = "Add to playlist";
"remove_from_playlist" = "Remove from playlist"; "remove_from_playlist" = "Remove from playlist";
"delete_playlist" = "Delete playlist"; "delete_playlist" = "Delete playlist";
"playlist_cover" = "Playlist cover";
"playlists_user" = "User's playlists"; "playlists_user" = "User's playlists";
"playlists_club" = "Group's playlists"; "playlists_club" = "Group's playlists";
"change_cover" = "Change cover"; "change_cover" = "Change cover";
"playlist_cover" = "Playlist's cover"; "playlist_cover" = "Playlist cover";
"minutes_count_zero" = "Lasts no minutes"; "minutes_count_zero" = "Lasts no minutes";
"minutes_count_one" = "Lasts one minute"; "minutes_count_one" = "Lasts one minute";
@ -1004,6 +1003,7 @@
"audio_ctx_add_to_group" = "Add to group"; "audio_ctx_add_to_group" = "Add to group";
"audio_ctx_add_to_playlist" = "Add to playlist"; "audio_ctx_add_to_playlist" = "Add to playlist";
"audio_ctx_play_next" = "Play next"; "audio_ctx_play_next" = "Play next";
"audio_ctx_clear_context" = "Clear tracks list";
/* Notifications */ /* Notifications */
@ -1603,6 +1603,7 @@
"error_adding_source_regex" = "Error adding source: incorrect link."; "error_adding_source_regex" = "Error adding source: incorrect link.";
"error_adding_source_long" = "Error adding source: link is too long."; "error_adding_source_long" = "Error adding source: link is too long.";
"error_adding_source_sus" = "Error adding source: suspicious link."; "error_adding_source_sus" = "Error adding source: suspicious link.";
"error_playlist_creating_too_small" = "Add at least one audio";
/* Admin actions */ /* Admin actions */

View file

@ -908,7 +908,7 @@
"playlists_user" = "Плейлисты пользователя"; "playlists_user" = "Плейлисты пользователя";
"playlists_club" = "Плейлисты группы"; "playlists_club" = "Плейлисты группы";
"change_cover" = "Сменить обложку"; "change_cover" = "Сменить обложку";
"playlist_cover" = "Обложка плейлиста"; "add_audio_verb" = "Добавить аудиозаписи";
"minutes_count_zero" = "длится ноль минут"; "minutes_count_zero" = "длится ноль минут";
"minutes_count_one" = "длится одну минуту"; "minutes_count_one" = "длится одну минуту";
@ -959,6 +959,7 @@
"audio_ctx_add_to_group" = "Добавить в группу"; "audio_ctx_add_to_group" = "Добавить в группу";
"audio_ctx_add_to_playlist" = "Добавить в плейлист"; "audio_ctx_add_to_playlist" = "Добавить в плейлист";
"audio_ctx_play_next" = "Воспроизвести следующим"; "audio_ctx_play_next" = "Воспроизвести следующим";
"audio_ctx_clear_context" = "Очистить список треков";
/* Notifications */ /* Notifications */
@ -1505,6 +1506,7 @@
"error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка."; "error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка.";
"error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка."; "error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка.";
"error_adding_source_sus" = "Ошибка добавления источника: гиперссылка заблокирована."; "error_adding_source_sus" = "Ошибка добавления источника: гиперссылка заблокирована.";
"error_playlist_creating_too_small" = "Добавь хотя бы одну аудиозапись.";
/* Admin actions */ /* Admin actions */