Make shuffle and "наушники" buttons work, add f...

avicons when playing audio, save some values (like volume and last played track) to localstorage, add ability to toggle time type in player, fix uploading audios with cover (maybe) and add dragndrop to upload page
This commit is contained in:
lalka2018 2023-10-17 16:02:28 +03:00
parent 75f93b7cfc
commit d71620dfd8
13 changed files with 207 additions and 41 deletions

View file

@ -14,7 +14,6 @@ class Audio extends Media
{
protected $tableName = "audios";
protected $fileExtension = "mpd";
// protected $fileExtension = "mp3";
# Taken from winamp :D
const genres = [
@ -63,7 +62,10 @@ class Audio extends Media
throw new \DomainException("$filename does not contain any audio streams");
$vstreams = Shell::ffprobe("-i", $filename, "-show_streams", "-select_streams v", "-loglevel error")->execute($error);
if(!empty($vstreams) && !ctype_space($vstreams))
# check if audio has cover (attached_pic)
preg_match("%attached_pic=([0-1])%", $vstreams, $hasCover);
if(!empty($vstreams) && !ctype_space($vstreams) && ((int)($hasCover[1]) !== 1))
throw new \DomainException("$filename is a video");
$durations = [];

View file

@ -23,7 +23,7 @@ Set-Location -Path $temp
Move-Item $filename $audioFile
ffmpeg -i $audioFile -f dash -encryption_scheme cenc-aes-ctr -encryption_key $key `
-encryption_kid $keyID -map 0 -c:a aac -ar 44100 -seg_duration $seg `
-encryption_kid $keyID -map 0:a -c:a aac -ar 44100 -seg_duration $seg `
-use_timeline 1 -use_template 1 -init_seg_name ($fileHash + '_fragments/0_0.$ext$') `
-media_seg_name ($fileHash + '_fragments/chunk$Number%06d$_$RepresentationID$.$ext$') -adaptation_sets 'id=0,streams=a' `
"$fileHash.mpd"

View file

@ -236,10 +236,9 @@ final class AudioPresenter extends OpenVKPresenter
$audio = $this->audios->get($id);
if ($audio && !$audio->isDeleted() && !$audio->isWithdrawn()) {
$audio->listen($this->user->identity);
$listen = $audio->listen($this->user->identity);
$this->returnJson(["success" => $listen]);
}
$this->returnJson(["response" => true]);
}
}
@ -458,8 +457,6 @@ final class AudioPresenter extends OpenVKPresenter
function renderApiGetContext()
{
$this->assertUserLoggedIn();
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
header("HTTP/1.1 405 Method Not Allowed");
exit("<select><select><select><select>");

View file

@ -149,5 +149,26 @@
document.querySelector("#audio_upload > form").submit();
})
$(document).on("dragover drop", (e) => {
e.preventDefault()
return false;
})
$(".container_gray").on("drop", (e) => {
e.originalEvent.dataTransfer.dropEffect = 'move';
e.preventDefault()
let file = e.originalEvent.dataTransfer.files[0]
if(!file.type.startsWith('audio/')) {
MessageBox(tr("error"), tr("only_audios_accepted", escapeHtml(file.name)), [tr("ok")], [() => Function.noop])
return;
}
document.getElementById("audio_input").files = e.originalEvent.dataTransfer.files
u("#audio_input").trigger("change")
})
</script>
{/block}

View file

@ -17,7 +17,8 @@
<div class="timer" style="float:right">
<span class="time">00:00</span>
<span class="elapsedTime">-00:00</span>
<span>/</span>
<span class="elapsedTime" style="cursor:pointer">-00:00</span>
</div>
</div>

View file

@ -3,8 +3,8 @@
{php $id = $audio->getId() . rand(0, 1000)}
{php $isWithdrawn = $audio->isWithdrawn()}
{php $editable = $audio->canBeModifiedBy($thisUser)}
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" data-genre="{$audio->getGenre()}" class="audioEmbed {if !$audio->isAvailable()}lagged{/if} {if $isWithdrawn}withdrawn{/if}" onmouseenter="!this.classList.contains('inited') ? initPlayer({$id}, {$audio->getKeys()}, {$audio->getURL()}, {$audio->getLength()}) : void(0)">
{php $editable = isset($thisUser) && $audio->canBeModifiedBy($thisUser)}
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" 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" />
<div id="miniplayer" class="audioEntry" style="min-height: 37px;">
@ -40,13 +40,13 @@
<div class="volume" style="display: flex; flex-direction: column;width:14%;">
<span class="nobold" data-unformatted="{$audio->getLength()}" style="text-align: center;margin-top: 12px;">{$audio->getFormattedLength()}</span>
<div class="buttons" style="margin-top: 8px;">
{php $hasAudio = $audio->isInLibraryOf($thisUser)}
{php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)}
{if !$addToPlaylistButton}
<div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="$hasAudio" ></div>
<div class="add-icon musicIcon" data-id="{$audio->getId()}" n:if="!$hasAudio" ></div>
<div class="edit-icon musicIcon" data-lyrics="{$audio->getLyrics()}" data-title="{$audio->getTitle()}" data-performer="{$audio->getPerformer()}" data-explicit="{(int)$audio->isExplicit()}" n:if="$editable && !$isWithdrawn" ></div>
<div class="report-icon musicIcon" data-id="{$audio->getId()}" n:if="!$editable && !$isWithdrawn" ></div>
<div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && $hasAudio" ></div>
<div class="add-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$hasAudio && !$isWithdrawn" ></div>
<div class="edit-icon musicIcon" data-lyrics="{$audio->getLyrics()}" data-title="{$audio->getTitle()}" data-performer="{$audio->getPerformer()}" data-explicit="{(int)$audio->isExplicit()}" n:if="isset($thisUser) && $editable && !$isWithdrawn" ></div>
<div class="report-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$editable && !$isWithdrawn" ></div>
{else}
jrgnwighweif
{/if}

View file

@ -1,17 +1,17 @@
<div class="searchOptions newer">
<ul class="searchList">
<a n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}">{_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}">{_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()}">{_my_playlists}</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'" href="/audios/newPlaylist{if $isMyClub}?owner={abs($ownerId)}{/if}">{_new_playlist}</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 === 'popular' ? 'used' : 'ki'" href="/audios/popular">{_audio_popular}</a>
<a href="/search?type=audios">{_audio_search}</a>
<a href="/search?type=audios" n:if="isset($thisUser)">{_audio_search}</a>
</ul>
</div>

View file

@ -35,7 +35,7 @@
<div class="limits" style="margin-top:17px">
<b style="color:#45688E">{_admin_limits}</b>
<ul class="blueList" style="margin-left: -25px;margin-top: 1px;">
<ul style="margin-top: 6px;">
<li>{_supported_formats}</li>
<li>{_max_load_photos}</li>
</ul>

View file

@ -52,7 +52,7 @@
{css "css/dialog.css"}
{css "css/nsfw-posts.css"}
{css "css/notifications.css"}
{css "css/avataredit.css"}
{css "css/audios.css"}
{if $isXmas}
{css "css/xmas.css"}

View file

@ -74,7 +74,7 @@
}
.bigPlayer .paddingLayer .additionalButtons .deviceButton {
width: 16px;
width: 12px;
height: 16px;
background-position: -202px -51px;
margin-left: 6px;
@ -82,7 +82,6 @@
}
.bigPlayer .paddingLayer .playButtons .arrowsButtons {
/* скибиди пидорас */
float: left;
display: flex;
padding-left: 4px;
@ -363,7 +362,7 @@
text-decoration: underline;
}
.audioEmbed.withdrawn .status > *, .audioEmbed.withdrawn .playerButton > * {
.audioEmbed.withdrawn .status > *, .audioEmbed.processed .status > *, .audioEmbed.withdrawn .playerButton > *, .audioEmbed.processed .playerButton > * {
pointer-events: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -37,10 +37,12 @@ class bigPlayer {
playButtons: null,
}
timeType = 0
constructor(context, context_id, page = 1) {
this.context["context_type"] = context
this.context["context_id"] = context_id
this.context["playedPages"].push(page)
this.context["playedPages"].push(Number(page))
this.nodes["thisPlayer"] = document.querySelector(".bigPlayer")
this.nodes["thisPlayer"].classList.add("lagged")
@ -74,6 +76,12 @@ class bigPlayer {
this.nodes["thisPlayer"].classList.remove("lagged")
this.tracks["tracks"] = contextObject["items"]
this.context["pagesCount"] = contextObject["pagesCount"]
if(localStorage.lastPlayedTrack != null && this.tracks["tracks"].find(item => item.id == localStorage.lastPlayedTrack) != null) {
this.setTrack(localStorage.lastPlayedTrack)
this.pause()
}
console.info("Context is successfully loaded")
}
]
@ -93,7 +101,8 @@ class bigPlayer {
const time = this.player().currentTime;
const ps = Math.ceil((time * 100) / this.tracks["currentTrack"].length);
this.nodes["thisPlayer"].querySelector(".time").innerHTML = fmtTime(time)
this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = getElapsedTime(this.tracks["currentTrack"].length, time)
this.timeType == 0 ? this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = getElapsedTime(this.tracks["currentTrack"].length, time)
: null
if (ps <= 100)
this.nodes["thisPlayer"].querySelector(".selectableTrack .slider").style.left = `${ ps}%`;
@ -106,6 +115,8 @@ class bigPlayer {
if (ps <= 100)
this.nodes["thisPlayer"].querySelector(".volumePanel .selectableTrack .slider").style.left = `${ ps}%`;
localStorage.volume = volume
})
u(".bigPlayer .track > div").on("click mouseup", (e) => {
@ -134,6 +145,20 @@ class bigPlayer {
this.player().volume = volume;
})
u(".bigPlayer .elapsedTime").on("click", (e) => {
if(this.tracks["currentTrack"] == null) {
return
}
this.timeType == 0 ? (this.timeType = 1) : (this.timeType = 0)
localStorage.playerTimeType = this.timeType
this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = this.timeType == 1 ?
fmtTime(this.tracks["currentTrack"].length)
: getElapsedTime(this.tracks["currentTrack"].length, this.player().currentTime)
})
u(".bigPlayer .additionalButtons .repeatButton").on("click", (e) => {
if(this.tracks["currentTrack"] == null) {
return
@ -148,6 +173,30 @@ class bigPlayer {
}
})
u(".bigPlayer .additionalButtons .shuffleButton").on("click", (e) => {
if(this.tracks["currentTrack"] == null) {
return
}
this.tracks["tracks"].sort(() => Math.random() - 0.5)
this.setTrack(this.tracks["tracks"].at(0).id)
})
// хз что она делала в самом вк, но тут сделаем вид что это просто мут музыки
u(".bigPlayer .additionalButtons .deviceButton").on("click", (e) => {
if(this.tracks["currentTrack"] == null) {
return
}
e.currentTarget.classList.toggle("pressed")
if(e.currentTarget.classList.contains("pressed")) {
this.player().muted = true
} else {
this.player().muted = false
}
})
u(".bigPlayer .arrowsButtons .nextButton").on("click", (e) => {
this.showPreviousTrack()
})
@ -204,7 +253,18 @@ class bigPlayer {
this.showNextTrack()
})
this.player().volume = 0.75
if(localStorage.volume != null && localStorage.volume < 1 && localStorage.volume > 0) {
this.player().volume = localStorage.volume
} else {
this.player().volume = 0.75
}
// В хромиумах - 'null', а в фурифоксах - null. Гыгы.
if(localStorage.playerTimeType == 'null' || localStorage.playerTimeType == null) {
this.timeType = 0
} else {
this.timeType = localStorage.playerTimeType
}
}
play() {
@ -216,6 +276,7 @@ class bigPlayer {
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.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")
}
pause() {
@ -226,6 +287,7 @@ class bigPlayer {
document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry .playerButton .playIcon`) != null ? document.querySelector(`.audioEmbed[data-realid='${this.tracks["currentTrack"].id}'] .audioEntry .playerButton .playIcon`).classList.remove("paused") : void(0)
this.player().pause()
this.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")
}
showPreviousTrack() {
@ -290,20 +352,24 @@ class bigPlayer {
this.tracks["previousTrack"] = null
}
if(this.tracks["nextTrack"] == null && Math.max(this.context["playedPages"]) < this.context["pagesCount"]
|| this.tracks["previousTrack"] == null && (Math.min(this.context["playedPages"]) > 1)) {
if(this.tracks["nextTrack"] == null && Math.max(...this.context["playedPages"]) < this.context["pagesCount"]
|| this.tracks["previousTrack"] == null && (Math.min(...this.context["playedPages"]) > 1)) {
// idk how it works
let lesser = this.tracks["previousTrack"] == null ? (Math.min(...this.context["playedPages"]) > 1)
: Math.max(...this.context["playedPages"]) > this.context["pagesCount"]
let formdata = new FormData()
formdata.append("context", this.context["context_type"])
formdata.append("context_entity", this.context["context_id"])
formdata.append("hash", u("meta[name=csrf]").attr("value"))
let lesser = Math.max(this.context["playedPages"]) > 1
if(lesser) {
formdata.append("page", Number(Math.min(this.context["playedPages"])) - 1)
formdata.append("page", Math.min(...this.context["playedPages"]) - 1)
} 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", {
hooks: {
afterResponse: [
@ -316,7 +382,7 @@ class bigPlayer {
this.tracks["tracks"] = this.tracks["tracks"].concat(newArr["items"])
}
this.context["playedPages"].push(newArr["page"])
this.context["playedPages"].push(Number(newArr["page"]))
if(lesser) {
this.tracks["previousTrack"] = this.tracks["tracks"].at(this.tracks["tracks"].indexOf(obj) - 1).id
@ -355,6 +421,33 @@ class bigPlayer {
null
document.querySelectorAll(`.audioEntry .playerButton .playIcon.paused`).forEach(el => el.classList.remove("paused"))
localStorage.lastPlayedTrack = this.tracks["currentTrack"].id
if(this.timeType == 1) {
this.nodes["thisPlayer"].querySelector(".elapsedTime").innerHTML = fmtTime(this.tracks["currentTrack"].length)
}
let tempThisTrack = this.tracks["currentTrack"]
// если трек слушали больше 10 сек.
setTimeout(() => {
if(tempThisTrack.id != this.tracks["currentTrack"].id)
return;
$.ajax({
type: "POST",
url: `/audio${id}/listen`,
data: {
hash: u("meta[name=csrf]").attr("value"),
},
success: (response) => {
if(response.success) {
console.info("Listen is counted.")
} else {
console.info("Listen is not counted.")
}
}
})
}, "10000")
}
}
@ -385,6 +478,7 @@ function initPlayer(id, keys, url, length) {
if(window.player.tracks["currentTrack"] == null || window.player.tracks["currentTrack"].id != playerObject.dataset.realid) {
window.player.setTrack(playerObject.dataset.realid)
playButton.addClass("paused")
} else {
document.querySelector(".bigPlayer .playButton").click()
}
@ -462,13 +556,13 @@ $(document).on("click", ".musicIcon.edit-icon", (e) => {
let lyrics = e.currentTarget.dataset.lyrics
MessageBox(tr("edit"), `
<div>
${tr("audio_name")}
<input name="name" maxlength="40" type="text" value="${name}">
${tr("performer")}
<input name="performer" maxlength="40" type="text" value="${performer}">
</div>
<div style="margin-top: 11px">
${tr("performer")}
<input name="performer" maxlength="40" type="text" value="${performer}">
${tr("audio_name")}
<input name="name" maxlength="40" type="text" value="${name}">
</div>
<div style="margin-top: 11px">
@ -624,7 +718,7 @@ $(document).on("click", "#_audioAttachment", (e) => {
MessageBox(tr("select_audio"), body, [tr("ok")], [Function.noop])
})
$(document).on("click", ".audioEmbed.lagged", (e) => {
$(document).on("click", ".audioEmbed.processed", (e) => {
MessageBox(tr("error"), tr("audio_embed_processing"), [tr("ok")], [Function.noop])
})
@ -660,3 +754,55 @@ $(document).on("click", "#bookmarkPlaylist", (e) => {
$(document).on("click", "#unbookmarkPlaylist", (e) => {
})
window.savedAudiosPages = {}
$(document).on("click", ".audiosContainer .paginator a", (e) => {
e.preventDefault()
e.currentTarget.parentNode.classList.add("lagged")
let url = new URL(e.currentTarget.href)
let page = Number(url.searchParams.get("p"))
if(window.savedAudiosPages[page] != null) {
history.pushState({}, "", e.currentTarget.href)
document.querySelector(".audiosContainer").innerHTML = window.savedAudiosPages[page].innerHTML
return
}
$.ajax({
type: "GET",
url: e.currentTarget.href,
success: (response) => {
let domparser = new DOMParser()
let result = domparser.parseFromString(response, "text/html")
document.querySelector(".audiosContainer").innerHTML = result.querySelector(".audiosContainer").innerHTML
history.pushState({}, "", e.currentTarget.href)
window.savedAudiosPages[page] = result.querySelector(".audiosContainer")
if(window.player.context["playedPages"].indexOf(page) == -1) {
$.ajax({
type: "POST",
url: "/audios/context",
data: {
context: window.player["context"].context_type,
context_entity: window.player["context"].context_id,
hash: u("meta[name=csrf]").attr("value"),
page: page
},
success: (response_2) => {
window.player.tracks["tracks"] = window.player.tracks["tracks"].concat(response_2["items"])
window.player.context["playedPages"].push(page)
console.info("Page is switched")
}
})
}
}
})
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")
}
})