[WIP] Add calls, stories and clips.

Изменены фавиконки (поменьше стали)
У миниплеера ползунок теперь в стиле bsdn и большого плеера, добавлен ползунок громкости
Добавлена кнопка добавления аудио в группу (у миниплеера)
Если вы смотрите аудио группы, которой можете управлять, появляется кнопка "удалить аудио из группы"
Снизу плейлиста в списке теперь показывается автор.
При прикреплении аудиозаписей к посту теперь есть поиск "по композиции" и "по исполнителю"
Добавил explicit.svg, который я забыл добавить в предыдущем коммите.
Вкладочки немного переделаны
При наведении на кнопки "трек вперёд" или "трек назад" показывается название предыдущего или следующего трека соответственно
This commit is contained in:
lalka2018 2023-10-26 20:04:43 +03:00
parent 5486327842
commit e1646cd8a8
16 changed files with 407 additions and 103 deletions

View file

@ -63,6 +63,7 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->owner = $entity; $this->template->owner = $entity;
$this->template->ownerId = $owner; $this->template->ownerId = $owner;
$this->template->club = $owner < 0 ? $entity : NULL;
$this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id)); $this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id));
$this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity)); $this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity));
} else if ($mode === "new") { } else if ($mode === "new") {
@ -252,7 +253,7 @@ final class AudioPresenter extends OpenVKPresenter
try { try {
$playlist->fastMakeCover($this->user->id, $_FILES["cover"]); $playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
} catch(\ImagickException $e) { } catch(\Throwable $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo")); $this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
} }
} }
@ -342,7 +343,7 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->playlist = $playlist; $this->template->playlist = $playlist;
$this->template->page = $page; $this->template->page = $page;
$audios = iterator_to_array($playlist->getAudios()); $audios = iterator_to_array($playlist->fetch(1, $playlist->size()));
$this->template->audios = array_slice($audios, 0, 10); $this->template->audios = array_slice($audios, 0, 10);
$audiosIds = []; $audiosIds = [];
@ -448,6 +449,30 @@ final class AudioPresenter extends OpenVKPresenter
else else
$this->flashFail("err", "error", tr("do_not_have_audio"), null, true); $this->flashFail("err", "error", tr("do_not_have_audio"), null, true);
break;
case "remove_club":
$club = (new Clubs)->get((int)$this->postParam("club"));
if(!$club || !$club->canBeModifiedBy($this->user->identity))
$this->flashFail("err", "error", tr("access_denied"), null, true);
if($audio->isInLibraryOf($club))
$audio->remove($club);
else
$this->flashFail("err", "error", tr("group_hasnt_audio"), null, true);
break;
case "add_to_club":
$club = (new Clubs)->get((int)$this->postParam("club"));
if(!$club || !$club->canBeModifiedBy($this->user->identity))
$this->flashFail("err", "error", tr("access_denied"), null, true);
if(!$audio->isInLibraryOf($club))
$audio->add($club);
else
$this->flashFail("err", "error", tr("group_has_audio"), null, true);
break; break;
case "delete": case "delete":
if($audio->canBeModifiedBy($this->user->identity)) if($audio->canBeModifiedBy($this->user->identity))
@ -555,7 +580,7 @@ final class AudioPresenter extends OpenVKPresenter
$audiosCount = $playlist->size(); $audiosCount = $playlist->size();
break; break;
case "search_context": case "search_context":
$stream = $this->audios->search($this->postParam("query"), 2); $stream = $this->audios->search($this->postParam("query"), 2, $this->postParam("type") === "by_performer");
$audios = $stream->page($page, 10); $audios = $stream->page($page, 10);
$audiosCount = $stream->size(); $audiosCount = $stream->size();
break; break;

View file

@ -74,6 +74,11 @@
}) })
u("#editPlaylistForm input[name='new_cover']").on("change", (e) => { u("#editPlaylistForm input[name='new_cover']").on("change", (e) => {
if(!e.currentTarget.files[0].type.startsWith("image/")) {
fastError(tr("not_a_photo"))
return
}
let image = URL.createObjectURL(e.currentTarget.files[0]) let image = URL.createObjectURL(e.currentTarget.files[0])
document.querySelector(".playlistCover img").src = image document.querySelector(".playlistCover img").src = image

View file

@ -55,11 +55,11 @@
<div style="width: 74%;" class="audiosContainer" n:if="$mode != 'playlists'"> <div style="width: 74%;" class="audiosContainer" n:if="$mode != 'playlists'">
<div style="padding: 8px;"> <div style="padding: 8px;">
<div n:if="$audiosCount <= 0"> <div n:if="$audiosCount <= 0">
{include "../components/nothing.xml"} {include "../components/error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_audios_thisuser") : tr("no_audios_user")) : tr("no_audios_club")}
</div> </div>
<div n:if="$audiosCount > 0" class="infContainer"> <div n:if="$audiosCount > 0" class="infContainer">
<div class="infObj" n:foreach="$audios as $audio"> <div class="infObj" n:foreach="$audios as $audio">
{include "player.xml", audio => $audio} {include "player.xml", audio => $audio, club => $club}
</div> </div>
</div> </div>
@ -89,9 +89,13 @@
</div> </div>
<div class="playlistInfo"> <div class="playlistInfo">
<span style="font-size: 12px"> <span style="font-size: 12px" class="playlistName">
{ovk_proc_strtr($playlist->getName(), 12)} {ovk_proc_strtr($playlist->getName(), 12)}
</span> </span>
<span style="font-size: 12px" class="playlistAuthor">
<a href="{$playlist->getOwner()->getURL()}">{ovk_proc_strtr($playlist->getOwner()->getCanonicalName(), 15)}</a>
</span>
</div> </div>
</a> </a>
</div> </div>

View file

@ -22,42 +22,45 @@
<style> <style>
textarea[name='description'] { textarea[name='description'] {
padding: 4px; padding: 4px;
resize: vertical; }
min-height: 150px;
max-height: 300px; .playlistInfo {
width: 76%;
margin-left: 8px;
} }
</style> </style>
<div class="playlistInfo"> <div style="display:flex;">
<input type="text" name="title" placeholder="{_title}" maxlength="128" /> <div class="playlistCover" onclick="document.querySelector(`#newPlaylistForm input[name='cover']`).click()">
<br /><br /> <a>
<textarea placeholder="{_description}" name="description" maxlength="2048" /> <img src="/assets/packages/static/openvk/img/song.jpg">
<br /><br /> </a>
{_playlist_cover}: <input type="button" class="button" value="{_upload_button}" onclick="document.querySelector(`#newPlaylistForm input[name='cover']`).click()">
<div class="playlistCover" style="margin-top: 6px;">
<img style="display:none">
</div> </div>
<hr /> <div class="playlistInfo">
<input type="text" name="title" placeholder="{_title}" maxlength="128" />
<br /><br />
<textarea placeholder="{_description}" name="description" maxlength="2048" />
<br /><br />
</div>
</div>
<div style="margin-top: 19px;"> <hr />
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}"> <div style="margin-top: 19px;">
<div class="playlistAudiosContainer" style="display:table;clear:both;width:100%;margin-top: 10px;"> <input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}">
<div id="newPlaylistAudios" n:foreach="$audios as $audio"> <div class="playlistAudiosContainer" style="display:table;clear:both;width:100%;margin-top: 10px;">
<div style="width: 78%;float: left;"> <div id="newPlaylistAudios" n:foreach="$audios as $audio">
{include "player.xml", audio => $audio, hideButtons => true} <div style="width: 78%;float: left;">
</div> {include "player.xml", audio => $audio, hideButtons => true}
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}" style="width: 22%;"> </div>
<span>{_add_to_playlist}</span> <div class="attachAudio addToPlaylist" data-id="{$audio->getId()}" style="width: 22%;">
</div> <span>{_add_to_playlist}</span>
</div> </div>
</div> </div>
</div>
<div class="showMoreAudiosPlaylist" data-page="2" n:if="$pagesCount > 1"> <div class="showMoreAudiosPlaylist" data-page="2" {if !is_null($_GET["owner"])}data-club="{abs($_GET['owner'])}"{/if} n:if="$pagesCount > 1">
{_show_more_audios} {_show_more_audios}
</div>
</div> </div>
</div> </div>
@ -82,6 +85,11 @@
}) })
u("#newPlaylistForm input[name='cover']").on("change", (e) => { u("#newPlaylistForm input[name='cover']").on("change", (e) => {
if(!e.currentTarget.files[0].type.startsWith("image/")) {
fastError(tr("not_a_photo"))
return
}
let image = URL.createObjectURL(e.currentTarget.files[0]) let image = URL.createObjectURL(e.currentTarget.files[0])
document.querySelector(".playlistCover img").src = image document.querySelector(".playlistCover img").src = image
@ -91,7 +99,6 @@
u(".playlistCover img").on("click", (e) => { u(".playlistCover img").on("click", (e) => {
document.querySelector("#newPlaylistForm input[name='cover']").value = "" document.querySelector("#newPlaylistForm input[name='cover']").value = ""
e.currentTarget.href = "" e.currentTarget.href = ""
e.currentTarget.style.display = "none"
}) })
document.querySelector("#newPlaylistForm input[name='cover']").value = "" document.querySelector("#newPlaylistForm input[name='cover']").value = ""

View file

@ -1,15 +0,0 @@
{extends "../@layout.xml"}
{block title}{_audios}{/block}
{block header}
{/block}
{block content}
{script "js/node_modules/umbrellajs/umbrella.min.js"}
<div style="width: 100%;display: flex;margin-bottom: -10px;">
{include "tabs.xml"}
</div>
{/block}

View file

@ -5,8 +5,13 @@
<div class="playButton musicIcon"></div> <div class="playButton musicIcon"></div>
<div class="arrowsButtons"> <div class="arrowsButtons">
<div class="nextButton musicIcon"></div> <div>
<div class="backButton musicIcon"></div> <div class="nextButton musicIcon"></div>
</div>
<div>
<div class="backButton musicIcon"></div>
</div>
</div> </div>
</div> </div>
@ -23,7 +28,7 @@
</div> </div>
<div class="track"> <div class="track">
<div class="timeTip">00:00</div> <div class="bigPlayerTip">00:00</div>
<div class="selectableTrack"> <div class="selectableTrack">
<div style="width: 95%;position: relative;">&nbsp; <div style="width: 95%;position: relative;">&nbsp;
<div class="slider"></div> <div class="slider"></div>

View file

@ -30,7 +30,9 @@
{if !$hideButtons} {if !$hideButtons}
<div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && $hasAudio" ></div> <div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && $hasAudio" ></div>
<div class="add-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$hasAudio && !$isWithdrawn" ></div> <div class="remove-icon-group musicIcon" data-id="{$audio->getId()}" data-club="{$club->getId()}" n:if="isset($thisUser) && isset($club) && $club->canBeModifiedBy($thisUser)" ></div>
<div class="add-icon musicIcon hovermeicon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$hasAudio && !$isWithdrawn" ></div>
<div class="add-icon-group musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser)" ></div>
<div class="edit-icon musicIcon" data-lyrics="{$audio->getLyrics()}" data-title="{$audio->getTitle()}" data-performer="{$audio->getPerformer()}" data-explicit="{(int)$audio->isExplicit()}" data-searchable="{(int)!$audio->isUnlisted()}" n:if="isset($thisUser) && $editable && !$isWithdrawn" ></div> <div class="edit-icon musicIcon" data-lyrics="{$audio->getLyrics()}" data-title="{$audio->getTitle()}" data-performer="{$audio->getPerformer()}" data-explicit="{(int)$audio->isExplicit()}" data-searchable="{(int)!$audio->isUnlisted()}" n:if="isset($thisUser) && $editable && !$isWithdrawn" ></div>
<div class="report-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$editable && !$isWithdrawn" ></div> <div class="report-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$editable && !$isWithdrawn" ></div>
{/if} {/if}
@ -38,11 +40,23 @@
</div> </div>
</div> </div>
<div class="tracks"> <div style="display: flex;">
<div class="track" style="margin-top: 3px;display:none"> <div style="width: 100%;">
<div class="selectableTrack" n:attr="style => $isWithdrawn ? 'display: none;' : ''"> <div class="track lengthTrack" style="margin-top: 3px;display:none">
<div>&nbsp; <div class="selectableTrack" style="width: 100%;" n:attr="style => $isWithdrawn ? 'display: none;' : ''">
<!-- actual track --> <div style="position: relative;width: 94.8%;">
<div class="slider"></div>
</div>
</div>
</div>
</div>
<div style="width: 21%;margin-left: 16px;">
<div class="track volumeTrack" style="margin-top: 3px;display:none">
<div class="selectableTrack" style="width: 100%;" n:attr="style => $isWithdrawn ? 'display: none;' : ''">
<div style="position: relative;width: 71%;">
<div class="slider"></div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -5,13 +5,20 @@
<a n:attr="id => $mode === 'new' ? 'used' : 'ki'" href="/audios/new">{_audio_new}</a> <a n:attr="id => $mode === 'new' ? 'used' : 'ki'" href="/audios/new">{_audio_new}</a>
<a n:attr="id => $mode === 'popular' ? 'used' : 'ki'" href="/audios/popular">{_audio_popular}</a> <a n:attr="id => $mode === 'popular' ? 'used' : 'ki'" href="/audios/popular">{_audio_popular}</a>
<a href="/search?type=audios" n:if="isset($thisUser)">{_audio_search}</a> <a href="/search?type=audios" n:if="isset($thisUser)">{_audio_search}</a>
<a n:if="!$isMy && $mode === 'list'" id="used" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
<hr> <hr>
<a n:attr="id => $mode === 'playlists' && $ownerId == $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$thisUser->getId()}" n:if="isset($thisUser)">{_my_playlists}</a> <a n:attr="id => $mode === 'playlists' && $ownerId == $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$thisUser->getId()}" n:if="isset($thisUser)">{_my_playlists}</a>
<a n:attr="id => $mode === 'playlists' && $ownerId != $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$ownerId}" n:if="isset($ownerId) && !$isMy">{if $ownerId > 0}{_playlists_user}{else}{_playlists_club}{/if}</a>
<a n:if="isset($thisUser)" href="/audios/newPlaylist{if $isMyClub}?owner={abs($ownerId)}{/if}">{_new_playlist}</a> <a n:if="isset($thisUser)" href="/audios/newPlaylist">{_new_playlist}</a>
{if !$isMy && $mode !== 'popular' && $mode !== 'new'}
<hr>
<a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
<a href="/player/upload?gid={abs($ownerId)}" n:if="isset($thisUser) && $isMyClub">{_upload_audio}</a>
<a n:attr="id => $mode === 'playlists' && $ownerId != $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$ownerId}" n:if="isset($ownerId) && !$isMy">{if $ownerId > 0}{_playlists_user}{else}{_playlists_club}{/if}</a>
<a href="/audios/newPlaylist{if $isMyClub}?owner={abs($ownerId)}{/if}" n:if="isset($thisUser) && $isMyClub">{_new_playlist}</a>
{/if}
</div> </div>
</div> </div>

View file

@ -20,6 +20,7 @@
height: 46px; height: 46px;
border-bottom: 1px solid #d8d8d8; border-bottom: 1px solid #d8d8d8;
box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2); box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2);
position: relative;
} }
.bigPlayer.floating { .bigPlayer.floating {
@ -126,7 +127,7 @@
position: relative; position: relative;
} }
.bigPlayer .paddingLayer .trackPanel .track .timeTip { .bigPlayer .paddingLayer .bigPlayerTip {
display: none; display: none;
z-index: 999; z-index: 999;
background: #cecece; background: #cecece;
@ -142,7 +143,7 @@
float: left; float: left;
} }
.bigPlayer .paddingLayer .slider { .bigPlayer .paddingLayer .slider, .audioEmbed .track .slider {
width: 18px; width: 18px;
height: 7px; height: 7px;
background: #707070; background: #707070;
@ -229,12 +230,6 @@
user-select: none; user-select: none;
} }
.audioEmbed .track > div > div {
height: 100%;
width: 0%;
background-color: #BFBFBF;
}
#audioEmbed { #audioEmbed {
user-select: none; user-select: none;
background: #eee; background: #eee;
@ -255,6 +250,10 @@
box-sizing: border-box; box-sizing: border-box;
} }
.audioEntry.nowPlaying .playIcon {
background-position-y: -16px !important;
}
.audioEntry.nowPlaying:hover { .audioEntry.nowPlaying:hover {
background: rgb(100, 100, 100) !important; background: rgb(100, 100, 100) !important;
} }
@ -342,6 +341,8 @@
position: absolute; position: absolute;
right: 11%; right: 11%;
top: 2px; top: 2px;
/* чтоб избежать заедания во время ховера кнопки добавления */
clip-path: inset(0 0 0 0);
} }
.audioEntry .buttons .edit-icon { .audioEntry .buttons .edit-icon {
@ -359,6 +360,16 @@
float: right; float: right;
background-position: -80px -52px; background-position: -80px -52px;
margin-top: 3px; margin-top: 3px;
margin-left: 2px;
}
.audioEntry .buttons .add-icon-group {
width: 14px;
height: 11px;
float: right;
background-position: -94px -52px;
margin-top: 3px;
transition: margin-right 0.1s ease-out, opacity 0.1s ease-out;
} }
.audioEntry .buttons .report-icon { .audioEntry .buttons .report-icon {
@ -384,10 +395,21 @@
margin-top: 3px; margin-top: 3px;
width: 11px; width: 11px;
height: 11px; height: 11px;
margin-left: 2px;
float: right; float: right;
background-position: -108px -52px; background-position: -108px -52px;
} }
.audioEntry .buttons .remove-icon-group {
margin-top: 3px;
width: 13px;
height: 11px;
float: right;
background-position: -122px -52px;
margin-left: 3px;
margin-right: 3px;
}
.audioEmbed .lyrics { .audioEmbed .lyrics {
display: none; display: none;
padding: 6px 33px 10px 33px; padding: 6px 33px 10px 33px;
@ -454,10 +476,19 @@
height: 35px; height: 35px;
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
display: flex;
} }
.ovk-diag-body .searchBox input { .ovk-diag-body .searchBox input {
height: 24px; height: 24px;
margin-right: -1px;
width: 77%;
}
.ovk-diag-body .searchBox select {
width: 29%;
padding-left: 8px;
height: 24px;
} }
.ovk-diag-body .audiosInsert { .ovk-diag-body .audiosInsert {
@ -509,3 +540,23 @@
padding-top: 2px; padding-top: 2px;
padding-bottom: 3px; padding-bottom: 3px;
} }
/* <center> 🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣*/
.audiosContainer center span {
color: #707070;
margin: 120px 0px !important;
display: block;
}
.audiosContainer center {
margin-left: -10px;
}
.playlistInfo {
display: flex;
flex-direction: column;
}
.playlistInfo .playlistName {
font-weight: 600;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" height="11" viewBox="0 0 11 11" width="11">
<path d="m1 2.506v5.988a1.5 1.5 0 0 0 1.491 1.506h6.019c.827 0 1.49-.674 1.49-1.506v-5.988a1.5 1.5 0 0 0 -1.491-1.506h-6.019c-.827 0-1.49.674-1.49 1.506zm4 2.494v-1h2v-1h-3v5h3v-1h-2v-1h2v-1zm-5-2.494a2.496 2.496 0 0 1 2.491-2.506h6.019a2.5 2.5 0 0 1 2.49 2.506v5.988a2.496 2.496 0 0 1 -2.491 2.506h-6.019a2.5 2.5 0 0 1 -2.49-2.506z"
fill="#828a99" fill-opacity=".7"/>
</svg>

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -41,6 +41,10 @@ class bigPlayer {
timeType = 0 timeType = 0
findTrack(id) {
return this.tracks["tracks"].find(item => item.id == id)
}
constructor(context, context_id, page = 1) { constructor(context, context_id, page = 1) {
this.context["context_type"] = context this.context["context_type"] = context
this.context["context_id"] = context_id this.context["context_id"] = context_id
@ -141,16 +145,52 @@ class bigPlayer {
const width = e.clientX - rect.left; const width = e.clientX - rect.left;
const time = Math.ceil((width * this.tracks["currentTrack"].length) / (rect.right - rect.left)); const time = Math.ceil((width * this.tracks["currentTrack"].length) / (rect.right - rect.left));
document.querySelector(".bigPlayer .track .timeTip").style.display = "block" document.querySelector(".bigPlayer .track .bigPlayerTip").style.display = "block"
document.querySelector(".bigPlayer .track .timeTip").innerHTML = fmtTime(time) document.querySelector(".bigPlayer .track .bigPlayerTip").innerHTML = fmtTime(time)
document.querySelector(".bigPlayer .track .timeTip").style.left = `min(${width - 15}px, 315.5px)` document.querySelector(".bigPlayer .track .bigPlayerTip").style.left = `min(${width - 15}px, 315.5px)`
})
u(".bigPlayer .nextButton").on("mouseover mouseleave", (e) => {
if(this.tracks["currentTrack"] == null)
return
if(e.type == "mouseleave") {
$(".nextTrackTip").remove()
return
}
e.currentTarget.parentNode.insertAdjacentHTML("afterbegin", `
<div class="bigPlayerTip nextTrackTip" style="left: 7%;">
${ovk_proc_strtr(escapeHtml(this.findTrack(this.tracks["previousTrack"]).name), 20) ?? ""}
</div>
`)
document.querySelector(".nextTrackTip").style.display = "block"
})
u(".bigPlayer .backButton").on("mouseover mouseleave", (e) => {
if(this.tracks["currentTrack"] == null)
return
if(e.type == "mouseleave") {
$(".previousTrackTip").remove()
return
}
e.currentTarget.parentNode.insertAdjacentHTML("afterbegin", `
<div class="bigPlayerTip previousTrackTip" style="left: 10%;">
${ovk_proc_strtr(escapeHtml(this.findTrack(this.tracks["nextTrack"]).name), 20) ?? ""}
</div>
`)
document.querySelector(".previousTrackTip").style.display = "block"
}) })
u(".bigPlayer .trackPanel .track").on("mouseleave", (e) => { u(".bigPlayer .trackPanel .track").on("mouseleave", (e) => {
if(this.tracks["currentTrack"] == null) if(this.tracks["currentTrack"] == null)
return return
document.querySelector(".bigPlayer .track .timeTip").style.display = "none" document.querySelector(".bigPlayer .track .bigPlayerTip").style.display = "none"
}) })
u(".bigPlayer .volumePanel > div").on("click mouseup mousemove", (e) => { u(".bigPlayer .volumePanel > div").on("click mouseup mousemove", (e) => {
@ -200,7 +240,7 @@ class bigPlayer {
if(this.tracks["currentTrack"] == null) if(this.tracks["currentTrack"] == null)
return return
this.tracks["tracks"].sort(() => Math.random() - 0.5) this.tracks["tracks"].sort(() => Math.random() - 0.59)
this.setTrack(this.tracks["tracks"].at(0).id) this.setTrack(this.tracks["tracks"].at(0).id)
}) })
@ -316,9 +356,8 @@ class bigPlayer {
} }
pause() { pause() {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry .playerButton .playIcon`) != null ? document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry .playerButton .playIcon`).classList.remove("paused") : void(0) document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry .playerButton .playIcon`) != null ? document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry .playerButton .playIcon`).classList.remove("paused") : void(0)
this.player().pause() this.player().pause()
@ -329,17 +368,15 @@ class bigPlayer {
} }
showPreviousTrack() { showPreviousTrack() {
if(this.tracks["currentTrack"] == null || this.tracks["previousTrack"] == null) { if(this.tracks["currentTrack"] == null || this.tracks["previousTrack"] == null)
return return
}
this.setTrack(this.tracks["previousTrack"]) this.setTrack(this.tracks["previousTrack"])
} }
showNextTrack() { showNextTrack() {
if(this.tracks["currentTrack"] == null || this.tracks["nextTrack"] == null) { if(this.tracks["currentTrack"] == null || this.tracks["nextTrack"] == null)
return return
}
this.setTrack(this.tracks["nextTrack"]) this.setTrack(this.tracks["nextTrack"])
} }
@ -349,16 +386,28 @@ class bigPlayer {
let prevButton = this.nodes["thisPlayer"].querySelector(".nextButton") let prevButton = this.nodes["thisPlayer"].querySelector(".nextButton")
let nextButton = this.nodes["thisPlayer"].querySelector(".backButton") let nextButton = this.nodes["thisPlayer"].querySelector(".backButton")
if(this.tracks["previousTrack"] == null) { if(this.tracks["previousTrack"] == null)
prevButton.classList.add("lagged") prevButton.classList.add("lagged")
} else { else
prevButton.classList.remove("lagged") prevButton.classList.remove("lagged")
if(this.tracks["nextTrack"] == null)
nextButton.classList.add("lagged")
else
nextButton.classList.remove("lagged")
if(document.querySelector(".nextTrackTip") != null) {
let track = this.findTrack(this.tracks["previousTrack"])
document.querySelector(".nextTrackTip").innerHTML = `
${track != null ? ovk_proc_strtr(escapeHtml(track.name), 20) : ""}
`
} }
if(this.tracks["nextTrack"] == null) { if(document.querySelector(".previousTrackTip") != null) {
nextButton.classList.add("lagged") let track = this.findTrack(this.tracks["nextTrack"])
} else { document.querySelector(".previousTrackTip").innerHTML = `
nextButton.classList.remove("lagged") ${track != null ? ovk_proc_strtr(escapeHtml(track.name ?? ""), 20) : ""}
`
} }
} }
@ -449,11 +498,12 @@ class bigPlayer {
this.play() this.play()
document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry`) != null ? let playerAtPage = document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry`)
document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry`).classList.add("nowPlaying") : if(playerAtPage != null)
null playerAtPage.classList.add("nowPlaying")
document.querySelectorAll(`.audioEntry .playerButton .playIcon.paused`).forEach(el => el.classList.remove("paused")) document.querySelectorAll(`.audioEntry .playerButton .playIcon.paused`).forEach(el => el.classList.remove("paused"))
localStorage.lastPlayedTrack = this.tracks["currentTrack"].id localStorage.lastPlayedTrack = this.tracks["currentTrack"].id
if(this.timeType == 1) if(this.timeType == 1)
@ -543,10 +593,9 @@ function initPlayer(id, keys, url, length) {
if(window.player.tracks["tracks"] == null) if(window.player.tracks["tracks"] == null)
return return
if(window.player.tracks["currentTrack"] == null || window.player.tracks["currentTrack"].id != playerObject.dataset.realid) { if(window.player.tracks["currentTrack"] == null || window.player.tracks["currentTrack"].id != playerObject.dataset.realid)
window.player.setTrack(playerObject.dataset.realid) window.player.setTrack(playerObject.dataset.realid)
playButton.addClass("paused") else
} else
document.querySelector(".bigPlayer .playButton").click() document.querySelector(".bigPlayer .playButton").click()
}) })
@ -576,10 +625,19 @@ function initPlayer(id, keys, url, length) {
const time = audio.currentTime; const time = audio.currentTime;
const ps = Math.ceil((time * 100) / length); const ps = Math.ceil((time * 100) / length);
volumeSpan.html(fmtTime(Math.floor(time))); volumeSpan.html(fmtTime(Math.floor(time)));
if (ps <= 100) if (ps <= 100)
trackDiv.nodes[0].style.width = `${ ps}%`; playerObject.querySelector(".lengthTrack .slider").style.left = `${ ps}%`;
}); });
u(audio).on("volumechange", (e) => {
const volume = audio.volume;
const ps = Math.ceil((volume * 100) / 1);
if (ps <= 100)
playerObject.querySelector(".volumeTrack .slider").style.left = `${ ps}%`;
})
const playButtonImageUpdate = () => { const playButtonImageUpdate = () => {
if (!audio.paused) { if (!audio.paused) {
playButton.addClass("paused") playButton.addClass("paused")
@ -603,13 +661,30 @@ function initPlayer(id, keys, url, length) {
u(audio).on("play", playButtonImageUpdate); u(audio).on("play", playButtonImageUpdate);
u(audio).on(["pause", "ended", "suspended"], playButtonImageUpdate); u(audio).on(["pause", "ended", "suspended"], playButtonImageUpdate);
u(`#audioEmbed-${ id} .track > div`).on("click", (e) => { u(`#audioEmbed-${ id} .lengthTrack > div`).on("click", (e) => {
let rect = document.querySelector("#audioEmbed-" + id + " .selectableTrack").getBoundingClientRect(); let rect = document.querySelector("#audioEmbed-" + id + " .selectableTrack").getBoundingClientRect();
const width = e.clientX - rect.left; const width = e.clientX - rect.left;
const time = Math.ceil((width * length) / (rect.right - rect.left)); const time = Math.ceil((width * length) / (rect.right - rect.left));
audio.currentTime = time; audio.currentTime = time;
}); });
u(`#audioEmbed-${ id} .volumeTrack > div`).on("click mouseup mousemove", (e) => {
if(e.type == "mousemove") {
let buttonsPresseed = _bsdnUnwrapBitMask(e.buttons)
if(!buttonsPresseed[0])
return;
}
let rect = document.querySelector("#audioEmbed-" + id + " .volumeTrack").getBoundingClientRect();
const width = e.clientX - rect.left;
const volume = (width * 1) / (rect.right - rect.left);
audio.volume = volume;
});
u(audio).trigger("volumechange")
} }
$(document).on("click", ".musicIcon.edit-icon", (e) => { $(document).on("click", ".musicIcon.edit-icon", (e) => {
@ -638,7 +713,7 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
<div style="margin-top: 11px"> <div style="margin-top: 11px">
${tr("lyrics")} ${tr("lyrics")}
<textarea name="lyrics" maxlength="500">${lyrics ?? ""}</textarea> <textarea name="lyrics" maxlength="5000" style="max-height: 200px;">${lyrics ?? ""}</textarea>
</div> </div>
<div style="margin-top: 11px"> <div style="margin-top: 11px">
@ -694,7 +769,7 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
e.currentTarget.setAttribute("data-lyrics", response.new_info.lyrics_unformatted) e.currentTarget.setAttribute("data-lyrics", response.new_info.lyrics_unformatted)
e.currentTarget.setAttribute("data-explicit", Number(response.new_info.explicit)) e.currentTarget.setAttribute("data-explicit", Number(response.new_info.explicit))
e.currentTarget.setAttribute("data-searchable", Number(response.new_info.unlisted)) e.currentTarget.setAttribute("data-searchable", Number(!response.new_info.unlisted))
player.setAttribute("data-genre", response.new_info.genre) player.setAttribute("data-genre", response.new_info.genre)
let url = new URL(location.href) let url = new URL(location.href)
@ -781,6 +856,86 @@ $(document).on("click", ".musicIcon.remove-icon", (e) => {
}) })
}) })
$(document).on("click", ".musicIcon.remove-icon-group", (e) => {
let id = e.currentTarget.dataset.id
let formdata = new FormData()
formdata.append("hash", u("meta[name=csrf]").attr("value"))
formdata.append("club", e.currentTarget.dataset.club)
ky.post(`/audio${id}/action?act=remove_club`, {
hooks: {
beforeRequest: [
(_request) => {
e.currentTarget.classList.add("lagged")
}
],
afterResponse: [
async (_request, _options, response) => {
let json = await response.json()
if(json.success)
$(e.currentTarget.closest(".audioEmbed")).remove()
else
fastError(json.flash.message)
}
]
}, body: formdata
})
})
$(document).on("click", ".musicIcon.add-icon-group", async (ev) => {
let body = `
${tr("what_club_add")}
<div style="margin-top: 4px;">
<select id="addIconsWindow" style="width: 36%;"></select>
<input name="addButton" type="button" class="button" value="${tr("add")}">
</div>
<span class="errorPlace"></span>
`
MessageBox(tr("add_audio_to_club"), body, [tr("close")], [Function.noop])
document.querySelector(".ovk-diag-body").style.padding = "11px"
if(window.openvk.writeableClubs == null) {
try {
window.openvk.writeableClubs = await API.Groups.getWriteableClubs()
} catch (e) {
document.querySelector(".errorPlace").innerHTML = tr("no_access_clubs")
document.querySelector(".ovk-diag-body input[name='addButton']").classList.add("lagged")
return
}
}
window.openvk.writeableClubs.forEach(el => {
document.querySelector("#addIconsWindow").insertAdjacentHTML("beforeend", `
<option value="${el.id}">${ovk_proc_strtr(el.name, 20)}</option>
`)
})
$(".ovk-diag-body").on("click", "input[name='addButton']", (e) => {
$.ajax({
type: "POST",
url: `/audio${ev.currentTarget.dataset.id}/action?act=add_to_club`,
data: {
hash: u("meta[name=csrf]").attr("value"),
club: document.querySelector("#addIconsWindow").value
},
beforeSend: () => {
e.currentTarget.classList.add("lagged")
document.querySelector(".errorPlace").innerHTML = ""
},
success: (response) => {
if(!response.success)
document.querySelector(".errorPlace").innerHTML = response.flash.message
e.currentTarget.classList.remove("lagged")
}
})
})
})
$(document).on("click", ".musicIcon.add-icon", (e) => { $(document).on("click", ".musicIcon.add-icon", (e) => {
let id = e.currentTarget.dataset.id let id = e.currentTarget.dataset.id
@ -836,7 +991,11 @@ $(document).on("click", "#_audioAttachment", (e) => {
let form = e.currentTarget.closest("form") let form = e.currentTarget.closest("form")
let body = ` let body = `
<div class="searchBox"> <div class="searchBox">
<input name="query" type="text" placeholder="${tr("header_search")}"> <input name="query" type="text" maxlength="50" placeholder="${tr("header_search")}">
<select name="perf">
<option value="by_name">${tr("by_name")}</option>
<option value="by_performer">${tr("by_performer")}</option>
</select>
</div> </div>
<div class="audiosInsert"></div> <div class="audiosInsert"></div>
@ -847,7 +1006,7 @@ $(document).on("click", "#_audioAttachment", (e) => {
document.querySelector(".ovk-diag-cont").style.width = "580px" document.querySelector(".ovk-diag-cont").style.width = "580px"
document.querySelector(".ovk-diag-body").style.height = "335px" document.querySelector(".ovk-diag-body").style.height = "335px"
async function insertAudios(page, query = "") { async function insertAudios(page, query = "", type = "by_name") {
document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`) document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
$.ajax({ $.ajax({
@ -859,6 +1018,7 @@ $(document).on("click", "#_audioAttachment", (e) => {
page: page, page: page,
query: query == "" ? null : query, query: query == "" ? null : query,
context_entity: 0, context_entity: 0,
type: type,
returnPlayers: 1, returnPlayers: 1,
}, },
success: (response) => { success: (response) => {
@ -911,11 +1071,17 @@ $(document).on("click", "#_audioAttachment", (e) => {
if(e.currentTarget.value === document.querySelector(".searchBox input").value) { if(e.currentTarget.value === document.querySelector(".searchBox input").value) {
document.querySelector(".audiosInsert").innerHTML = "" document.querySelector(".audiosInsert").innerHTML = ""
insertAudios(1, e.currentTarget.value) insertAudios(1, e.currentTarget.value, document.querySelector(".searchBox select").value)
return; return;
} }
}) })
$(".searchBox select").on("change", async (e) => {
document.querySelector(".audiosInsert").innerHTML = ""
insertAudios(1, document.querySelector(".searchBox input").value, e.currentTarget.value)
return;
})
function insertAttachment(id) { function insertAttachment(id) {
let audios = form.querySelector("input[name='audios']") let audios = form.querySelector("input[name='audios']")
@ -1074,7 +1240,7 @@ $(document).on("click", "#bookmarkPlaylist, #unbookmarkPlaylist", (e) => {
}) })
}) })
function getPlayers(page = 1, query = "", playlist = 0) { function getPlayers(page = 1, query = "", playlist = 0, club = 0) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: "/audios/context", url: "/audios/context",
@ -1082,12 +1248,12 @@ function getPlayers(page = 1, query = "", playlist = 0) {
context: query == "" ? (playlist == 0 ? "entity_audios" : "playlist_context") : "search_context", context: query == "" ? (playlist == 0 ? "entity_audios" : "playlist_context") : "search_context",
hash: u("meta[name=csrf]").attr("value"), hash: u("meta[name=csrf]").attr("value"),
page: page, page: page,
context_entity: playlist, context_entity: playlist == 0 ? club * -1 : playlist,
query: query, query: query,
returnPlayers: 1, returnPlayers: 1,
}, },
beforeSend: () => { beforeSend: () => {
document.querySelector(".playlistAudiosContainer").insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`) document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
if(document.querySelector(".showMoreAudiosPlaylist") != null) if(document.querySelector(".showMoreAudiosPlaylist") != null)
document.querySelector(".showMoreAudiosPlaylist").style.display = "none" document.querySelector(".showMoreAudiosPlaylist").style.display = "none"
@ -1143,7 +1309,10 @@ function getPlayers(page = 1, query = "", playlist = 0) {
} }
$(document).on("click", ".showMoreAudiosPlaylist", (e) => { $(document).on("click", ".showMoreAudiosPlaylist", (e) => {
getPlayers(Number(e.currentTarget.dataset.page), "", e.currentTarget.dataset.playlist != null ? Number(e.currentTarget.dataset.playlist) : 0) getPlayers(Number(e.currentTarget.dataset.page), "",
e.currentTarget.dataset.playlist != null ? Number(e.currentTarget.dataset.playlist) : 0,
e.currentTarget.dataset.club != null ? Number(e.currentTarget.dataset.club) : 0,
)
}) })
$(document).on("change", "input#playlist_query", async (e) => { $(document).on("change", "input#playlist_query", async (e) => {

View file

@ -728,6 +728,7 @@
"audio" = "Audio"; "audio" = "Audio";
"playlist" = "Playlist"; "playlist" = "Playlist";
"upload_audio" = "Upload audio"; "upload_audio" = "Upload audio";
"upload_audio_to_group" = "Upload audio to group";
"performer" = "Performer"; "performer" = "Performer";
"audio_name" = "Name"; "audio_name" = "Name";
@ -778,6 +779,10 @@
"no_playlists_user" = "This user has not added any playlists yet."; "no_playlists_user" = "This user has not added any playlists yet.";
"no_playlists_club" = "This group hasn't added playlists yet."; "no_playlists_club" = "This group hasn't added playlists yet.";
"no_audios_thisuser" = "You haven't added any audios yet.";
"no_audios_user" = "This user has not added any audios yet.";
"no_audios_club" = "This group has not added any audios yet.";
"new_playlist" = "New playlist"; "new_playlist" = "New playlist";
"created_playlist" = "created"; "created_playlist" = "created";
"bookmark" = "Add to collection"; "bookmark" = "Add to collection";
@ -807,6 +812,15 @@
"minutes_count_many" = "lasts $1 minutes"; "minutes_count_many" = "lasts $1 minutes";
"minutes_count_other" = "lasts $1 minutes"; "minutes_count_other" = "lasts $1 minutes";
"add_audio_to_club" = "Add audio to group";
"what_club_add" = "Which group do you want to add the song to?";
"group_has_audio" = "This group already has this song.";
"group_hasnt_audio" = "This group doesn't have this song.";
"by_name" = "by name";
"by_performer" = "by performer";
"no_access_clubs" = "There are no groups where you are an administrator.";
/* Notifications */ /* Notifications */
"feedback" = "Feedback"; "feedback" = "Feedback";

View file

@ -684,6 +684,7 @@
"audio" = "Аудиозапись"; "audio" = "Аудиозапись";
"playlist" = "Плейлист"; "playlist" = "Плейлист";
"upload_audio" = "Загрузить аудио"; "upload_audio" = "Загрузить аудио";
"upload_audio_to_group" = "Загрузить аудио в группу";
"performer" = "Исполнитель"; "performer" = "Исполнитель";
"audio_name" = "Название"; "audio_name" = "Название";
@ -734,6 +735,10 @@
"no_playlists_user" = "Этот пользователь ещё не добавлял плейлистов."; "no_playlists_user" = "Этот пользователь ещё не добавлял плейлистов.";
"no_playlists_club" = "Эта группа ещё не добавляла плейлистов."; "no_playlists_club" = "Эта группа ещё не добавляла плейлистов.";
"no_audios_thisuser" = "Вы ещё не добавляли аудиозаписей.";
"no_audios_user" = "Этот пользователь ещё не добавлял аудиозаписей.";
"no_audios_club" = "Эта группа ещё не добавляла аудиозаписей.";
"new_playlist" = "Новый плейлист"; "new_playlist" = "Новый плейлист";
"created_playlist" = "создан"; "created_playlist" = "создан";
"bookmark" = "Добавить в коллекцию"; "bookmark" = "Добавить в коллекцию";
@ -762,6 +767,15 @@
"minutes_count_many" = "длится $1 минут"; "minutes_count_many" = "длится $1 минут";
"minutes_count_other" = "длится $1 минут"; "minutes_count_other" = "длится $1 минут";
"add_audio_to_club" = "Добавить аудио в группу";
"what_club_add" = "В какую группу вы хотите добавить песню?";
"group_has_audio" = "У группы уже есть эта песня.";
"group_hasnt_audio" = "У группы нет этой песни.";
"by_name" = "по композициям";
"by_performer" = "по исполнителю";
"no_access_clubs" = "Нет групп, где вы являетесь администратором.";
/* Notifications */ /* Notifications */
"feedback" = "Ответы"; "feedback" = "Ответы";