Refactor audio upload page

This commit is contained in:
mrilyew 2024-11-30 19:41:08 +03:00
parent 1d719309c7
commit 5bc306ebf9
10 changed files with 286 additions and 194 deletions

View file

@ -809,7 +809,7 @@ final class AudioPresenter extends OpenVKPresenter
$output_array['name'] = $audio->getTitle();
$output_array['performer'] = $audio->getPerformer();
if(!$audio->isWithdrawn() && $audio->isAvailable()) {
if(!$audio->isWithdrawn()) {
$output_array['keys'] = $audio->getKeys();
$output_array['url'] = $audio->getUrl();
}

View file

@ -38,217 +38,196 @@
<li>{tr("audio_requirements_2")}</li>
</ul>
<div id="audio_upload">
<form enctype="multipart/form-data" method="POST">
<input type="hidden" name="name" />
<input type="hidden" name="performer" />
<input type="hidden" name="lyrics" />
<input type="hidden" name="genre" />
<input type="hidden" name="explicit" />
<input type="hidden" name="unlisted" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input id="audio_input" type="file" name="blob" accept="audio/*" style="display:none" />
<input value="{_upload_button}" class="button" type="button" onclick="document.querySelector('#audio_input').click()">
</form>
<input id="audio_input" multiple type="file" name="blob" accept="audio/*" style="display:none" />
<input value="{_upload_button}" class="button" type="button" onclick="document.querySelector('#audio_input').click()">
</div><br/>
<span>{_you_can_also_add_audio_using} <b><a href="/search?section=audios">{_search_audio_inst}</a></b>.<span>
</div>
<div id="lastStep" style="display:none;">
<table cellspacing="7" cellpadding="0" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top"><span class="nobold">{_performer}:</span></td>
<td><input name="performer" type="text" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_audio_name}:</span></td>
<td><input type="text" name="name" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_genre}:</span></td>
<td>
<select name="genre">
<option n:foreach='\openvk\Web\Models\Entities\Audio::genres as $genre' n:attr="selected: $genre == 'Other'" value="{$genre}">
{$genre}
</option>
</select>
</td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_lyrics}:</span></td>
<td><textarea name="lyrics" style="resize: vertical;max-height: 300px;"></textarea></td>
</tr>
<tr>
<td width="120" valign="top"></td>
<td>
<label style='display:block'><input type="checkbox" name="explicit">{_audios_explicit}</label>
<label><input type="checkbox" name="unlisted">{_audios_unlisted}</label>
</td>
</tr>
<tr>
<td width="120" valign="top"></td>
<td>
<input class="button" type="button" id="uploadMuziko" value="{_upload_button}">
<input class="button" type="button" id="backToUpload" value="{_select_another_file}">
</td>
</tr>
</tbody>
</table>
<div id="lastStepContainers"></div>
<div id="lastStepButtons" style="text-align: center;margin-top: 10px;">
<input class="button" type="button" id="uploadMusic" value="{_upload_button}">
<input class="button" type="button" id="backToUpload" onclick="document.querySelector('#audio_input').click()" value="{_select_another_file}">
</div>
</div>
</div>
</div>
<script type="module">
<script type="module" n:syntax='off'>
import * as id3 from "/assets/packages/static/openvk/js/node_modules/id3js/lib/id3.js";
u("#audio_input").on("change", async function(e) {
let files = e.currentTarget.files
if(files.length <= 0)
return;
window.__audio_upload_page = new class {
files_list = []
document.querySelector("#firstStep").style.display = "none"
document.querySelector("#lastStep").style.display = "block"
function fallback() {
console.info('Tags not found, setting default values.')
document.querySelector("#lastStep input[name=name]").value = files[0].name
document.querySelector("#lastStep select[name=genre]").value = "Other"
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
hideFirstPage() {
u('#firstStep').attr('style', 'display:none')
u('#lastStep').attr('style', 'display:block')
}
let tags = null
try {
tags = await id3.fromFile(files[0]);
} catch(e) {
console.error(e)
showFirstPage() {
u('#firstStep').attr('style', 'display:block')
u('#lastStep').attr('style', 'display:none')
}
console.log(tags)
if(tags != null) {
console.log("ID" + tags.kind + " detected, setting values...");
if(tags.title != null)
document.querySelector("#lastStep input[name=name]").value = tags.title;
else
document.querySelector("#lastStep input[name=name]").value = files[0].name
if(tags.artist != null)
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 there are more than one genre
if(tags.genre.split(', ').length > 1) {
const genres = tags.genre.split(', ')
genres.forEach(genre => {
if(document.querySelector("#lastStep select[name=genre] > option[value='" + genre + "']") != null) {
document.querySelector("#lastStep select[name=genre]").value = genre;
}
})
} else {
if(document.querySelector("#lastStep select[name=genre] > option[value='" + tags.genre + "']") != null) {
document.querySelector("#lastStep select[name=genre]").value = tags.genre;
} else {
console.warn("Unknown genre: " + tags.genre);
document.querySelector("#lastStep select[name=genre]").value = "Other"
}
}
} else {
document.querySelector("#lastStep select[name=genre]").value = "Other"
async detectTags(blob) {
const return_params = {
performer: '',
name: '',
genre: '',
lyrics: '',
explicit: 0,
unlisted: 0,
}
if(tags.comments != null)
document.querySelector("#lastStep textarea[name=lyrics]").value = tags.comments
} else {
fallback()
}
});
function fallback() {
console.info('Tags not found, setting default values.')
return_params.name = remove_file_format(blob.name)
return_params.genre = 'Other'
return_params.performer = tr('track_unknown')
}
u("#backToUpload").on("click", (e) => {
document.querySelector("#firstStep").style.display = "block"
document.querySelector("#lastStep").style.display = "none"
let tags = null
try {
tags = await id3.fromFile(blob)
} catch(e) {
console.error(e)
}
document.querySelector("#lastStep input[name=name]").value = ""
document.querySelector("#lastStep input[name=performer]").value = ""
document.querySelector("#lastStep select[name=genre]").value = ""
document.querySelector("#lastStep textarea[name=lyrics]").value = ""
document.querySelector("#audio_input").value = ""
})
u("#uploadMuziko").on("click", (e) => {
var name_ = document.querySelector("#audio_upload input[name=name]");
var perf_ = document.querySelector("#audio_upload input[name=performer]");
var genre_ = document.querySelector("#audio_upload input[name=genre]");
var lyrics_ = document.querySelector("#audio_upload input[name=lyrics]");
var explicit_ = document.querySelector("#audio_upload input[name=explicit]");
var unlisted_ = document.querySelector("#audio_upload input[name=unlisted]");
name_.value = document.querySelector("#lastStep input[name=name]").value
perf_.value = document.querySelector("#lastStep input[name=performer]").value
genre_.value = document.querySelector("#lastStep select[name=genre]").value
lyrics_.value = document.querySelector("#lastStep textarea[name=lyrics]").value
explicit_.value = document.querySelector("#lastStep input[name=explicit]").checked ? "on" : "off"
unlisted_.value = document.querySelector("#lastStep input[name=unlisted]").checked ? "on" : "off"
$("#audio_upload > form").trigger("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")
})
$("#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.router.route(response.redirect_link)
})
console.log(tags)
if(tags != null) {
console.log("ID" + tags.kind + " detected, setting values...")
if(tags.title) {
return_params.name = tags.title
} else {
fastError(response.flash.message)
return_params.name = remove_file_format(blob.name)
}
if(tags.artist) {
return_params.performer = tags.artist
} else {
return_params.performer = tr('track_unknown')
// todo: split performer and title from filename
}
if(tags.genre != null) {
if(tags.genre.split(', ').length > 1) {
const genres = tags.genre.split(', ')
genres.forEach(genre => {
if(window.openvk.audio_genres[genre]) {
return_params.genre = genre;
}
})
} else {
if(window.openvk.audio_genres.indexOf(tags.genre) != -1) {
return_params.genre = tags.genre
} else {
console.warn("Unknown genre: " + tags.genre)
return_params.genre = 'Other'
}
}
} else {
return_params.genre = 'Other'
}
if(tags.comments != null)
return_params.lyrics = tags.comments
} else {
fallback()
}
return return_params
}
async appendFile(appender)
{
appender.info = await this.detectTags(appender.file)
const audio_index = this.files_list.push(appender) - 1
this.appendAudioFrame(audio_index)
}
appendAudioFrame(audio_index) {
const audio_element = this.files_list[audio_index]
if(!audio_element) {
return
}
const template = u(`
<div class='upload_container_element' data-index="${audio_index}">
<div class='upload_container_name'>
<span>${ovk_proc_strtr(escapeHtml(audio_element.file.name), 63)}</span>
<div id="small_remove_button"></div>
</div>
<table cellspacing="7" cellpadding="0" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('performer')}:</span></td>
<td><input value='${audio_element.info.performer}' name="performer" type="text" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('audio_name')}:</span></td>
<td><input type="text" value='${audio_element.info.name}' name="name" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('genre')}:</span></td>
<td>
<select name="genre"></select>
</td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('lyrics')}:</span></td>
<td><textarea name="lyrics" style="resize: vertical;max-height: 300px;">${audio_element.info.lyrics}</textarea></td>
</tr>
<tr>
<td width="120" valign="top"></td>
<td>
<label style='display:block'><input type="checkbox" name="explicit">${tr('audios_explicit')}</label>
<label><input type="checkbox" name="unlisted">${tr('audios_unlisted')}</label>
</td>
</tr>
</tbody>
</table>
</div>
`)
window.openvk.audio_genres.forEach(genre => {
template.find('select').append(`
<option ${genre == audio_element.info.genre ? 'selected': ''} value='${genre}'>${genre}</option>
`)
})
u('#lastStep #lastStepContainers').append(template)
}
}
u(`#audio_upload input`).on('change', (e) => {
const files = e.target.files
if(files.length <= 0) {
return
}
Array.from(files).forEach(async file => {
let has_duplicates = false
const appender = {
'file': file
}
if(!file.type.startsWith('audio/')) {
makeError(tr('only_audios_accepted', escapeHtml(file.name)))
return
}
window.__audio_upload_page.files_list.forEach(el => {
if(el && file.name == el.file.name) {
has_duplicates = true
}
})
if(!has_duplicates) {
window.__audio_upload_page.appendFile(appender)
}
})
window.__audio_upload_page.hideFirstPage()
})
</script>
{/block}

View file

@ -966,6 +966,14 @@
color: white;
}
#ajax_audio_player #aj_time {
cursor: pointer;
}
#ajax_audio_player #aj_time:hover {
text-decoration: underline;
}
#ajax_audio_player .selectableTrack {
width: 100%;
position: relative;

View file

@ -2261,6 +2261,25 @@ table td[width="120"] {
margin: 0;
}
#upload_container #lastStepContainers {
display: flex;
flex-direction: column;
gap: 10px;
}
#upload_container .upload_container_element {
border: 1px solid #d6d6d6;
background: #f6f6f6;
}
#upload_container .upload_container_element .upload_container_name {
padding: 5px 5px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#audio_upload {
width: 350px;
margin: 20px auto;
@ -3561,7 +3580,7 @@ hr {
cursor: pointer;
}
#upload_container.uploading {
#upload_container .upload_container_element .upload_container_name.uploading {
background: white url('/assets/packages/static/openvk/img/progressbar.gif') !important;
background-position-x: 0% !important;
background-position-y: 0% !important;

View file

@ -690,7 +690,7 @@ window.player = new class {
miniplayer_template.find('#aj_player_close_btn').on('click', (e) => {
this.ajClose()
})
miniplayer_template.find('#aj_player_track_title').on('click', (e) => {
miniplayer_template.find('#aj_time').on('click', (e) => {
if(window.player && window.player.context && window.player.context.object) {
window.router.route(window.player.context.object.url)
}
@ -698,7 +698,7 @@ window.player = new class {
$('#ajax_audio_player').draggable({
cursor: 'grabbing',
containment: 'window',
cancel: '#aj_player_track, #aj_player_volume, #aj_player_buttons',
cancel: '#aj_player_track .selectableTrack, #aj_player_volume .selectableTrack, #aj_player_buttons',
stop: function(e) {
if(window.player.ajaxPlayer.length > 0) {
const left = parseInt(window.player.ajaxPlayer.nodes[0].style.left)
@ -906,6 +906,10 @@ u(document).on("mousemove click mouseup", ".bigPlayer .trackPanel .selectableTra
if(window.player.isAtAudiosPage() && window.player.current_track_id == 0)
return
if(u('.ui-draggable-dragging').length > 0) {
return
}
function __defaultAction(i_time) {
window.player.listen_coef -= 0.5
window.player.audioPlayer.currentTime = i_time
@ -944,6 +948,10 @@ u(document).on("mousemove click mouseup", ".bigPlayer .volumePanelTrack .selecta
if(window.player.isAtAudiosPage() && window.player.current_track_id == 0)
return
if(u('.ui-draggable-dragging').length > 0) {
return
}
function __defaultAction(i_volume) {
window.player.audioPlayer.volume = i_volume
}
@ -1849,3 +1857,66 @@ $(document).on("click", "#bookmarkPlaylist, #unbookmarkPlaylist", (e) => {
}
})
})
u(document).on('click', '.upload_container_element #small_remove_button', (e) => {
if(u('.uploading').length > 0) {
return
}
const element = u(e.target).closest('.upload_container_element')
const element_index = Number(element.attr('data-index'))
element.remove()
window.__audio_upload_page.files_list[element_index] = null
if(u('#lastStep .upload_container_element').length < 1) {
window.__audio_upload_page.showFirstPage()
}
})
u(document).on('click', `#upload_container #uploadMusic`, async (e) => {
const current_upload_page = location.href
let end_redir = ''
u('#lastStepButtons').addClass('lagged')
for(const elem of u('#lastStepContainers .upload_container_element').nodes) {
if(!elem) {
return
}
const elem_u = u(elem)
const index = elem.dataset.index
const file = window.__audio_upload_page.files_list[index]
if(!file || !index) {
return
}
elem_u.addClass('lagged').find('.upload_container_name').addClass('uploading')
// Upload process
const fd = serializeForm(elem)
fd.append('blob', file.file)
fd.append('ajax', 1)
fd.append('hash', window.router.csrf)
const result = await fetch(current_upload_page, {
method: 'POST',
body: fd,
})
const result_text = await result.json()
if(result_text.success && result_text.redirect_link) {
end_redir = result_text.redirect_link
}
await sleep(6000)
elem_u.remove()
}
if(current_upload_page == location.href) {
window.router.route(end_redir)
}
})
u(document).on("drop", "#upload_container", function (e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'move';
document.getElementById("audio_input").files = e.dataTransfer.files
u("#audio_input").trigger("change")
})

View file

@ -1125,6 +1125,11 @@ u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical
return
})
u(document).on("dragover drop", async (e) => {
e.preventDefault()
return false;
})
u(document).on('dragleave dragend', '#write .post-horizontal .upload-item, .post-vertical .upload-item', (e) => {
//console.log(e)
u(e.target).closest('.upload-item').removeClass('dragged')

View file

@ -224,7 +224,7 @@ u(document).on('click', 'a', async (e) => {
return
}
if(target.download != null) {
if(target.nodes[0].hasAttribute('download')) {
console.log('AJAX | Skipped because its download')
return
}

View file

@ -238,3 +238,13 @@ async function copyToClipboard(text) {
}
}
}
function remove_file_format(text)
{
return text.replace(/\.[^.]*$/, '')
}
function sleep(time)
{
return new Promise((resolve) => setTimeout(resolve, time));
}

View file

@ -882,7 +882,7 @@
"genre" = "Genre";
"lyrics" = "Lyrics";
"select_another_file" = "Select another file";
"select_another_file" = "Upload another file";
"limits" = "Limits";
"select_audio" = "Select audio from your computer";

View file

@ -837,7 +837,7 @@
"genre" = "Жанр";
"lyrics" = "Текст";
"select_another_file" = "Выбрать другой файл";
"select_another_file" = "Загрузить ещё";
"limits" = "Ограничения";
"select_audio" = "Выберите аудиозапись на Вашем компьютере";