Commit 1aa3630b authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch '598-allow-opus-upload' into 'develop'

Fix #598: Allow opus file upload

Closes #598

See merge request funkwhale/funkwhale!461
parents 109b8019 bb1de481
Allow opus file upload (#598)
...@@ -66,18 +66,19 @@ ...@@ -66,18 +66,19 @@
:multiple="true" :multiple="true"
:data="uploadData" :data="uploadData"
:drop="true" :drop="true"
accept="audio/*" :extensions="supportedExtensions"
v-model="files" v-model="files"
name="audio_file" name="audio_file"
:thread="1" :thread="1"
@input-filter="inputFilter"
@input-file="inputFile" @input-file="inputFile"
ref="upload"> ref="upload">
<i class="upload icon"></i> <i class="upload icon"></i>&nbsp;
<translate>Click to select files to upload or drag and drop files or directories</translate> <translate>Click to select files to upload or drag and drop files or directories</translate>
<br />
<br />
<i><translate :translate-params="{extensions: supportedExtensions.join(', ')}"> Supported extensions: %{ extensions }</translate></i>
</file-upload-widget> </file-upload-widget>
</div> </div>
<table v-if="files.length > 0" class="ui single line table"> <table v-if="files.length > 0" class="ui single line table">
<thead> <thead>
<tr> <tr>
...@@ -122,184 +123,213 @@ ...@@ -122,184 +123,213 @@
</template> </template>
<script> <script>
import $ from 'jquery' import $ from "jquery";
import axios from 'axios' import axios from "axios";
import logger from '@/logging' import logger from "@/logging";
import FileUploadWidget from './FileUploadWidget' import FileUploadWidget from "./FileUploadWidget";
import LibraryFilesTable from '@/views/content/libraries/FilesTable' import LibraryFilesTable from "@/views/content/libraries/FilesTable";
import moment from 'moment' import moment from "moment";
export default { export default {
props: ['library', 'defaultImportReference'], props: ["library", "defaultImportReference"],
components: { components: {
FileUploadWidget, FileUploadWidget,
LibraryFilesTable LibraryFilesTable
}, },
data () { data() {
let importReference = this.defaultImportReference || moment().format() let importReference = this.defaultImportReference || moment().format();
this.$router.replace({query: {import: importReference}}) this.$router.replace({ query: { import: importReference } });
return { return {
files: [], files: [],
currentTab: 'summary', currentTab: "summary",
uploadUrl: '/api/v1/uploads/', uploadUrl: "/api/v1/uploads/",
importReference, importReference,
supportedExtensions: ["flac", "ogg", "mp3", "opus"],
uploads: { uploads: {
pending: 0, pending: 0,
finished: 0, finished: 0,
skipped: 0, skipped: 0,
errored: 0, errored: 0,
objects: {}, objects: {}
}, },
processTimestamp: new Date() processTimestamp: new Date()
} };
}, },
created () { created() {
this.fetchStatus() this.fetchStatus();
this.$store.commit('ui/addWebsocketEventHandler', { this.$store.commit("ui/addWebsocketEventHandler", {
eventName: 'import.status_updated', eventName: "import.status_updated",
id: 'fileUpload', id: "fileUpload",
handler: this.handleImportEvent handler: this.handleImportEvent
}) });
}, },
destroyed () { destroyed() {
this.$store.commit('ui/removeWebsocketEventHandler', { this.$store.commit("ui/removeWebsocketEventHandler", {
eventName: 'import.status_updated', eventName: "import.status_updated",
id: 'fileUpload', id: "fileUpload"
}) });
}, },
methods: { methods: {
inputFilter (newFile, oldFile, prevent) { inputFile(newFile, oldFile) {
if (newFile && !oldFile) { this.$refs.upload.active = true;
let extension = newFile.name.split('.').pop()
if (['ogg', 'mp3', 'flac'].indexOf(extension) < 0) {
prevent()
}
}
},
inputFile (newFile, oldFile) {
this.$refs.upload.active = true
}, },
fetchStatus () { fetchStatus() {
let self = this let self = this;
let statuses = ['pending', 'errored', 'skipped', 'finished'] let statuses = ["pending", "errored", "skipped", "finished"];
statuses.forEach((status) => { statuses.forEach(status => {
axios.get('uploads/', {params: {import_reference: self.importReference, import_status: status, page_size: 1}}).then((response) => { axios
self.uploads[status] = response.data.count .get("uploads/", {
}) params: {
}) import_reference: self.importReference,
import_status: status,
page_size: 1
}
})
.then(response => {
self.uploads[status] = response.data.count;
});
});
}, },
updateProgressBar () { updateProgressBar() {
$(this.$el).find('.progress').progress({ $(this.$el)
total: this.uploads.length * 2, .find(".progress")
value: this.uploadedFilesCount + this.finishedJobs .progress({
}) total: this.uploads.length * 2,
value: this.uploadedFilesCount + this.finishedJobs
});
}, },
disconnect () { disconnect() {
if (!this.bridge) { if (!this.bridge) {
return return;
} }
this.bridge.socket.close(1000, 'goodbye', {keepClosed: true}) this.bridge.socket.close(1000, "goodbye", { keepClosed: true });
}, },
openWebsocket () { openWebsocket() {
this.disconnect() this.disconnect();
let self = this let self = this;
let token = this.$store.state.auth.token let token = this.$store.state.auth.token;
const bridge = new WebSocketBridge() const bridge = new WebSocketBridge();
this.bridge = bridge this.bridge = bridge;
let url = this.$store.getters['instance/absoluteUrl'](`api/v1/activity?token=${token}`) let url = this.$store.getters["instance/absoluteUrl"](
url = url.replace('http://', 'ws://') `api/v1/activity?token=${token}`
url = url.replace('https://', 'wss://') );
bridge.connect(url) url = url.replace("http://", "ws://");
bridge.listen(function (event) { url = url.replace("https://", "wss://");
self.handleEvent(event) bridge.connect(url);
}) bridge.listen(function(event) {
bridge.socket.addEventListener('open', function () { self.handleEvent(event);
console.log('Connected to WebSocket') });
}) bridge.socket.addEventListener("open", function() {
console.log("Connected to WebSocket");
});
}, },
handleImportEvent (event) { handleImportEvent(event) {
let self = this let self = this;
if (event.upload.import_reference != self.importReference) { if (event.upload.import_reference != self.importReference) {
return return;
} }
this.$nextTick(() => { this.$nextTick(() => {
self.uploads[event.old_status] -= 1 self.uploads[event.old_status] -= 1;
self.uploads[event.new_status] += 1 self.uploads[event.new_status] += 1;
self.uploads.objects[event.track_file.uuid] = event.track_file self.uploads.objects[event.upload.uuid] = event.track_file;
self.triggerReload() self.triggerReload();
}) });
}, },
triggerReload: _.throttle(function () { triggerReload: _.throttle(
this.processTimestamp = new Date() function() {
}, 10000, {'leading': true}) this.processTimestamp = new Date();
},
10000,
{ leading: true }
)
}, },
computed: { computed: {
labels () { labels() {
let denied = this.$gettext('Upload refused, ensure the file is not too big and you have not reached your quota') let denied = this.$gettext(
let server = this.$gettext('Impossible to upload this file, ensure it is not too big') "Upload refused, ensure the file is not too big and you have not reached your quota"
let network = this.$gettext('A network error occured while uploading this file') );
let timeout = this.$gettext('Upload timeout, please try again') let server = this.$gettext(
"Impossible to upload this file, ensure it is not too big"
);
let network = this.$gettext(
"A network error occured while uploading this file"
);
let timeout = this.$gettext("Upload timeout, please try again");
let extension = this.$gettext(
"Invalid file type, ensure you are uploading an audio file. Supported file extensions are %{ extensions }"
);
return { return {
tooltips: { tooltips: {
denied, denied,
server, server,
network, network,
timeout timeout,
extension: this.$gettextInterpolate(extension, {
extensions: this.supportedExtensions.join(", ")
})
} }
} };
}, },
uploadedFilesCount () { uploadedFilesCount() {
return this.files.filter((f) => { return this.files.filter(f => {
return f.success return f.success;
}).length }).length;
}, },
uploadingFilesCount () { uploadingFilesCount() {
return this.files.filter((f) => { return this.files.filter(f => {
return !f.success && !f.error return !f.success && !f.error;
}).length }).length;
}, },
erroredFilesCount () { erroredFilesCount() {
return this.files.filter((f) => { return this.files.filter(f => {
return f.error return f.error;
}).length }).length;
}, },
processableFiles () { processableFiles() {
return this.uploads.pending + this.uploads.skipped + this.uploads.errored + this.uploads.finished + this.uploadedFilesCount return (
this.uploads.pending +
this.uploads.skipped +
this.uploads.errored +
this.uploads.finished +
this.uploadedFilesCount
);
}, },
processedFilesCount () { processedFilesCount() {
return this.uploads.skipped + this.uploads.errored + this.uploads.finished return (
this.uploads.skipped + this.uploads.errored + this.uploads.finished
);
}, },
uploadData: function () { uploadData: function() {
return { return {
'library': this.library.uuid, library: this.library.uuid,
'import_reference': this.importReference, import_reference: this.importReference
} };
}, },
sortedFiles () { sortedFiles() {
// return errored files on top // return errored files on top
return this.files.sort((f) => { return this.files.sort(f => {
if (f.errored) { if (f.errored) {
return -5 return -5;
} }
if (f.success) { if (f.success) {
return 5 return 5;
} }
return 0 return 0;
}) });
} }
}, },
watch: { watch: {
uploadedFilesCount () { uploadedFilesCount() {
this.updateProgressBar() this.updateProgressBar();
}, },
finishedJobs () { finishedJobs() {
this.updateProgressBar() this.updateProgressBar();
}, },
importReference: _.debounce(function () { importReference: _.debounce(function() {
this.$router.replace({query: {import: this.importReference}}) this.$router.replace({ query: { import: this.importReference } });
}, 500) }, 500)
} }
} };
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment