diff --git a/data/interfaces/default/export_modal.html b/data/interfaces/default/export_modal.html index 5830d5e3..37da7804 100644 --- a/data/interfaces/default/export_modal.html +++ b/data/interfaces/default/export_modal.html @@ -28,6 +28,7 @@ DOCUMENTATION :: END +
@@ -194,6 +195,7 @@ DOCUMENTATION :: END $('#export_custom_metadata_fields').val(), $('#export_custom_media_info_fields').val() ].filter(Boolean).join(','); + var library_export = $('#export_library_export').val() $.ajax({ url: 'export_metadata', @@ -205,7 +207,8 @@ DOCUMENTATION :: END file_format: file_format, include_thumb: include_thumb, include_art: include_art, - custom_fields: custom_fields + custom_fields: custom_fields, + library_export: library_export }, async: true, success: function (data) { diff --git a/data/interfaces/default/info.html b/data/interfaces/default/info.html index f94b219d..f075ed04 100644 --- a/data/interfaces/default/info.html +++ b/data/interfaces/default/info.html @@ -614,8 +614,8 @@ DOCUMENTATION :: END
diff --git a/data/interfaces/default/library.html b/data/interfaces/default/library.html index ab757cfe..85f94e75 100644 --- a/data/interfaces/default/library.html +++ b/data/interfaces/default/library.html @@ -322,6 +322,13 @@ DOCUMENTATION :: END
% if _session['user_group'] == 'admin': +
+ +
% if _session['user_group'] == 'admin': + <% playlist_sub_media_type = {'movie': 'video', 'show': 'video', 'artist': 'audio', 'photo': 'photo'} %> +
+ +
@@ -705,12 +721,14 @@ DOCUMENTATION :: END }); }); - $("#toggle-export-modal").click(function() { + $(".export-button").click(function() { $.ajax({ url: 'export_metadata_modal', data: { 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'), + library_export: $(this).data('library_export') }, cache: false, async: true, diff --git a/plexpy/exporter.py b/plexpy/exporter.py index 9a5ea94a..bcb2861f 100644 --- a/plexpy/exporter.py +++ b/plexpy/exporter.py @@ -90,11 +90,12 @@ class Export(object): METADATA_LEVELS = (0, 1, 2, 3, 9) MEDIA_INFO_LEVELS = (0, 1, 2, 3, 9) FILE_FORMATS = ('csv', 'json', 'xml') + LIBRARY_EXPORTS = ('all', 'collection', 'playlist') def __init__(self, section_id=None, rating_key=None, file_format='csv', metadata_level=1, media_info_level=1, include_thumb=False, include_art=False, - custom_fields=''): + custom_fields='', library_export=None): self.section_id = helpers.cast_to_int(section_id) or None self.rating_key = helpers.cast_to_int(rating_key) or None self.file_format = file_format @@ -104,6 +105,7 @@ class Export(object): self.include_art = include_art self.custom_fields = custom_fields.replace(' ', '') self._custom_fields = {} + self.library_export = library_export or 'all' self.timestamp = helpers.timestamp() @@ -1080,13 +1082,11 @@ class Export(object): def show_levels(): _media_type = 'show' _metadata_levels = { - 0: [ - 'seasons' - ], 1: [ 'ratingKey', 'title', 'titleSort', 'originallyAvailableAt', 'year', 'addedAt', 'rating', 'userRating', 'contentRating', - 'studio', 'summary', 'guid', 'duration', 'durationHuman', 'type', 'childCount' + 'studio', 'summary', 'guid', 'duration', 'durationHuman', 'type', 'childCount', + 'seasons' ], 2: [ 'roles.tag', 'roles.role', @@ -1105,14 +1105,12 @@ class Export(object): def season_levels(): _media_type = 'season' _metadata_levels = { - 0: [ - 'episodes' - ], 1: [ 'ratingKey', 'title', 'titleSort', 'addedAt', 'userRating', 'summary', 'guid', 'type', 'index', - 'parentTitle', 'parentRatingKey', 'parentGuid' + 'parentTitle', 'parentRatingKey', 'parentGuid', + 'episodes' ], 2: [ 'fields.name', 'fields.locked' @@ -1194,13 +1192,11 @@ class Export(object): def artist_levels(): _media_type = 'artist' _metadata_levels = { - 0: [ - 'albums' - ], 1: [ 'ratingKey', 'title', 'titleSort', 'addedAt', 'rating', 'userRating', - 'summary', 'guid', 'type' + 'summary', 'guid', 'type', + 'albums' ], 2: [ 'collections.tag', 'genres.tag', 'countries.tag', 'moods.tag', 'styles.tag', @@ -1218,14 +1214,12 @@ class Export(object): def album_levels(): _media_type = 'album' _metadata_levels = { - 0: [ - 'tracks' - ], 1: [ 'ratingKey', 'title', 'titleSort', 'originallyAvailableAt', 'addedAt', 'rating', 'userRating', 'summary', 'guid', 'type', 'index', - 'parentTitle', 'parentRatingKey', 'parentGuid' + 'parentTitle', 'parentRatingKey', 'parentGuid', + 'tracks' ], 2: [ 'collections.tag', 'genres.tag', 'labels.tag', 'moods.tag', 'styles.tag', @@ -1302,12 +1296,10 @@ class Export(object): def photo_album_levels(): _media_type = 'photoalbum' _metadata_levels = { - 0: [ - 'photos' - ], 1: [ 'ratingKey', 'title', 'titleSort', 'addedAt', 'summary', 'guid', 'type', 'index', + 'photos' ], 2: [ 'fields.name', 'fields.locked' @@ -1361,14 +1353,12 @@ class Export(object): def collection_levels(): _media_type = 'collection' _metadata_levels = { - 0: [ - 'children' - ], 1: [ 'ratingKey', 'title', 'titleSort', 'minYear', 'maxYear', 'addedAt', 'contentRating', 'summary', 'guid', 'type', 'subtype', 'childCount', - 'collectionMode', 'collectionSort' + 'collectionMode', 'collectionSort', + 'children' ], 2: [ 'labels.tag', @@ -1386,13 +1376,11 @@ class Export(object): def playlist_levels(): _media_type = 'playlist' _metadata_levels = { - 0: [ - 'items' - ], 1: [ 'ratingKey', 'title', 'addedAt', 'summary', 'guid', 'type', 'duration', 'durationHuman', - 'playlistType', 'smart' + 'playlistType', 'smart', + 'items' ], 2: [ ], @@ -1459,6 +1447,8 @@ class Export(object): msg = "Export called with invalid media_info_level '{}'.".format(self.media_info_level) elif self.file_format not in self.FILE_FORMATS: msg = "Export called with invalid file_format '{}'.".format(self.file_format) + elif self.library_export not in self.LIBRARY_EXPORTS: + msg = "Export called with invalid library_export '{}'.".format(self.library_export) if msg: logger.error("Tautulli Exporter :: %s", msg) @@ -1494,12 +1484,17 @@ class Export(object): elif self.section_id: logger.debug( "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", self.section_id, self.metadata_level, self.media_info_level, - self.include_thumb, self.include_art) + self.include_thumb, self.include_art, self.library_export) self.obj = plex.get_library(str(self.section_id)) - self.media_type = self.obj.type + if self.library_export == 'all': + self.media_type = self.obj.type + else: + self.media_type = self.library_export + library_title = self.obj.title filename = 'Library - {} [{}].{}'.format( @@ -1574,10 +1569,15 @@ class Export(object): filepath = get_export_filepath(self.filename) images_folder = get_export_filepath(self.filename, images=True) - if hasattr(self.obj, 'all'): - items = self.obj.all() - else: + if self.rating_key: items = [self.obj] + else: + if self.library_export == 'collection': + items = self.obj.collection() + elif self.library_export == 'playlist': + items = self.obj.playlist() + else: + items = self.obj.all() pool = ThreadPool(processes=4) @@ -1910,29 +1910,39 @@ def get_custom_fields(media_type, sub_media_type=None): if media_type not in export.MEDIA_TYPES: return custom_fields - elif media_type in ('collection', 'playlist') and sub_media_type not in export.MEDIA_TYPES: + elif media_type == 'collection' and sub_media_type not in ('movie', 'show', 'artist', 'album', 'photoalbum'): return custom_fields + elif media_type == 'playlist' and sub_media_type not in ('video', 'audio', 'photo'): + return custom_fields + + if media_type == 'playlist' and sub_media_type == 'video': + sub_media_types = ['movie', 'episode'] + elif media_type == 'playlist' and sub_media_type == 'audio': + sub_media_types = ['track'] + else: + sub_media_types = [sub_media_type] metadata_levels_map, media_info_levels_map = export.return_attrs_level_map(media_type) - prefix = '' - child_media_type = export.CHILD_MEDIA_TYPES[media_type] + for sub_media_type in sub_media_types: + prefix = '' + child_media_type = export.CHILD_MEDIA_TYPES[media_type] - while child_media_type: - if child_media_type in ('children', 'item'): - fields_child_media_type = sub_media_type - else: - fields_child_media_type = child_media_type + while child_media_type: + if child_media_type in ('children', 'item'): + fields_child_media_type = sub_media_type + else: + fields_child_media_type = child_media_type - prefix = prefix + export.PLURAL_MEDIA_TYPES[child_media_type] + '.' + prefix = prefix + export.PLURAL_MEDIA_TYPES[child_media_type] + '.' - child_metadata_levels_map, child_media_info_levels_map = export.return_attrs_level_map( - fields_child_media_type, prefix=prefix) + child_metadata_levels_map, child_media_info_levels_map = export.return_attrs_level_map( + fields_child_media_type, prefix=prefix) - metadata_levels_map.update(child_metadata_levels_map) - media_info_levels_map.update(child_media_info_levels_map) + metadata_levels_map.update(child_metadata_levels_map) + media_info_levels_map.update(child_media_info_levels_map) - child_media_type = export.CHILD_MEDIA_TYPES.get(fields_child_media_type) + child_media_type = export.CHILD_MEDIA_TYPES.get(fields_child_media_type) custom_fields['metadata_fields'] = [{'field': attr, 'level': level} for attr, level in sorted(metadata_levels_map.items()) if level] diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 6dce962f..cf446d4e 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -6557,13 +6557,14 @@ class WebInterface(object): @cherrypy.expose @requireAuth(member_of("admin")) def export_metadata_modal(self, section_id=None, rating_key=None, - media_type=None, sub_media_type=None, **kwargs): + media_type=None, sub_media_type=None, + library_export=None, **kwargs): file_formats = exporter.Export.FILE_FORMATS return serve_template(templatename="export_modal.html", title="Export Metadata", section_id=section_id, rating_key=rating_key, media_type=media_type, sub_media_type=sub_media_type, - file_formats=file_formats) + library_export=library_export, file_formats=file_formats) @cherrypy.expose @cherrypy.tools.json_out() @@ -6577,7 +6578,9 @@ class WebInterface(object): media_type (str): The media type of the fields to return Optional parameters: - sub_media_type (str): The child media type for collections or playlists + sub_media_type (str): The child media type for + collections (movie, show, artist, album, photoalbum), + or playlists (video, audio, photo) Returns: json: @@ -6604,12 +6607,12 @@ class WebInterface(object): def export_metadata(self, section_id=None, rating_key=None, file_format='csv', metadata_level=1, media_info_level=1, include_thumb=False, include_art=False, - custom_fields='', **kwargs): + custom_fields='', library_export=None, **kwargs): """ Export library or media metadata to a file ``` Required parameters: - section_id (int): The section id of the library to export, OR + section_id (int): The section id of the library items to export, OR rating_key (int): The rating key of the media item to export Optional parameters: @@ -6620,6 +6623,8 @@ class WebInterface(object): include_art (bool): True to export background artwork images custom_fields (str): Comma separated list of custom fields to export in addition to the export level selected + library_export (str): collection or playlist for library export, + otherwise default to all library items Returns: json: @@ -6635,7 +6640,8 @@ class WebInterface(object): media_info_level=media_info_level, include_thumb=helpers.bool_true(include_thumb), include_art=helpers.bool_true(include_art), - custom_fields=custom_fields).export() + custom_fields=custom_fields, + library_export=library_export).export() if result is True: return {'result': 'success', 'message': 'Metadata export has started.'}