mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-16 02:02:58 -07:00
Add playlist export for users
This commit is contained in:
parent
501f08dd5e
commit
ea9904bd56
7 changed files with 201 additions and 55 deletions
|
@ -25,10 +25,11 @@ DOCUMENTATION :: END
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form method="post" class="form" id="export_metadata_form">
|
<form method="post" class="form" id="export_metadata_form">
|
||||||
<input type="hidden" id="export_section_id" name="export_section_id" value="${section_id or ''}" />
|
<input type="hidden" id="export_section_id" name="export_section_id" value="${section_id or ''}" />
|
||||||
|
<input type="hidden" id="export_user_id" name="export_user_id" value="${user_id or ''}" />
|
||||||
<input type="hidden" id="export_rating_key" name="export_rating_key" value="${rating_key or ''}" />
|
<input type="hidden" id="export_rating_key" name="export_rating_key" value="${rating_key or ''}" />
|
||||||
<input type="hidden" id="export_media_type" name="export_media_type" value="${media_type or ''}" />
|
<input type="hidden" id="export_media_type" name="export_media_type" value="${media_type or ''}" />
|
||||||
<input type="hidden" id="export_sub_media_type" name="export_sub_media_type" value="${sub_media_type or ''}" />
|
<input type="hidden" id="export_sub_media_type" name="export_sub_media_type" value="${sub_media_type or ''}" />
|
||||||
<input type="hidden" id="export_library_export" name="export_library_export" value="${library_export or ''}" />
|
<input type="hidden" id="export_export_type" name="export_export_type" value="${export_type or ''}" />
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="metadata_export_level_select">Metadata Export Level</label>
|
<label for="metadata_export_level_select">Metadata Export Level</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -185,6 +186,7 @@ DOCUMENTATION :: END
|
||||||
|
|
||||||
$("#export_metadata").click(function() {
|
$("#export_metadata").click(function() {
|
||||||
var section_id = $('#export_section_id').val();
|
var section_id = $('#export_section_id').val();
|
||||||
|
var user_id = $('#export_user_id').val();
|
||||||
var rating_key = $('#export_rating_key').val();
|
var rating_key = $('#export_rating_key').val();
|
||||||
var metadata_export_level = $('#metadata_export_level_select option:selected').val();
|
var metadata_export_level = $('#metadata_export_level_select option:selected').val();
|
||||||
var media_info_export_level = $('#media_info_export_level_select option:selected').val();
|
var media_info_export_level = $('#media_info_export_level_select option:selected').val();
|
||||||
|
@ -195,12 +197,13 @@ DOCUMENTATION :: END
|
||||||
$('#export_custom_metadata_fields').val(),
|
$('#export_custom_metadata_fields').val(),
|
||||||
$('#export_custom_media_info_fields').val()
|
$('#export_custom_media_info_fields').val()
|
||||||
].filter(Boolean).join(',');
|
].filter(Boolean).join(',');
|
||||||
var library_export = $('#export_library_export').val()
|
var export_type = $('#export_export_type').val()
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'export_metadata',
|
url: 'export_metadata',
|
||||||
data: {
|
data: {
|
||||||
section_id: section_id,
|
section_id: section_id,
|
||||||
|
user_id: user_id,
|
||||||
rating_key: rating_key,
|
rating_key: rating_key,
|
||||||
metadata_level: metadata_export_level,
|
metadata_level: metadata_export_level,
|
||||||
media_info_level: media_info_export_level,
|
media_info_level: media_info_export_level,
|
||||||
|
@ -208,7 +211,7 @@ DOCUMENTATION :: END
|
||||||
include_thumb: include_thumb,
|
include_thumb: include_thumb,
|
||||||
include_art: include_art,
|
include_art: include_art,
|
||||||
custom_fields: custom_fields,
|
custom_fields: custom_fields,
|
||||||
library_export: library_export
|
export_type: export_type
|
||||||
},
|
},
|
||||||
async: true,
|
async: true,
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
|
|
|
@ -169,10 +169,11 @@ DOCUMENTATION :: END
|
||||||
% elif data['media_type'] == 'playlist':
|
% elif data['media_type'] == 'playlist':
|
||||||
% if user_info.get('user_id'):
|
% if user_info.get('user_id'):
|
||||||
<li><a href="${page('user', user_info.get('user_id'))}">${user_info.get('friendly_name')}</a></li>
|
<li><a href="${page('user', user_info.get('user_id'))}">${user_info.get('friendly_name')}</a></li>
|
||||||
% else:
|
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||||
|
% elif data['section_id']:
|
||||||
<li><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
<li><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||||
|
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||||
% endif
|
% endif
|
||||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
|
||||||
<li class="active metadata-xml">${data['title']}</li>
|
<li class="active metadata-xml">${data['title']}</li>
|
||||||
% endif
|
% endif
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -327,7 +327,7 @@ DOCUMENTATION :: END
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
||||||
data-section_id="${data['section_id']}" data-media_type="collection" data-sub_media_type="${data['section_type']}"
|
data-section_id="${data['section_id']}" data-media_type="collection" data-sub_media_type="${data['section_type']}"
|
||||||
data-library_export="collection">
|
data-export_type="collection">
|
||||||
<i class="fa fa-file-export"></i> Export collections
|
<i class="fa fa-file-export"></i> Export collections
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -375,7 +375,7 @@ DOCUMENTATION :: END
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
||||||
data-section_id="${data['section_id']}" data-media_type="playlist" data-sub_media_type="${playlist_sub_media_type.get(data['section_type'])}"
|
data-section_id="${data['section_id']}" data-media_type="playlist" data-sub_media_type="${playlist_sub_media_type.get(data['section_type'])}"
|
||||||
data-library_export="playlist">
|
data-export_type="playlist">
|
||||||
<i class="fa fa-file-export"></i> Export playlists
|
<i class="fa fa-file-export"></i> Export playlists
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -418,11 +418,10 @@ DOCUMENTATION :: END
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-bar">
|
<div class="button-bar">
|
||||||
% if _session['user_group'] == 'admin':
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
||||||
data-section_id="${data['section_id']}" data-media_type="${'photoalbum' if data['section_type'] == 'photo' else data['section_type']}"
|
data-section_id="${data['section_id']}" data-media_type="${'photoalbum' if data['section_type'] == 'photo' else data['section_type']}"
|
||||||
data-library_export="all">
|
data-export_type="all">
|
||||||
<i class="fa fa-file-export"></i> Export metadata
|
<i class="fa fa-file-export"></i> Export metadata
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -431,7 +430,6 @@ DOCUMENTATION :: END
|
||||||
<i class="fa fa-refresh"></i> Refresh exports
|
<i class="fa fa-refresh"></i> Refresh exports
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
|
||||||
<div class="btn-group colvis-button-bar" id="button-bar-export"></div>
|
<div class="btn-group colvis-button-bar" id="button-bar-export"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -888,7 +886,7 @@ DOCUMENTATION :: END
|
||||||
section_id: $(this).data('section_id'),
|
section_id: $(this).data('section_id'),
|
||||||
media_type: $(this).data('media_type'),
|
media_type: $(this).data('media_type'),
|
||||||
sub_media_type: $(this).data('sub_media_type'),
|
sub_media_type: $(this).data('sub_media_type'),
|
||||||
library_export: $(this).data('library_export')
|
export_type: $(this).data('export_type')
|
||||||
},
|
},
|
||||||
cache: false,
|
cache: false,
|
||||||
async: true,
|
async: true,
|
||||||
|
|
|
@ -73,6 +73,9 @@ DOCUMENTATION :: END
|
||||||
<li class="active"><a href="#tabs-profile" role="tab" data-toggle="tab">Profile</a></li>
|
<li class="active"><a href="#tabs-profile" role="tab" data-toggle="tab">Profile</a></li>
|
||||||
<li><a href="#tabs-history" role="tab" data-toggle="tab">History</a></li>
|
<li><a href="#tabs-history" role="tab" data-toggle="tab">History</a></li>
|
||||||
<li><a href="#tabs-playlists" role="tab" data-toggle="tab">Playlists</a></li>
|
<li><a href="#tabs-playlists" role="tab" data-toggle="tab">Playlists</a></li>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<li><a href="#tabs-export" role="tab" data-toggle="tab">Export</a></li>
|
||||||
|
% endif
|
||||||
<li><a href="#tabs-synceditems" role="tab" data-toggle="tab">Synced Items</a></li>
|
<li><a href="#tabs-synceditems" role="tab" data-toggle="tab">Synced Items</a></li>
|
||||||
<li><a href="#tabs-ipaddresses" role="tab" data-toggle="tab">IP Addresses</a></li>
|
<li><a href="#tabs-ipaddresses" role="tab" data-toggle="tab">IP Addresses</a></li>
|
||||||
<li><a href="#tabs-tautullilogins" role="tab" data-toggle="tab">Tautulli Logins</a></li>
|
<li><a href="#tabs-tautullilogins" role="tab" data-toggle="tab">Tautulli Logins</a></li>
|
||||||
|
@ -227,11 +230,10 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
<div class="button-bar">
|
<div class="button-bar">
|
||||||
% if _session['user_group'] == 'admin':
|
% if _session['user_group'] == 'admin':
|
||||||
<% playlist_sub_media_type = {'movie': 'video', 'show': 'video', 'artist': 'audio', 'photo': 'photo'} %>
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
<button class="btn btn-dark export-button" id="toggle-export-modal" data-toggle="modal" data-target="#export-modal"
|
||||||
data-media_type="playlist" data-sub_media_type=""
|
data-user_id="${data['user_id']}" data-media_type="playlist" data-sub_media_type="video,audio,photo"
|
||||||
data-library_export="playlist">
|
data-export_type="playlist">
|
||||||
<i class="fa fa-file-export"></i> Export playlists
|
<i class="fa fa-file-export"></i> Export playlists
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -260,6 +262,53 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tabs-export">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class='table-card-header'>
|
||||||
|
<div class="header-bar">
|
||||||
|
<span>
|
||||||
|
<i class="fa fa-file-export"></i> Metadata Exports for <strong>
|
||||||
|
<span class="set-username">${data['friendly_name']}</span>
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="button-bar">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-dark refresh-export-table-button" id="refresh-export-table">
|
||||||
|
<i class="fa fa-refresh"></i> Refresh exports
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group colvis-button-bar" id="button-bar-export"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-card-back">
|
||||||
|
<table class="display export_table" id="export_table-SID-${data['user_id']}" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th align="left" id="timestamp">Exported At</th>
|
||||||
|
<th align="left" id="media_type_title">Media Type</th>
|
||||||
|
<th align="left" id="rating_key">Rating Key</th>
|
||||||
|
<th align="left" id="filename">Filename</th>
|
||||||
|
<th align="left" id="file_format">File Format</th>
|
||||||
|
<th align="left" id="metadata_level">Metadata Level</th>
|
||||||
|
<th align="left" id="media_info_level">Media Info Level</th>
|
||||||
|
<th align="left" id="media_info_level">Custom Fields</th>
|
||||||
|
<th align="left" id="file_size">File Size</th>
|
||||||
|
<th align="left" id="complete">Download</th>
|
||||||
|
<th align="left" id="delete">Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-synceditems">
|
<div role="tabpanel" class="tab-pane" id="tabs-synceditems">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -443,6 +492,8 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="export-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="export-modal">
|
||||||
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
|
@ -463,6 +514,7 @@ DOCUMENTATION :: END
|
||||||
<script src="${http_root}js/moment-with-locale.js"></script>
|
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||||
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
|
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
|
||||||
<script src="${http_root}js/tables/playlists_table.js${cache_param}"></script>
|
<script src="${http_root}js/tables/playlists_table.js${cache_param}"></script>
|
||||||
|
<script src="${http_root}js/tables/export_table.js${cache_param}"></script>
|
||||||
<script src="${http_root}js/tables/user_ips.js${cache_param}"></script>
|
<script src="${http_root}js/tables/user_ips.js${cache_param}"></script>
|
||||||
<script src="${http_root}js/tables/sync_table.js${cache_param}"></script>
|
<script src="${http_root}js/tables/sync_table.js${cache_param}"></script>
|
||||||
<script src="${http_root}js/tables/login_logs.js${cache_param}"></script>
|
<script src="${http_root}js/tables/login_logs.js${cache_param}"></script>
|
||||||
|
@ -725,6 +777,37 @@ DOCUMENTATION :: END
|
||||||
</script>
|
</script>
|
||||||
% if _session['user_group'] == 'admin':
|
% if _session['user_group'] == 'admin':
|
||||||
<script>
|
<script>
|
||||||
|
function loadExportTable() {
|
||||||
|
// Build export table
|
||||||
|
export_table_options.ajax = {
|
||||||
|
url: 'get_export_list',
|
||||||
|
type: 'POST',
|
||||||
|
data: function ( d ) {
|
||||||
|
return {
|
||||||
|
json_data: JSON.stringify( d ),
|
||||||
|
user_id: user_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export_table = $('#export_table-SID-${data["user_id"]}').DataTable(export_table_options);
|
||||||
|
export_table.columns([2, 7]).visible(false);
|
||||||
|
|
||||||
|
var colvis = new $.fn.dataTable.ColVis(export_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' });
|
||||||
|
$(colvis.button()).appendTo('#button-bar-export');
|
||||||
|
|
||||||
|
clearSearchButton('export_table-SID-${data["user_id"]}', export_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('a[href="#tabs-export"]').on('shown.bs.tab', function() {
|
||||||
|
if (typeof(export_table) === 'undefined') {
|
||||||
|
loadExportTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#refresh-export-table").click(function () {
|
||||||
|
export_table.draw();
|
||||||
|
});
|
||||||
|
|
||||||
$("#edit-user-tooltip").tooltip();
|
$("#edit-user-tooltip").tooltip();
|
||||||
|
|
||||||
// Load edit user modal
|
// Load edit user modal
|
||||||
|
@ -741,6 +824,23 @@ DOCUMENTATION :: END
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".export-button").click(function() {
|
||||||
|
$.ajax({
|
||||||
|
url: 'export_metadata_modal',
|
||||||
|
data: {
|
||||||
|
user_id: $(this).data('user_id'),
|
||||||
|
media_type: $(this).data('media_type'),
|
||||||
|
sub_media_type: $(this).data('sub_media_type'),
|
||||||
|
export_type: $(this).data('export_type')
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
complete: function(xhr, status) {
|
||||||
|
$("#export-modal").html(xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$('#row-edit-mode').on('click', function() {
|
$('#row-edit-mode').on('click', function() {
|
||||||
$('#row-edit-mode-alert').fadeIn(200);
|
$('#row-edit-mode-alert').fadeIn(200);
|
||||||
|
|
||||||
|
|
|
@ -796,7 +796,7 @@ def dbcheck():
|
||||||
# exports table :: This table keeps record of the exported files
|
# exports table :: This table keeps record of the exported files
|
||||||
c_db.execute(
|
c_db.execute(
|
||||||
'CREATE TABLE IF NOT EXISTS exports (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
'CREATE TABLE IF NOT EXISTS exports (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||||
'timestamp INTEGER, section_id INTEGER, rating_key INTEGER, media_type TEXT, '
|
'timestamp INTEGER, section_id INTEGER, user_id INTEGER, rating_key INTEGER, media_type TEXT, '
|
||||||
'filename TEXT, file_format TEXT, '
|
'filename TEXT, file_format TEXT, '
|
||||||
'metadata_level INTEGER, media_info_level INTEGER, '
|
'metadata_level INTEGER, media_info_level INTEGER, '
|
||||||
'include_thumb INTEGER DEFAULT 0, include_art INTEGER DEFAULT 0, '
|
'include_thumb INTEGER DEFAULT 0, include_art INTEGER DEFAULT 0, '
|
||||||
|
|
|
@ -35,12 +35,14 @@ if plexpy.PYTHON2:
|
||||||
import datatables
|
import datatables
|
||||||
import helpers
|
import helpers
|
||||||
import logger
|
import logger
|
||||||
|
import users
|
||||||
from plex import Plex
|
from plex import Plex
|
||||||
else:
|
else:
|
||||||
from plexpy import database
|
from plexpy import database
|
||||||
from plexpy import datatables
|
from plexpy import datatables
|
||||||
from plexpy import helpers
|
from plexpy import helpers
|
||||||
from plexpy import logger
|
from plexpy import logger
|
||||||
|
from plexpy import users
|
||||||
from plexpy.plex import Plex
|
from plexpy.plex import Plex
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,13 +92,14 @@ class Export(object):
|
||||||
METADATA_LEVELS = (0, 1, 2, 3, 9)
|
METADATA_LEVELS = (0, 1, 2, 3, 9)
|
||||||
MEDIA_INFO_LEVELS = (0, 1, 2, 3, 9)
|
MEDIA_INFO_LEVELS = (0, 1, 2, 3, 9)
|
||||||
FILE_FORMATS = ('csv', 'json', 'xml')
|
FILE_FORMATS = ('csv', 'json', 'xml')
|
||||||
LIBRARY_EXPORTS = ('all', 'collection', 'playlist')
|
EXPORT_TYPES = ('all', 'collection', 'playlist')
|
||||||
|
|
||||||
def __init__(self, section_id=None, rating_key=None, file_format='csv',
|
def __init__(self, section_id=None, user_id=None, rating_key=None, file_format='csv',
|
||||||
metadata_level=1, media_info_level=1,
|
metadata_level=1, media_info_level=1,
|
||||||
include_thumb=False, include_art=False,
|
include_thumb=False, include_art=False,
|
||||||
custom_fields='', library_export=None):
|
custom_fields='', export_type=None):
|
||||||
self.section_id = helpers.cast_to_int(section_id) or None
|
self.section_id = helpers.cast_to_int(section_id) or None
|
||||||
|
self.user_id = helpers.cast_to_int(user_id) or None
|
||||||
self.rating_key = helpers.cast_to_int(rating_key) or None
|
self.rating_key = helpers.cast_to_int(rating_key) or None
|
||||||
self.file_format = file_format
|
self.file_format = file_format
|
||||||
self.metadata_level = helpers.cast_to_int(metadata_level)
|
self.metadata_level = helpers.cast_to_int(metadata_level)
|
||||||
|
@ -105,7 +108,7 @@ class Export(object):
|
||||||
self.include_art = include_art
|
self.include_art = include_art
|
||||||
self.custom_fields = custom_fields.replace(' ', '')
|
self.custom_fields = custom_fields.replace(' ', '')
|
||||||
self._custom_fields = {}
|
self._custom_fields = {}
|
||||||
self.library_export = library_export or 'all'
|
self.export_type = export_type or 'all'
|
||||||
|
|
||||||
self.timestamp = helpers.timestamp()
|
self.timestamp = helpers.timestamp()
|
||||||
|
|
||||||
|
@ -1439,22 +1442,33 @@ class Export(object):
|
||||||
|
|
||||||
def export(self):
|
def export(self):
|
||||||
msg = ''
|
msg = ''
|
||||||
if not self.section_id and not self.rating_key:
|
if not self.section_id and not self.user_id and not self.rating_key:
|
||||||
msg = "Export called but no section_id or rating_key provided."
|
msg = "Export called but no section_id, user_id, or rating_key provided."
|
||||||
elif self.metadata_level not in self.METADATA_LEVELS:
|
elif self.metadata_level not in self.METADATA_LEVELS:
|
||||||
msg = "Export called with invalid metadata_level '{}'.".format(self.metadata_level)
|
msg = "Export called with invalid metadata_level '{}'.".format(self.metadata_level)
|
||||||
elif self.media_info_level not in self.MEDIA_INFO_LEVELS:
|
elif self.media_info_level not in self.MEDIA_INFO_LEVELS:
|
||||||
msg = "Export called with invalid media_info_level '{}'.".format(self.media_info_level)
|
msg = "Export called with invalid media_info_level '{}'.".format(self.media_info_level)
|
||||||
elif self.file_format not in self.FILE_FORMATS:
|
elif self.file_format not in self.FILE_FORMATS:
|
||||||
msg = "Export called with invalid file_format '{}'.".format(self.file_format)
|
msg = "Export called with invalid file_format '{}'.".format(self.file_format)
|
||||||
elif self.library_export not in self.LIBRARY_EXPORTS:
|
elif self.export_type not in self.EXPORT_TYPES:
|
||||||
msg = "Export called with invalid library_export '{}'.".format(self.library_export)
|
msg = "Export called with invalid export_type '{}'.".format(self.export_type)
|
||||||
|
elif self.user_id and self.export_type != 'playlist':
|
||||||
|
msg = "Export called with invalid export_type '{}'. " \
|
||||||
|
"Only export_type 'playlist' is allowed for user export."
|
||||||
|
|
||||||
if msg:
|
if msg:
|
||||||
logger.error("Tautulli Exporter :: %s", msg)
|
logger.error("Tautulli Exporter :: %s", msg)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
plex = Plex(plexpy.CONFIG.PMS_URL, plexpy.CONFIG.PMS_TOKEN)
|
if self.user_id:
|
||||||
|
user_data = users.Users()
|
||||||
|
user_info = user_data.get_details(user_id=self.user_id)
|
||||||
|
user_tokens = user_data.get_tokens(user_id=self.user_id)
|
||||||
|
plex_token = user_tokens['server_token']
|
||||||
|
else:
|
||||||
|
plex_token = plexpy.CONFIG.PMS_TOKEN
|
||||||
|
|
||||||
|
plex = Plex(plexpy.CONFIG.PMS_URL, plex_token)
|
||||||
|
|
||||||
if self.rating_key:
|
if self.rating_key:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -1481,28 +1495,45 @@ class Export(object):
|
||||||
self.media_type.title(), item_title, self.rating_key,
|
self.media_type.title(), item_title, self.rating_key,
|
||||||
helpers.timestamp_to_YMDHMS(self.timestamp))
|
helpers.timestamp_to_YMDHMS(self.timestamp))
|
||||||
|
|
||||||
|
elif self.user_id:
|
||||||
|
logger.debug(
|
||||||
|
"Tautulli Exporter :: Export called with user_id %s, "
|
||||||
|
"metadata_level %d, media_info_level %d, include_thumb %s, include_art %s, "
|
||||||
|
"export_type %s",
|
||||||
|
self.user_id, self.metadata_level, self.media_info_level,
|
||||||
|
self.include_thumb, self.include_art, self.export_type)
|
||||||
|
|
||||||
|
self.obj = plex.plex
|
||||||
|
self.media_type = self.export_type
|
||||||
|
|
||||||
|
username = user_info['username']
|
||||||
|
|
||||||
|
filename = 'User - {} - {} [{}].{}'.format(
|
||||||
|
username, self.export_type.capitalize(), self.user_id,
|
||||||
|
helpers.timestamp_to_YMDHMS(self.timestamp))
|
||||||
|
|
||||||
elif self.section_id:
|
elif self.section_id:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Tautulli Exporter :: Export called with section_id %s, "
|
"Tautulli Exporter :: Export called with section_id %s, "
|
||||||
"metadata_level %d, media_info_level %d, include_thumb %s, include_art %s, "
|
"metadata_level %d, media_info_level %d, include_thumb %s, include_art %s, "
|
||||||
"library_export %s",
|
"export_type %s",
|
||||||
self.section_id, self.metadata_level, self.media_info_level,
|
self.section_id, self.metadata_level, self.media_info_level,
|
||||||
self.include_thumb, self.include_art, self.library_export)
|
self.include_thumb, self.include_art, self.export_type)
|
||||||
|
|
||||||
self.obj = plex.get_library(str(self.section_id))
|
self.obj = plex.get_library(str(self.section_id))
|
||||||
if self.library_export == 'all':
|
if self.export_type == 'all':
|
||||||
self.media_type = self.obj.type
|
self.media_type = self.obj.type
|
||||||
else:
|
else:
|
||||||
self.media_type = self.library_export
|
self.media_type = self.export_type
|
||||||
|
|
||||||
library_title = self.obj.title
|
library_title = self.obj.title
|
||||||
|
|
||||||
filename = 'Library - {} [{}].{}'.format(
|
filename = 'Library - {} - {} [{}].{}'.format(
|
||||||
library_title, self.section_id,
|
library_title, self.export_type.capitalize(), self.section_id,
|
||||||
helpers.timestamp_to_YMDHMS(self.timestamp))
|
helpers.timestamp_to_YMDHMS(self.timestamp))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = "Export called but no section_id or rating_key provided."
|
msg = "Export called but no section_id, user_id, or rating_key provided."
|
||||||
logger.error("Tautulli Exporter :: %s", msg)
|
logger.error("Tautulli Exporter :: %s", msg)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
@ -1529,6 +1560,7 @@ class Export(object):
|
||||||
def add_export(self):
|
def add_export(self):
|
||||||
keys = {'timestamp': self.timestamp,
|
keys = {'timestamp': self.timestamp,
|
||||||
'section_id': self.section_id,
|
'section_id': self.section_id,
|
||||||
|
'user_id': self.user_id,
|
||||||
'rating_key': self.rating_key,
|
'rating_key': self.rating_key,
|
||||||
'media_type': self.media_type}
|
'media_type': self.media_type}
|
||||||
|
|
||||||
|
@ -1571,13 +1603,12 @@ class Export(object):
|
||||||
|
|
||||||
if self.rating_key:
|
if self.rating_key:
|
||||||
items = [self.obj]
|
items = [self.obj]
|
||||||
|
elif self.user_id:
|
||||||
|
# Only playlists export allowed for users
|
||||||
|
items = self.obj.playlists()
|
||||||
else:
|
else:
|
||||||
if self.library_export == 'collection':
|
method = getattr(self.obj, self.export_type)
|
||||||
items = self.obj.collection()
|
items = method()
|
||||||
elif self.library_export == 'playlist':
|
|
||||||
items = self.obj.playlist()
|
|
||||||
else:
|
|
||||||
items = self.obj.all()
|
|
||||||
|
|
||||||
pool = ThreadPool(processes=4)
|
pool = ThreadPool(processes=4)
|
||||||
|
|
||||||
|
@ -1810,7 +1841,7 @@ def cancel_exports():
|
||||||
db.action('UPDATE exports SET complete = -1 WHERE complete = 0')
|
db.action('UPDATE exports SET complete = -1 WHERE complete = 0')
|
||||||
|
|
||||||
|
|
||||||
def get_export_datatable(section_id=None, rating_key=None, kwargs=None):
|
def get_export_datatable(section_id=None, user_id=None, rating_key=None, kwargs=None):
|
||||||
default_return = {'recordsFiltered': 0,
|
default_return = {'recordsFiltered': 0,
|
||||||
'recordsTotal': 0,
|
'recordsTotal': 0,
|
||||||
'draw': 0,
|
'draw': 0,
|
||||||
|
@ -1822,12 +1853,15 @@ def get_export_datatable(section_id=None, rating_key=None, kwargs=None):
|
||||||
custom_where = []
|
custom_where = []
|
||||||
if section_id:
|
if section_id:
|
||||||
custom_where.append(['exports.section_id', section_id])
|
custom_where.append(['exports.section_id', section_id])
|
||||||
|
if user_id:
|
||||||
|
custom_where.append(['exports.user_id', user_id])
|
||||||
if rating_key:
|
if rating_key:
|
||||||
custom_where.append(['exports.rating_key', rating_key])
|
custom_where.append(['exports.rating_key', rating_key])
|
||||||
|
|
||||||
columns = ['exports.id AS export_id',
|
columns = ['exports.id AS export_id',
|
||||||
'exports.timestamp',
|
'exports.timestamp',
|
||||||
'exports.section_id',
|
'exports.section_id',
|
||||||
|
'exports.user_id',
|
||||||
'exports.rating_key',
|
'exports.rating_key',
|
||||||
'exports.media_type',
|
'exports.media_type',
|
||||||
'exports.filename',
|
'exports.filename',
|
||||||
|
@ -1863,6 +1897,7 @@ def get_export_datatable(section_id=None, rating_key=None, kwargs=None):
|
||||||
row = {'export_id': item['export_id'],
|
row = {'export_id': item['export_id'],
|
||||||
'timestamp': item['timestamp'],
|
'timestamp': item['timestamp'],
|
||||||
'section_id': item['section_id'],
|
'section_id': item['section_id'],
|
||||||
|
'user_id': item['user_id'],
|
||||||
'rating_key': item['rating_key'],
|
'rating_key': item['rating_key'],
|
||||||
'media_type': item['media_type'],
|
'media_type': item['media_type'],
|
||||||
'media_type_title': media_type_title,
|
'media_type_title': media_type_title,
|
||||||
|
@ -1906,21 +1941,26 @@ def get_custom_fields(media_type, sub_media_type=None):
|
||||||
'media_info_fields': []
|
'media_info_fields': []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collection_sub_media_types = {'movie', 'show', 'artist', 'album', 'photoalbum'}
|
||||||
|
playlist_sub_media_types = {'video', 'audio', 'photo'}
|
||||||
|
sub_media_type = {s.strip().lower() for s in sub_media_type.split(',')}
|
||||||
|
|
||||||
export = Export()
|
export = Export()
|
||||||
|
|
||||||
if media_type not in export.MEDIA_TYPES:
|
if media_type not in export.MEDIA_TYPES:
|
||||||
return custom_fields
|
return custom_fields
|
||||||
elif media_type == 'collection' and sub_media_type not in ('movie', 'show', 'artist', 'album', 'photoalbum'):
|
elif media_type == 'collection' and not sub_media_type.issubset(collection_sub_media_types):
|
||||||
return custom_fields
|
return custom_fields
|
||||||
elif media_type == 'playlist' and sub_media_type not in ('video', 'audio', 'photo'):
|
elif media_type == 'playlist' and not sub_media_type.issubset(playlist_sub_media_types):
|
||||||
return custom_fields
|
return custom_fields
|
||||||
|
|
||||||
if media_type == 'playlist' and sub_media_type == 'video':
|
sub_media_types = list(sub_media_type.difference(playlist_sub_media_types))
|
||||||
sub_media_types = ['movie', 'episode']
|
if media_type == 'playlist' and 'video' in sub_media_type:
|
||||||
elif media_type == 'playlist' and sub_media_type == 'audio':
|
sub_media_types += ['movie', 'episode']
|
||||||
sub_media_types = ['track']
|
elif media_type == 'playlist' and 'audio' in sub_media_type:
|
||||||
else:
|
sub_media_types += ['track']
|
||||||
sub_media_types = [sub_media_type]
|
elif media_type == 'playlist' and 'photo' in sub_media_type:
|
||||||
|
sub_media_types += ['photo']
|
||||||
|
|
||||||
metadata_levels_map, media_info_levels_map = export.return_attrs_level_map(media_type)
|
metadata_levels_map, media_info_levels_map = export.return_attrs_level_map(media_type)
|
||||||
|
|
||||||
|
|
|
@ -6508,12 +6508,13 @@ class WebInterface(object):
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi("get_exports_table")
|
@addtoapi("get_exports_table")
|
||||||
def get_export_list(self, section_id=None, rating_key=None, **kwargs):
|
def get_export_list(self, section_id=None, user_id=None, rating_key=None, **kwargs):
|
||||||
""" Get the data on the Tautulli export tables.
|
""" Get the data on the Tautulli export tables.
|
||||||
|
|
||||||
```
|
```
|
||||||
Required parameters:
|
Required parameters:
|
||||||
section_id (str): The id of the Plex library section, OR
|
section_id (str): The id of the Plex library section, OR
|
||||||
|
user_id (str): The id of the Plex user, OR
|
||||||
rating_key (str): The rating key of the exported item
|
rating_key (str): The rating key of the exported item
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
@ -6559,6 +6560,7 @@ class WebInterface(object):
|
||||||
kwargs['json_data'] = build_datatables_json(kwargs, dt_columns, "timestamp")
|
kwargs['json_data'] = build_datatables_json(kwargs, dt_columns, "timestamp")
|
||||||
|
|
||||||
result = exporter.get_export_datatable(section_id=section_id,
|
result = exporter.get_export_datatable(section_id=section_id,
|
||||||
|
user_id=user_id,
|
||||||
rating_key=rating_key,
|
rating_key=rating_key,
|
||||||
kwargs=kwargs)
|
kwargs=kwargs)
|
||||||
|
|
||||||
|
@ -6566,15 +6568,15 @@ class WebInterface(object):
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
def export_metadata_modal(self, section_id=None, rating_key=None,
|
def export_metadata_modal(self, section_id=None, user_id=None, rating_key=None,
|
||||||
media_type=None, sub_media_type=None,
|
media_type=None, sub_media_type=None,
|
||||||
library_export=None, **kwargs):
|
export_type=None, **kwargs):
|
||||||
file_formats = exporter.Export.FILE_FORMATS
|
file_formats = exporter.Export.FILE_FORMATS
|
||||||
|
|
||||||
return serve_template(templatename="export_modal.html", title="Export Metadata",
|
return serve_template(templatename="export_modal.html", title="Export Metadata",
|
||||||
section_id=section_id, rating_key=rating_key,
|
section_id=section_id, user_id=user_id, rating_key=rating_key,
|
||||||
media_type=media_type, sub_media_type=sub_media_type,
|
media_type=media_type, sub_media_type=sub_media_type,
|
||||||
library_export=library_export, file_formats=file_formats)
|
export_type=export_type, file_formats=file_formats)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
|
@ -6614,15 +6616,16 @@ class WebInterface(object):
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi()
|
@addtoapi()
|
||||||
def export_metadata(self, section_id=None, rating_key=None, file_format='csv',
|
def export_metadata(self, section_id=None, user_id=None, rating_key=None, file_format='csv',
|
||||||
metadata_level=1, media_info_level=1,
|
metadata_level=1, media_info_level=1,
|
||||||
include_thumb=False, include_art=False,
|
include_thumb=False, include_art=False,
|
||||||
custom_fields='', library_export=None, **kwargs):
|
custom_fields='', export_type=None, **kwargs):
|
||||||
""" Export library or media metadata to a file
|
""" Export library or media metadata to a file
|
||||||
|
|
||||||
```
|
```
|
||||||
Required parameters:
|
Required parameters:
|
||||||
section_id (int): The section id of the library items to export, OR
|
section_id (int): The section id of the library items to export, OR
|
||||||
|
user_id (int): The user id of the playlist items to export,
|
||||||
rating_key (int): The rating key of the media item to export
|
rating_key (int): The rating key of the media item to export
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
@ -6633,7 +6636,7 @@ class WebInterface(object):
|
||||||
include_art (bool): True to export background artwork images
|
include_art (bool): True to export background artwork images
|
||||||
custom_fields (str): Comma separated list of custom fields to export
|
custom_fields (str): Comma separated list of custom fields to export
|
||||||
in addition to the export level selected
|
in addition to the export level selected
|
||||||
library_export (str): collection or playlist for library export,
|
export_type (str): collection or playlist for library/user export,
|
||||||
otherwise default to all library items
|
otherwise default to all library items
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -6644,6 +6647,7 @@ class WebInterface(object):
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
result = exporter.Export(section_id=section_id,
|
result = exporter.Export(section_id=section_id,
|
||||||
|
user_id=user_id,
|
||||||
rating_key=rating_key,
|
rating_key=rating_key,
|
||||||
file_format=file_format,
|
file_format=file_format,
|
||||||
metadata_level=metadata_level,
|
metadata_level=metadata_level,
|
||||||
|
@ -6651,7 +6655,7 @@ class WebInterface(object):
|
||||||
include_thumb=helpers.bool_true(include_thumb),
|
include_thumb=helpers.bool_true(include_thumb),
|
||||||
include_art=helpers.bool_true(include_art),
|
include_art=helpers.bool_true(include_art),
|
||||||
custom_fields=custom_fields,
|
custom_fields=custom_fields,
|
||||||
library_export=library_export).export()
|
export_type=export_type).export()
|
||||||
|
|
||||||
if result is True:
|
if result is True:
|
||||||
return {'result': 'success', 'message': 'Metadata export has started.'}
|
return {'result': 'success', 'message': 'Metadata export has started.'}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue