1 new commit to master: [WIP]: Add audios

- Теперь группа может разрешать загружать всем треки в неё
- Теперь треки загружаются на сервер ajax'ом, и так можно очень много аудио загружать
- Вёрстка списка плейлистов изменена, теперь она на гридах
- Немного изменено апи, теперь метод editAlbum сохраняет новую информацию ee объект плейлистов теперь возвращают реальное время
- Удалены лишние пути из routes.yml
- При переключении страниц теперь если на текущей странице есть играющий трек, он нормально подсвечивается
- Из init-db.sql удалены таблицы аудиозаписей
- В Groups.getSettings и groups.edit теперь есть информация о аудиозаписях
This commit is contained in:
lalka2018 2023-10-28 22:30:15 +03:00
parent e1646cd8a8
commit 4defb88846
21 changed files with 199 additions and 106 deletions

View file

@ -487,6 +487,7 @@ final class Audio extends VKAPIRequestHandler
$audio->setName($title); $audio->setName($title);
$audio->setSearchability(!((bool) $no_search)); $audio->setSearchability(!((bool) $no_search));
$audio->setEdited(time());
$audio->save(); $audio->save();
return $lyrics; return $lyrics;
@ -654,6 +655,9 @@ final class Audio extends VKAPIRequestHandler
if(!is_null($description)) if(!is_null($description))
$album->setDescription($description); $album->setDescription($description);
$album->setEdited(time());
$album->save();
return (int) !(!$title && !$description); return (int) !(!$title && !$description);
} }

View file

@ -292,7 +292,8 @@ final class Groups extends VKAPIRequestHandler
int $topics = NULL, int $topics = NULL,
int $adminlist = NULL, int $adminlist = NULL,
int $topicsAboveWall = NULL, int $topicsAboveWall = NULL,
int $hideFromGlobalFeed = NULL) int $hideFromGlobalFeed = NULL,
int $audio = NULL)
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
@ -303,17 +304,22 @@ final class Groups extends VKAPIRequestHandler
if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group."); if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group.");
if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode."); if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode.");
!is_null($title) ? $club->setName($title) : NULL; !empty($title) ? $club->setName($title) : NULL;
!is_null($description) ? $club->setAbout($description) : NULL; !empty($description) ? $club->setAbout($description) : NULL;
!is_null($screen_name) ? $club->setShortcode($screen_name) : NULL; !empty($screen_name) ? $club->setShortcode($screen_name) : NULL;
!is_null($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL; !empty($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL;
!is_null($wall) ? $club->setWall($wall) : NULL; !empty($wall) ? $club->setWall($wall) : NULL;
!is_null($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL; !empty($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL;
!is_null($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL; !empty($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
!is_null($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL; !empty($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
!is_null($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL; !empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
in_array($audio, [0, 1]) ? $club->setEveryone_can_upload_audios($audio) : NULL;
$club->save(); try {
$club->save();
} catch(\TypeError $e) {
$this->fail(8, "Nothing changed");
}
return 1; return 1;
} }
@ -370,7 +376,7 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->can_see_all_posts = 1; $arr->items[$i]->can_see_all_posts = 1;
break; break;
case "can_see_audio": case "can_see_audio":
$arr->items[$i]->can_see_audio = 0; $arr->items[$i]->can_see_audio = 1;
break; break;
case "can_write_private_message": case "can_write_private_message":
$arr->items[$i]->can_write_private_message = 0; $arr->items[$i]->can_write_private_message = 0;
@ -469,7 +475,7 @@ final class Groups extends VKAPIRequestHandler
"wall" => $club->canPost() == true ? 1 : 0, "wall" => $club->canPost() == true ? 1 : 0,
"photos" => 1, "photos" => 1,
"video" => 0, "video" => 0,
"audio" => 0, "audio" => $club->isEveryoneCanUploadAudios() ? 1 : 0,
"docs" => 0, "docs" => 0,
"topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0, "topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0,
"wiki" => 0, "wiki" => 0,

View file

@ -372,11 +372,24 @@ class Club extends RowModel
return $this->getRecord()->alert; return $this->getRecord()->alert;
} }
function getRealId() function getRealId(): int
{ {
return $this->getId() * -1; return $this->getId() * -1;
} }
function isEveryoneCanUploadAudios(): bool
{
return (bool) $this->getRecord()->everyone_can_upload_audios;
}
function canUploadAudio(?User $user): bool
{
if(!$user)
return NULL;
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
}
function toVkApiStruct(?User $user = NULL): object function toVkApiStruct(?User $user = NULL): object
{ {
$res = []; $res = [];

View file

@ -150,8 +150,8 @@ class Playlist extends MediaCollection
"description" => $this->getDescription(), "description" => $this->getDescription(),
"size" => $this->size(), "size" => $this->size(),
"length" => $this->getLength(), "length" => $this->getLength(),
"created" => $this->getCreationTime()->relative(), "created" => $this->getCreationTime()->timestamp(),
"modified" => $this->getCreationTime()->relative(), "modified" => $this->getEditTime() ? $this->getEditTime()->timestamp() : NULL,
"accessible" => $this->canBeViewedBy($user), "accessible" => $this->canBeViewedBy($user),
"editable" => $this->canBeModifiedBy($user), "editable" => $this->canBeModifiedBy($user),
"bookmarked" => $this->isBookmarkedBy($user), "bookmarked" => $this->isBookmarkedBy($user),

View file

@ -124,6 +124,11 @@ class Audios
return $this->getPlaylistsByEntityId($club->getId() * -1, ($perPage * ($page - 1)), $perPage, $deleted); return $this->getPlaylistsByEntityId($club->getId() * -1, ($perPage * ($page - 1)), $perPage, $deleted);
} }
function getCollectionSizeByEntityId(int $id): int
{
return sizeof($this->rels->where("entity", $id));
}
function getUserCollectionSize(User $user): int function getUserCollectionSize(User $user): int
{ {
return sizeof($this->rels->where("entity", $user->getId())); return sizeof($this->rels->where("entity", $user->getId()));

View file

@ -93,6 +93,7 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->playlistsCount = $playlistsCount; $this->template->playlistsCount = $playlistsCount;
$this->template->owner = $entity; $this->template->owner = $entity;
$this->template->ownerId = $owner; $this->template->ownerId = $owner;
$this->template->club = $owner < 0 ? $entity : NULL;
$this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id)); $this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id));
$this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity)); $this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity));
} else { } else {
@ -138,15 +139,15 @@ final class AudioPresenter extends OpenVKPresenter
$this->assertUserLoggedIn(); $this->assertUserLoggedIn();
$group = NULL; $group = NULL;
$isAjax = $this->postParam("ajax", false) == 1;
if(!is_null($this->queryParam("gid"))) { if(!is_null($this->queryParam("gid"))) {
$gid = (int) $this->queryParam("gid"); $gid = (int) $this->queryParam("gid");
$group = (new Clubs)->get($gid); $group = (new Clubs)->get($gid);
if(!$group) if(!$group)
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment")); $this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
// TODO check if group allows uploads to anyone if(!$group->canUploadAudio($this->user->identity))
if(!$group->canBeModifiedBy($this->user->identity)) $this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"));
} }
$this->template->group = $group; $this->template->group = $group;
@ -157,20 +158,20 @@ final class AudioPresenter extends OpenVKPresenter
$upload = $_FILES["blob"]; $upload = $_FILES["blob"];
if(isset($upload) && file_exists($upload["tmp_name"])) { if(isset($upload) && file_exists($upload["tmp_name"])) {
if($upload["size"] > self::MAX_AUDIO_SIZE) if($upload["size"] > self::MAX_AUDIO_SIZE)
$this->flashFail("err", tr("error"), tr("media_file_corrupted_or_too_large")); $this->flashFail("err", tr("error"), tr("media_file_corrupted_or_too_large"), null, $isAjax);
} else { } else {
$err = !isset($upload) ? 65536 : $upload["error"]; $err = !isset($upload) ? 65536 : $upload["error"];
$err = str_pad(dechex($err), 9, "0", STR_PAD_LEFT); $err = str_pad(dechex($err), 9, "0", STR_PAD_LEFT);
$this->flashFail("err", tr("error"), tr("error_generic") . "Upload error: 0x$err"); $this->flashFail("err", tr("error"), tr("error_generic") . "Upload error: 0x$err", null, $isAjax);
} }
$performer = $this->postParam("performer"); $performer = $this->postParam("performer");
$name = $this->postParam("name"); $name = $this->postParam("name");
$lyrics = $this->postParam("lyrics"); $lyrics = $this->postParam("lyrics");
$genre = empty($this->postParam("genre")) ? "undefined" : $this->postParam("genre"); $genre = empty($this->postParam("genre")) ? "Other" : $this->postParam("genre");
$nsfw = ($this->postParam("explicit") ?? "off") === "on"; $nsfw = ($this->postParam("explicit") ?? "off") === "on";
if(empty($performer) || empty($name) || iconv_strlen($performer . $name) > 128) # FQN of audio must not be more than 128 chars if(empty($performer) || empty($name) || iconv_strlen($performer . $name) > 128) # FQN of audio must not be more than 128 chars
$this->flashFail("err", tr("error"), tr("error_insufficient_info")); $this->flashFail("err", tr("error"), tr("error_insufficient_info"), null, $isAjax);
$audio = new Audio; $audio = new Audio;
$audio->setOwner($this->user->id); $audio->setOwner($this->user->id);
@ -184,13 +185,30 @@ final class AudioPresenter extends OpenVKPresenter
$audio->setFile($upload); $audio->setFile($upload);
} catch(\DomainException $ex) { } catch(\DomainException $ex) {
$e = $ex->getMessage(); $e = $ex->getMessage();
$this->flashFail("err", tr("error"), tr("media_file_corrupted_or_too_large") . " $e."); $this->flashFail("err", tr("error"), tr("media_file_corrupted_or_too_large") . " $e.", null, $isAjax);
} }
$audio->save(); $audio->save();
$audio->add($group ?? $this->user->identity); $audio->add($group ?? $this->user->identity);
$this->redirect(is_null($group) ? "/audios" . $this->user->id : "/audios-" . $group->getId()); if(!$isAjax)
$this->redirect(is_null($group) ? "/audios" . $this->user->id : "/audios-" . $group->getId());
else {
$redirectLink = "/audios";
if(!is_null($group))
$redirectLink .= $group->getRealId();
else
$redirectLink .= $this->user->id;
$pagesCount = (int)ceil((new Audios)->getCollectionSizeByEntityId(isset($group) ? $group->getRealId() : $this->user->id) / 10);
$redirectLink .= "?p=".$pagesCount;
$this->returnJson([
"success" => true,
"redirect_link" => $redirectLink,
]);
}
} }
function renderListen(int $id): void function renderListen(int $id): void
@ -222,8 +240,8 @@ final class AudioPresenter extends OpenVKPresenter
$owner = $this->user->id; $owner = $this->user->id;
if ($this->requestParam("owner")) { if ($this->requestParam("gid")) {
$club = (new Clubs)->get((int) abs($this->requestParam("owner"))); $club = (new Clubs)->get((int) abs((int)$this->requestParam("gid")));
if (!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity)) if (!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity))
$this->redirect("/audios" . $this->user->id); $this->redirect("/audios" . $this->user->id);
@ -367,6 +385,7 @@ final class AudioPresenter extends OpenVKPresenter
$playlist->setName(ovk_proc_strtr($title, 128)); $playlist->setName(ovk_proc_strtr($title, 128));
$playlist->setDescription(ovk_proc_strtr($description, 2048)); $playlist->setDescription(ovk_proc_strtr($description, 2048));
$playlist->setEdited(time());
$playlist->resetLength(); $playlist->resetLength();
if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) { if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) {
@ -504,6 +523,7 @@ final class AudioPresenter extends OpenVKPresenter
$audio->setGenre($genre); $audio->setGenre($genre);
$audio->setExplicit($nsfw); $audio->setExplicit($nsfw);
$audio->setSearchability($unlisted); $audio->setSearchability($unlisted);
$audio->setEdited(time());
$audio->save(); $audio->save();
$this->returnJson(["success" => true, "new_info" => [ $this->returnJson(["success" => true, "new_info" => [

View file

@ -220,6 +220,7 @@ final class GroupPresenter extends OpenVKPresenter
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display")); $club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1); $club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
$club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1); $club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);
$club->setEveryone_can_upload_audios(empty($this->postParam("upload_audios")) ? 0 : 1);
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1); $club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1);
$website = $this->postParam("website") ?? ""; $website = $this->postParam("website") ?? "";

View file

@ -94,7 +94,7 @@
</span> </span>
<span style="font-size: 12px" class="playlistAuthor"> <span style="font-size: 12px" class="playlistAuthor">
<a href="{$playlist->getOwner()->getURL()}">{ovk_proc_strtr($playlist->getOwner()->getCanonicalName(), 15)}</a> <a href="{$playlist->getOwner()->getURL()}">{ovk_proc_strtr($playlist->getOwner()->getCanonicalName(), 20)}</a>
</span> </span>
</div> </div>
</a> </a>

View file

@ -5,7 +5,7 @@
{/block} {/block}
{block header} {block header}
{if !$_GET["owner"]} {if !$_GET["gid"]}
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a> <a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
» »
<a href="/audios{$thisUser->getId()}">{_audios}</a> <a href="/audios{$thisUser->getId()}">{_audios}</a>
@ -37,15 +37,16 @@
</a> </a>
</div> </div>
<div class="playlistInfo"> <div style="padding-left: 17px;width: 75%;" class="plinfo">
<input type="text" name="title" placeholder="{_title}" maxlength="128" /> <div>
<br /><br /> <input type="text" name="title" placeholder="{_title}" maxlength="128" />
<textarea placeholder="{_description}" name="description" maxlength="2048" /> </div>
<br /><br /> <div class="moreInfo" style="margin-top: 11px;">
<textarea placeholder="{_description}" name="description" maxlength="2048" />
</div>
</div> </div>
</div> </div>
<hr />
<div style="margin-top: 19px;"> <div style="margin-top: 19px;">
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}"> <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 class="playlistAudiosContainer" style="display:table;clear:both;width:100%;margin-top: 10px;">
@ -80,8 +81,8 @@
document.querySelector("input[name='audios']").value = "" document.querySelector("input[name='audios']").value = ""
u("#newPlaylistForm").on("submit", (e) => { u("#newPlaylistForm").on("submit", (e) => {
document.querySelector("#newPlaylistForm input[name='title']").value = document.querySelector(".playlistInfo input[name='title']").value document.querySelector("#newPlaylistForm input[name='title']").value = document.querySelector(".plinfo input[name='title']").value
document.querySelector("#newPlaylistForm textarea[name='description']").value = document.querySelector(".playlistInfo textarea[name='description']").value document.querySelector("#newPlaylistForm textarea[name='description']").value = document.querySelector(".plinfo textarea[name='description']").value
}) })
u("#newPlaylistForm input[name='cover']").on("change", (e) => { u("#newPlaylistForm input[name='cover']").on("change", (e) => {

View file

@ -24,9 +24,10 @@
<div id="upload_container"> <div id="upload_container">
<div id="firstStep"> <div id="firstStep">
<h4>{_select_audio}</h4><br/> <h4>{_select_audio}</h4><br/>
<b><a href="javascript:false">{_limits}</a></b> <b><a href="javascript:void(0)">{_limits}</a></b>
<ul> <ul>
<li>{tr("audio_requirements", 1, 30, 25)}</li> <li>{tr("audio_requirements", 1, 30, 25)}</li>
<li>{tr("audio_requirements_2")}</li>
</ul> </ul>
<div id="audio_upload"> <div id="audio_upload">
<form enctype="multipart/form-data" method="POST"> <form enctype="multipart/form-data" method="POST">
@ -106,20 +107,26 @@
if(tags.title != null) if(tags.title != null)
document.querySelector("#lastStep input[name=name]").value = tags.title; document.querySelector("#lastStep input[name=name]").value = tags.title;
else
document.querySelector("#lastStep input[name=name]").value = files[0].name
if(tags.artist != null) if(tags.artist != null)
document.querySelector("#lastStep input[name=performer]").value = tags.artist; document.querySelector("#lastStep input[name=performer]").value = tags.artist;
else
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
if(tags.genre != null) { if(tags.genre != null) {
if(document.querySelector("#lastStep select[name=genre] > option[value='" + tags.genre + "']") != null) { if(document.querySelector("#lastStep select[name=genre] > option[value='" + tags.genre + "']") != null) {
document.querySelector("#lastStep select[name=genre]").value = tags.genre; document.querySelector("#lastStep select[name=genre]").value = tags.genre;
} else { } else {
console.warn("Unknown genre: " + tags.genre); console.warn("Unknown genre: " + tags.genre);
document.querySelector("#lastStep select[name=genre]").value = "Other"
} }
} }
} else { } else {
document.querySelector("#lastStep input[name=name]").value = files[0].name document.querySelector("#lastStep input[name=name]").value = files[0].name
document.querySelector("#lastStep select[name=genre]").value = "Other" document.querySelector("#lastStep select[name=genre]").value = "Other"
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
} }
}); });
@ -147,7 +154,7 @@
lyrics_.value = document.querySelector("#lastStep textarea[name=lyrics]").value lyrics_.value = document.querySelector("#lastStep textarea[name=lyrics]").value
explicit_.value = document.querySelector("#lastStep input[name=explicit]").checked ? "on" : "off" explicit_.value = document.querySelector("#lastStep input[name=explicit]").checked ? "on" : "off"
document.querySelector("#audio_upload > form").submit(); $("#audio_upload > form").trigger("submit");
}) })
$(document).on("dragover drop", (e) => { $(document).on("dragover drop", (e) => {
@ -170,5 +177,36 @@
document.getElementById("audio_input").files = e.originalEvent.dataTransfer.files document.getElementById("audio_input").files = e.originalEvent.dataTransfer.files
u("#audio_input").trigger("change") u("#audio_input").trigger("change")
}) })
$("#audio_upload").on("submit", "form", (e) => {
e.preventDefault()
let fd = new FormData(e.currentTarget)
fd.append("ajax", 1)
$.ajax({
type: "POST",
url: location.href,
contentType: false,
processData: false,
data: fd,
beforeSend: function() {
document.querySelector("#lastStep").classList.add("lagged")
document.querySelector("#upload_container").classList.add("uploading")
},
success: (response) => {
document.querySelector("#lastStep").classList.remove("lagged")
document.querySelector("#upload_container").classList.remove("uploading")
if(response.success) {
u("#backToUpload").trigger("click")
NewNotification(tr("success"), tr("audio_successfully_uploaded"), null, () => {
window.location.assign(response.redirect_link)
})
} else {
fastError(response.flash.message)
}
}
})
})
</script> </script>
{/block} {/block}

View file

@ -16,9 +16,9 @@
<hr> <hr>
<a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a> <a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
<a href="/player/upload?gid={abs($ownerId)}" n:if="isset($thisUser) && $isMyClub">{_upload_audio}</a> <a 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($ownerId) && !$isMy">{if $ownerId > 0}{_playlists_user}{else}{_playlists_club}{/if}</a>
<a href="/audios/newPlaylist{if $isMyClub}?owner={abs($ownerId)}{/if}" n:if="isset($thisUser) && $isMyClub">{_new_playlist}</a> <a href="/audios/newPlaylist{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser) && $isMyClub">{_new_playlist}</a>
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -102,6 +102,15 @@
</td> </td>
</tr> </tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_audios}: </span>
</td>
<td>
<label><input type="checkbox" name="upload_audios" value="1" n:attr="checked => $club->isEveryoneCanUploadAudios()" /> {_everyone_can_upload_audios}</label>
</td>
</tr>
<tr> <tr>
<td> <td>

View file

@ -251,22 +251,6 @@ routes:
handler: "Topics->delete" handler: "Topics->delete"
- url: "/audios{num}" - url: "/audios{num}"
handler: "Audios->app" handler: "Audios->app"
- url: "/audios{num}.json"
handler: "Audios->apiListSongs"
- url: "/audios/popular.json"
handler: "Audios->apiListPopSongs"
- url: "/audios/playlist{num}.json"
handler: "Audios->apiListPlaylists"
- url: "/audios/search.json"
handler: "Audios->apiSearch"
- url: "/audios/add.json"
handler: "Audios->apiAdd"
- url: "/audios/playlist.json"
handler: "Audios->apiAddPlaylist"
- url: "/audios/upload.json"
handler: "Audios->apiUpload"
- url: "/audios/beacon"
handler: "Audios->apiBeacon"
- url: "/im" - url: "/im"
handler: "Messenger->index" handler: "Messenger->index"
- url: "/im/sel{num}" - url: "/im/sel{num}"

View file

@ -163,6 +163,11 @@
font-size: 10px; font-size: 10px;
} }
.bigPlayer .paddingLayer .trackInfo b:hover {
text-decoration: underline;
cursor: pointer;
}
.music-app { .music-app {
display: grid; display: grid;
} }
@ -446,10 +451,9 @@
} }
.playlistContainer { .playlistContainer {
display: flex; display: grid;
flex-wrap: wrap; grid-template-columns: repeat(3, 146px);
margin-top: -15px; gap: 18px 10px;
margin-left: -30px;
} }
.playlistContainer .playlistCover { .playlistContainer .playlistCover {
@ -465,11 +469,6 @@
margin: auto; margin: auto;
} }
.playlistContainer .infObj {
margin-left: 39px;
margin-top: 19px;
}
.ovk-diag-body .searchBox { .ovk-diag-body .searchBox {
background: #e6e6e6; background: #e6e6e6;
padding-top: 10px; padding-top: 10px;

View file

@ -2990,3 +2990,12 @@ hr {
padding-top: 9px; padding-top: 9px;
cursor: pointer; cursor: pointer;
} }
#upload_container.uploading {
background: white url('/assets/packages/static/openvk/img/progressbar.gif') !important;
background-position-x: 0% !important;
background-position-y: 0% !important;
background-repeat: repeat !important;
background-repeat: no-repeat !important;
background-position: 50% !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,018 B

View file

@ -48,7 +48,7 @@ class bigPlayer {
constructor(context, context_id, page = 1) { constructor(context, context_id, page = 1) {
this.context["context_type"] = context this.context["context_type"] = context
this.context["context_id"] = context_id this.context["context_id"] = context_id
this.context["playedPages"].push(Number(page)) this.context["playedPages"].push(String(page))
this.nodes["thisPlayer"] = document.querySelector(".bigPlayer") this.nodes["thisPlayer"] = document.querySelector(".bigPlayer")
this.nodes["thisPlayer"].classList.add("lagged") this.nodes["thisPlayer"].classList.add("lagged")
@ -160,7 +160,7 @@ class bigPlayer {
} }
e.currentTarget.parentNode.insertAdjacentHTML("afterbegin", ` e.currentTarget.parentNode.insertAdjacentHTML("afterbegin", `
<div class="bigPlayerTip nextTrackTip" style="left: 7%;"> <div class="bigPlayerTip nextTrackTip" style="left: 5%;">
${ovk_proc_strtr(escapeHtml(this.findTrack(this.tracks["previousTrack"]).name), 20) ?? ""} ${ovk_proc_strtr(escapeHtml(this.findTrack(this.tracks["previousTrack"]).name), 20) ?? ""}
</div> </div>
`) `)
@ -178,7 +178,7 @@ class bigPlayer {
} }
e.currentTarget.parentNode.insertAdjacentHTML("afterbegin", ` e.currentTarget.parentNode.insertAdjacentHTML("afterbegin", `
<div class="bigPlayerTip previousTrackTip" style="left: 10%;"> <div class="bigPlayerTip previousTrackTip" style="left: 8%;">
${ovk_proc_strtr(escapeHtml(this.findTrack(this.tracks["nextTrack"]).name), 20) ?? ""} ${ovk_proc_strtr(escapeHtml(this.findTrack(this.tracks["nextTrack"]).name), 20) ?? ""}
</div> </div>
`) `)
@ -265,6 +265,10 @@ class bigPlayer {
this.showNextTrack() this.showNextTrack()
}) })
u(".bigPlayer .trackInfo b").on("click", (e) => {
window.location.assign(`/search?query=${e.currentTarget.innerHTML}&type=audios&only_performers=on`)
})
u(document).on("keydown", (e) => { u(document).on("keydown", (e) => {
if(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) { if(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
e.preventDefault() e.preventDefault()
@ -332,7 +336,6 @@ class bigPlayer {
else else
this.timeType = localStorage.playerTimeType this.timeType = localStorage.playerTimeType
navigator.mediaSession.setActionHandler('play', () => { this.play() }); navigator.mediaSession.setActionHandler('play', () => { this.play() });
navigator.mediaSession.setActionHandler('pause', () => { this.pause() }); navigator.mediaSession.setActionHandler('pause', () => { this.pause() });
navigator.mediaSession.setActionHandler('previoustrack', () => { this.showPreviousTrack() }); navigator.mediaSession.setActionHandler('previoustrack', () => { this.showPreviousTrack() });
@ -1158,9 +1161,19 @@ $(document).on("click", ".audiosContainer .paginator a", (e) => {
let url = new URL(e.currentTarget.href) let url = new URL(e.currentTarget.href)
let page = url.searchParams.get("p") let page = url.searchParams.get("p")
function searchNode(id) {
let node = document.querySelector(`.audioEmbed[data-realid='${id}'] .audioEntry`)
if(node != null) {
node.classList.add("nowPlaying")
}
}
if(window.savedAudiosPages[page] != null) { if(window.savedAudiosPages[page] != null) {
history.pushState({}, "", e.currentTarget.href) history.pushState({}, "", e.currentTarget.href)
document.querySelector(".audiosContainer").innerHTML = window.savedAudiosPages[page].innerHTML document.querySelector(".audiosContainer").innerHTML = window.savedAudiosPages[page].innerHTML
searchNode(window.player["tracks"].currentTrack != null ? window.player["tracks"].currentTrack.id : 0)
return return
} }
@ -1175,8 +1188,9 @@ $(document).on("click", ".audiosContainer .paginator a", (e) => {
document.querySelector(".audiosContainer").innerHTML = result.querySelector(".audiosContainer").innerHTML document.querySelector(".audiosContainer").innerHTML = result.querySelector(".audiosContainer").innerHTML
history.pushState({}, "", e.currentTarget.href) history.pushState({}, "", e.currentTarget.href)
window.savedAudiosPages[page] = result.querySelector(".audiosContainer") window.savedAudiosPages[page] = result.querySelector(".audiosContainer")
searchNode(window.player["tracks"].currentTrack != null ? window.player["tracks"].currentTrack.id : 0)
if(window.player.context["playedPages"].indexOf(page) == -1) { if(!window.player.context["playedPages"].includes(page)) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: "/audios/context", url: "/audios/context",
@ -1195,12 +1209,6 @@ $(document).on("click", ".audiosContainer .paginator a", (e) => {
} }
} }
}) })
let node = document.querySelector(`.audioEmbed[data-realid='${window.player["tracks"].currentTrack != null ? window.player["tracks"].currentTrack.id : 0}'] .audioEntry`)
if(node != null) {
node.classList.add("nowPlaying")
}
}) })
$(document).on("click", ".addToPlaylist", (e) => { $(document).on("click", ".addToPlaylist", (e) => {
@ -1293,10 +1301,18 @@ function getPlayers(page = 1, query = "", playlist = 0, club = 0) {
else { else {
if(document.querySelector(".showMoreAudiosPlaylist") != null) { if(document.querySelector(".showMoreAudiosPlaylist") != null) {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-page", page + 1) document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-page", page + 1)
if(query != "") {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-query", query)
}
document.querySelector(".showMoreAudiosPlaylist").style.display = "block" document.querySelector(".showMoreAudiosPlaylist").style.display = "block"
} else { } else {
document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", ` document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `
<div class="showMoreAudiosPlaylist" data-page="2"> <div class="showMoreAudiosPlaylist" data-page="2"
${query != "" ? `"data-query="${query}"` : ""}
${playlist != 0 ? `"data-playlist="${playlist}"` : ""}
${club != 0 ? `"data-club="${club}"` : ""}>
${tr("show_more_audios")} ${tr("show_more_audios")}
</div> </div>
`) `)
@ -1309,7 +1325,8 @@ function getPlayers(page = 1, query = "", playlist = 0, club = 0) {
} }
$(document).on("click", ".showMoreAudiosPlaylist", (e) => { $(document).on("click", ".showMoreAudiosPlaylist", (e) => {
getPlayers(Number(e.currentTarget.dataset.page), "", getPlayers(Number(e.currentTarget.dataset.page),
e.currentTarget.dataset.query != null ? e.currentTarget.dataset.query : "",
e.currentTarget.dataset.playlist != null ? Number(e.currentTarget.dataset.playlist) : 0, e.currentTarget.dataset.playlist != null ? Number(e.currentTarget.dataset.playlist) : 0,
e.currentTarget.dataset.club != null ? Number(e.currentTarget.dataset.club) : 0, e.currentTarget.dataset.club != null ? Number(e.currentTarget.dataset.club) : 0,
) )

View file

@ -54,27 +54,6 @@ CREATE TABLE `attachments` (
`index` bigint(20) UNSIGNED NOT NULL `index` bigint(20) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE `audios` (
`id` bigint(20) UNSIGNED NOT NULL,
`owner` bigint(20) UNSIGNED NOT NULL,
`virtual_id` bigint(20) UNSIGNED NOT NULL,
`created` bigint(20) UNSIGNED NOT NULL,
`edited` bigint(20) UNSIGNED DEFAULT NULL,
`hash` char(128) COLLATE utf8mb4_unicode_520_ci NOT NULL,
`deleted` tinyint(4) DEFAULT 0,
`name` varchar(190) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '(no name)',
`performer` varchar(190) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT 'Unknown',
`genre` varchar(190) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT 'K-POP',
`lyrics` longtext COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`explicit` tinyint(4) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE `audio_relations` (
`user` bigint(20) UNSIGNED NOT NULL,
`audio` bigint(20) UNSIGNED NOT NULL,
`index` bigint(20) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE `comments` ( CREATE TABLE `comments` (
`id` bigint(20) UNSIGNED NOT NULL, `id` bigint(20) UNSIGNED NOT NULL,
`owner` bigint(20) NOT NULL, `owner` bigint(20) NOT NULL,

View file

@ -32,7 +32,7 @@ CREATE TABLE IF NOT EXISTS `audios` (
FULLTEXT KEY `performer_name` (`performer`,`name`), FULLTEXT KEY `performer_name` (`performer`,`name`),
FULLTEXT KEY `lyrics` (`lyrics`), FULLTEXT KEY `lyrics` (`lyrics`),
FULLTEXT KEY `performer` (`performer`) FULLTEXT KEY `performer` (`performer`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `audio_listens` ( CREATE TABLE IF NOT EXISTS `audio_listens` (
`entity` bigint NOT NULL, `entity` bigint NOT NULL,
@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS `audio_listens` (
KEY `audio` (`audio`), KEY `audio` (`audio`),
KEY `user` (`entity`) USING BTREE, KEY `user` (`entity`) USING BTREE,
KEY `user_time` (`entity`,`time`) USING BTREE KEY `user_time` (`entity`,`time`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `audio_relations` ( CREATE TABLE IF NOT EXISTS `audio_relations` (
`entity` bigint NOT NULL, `entity` bigint NOT NULL,
@ -88,3 +88,5 @@ CREATE TABLE IF NOT EXISTS `playlist_relations` (
KEY `playlist` (`collection`) USING BTREE, KEY `playlist` (`collection`) USING BTREE,
KEY `audio` (`media`) USING BTREE KEY `audio` (`media`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; ) 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`;

View file

@ -739,7 +739,8 @@
"limits" = "Limits"; "limits" = "Limits";
"select_audio" = "Select audio from your computer"; "select_audio" = "Select audio from your computer";
"audio_requirements" = "Audio must be between $1s to $2 minutes, weights to $3MB, contain audio stream must not infringe on the copyright"; "audio_requirements" = "Audio must be between $1s to $2 minutes, weights to $3MB and contain audio stream.";
"audio_requirements_2" = "Audio must not infringe copyright and related rights";
"you_can_also_add_audio_using" = "You can also add audio from among the files you have already downloaded using"; "you_can_also_add_audio_using" = "You can also add audio from among the files you have already downloaded using";
"search_audio_inst" = "audios search"; "search_audio_inst" = "audios search";
@ -820,6 +821,7 @@
"by_name" = "by name"; "by_name" = "by name";
"by_performer" = "by performer"; "by_performer" = "by performer";
"no_access_clubs" = "There are no groups where you are an administrator."; "no_access_clubs" = "There are no groups where you are an administrator.";
"audio_successfully_uploaded" = "Audio has been successfully uploaded and is currently being processed.";
/* Notifications */ /* Notifications */
@ -1214,6 +1216,7 @@
"created" = "Created"; "created" = "Created";
"everyone_can_create_topics" = "Everyone can create topics"; "everyone_can_create_topics" = "Everyone can create topics";
"everyone_can_upload_audios" = "Everyone can upload audios";
"display_list_of_topics_above_wall" = "Display a list of topics above the wall"; "display_list_of_topics_above_wall" = "Display a list of topics above the wall";
"topic_changes_saved_comment" = "The updated title and settings will appear on the topic page."; "topic_changes_saved_comment" = "The updated title and settings will appear on the topic page.";

View file

@ -695,7 +695,8 @@
"limits" = "Ограничения"; "limits" = "Ограничения";
"select_audio" = "Выберите аудиозапись на Вашем компьютере"; "select_audio" = "Выберите аудиозапись на Вашем компьютере";
"audio_requirements" = "Аудиозапись должна быть длинной от $1c до $2 минут, весить до $3мб, содержать аудиопоток и не нарушать авторские права."; "audio_requirements" = "Аудиозапись должна быть длинной от $1c до $2 минут, весить до $3мб и содержать аудиопоток.";
"audio_requirements_2" = "Аудиозапись не должна нарушать авторские и смежные права.";
"you_can_also_add_audio_using" = "Вы также можете добавить аудиозапись из числа уже загруженных файлов, воспользовавшись"; "you_can_also_add_audio_using" = "Вы также можете добавить аудиозапись из числа уже загруженных файлов, воспользовавшись";
"search_audio_inst" = "поиском по аудио"; "search_audio_inst" = "поиском по аудио";
@ -775,6 +776,7 @@
"by_name" = "по композициям"; "by_name" = "по композициям";
"by_performer" = "по исполнителю"; "by_performer" = "по исполнителю";
"no_access_clubs" = "Нет групп, где вы являетесь администратором."; "no_access_clubs" = "Нет групп, где вы являетесь администратором.";
"audio_successfully_uploaded" = "Аудио успешно загружено и на данный момент обрабатывается.";
/* Notifications */ /* Notifications */
@ -1132,6 +1134,7 @@
"topics_other" = "$1 тем"; "topics_other" = "$1 тем";
"created" = "Создано"; "created" = "Создано";
"everyone_can_create_topics" = "Все могут создавать темы"; "everyone_can_create_topics" = "Все могут создавать темы";
"everyone_can_upload_audios" = "Все могут загружать аудиозаписи";
"display_list_of_topics_above_wall" = "Отображать список тем над стеной"; "display_list_of_topics_above_wall" = "Отображать список тем над стеной";
"topic_changes_saved_comment" = "Обновлённый заголовок и настройки появятся на странице с темой."; "topic_changes_saved_comment" = "Обновлённый заголовок и настройки появятся на странице с темой.";
"failed_to_create_topic" = "Не удалось создать тему"; "failed_to_create_topic" = "Не удалось создать тему";