Commit 772b16bd authored by jackie / Andrea Ida Malkah Klaura's avatar jackie / Andrea Ida Malkah Klaura
Browse files

move all file related actions to store

parent 2ac89e89
......@@ -278,7 +278,6 @@
<script>
import { mapGetters } from 'vuex'
import axios from 'axios'
import filesize from 'filesize'
import prettyDate from '../../mixins/prettyDate'
import importLog from './ImportLog.vue'
......@@ -375,213 +374,69 @@ export default {
}
},
// Deletes a file with a specific ID calling the AuRa tank API and afterwards
// fetching a fresh list of files from it.
deleteFile: function (id) {
let uri = process.env.VUE_APP_API_TANK + 'shows/' + this.selectedShow.slug + '/files/' + id
// TODO: add mechanism to indicate the running delete request in the files table
axios.delete(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$store.state.auth.user.access_token },
}).then(() => {
this.$log.debug(`Fetching files for show ${this.selectedShow.slug} (ID: ${id})`)
this.fetchFiles(this.selectedShow.slug)
}).catch(error => {
// if there was a 409 Conflict response it means, that this file is
// still used in one or more playlists.
if (error.response.status === 409) {
let pls = error.response.data.detail.playlists.length
let msg = 'Cannot delete file. Still used in ' + pls + ' playlists:\n\n'
for (let pl of error.response.data.detail.playlists) {
msg += 'ID: ' + pl.id
if (pl.description) {
msg += ' (' + pl.description + ')'
}
msg += '\n'
}
msg += '\nIf you want to delete the file, remove it from those playlists first.'
alert(msg)
} else {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
alert('Error: could not delete file. See console for details.')
}
this.$store.dispatch('files/deleteFile', {
show: this.selectedShow.slug,
file: id,
})
},
// With this function we add a new file in the AuRa tank by calling its API.
// Depending on wheter we add a remote file which tank then imports by itself,
// or if we want to upload a local file, we source-uri has to look different.
// And for uploading a local file this is just the first step. Afterwards the
// actual upload has to be started with the startUpload function.
addFile: function () {
var uri = process.env.VUE_APP_API_TANK + 'shows/' + this.selectedShow.slug + '/files'
if (this.addNewFileURI) {
// TODO: add mechanism to indicate the running post request in the files table
axios.post(uri, { 'source-uri': this.uploadSourceURI }, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$store.state.auth.user.access_token }
}).then(() => {
this.fetchFiles(this.selectedShow.slug)
if (this.uploadInterval === null) {
this.uploadInterval = setInterval(() => { this.fetchImports(this.selectedShow.slug) }, 250)
}
}).catch(error => {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
alert('Error: could not add the new remote import. See console for details.')
})
} else if (this.uploadSourceFile) {
// TODO: add mechanism to indicate the running post request in the files table
axios.post(uri, { 'source-uri': encodeURI(encodeURI('upload://' + this.uploadSourceFile.name)) }, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$store.state.auth.user.access_token }
}).then(response => {
this.startUpload(response.data.id)
addFile () {
this.$store.dispatch('files/addFile', {
show: this.selectedShow.slug,
addNewFileURI: this.addNewFileURI,
uploadSourceURI: this.uploadSourceURI,
uploadSourceFile: this.uploadSourceFile,
callback: () => {
this.fetchFiles(this.selectedShow.slug)
if (this.uploadInterval === null) {
this.uploadInterval = setInterval(() => { this.fetchImports(this.selectedShow.slug) }, 250)
this.uploadInterval = setInterval(() => { this.fetchImports() }, 250)
}
}).catch(error => {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
alert('Error: could not add the new file upload. See console for details.')
})
} else {
alert('Something is wrong. You have choosen to upload a file, but the corresponding file object does not exist.')
}
},
// When a new file was added with the addFile function we can start an upload
// fetching the import endpoint of this file and then call the upload
// function, which atually puts the file onto the server.
startUpload: function (id) {
var uri = process.env.VUE_APP_API_TANK + 'shows/' + this.selectedShow.slug + '/files/' + id + '/import'
axios.get(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$store.state.auth.user.access_token },
params: {'wait-for': 'running'}
}).then(
this.upload(id)
).catch(error => {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
alert('Error: could not start the file upload. See console for details.')
},
})
},
// Upload a file to the AuRa tank API - given it was created with the addFile
// and started with the startUpload methods.
upload: function (id) {
/*
* NOTE: there is no npm package for flow.js and importing it manually did not
* work so far. therefore this is commented out and we are using the simple
* upload method, until there is a nice npm package for flow.js or somone
* resolves this issue otherwise
var flow = new Flow({
target: process.env.VUE_APP_API_TANK + 'shows/' + this.selectedShow.slug + '/files/' + id + '/upload',
chunkSize: 100 * 1024,
prioritizeFirstAndLastChunk: true
})
flow.on('fileSuccess', function(file, message) {
this.$log.error(file, message)
})
flow.on('fileError', function(file, message) {
this.$log.error(file, message)
alert('Error: could not upload your file. See console for details.')
})
flow.addFile(this.uploadSourceFile)
flow.upload()
*/
var uri = process.env.VUE_APP_API_TANK + 'shows/' + this.selectedShow.slug + '/files/' + id + '/upload'
axios.put(uri, this.uploadSourceFile, {
withCredentials: true,
headers: {
'Authorization': 'Bearer ' + this.$store.state.auth.user.access_token,
'Content-Type': 'application/octet-stream'
}
}).then(() => {
this.$log.info('Sucessfully uploaded file.')
// now we start polling for the import progress
// the fetchImports function has to make sure to deactivate the interval
// again, when all running imports are done (in this first raw version;
// ideally we should refine this so that every single file gets updated independently)
//this.uploadInterval = setInterval(() => { this.fetchImports(this.selectedShow.slug) }, 100)
}).catch(error => {
if (error.response.status === 500 && error.response.data.error === 'ffmpeg returned 1') {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
// if we use a file format that is not supported by ffmpeg, we should find
// the second to last line should notify us about invalid data
let ffmpegError = error.response.data.detail[error.response.data.detail.length - 2]
if (ffmpegError.line === 'pipe:: Invalid data found when processing input') {
// in this case we can make the error message in the files table more specific
alert('Error: import aborted. The audio data format of your file is not valid!')
} else {
alert('Error: ffmpeg could not processs your file! See console for details.')
fetchImports () {
this.$store.dispatch('files/fetchImports', {
show: this.selectedShow.slug,
callback: (response) => {
// if all imports are done, we will receive an empty result set and stop
// polling the server again. then we can also refetch all file statuses.
if (response.data.results.length === 0) {
clearInterval(this.uploadInterval)
this.uploadInterval = null
this.fetchFiles()
}
} else {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
alert('Error: could not finish the file upload/import. See console for details.')
}
})
},
// This function fetches all running imports for a given show. It should
// be called periodically to reflect the upload/import progress. When no
// more active imports are available the corresponding updateInterval
// should be cleared again.
fetchImports: function (slug){
var uri = process.env.VUE_APP_API_TANK + 'shows/' + slug + '/imports'
axios.get(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$store.state.auth.user.access_token }
}).then(response => {
// if all imports are done, we will receive an empty result set and stop
// polling the server again. then we can also refetch all file statuses.
if (response.data.results.length === 0) {
clearInterval(this.uploadInterval)
this.uploadInterval = null
this.fetchShow(slug)
}
// if there are still imports going on, we just updated the respective
// progress information for the import we just fetched and rerender
// the filesTable
else {
for (var i in response.data.results) {
var f = this.getFileById(response.data.results[i].id)
if (f) {
f.source.import.progress = {
// if there are still imports going on, we just update the respective
// progress information for the import we just fetched and rerender
// the filesTable
else {
for (let i in response.data.results) {
this.$store.commit('files/updateProgress', {
id: response.data.results[i].id,
progress: response.data.results[i].progress.progress,
step: response.data.results[i].progress.step
}
})
let p = '[import]'
p += ' id: ' + response.data.results[i].id
p += ', progress: ' + response.data.results[i].progress.progress
p += ', step: ' + response.data.results[i].progress.step
this.$log.debug(p)
this.$refs.filesTable.refresh()
}
this.$refs.filesTable.refresh()
}
},
callbackCancel: () => {
// also in case of an error we have to cancel the uploadInterval,
// otherwise we'll continuosly get alerts if the error is persistent
clearInterval(this.uploadInterval)
this.uploadInterval = null
}
}).catch(error => {
// also in case of an error we have to cancel the uploadInterval,
// otherwise we'll continuosly get alerts if the error is persistent
clearInterval(this.uploadInterval)
if (error.response) {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
} else {
this.$log.error(error)
}
alert('Error: could not fetch current imports. See console for details.')
})
},
// Fetch all files for a given show from the AuRa tank API
fetchFiles: function (slug) {
this.$store.dispatch('files/fetchFiles', {slug: slug})
fetchFiles () {
this.$store.dispatch('files/fetchFiles', {slug: this.selectedShow.slug})
},
},
......
......@@ -36,8 +36,24 @@ const mutations = {
let file = state.files.find(f => f.id === data.id)
file.metadata = data.metadata
},
deleteFile (state, id) {
let i = state.files.findIndex(f => f.id === id)
if (i >= 0) {
state.files.splice(i, 1)
}
},
setLog (state, log) { state.log = log },
updateProgress (state, data) {
let file = state.files.find(f => f.id == data.id)
if (file) {
file.source.import.progress = {
progress: data.progress,
step: data.step
}
}
},
}
const actions = {
......@@ -78,6 +94,23 @@ const actions = {
})
},
// This function fetches all running imports for a given show. It should
// be called periodically to reflect the upload/import progress. When no
// more active imports are available the corresponding updateInterval
// should be cleared again.
fetchImports (ctx, data) {
let uri = process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/imports'
axios.get(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token }
}).then(response => {
if (data && typeof(data.callback) === 'function') { data.callback(response) }
}).catch(error => {
handleApiError(this, error, 'could not fetch current imports')
if (data && typeof(data.callbackCancel) === 'function') { data.callbackCancel() }
})
},
updateFile (ctx, data) {
let uri = process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/files/' + data.file
// TODO: add mechanism to indicate the running patch request in the files table
......@@ -91,6 +124,137 @@ const actions = {
handleApiError(this, error, 'could not update file')
if (data && typeof(data.callbackCancel) === 'function') { data.callbackCancel() }
})
},
// With this function we add a new file in the AuRa tank by calling its API.
// Depending on wheter we add a remote file which tank then imports by itself,
// or if we want to upload a local file, the source-uri has to look different.
// And for uploading a local file this is just the first step. Afterwards the
// actual upload has to be started with the startUpload function.
addFile (ctx, data) {
let uri = process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/files'
let payload = {}
if (data.addNewFileURI) {
payload['source-uri'] = data.uploadSourceURI
} else {
payload['source-uri'] = encodeURI(encodeURI('upload://' + data.uploadSourceFile.name))
}
axios.post(uri, payload, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token }
}).then(response => {
if (!data.addNewFileURI) {
ctx.dispatch('startUpload', {
show: data.show,
file: response.data.id
})
}
if (data && typeof(data.callback) === 'function') { data.callback() }
}).catch(error => {
let msg = 'could not add the new remote import'
if (!data.addNewFileURI) { msg = 'could not add the new file upload' }
handleApiError(this, error, msg)
if (data && typeof(data.callbackCancel) === 'function') { data.callbackCancel() }
})
},
// When a new file was added with the addFile dispatch we can start an upload
// fetching the import endpoint of this file and then call the upload
// function, which atually puts the file onto the server.
startUpload (ctx, data) {
let uri = process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/files/' + data.file + '/import'
axios.get(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token },
params: {'wait-for': 'running'}
}).then(
ctx.dispatch('upload', data)
).catch(error => {
handleApiError(this, error, 'could not start the file upload')
})
},
// Upload a file to the AuRa tank API - given it was created with the addFile
// and started with the startUpload dispatch.
upload (ctx, data) {
/*
* NOTE: there is no npm package for flow.js and importing it manually did not
* work so far. therefore this is commented out and we are using the simple
* upload method, until there is a nice npm package for flow.js or somone
* resolves this issue otherwise
let flow = new Flow({
target: process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/files/' + data.file + '/upload',
chunkSize: 100 * 1024,
prioritizeFirstAndLastChunk: true
})
flow.on('fileSuccess', function(file, message) {
this.$log.error(file, message)
})
flow.on('fileError', function(file, message) {
this.$log.error(file, message)
alert('Error: could not upload your file. See console for details.')
})
flow.addFile(this.uploadSourceFile)
flow.upload()
*/
let uri = process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/files/' + data.file + '/upload'
axios.put(uri, data.uploadSourceFile, {
withCredentials: true,
headers: {
'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token,
'Content-Type': 'application/octet-stream'
}
}).then(() => {
this.$log.info('Sucessfully uploaded file: ' + data.uploadSourceFile.name)
}).catch(error => {
if (error.response.status === 500 && error.response.data.error === 'ffmpeg returned 1') {
this.$log.error(error.response.status + ' ' + error.response.statusText)
this.$log.error(error.response)
// if we use a file format that is not supported by ffmpeg, we should find
// the second to last line should notify us about invalid data
let ffmpegError = error.response.data.detail[error.response.data.detail.length - 2]
if (ffmpegError.line === 'pipe:: Invalid data found when processing input') {
// in this case we can make the error message in the files table more specific
alert('Error: import aborted. The audio data format of your file is not valid!')
} else {
alert('Error: ffmpeg could not process your file! See console for details.')
}
} else {
handleApiError(this, error, 'could not finish the file upload/import')
}
})
},
// Deletes a file with a specific ID calling the AuRa tank API
deleteFile (ctx, data) {
let uri = process.env.VUE_APP_API_TANK + 'shows/' + data.show + '/files/' + data.file
// TODO: add mechanism to indicate the running delete request in the files table
axios.delete(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token },
}).then(() => {
ctx.commit('deleteFile', data.file)
if (data && typeof(data.callback) === 'function') { data.callback() }
}).catch(error => {
// if there was a 409 Conflict response it means, that this file is
// still used in one or more playlists.
if (error.response && error.response.status === 409) {
let pls = error.response.data.detail.playlists.length
let msg = 'Cannot delete file. Still used in ' + pls + ' playlists:\n\n'
for (let pl of error.response.data.detail.playlists) {
msg += 'ID: ' + pl.id
if (pl.description) {
msg += ' (' + pl.description + ')'
}
msg += '\n'
}
msg += '\nIf you want to delete the file, remove it from those playlists first.'
alert(msg)
} else {
handleApiError(this, error, 'could not delete file')
if (data && typeof(data.callbackCancel) === 'function') { data.callbackCancel() }
}
})
},
......
Markdown is supported
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