Skip to content

Commit

Permalink
Properly close file object in mixin uploads (#1074)
Browse files Browse the repository at this point in the history
* Properly close file object in mixin uploads

* Also support a file-like object as the upload `filepath` parameter

* Update image upload tests for file-like object
  • Loading branch information
JonnyWong16 authored Dec 21, 2022
1 parent e1e9a69 commit 9b8c7d5
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 9 deletions.
18 changes: 9 additions & 9 deletions plexapi/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from plexapi import media, settings, utils
from plexapi.exceptions import BadRequest, NotFound
from plexapi.utils import deprecated
from plexapi.utils import deprecated, openOrRead


class AdvancedSettingsMixin:
Expand Down Expand Up @@ -341,14 +341,14 @@ def uploadArt(self, url=None, filepath=None):
Parameters:
url (str): The full URL to the image to upload.
filepath (str): The full file path the the image to upload.
filepath (str): The full file path the the image to upload or file-like object.
"""
if url:
key = f'/library/metadata/{self.ratingKey}/arts?url={quote_plus(url)}'
self._server.query(key, method=self._server._session.post)
elif filepath:
key = f'/library/metadata/{self.ratingKey}/arts'
data = open(filepath, 'rb').read()
data = openOrRead(filepath)
self._server.query(key, method=self._server._session.post, data=data)
return self

Expand Down Expand Up @@ -392,14 +392,14 @@ def uploadBanner(self, url=None, filepath=None):
Parameters:
url (str): The full URL to the image to upload.
filepath (str): The full file path the the image to upload.
filepath (str): The full file path the the image to upload or file-like object.
"""
if url:
key = f'/library/metadata/{self.ratingKey}/banners?url={quote_plus(url)}'
self._server.query(key, method=self._server._session.post)
elif filepath:
key = f'/library/metadata/{self.ratingKey}/banners'
data = open(filepath, 'rb').read()
data = openOrRead(filepath)
self._server.query(key, method=self._server._session.post, data=data)
return self

Expand Down Expand Up @@ -448,14 +448,14 @@ def uploadPoster(self, url=None, filepath=None):
Parameters:
url (str): The full URL to the image to upload.
filepath (str): The full file path the the image to upload.
filepath (str): The full file path the the image to upload or file-like object.
"""
if url:
key = f'/library/metadata/{self.ratingKey}/posters?url={quote_plus(url)}'
self._server.query(key, method=self._server._session.post)
elif filepath:
key = f'/library/metadata/{self.ratingKey}/posters'
data = open(filepath, 'rb').read()
data = openOrRead(filepath)
self._server.query(key, method=self._server._session.post, data=data)
return self

Expand Down Expand Up @@ -501,7 +501,7 @@ def uploadTheme(self, url=None, filepath=None, timeout=None):
Parameters:
url (str): The full URL to the theme to upload.
filepath (str): The full file path to the theme to upload.
filepath (str): The full file path to the theme to upload or file-like object.
timeout (int, optional): Timeout, in seconds, to use when uploading themes to the server.
(default config.TIMEOUT).
"""
Expand All @@ -510,7 +510,7 @@ def uploadTheme(self, url=None, filepath=None, timeout=None):
self._server.query(key, method=self._server._session.post, timeout=timeout)
elif filepath:
key = f'/library/metadata/{self.ratingKey}/themes'
data = open(filepath, 'rb').read()
data = openOrRead(filepath)
self._server.query(key, method=self._server._session.post, data=data, timeout=timeout)
return self

Expand Down
7 changes: 7 additions & 0 deletions plexapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,10 @@ def serialize(obj):
return obj.isoformat()
return {k: v for k, v in obj.__dict__.items() if not k.startswith('_')}
return json.dumps(obj, default=serialize, **kwargs)


def openOrRead(file):
if hasattr(file, 'read'):
return file.read()
with open(file, 'rb') as f:
return f.read()
23 changes: 23 additions & 0 deletions tests/test_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def _test_mixins_field(obj, attr, field_method):
edit_field_method = getattr(obj, "edit" + field_method)
_value = lambda: getattr(obj, attr)
_fields = lambda: [f for f in obj.fields if f.name == attr]

# Check field does not match to begin with
default_value = _value()
if isinstance(default_value, datetime):
Expand All @@ -26,13 +27,15 @@ def _test_mixins_field(obj, attr, field_method):
else:
test_value = TEST_MIXIN_FIELD
assert default_value != test_value

# Edit and lock the field
edit_field_method(test_value)
obj.reload()
value = _value()
fields = _fields()
assert value == test_value
assert fields and fields[0].locked

# Reset and unlock the field to restore the clean state
edit_field_method(default_value, locked=False)
obj.reload()
Expand Down Expand Up @@ -229,6 +232,7 @@ def _test_mixins_edit_image(obj, attr):
assert images[1].selected is True
else:
default_image = None

# Test upload image from file
upload_img_method(filepath=utils.STUB_IMAGE_PATH)
images = get_img_method()
Expand All @@ -237,9 +241,25 @@ def _test_mixins_edit_image(obj, attr):
if i.ratingKey.startswith("upload://") and i.ratingKey.endswith(CUTE_CAT_SHA1)
]
assert file_image

# Reset to default image
if default_image:
set_img_method(default_image)

# Test upload image from file-like ojbect
with open(utils.STUB_IMAGE_PATH, "rb") as f:
upload_img_method(filepath=f)
images = get_img_method()
file_image = [
i for i in images
if i.ratingKey.startswith("upload://") and i.ratingKey.endswith(CUTE_CAT_SHA1)
]
assert file_image

# Reset to default image
if default_image:
set_img_method(default_image)

# Unlock the image
unlock_img_method = getattr(obj, "unlock" + cap_attr)
unlock_img_method()
Expand Down Expand Up @@ -283,6 +303,7 @@ def attr_posterUrl(obj):

def _test_mixins_edit_theme(obj):
_fields = lambda: [f.name for f in obj.fields]

# Test upload theme from file
obj.uploadTheme(filepath=utils.STUB_MP3_PATH)
themes = obj.themes()
Expand All @@ -293,10 +314,12 @@ def _test_mixins_edit_theme(obj):
assert file_theme
obj.reload()
assert "theme" in _fields()

# Unlock the theme
obj.unlockTheme()
obj.reload()
assert "theme" not in _fields()

# Lock the theme
obj.lockTheme()
obj.reload()
Expand Down

0 comments on commit 9b8c7d5

Please sign in to comment.