client/src/components/dialogs/AudioUpload.vue

262 lines
7.0 KiB
Vue
Raw Normal View History

2023-02-08 12:37:55 +01:00
<template>
<DialogBase
ref="dialogWindow"
:closeOnButtonClick="false"
:closeOnFocusLost="false"
:maxSize="true"
:enableFooterButtons="uploadable"
title="Upload audio files..."
buttonText="Upload"
buttonClass="success large"
@accept="uploadFiles(0)"
@closing="closing"
>
<div class="flex-column grow">
<input
style="display: none"
ref="files"
type="file"
accept="audio/*"
@change="openFiles"
multiple
/>
<ul id="audioUploadFiles">
<li v-for="(file, i) in files" :key="i" class="pa-top">
<h4 class="ma-left darkgray-text">{{ file.name }}</h4>
<div class="flex-row ma">
<awesome-icon
:icon="file.state == 'uploading' ? 'upload' : 'check'"
size="2x"
:class="{
'success-text': file.state == 'done',
'gray-text': file.state == 'ready',
'primary-text': file.state == 'uploading',
}"
/>
<input
placeholder="artist name"
type="text"
v-model="file.tags.artist"
class="left pa8"
/>
<input
placeholder="album name"
type="text"
v-model="file.tags.album"
class="pa8"
/>
<input
placeholder="track number"
title="track number"
type="text"
v-model="file.tags.track"
style="width: 32px"
class="right pa8"
/>
<input
placeholder="track title"
type="text"
v-model="file.tags.title"
class="pa8 grow"
/>
<input
placeholder="genre"
type="text"
v-model="file.tags.genre"
class="pa8"
/>
<input
placeholder="year"
type="text"
v-model="file.tags.year"
style="width: 48px"
class="right pa8"
/>
<button
class="danger"
title="Remove from list"
@click="remove(file)"
>
<awesome-icon icon="minus" />
</button>
</div>
<hr />
</li>
</ul>
<div
class="flex-column grow center ma primary-text"
id="dropzone"
@dragover="dropover"
@drop="droped"
@click="emitFileClick"
>
<h2>
Drop your files here<br />
<awesome-icon icon="plus" size="4x" />
</h2>
</div>
</div>
</DialogBase>
</template>
<script>
import mmreader from "mp3tag.js";
export default {
data() {
return {
files: [],
};
},
methods: {
open() {
this.$refs.dialogWindow.open();
},
closing() {
this.files = [];
},
dropover(e) {
e.preventDefault();
e.stopPropagation();
},
droped(e) {
e.preventDefault();
e.stopPropagation();
let files = [];
for (let i = 0; i < e.dataTransfer.files.length; i++) {
let file = e.dataTransfer.files[i];
if (file.type.indexOf("audio/") == 0) {
files.push({ file: file, state: "ready" });
}
}
if (files.length > 0) {
this.readTags(files, 0);
}
},
emitFileClick() {
this.$refs.files.click();
},
openFiles(e) {
let files = [];
if (e.srcElement.value) {
for (let i = 0; i < e.srcElement.files.length; i++) {
let file = e.srcElement.files[i];
files.push({ file: file, state: "ready" });
}
this.readTags(files, 0);
}
},
readTags(files, index) {
let fileReader = new FileReader();
var file = files[index];
fileReader.onload = () => {
if (!this.files.find((f) => f.file.name == file.file.name)) {
const mp3tag = new mmreader(fileReader.result);
mp3tag.read();
file.tags = {};
file.tags.title = mp3tag.tags.title;
file.tags.artist = mp3tag.tags.artist;
file.tags.album = mp3tag.tags.album;
file.tags.track = mp3tag.tags.track;
file.tags.year = mp3tag.tags.year;
if (!file.tags.title || file.tags.title.trim() == "") {
file.tags.title = file.file.name;
}
this.files.push(file);
}
if (files.length > index + 1) {
this.readTags(files, ++index);
} else {
this.files.sort((a, b) => {
if (
a.tags.artist > b.tags.artist ||
(a.tags.artist == b.tags.artist && a.tags.album > b.tags.album) ||
(a.tags.artist == b.tags.artist &&
a.tags.album == b.tags.album &&
a.tags.track > b.tags.track) ||
(a.tags.artist == b.tags.artist &&
a.tags.album == b.tags.album &&
a.tags.track == b.tags.track &&
a.tags.title > b.tags.title)
) {
return 1;
}
return -1;
});
}
};
fileReader.readAsArrayBuffer(file.file);
},
remove(file) {
this.files.splice(this.files.indexOf(file), 1);
},
uploadFiles(index = 0) {
if (this.files.length > index) {
let file = this.files[index];
if (file.state == "ready") {
file.state = "uploading";
let formData = new FormData();
formData.append("file", file.file);
formData.append("track", JSON.stringify(file.tags));
this.$store
.dispatch("tracks/upload", formData)
.then(() => {
file.state = "done";
this.uploadFiles(++index);
})
.catch((err) => {
console.log(err);
this.$refs.dialogWindow.messageText = err;
});
} else {
this.uploadFiles(++index);
}
} else {
this.$store.dispatch("albums/loadNewest");
this.$refs.dialogWindow.close();
}
},
},
computed: {
uploadable() {
let has_empty_artist =
this.files.find((f) => (f.tags.artist || "").trim() == "") != undefined;
let has_empty_album =
this.files.find((f) => (f.tags.album || "").trim() == "") != undefined;
let has_empty_title =
this.files.find((f) => (f.tags.title || "").trim() == "") != undefined;
let has_empty_track =
this.files.find((f) => (f.tags.track || "").trim() == "") != undefined;
return (
!has_empty_artist &&
!has_empty_album &&
!has_empty_title &&
!has_empty_track
);
},
},
};
</script>
<style scoped>
#audioUploadFiles input {
border: none;
font-size: large;
}
#audioUploadFiles button {
height: 32px;
min-width: 32px;
justify-content: center;
align-self: center;
border-radius: 16px;
}
#dropzone {
border: 2px dashed var(--primary);
cursor: pointer;
}
</style>