#842 -- If looking at a playlist's contents, new uploaded files go into that playlist.

This commit is contained in:
Buster "Silver Eagle" Neece 2018-10-07 10:39:00 -05:00
parent e53b4e456d
commit 9eb00ce64a
4 changed files with 349 additions and 312 deletions

View File

@ -391,6 +391,19 @@ return [
],
],
'flowjs' => [
'order' => 10,
'files' => [
'js' => [
[
'src' => 'https://cdnjs.cloudflare.com/ajax/libs/flow.js/2.13.0/flow.min.js',
'integrity' => 'sha256-F82/auvnDa23QdME+MJ76ucrjnWyUDxKwEvCuhT1m2Y=',
'defer' => true,
],
],
],
],
'fullcalendar' => [
'order' => 10,
'require' => ['moment'],

View File

@ -0,0 +1,310 @@
$(function() {
var CSRF = '<?=$csrf ?>';
var MAX_UPLOAD_SIZE = <?=$max_upload_size ?>;
var $current_dir = '';
var appToolbar = new Vue({
el: '#app-toolbar',
data: {
playlists: <?=json_encode($playlists) ?>
},
methods: {
doBatch: function (e) {
e.preventDefault();
var $files = getSelectedFiles();
$files.length && $.post('<?=$router->fromHere('stations:files:batch') ?>',{
'do': $(e.target).data('action'),
'files': $files.join('|'),
'csrf': CSRF,
'file': getUrlHash()
},function(data){
list();
},'json');
$(this).closest('.btn-group').removeClass('open');
return false;
}
}
});
var grid = $("#file-table").bootgrid({
ajax: true,
selection: true,
multiSelect: true,
rowSelect: false,
caseSensitive: false,
css: {
icon: 'zmdi icon',
iconColumns: 'zmdi-view-module',
iconDown: 'zmdi-sort-amount-desc',
iconRefresh: 'zmdi-refresh',
iconUp: 'zmdi-sort-amount-asc'
},
url: "<?=$router->fromHere('stations:files:list') ?>",
post: function() {
return {
'file': $current_dir,
'csrf': CSRF
};
},
formatters: {
"playable": function(column, row) {
var $url = row.is_dir ? '#' + row.path : row.media_play_url;
var $icon = '';
if (row.media_is_playable)
$icon = '<a class="file-icon btn-audio" href="#" data-url="'+$url+'"><i class="zmdi zmdi-play"></i></a>';
else
$icon = '<span class="file-icon"><i class="zmdi zmdi-'+(row.is_dir ? 'folder' : 'file')+'"></i></span>';
var $link = '<a class="name" href="'+$url+'" title="'+row.name+'">'+(row.is_dir ? row.text : row.media_name)+'</a>';
var art_src = (row.media_art) ? row.media_art : '/static/img/generic_song.jpg';
var $art = (row.is_dir) ? '' : '<a href="'+art_src+'" target="_blank" class="pull-right p-l-10"><img style="width: 40px; height: auto; border-radius: 5px;" alt="<?=__('Album Artwork') ?>" src="'+art_src+'"></a>';
return '<div class="'+(row.is_dir ? 'is_dir' : 'is_file')+'">'+$art+$icon + $link + '<br><small>'+(row.is_dir ? 'Directory' : row.text)+'</small></div>';
},
"commands": function(column, row) {
if (row.media_edit_url)
return '<a class="btn btn-sm btn-primary" href="'+row.media_edit_url+'"><?=__('Edit') ?></a>';
else
return '<a class="btn btn-sm btn-primary" href="'+row.rename_url+'"><?=__('Rename') ?></a>';
},
"file_length": function(column, row) {
if (row.media_length_text)
return row.media_length_text;
else
return 'N/A';
},
"file_size": function(column, row) {
return formatFileSize(row.size);
},
"file_mtime": function(column, row) {
return formatTimestamp(row.mtime);
},
"playlists": function(column, row) {
if (row.media_playlists.length > 0) {
var playlists = [];
var playlist;
for (var i = 0; i < row.media_playlists.length; i++) {
playlist = row.media_playlists[i];
playlists.push('<a class="btn-search" href="#" data-term="playlist:'+playlist+'">'+playlist+'</a>');
}
return playlists.join(', ');
} else {
return '';
}
}
}
}).on("loaded.rs.jquery.bootgrid", function() {
/* Executes after data is loaded and rendered */
grid.find(".btn-audio").on("click", function(e) {
e.preventDefault();
handlePlayClick($(this).data('url'));
return false;
});
/* Handle playlist search function */
grid.find(".btn-search").on("click", function(e) {
e.preventDefault();
grid.bootgrid("clear").bootgrid("search", $(this).data('term'));
return false;
});
});
// Check if initial URL has a hash.
var hashval = getUrlHash();
if (hashval.length > 0)
list();
else
$('#breadcrumb').empty().html(renderBreadcrumbs(hashval));
$(window).bind('hashchange',list);
// Create new directory
$('form#mkdir').submit(function(e) {
e.preventDefault();
var hashval = getUrlHash();
var $dir = $(this).find('[name=name]');
$dir.val().length && $.post('<?=$router->fromHere('stations:files:mkdir') ?>',{
name: $dir.val(),
csrf: CSRF,
file: hashval
},function(data){
list();
},'json');
$dir.val('');
return false;
});
$('.btn-new-playlist').on('click', function(e) {
$(this).closest('.btn-group').removeClass('open');
});
$('#frm-create-playlist').submit(function(e) {
e.preventDefault();
var $files = getSelectedFiles();
$files.length && $.post('<?=$router->fromHere('stations:files:batch') ?>',{
'do': 'playlist_new',
'name': $('#fld-playlist-name').val(),
'files': $files.join('|'),
'csrf': CSRF,
'file': getUrlHash()
},function(data){
if (data.success) {
appToolbar.playlists.push(data.record);
}
list();
},'json');
$('#fld-playlist-name').val('');
$('#mdl-create-playlist').modal('hide');
return false;
});
$('#frm-create-directory').submit(function(e) {
e.preventDefault();
var $dir = $('#fld-directory-name');
$dir.val().length && $.post('<?=$router->fromHere('stations:files:mkdir') ?>',{
name: $dir.val(),
csrf: CSRF,
file: getUrlHash()
},function(data){
list();
},'json');
$dir.val('');
$('#mdl-create-directory').modal('hide');
return false;
});
// File upload stuff
var flow = new Flow({
target: '<?=$router->fromHere('stations:files:upload') ?>',
query: function() {
return {
csrf: CSRF,
file: getUrlHash(),
searchPhrase: $('input.search-field').val()
};
},
withCredentials: true,
allowDuplicateUploads: true,
fileParameterName: 'file_data'
});
flow.assignBrowse(document.getElementById('file_browse_target'));
flow.assignDrop(document.getElementById('file_drop_target'));
flow.on('fileAdded', function(file, event) {
$('#upload_progress').append(renderFileUploadRow(file));
return true;
});
flow.on('filesSubmitted', function(array, event) {
flow.upload();
});
flow.on('fileProgress', function(file) {
var $row = $('#file_upload_'+file.uniqueIdentifier);
$row.find('.progress-bar').css('width',(file.progress() * 100)+'%' );
});
flow.on('fileSuccess', function(file, message){
var $row = $('#file_upload_'+file.uniqueIdentifier);
$row.fadeOut();
list();
});
flow.on('fileError', function(file, message){
var $row = $('#file_upload_'+file.uniqueIdentifier);
$row.remove();
var $error_row = renderFileSizeErrorRow(file, message);
$('#upload_progress').append($error_row);
window.setTimeout(function(){ $error_row.fadeOut(); },5000);
});
flow.on('error', function(message, file, chunk) {
console.log(message, file, chunk);
});
function renderFileUploadRow(file) {
return $row = $('<div id="file_upload_'+file.uniqueIdentifier+'"/>')
.append( $('<span class="fileuploadname" />').text(file.name) )
.append( $('<div class="progress"><div class="progress-bar"></div></div>') )
.append( $('<span class="size" />').text(formatFileSize(file.size)) );
}
function renderFileSizeErrorRow(file, message) {
return $row = $('<div id="file_upload_error_'+file.uniqueIdentifier+'" class="error" />')
.append( $('<span class="fileuploadname" />').text('Error: ' + file.name))
.append( $('<span/>').text(message) );
}
function list() {
var hashval = getUrlHash();
$('#breadcrumb').empty().html(renderBreadcrumbs(hashval));
console.log('Relisting for directory: #'+hashval);
$current_dir = hashval;
$('#file-table').bootgrid("reload");
}
function getUrlHash()
{
var url_hash = decodeURIComponent(window.location.hash.substr(1).replace(/\+/g, "%20"));
if (url_hash.substr(0, 9) === 'playlist:') {
window.location.hash = '';
grid.bootgrid("clear").bootgrid("search", url_hash);
return '';
}
return url_hash;
}
function getSelectedFiles()
{
$files = [];
$('#file-table').find('tr.active').each(function() {
$files.push($(this).data('row-id'));
});
console.log($files);
return $files;
}
function renderBreadcrumbs(path) {
// noinspection JSAnnotator
var base = "",
$html = $('<div/>').append( $('<a href=#>'+<?=$this->escapeJs(__('Home')) ?>+'</a></div>') );
$.each(path.split('/'),function(k,v){
if(v) {
$html.append( $('<span/>').text(' ▸ ') )
.append( $('<a/>').attr('href','#'+base+v).text(v) );
base += v + '/';
}
});
return $html;
}
function formatTimestamp(unix_timestamp) {
return moment.unix(unix_timestamp).format('lll');
}
function formatFileSize(bytes) {
var s = ['bytes', 'KB','MB','GB','TB','PB','EB'];
for(var pos = 0;bytes >= 1000; pos++,bytes /= 1024);
var d = Math.round(bytes*10);
return pos ? [parseInt(d/10),".",d%10," ",s[pos]].join('') : bytes + ' bytes';
}
});

View File

@ -7,10 +7,12 @@ $assets
->load('radio')
->load('bootgrid')
->load('moment')
->addJs([
'src' => 'https://cdnjs.cloudflare.com/ajax/libs/flow.js/2.13.0/flow.min.js',
'sri' => 'sha256-F82/auvnDa23QdME+MJ76ucrjnWyUDxKwEvCuhT1m2Y=',
], 'header');
->load('flowjs')
->addInlineJs($this->fetch('stations/files/index.js', [
'csrf' => $csrf,
'max_upload_size' => $max_upload_size,
'playlists' => $playlists,
]));
?>
<div class="row">
@ -140,312 +142,5 @@ $assets
</div>
<script type="text/javascript" nonce="<?=$assets->getCspNonce() ?>">
$(function() {
var CSRF = '<?=$csrf ?>';
var MAX_UPLOAD_SIZE = <?=$max_upload_size ?>;
var $current_dir = '';
var appToolbar = new Vue({
el: '#app-toolbar',
data: {
playlists: <?=json_encode($playlists) ?>
},
methods: {
doBatch: function (e) {
e.preventDefault();
var $files = getSelectedFiles();
$files.length && $.post('<?=$router->fromHere('stations:files:batch') ?>',{
'do': $(e.target).data('action'),
'files': $files.join('|'),
'csrf': CSRF,
'file': getUrlHash()
},function(data){
list();
},'json');
$(this).closest('.btn-group').removeClass('open');
return false;
}
}
});
var grid = $("#file-table").bootgrid({
ajax: true,
selection: true,
multiSelect: true,
rowSelect: false,
caseSensitive: false,
css: {
icon: 'zmdi icon',
iconColumns: 'zmdi-view-module',
iconDown: 'zmdi-sort-amount-desc',
iconRefresh: 'zmdi-refresh',
iconUp: 'zmdi-sort-amount-asc'
},
url: "<?=$router->fromHere('stations:files:list') ?>",
post: function() {
return {
'file': $current_dir,
'csrf': CSRF
};
},
formatters: {
"playable": function(column, row) {
var $url = row.is_dir ? '#' + row.path : row.media_play_url;
var $icon = '';
if (row.media_is_playable)
$icon = '<a class="file-icon btn-audio" href="#" data-url="'+$url+'"><i class="zmdi zmdi-play"></i></a>';
else
$icon = '<span class="file-icon"><i class="zmdi zmdi-'+(row.is_dir ? 'folder' : 'file')+'"></i></span>';
var $link = '<a class="name" href="'+$url+'" title="'+row.name+'">'+(row.is_dir ? row.text : row.media_name)+'</a>';
var art_src = (row.media_art) ? row.media_art : '/static/img/generic_song.jpg';
var $art = (row.is_dir) ? '' : '<a href="'+art_src+'" target="_blank" class="pull-right p-l-10"><img style="width: 40px; height: auto; border-radius: 5px;" alt="<?=__('Album Artwork') ?>" src="'+art_src+'"></a>';
return '<div class="'+(row.is_dir ? 'is_dir' : 'is_file')+'">'+$art+$icon + $link + '<br><small>'+(row.is_dir ? 'Directory' : row.text)+'</small></div>';
},
"commands": function(column, row) {
if (row.media_edit_url)
return '<a class="btn btn-sm btn-primary" href="'+row.media_edit_url+'"><?=__('Edit') ?></a>';
else
return '<a class="btn btn-sm btn-primary" href="'+row.rename_url+'"><?=__('Rename') ?></a>';
},
"file_length": function(column, row) {
if (row.media_length_text)
return row.media_length_text;
else
return 'N/A';
},
"file_size": function(column, row) {
return formatFileSize(row.size);
},
"file_mtime": function(column, row) {
return formatTimestamp(row.mtime);
},
"playlists": function(column, row) {
if (row.media_playlists.length > 0) {
var playlists = [];
var playlist;
for (var i = 0; i < row.media_playlists.length; i++) {
playlist = row.media_playlists[i];
playlists.push('<a class="btn-search" href="#" data-term="playlist:'+playlist+'">'+playlist+'</a>');
}
return playlists.join(', ');
} else {
return '';
}
}
}
}).on("loaded.rs.jquery.bootgrid", function() {
/* Executes after data is loaded and rendered */
grid.find(".btn-audio").on("click", function(e) {
e.preventDefault();
handlePlayClick($(this).data('url'));
return false;
});
/* Handle playlist search function */
grid.find(".btn-search").on("click", function(e) {
e.preventDefault();
grid.bootgrid("clear").bootgrid("search", $(this).data('term'));
return false;
});
});
// Check if initial URL has a hash.
var hashval = getUrlHash();
if (hashval.length > 0)
list();
else
$('#breadcrumb').empty().html(renderBreadcrumbs(hashval));
$(window).bind('hashchange',list);
// Create new directory
$('form#mkdir').submit(function(e) {
e.preventDefault();
var hashval = getUrlHash();
var $dir = $(this).find('[name=name]');
$dir.val().length && $.post('<?=$router->fromHere('stations:files:mkdir') ?>',{
name: $dir.val(),
csrf: CSRF,
file: hashval
},function(data){
list();
},'json');
$dir.val('');
return false;
});
$('.btn-new-playlist').on('click', function(e) {
$(this).closest('.btn-group').removeClass('open');
});
$('#frm-create-playlist').submit(function(e) {
e.preventDefault();
var $files = getSelectedFiles();
$files.length && $.post('<?=$router->fromHere('stations:files:batch') ?>',{
'do': 'playlist_new',
'name': $('#fld-playlist-name').val(),
'files': $files.join('|'),
'csrf': CSRF,
'file': getUrlHash()
},function(data){
if (data.success) {
appToolbar.playlists.push(data.record);
}
list();
},'json');
$('#fld-playlist-name').val('');
$('#mdl-create-playlist').modal('hide');
return false;
});
$('#frm-create-directory').submit(function(e) {
e.preventDefault();
var $dir = $('#fld-directory-name');
$dir.val().length && $.post('<?=$router->fromHere('stations:files:mkdir') ?>',{
name: $dir.val(),
csrf: CSRF,
file: getUrlHash()
},function(data){
list();
},'json');
$dir.val('');
$('#mdl-create-directory').modal('hide');
return false;
});
// File upload stuff
var flow = new Flow({
target: '<?=$router->fromHere('stations:files:upload') ?>',
query: function() {
return {
csrf: CSRF,
file: getUrlHash()
};
},
withCredentials: true,
allowDuplicateUploads: true,
fileParameterName: 'file_data'
});
flow.assignBrowse(document.getElementById('file_browse_target'));
flow.assignDrop(document.getElementById('file_drop_target'));
flow.on('fileAdded', function(file, event) {
$('#upload_progress').append(renderFileUploadRow(file));
return true;
});
flow.on('filesSubmitted', function(array, event) {
flow.upload();
});
flow.on('fileProgress', function(file) {
var $row = $('#file_upload_'+file.uniqueIdentifier);
$row.find('.progress-bar').css('width',(file.progress() * 100)+'%' );
});
flow.on('fileSuccess', function(file, message){
var $row = $('#file_upload_'+file.uniqueIdentifier);
$row.fadeOut();
list();
});
flow.on('fileError', function(file, message){
var $row = $('#file_upload_'+file.uniqueIdentifier);
$row.remove();
var $error_row = renderFileSizeErrorRow(file, message);
$('#upload_progress').append($error_row);
window.setTimeout(function(){ $error_row.fadeOut(); },5000);
});
flow.on('error', function(message, file, chunk) {
console.log(message, file, chunk);
});
function renderFileUploadRow(file) {
return $row = $('<div id="file_upload_'+file.uniqueIdentifier+'"/>')
.append( $('<span class="fileuploadname" />').text(file.name) )
.append( $('<div class="progress"><div class="progress-bar"></div></div>') )
.append( $('<span class="size" />').text(formatFileSize(file.size)) );
}
function renderFileSizeErrorRow(file, message) {
return $row = $('<div id="file_upload_error_'+file.uniqueIdentifier+'" class="error" />')
.append( $('<span class="fileuploadname" />').text('Error: ' + file.name))
.append( $('<span/>').text(message) );
}
function list() {
var hashval = getUrlHash();
$('#breadcrumb').empty().html(renderBreadcrumbs(hashval));
console.log('Relisting for directory: #'+hashval);
$current_dir = hashval;
$('#file-table').bootgrid("reload");
}
function getUrlHash()
{
var url_hash = decodeURIComponent(window.location.hash.substr(1).replace(/\+/g, "%20"));
if (url_hash.substr(0, 9) === 'playlist:') {
window.location.hash = '';
grid.bootgrid("clear").bootgrid("search", url_hash);
return '';
}
return url_hash;
}
function getSelectedFiles()
{
$files = [];
$('#file-table').find('tr.active').each(function() {
$files.push($(this).data('row-id'));
});
console.log($files);
return $files;
}
function renderBreadcrumbs(path) {
var base = "",
$html = $('<div/>').append( $('<a href=#>Home</a></div>') );
$.each(path.split('/'),function(k,v){
if(v) {
$html.append( $('<span/>').text(' ▸ ') )
.append( $('<a/>').attr('href','#'+base+v).text(v) );
base += v + '/';
}
});
return $html;
}
function formatTimestamp(unix_timestamp) {
return moment.unix(unix_timestamp).format('lll');
}
function formatFileSize(bytes) {
var s = ['bytes', 'KB','MB','GB','TB','PB','EB'];
for(var pos = 0;bytes >= 1000; pos++,bytes /= 1024);
var d = Math.round(bytes*10);
return pos ? [parseInt(d/10),".",d%10," ",s[pos]].join('') : bytes + ' bytes';
}
})
</script>

View File

@ -484,8 +484,27 @@ class FilesController extends FilesControllerAbstract
rename($flow_response['path'], $final_path);
$station_media = $this->media_repo->getOrCreate($station, $final_path);
$this->em->persist($station_media);
$this->em->flush($station_media);
// If the user is looking at a playlist's contents, add uploaded media to that playlist.
if ($request->hasParam('searchPhrase')) {
$search_phrase = $request->getParam('searchPhrase');
if (substr($search_phrase, 0, 9) === 'playlist:') {
$playlist_name = substr($search_phrase, 9);
$playlist = $this->em->getRepository(Entity\StationPlaylist::class)->findOneBy([
'station_id' => $station->getId(),
'name' => $playlist_name,
]);
if ($playlist instanceof Entity\StationPlaylist) {
$this->playlists_media_repo->addMediaToPlaylist($station_media, $playlist);
}
}
}
$this->em->flush();
return $response->withJson(['success' => true]);