- Переделан метод в классе user для получения друзей с проигрываемыми песнями. Теперь среди них могут появляться и группы (хз стоит ли оставлять это или нет). Так же больше не показываются удалённые пользователи
- Трек у плеера теперь двигается немного плавнее. Ещё теперь нету смешных багов с подсказкой времени, когда можно было увести её за экран или  промотать дальше трека. Переключить повторение трека теперь можно нажатием кнопки R.
- Длинное название трека больше не сносит время
- Наверное, теперь аудиозаписи нормально отображаются в темах midnight и modern
- Аудиозаписи больше не крашаются, если пользователь неавторизован.
- Немного переделан миниплеер.
- В миниплеере теперь громкость берётся из локалсторейджа.
- Улучшено редактирование аудиозаписей. Теперь данные в дата атрибуты нормально сохраняются, а так же слова песни и метка "explicit" меняются
- Удалён css, оставшийся ещё от public technical preview 1, а так же путь /audios{num}
- При наведении на трек теперь пропадает время, и на его месте появляются кнопки
- Стандартная аватарка в midnight теперь инвертируется
- В админке в редактировании аудио теперь показывается дата редактирования, дата создания, длина и оригинальный файл аудио. Так же на странице редактирования больше нет вылетов, если вы задали несуществующий аккаунт
This commit is contained in:
lalka2018 2023-11-02 20:56:02 +03:00
parent ef26b1044a
commit 5cd52c22f0
23 changed files with 363 additions and 232 deletions

View file

@ -359,7 +359,7 @@ final class Audio extends VKAPIRequestHandler
$this->fail(201, "Insufficient permissions to listen this audio");
$group = NULL;
if(!is_null($group)) {
if(!is_null($gid)) {
$group = (new Clubs)->get($gid);
if(!$group)
$this->fail(0404, "Not Found");
@ -396,7 +396,7 @@ final class Audio extends VKAPIRequestHandler
$this->fail(203,"Insufficient rights to this group");
$ids[] = $id;
$this->beacon($song->getId(), $id * -1);
$this->beacon($song ? $song->getId() : 0, $id * -1);
}
return $ids;
@ -409,40 +409,14 @@ final class Audio extends VKAPIRequestHandler
if(!in_array($filter, ["all", "friends", "groups"]))
$this->fail(8, "Invalid filter $filter");
$dbContext = DatabaseConnection::i()->getContext();
$entityIds = [];
$query = $dbContext->table("subscriptions")->select("model, target")
->where("follower", $this->getUser()->getId());
if($filter != "all")
$query = $query->where("model = ?", "openvk\\Web\\Models\\Entities\\" . ($filter == "groups" ? "Club" : "User"));
foreach($query as $_rel) {
$id = $_rel->target;
if($_rel->model === "openvk\\Web\\Models\\Entities\\Club")
$id *= -1;
$entityIds[] = $id;
}
$audioIds = [];
$threshold = $active === 0 ? 3600 : 120;
foreach($entityIds as $ent) {
$lastListen = $dbContext->table("audio_listens")->where("entity", $ent)
->where("time >= ?", time() - $threshold)->fetch();
if(!$lastListen)
continue;
$audio = (new Audios)->get($lastListen->audio);
$audioIds[$ent] = $this->toSafeAudioStruct($audio, $hash);
}
$broadcastList = $this->getUser()->getBroadcastList($filter);
$items = [];
foreach($audioIds as $ent => $audio) {
$entity = ($ent < 0 ? (new Groups($this->getUser())) : (new Users($this->getUser())))
->get((string) abs($ent));
foreach($broadcastList as $res) {
$struct = $res->toVkApiStruct();
$status = $res->getCurrentAudioStatus();
$entity->status_audio = $audio;
$struct->status_audio = $status ? $this->toSafeAudioStruct($status) : NULL;
$items[] = $struct;
}
return (object) [

View file

@ -305,19 +305,15 @@ class Audio extends Media
function listen($entity, Playlist $playlist = NULL): bool
{
$entityId = $entity->getId();
if($entity instanceof Club)
$entityId *= -1;
$listensTable = DatabaseConnection::i()->getContext()->table("audio_listens");
$lastListen = $listensTable->where([
"entity" => $entityId,
"entity" => $entity->getRealId(),
"audio" => $this->getId(),
])->order("index DESC")->fetch();
if(!$lastListen || (time() - $lastListen->time >= $this->getLength())) {
$listensTable->insert([
"entity" => $entityId,
"entity" => $entity->getRealId(),
"audio" => $this->getId(),
"time" => time(),
"playlist" => $playlist ? $playlist->getId() : NULL,
@ -331,11 +327,11 @@ class Audio extends Media
$playlist->incrementListens();
$playlist->save();
}
$entity->setLast_played_track($this->getId());
$entity->save();
}
$entity->setLast_played_track($this->getId());
$entity->save();
return true;
}

View file

@ -389,34 +389,39 @@ class Club extends RowModel
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
}
function getAudiosCollectionSize()
{
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);
}
function toVkApiStruct(?User $user = NULL): object
{
$res = [];
$res = (object)[];
$res->id = $this->getId();
$res->name = $this->getName();
$res->screen_name = $this->getShortCode();
$res->is_closed = 0;
$res->deactivated = NULL;
$res->is_admin = $this->canBeModifiedBy($user);
$res->is_admin = $user && $this->canBeModifiedBy($user);
if($this->canBeModifiedBy($user)) {
if($user && $this->canBeModifiedBy($user)) {
$res->admin_level = 3;
}
$res->is_member = $this->getSubscriptionStatus($user) ? 1 : 0;
$res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0;
$res->type = "group";
$res->photo_50 = $this->getAvatarUrl("miniscule");
$res->photo_100 = $this->getAvatarUrl("tiny");
$res->photo_200 = $this->getAvatarUrl("normal");
$res->can_create_topic = $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
$res->can_create_topic = $user && $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
$res->can_post = $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
$res->can_post = $user && $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
return (object) $res;
return $res;
}
use Traits\TBackDrops;

View file

@ -7,6 +7,7 @@ trait TAudioStatuses
{
function isBroadcastEnabled(): bool
{
if($this->getRealId() < 0) return true;
return (bool) $this->getRecord()->audio_broadcast_enabled;
}

View file

@ -1250,25 +1250,38 @@ class User extends RowModel
return $res;
}
function getFriendsAudios()
function getAudiosCollectionSize()
{
$friendsCount = $this->getFriendsCount();
$friends = $this->getFriends(max(rand(1, (int)ceil($friendsCount / 6)), 1), 6);
return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this);
}
$shuffleSeed = openssl_random_pseudo_bytes(6);
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
function getBroadcastList(string $filter = "friends", bool $shuffle = false)
{
$dbContext = DatabaseConnection::i()->getContext();
$entityIds = [];
$query = $dbContext->table("subscriptions")->where("follower", $this->getRealId());
if($filter != "all")
$query = $query->where("model = ?", "openvk\\Web\\Models\\Entities\\" . ($filter == "groups" ? "Club" : "User"));
foreach($query as $_rel) {
$entityIds[] = $_rel->model == "openvk\\Web\\Models\\Entities\\Club" ? $_rel->target * -1 : $_rel->target;
}
if($shuffle) {
$shuffleSeed = openssl_random_pseudo_bytes(6);
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
$entityIds = knuth_shuffle($entityIds, $shuffleSeed);
}
$friends = knuth_shuffle($friends, $shuffleSeed);
$returnArr = [];
foreach($friends as $friend) {
$returnArr[] = [
"id" => $friend->getRealId(),
"name" => $friend->getCanonicalName(),
"avatar" => $friend->getAvatarURL("miniscule"),
"tracksCount" => (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($friend),
"nowListening" => $friend->getCurrentAudioStatus(),
];
foreach($entityIds as $id) {
$entit = $id > 0 ? (new Users)->get($id) : (new Clubs)->get(abs($id));
if($id > 0 && $entit->isDeleted()) return;
$returnArr[] = $entit;
}
return $returnArr;

View file

@ -615,6 +615,12 @@ final class AdminPresenter extends OpenVKPresenter
$audio = $this->audios->get($audio_id);
$this->template->audio = $audio;
try {
$this->template->owner = $audio->getOwner()->getId();
} catch(\Throwable $e) {
$this->template->owner = 1;
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$audio->setName($this->postParam("name"));
$audio->setPerformer($this->postParam("performer"));

View file

@ -110,8 +110,8 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->mode = $mode;
$this->template->page = $page;
if(in_array($mode, ["list", "new", "popular"]))
$this->template->friendsAudios = $this->user->identity->getFriendsAudios();
if(in_array($mode, ["list", "new", "popular"]) && $this->user->identity)
$this->template->friendsAudios = $this->user->identity->getBroadcastList("all", true);
}
function renderEmbed(int $owner, int $id): void
@ -246,9 +246,11 @@ final class AudioPresenter extends OpenVKPresenter
function renderListen(int $id): void
{
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$this->assertUserLoggedIn();
$this->assertNoCSRF();
if(is_null($this->user))
$this->returnJson(["success" => false]);
$audio = $this->audios->get($id);
if ($audio && !$audio->isDeleted() && !$audio->isWithdrawn()) {

View file

@ -331,7 +331,7 @@ final class WallPresenter extends OpenVKPresenter
}
}
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) && sizeof($audios) < 1 && !$poll && !$note)
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1 && sizeof($audios) < 1 && !$poll && !$note)
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
try {

View file

@ -15,6 +15,14 @@
<label for="id">ID</label>
<input class="text medium-field" type="number" id="id" disabled value="{$audio->getId()}" />
</div>
<div class="field-group">
<label>{_created}</label>
{$audio->getPublicationTime()}
</div>
<div class="field-group">
<label>{_edited}</label>
{$audio->getEditTime() ?? "never"}
</div>
<div class="field-group">
<label for="name">{_name}</label>
<input class="text medium-field" type="text" id="name" name="name" value="{$audio->getTitle()}" />
@ -27,6 +35,10 @@
<label for="ext">{_lyrics}</label>
<textarea class="text medium-field" type="text" id="text" name="text" style="resize: vertical;">{$audio->getLyrics()}</textarea>
</div>
<div class="field-group">
<label>{_admin_audio_length}</label>
{$audio->getFormattedLength()}
</div>
<div class="field-group">
<label for="ext">{_genre}</label>
<select class="select medium-field" name="genre">
@ -36,10 +48,14 @@
</option>
</select>
</div>
<div class="field-group">
<label>{_admin_original_file}</label>
<audio controls src="{$audio->getOriginalURL(true)}">
</div>
<hr />
<div class="field-group">
<label for="owner">{_owner}</label>
<input class="text medium-field" type="number" id="owner_id" name="owner" value="{$audio->getOwner()->getId()}" />
<input class="text medium-field" type="number" id="owner_id" name="owner" value="{$owner}" />
</div>
<div class="field-group">
<label for="explicit">Explicit</label>

View file

@ -17,8 +17,10 @@
<div class="trackPanel">
<div class="trackInfo">
<b>{_track_unknown}</b>
<span>{_track_noname}</span>
<div class="trackName">
<b>{_track_unknown}</b>
<span>{_track_noname}</span>
</div>
<div class="timer" style="float:right">
<span class="time">00:00</span>
@ -27,7 +29,7 @@
</div>
</div>
<div class="track">
<div class="track" style="margin-top: -2px;">
<div class="bigPlayerTip">00:00</div>
<div class="selectableTrack">
<div style="width: 95%;position: relative;">&nbsp;
@ -39,7 +41,7 @@
<div class="volumePanel">
<div class="selectableTrack">
<div style="position: relative;width:73%">&nbsp;
<div style="position: relative;width:72%">&nbsp;
<div class="slider"></div>
</div>
</div>

View file

@ -5,57 +5,58 @@
<audio class="audio" />
<div id="miniplayer" class="audioEntry" style="min-height: 39px;">
<div class="playerButton">
<div class="playIcon"></div>
</div>
<div style="display: flex;">
<div class="playerButton">
<div class="playIcon"></div>
</div>
<div class="status" style="margin-top: 12px;">
<div class="mediaInfo" style="margin-bottom: -8px; cursor: pointer;display:flex;width: 85%;">
<div style="overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
<strong class="performer">
<a style="color: unset" href="/search?query=&type=audios&sort=id&only_performers=on&query={$audio->getPerformer()}">{ovk_proc_strtr($audio->getPerformer(), 50)}</a>
</strong>
<span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{ovk_proc_strtr($audio->getTitle(), 50)}</span>
<div class="status" style="margin-top: 12px;">
<div class="mediaInfo" style="margin-bottom: -8px; cursor: pointer;display:flex;width: 85%;">
<div style="overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
<strong class="performer">
<a href="/search?query=&type=audios&sort=id&only_performers=on&query={$audio->getPerformer()}">{ovk_proc_strtr($audio->getPerformer(), 50)}</a>
</strong>
<span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{ovk_proc_strtr($audio->getTitle(), 50)}</span>
</div>
<div class="explicitMark" n:if="$audio->isExplicit()"></div>
</div>
</div>
<div class="explicitMark" n:if="$audio->isExplicit()"></div>
<div class="volume" style="display: flex; flex-direction: column;width:14%;">
<span class="nobold" data-unformatted="{$audio->getLength()}" style="text-align: center;margin-top: 12px;">{$audio->getFormattedLength()}</span>
<div class="buttons" style="margin-top: 8px;">
{php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)}
{if !$hideButtons}
<div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && $hasAudio" ></div>
<div class="add-icon musicIcon hovermeicon" 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-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="report-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$editable && !$isWithdrawn" ></div>
{/if}
</div>
</div>
</div>
<div class="volume" style="display: flex; flex-direction: column;width:14%;">
<span class="nobold" data-unformatted="{$audio->getLength()}" style="text-align: center;margin-top: 12px;">{$audio->getFormattedLength()}</span>
<div class="buttons" style="margin-top: 8px;">
{php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)}
{if !$hideButtons}
<div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && $hasAudio" ></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="report-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$editable && !$isWithdrawn" ></div>
{/if}
</div>
</div>
</div>
<div style="display: flex;">
<div style="width: 100%;">
<div class="track lengthTrack" style="margin-top: 3px;display:none">
<div class="selectableTrack" style="width: 100%;" n:attr="style => $isWithdrawn ? 'display: none;' : ''">
<div style="position: relative;width: 94.8%;">
<div class="slider"></div>
<div class="subTracks">
<div style="width: 100%;">
<div class="track lengthTrack" style="margin-top: 3px;display:none">
<div class="selectableTrack" style="width: 100%;" n:attr="style => $isWithdrawn ? 'display: none;' : ''">
<div style="position: relative;width: calc(100% - 18px);">
<div class="slider"></div>
</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 style="width: 81px;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: calc(100% - 18px);">
<div class="slider"></div>
</div>
</div>
</div>
</div>

View file

@ -6,7 +6,7 @@
<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>
<hr>
<hr n:if="isset($thisUser)">
<a n:attr="id => $mode === 'playlists' && $ownerId == $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$thisUser->getId()}" n:if="isset($thisUser)">{_my_playlists}</a>
@ -17,19 +17,20 @@
<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) && isset($club) && $club->canUploadAudio($thisUser)">{_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 n:attr="id => $mode === 'playlists' && $ownerId != $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$ownerId}" n:if="isset($thisUser) && isset($ownerId) && !$isMy">{if $ownerId > 0}{_playlists_user}{else}{_playlists_club}{/if}</a>
<a href="/audios/newPlaylist{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser) && $isMyClub">{_new_playlist}</a>
{/if}
{if $friendsAudios}
<div class="friendsAudiosList">
<a href="/audios{$fr['id']}" style="width: 94%;padding-left: 10px;" n:foreach="$friendsAudios as $fr">
<a href="/audios{$fr->getRealId()}" style="width: 94%;padding-left: 10px;" n:foreach="$friendsAudios as $fr">
<div class="elem">
<img src="{$fr['avatar']}" />
<img src="{$fr->getAvatarURL()}" />
<div class="additionalInfo">
<span class="name">{$fr["name"]}</span>
<span class="desc">{$fr["nowListening"] ? $fr["nowListening"]->getName() : tr("audios_count", $fr["tracksCount"])}</span>
{php $audioStatus = $fr->getCurrentAudioStatus()}
<span class="name">{$fr->getCanonicalName()}</span>
<span class="desc">{$audioStatus ? $audioStatus->getName() : tr("audios_count", $fr->getAudiosCollectionSize())}</span>
</div>
</div>
</a>

View file

@ -249,8 +249,6 @@ routes:
handler: "Topics->edit"
- url: "/topic{num}_{num}/delete"
handler: "Topics->delete"
- url: "/audios{num}"
handler: "Audios->app"
- url: "/im"
handler: "Messenger->index"
- url: "/im/sel{num}"

View file

@ -160,6 +160,14 @@
font-size: 10px;
}
.bigPlayer .paddingLayer .trackInfo .trackName {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 81%;
display: inline-block;
}
.bigPlayer .paddingLayer .trackInfo .timer span {
font-size: 10px;
}
@ -169,64 +177,6 @@
cursor: pointer;
}
.music-app {
display: grid;
}
.music-app--player {
display: grid;
grid-template-columns: 32px 32px 32px 1fr;
padding: 8px;
border-bottom: 1px solid #c1c1c1;
border-bottom-style: solid;
border-bottom-style: dashed;
}
.music-app--player .play,
.music-app--player .perv,
.music-app--player .next {
-webkit-appearance: none;
-moz-appearance: none;
background-color: #507597;
color: #fff;
height: 20px;
margin: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
padding: 0;
font-size: 10px;
}
.music-app--player .info {
margin-left: 10px;
width: 550px;
}
.music-app--player .info .song-name * {
color: black;
}
.music-app--player .info .song-name time {
float: right;
}
.music-app--player .info .track {
margin-top: 8px;
height: 5px;
width: 70%;
background-color: #fff;
border-top: 1px solid #507597;
float: left;
}
.music-app--player .info .track .inner-track {
background-color: #507597;
height: inherit;
width: 15px;
opacity: .7;
}
.audioEmbed .track > .selectableTrack, .bigPlayer .selectableTrack {
margin-top: 3px;
width: calc(100% - 8px);
@ -237,6 +187,7 @@
}
#audioEmbed {
cursor: pointer;
user-select: none;
background: #eee;
height: 40px;
@ -251,7 +202,7 @@
}
.audioEntry.nowPlaying {
background: #787878;
background: #606060;
border: 1px solid #4f4f4f;
box-sizing: border-box;
}
@ -261,13 +212,17 @@
}
.audioEntry.nowPlaying:hover {
background: rgb(100, 100, 100) !important;
background: #4e4e4e !important;
}
.audioEntry.nowPlaying .performer a {
color: #f4f4f4 !important;
}
.audioEntry .performer a {
color: #4C4C4C;
}
.audioEntry.nowPlaying .title {
color: #fff;
}
@ -285,7 +240,6 @@
}
.audioEntry {
display: flex;
height: 100%;
position: relative;
width: 100%;
@ -298,6 +252,13 @@
height: 16px;
}
.audioEntry .subTracks {
display: flex;
padding-bottom: 5px;
padding-left: 8px;
padding-right: 12px;
}
.audioEntry .playerButton .playIcon {
background-image: url('/assets/packages/static/openvk/img/play_buttons.gif');
cursor: pointer;
@ -340,12 +301,16 @@
display: block;
}
.audioEntry:hover .volume span {
display: none;
}
.audioEntry .buttons {
display: none;
width: 62px;
height: 20px;
position: absolute;
right: 11%;
right: 3%;
top: 2px;
/* чтоб избежать заедания во время ховера кнопки добавления */
clip-path: inset(0 0 0 0);
@ -542,13 +507,13 @@
}
/* <center> 🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣*/
.audiosContainer center span {
.audiosDiv center span {
color: #707070;
margin: 120px 0px !important;
display: block;
}
.audiosContainer center {
.audiosDiv center {
margin-left: -10px;
}
@ -614,12 +579,15 @@
.friendsAudiosList .elem {
display: flex;
padding: 1px 1px;
width: 100%;
width: 148px;
}
.friendsAudiosList .elem img {
width: 30px;
border-radius: 2px;
object-fit: cover;
height: 31px;
min-width: 30px;
}
.friendsAudiosList .elem .additionalInfo {
@ -637,6 +605,10 @@
color: #2B587A;
}
.friendsAudiosList #used .elem .additionalInfo .name {
color: #F4F4F4;
}
.friendsAudiosList .elem .additionalInfo .desc {
text-overflow: ellipsis;
overflow: hidden;
@ -645,7 +617,16 @@
font-size: 11px;
}
.friendsAudiosList #used .elem .additionalInfo .desc {
color: #F4F4F4;
}
.friendsAudiosList .elem:hover {
background: #E8EBF0;
cursor: pointer;
}
.friendsAudiosList #used .elem:hover {
background: #787878;
cursor: pointer;
}

View file

@ -2514,7 +2514,7 @@ a.poll-retract-vote {
padding-top:5px;
padding-bottom:5px;
border: solid 0.125rem #4F4F4F;
background: #787878;
background: #606060;
margin-bottom:2px;
padding-left:9px;
width:87%;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 103 B

View file

@ -8,6 +8,7 @@ function fastError(message) {
MessageBox(tr("error"), message, [tr("ok")], [Function.noop])
}
// elapsed это вроде прошедшие, а оставшееся это remaining но ладно уже
function getElapsedTime(fullTime, time) {
let timer = fullTime - time
@ -150,7 +151,7 @@ class bigPlayer {
u(this.player()).on("timeupdate", (e) => {
const time = this.player().currentTime;
const ps = Math.ceil((time * 100) / this.tracks["currentTrack"].length);
const ps = ((time * 100) / this.tracks["currentTrack"].length).toFixed(3)
this.nodes["thisPlayer"].querySelector(".time").innerHTML = fmtTime(time)
this.timeType == 0 ? this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = getElapsedTime(this.tracks["currentTrack"].length, time)
: null
@ -182,7 +183,7 @@ class bigPlayer {
this.player().currentTime = time;
})
u(".bigPlayer .trackPanel .track").on("mousemove", (e) => {
u(".bigPlayer .trackPanel .selectableTrack").on("mousemove", (e) => {
if(this.tracks["currentTrack"] == null)
return
@ -232,7 +233,7 @@ class bigPlayer {
document.querySelector(".previousTrackTip").style.display = "block"
})
u(".bigPlayer .trackPanel .track").on("mouseleave", (e) => {
u(".bigPlayer .trackPanel .selectableTrack").on("mouseleave", (e) => {
if(this.tracks["currentTrack"] == null)
return
@ -252,7 +253,7 @@ class bigPlayer {
let rect = this.nodes["thisPlayer"].querySelector(".volumePanel .selectableTrack").getBoundingClientRect();
const width = e.clientX - rect.left;
const volume = (width * 1) / (rect.right - rect.left);
const volume = Math.max(0, (width * 1) / (rect.right - rect.left));
this.player().volume = volume;
})
@ -314,10 +315,10 @@ class bigPlayer {
u(document).on("keydown", (e) => {
if(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", " "].includes(e.key)) {
e.preventDefault()
if(document.querySelector(".ovk-diag-cont") != null)
return
e.preventDefault()
}
switch(e.key) {
@ -345,11 +346,11 @@ class bigPlayer {
})
u(document).on("keyup", (e) => {
if([87, 65, 83, 68].includes(e.keyCode)) {
e.preventDefault()
if([87, 65, 83, 68, 82].includes(e.keyCode)) {
if(document.querySelector(".ovk-diag-cont") != null)
return
e.preventDefault()
}
switch(e.keyCode) {
@ -361,6 +362,9 @@ class bigPlayer {
case 68:
this.showNextTrack()
break
case 82:
document.querySelector(".bigPlayer .additionalButtons .repeatButton").click()
break
}
})
@ -718,7 +722,7 @@ function initPlayer(id, keys, url, length) {
u(audio).on("timeupdate", () => {
const time = audio.currentTime;
const ps = Math.ceil((time * 100) / length);
const ps = ((time * 100) / length).toFixed(3);
volumeSpan.html(fmtTime(Math.floor(time)));
if (ps <= 100)
@ -811,6 +815,7 @@ function initPlayer(id, keys, url, length) {
audio.volume = volume;
});
audio.volume = localStorage.volume ?? 0.75
u(audio).trigger("volumechange")
}
@ -876,27 +881,44 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
perf.innerHTML = escapeHtml(response.new_info.performer)
perf.setAttribute("href", "/search?query=&type=audios&sort=id&only_performers=on&query="+response.new_info.performer)
e.currentTarget.setAttribute("data-performer", escapeHtml(response.new_info.performer))
e.target.setAttribute("data-performer", escapeHtml(response.new_info.performer))
let name = player.querySelector(".title")
name.innerHTML = escapeHtml(response.new_info.name)
e.currentTarget.setAttribute("data-title", escapeHtml(response.new_info.name))
e.target.setAttribute("data-title", escapeHtml(response.new_info.name))
if(player.querySelector(".lyrics") != null) {
player.querySelector(".lyrics").innerHTML = response.new_info.lyrics
player.querySelector(".title").classList.ad
if(response.new_info.lyrics_unformatted != "") {
if(player.querySelector(".lyrics") != null) {
player.querySelector(".lyrics").innerHTML = response.new_info.lyrics
player.querySelector(".title").classList.add("withLyrics")
} else {
player.insertAdjacentHTML("beforeend", `
<div class="lyrics" n:if="!empty($audio->getLyrics())">
${response.new_info.lyrics}
</div>
`)
player.querySelector(".title").classList.add("withLyrics")
}
} else {
player.insertAdjacentHTML("beforeend", `
<div class="lyrics" n:if="!empty($audio->getLyrics())">
${response.new_info.lyrics}
</div>
`)
$(player.querySelector(".lyrics")).remove()
player.querySelector(".title").classList.remove("withLyrics")
}
e.currentTarget.setAttribute("data-lyrics", response.new_info.lyrics_unformatted)
e.currentTarget.setAttribute("data-explicit", Number(response.new_info.explicit))
e.currentTarget.setAttribute("data-searchable", Number(!response.new_info.unlisted))
e.target.setAttribute("data-lyrics", response.new_info.lyrics_unformatted)
e.target.setAttribute("data-explicit", Number(response.new_info.explicit))
if(Number(response.new_info.explicit) == 1) {
if(!player.querySelector(".mediaInfo .explicitMark"))
player.querySelector(".mediaInfo").insertAdjacentHTML("beforeend", `
<div class="explicitMark"></div>
`)
} else {
$(player.querySelector(".mediaInfo .explicitMark")).remove()
}
e.target.setAttribute("data-searchable", Number(!response.new_info.unlisted))
player.setAttribute("data-genre", response.new_info.genre)
let url = new URL(location.href)
@ -1134,7 +1156,7 @@ $(document).on("click", "#_audioAttachment", (e) => {
document.querySelector(".ovk-diag-body").style.height = "335px"
let searcher = new playersSearcher("entity_audios", 0)
searcher.successCallback = (response, page) => {
searcher.successCallback = (response, thisc) => {
let domparser = new DOMParser()
let result = domparser.parseFromString(response, "text/html")
@ -1162,9 +1184,9 @@ $(document).on("click", "#_audioAttachment", (e) => {
u("#loader").remove()
if(this.page < pagesCount) {
if(thisc.page < pagesCount) {
document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", `
<div id="showMoreAudios" data-pagesCount="${pagesCount}" data-page="${this.page + 1}" style="width: 100%;text-align: center;background: #d5d5d5;height: 22px;padding-top: 9px;cursor:pointer;">
<div id="showMoreAudios" data-pagesCount="${pagesCount}" data-page="${thisc.page + 1}" class="showMore">
<span>more...</span>
</div>`)
}
@ -1280,7 +1302,7 @@ $(document).on("click", ".musicIcon.report-icon", (e) => {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + e.currentTarget.dataset.id + "?reason=" + res + "&type=audio", true);
xhr.open("GET", "/report/" + e.target.dataset.id + "?reason=" + res + "&type=audio", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);

View file

@ -92,4 +92,5 @@ CREATE TABLE IF NOT EXISTS `playlist_relations` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
ALTER TABLE `groups` ADD `everyone_can_upload_audios` TINYINT(1) NOT NULL DEFAULT '0' AFTER `backdrop_2`;
ALTER TABLE `profiles` ADD `last_played_track` BIGINT(20) UNSIGNED NULL DEFAULT NULL AFTER `client_name`, ADD `audio_broadcast_enabled` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `last_played_track`;
ALTER TABLE `profiles` ADD `last_played_track` BIGINT(20) UNSIGNED NULL DEFAULT NULL AFTER `client_name`, ADD `audio_broadcast_enabled` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `last_played_track`;
ALTER TABLE `groups` ADD `last_played_track` BIGINT(20) UNSIGNED NULL DEFAULT NULL AFTER `everyone_can_upload_audios`, ADD `audio_broadcast_enabled` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `last_played_track`;

View file

@ -1524,6 +1524,8 @@
"admin_gift_moved_successfully" = "Gift moved successfully";
"admin_gift_moved_to_recycle" = "This gift will now be in <b>Recycle Bin</b>.";
"admin_original_file" = "Original file";
"admin_audio_length" = "Length";
"logs" = "Logs";
"logs_anything" = "Anything";

View file

@ -1409,6 +1409,8 @@
"admin_gift_moved_successfully" = "Подарок успешно перемещён";
"admin_gift_moved_to_recycle" = "Теперь подарок находится в <b>корзине</b>.";
"admin_original_file" = "Оригинальный файл";
"admin_audio_length" = "Длина";
"logs" = "Логи";
"logs_anything" = "Любое";

View file

@ -235,7 +235,7 @@ input[type="radio"] {
}
.searchList #used {
background: linear-gradient(#453e5e,#473f61);
background: #463f60 !important;
}
#backdropEditor {
@ -247,11 +247,11 @@ input[type="radio"] {
background-color: rgb(30, 26, 43) !important;
}
.bigPlayer .selectableTrack {
.bigPlayer .selectableTrack, .audioEmbed .track > .selectableTrack {
border-top: #b9b9b9 1px solid !important;
}
.bigPlayer .paddingLayer .slider {
.bigPlayer .paddingLayer .slider, .audioEmbed .track .slider {
background: #b9b9b9 !important;
}
@ -280,3 +280,88 @@ input[type="radio"] {
.audioEntry:hover {
background: #19142D !important;
}
.audioEntry .performer a {
color: #a2a1a1 !important;
}
.musicIcon.lagged {
opacity: 49%;
}
.bigPlayer .paddingLayer .bigPlayerTip {
color: black !important;
}
.searchList a {
color: #bbb !important;
}
.searchList a:hover {
color: #eeeeee !important;
background: #332d46 !important;
}
.friendsAudiosList .elem:hover {
background: #332d46 !important;
}
.audioEntry .playerButton .playIcon {
filter: invert(81%);
}
img[src$='/assets/packages/static/openvk/img/camera_200.png'], img[src$='/assets/packages/static/openvk/img/song.jpg'] {
filter: invert(100%);
}
.audioStatus {
color: #8E8E8E !important;
}
.audioEntry .withLyrics {
color: #6f6497 !important;
}
#listensCount {
color: unset !important;
}
#upload_container, .whiteBox {
background: #1d1928 !important;
border: 1px solid #383052 !important;
}
ul {
color: #8b9ab5 !important;
}
#audio_upload {
border: 2px solid #383052 !important;
background-color: #262133 !important;
}
/* вот бы css в овк был бы написан на var()'ах( */
#upload_container.uploading {
background: #121017 url('/assets/packages/static/openvk/img/progressbar.gif') !important;
}
.musicIcon.pressed {
opacity: 41% !important;
}
.ovk-diag-body .searchBox {
background: #1e1a2b !important;
}
.audioEntry.nowPlaying .title {
color: #fff !important;
}
.attachAudio:hover {
background: #19142D !important;
cursor: pointer;
}
.showMore, .showMoreAudiosPlaylist {
background: #181826 !important;
}

View file

@ -279,18 +279,9 @@ input[type=checkbox] {
box-shadow: none;
}
.searchList #used
{
margin-left:0px;
color: white;
padding: 2px;
padding-top: 5px;
padding-bottom: 5px;
border: none;
background: #a4a4a4;
margin-bottom: 2px;
padding-left: 5px;
width: 90%;
.searchList #used {
background: #3c3c3c !important;
border: unset !important;
}
.searchList #used a
@ -337,3 +328,35 @@ input[type=checkbox] {
{
border-top: 1px solid #2f2f2f;
}
.musicIcon {
filter: contrast(202%) !important;
}
.audioEntry .playerButton .playIcon {
filter: contrast(7) !important;
}
.audioEmbed .track > .selectableTrack, .bigPlayer .selectableTrack {
border-top: #404040 1px solid !important;
}
.bigPlayer .paddingLayer .slider, .audioEmbed .track .slider {
background: #3c3c3c !important;
}
.audioEntry.nowPlaying {
background: #4b4b4b !important;
}
.audioEntry.nowPlaying:hover {
background: #373737 !important;
}
.musicIcon.pressed {
filter: brightness(150%) !important;
}
.musicIcon.lagged {
opacity: 50%;
}