Summary (required)

Description
This commit is contained in:
lalka2018 2023-10-19 13:05:17 +03:00
parent ac165c528a
commit 5486327842
26 changed files with 949 additions and 301 deletions

View file

@ -310,6 +310,17 @@ final class Audio extends VKAPIRequestHandler
} }
$items = []; $items = [];
if($owner_id > 0) {
$user = (new \openvk\Web\Models\Repositories\Users)->get($owner_id);
if(!$user)
$this->fail(50, "Invalid user");
if(!$user->getPrivacyPermission("audios.read", $this->getUser()))
$this->fail(15, "Access denied: this user chose to hide his audios");
}
$audios = (new Audios)->getByEntityID($owner_id, $offset, $count); $audios = (new Audios)->getByEntityID($owner_id, $offset, $count);
foreach($audios as $audio) foreach($audios as $audio)
$items[] = $this->toSafeAudioStruct($audio, $hash, $need_user == 1); $items[] = $this->toSafeAudioStruct($audio, $hash, $need_user == 1);

View file

@ -148,7 +148,7 @@ class Audio extends Media
function getName(): string function getName(): string
{ {
return $this->getTitle() . " - " . $this->getPerformer(); return $this->getPerformer() . "" . $this->getTitle();
} }
function getGenre(): ?string function getGenre(): ?string

View file

@ -372,6 +372,11 @@ class Club extends RowModel
return $this->getRecord()->alert; return $this->getRecord()->alert;
} }
function getRealId()
{
return $this->getId() * -1;
}
function toVkApiStruct(?User $user = NULL): object function toVkApiStruct(?User $user = NULL): object
{ {
$res = []; $res = [];

View file

@ -5,6 +5,7 @@ use Nette\Database\Table\ActiveRow;
use openvk\Web\Models\Repositories\Audios; use openvk\Web\Models\Repositories\Audios;
use openvk\Web\Models\Repositories\Photos; use openvk\Web\Models\Repositories\Photos;
use openvk\Web\Models\RowModel; use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\Photo;
/** /**
* @method setName(string $name) * @method setName(string $name)
@ -126,6 +127,16 @@ class Playlist extends MediaCollection
return $count > 0; return $count > 0;
} }
function getDescription(): ?string
{
return $this->getRecord()->description;
}
function getDescriptionHTML(): ?string
{
return htmlspecialchars($this->getRecord()->description, ENT_DISALLOWED | ENT_XHTML);
}
function toVkApiStruct(?User $user = NULL): object function toVkApiStruct(?User $user = NULL): object
{ {
$oid = $this->getOwner()->getId(); $oid = $this->getOwner()->getId();
@ -152,6 +163,13 @@ class Playlist extends MediaCollection
throw new \LogicException("Can't set length of playlist manually"); throw new \LogicException("Can't set length of playlist manually");
} }
function resetLength(): bool
{
$this->stateChanges("length", 0);
return true;
}
function delete(bool $softly = true): void function delete(bool $softly = true): void
{ {
$ctx = DatabaseConnection::i()->getContext(); $ctx = DatabaseConnection::i()->getContext();
@ -174,4 +192,34 @@ class Playlist extends MediaCollection
{ {
return $this->getRecord()->cover_photo_id; return $this->getRecord()->cover_photo_id;
} }
function canBeModifiedBy(User $user): bool
{
if(!$user)
return false;
if($this->getOwner() instanceof User)
return $user->getId() == $this->getOwner()->getId();
else
return $this->getOwner()->canBeModifiedBy($user);
}
function getLengthInMinutes(): int
{
return (int)round($this->getLength() / 60, PHP_ROUND_HALF_DOWN);
}
function fastMakeCover(int $owner, array $file)
{
$cover = new Photo;
$cover->setOwner($owner);
$cover->setDescription("Playlist cover image");
$cover->setFile($file);
$cover->setCreated(time());
$cover->save();
$this->setCover_photo_id($cover->getId());
return $cover;
}
} }

View file

@ -1228,6 +1228,11 @@ class User extends RowModel
return $response; return $response;
} }
function getRealId()
{
return $this->getId();
}
function toVkApiStruct(): object function toVkApiStruct(): object
{ {
$res = (object) []; $res = (object) [];

View file

@ -12,6 +12,7 @@ use openvk\Web\Models\Repositories\Users;
final class AudioPresenter extends OpenVKPresenter final class AudioPresenter extends OpenVKPresenter
{ {
private $audios; private $audios;
protected $presenterName = "audios";
const MAX_AUDIO_SIZE = 25000000; const MAX_AUDIO_SIZE = 25000000;
@ -20,14 +21,6 @@ final class AudioPresenter extends OpenVKPresenter
$this->audios = $audios; $this->audios = $audios;
} }
private function renderApp(string $playlistHandle): void
{
$this->assertUserLoggedIn();
$this->template->_template = "Audio/Player";
$this->template->handle = $playlistHandle;
}
function renderPopular(): void function renderPopular(): void
{ {
$this->renderList(NULL, "popular"); $this->renderList(NULL, "popular");
@ -91,7 +84,7 @@ final class AudioPresenter extends OpenVKPresenter
if(!$entity->getPrivacyPermission("audios.read", $this->user->identity)) if(!$entity->getPrivacyPermission("audios.read", $this->user->identity))
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); $this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
$playlists = $this->audios->getPlaylistsByUser($entity, $page, 10); $playlists = $this->audios->getPlaylistsByUser($entity, $page, 9);
$playlistsCount = $this->audios->getUserPlaylistsCount($entity); $playlistsCount = $this->audios->getUserPlaylistsCount($entity);
} }
@ -116,34 +109,6 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->page = $page; $this->template->page = $page;
} }
function renderView(int $owner, int $id): void
{
$this->assertUserLoggedIn();
$audio = $this->audios->getByOwnerAndVID($owner, $id);
if(!$audio || $audio->isDeleted())
$this->notFound();
if(!$audio->canBeViewedBy($this->user->identity))
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
if ($_SERVER["REQUEST_METHOD"] === "POST") {
switch ($this->queryParam("act")) {
case "remove":
DatabaseConnection::i()->getContext()->query("DELETE FROM `audio_relations` WHERE `entity` = ? AND `audio` = ?", $this->user->id, $audio->getId());
break;
case "edit":
break;
default:
$this->returnJson(["success" => false, "error" => "Action not implemented or not exists"]);
}
} else {
$this->renderApp("id=" . $audio->getId());
}
}
function renderEmbed(int $owner, int $id): void function renderEmbed(int $owner, int $id): void
{ {
$audio = $this->audios->getByOwnerAndVID($owner, $id); $audio = $this->audios->getByOwnerAndVID($owner, $id);
@ -239,6 +204,8 @@ final class AudioPresenter extends OpenVKPresenter
$listen = $audio->listen($this->user->identity); $listen = $audio->listen($this->user->identity);
$this->returnJson(["success" => $listen]); $this->returnJson(["success" => $listen]);
} }
$this->returnJson(["success" => false]);
} }
} }
@ -249,6 +216,9 @@ final class AudioPresenter extends OpenVKPresenter
function renderNewPlaylist(): void function renderNewPlaylist(): void
{ {
$this->assertUserLoggedIn();
$this->willExecuteWriteAction(true);
$owner = $this->user->id; $owner = $this->user->id;
if ($this->requestParam("owner")) { if ($this->requestParam("owner")) {
@ -266,27 +236,165 @@ final class AudioPresenter extends OpenVKPresenter
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");
$audios = !empty($this->postParam("audios")) ? explode(",", $this->postParam("audios")) : []; $audios = !empty($this->postParam("audios")) ? array_slice(explode(",", $this->postParam("audios")), 0, 100) : [];
if(empty($title) || iconv_strlen($title) < 1) if(empty($title) || iconv_strlen($title) < 1)
$this->flash("err", tr("error"), "ну там короч нету имени ну хз"); $this->flashFail("err", tr("error"), tr("set_playlist_name"));
$playlist = new Playlist; $playlist = new Playlist;
$playlist->setOwner($owner); $playlist->setOwner($owner);
$playlist->setName(substr($title, 0, 128)); $playlist->setName(substr($title, 0, 128));
$playlist->setDescription(substr($description, 0, 2048)); $playlist->setDescription(substr($description, 0, 2048));
if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo"));
try {
$playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
} catch(\ImagickException $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
}
}
$playlist->save(); $playlist->save();
foreach ($audios as $audio) { foreach($audios as $audio) {
DatabaseConnection::i()->getContext()->query("INSERT INTO `playlist_relations` (`collection`, `media`) VALUES (?, ?)", $playlist->getId(), $audio); $audio = $this->audios->get((int)$audio);
if(!$audio || $audio->isDeleted() || !$audio->canBeViewedBy($this->user->identity))
continue;
$playlist->add($audio);
} }
DatabaseConnection::i()->getContext()->query("INSERT INTO `playlist_imports` (`entity`, `playlist`) VALUES (?, ?)", $owner, $playlist->getId()); $playlist->bookmark(isset($club) ? $club : $this->user->identity);
$this->redirect("/playlist" . $owner . "_" . $playlist->getId()); $this->redirect("/playlist" . $owner . "_" . $playlist->getId());
} else {
if(isset($club)) {
$this->template->audios = iterator_to_array($this->audios->getByClub($club, 1, 10));
$count = (new Audios)->getClubCollectionSize($club);
} else { } else {
$this->template->audios = iterator_to_array($this->audios->getByUser($this->user->identity, 1, 10)); $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);
}
}
function renderPlaylistAction(int $id) {
$this->assertUserLoggedIn();
$this->willExecuteWriteAction(true);
$this->assertNoCSRF();
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
header("HTTP/1.1 405 Method Not Allowed");
exit(",");
}
$playlist = $this->audios->getPlaylist($id);
if(!$playlist || $playlist->isDeleted())
$this->flashFail("err", "error", tr("invalid_playlist"), null, true);
switch ($this->queryParam("act")) {
case "bookmark":
if(!$playlist->isBookmarkedBy($this->user->identity))
$playlist->bookmark($this->user->identity);
else
$this->flashFail("err", "error", tr("playlist_already_bookmarked"), null, true);
break;
case "unbookmark":
if($playlist->isBookmarkedBy($this->user->identity))
$playlist->unbookmark($this->user->identity);
else
$this->flashFail("err", "error", tr("playlist_not_bookmarked"), null, true);
break;
case "delete":
if($playlist->canBeModifiedBy($this->user->identity)) {
$tmOwner = $playlist->getOwner();
$playlist->delete();
} else
$this->flashFail("err", "error", tr("access_denied"), null, true);
$this->returnJson(["success" => true, "id" => $tmOwner->getRealId()]);
break;
default:
break;
}
$this->returnJson(["success" => true]);
}
function renderEditPlaylist(int $owner_id, int $virtual_id)
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
$playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id);
$page = (int)($this->queryParam("p") ?? 1);
if (!$playlist || $playlist->isDeleted() || !$playlist->canBeModifiedBy($this->user->identity))
$this->notFound();
$this->template->playlist = $playlist;
$this->template->page = $page;
$audios = iterator_to_array($playlist->getAudios());
$this->template->audios = array_slice($audios, 0, 10);
$audiosIds = [];
foreach($audios as $aud)
$audiosIds[] = $aud->getId();
$this->template->audiosIds = implode(",", array_unique($audiosIds)) . ",";
$this->template->ownerId = $owner_id;
$this->template->owner = $playlist->getOwner();
$this->template->pagesCount = $pagesCount = ceil($playlist->size() / 10);
if($_SERVER["REQUEST_METHOD"] !== "POST")
return;
$title = $this->postParam("title");
$description = $this->postParam("description");
$new_audios = !empty($this->postParam("audios")) ? explode(",", rtrim($this->postParam("audios"), ",")) : [];
if(empty($title) || iconv_strlen($title) < 1)
$this->flashFail("err", tr("error"), tr("set_playlist_name"));
$playlist->setName(ovk_proc_strtr($title, 128));
$playlist->setDescription(ovk_proc_strtr($description, 2048));
$playlist->resetLength();
if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["new_cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo"));
try {
$playlist->fastMakeCover($this->user->id, $_FILES["new_cover"]);
} catch(\Throwable $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
}
}
$playlist->save();
DatabaseConnection::i()->getContext()->table("playlist_relations")->where([
"collection" => $playlist->getId()
])->delete();
foreach ($new_audios as $new_audio) {
$audio = (new Audios)->get((int)$new_audio);
if(!$audio || $audio->isDeleted())
continue;
$playlist->add($audio);
}
$this->redirect("/playlist".$playlist->getPrettyId());
} }
function renderPlaylist(int $owner_id, int $virtual_id): void function renderPlaylist(int $owner_id, int $virtual_id): void
@ -299,57 +407,11 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->playlist = $playlist; $this->template->playlist = $playlist;
$this->template->page = $page; $this->template->page = $page;
$this->template->audios = iterator_to_array($playlist->fetch($page, 10)); $this->template->audios = iterator_to_array($playlist->fetch($page, 10));
$this->template->ownerId = $owner_id;
$this->template->owner = $playlist->getOwner();
$this->template->isBookmarked = $playlist->isBookmarkedBy($this->user->identity); $this->template->isBookmarked = $playlist->isBookmarkedBy($this->user->identity);
$this->template->isMy = $playlist->getOwner()->getId() === $this->user->id; $this->template->isMy = $playlist->getOwner()->getId() === $this->user->id;
$this->template->canEdit = ($this->template->isMy || ($playlist->getOwner() instanceof Club && $playlist->getOwner()->canBeModifiedBy($this->user->identity))); $this->template->canEdit = $playlist->canBeModifiedBy($this->user->identity);
/*if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (!$this->template->canEdit) {
$this->flashFail("err", tr("error"), tr("forbidden"));
}
$title = $this->postParam("title");
$description = $this->postParam("description");
$audios = !empty($this->postParam("audios")) ? explode(",", $this->postParam("audios")) : [];
$playlist->setName(substr($title, 0, 128));
$playlist->setDescription(substr($description, 0, 2048));
$playlist->setEdited(time());
if ($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
$photo = new Photo;
$photo->setOwner($this->user->id);
$photo->setDescription("Playlist #" . $playlist->getId() . " cover image");
$photo->setFile($_FILES["cover"]);
$photo->setCreated(time());
$photo->save();
$playlist->setCover_Photo_Id($photo->getId());
}
$playlist->save();
$_ids = [];
foreach ($playlist->getAudios() as $audio) {
$_ids[] = $audio->getId();
}
foreach ($playlist->getAudios() as $audio) {
if (!in_array($audio->getId(), $audios)) {
DatabaseConnection::i()->getContext()->query("DELETE FROM `playlist_relations` WHERE `collection` = ? AND `media` = ?", $playlist->getId(), $audio->getId());
}
}
foreach ($audios as $audio) {
if (!in_array($audio, $_ids)) {
DatabaseConnection::i()->getContext()->query("INSERT INTO `playlist_relations` (`collection`, `media`) VALUES (?, ?)", $playlist->getId(), $audio);
}
}
$this->flash("succ", tr("changes_saved"));
$this->redirect("/playlist" . $playlist->getOwner()->getId() . "_" . $playlist->getId());
}*/
} }
function renderAction(int $audio_id): void function renderAction(int $audio_id): void
@ -386,6 +448,13 @@ 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 "delete":
if($audio->canBeModifiedBy($this->user->identity))
$audio->delete();
else
$this->flashFail("err", "error", tr("access_denied"), null, true);
break; break;
case "edit": case "edit":
$audio = $this->audios->get($audio_id); $audio = $this->audios->get($audio_id);

View file

@ -129,7 +129,7 @@
<div align="right"> <div align="right">
{var $isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} {var $isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">&laquo;</a> <a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="/admin/music?act={($_GET['act'] ?? 'audios')}&p={($_GET['p'] ?? 1) - 1}">&laquo;</a>
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">&raquo;</a> <a n:if="$isLast" class="aui-button" href="/admin/music?act={($_GET['act'] ?? 'audios')}&p={($_GET['p'] ?? 1) + 1}">&raquo;</a>
</div> </div>
{/block} {/block}

View file

@ -0,0 +1,88 @@
{extends "../@layout.xml"}
{block title}{_edit_playlist}{/block}
{block header}
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
»
<a href="/audios{$ownerId}">{_audios}</a>
»
<a href="/playlist{$playlist->getPrettyId()}">{_playlist}</a>
»
{_edit_playlist}
{/block}
{block content}
<div class="playlistBlock" style="display: flex;margin-top: 0px;">
<div class="playlistCover">
<a>
<img src="{$playlist->getCoverURL('normal')}">
</a>
<div class="profile_links" style="width: 139px;">
<a class="profile_link" style="width: 98%;" id="_deletePlaylist" data-id="{$playlist->getId()}">{_delete_playlist}</a>
</div>
</div>
<div style="padding-left: 13px;width:75%">
<div class="playlistInfo">
<input value="{$playlist->getName()}" type="text" name="title">
</div>
<div class="moreInfo">
<textarea name="description" style="margin-top: 11px;">{$playlist->getDescription()}</textarea>
</div>
</div>
</div>
<div style="margin-top: 19px;">
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}">
<div class="playlistAudiosContainer" style="display:table;clear:both;width:100%;margin-top: 10px;">
<div id="newPlaylistAudios" n:foreach="$audios as $audio">
<div style="width: 78%;float: left;">
{include "player.xml", audio => $audio, hideButtons => true}
</div>
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}" style="width: 22%;">
<span>{_remove_from_playlist}</span>
</div>
</div>
</div>
<div class="showMoreAudiosPlaylist" data-page="2" data-playlist="{$playlist->getId()}" n:if="$pagesCount > 1">
{_show_more_audios}
</div>
</div>
<form method="post" id="editPlaylistForm" enctype="multipart/form-data">
<input type="hidden" name="title" maxlength="128" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<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>
<script>
document.querySelector("input[name='audios']").value = {$audiosIds}
u("#editPlaylistForm").on("submit", (e) => {
document.querySelector("#editPlaylistForm input[name='title']").value = document.querySelector(".playlistInfo input[name='title']").value
document.querySelector("#editPlaylistForm textarea[name='description']").value = document.querySelector(".playlistBlock textarea[name='description']").value
})
u("#editPlaylistForm input[name='new_cover']").on("change", (e) => {
let image = URL.createObjectURL(e.currentTarget.files[0])
document.querySelector(".playlistCover img").src = image
})
u(".playlistCover img").on("click", (e) => {
document.querySelector("input[name='new_cover']").click()
})
document.querySelector("#editPlaylistForm input[name='new_cover']").value = ""
</script>
{/block}

View file

@ -1,6 +1,16 @@
{extends "../@layout.xml"} {extends "../@layout.xml"}
{block title}{_audios}{/block} {block title}
{if $mode == 'list'}
{_audios}
{elseif $mode == 'new'}
{_audio_new}
{elseif $mode == 'popular'}
{_audio_popular}
{else}
{_playlists}
{/if}
{/block}
{block header} {block header}
<div n:if="$mode == 'list'"> <div n:if="$mode == 'list'">
@ -27,7 +37,7 @@
<div n:if="$mode == 'playlists'"> <div n:if="$mode == 'playlists'">
{_audios} {_audios}
» »
{_audio_popular} {if $isMy}{_my_playlists}{else}{_playlists}{/if}
</div> </div>
{/block} {/block}
@ -39,8 +49,9 @@
<input n:if="$mode == 'list'" type="hidden" name="bigplayer_context" data-type="entity_audios" data-entity="{$ownerId}" data-page="{$page}"> <input n:if="$mode == 'list'" type="hidden" name="bigplayer_context" data-type="entity_audios" data-entity="{$ownerId}" data-page="{$page}">
<input n:if="$mode == 'new'" type="hidden" name="bigplayer_context" data-type="new_audios" data-entity="0" data-page="1"> <input n:if="$mode == 'new'" type="hidden" name="bigplayer_context" data-type="new_audios" data-entity="0" data-page="1">
<input n:if="$mode == 'popular'" type="hidden" name="bigplayer_context" data-type="popular_audios" data-entity="0" data-page="1"> <input n:if="$mode == 'popular'" type="hidden" name="bigplayer_context" data-type="popular_audios" data-entity="0" data-page="1">
<div class="bigPlayerDetector"></div>
<div style="width: 100%;display: flex;margin-bottom: -10px;"> <div style="width: 100%;display: flex;margin-bottom: -10px;" class="audiosDiv">
<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">
@ -70,7 +81,7 @@
{include "../components/error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_playlists_thisuser") : tr("no_playlists_user")) : tr("no_playlists_club")} {include "../components/error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_playlists_thisuser") : tr("no_playlists_user")) : tr("no_playlists_club")}
</div> </div>
<div class="infContainer playlistContainer" style="display: flex;" n:if="$playlistsCount > 0"> <div class="infContainer playlistContainer" n:if="$playlistsCount > 0">
<div class="infObj" n:foreach="$playlists as $playlist"> <div class="infObj" n:foreach="$playlists as $playlist">
<a href="/playlist{$playlist->getPrettyId()}"> <a href="/playlist{$playlist->getPrettyId()}">
<div class="playlistCover"> <div class="playlistCover">
@ -78,7 +89,9 @@
</div> </div>
<div class="playlistInfo"> <div class="playlistInfo">
<span>{$playlist->getName()}</span> <span style="font-size: 12px">
{ovk_proc_strtr($playlist->getName(), 12)}
</span>
</div> </div>
</a> </a>
</div> </div>

View file

@ -28,19 +28,72 @@
} }
</style> </style>
<form method="post" id="newPlaylistForm"> <div class="playlistInfo">
<input type="text" name="title" placeholder="{_title}" maxlength="128" /> <input type="text" name="title" placeholder="{_title}" maxlength="128" />
<br /><br /> <br /><br />
<textarea placeholder="{_description}" name="description" maxlength="2048" /> <textarea placeholder="{_description}" name="description" maxlength="2048" />
<br /><br /> <br /><br />
<div n:if="sizeof($audios) > 0"> {_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>
<hr />
<div style="margin-top: 19px;">
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}">
<div class="playlistAudiosContainer" style="display:table;clear:both;width:100%;margin-top: 10px;">
<div id="newPlaylistAudios" n:foreach="$audios as $audio"> <div id="newPlaylistAudios" n:foreach="$audios as $audio">
{include "player.xml", audio => $audio, addToPlaylistButton => true} <div style="width: 78%;float: left;">
{include "player.xml", audio => $audio, hideButtons => true}
</div>
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}" style="width: 22%;">
<span>{_add_to_playlist}</span>
</div>
</div> </div>
</div> </div>
<div class="showMoreAudiosPlaylist" data-page="2" n:if="$pagesCount > 1">
{_show_more_audios}
</div>
</div>
</div>
<form method="post" id="newPlaylistForm" enctype="multipart/form-data">
<input type="hidden" name="title" maxlength="128" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
<button class="button" style="float: right;">{_create}</button> <textarea style="display:none;" name="description" maxlength="2048" />
<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> </form>
<script>
document.querySelector("input[name='audios']").value = ""
u("#newPlaylistForm").on("submit", (e) => {
document.querySelector("#newPlaylistForm input[name='title']").value = document.querySelector(".playlistInfo input[name='title']").value
document.querySelector("#newPlaylistForm textarea[name='description']").value = document.querySelector(".playlistInfo textarea[name='description']").value
})
u("#newPlaylistForm input[name='cover']").on("change", (e) => {
let image = URL.createObjectURL(e.currentTarget.files[0])
document.querySelector(".playlistCover img").src = image
document.querySelector(".playlistCover img").style.display = "block"
})
u(".playlistCover img").on("click", (e) => {
document.querySelector("#newPlaylistForm input[name='cover']").value = ""
e.currentTarget.href = ""
e.currentTarget.style.display = "none"
})
document.querySelector("#newPlaylistForm input[name='cover']").value = ""
</script>
{/block} {/block}

View file

@ -3,7 +3,9 @@
{block title}{_playlist}{/block} {block title}{_playlist}{/block}
{block header} {block header}
<a href="/audios{$thisUser->getId()}">{_audios}</a> <a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
»
<a href="/audios{$ownerId}">{_audios}</a>
» »
{_playlist} {_playlist}
{/block} {/block}
@ -17,13 +19,13 @@
<div class="playlistBlock"> <div class="playlistBlock">
<div class="playlistCover" style="float: left;"> <div class="playlistCover" style="float: left;">
<a href="{$playlist->getCoverURL()}" target="_blank"> <a href="{$playlist->getCoverURL()}" target="_blank">
<img src="{$playlist->getCoverURL('normal')}"> <img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}">
</a> </a>
<div class="profile_links" style="width: 139px;"> <div class="profile_links" style="width: 139px;">
<a id="profile_link" style="width: 98%;" n:if="$canEdit">{_edit_playlist}</a> <a class="profile_link" style="width: 98%;" href="/playlist{$playlist->getPrettyId()}/edit" n:if="$playlist->canBeModifiedBy($thisUser)">{_edit_playlist}</a>
<a id="profile_link" style="width: 98%;" id="bookmarkPlaylist" n:if="$isBookmarked">{_unbookmark}</a> <a class="profile_link" style="width: 98%;" id="bookmarkPlaylist" data-id="{$playlist->getId()}" n:if="!$isBookmarked">{_bookmark}</a>
<a id="profile_link" style="width: 98%;" id="unbookmarkPlaylist" n:if="!$isBookmarked">{_bookmark}</a> <a class="profile_link" style="width: 98%;" id="unbookmarkPlaylist" data-id="{$playlist->getId()}" n:if="$isBookmarked">{_unbookmark}</a>
</div> </div>
</div> </div>
@ -32,11 +34,15 @@
<h4 style="border-bottom:unset;">{$playlist->getName()}</h4> <h4 style="border-bottom:unset;">{$playlist->getName()}</h4>
<div class="moreInfo"> <div class="moreInfo">
{php $length = $playlist->getLengthInMinutes()}
<span>{tr("audios_count", $count)}</span> <span>{tr("audios_count", $count)}</span>
<span>{_created_playlist}</span> {$playlist->getPublicationTime()} <span>{_created_playlist}</span> {$playlist->getPublicationTime()}
{if $length > 0} •
<span>{tr("minutes_count", $length)}</span>
{/if}
<div style="margin-top: 11px;"> <div style="margin-top: 11px;">
<span>{$playlist->getDescription()}</span> <span>{nl2br($playlist->getDescriptionHTML())|noescape}</span>
</div> </div>
<hr style="color: #f7f7f7;"> <hr style="color: #f7f7f7;">
</div> </div>

View file

@ -42,7 +42,7 @@
</form> </form>
</div><br/> </div><br/>
<span>{_you_can_also_add_audio_using} <b><a href="/player">{_search_audio_inst}</a></b>.<span> <span>{_you_can_also_add_audio_using} <b><a href="/search?type=audios">{_search_audio_inst}</a></b>.<span>
</div> </div>
<div id="lastStep" style="display:none;"> <div id="lastStep" style="display:none;">

View file

@ -34,7 +34,7 @@
<div class="volumePanel"> <div class="volumePanel">
<div class="selectableTrack"> <div class="selectableTrack">
<div style="position: relative;width:74%">&nbsp; <div style="position: relative;width:73%">&nbsp;
<div class="slider"></div> <div class="slider"></div>
</div> </div>
</div> </div>

View file

@ -4,22 +4,22 @@
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}" data-name="{$audio->getName()}"{/if} data-genre="{$audio->getGenre()}" class="audioEmbed {if !$audio->isAvailable()}processed{/if} {if $isWithdrawn}withdrawn{/if}" onmouseenter="!this.classList.contains('inited') ? initPlayer({$id}, {$audio->getKeys()}, {$audio->getURL()}, {$audio->getLength()}) : void(0)"> <div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}" data-name="{$audio->getName()}"{/if} data-genre="{$audio->getGenre()}" class="audioEmbed {if !$audio->isAvailable()}processed{/if} {if $isWithdrawn}withdrawn{/if}" onmouseenter="!this.classList.contains('inited') ? initPlayer({$id}, {$audio->getKeys()}, {$audio->getURL()}, {$audio->getLength()}) : void(0)">
<audio class="audio" /> <audio class="audio" />
<div id="miniplayer" class="audioEntry" style="min-height: 37px;"> <div id="miniplayer" class="audioEntry" style="min-height: 39px;">
<div class="playerButton"> <div class="playerButton">
<div class="playIcon"></div> <div class="playIcon"></div>
</div> </div>
<div class="status" style="margin-top: 11px;"> <div class="status" style="margin-top: 12px;">
<div class="mediaInfo" style="margin-bottom: -8px; cursor: pointer;"> <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"> <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> <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> </strong>
<span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{ovk_proc_strtr($audio->getTitle(), 50)}</span> <span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{ovk_proc_strtr($audio->getTitle(), 50)}</span>
<svg n:if="$audio->isExplicit()" xmlns="http://www.w3.org/2000/svg" height="11" viewBox="0 0 11 11" width="11"> </div>
<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"/> <div class="explicitMark" n:if="$audio->isExplicit()"></div>
</svg>
</div> </div>
</div> </div>

View file

@ -1,17 +1,17 @@
<div class="searchOptions newer"> <div class="searchOptions newer">
<ul class="searchList"> <div class="searchList" style="margin-top:10px">
<a n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}" n:if="isset($thisUser)">{_my_music}</a> <a n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}" n:if="isset($thisUser)">{_my_music}</a>
<a n:if="!$isMy && $mode === 'list'" id="used" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
<a href="/player/upload{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser)">{_upload_audio}</a> <a href="/player/upload{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser)">{_upload_audio}</a>
<a n:attr="id => $mode === 'playlists' ? 'used' : 'ki'" href="/playlists{$thisUser->getId()}" n:if="isset($thisUser)">{_my_playlists}</a>
<a n:if="$mode === 'playlists' && isset($thisUser)" href="/audios/newPlaylist{if $isMyClub}?owner={abs($ownerId)}{/if}">{_new_playlist}</a>
<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>
</ul> <a n:if="!$isMy && $mode === 'list'" id="used" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
<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{$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>
</div>
</div> </div>

View file

@ -365,7 +365,7 @@
<div class="searchOption"> <div class="searchOption">
<div class="searchOptionName" id="n_main_audio" onclick="hideParams('main_audio')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_s_main}</div> <div class="searchOptionName" id="n_main_audio" onclick="hideParams('main_audio')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_s_main}</div>
<div class="searchOptionBlock" id="s_main_audio"> <div class="searchOptionBlock" id="s_main_audio">
<label><input type="checkbox" name="only_performers" n:attr="checked => !empty($_GET['only_performers'])" form="searcher">{_s_only_performers}</label> <label><input type="checkbox" name="only_performers" n:attr="checked => !empty($_GET['only_performers'])" form="searcher">{_s_only_performers}</label><br>
<label><input type="checkbox" name="with_lyrics" n:attr="checked => !empty($_GET['with_lyrics'])" form="searcher">{_s_with_lyrics}</label> <label><input type="checkbox" name="with_lyrics" n:attr="checked => !empty($_GET['with_lyrics'])" form="searcher">{_s_with_lyrics}</label>
</div> </div>
</div> </div>

View file

@ -389,7 +389,7 @@
<span class="nobold">{_privacy_setting_view_audio}</span> <span class="nobold">{_privacy_setting_view_audio}</span>
</td> </td>
<td> <td>
<select name="audios.read", style="width: 164px;"> <select name="audios.read" style="width: 164px;">
<option value="2" {if $user->getPrivacySetting('audios.read') == 2}selected{/if}>{_privacy_value_anybody}</option> <option value="2" {if $user->getPrivacySetting('audios.read') == 2}selected{/if}>{_privacy_value_anybody}</option>
<option value="1" {if $user->getPrivacySetting('audios.read') == 1}selected{/if}>{_privacy_value_friends}</option> <option value="1" {if $user->getPrivacySetting('audios.read') == 1}selected{/if}>{_privacy_value_friends}</option>
<option value="0" {if $user->getPrivacySetting('audios.read') == 0}selected{/if}>{_privacy_value_nobody}</option> <option value="0" {if $user->getPrivacySetting('audios.read') == 0}selected{/if}>{_privacy_value_nobody}</option>
@ -618,7 +618,7 @@
name="menu_muziko" /> name="menu_muziko" />
</td> </td>
<td> <td>
<span class="nobold">Мои Аудиозаписи</span> <span class="nobold">{_my_audios}</span>
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -187,8 +187,6 @@ routes:
handler: "Videos->edit" handler: "Videos->edit"
- url: "/video{num}_{num}/remove" - url: "/video{num}_{num}/remove"
handler: "Videos->remove" handler: "Videos->remove"
- url: "/player"
handler: "Audio->app"
- url: "/player/upload" - url: "/player/upload"
handler: "Audio->upload" handler: "Audio->upload"
- url: "/audios{num}" - url: "/audios{num}"
@ -197,8 +195,6 @@ routes:
handler: "Audio->popular" handler: "Audio->popular"
- url: "/audios/new" - url: "/audios/new"
handler: "Audio->new" handler: "Audio->new"
- url: "/audio{num}_{num}"
handler: "Audio->view"
- url: "/audio{num}_{num}/embed.xhtml" - url: "/audio{num}_{num}/embed.xhtml"
handler: "Audio->embed" handler: "Audio->embed"
- url: "/audio{num}/listen" - url: "/audio{num}/listen"
@ -211,6 +207,10 @@ routes:
handler: "Audio->apiGetContext" handler: "Audio->apiGetContext"
- url: "/playlist{num}_{num}" - url: "/playlist{num}_{num}"
handler: "Audio->playlist" handler: "Audio->playlist"
- url: "/playlist{num}_{num}/edit"
handler: "Audio->editPlaylist"
- url: "/playlist{num}/action"
handler: "Audio->playlistAction"
- url: "/playlists{num}" - url: "/playlists{num}"
handler: "Audio->playlists" handler: "Audio->playlists"
- url: "/audio{num}/action" - url: "/audio{num}/action"

View file

@ -4,6 +4,10 @@
cursor: pointer; cursor: pointer;
} }
.musicIcon:hover {
filter: brightness(99%);
}
.musicIcon.pressed { .musicIcon.pressed {
filter: brightness(150%); filter: brightness(150%);
} }
@ -18,6 +22,13 @@
box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2); box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2);
} }
.bigPlayer.floating {
position: fixed;
z-index: 199;
width: 627px;
margin-top: -76px;
}
.bigPlayer .paddingLayer { .bigPlayer .paddingLayer {
padding: 0px 0px 0px 14px; padding: 0px 0px 0px 14px;
} }
@ -53,31 +64,50 @@
.bigPlayer .paddingLayer .additionalButtons { .bigPlayer .paddingLayer .additionalButtons {
float: left; float: left;
margin-top: -6px; margin-top: -6px;
width: 12%; width: 11%;
} }
.bigPlayer .paddingLayer .additionalButtons .repeatButton { .bigPlayer .paddingLayer .additionalButtons .repeatButton {
width: 16px; width: 14px;
height: 16px; height: 16px;
background-position-y: -49px; background-position-y: -49px;
background-position-x: -31px; background-position-x: -31px;
margin-left: 7px;
float: left;
}
.broadcastButton {
width: 16px;
height: 12px;
background-position-y: -50px;
background-position-x: -64px;
margin-left: 6px; margin-left: 6px;
float: left; float: left;
} }
.broadcastButton.atProfile {
width: 13px;
height: 12px;
background-position-y: -50px;
background-position-x: -64px;
margin-left: 0px !important;
margin-right: 5px;
float: left;
}
.bigPlayer .paddingLayer .additionalButtons .shuffleButton { .bigPlayer .paddingLayer .additionalButtons .shuffleButton {
width: 16px; width: 14px;
height: 16px; height: 16px;
background-position: -50px -50px; background-position: -50px -50px;
margin-left: 6px; margin-left: 7px;
float: left; float: left;
} }
.bigPlayer .paddingLayer .additionalButtons .deviceButton { .bigPlayer .paddingLayer .additionalButtons .deviceButton {
width: 12px; width: 12px;
height: 16px; height: 16px;
background-position: -202px -51px; background-position: -202px -50px;
margin-left: 6px; margin-left: 7px;
float: left; float: left;
} }
@ -92,7 +122,7 @@
float: left; float: left;
margin-top: -13px; margin-top: -13px;
margin-left: 13px; margin-left: 13px;
width: 386px; width: 63%;
position: relative; position: relative;
} }
@ -222,6 +252,7 @@
.audioEntry.nowPlaying { .audioEntry.nowPlaying {
background: #787878; background: #787878;
border: 1px solid #4f4f4f; border: 1px solid #4f4f4f;
box-sizing: border-box;
} }
.audioEntry.nowPlaying:hover { .audioEntry.nowPlaying:hover {
@ -244,7 +275,7 @@
color: white !important; color: white !important;
} }
.audioEntry.nowPlaying .buttons .musicIcon, .audioEntry.nowPlaying svg { .audioEntry.nowPlaying .buttons .musicIcon, .audioEntry.nowPlaying .explicitMark {
filter: brightness(187%) opacity(72%); filter: brightness(187%) opacity(72%);
} }
@ -257,7 +288,7 @@
.audioEntry .playerButton { .audioEntry .playerButton {
position: relative; position: relative;
padding: 9px; padding: 10px 9px 9px 9px;
width: 16px; width: 16px;
height: 16px; height: 16px;
} }
@ -309,7 +340,7 @@
width: 62px; width: 62px;
height: 20px; height: 20px;
position: absolute; position: absolute;
right: 10%; right: 11%;
top: 2px; top: 2px;
} }
@ -359,7 +390,7 @@
.audioEmbed .lyrics { .audioEmbed .lyrics {
display: none; display: none;
padding: 0px 33px; padding: 6px 33px 10px 33px;
} }
.audioEmbed .lyrics.showed { .audioEmbed .lyrics.showed {
@ -384,8 +415,8 @@
} }
.playlistCover img { .playlistCover img {
width: 135px; max-width: 135px;
height: 135px; max-height: 135px;
} }
.playlistBlock { .playlistBlock {
@ -394,19 +425,27 @@
.playlistContainer { .playlistContainer {
display: flex; display: flex;
flex-wrap: wrap;
margin-top: -15px;
margin-left: -30px;
}
.playlistContainer .playlistCover {
width: 111px;
height: 111px;
display: flex;
background: #c4c4c4;
} }
.playlistContainer .playlistCover img { .playlistContainer .playlistCover img {
width:111px; max-width: 111px;
height:111px; max-height: 111px;
margin: auto;
} }
.playlistContainer .infObj { .playlistContainer .infObj {
margin-left: 15px; margin-left: 39px;
} margin-top: 19px;
.playlistContainer .infObj:first-of-type {
margin-left: 0px;
} }
.ovk-diag-body .searchBox { .ovk-diag-body .searchBox {
@ -427,7 +466,7 @@
overflow-y: auto; overflow-y: auto;
} }
.ovk-diag-body .attachAudio { .attachAudio {
float: left; float: left;
width: 28%; width: 28%;
height: 26px; height: 26px;
@ -435,11 +474,38 @@
text-align: center; text-align: center;
} }
.ovk-diag-body .attachAudio span { .attachAudio span {
user-select: none; user-select: none;
} }
.ovk-diag-body .attachAudio:hover { .attachAudio:hover {
background: rgb(236, 236, 236); background: rgb(236, 236, 236);
cursor: pointer; cursor: pointer;
} }
.playlistCover img {
cursor: pointer;
}
.explicitMark {
margin-top: 2px;
margin-left: 3px;
width: 11px;
height: 11px;
background: url('/assets/packages/static/openvk/img/explicit.svg');
background-repeat: no-repeat;
}
.audioStatus span {
color: #2B587A;
}
.audioStatus span:hover {
text-decoration: underline;
cursor: pointer;
}
.audioStatus {
padding-top: 2px;
padding-bottom: 3px;
}

View file

@ -2512,8 +2512,8 @@ a.poll-retract-vote {
padding:2px; padding:2px;
padding-top:5px; padding-top:5px;
padding-bottom:5px; padding-bottom:5px;
border: solid 0.125rem #696969; border: solid 0.125rem #4F4F4F;
background:linear-gradient(#888888,#858585); background: #787878;
margin-bottom:2px; margin-bottom:2px;
padding-left:9px; padding-left:9px;
width:87%; width:87%;
@ -2552,16 +2552,16 @@ a.poll-retract-vote {
min-width: 88%; min-width: 88%;
} }
.searchList li:hover, .searchList a:hover { .searchList a:hover {
margin-left:0px; margin-left: 0px;
color: #2B587A !important; color: #2B587A !important;
background:#ebebeb; background: #ebebeb;
padding:2px; padding: 2px;
padding-top:5px; padding-top: 5px;
padding-bottom:5px; padding-bottom: 5px;
margin-bottom:2px; margin-bottom: 2px;
padding-left:9px; padding-left: 9px;
width:91%; width: 89.9%;
} }
.whatFind { .whatFind {
@ -2576,8 +2576,7 @@ a.poll-retract-vote {
margin-top: 0.5px; margin-top: 0.5px;
} }
.searchOptionName .searchOptionName {
{
cursor:pointer; cursor:pointer;
background-color: #EAEAEA; background-color: #EAEAEA;
padding-left:5px; padding-left:5px;
@ -2589,8 +2588,7 @@ a.poll-retract-vote {
border-bottom: 2px solid #E4E4E4; border-bottom: 2px solid #E4E4E4;
} }
.searchOption .searchOption {
{
user-select: none; user-select: none;
} }
@ -2973,3 +2971,22 @@ body.article .floating_sidebar, body.article .page_content {
border-left: 1px solid #d8d8d8; border-left: 1px solid #d8d8d8;
width: 26% !important; width: 26% !important;
} }
hr {
color: #d0cdcd;
}
.searchList hr {
width: 153px;
margin-left: 0px;
margin-top: 6px;
}
.showMore, .showMoreAudiosPlaylist {
width: 100%;
text-align: center;
background: #d5d5d5;
height: 22px;
padding-top: 9px;
cursor: pointer;
}

View file

@ -14,6 +14,8 @@ function getElapsedTime(fullTime, time) {
return "-" + fmtTime(timer) return "-" + fmtTime(timer)
} }
window.savedAudiosPages = {}
class bigPlayer { class bigPlayer {
tracks = { tracks = {
currentTrack: null, currentTrack: null,
@ -90,11 +92,10 @@ class bigPlayer {
}) })
u(this.nodes["playButtons"].querySelector(".playButton")).on("click", (e) => { u(this.nodes["playButtons"].querySelector(".playButton")).on("click", (e) => {
if(this.player().paused) { if(this.player().paused)
this.play() this.play()
} else { else
this.pause() this.pause()
}
}) })
u(this.player()).on("timeupdate", (e) => { u(this.player()).on("timeupdate", (e) => {
@ -120,9 +121,8 @@ class bigPlayer {
}) })
u(".bigPlayer .track > div").on("click mouseup", (e) => { u(".bigPlayer .track > div").on("click mouseup", (e) => {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
let rect = this.nodes["thisPlayer"].querySelector(".selectableTrack").getBoundingClientRect(); let rect = this.nodes["thisPlayer"].querySelector(".selectableTrack").getBoundingClientRect();
@ -133,9 +133,8 @@ class bigPlayer {
}) })
u(".bigPlayer .trackPanel .track").on("mousemove", (e) => { u(".bigPlayer .trackPanel .track").on("mousemove", (e) => {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
let rect = this.nodes["thisPlayer"].querySelector(".selectableTrack").getBoundingClientRect(); let rect = this.nodes["thisPlayer"].querySelector(".selectableTrack").getBoundingClientRect();
@ -144,21 +143,19 @@ class bigPlayer {
document.querySelector(".bigPlayer .track .timeTip").style.display = "block" document.querySelector(".bigPlayer .track .timeTip").style.display = "block"
document.querySelector(".bigPlayer .track .timeTip").innerHTML = fmtTime(time) document.querySelector(".bigPlayer .track .timeTip").innerHTML = fmtTime(time)
document.querySelector(".bigPlayer .track .timeTip").style.left = `min(${width - 15}px, 317.5px)` document.querySelector(".bigPlayer .track .timeTip").style.left = `min(${width - 15}px, 315.5px)`
}) })
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 .timeTip").style.display = "none"
}) })
u(".bigPlayer .volumePanel > div").on("click mouseup mousemove", (e) => { u(".bigPlayer .volumePanel > div").on("click mouseup mousemove", (e) => {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
if(e.type == "mousemove") { if(e.type == "mousemove") {
let buttonsPresseed = _bsdnUnwrapBitMask(e.buttons) let buttonsPresseed = _bsdnUnwrapBitMask(e.buttons)
@ -175,9 +172,8 @@ class bigPlayer {
}) })
u(".bigPlayer .elapsedTime").on("click", (e) => { u(".bigPlayer .elapsedTime").on("click", (e) => {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
this.timeType == 0 ? (this.timeType = 1) : (this.timeType = 0) this.timeType == 0 ? (this.timeType = 1) : (this.timeType = 0)
@ -189,23 +185,20 @@ class bigPlayer {
}) })
u(".bigPlayer .additionalButtons .repeatButton").on("click", (e) => { u(".bigPlayer .additionalButtons .repeatButton").on("click", (e) => {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
e.currentTarget.classList.toggle("pressed") e.currentTarget.classList.toggle("pressed")
if(e.currentTarget.classList.contains("pressed")) { if(e.currentTarget.classList.contains("pressed"))
this.player().loop = true this.player().loop = true
} else { else
this.player().loop = false this.player().loop = false
}
}) })
u(".bigPlayer .additionalButtons .shuffleButton").on("click", (e) => { u(".bigPlayer .additionalButtons .shuffleButton").on("click", (e) => {
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.5)
this.setTrack(this.tracks["tracks"].at(0).id) this.setTrack(this.tracks["tracks"].at(0).id)
@ -213,17 +206,15 @@ class bigPlayer {
// хз что она делала в самом вк, но тут сделаем вид что это просто мут музыки // хз что она делала в самом вк, но тут сделаем вид что это просто мут музыки
u(".bigPlayer .additionalButtons .deviceButton").on("click", (e) => { u(".bigPlayer .additionalButtons .deviceButton").on("click", (e) => {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
e.currentTarget.classList.toggle("pressed") e.currentTarget.classList.toggle("pressed")
if(e.currentTarget.classList.contains("pressed")) { if(e.currentTarget.classList.contains("pressed"))
this.player().muted = true this.player().muted = true
} else { else
this.player().muted = false this.player().muted = false
}
}) })
u(".bigPlayer .arrowsButtons .nextButton").on("click", (e) => { u(".bigPlayer .arrowsButtons .nextButton").on("click", (e) => {
@ -235,13 +226,18 @@ class bigPlayer {
}) })
u(document).on("keydown", (e) => { u(document).on("keydown", (e) => {
if(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
e.preventDefault()
if(document.querySelector(".ovk-diag-cont") != null)
return
}
switch(e.key) { switch(e.key) {
case "ArrowUp": case "ArrowUp":
e.preventDefault()
this.player().volume = Math.min(0.99, this.player().volume + 0.1) this.player().volume = Math.min(0.99, this.player().volume + 0.1)
break break
case "ArrowDown": case "ArrowDown":
e.preventDefault()
this.player().volume = Math.max(0, this.player().volume - 0.1) this.player().volume = Math.max(0, this.player().volume - 0.1)
break break
case "ArrowLeft": case "ArrowLeft":
@ -254,9 +250,15 @@ class bigPlayer {
}) })
u(document).on("keyup", (e) => { u(document).on("keyup", (e) => {
if([32, 87, 65, 83, 68].includes(e.keyCode)) {
e.preventDefault()
if(document.querySelector(".ovk-diag-cont") != null)
return
}
switch(e.keyCode) { switch(e.keyCode) {
case 32: case 32:
e.preventDefault()
if(this.player().paused) if(this.player().paused)
this.play() this.play()
else else
@ -265,12 +267,10 @@ class bigPlayer {
break break
case 87: case 87:
case 65: case 65:
e.preventDefault()
this.showPreviousTrack() this.showPreviousTrack()
break break
case 83: case 83:
case 68: case 68:
e.preventDefault()
this.showNextTrack() this.showNextTrack()
break break
} }
@ -282,30 +282,37 @@ class bigPlayer {
this.showNextTrack() this.showNextTrack()
}) })
if(localStorage.volume != null && localStorage.volume < 1 && localStorage.volume > 0) { if(localStorage.volume != null && localStorage.volume < 1 && localStorage.volume > 0)
this.player().volume = localStorage.volume this.player().volume = localStorage.volume
} else { else
this.player().volume = 0.75 this.player().volume = 0.75
}
// В хромиумах - 'null', а в фурифоксах - null. Гыгы. if(localStorage.playerTimeType == 'null' || localStorage.playerTimeType == null)
if(localStorage.playerTimeType == 'null' || localStorage.playerTimeType == null) {
this.timeType = 0 this.timeType = 0
} else { else
this.timeType = localStorage.playerTimeType this.timeType = localStorage.playerTimeType
}
navigator.mediaSession.setActionHandler('play', () => { this.play() });
navigator.mediaSession.setActionHandler('pause', () => { this.pause() });
navigator.mediaSession.setActionHandler('previoustrack', () => { this.showPreviousTrack() });
navigator.mediaSession.setActionHandler('nexttrack', () => { this.showNextTrack() });
navigator.mediaSession.setActionHandler("seekto", (details) => {
this.player().currentTime = details.seekTime;
});
} }
play() { play() {
if(this.tracks["currentTrack"] == null) { if(this.tracks["currentTrack"] == null)
return return
}
document.querySelectorAll('audio').forEach(el => el.pause()); document.querySelectorAll('audio').forEach(el => el.pause());
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.add("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.add("paused") : void(0)
this.player().play() this.player().play()
this.nodes["playButtons"].querySelector(".playButton").classList.add("pause") this.nodes["playButtons"].querySelector(".playButton").classList.add("pause")
document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_paused.png") document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_paused.png")
navigator.mediaSession.playbackState = "playing"
} }
pause() { pause() {
@ -317,6 +324,8 @@ class bigPlayer {
this.player().pause() this.player().pause()
this.nodes["playButtons"].querySelector(".playButton").classList.remove("pause") this.nodes["playButtons"].querySelector(".playButton").classList.remove("pause")
document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_playing.png") document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_playing.png")
navigator.mediaSession.playbackState = "paused"
} }
showPreviousTrack() { showPreviousTrack() {
@ -375,11 +384,10 @@ class bigPlayer {
let indexOfCurrentTrack = this.tracks["tracks"].indexOf(obj) ?? 0 let indexOfCurrentTrack = this.tracks["tracks"].indexOf(obj) ?? 0
this.tracks["nextTrack"] = this.tracks["tracks"].at(indexOfCurrentTrack + 1) != null ? this.tracks["tracks"].at(indexOfCurrentTrack + 1).id : null this.tracks["nextTrack"] = this.tracks["tracks"].at(indexOfCurrentTrack + 1) != null ? this.tracks["tracks"].at(indexOfCurrentTrack + 1).id : null
if(indexOfCurrentTrack - 1 >= 0) { if(indexOfCurrentTrack - 1 >= 0)
this.tracks["previousTrack"] = this.tracks["tracks"].at(indexOfCurrentTrack - 1).id this.tracks["previousTrack"] = this.tracks["tracks"].at(indexOfCurrentTrack - 1).id
} else { else
this.tracks["previousTrack"] = null this.tracks["previousTrack"] = null
}
if(this.tracks["nextTrack"] == null && Math.max(...this.context["playedPages"]) < this.context["pagesCount"] if(this.tracks["nextTrack"] == null && Math.max(...this.context["playedPages"]) < this.context["pagesCount"]
|| this.tracks["previousTrack"] == null && (Math.min(...this.context["playedPages"]) > 1)) { || this.tracks["previousTrack"] == null && (Math.min(...this.context["playedPages"]) > 1)) {
@ -393,11 +401,10 @@ class bigPlayer {
formdata.append("context_entity", this.context["context_id"]) formdata.append("context_entity", this.context["context_id"])
formdata.append("hash", u("meta[name=csrf]").attr("value")) formdata.append("hash", u("meta[name=csrf]").attr("value"))
if(lesser) { if(lesser)
formdata.append("page", Math.min(...this.context["playedPages"]) - 1) formdata.append("page", Math.min(...this.context["playedPages"]) - 1)
} else { else
formdata.append("page", Number(Math.max(...this.context["playedPages"])) + 1) formdata.append("page", Number(Math.max(...this.context["playedPages"])) + 1)
}
ky.post("/audios/context", { ky.post("/audios/context", {
hooks: { hooks: {
@ -405,19 +412,17 @@ class bigPlayer {
async (_request, _options, response) => { async (_request, _options, response) => {
let newArr = await response.json() let newArr = await response.json()
if(lesser) { if(lesser)
this.tracks["tracks"] = newArr["items"].concat(this.tracks["tracks"]) this.tracks["tracks"] = newArr["items"].concat(this.tracks["tracks"])
} else { else
this.tracks["tracks"] = this.tracks["tracks"].concat(newArr["items"]) this.tracks["tracks"] = this.tracks["tracks"].concat(newArr["items"])
}
this.context["playedPages"].push(Number(newArr["page"])) this.context["playedPages"].push(Number(newArr["page"]))
if(lesser) { if(lesser)
this.tracks["previousTrack"] = this.tracks["tracks"].at(this.tracks["tracks"].indexOf(obj) - 1).id this.tracks["previousTrack"] = this.tracks["tracks"].at(this.tracks["tracks"].indexOf(obj) - 1).id
} else { else
this.tracks["nextTrack"] = this.tracks["tracks"].at(indexOfCurrentTrack + 1) != null ? this.tracks["tracks"].at(indexOfCurrentTrack + 1).id : null this.tracks["nextTrack"] = this.tracks["tracks"].at(indexOfCurrentTrack + 1) != null ? this.tracks["tracks"].at(indexOfCurrentTrack + 1).id : null
}
this.updateButtons() this.updateButtons()
console.info("Context is successfully loaded") console.info("Context is successfully loaded")
@ -428,9 +433,8 @@ class bigPlayer {
}) })
} }
if(this.tracks["currentTrack"].available == false || this.tracks["currentTrack"].withdrawn) { if(this.tracks["currentTrack"].available == false || this.tracks["currentTrack"].withdrawn)
this.showNextTrack() this.showNextTrack()
}
this.updateButtons() this.updateButtons()
@ -452,9 +456,8 @@ class bigPlayer {
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)
this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = fmtTime(this.tracks["currentTrack"].length) this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = fmtTime(this.tracks["currentTrack"].length)
}
let tempThisTrack = this.tracks["currentTrack"] let tempThisTrack = this.tracks["currentTrack"]
// если трек слушали больше 10 сек. // если трек слушали больше 10 сек.
@ -469,12 +472,11 @@ class bigPlayer {
hash: u("meta[name=csrf]").attr("value"), hash: u("meta[name=csrf]").attr("value"),
}, },
success: (response) => { success: (response) => {
if(response.success) { if(response.success)
console.info("Listen is counted.") console.info("Listen is counted.")
} else { else
console.info("Listen is not counted.") console.info("Listen is not counted.")
} }
}
}) })
}, "10000") }, "10000")
@ -486,16 +488,44 @@ class bigPlayer {
album: album == null ? "OpenVK Audios" : album.querySelector(".playlistInfo h4").innerHTML, album: album == null ? "OpenVK Audios" : album.querySelector(".playlistInfo h4").innerHTML,
artwork: [{ src: album == null ? "/assets/packages/static/openvk/img/song.jpg" : album.querySelector(".playlistCover img").src }], artwork: [{ src: album == null ? "/assets/packages/static/openvk/img/song.jpg" : album.querySelector(".playlistCover img").src }],
}); });
navigator.mediaSession.setPositionState({
duration: this.tracks["currentTrack"].length
})
} }
} }
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
if(document.querySelector(".bigPlayer") != null) { if(document.querySelector(".bigPlayer") != null) {
let context = document.querySelector("input[name='bigplayer_context']") let context = document.querySelector("input[name='bigplayer_context']")
if(!context)
return
let type = context.dataset.type let type = context.dataset.type
let entity = context.dataset.entity let entity = context.dataset.entity
window.player = new bigPlayer(type, entity, context.dataset.page) window.player = new bigPlayer(type, entity, context.dataset.page)
let bigplayer = document.querySelector('.bigPlayerDetector')
let bigPlayerObserver = new IntersectionObserver(entries => {
entries.forEach(x => {
if(x.isIntersecting) {
document.querySelector('.bigPlayer').classList.remove("floating")
document.querySelector('.bigPlayerDetector').style.marginTop = "0px"
} else {
document.querySelector('.bigPlayer').classList.add("floating")
document.querySelector('.bigPlayerDetector').style.marginTop = "46px"
}
});
}, {
root: null,
rootMargin: "0px",
threshold: 0
});
if(bigplayer != null)
bigPlayerObserver.observe(bigplayer);
}}) }})
function initPlayer(id, keys, url, length) { function initPlayer(id, keys, url, length) {
@ -510,16 +540,14 @@ function initPlayer(id, keys, url, length) {
if(document.querySelector(".bigPlayer") != null) { if(document.querySelector(".bigPlayer") != null) {
playButton.on("click", () => { playButton.on("click", () => {
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") playButton.addClass("paused")
} else { } else
document.querySelector(".bigPlayer .playButton").click() document.querySelector(".bigPlayer .playButton").click()
}
}) })
return return
@ -550,26 +578,25 @@ function initPlayer(id, keys, url, 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}%`; trackDiv.nodes[0].style.width = `${ ps}%`;
if(audio.paused) {
playButtonImageUpdate()
}
}); });
const playButtonImageUpdate = () => { const playButtonImageUpdate = () => {
if (!audio.paused) { if (!audio.paused) {
playButton.addClass("paused") playButton.addClass("paused")
/*$.post(`/audio${ id}/listen`, { document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_paused.png")
hash: u("meta[name=csrf]").attr("value")
});*/
} else { } else {
playButton.removeClass("paused") playButton.removeClass("paused")
document.querySelector('link[rel="icon"], link[rel="shortcut icon"]').setAttribute("href", "/assets/packages/static/openvk/img/favicons/favicon24_playing.png")
} }
if(!$(`#audioEmbed-${ id}`).hasClass("havePlayed")) { if(!$(`#audioEmbed-${ id}`).hasClass("havePlayed")) {
$(`#audioEmbed-${ id}`).addClass("havePlayed") $(`#audioEmbed-${ id}`).addClass("havePlayed")
$(`#audioEmbed-${ id} .track`).toggle() $(`#audioEmbed-${ id} .track`).toggle()
$.post(`/audio${playerObject.dataset.realid}/listen`, {
hash: u("meta[name=csrf]").attr("value")
});
} }
}; };
@ -592,6 +619,7 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
let name = e.currentTarget.dataset.title let name = e.currentTarget.dataset.title
let genre = player.dataset.genre let genre = player.dataset.genre
let lyrics = e.currentTarget.dataset.lyrics let lyrics = e.currentTarget.dataset.lyrics
MessageBox(tr("edit"), ` MessageBox(tr("edit"), `
<div> <div>
${tr("performer")} ${tr("performer")}
@ -647,6 +675,7 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
perf.setAttribute("href", "/search?query=&type=audios&sort=id&only_performers=on&query="+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.currentTarget.setAttribute("data-performer", escapeHtml(response.new_info.performer))
let name = player.querySelector(".title") let name = player.querySelector(".title")
name.innerHTML = escapeHtml(response.new_info.name) name.innerHTML = escapeHtml(response.new_info.name)
@ -667,9 +696,16 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
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)
} else {
MessageBox(tr("error"), response.flash.message, [tr("ok")], [Function.noop]) let url = new URL(location.href)
} let page = "1"
if(url.searchParams.p != null)
page = String(url.searchParams.p)
window.savedAudiosPages[page] = null
} else
fastError(response.flash.message)
} }
}); });
}, },
@ -683,6 +719,26 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
`) `)
}) })
u(".ovk-diag-body #_fullyDeleteAudio").on("click", (e) => {
u("body").removeClass("dimmed");
document.querySelector("html").style.overflowY = "scroll"
u(".ovk-diag-cont").remove();
$.ajax({
type: "POST",
url: `/audio${id}/action?act=delete`,
data: {
hash: u("meta[name=csrf]").attr("value")
},
success: (response) => {
if(response.success)
u(player).remove()
else
fastError(response.flash.message)
}
});
})
}) })
$(document).on("click", ".title.withLyrics", (e) => { $(document).on("click", ".title.withLyrics", (e) => {
@ -693,6 +749,7 @@ $(document).on("click", ".title.withLyrics", (e) => {
$(document).on("click", ".musicIcon.remove-icon", (e) => { $(document).on("click", ".musicIcon.remove-icon", (e) => {
let id = e.currentTarget.dataset.id let id = e.currentTarget.dataset.id
let formdata = new FormData() let formdata = new FormData()
formdata.append("hash", u("meta[name=csrf]").attr("value")) formdata.append("hash", u("meta[name=csrf]").attr("value"))
@ -713,12 +770,11 @@ $(document).on("click", ".musicIcon.remove-icon", (e) => {
e.currentTarget.classList.remove("lagged") e.currentTarget.classList.remove("lagged")
let withd = e.currentTarget.closest(".audioEmbed.withdrawn") let withd = e.currentTarget.closest(".audioEmbed.withdrawn")
if(withd != null) {
if(withd != null)
u(withd).remove() u(withd).remove()
} } else
} else { fastError(json.flash.message)
MessageBox(tr("error"), json.flash.message, [tr("ok")], [Function.noop])
}
} }
] ]
}, body: formdata }, body: formdata
@ -746,15 +802,36 @@ $(document).on("click", ".musicIcon.add-icon", (e) => {
e.currentTarget.classList.remove("add-icon") e.currentTarget.classList.remove("add-icon")
e.currentTarget.classList.add("remove-icon") e.currentTarget.classList.add("remove-icon")
e.currentTarget.classList.remove("lagged") e.currentTarget.classList.remove("lagged")
} else { } else
MessageBox(tr("error"), json.flash.message, [tr("ok")], [Function.noop]) fastError(json.flash.message)
}
} }
] ]
}, body: formdata }, body: formdata
}) })
}) })
$(document).on("click", "#_deletePlaylist", (e) => {
let id = e.currentTarget.dataset.id
$.ajax({
type: "POST",
url: `/playlist${id}/action?act=delete`,
data: {
hash: u("meta[name=csrf]").attr("value"),
},
beforeSend: () => {
e.currentTarget.classList.add("lagged")
},
success: (response) => {
if(response.success) {
window.location.assign("/playlists" + response.id)
} else {
fastError(response.flash.message)
}
}
})
})
$(document).on("click", "#_audioAttachment", (e) => { $(document).on("click", "#_audioAttachment", (e) => {
let form = e.currentTarget.closest("form") let form = e.currentTarget.closest("form")
let body = ` let body = `
@ -836,8 +913,6 @@ $(document).on("click", "#_audioAttachment", (e) => {
document.querySelector(".audiosInsert").innerHTML = "" document.querySelector(".audiosInsert").innerHTML = ""
insertAudios(1, e.currentTarget.value) insertAudios(1, e.currentTarget.value)
return; return;
} else {
console.info("skipping")
} }
}) })
@ -852,12 +927,10 @@ $(document).on("click", "#_audioAttachment", (e) => {
form.querySelector("input[name='audios']").value += (id + ",") form.querySelector("input[name='audios']").value += (id + ",")
console.info(id + " attached")
return true return true
} else { } else {
form.querySelector("input[name='audios']").value = form.querySelector("input[name='audios']").value.replace(id + ",", "") form.querySelector("input[name='audios']").value = form.querySelector("input[name='audios']").value.replace(id + ",", "")
console.info(id + " detached")
return false return false
} }
} }
@ -879,8 +952,6 @@ $(document).on("click", "#_audioAttachment", (e) => {
let id = ev.currentTarget.dataset.attachmentdata let id = ev.currentTarget.dataset.attachmentdata
form.querySelector("input[name='audios']").value = form.querySelector("input[name='audios']").value.replace(id + ",", "") form.querySelector("input[name='audios']").value = form.querySelector("input[name='audios']").value.replace(id + ",", "")
console.info(id + " detached")
u(e.currentTarget).remove() u(e.currentTarget).remove()
}) })
} }
@ -916,21 +987,10 @@ $(document).on("click", ".musicIcon.report-icon", (e) => {
Function.noop]) Function.noop])
}) })
$(document).on("click", "#bookmarkPlaylist", (e) => {
})
$(document).on("click", "#unbookmarkPlaylist", (e) => {
})
window.savedAudiosPages = {}
$(document).on("click", ".audiosContainer .paginator a", (e) => { $(document).on("click", ".audiosContainer .paginator a", (e) => {
e.preventDefault() e.preventDefault()
e.currentTarget.parentNode.classList.add("lagged")
let url = new URL(e.currentTarget.href) let url = new URL(e.currentTarget.href)
let page = Number(url.searchParams.get("p")) let page = url.searchParams.get("p")
if(window.savedAudiosPages[page] != null) { if(window.savedAudiosPages[page] != null) {
history.pushState({}, "", e.currentTarget.href) history.pushState({}, "", e.currentTarget.href)
@ -938,6 +998,7 @@ $(document).on("click", ".audiosContainer .paginator a", (e) => {
return return
} }
e.currentTarget.parentNode.classList.add("lagged")
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: e.currentTarget.href, url: e.currentTarget.href,
@ -975,3 +1036,124 @@ $(document).on("click", ".audiosContainer .paginator a", (e) => {
node.classList.add("nowPlaying") node.classList.add("nowPlaying")
} }
}) })
$(document).on("click", ".addToPlaylist", (e) => {
let audios = document.querySelector("input[name='audios']")
let id = e.currentTarget.dataset.id
if(!audios.value.includes(id + ",")) {
document.querySelector("input[name='audios']").value += (id + ",")
e.currentTarget.querySelector("span").innerHTML = tr("remove_from_playlist")
} else {
document.querySelector("input[name='audios']").value = document.querySelector("input[name='audios']").value.replace(id + ",", "")
e.currentTarget.querySelector("span").innerHTML = tr("add_to_playlist")
}
})
$(document).on("click", "#bookmarkPlaylist, #unbookmarkPlaylist", (e) => {
let target = e.currentTarget
let id = target.id
$.ajax({
type: "POST",
url: `/playlist${e.currentTarget.dataset.id}/action?act=${id == "unbookmarkPlaylist" ? "unbookmark" : "bookmark"}`,
data: {
hash: u("meta[name=csrf]").attr("value"),
},
beforeSend: () => {
e.currentTarget.classList.add("lagged")
},
success: (response) => {
if(response.success) {
e.currentTarget.setAttribute("id", id == "unbookmarkPlaylist" ? "bookmarkPlaylist" : "unbookmarkPlaylist")
e.currentTarget.innerHTML = id == "unbookmarkPlaylist" ? tr("bookmark") : tr("unbookmark")
e.currentTarget.classList.remove("lagged")
} else
fastError(response.flash.message)
}
})
})
function getPlayers(page = 1, query = "", playlist = 0) {
$.ajax({
type: "POST",
url: "/audios/context",
data: {
context: query == "" ? (playlist == 0 ? "entity_audios" : "playlist_context") : "search_context",
hash: u("meta[name=csrf]").attr("value"),
page: page,
context_entity: playlist,
query: query,
returnPlayers: 1,
},
beforeSend: () => {
document.querySelector(".playlistAudiosContainer").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"
},
error: () => {
fastError("Error when loading players")
},
success: (response) => {
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 style="width: 78%;float: left;">
${el.outerHTML}
</div>
<div class="attachAudio addToPlaylist" data-id="${id}" style="width: 22%;">
<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(page) >= pagesCount)
u(".showMoreAudiosPlaylist").remove()
else {
if(document.querySelector(".showMoreAudiosPlaylist") != null) {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-page", page + 1)
document.querySelector(".showMoreAudiosPlaylist").style.display = "block"
} else {
document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `
<div class="showMoreAudiosPlaylist" data-page="2">
${tr("show_more_audios")}
</div>
`)
}
}
u("#loader").remove()
}
})
}
$(document).on("click", ".showMoreAudiosPlaylist", (e) => {
getPlayers(Number(e.currentTarget.dataset.page), "", e.currentTarget.dataset.playlist != null ? Number(e.currentTarget.dataset.playlist) : 0)
})
$(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) {
document.querySelector(".playlistAudiosContainer").innerHTML = ""
getPlayers(1, e.currentTarget.value)
return
} else
return
})

View file

@ -156,18 +156,6 @@ function setupWallPostInputHandlers(id) {
} }
}); });
u("#wall-post-input" + id).on("input", function(e) {
var boost = 5;
var textArea = e.target;
textArea.style.height = "5px";
var newHeight = textArea.scrollHeight;
textArea.style.height = newHeight + boost + "px";
return;
// revert to original size if it is larger (possibly changed by user)
// textArea.style.height = (newHeight > originalHeight ? (newHeight + boost) : originalHeight) + "px";
});
u("#wall-post-input" + id).on("dragover", function(e) { u("#wall-post-input" + id).on("dragover", function(e) {
e.preventDefault() e.preventDefault()
@ -182,6 +170,18 @@ function setupWallPostInputHandlers(id) {
}); });
} }
u(document).on("input", "textarea", function(e) {
var boost = 5;
var textArea = e.target;
textArea.style.height = "5px";
var newHeight = textArea.scrollHeight;
textArea.style.height = newHeight + boost + "px";
return;
// revert to original size if it is larger (possibly changed by user)
// textArea.style.height = (newHeight > originalHeight ? (newHeight + boost) : originalHeight) + "px";
});
function OpenMiniature(e, photo, post, photo_id, type = "post") { function OpenMiniature(e, photo, post, photo_id, type = "post") {
/* /*
костыли но смешные однако костыли но смешные однако

View file

@ -714,6 +714,7 @@
"upload_new_video" = "Upload new video"; "upload_new_video" = "Upload new video";
"max_attached_videos" = "Max is 10 videos"; "max_attached_videos" = "Max is 10 videos";
"max_attached_photos" = "Max is 10 photos"; "max_attached_photos" = "Max is 10 photos";
"max_attached_audios" = "Max is 10 audios";
"no_videos" = "You don't have uploaded videos."; "no_videos" = "You don't have uploaded videos.";
"no_videos_results" = "No results."; "no_videos_results" = "No results.";
@ -789,6 +790,23 @@
"attach_audio" = "Attach audio"; "attach_audio" = "Attach audio";
"detach_audio" = "Detach audio"; "detach_audio" = "Detach audio";
"show_more_audios" = "Show more audios";
"add_to_playlist" = "Add to playlist";
"remove_from_playlist" = "Remove from playlist";
"delete_playlist" = "Delete playlist";
"playlist_cover" = "Playlist cover";
"playlists_user" = "Users playlists";
"playlists_club" = "Groups playlists";
"change_cover" = "Change cover";
"playlist_cover" = "Playlist's cover";
"minutes_count_zero" = "lasts no minutes";
"minutes_count_one" = "lasts one minute";
"minutes_count_few" = "lasts $1 minutes";
"minutes_count_many" = "lasts $1 minutes";
"minutes_count_other" = "lasts $1 minutes";
/* Notifications */ /* Notifications */
"feedback" = "Feedback"; "feedback" = "Feedback";
@ -1362,6 +1380,12 @@
"do_not_have_audio" = "You don't have this audio."; "do_not_have_audio" = "You don't have this audio.";
"do_have_audio" = "You already have this audio."; "do_have_audio" = "You already have this audio.";
"set_playlist_name" = "Enter the playlist name.";
"playlist_already_bookmarked" = "This playlist is already in your collection.";
"playlist_not_bookmarked" = "This playlist is not in your collection.";
"invalid_cover_photo" = "Error when loading cover photo.";
"not_a_photo" = "Uploaded file doesn't look like a photo.";
/* Admin actions */ /* Admin actions */
"login_as" = "Login as $1"; "login_as" = "Login as $1";

View file

@ -674,6 +674,7 @@
"upload_new_video" = "Загрузить новое видео"; "upload_new_video" = "Загрузить новое видео";
"max_attached_videos" = "Максимум 10 видеозаписей"; "max_attached_videos" = "Максимум 10 видеозаписей";
"max_attached_photos" = "Максимум 10 фотографий"; "max_attached_photos" = "Максимум 10 фотографий";
"max_attached_audios" = "Максимум 10 аудиозаписей";
"no_videos" = "У вас нет видео."; "no_videos" = "У вас нет видео.";
"no_videos_results" = "Нет результатов."; "no_videos_results" = "Нет результатов.";
@ -704,7 +705,7 @@
"audio_embed_processing" = "Аудио ещё обрабатывается, либо обработалось неправильно."; "audio_embed_processing" = "Аудио ещё обрабатывается, либо обработалось неправильно.";
"audios_count_zero" = "Нет аудиозаписей"; "audios_count_zero" = "Нет аудиозаписей";
"audios_count_one" = "Одна аудиозапись"; "audios_count_one" = "Одна аудиозапись"; /* сингл */
"audios_count_few" = "$1 аудиозаписи"; "audios_count_few" = "$1 аудиозаписи";
"audios_count_many" = "$1 аудиозаписей"; "audios_count_many" = "$1 аудиозаписей";
"audios_count_other" = "$1 аудиозаписей"; "audios_count_other" = "$1 аудиозаписей";
@ -745,6 +746,22 @@
"attach_audio" = "Прикрепить аудиозапись"; "attach_audio" = "Прикрепить аудиозапись";
"detach_audio" = "Открепить аудиозапись"; "detach_audio" = "Открепить аудиозапись";
"show_more_audios" = "Показать больше аудиозаписей";
"add_to_playlist" = "Добавить в плейлист";
"remove_from_playlist" = "Удалить из плейлиста";
"delete_playlist" = "Удалить плейлист";
"playlist_cover" = "Обложка плейлиста";
"playlists_user" = "Плейлисты польз.";
"playlists_club" = "Плейлисты группы";
"change_cover" = "Сменить обложку";
"playlist_cover" = "Обложка плейлиста";
"minutes_count_zero" = "длится ноль минут";
"minutes_count_one" = "длится одну минуту";
"minutes_count_few" = "длится $1 минуты";
"minutes_count_many" = "длится $1 минут";
"minutes_count_other" = "длится $1 минут";
/* Notifications */ /* Notifications */
"feedback" = "Ответы"; "feedback" = "Ответы";
@ -1259,6 +1276,12 @@
"do_not_have_audio" = "У вас нет этой аудиозаписи."; "do_not_have_audio" = "У вас нет этой аудиозаписи.";
"do_have_audio" = "У вас уже есть эта аудиозапись."; "do_have_audio" = "У вас уже есть эта аудиозапись.";
"set_playlist_name" = "Укажите название плейлиста.";
"playlist_already_bookmarked" = "Плейлист уже есть в вашей коллекции.";
"playlist_not_bookmarked" = "Плейлиста нет в вашей коллекции.";
"invalid_cover_photo" = "Не удалось сохранить обложку плейлиста.";
"not_a_photo" = "Загруженный файл не похож на фотографию.";
/* Admin actions */ /* Admin actions */
"login_as" = "Войти как $1"; "login_as" = "Войти как $1";

View file

@ -242,3 +242,41 @@ input[type="radio"] {
background-image: url("/themepack/midnight/0.0.2.8/resource/backdrop-editor.gif") !important; background-image: url("/themepack/midnight/0.0.2.8/resource/backdrop-editor.gif") !important;
border-color: #473e66 !important; border-color: #473e66 !important;
} }
.bigPlayer {
background-color: rgb(30, 26, 43) !important;
}
.bigPlayer .selectableTrack {
border-top: #b9b9b9 1px solid !important;
}
.bigPlayer .paddingLayer .slider {
background: #b9b9b9 !important;
}
.musicIcon {
filter: invert(81%) !important;
}
.audioEntry.nowPlaying {
background: #463f60 !important;
border: 1px solid #645a86 !important;
}
.preformer {
color: #b7b7b7 !important;
}
.bigPlayer .paddingLayer .trackPanel .track .timeTip {
background: #b9b9b9 !important;
color: black !important;
}
.audioEntry.nowPlaying:hover {
background: #50486f !important;
}
.audioEntry:hover {
background: #19142D !important;
}