diff --git a/plexpy/exporter.py b/plexpy/exporter.py index 9c9880c2..9f3b323a 100644 --- a/plexpy/exporter.py +++ b/plexpy/exporter.py @@ -69,7 +69,6 @@ class Export(object): self.items = [] self.filename = None - self.filename_ext = None self.export_id = None self.file_size = None self.success = False @@ -1205,8 +1204,9 @@ class Export(object): if self.rating_key: logger.debug( - "Tautulli Exporter :: Export called with rating_key %s, metadata_level %d, media_info_level %d", - self.rating_key, self.metadata_level, self.media_info_level) + "Tautulli Exporter :: Export called with rating_key %s, " + "metadata_level %d, media_info_level %d, include_images %s", + self.rating_key, self.metadata_level, self.media_info_level, self.include_images) item = plex.get_item(self.rating_key) self.media_type = item.type @@ -1290,8 +1290,7 @@ class Export(object): export_attrs = reduce(helpers.dict_merge, export_attrs_list) - self.filename = helpers.clean_filename(filename) - self.filename_ext = '{}.{}'.format(self.filename, self.file_format) + self.filename = '{}.{}'.format(helpers.clean_filename(filename), self.file_format) self.export_id = self.add_export() @@ -1305,9 +1304,9 @@ class Export(object): return True def _real_export(self, attrs): - logger.info("Tautulli Exporter :: Starting export for '%s'...", self.filename_ext) + logger.info("Tautulli Exporter :: Starting export for '%s'...", self.filename) - filepath = get_export_filepath(self.filename_ext) + filepath = get_export_filepath(self.filename) part = partial(helpers.get_attrs_to_dict, attrs=attrs) pool = ThreadPool(processes=4) @@ -1331,7 +1330,7 @@ class Export(object): self.file_size = os.path.getsize(filepath) if self.include_images: - images_folder = get_export_filepath('{}.images'.format(self.filename)) + images_folder = get_export_filepath(self.filename, images=True) for f in os.listdir(images_folder): image_path = os.path.join(images_folder, f) if os.path.isfile(image_path): @@ -1341,7 +1340,7 @@ class Export(object): logger.info("Tautulli Exporter :: Successfully exported to '%s'", filepath) except Exception as e: - logger.error("Tautulli Exporter :: Failed to export '%s': %s", self.filename_ext, e) + logger.error("Tautulli Exporter :: Failed to export '%s': %s", self.filename, e) import traceback traceback.print_exc() @@ -1357,7 +1356,7 @@ class Export(object): 'media_type': self.media_type} values = {'file_format': self.file_format, - 'filename': self.filename_ext, + 'filename': self.filename, 'include_images': self.include_images} db = database.MonitorDatabase() @@ -1397,8 +1396,10 @@ def get_image(item, image, export_filename): else: item_title = item.title - folder = get_export_filepath('{}.images'.format(export_filename)) - filepath = os.path.join(folder, '{} [{}].{}.jpg'.format(item_title, rating_key, image)) + folder = get_export_filepath(export_filename, images=True) + filename = helpers.clean_filename('{} [{}].{}.jpg'.format(item_title, rating_key, image)) + filepath = os.path.join(folder, filename) + if not os.path.exists(folder): os.makedirs(folder) @@ -1422,7 +1423,7 @@ def get_image(item, image, export_filename): def get_export(export_id): db = database.MonitorDatabase() - result = db.select_single('SELECT filename, file_format, complete ' + result = db.select_single('SELECT filename, file_format, include_images, complete ' 'FROM exports WHERE id = ?', [export_id]) @@ -1445,9 +1446,10 @@ def delete_export(export_id): logger.info("Tautulli Exporter :: Deleting exported file from '%s'.", filepath) try: os.remove(filepath) - folder = '{}.images'.format(os.path.splitext(filepath)[0]) - if os.path.exists(folder): - shutil.rmtree(folder) + if export_data['include_images']: + images_folder = get_export_filepath(export_data['filename'], images=True) + if os.path.exists(images_folder): + shutil.rmtree(images_folder) except OSError as e: logger.error("Tautulli Exporter :: Failed to delete exported file '%s': %s", filepath, e) return True @@ -1457,7 +1459,7 @@ def delete_export(export_id): def delete_all_exports(): db = database.MonitorDatabase() - result = db.select('SELECT filename FROM exports') + result = db.select('SELECT filename, include_images FROM exports') logger.info("Tautulli Exporter :: Deleting all exports from the database.") @@ -1467,9 +1469,10 @@ def delete_all_exports(): filepath = get_export_filepath(row['filename']) try: os.remove(filepath) - folder = '{}.images'.format(os.path.splitext(filepath)[0]) - if os.path.exists(folder): - shutil.rmtree(folder) + if row['include_images']: + images_folder = get_export_filepath(row['filename'], images=True) + if os.path.exists(images_folder): + shutil.rmtree(images_folder) except OSError as e: logger.error("Tautulli Exporter :: Failed to delete exported file '%s': %s", filepath, e) deleted_files = False @@ -1556,7 +1559,10 @@ def get_export_datatable(section_id=None, rating_key=None, kwargs=None): return result -def get_export_filepath(filename): +def get_export_filepath(filename, images=False): + if images: + images_folder = '{}.images'.format(os.path.splitext(filename)[0]) + return os.path.join(plexpy.CONFIG.EXPORT_DIR, images_folder) return os.path.join(plexpy.CONFIG.EXPORT_DIR, filename) diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 90f2e4f4..7af497c1 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -21,7 +21,7 @@ from future.builtins import object from future.builtins import str from backports import csv -from io import open +from io import open, BytesIO import base64 import json import linecache @@ -29,10 +29,11 @@ import os import shutil import sys import threading +import zipfile from future.moves.urllib.parse import urlencode import cherrypy -from cherrypy.lib.static import serve_file, serve_download +from cherrypy.lib.static import serve_file, serve_fileobj, serve_download from cherrypy._cperror import NotFound from hashing_passwords import make_hash @@ -6594,6 +6595,27 @@ class WebInterface(object): result = exporter.get_export(export_id=export_id) if result and result['complete'] == 1 and result['exists']: + export_filepath = exporter.get_export_filepath(result['filename']) + + if result['include_images']: + zip_filename = '{}.zip'.format(os.path.splitext(result['filename'])[0]) + images_folder = exporter.get_export_filepath(result['filename'], images=True) + + if os.path.exists(images_folder): + buffer = BytesIO() + temp_zip = zipfile.ZipFile(buffer, 'w') + temp_zip.write(export_filepath, arcname=result['filename']) + + _images_folder = os.path.basename(images_folder) + + for f in os.listdir(images_folder): + image_path = os.path.join(images_folder, f) + temp_zip.write(image_path, arcname=os.path.join(_images_folder, f)) + + temp_zip.close() + return serve_fileobj(buffer.getvalue(), content_type='application/zip', + disposition='attachment', name=zip_filename) + return serve_download(exporter.get_export_filepath(result['filename']), name=result['filename']) else: if result and result.get('complete') == 0: