Add playlist export for users

This commit is contained in:
JonnyWong16 2020-10-02 12:54:07 -07:00
parent 501f08dd5e
commit ea9904bd56
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
7 changed files with 201 additions and 55 deletions

View file

@ -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) {

View file

@ -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>

View file

@ -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,

View file

@ -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);

View file

@ -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, '

View file

@ -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)

View file

@ -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.'}