Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
AURA
dashboard
Commits
30a9c80d
Commit
30a9c80d
authored
May 29, 2020
by
jackie / Andrea Ida Malkah Klaura
Browse files
move all playlist related actions to store
parent
772b16bd
Changes
4
Show whitespace changes
Inline
Side-by-side
src/components/FileManager.vue
View file @
30a9c80d
...
...
@@ -17,372 +17,43 @@
<!-- All the UI for creating and editing playlists is only shown if the user
choose to edit playlists in the jumbotron above -->
<div
v-if=
"mode === 'playlists'"
>
<!-- Modal: Add new file
This is the modal that is used when the user clicks on the
"Upload or add a file" button. When the user hits the OK button,
the addFile method will be called. -->
<b-modal
id=
"modal-edit-playlist"
size=
"lg"
:title=
"playlistEditor.mode === 'edit' ? 'Edit playlist' : 'Add new playlist'"
@
ok=
"storePlaylist"
>
<b-row
v-if=
"playlistEditor.mode === 'edit'"
style=
"padding-bottom: 1em;"
>
<b-col
cols=
"3"
>
Playlist index:
</b-col>
<b-col>
{{
playlistEditor
.
id
}}
</b-col>
</b-row>
<b-row>
<b-col
cols=
"3"
>
Description:
</b-col>
<b-col>
<b-form-input
v-model=
"playlistEditor.description"
type=
"text"
/>
</b-col>
</b-row>
<hr>
<p>
Playlist entries:
</p>
<!-- If no entries are here (i.e. we add a new playlist), only show
a hint that there's nothing here yet. -->
<div
v-if=
"playlistEditor.entries.length === 0"
align=
"center"
>
No entries yet. Add some.
</div>
<!-- As soon as we have at least one entry in our temporary playlist
we can display a table with all the info and action buttons -->
<div
v-else
>
<b-table
ref=
"playlistEditTable"
striped
:items=
"playlistEditor.entries"
:fields=
"playlistEditTableFields"
>
<!-- Column: Index
Here we just use the array index, because the playlist entries
are ordered as an array, without the need for an extra id field
-->
<template
v-slot:cell(id)=
"data"
>
{{
data
.
index
+
1
}}
.
</
template
>
<!-- Column: Type
Based on the entry content (either file or uri), we display
a small badge indicating which type of source this is
-->
<
template
v-slot:cell(type)=
"data"
>
<b-badge
v-if=
"data.item.file"
variant=
"success"
>
File
</b-badge>
<b-badge
v-else-if=
"data.item.uri.startsWith('line://')"
variant=
"info"
>
Line-in
</b-badge>
<b-badge
v-else-if=
"data.item.uri.startsWith('http://') || data.item.uri.startsWith('https://')"
variant=
"light"
>
Stream
</b-badge>
<b-badge
v-else
variant=
"dark"
>
Other
</b-badge>
</
template
>
<!-- Column: Source
Here we display where this playlist entry is coming from
-->
<
template
v-slot:cell(source)=
"data"
>
<span
v-if=
"data.item.file"
>
{{
getFileTitleForPlaylist
(
data
.
item
.
file
.
show
,
data
.
item
.
file
.
id
)
}}
<small><i>
( file://
{{
data
.
item
.
file
.
show
}}
/
{{
data
.
item
.
file
.
id
}}
)
</i></small>
</span>
<span
v-else
>
{{
data
.
item
.
uri
}}
</span>
</
template
>
<!-- Column: Actions
Finally some buttons to reorder or delete playlist entries
-->
<
template
v-slot:cell(actions)=
"data"
>
<b-button-group
size=
"sm"
>
<b-button
:disabled=
"data.index === 0"
@
click=
"movePlaylistItemUp(data.index)"
>
<b
class=
"upDownArrows"
>
↑
</b>
</b-button>
<b-button
:disabled=
"data.index === playlistEditor.entries.length - 1"
@
click=
"movePlaylistItemDown(data.index)"
>
<b
class=
"upDownArrows"
>
↓
</b>
</b-button>
<b-button
variant=
"danger"
@
click=
"deletePlaylistItem(data.index)"
>
Delete
</b-button>
</b-button-group>
</
template
>
</b-table>
</div>
<hr>
<!-- Below the table with the playlists entry we display buttons to
add new entries to the table - these can either be files from our
uploaded/imported files, or one of the preconfigured inputs, or a
stream.
TODO: should we disable choosing files that are still being imported?
TODO: make the inputs configurable
-->
<div>
<b-modal
id=
"modal-edit-playlist-add-stream"
title=
"Add stream to the playlist"
@
ok=
"addPlaylistItemStream('save')"
>
<b-input
v-model=
"playlistEditor.newStreamURL"
type=
"url"
>
...
</b-input>
</b-modal>
Add:
<b-button-group>
<b-dropdown
text=
"File"
>
<b-dropdown-item
v-for=
"(file, index) in files"
:key=
"index"
@
click=
"addPlaylistItemFile(file.show, file.id)"
>
{{ file.id }}: {{ file.metadata.title ? file.metadata.title : "" }} ({{ prettyNanoseconds(file.duration) }}, {{ prettyFileSize(file.size) }}, {{ file.source.uri }})
</b-dropdown-item>
</b-dropdown>
<b-dropdown
text=
"Line-in"
>
<b-dropdown-item
@
click=
"addPlaylistItemLine('0')"
>
Studio 1
</b-dropdown-item>
<b-dropdown-item
@
click=
"addPlaylistItemLine('1')"
>
Preprod
</b-dropdown-item>
<b-dropdown-item
@
click=
"addPlaylistItemLine('2')"
>
Line 2
</b-dropdown-item>
</b-dropdown>
<b-button
@
click=
"addPlaylistItemStream('openModal')"
>
Stream
</b-button>
</b-button-group>
</div>
</b-modal>
<!-- Only display a spinner if the playlists are not loaded yet -->
<div
v-if=
"!loaded.playlists"
>
<b-row>
<b-col
align=
"center"
>
<img
src=
"../assets/radio.gif"
alt=
"loading data"
>
</b-col>
</b-row>
</div>
<!-- If all playlist data is loaded we can present a create button and
a table of playlists (if there are already any)-->
<div
v-else
>
<!-- If no playlists are available, only show an alert and a create button -->
<div
v-if=
"playlists.length === 0"
align=
"center"
>
<b-alert
show
variant=
"warning"
>
There are no playlists for this show yet.
</b-alert>
<b-button
variant=
"success"
@
click=
"addPlaylist"
>
Create a playlist
</b-button>
</div>
<!-- In case there are playlists, show the button and then the table -->
<div
v-else
>
<div
align=
"center"
style=
"padding-bottom: 1.5em;"
>
<b-button
variant=
"success"
@
click=
"addPlaylist"
>
Create a playlist
</b-button>
</div>
<b-table
ref=
"playlistsTable"
striped
:fields=
"playlistsTableFields"
:items=
"playlists"
>
<!-- Column: Entries
This column displays the number of entries of the playlist.
-->
<
template
v-slot:cell(entries)=
"data"
>
{{
data
.
value
.
length
}}
items
<b-button
v-b-tooltip.html=
"playlistToolTip(data.value)"
variant=
"outline-success"
size=
"sm"
>
show entries
</b-button>
</
template
>
<!-- Column: Last edit
This column lists the last date this playlist was changed.
-->
<
template
v-slot:cell(updated)=
"data"
>
{{
prettyDateTime
(
data
.
value
)
}}
</
template
>
<!-- Column: Actions
This column displays the available buttons for actions the user can
take on this playlist (e.g. editing and deleting).
-->
<
template
v-slot:cell(actions)=
"data"
>
<b-button-group
size=
"sm"
>
<b-button
@
click=
"editPlaylist(data.item.id)"
>
Edit
</b-button>
<b-button
variant=
"danger"
@
click=
"deletePlaylist(data.item.id)"
>
Delete
</b-button>
</b-button-group>
</
template
>
</b-table>
</div>
<!-- End of playlists table -->
</div>
<playlists
/>
</div>
</b-container>
</
template
>
<
script
>
import
{
mapGetters
}
from
'
vuex
'
import
axios
from
'
axios
'
import
filesize
from
'
filesize
'
import
prettyDate
from
'
../mixins/prettyDate
'
import
showSelector
from
'
./ShowSelector.vue
'
import
jumbotron
from
'
./filemanager/Jumbotron.vue
'
import
files
from
'
./filemanager/Files.vue
'
import
playlists
from
'
./filemanager/Playlists.vue
'
export
default
{
components
:
{
'
show-selector
'
:
showSelector
,
'
jumbotron
'
:
jumbotron
,
'
files
'
:
files
,
'
playlists
'
:
playlists
,
},
// generic functions that we want to use from our mixins folder
mixins
:
[
prettyDate
],
// the data this component will be handling: mostly flags and local versions
// of the data fetched from the AuRa tank API
data
()
{
return
{
shows
:
[],
// an array of objects describing our shows (empty at load, will be populated on created())
currentShow
:
0
,
// index of the currently selected show in our shows array
currentShowID
:
0
,
// actual id of the currently selected show
files
:
[],
playlists
:
[],
mode
:
'
files
'
,
uploadInterval
:
null
,
// some flags to signal if API data is already fully loaded
loadedLocal
:
{
playlists
:
false
},
// and for adding and editing the playlists we need some temporary stuff
playlistEditor
:
{
id
:
null
,
mode
:
'
add
'
,
// should be either 'add' or 'edit'
description
:
''
,
entries
:
[],
newStreamURL
:
null
},
// for formatting the buttons - this way we could customize it later
button
:
{
files
:
'
info
'
,
playlists
:
'
outline-info
'
},
// configuration of the playlists table, which will use the playlists array as data
playlistsTableFields
:
[
{
key
:
'
id
'
,
label
:
'
Index
'
},
{
key
:
'
description
'
,
label
:
'
Description
'
},
{
key
:
'
entries
'
,
label
:
'
Entries
'
},
{
key
:
'
updated
'
,
label
:
'
Last edit
'
},
{
key
:
'
actions
'
,
label
:
'
Actions
'
,
class
:
'
text-right
'
},
],
playlistEditTableFields
:
[
{
key
:
'
id
'
,
label
:
'
Index
'
},
{
key
:
'
type
'
,
label
:
'
Type
'
},
{
key
:
'
source
'
,
label
:
'
Source
'
},
{
key
:
'
actions
'
,
label
:
'
Actions
'
,
class
:
'
text-right
'
},
]
}
},
computed
:
{
loaded
()
{
return
{
shows
:
this
.
$store
.
state
.
shows
.
loaded
.
shows
,
files
:
this
.
$store
.
state
.
files
.
loaded
.
files
,
playlists
:
this
.
loadedLocal
.
playlists
}
},
...
mapGetters
({
selectedShow
:
'
shows/selectedShow
'
,
mode
:
'
fileManagerMode
'
})
},
...
...
@@ -400,203 +71,6 @@ export default {
// posting/updating data to the AuRa tank API
methods
:
{
// Just a placeholder function we can use in the UI, to signal if something
// is not yet implemented
notYetImplemented
:
function
()
{
alert
(
'
By the mighty witchcraftry of the mother of time!
\n\n
This feature is not implemented yet.
'
)
},
// We want to have a nice format for the file size, given a size in Bytes.
// For that we're useing the filesize npm module.
prettyFileSize
:
function
(
s
)
{
return
filesize
(
s
)
},
// Returns a file object from the files array, given a file ID
getFileById
:
function
(
id
)
{
for
(
var
i
in
this
.
files
)
{
if
(
this
.
files
[
i
].
id
===
id
)
{
return
this
.
files
[
i
]
}
}
return
null
},
// Returns a playlist object from the playlists array, given a playlist ID.
// If the mode parameter is used and set to 'index', then the playlists
// index in the playlist array is returned instead of the object
getPlaylistById
:
function
(
id
,
mode
)
{
for
(
var
i
in
this
.
playlists
)
{
if
(
this
.
playlists
[
i
].
id
===
id
)
{
if
(
mode
===
'
index
'
)
{
return
i
}
else
{
return
this
.
playlists
[
i
]
}
}
}
return
null
},
// return a string representing a file entry for the playlist editor
getFileTitleForPlaylist
:
function
(
show
,
id
)
{
// TODO: change structure of files array, so we can access all shows
// the user has access to.
var
file
=
this
.
getFileById
(
id
)
if
(
file
&&
file
.
metadata
.
title
)
{
return
show
+
"
:
"
+
file
.
metadata
.
title
}
else
{
return
""
}
},
playlistToolTip
:
function
(
entries
)
{
var
text
=
'
<div style="white-space: nowrap;" align="left">
'
for
(
var
i
in
entries
)
{
text
+=
i
+
'
:
'
+
entries
[
i
].
uri
+
'
<br>
'
}
text
+=
'
</div>
'
return
text
},
movePlaylistItemUp
:
function
(
index
)
{
if
(
index
>
0
&&
index
<
this
.
playlistEditor
.
entries
.
length
)
{
var
temp
=
this
.
playlistEditor
.
entries
[
index
-
1
]
this
.
playlistEditor
.
entries
[
index
-
1
]
=
this
.
playlistEditor
.
entries
[
index
]
this
.
playlistEditor
.
entries
[
index
]
=
temp
this
.
$refs
.
playlistEditTable
.
refresh
()
}
},
movePlaylistItemDown
:
function
(
index
)
{
if
(
index
<
this
.
playlistEditor
.
entries
.
length
-
1
&&
index
>=
0
)
{
var
temp
=
this
.
playlistEditor
.
entries
[
index
+
1
]
this
.
playlistEditor
.
entries
[
index
+
1
]
=
this
.
playlistEditor
.
entries
[
index
]
this
.
playlistEditor
.
entries
[
index
]
=
temp
this
.
$refs
.
playlistEditTable
.
refresh
()
}
},
deletePlaylistItem
:
function
(
index
)
{
if
(
index
>=
0
&&
index
<
this
.
playlistEditor
.
entries
.
length
)
{
this
.
playlistEditor
.
entries
.
splice
(
index
,
1
)
this
.
$refs
.
playlistEditTable
.
refresh
()
}
},
// add a file from the file manager to the playlist that is being edited
addPlaylistItemFile
:
function
(
show
,
id
)
{
var
item
=
{}
item
.
file
=
{
show
:
show
,
id
:
id
}
this
.
playlistEditor
.
entries
.
push
(
item
)
},
// add a line input to the playlist that is being edited
addPlaylistItemLine
:
function
(
line
)
{
this
.
playlistEditor
.
entries
.
push
({
uri
:
'
line://
'
+
line
})
},
// controls sub-modal to add a new URI to the playlist editor
addPlaylistItemStream
:
function
(
action
)
{
if
(
action
===
'
openModal
'
)
{
// the function gets called with the action 'openModal' when the users
// clicks on the add new stream button. then we clear all temp data and
// open the modal
this
.
playlistEditor
.
newStreamURL
=
''
this
.
$bvModal
.
show
(
'
modal-edit-playlist-add-stream
'
)
}
else
{
// when the user hits ok to add the new uri, this function gets called
// with the action 'save' and the modal closes automagically. so we
// just have to push a new entry to the playlist in the editor
if
(
this
.
playlistEditor
.
newStreamURL
.
trim
().
length
>
0
)
{
this
.
playlistEditor
.
entries
.
push
({
uri
:
this
.
playlistEditor
.
newStreamURL
})
}
// if an empty string was provided, we just do nothing
}
},
deletePlaylist
:
function
(
id
)
{
var
uri
=
process
.
env
.
VUE_APP_API_TANK
+
'
shows/
'
+
this
.
shows
[
this
.
currentShow
].
slug
+
'
/playlists/
'
+
id
// TODO: add mechanism to indicate the running delete request in the files table
axios
.
delete
(
uri
,
{
withCredentials
:
true
,
headers
:
{
'
Authorization
'
:
'
Bearer
'
+
this
.
$parent
.
user
.
access_token
},
}).
then
(
this
.
fetchPlaylists
(
this
.
shows
[
this
.
currentShow
].
slug
)
).
catch
(
error
=>
{
this
.
$log
.
error
(
error
.
response
.
status
+
'
'
+
error
.
response
.
statusText
)
this
.
$log
.
error
(
error
.
response
)
alert
(
'
Error: could not delete playlist. See console for details.
'
)
})
},
addPlaylist
:
function
()
{
this
.
playlistEditor
.
mode
=
'
add
'
this
.
playlistEditor
.
id
=
null
this
.
playlistEditor
.
description
=
''
this
.
playlistEditor
.
entries
=
[]
this
.
$bvModal
.
show
(
'
modal-edit-playlist
'
)
},
editPlaylist
:
function
(
id
)
{
this
.
playlistEditor
.
mode
=
'
edit
'
this
.
playlistEditor
.
entries
=
[]
this
.
playlistEditor
.
id
=
id
var
playlist
=
this
.
getPlaylistById
(
id
)
this
.
playlistEditor
.
description
=
playlist
.
description
for
(
var
i
in
playlist
.
entries
)
{
var
entry
=
{}
if
(
playlist
.
entries
[
i
].
file
)
{
entry
.
file
=
{}
entry
.
file
.
show
=
playlist
.
entries
[
i
].
file
.
show
entry
.
file
.
id
=
playlist
.
entries
[
i
].
file
.
id
}
else
{
entry
.
uri
=
playlist
.
entries
[
i
].
uri
}
this
.
playlistEditor
.
entries
.
push
(
entry
)
}
this
.
$bvModal
.
show
(
'
modal-edit-playlist
'
)
},
// Adds a new or updates an existing playlist by sending a POST/PUT
// request to the AURA tank API
storePlaylist
:
function
()
{
var
data
=
{
description
:
this
.
playlistEditor
.
description
,
entries
:
this
.
playlistEditor
.
entries
}
var
uri
=
process
.
env
.
VUE_APP_API_TANK
+
'
shows/
'
+
this
.
shows
[
this
.
currentShow
].
slug
+
'
/playlists
'
// TODO: add some spinner or other visualisation while the playlist is added/updated
if
(
this
.
playlistEditor
.
mode
===
'
add
'
)
{
axios
.
post
(
uri
,
data
,
{
withCredentials
:
true
,
headers
:
{
'
Authorization
'
:
'
Bearer
'
+
this
.
$parent
.
user
.
access_token
}
}).
then
(
response
=>
{
this
.
playlists
.
push
(
response
.
data
)
}).
catch
(
error
=>
{
this
.
$log
.
error
(
error
.
response
.
status
+
'
'
+
error
.
response
.
statusText
)
this
.
$log
.
error
(
error
.
response
)
alert
(
'
Error: could not add new playlist. See console for details.
'
)
})
// when updating an existing playlist we use a PUT request and add the id
// of the playlist to the uri
}
else
if
(
this
.
playlistEditor
.
mode
===
'
edit
'
)
{
var
id
=
this
.
playlistEditor
.
id
uri
+=
'
/
'
+
id
axios
.
put
(
uri
,
data
,
{
withCredentials
:
true
,
headers
:
{
'
Authorization
'
:
'
Bearer
'
+
this
.
$parent
.
user
.
access_token
}
}).
then
(
response
=>
{
this
.
playlists
[
this
.
getPlaylistById
(
id
,
'
index
'
)]
=
response
.
data
this
.
$refs
.
playlistsTable
.
refresh
()
}).
catch
(
error
=>
{
this
.
$log
.
error
(
error
.
response
.
status
+
'
'
+
error
.
response
.
statusText
)
this
.
$log
.
error
(
error
.
response
)
alert
(
'
Error: could not update playlist. See console for details.
'
)
})
}
},
// This switches the UI elements to reflect another show and fetches all
// relevent data from the tank API.
showHasSwitched
()
{
...
...
@@ -604,27 +78,11 @@ export default {
this
.
$store
.
dispatch
(
'
files/fetchFiles
'
,
{
slug
:
this
.
selectedShow
.
slug
})
this
.
fetchPlaylists
(
this
.
selectedShow
.
slug
)
this
.
$store
.
dispatch
(
'
playlists/fetch
'
,
{
slug
:
this
.
selectedShow
.
slug
})
},
// Fetch all playlists for a given show from the AuRa tank API
fetchPlaylists
:
function
(
slug
)
{
this
.
loadedLocal
.
playlists
=
false
var
uri
=
process
.
env
.
VUE_APP_API_TANK
+
'
shows/
'
+
slug
+
'
/playlists
'
axios
.
get
(
uri
,
{
withCredentials
:
true
,
headers
:
{
'
Authorization
'
:
'
Bearer
'
+
this
.
$parent
.
user
.
access_token
}
}).
then
(
response
=>
{
// we don't have to check separately, if there are playlists, because tank
// always provides an empty array if there are no playlists (or even if there is no corresponding show)
this
.
playlists
=
response
.
data
.
results
this
.
loadedLocal
.
playlists
=
true
}).
catch
(
error
=>
{
this
.
$log
.
error
(
error
.
response
.
status
+
'
'
+
error
.
response
.
statusText
)
this
.
$log
.
error
(
error
.
response
)
alert
(
'
Error: could not fetch playlists from tank. See console for details.
'
)
})
}
}
}
</
script
>
...
...
src/components/filemanager/EditPlaylistModal.vue
0 → 100644
View file @
30a9c80d
<
template
>