262 lines
7.0 KiB
Vue
262 lines
7.0 KiB
Vue
<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> |