diff --git a/plexapi/server.py b/plexapi/server.py index 5c0beaeda..ab359b3f6 100644 --- a/plexapi/server.py +++ b/plexapi/server.py @@ -734,21 +734,46 @@ def startAlertListener(self, callback=None): notifier.start() return notifier - def transcodeImage(self, media, height, width, opacity=100, saturation=100): - """ Returns the URL for a transcoded image from the specified media object. - Returns None if no media specified (needed if user tries to pass thumb - or art directly). + def transcodeImage(self, imageUrl, height, width, + opacity=None, saturation=None, blur=None, background=None, + minSize=True, upscale=True, imageFormat=None): + """ Returns the URL for a transcoded image. Parameters: + imageUrl (str): The URL to the image + (eg. returned by :func:`~plexapi.mixins.PosterUrlMixin.thumbUrl` + or :func:`~plexapi.mixins.ArtUrlMixin.artUrl`). + The URL can be an online image. height (int): Height to transcode the image to. width (int): Width to transcode the image to. - opacity (int): Opacity of the resulting image (possibly deprecated). - saturation (int): Saturating of the resulting image. + opacity (int, optional): Change the opacity of the image (0 to 100) + saturation (int, optional): Change the saturation of the image (0 to 100). + blur (int, optional): The blur to apply to the image in pixels (e.g. 3). + background (str, optional): The background hex colour to apply behind the opacity (e.g. '000000'). + minSize (bool, optional): Maintain smallest dimension. Default True. + upscale (bool, optional): Upscale the image if required. Default True. + imageFormat (str, optional): 'jpeg' (default) or 'png'. """ - if media: - transcode_url = '/photo/:/transcode?height=%s&width=%s&opacity=%s&saturation=%s&url=%s' % ( - height, width, opacity, saturation, media) - return self.url(transcode_url, includeToken=True) + params = { + 'url': imageUrl, + 'height': height, + 'width': width, + 'minSize': int(bool(minSize)), + 'upscale': int(bool(upscale)) + } + if opacity is not None: + params['opacity'] = opacity + if saturation is not None: + params['saturation'] = saturation + if blur is not None: + params['blur'] = blur + if background is not None: + params['background'] = str(background).strip('#') + if imageFormat is not None: + params['format'] = imageFormat.lower() + + key = '/photo/:/transcode%s' % utils.joinArgs(params) + return self.url(key, includeToken=True) def url(self, key, includeToken=None): """ Build a URL string with proper token argument. Token will be appended to the URL diff --git a/tests/test_server.py b/tests/test_server.py index a6b364969..f30f4ad8e 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -60,24 +60,52 @@ def test_server_url(plex): def test_server_transcodeImage(tmpdir, plex, movie): - width, height = 500, 500 - original_url = plex.url(movie.thumb) - resize_url = plex.transcodeImage(movie.thumb, height, width) - grayscale_url = plex.transcodeImage(movie.thumb, height, width, saturation=0) + width, height = 500, 100 + background = "000000" + + original_url = movie.thumbUrl + resize_jpeg_url = plex.transcodeImage(original_url, height, width) + no_minSize_png_url = plex.transcodeImage(original_url, height, width, minSize=False, imageFormat="png") + grayscale_url = plex.transcodeImage(original_url, height, width, saturation=0) + opacity_background_url = plex.transcodeImage(original_url, height, width, opacity=0, background=background, blur=100) + online_no_upscale_url = plex.transcodeImage( + "https://raw.githubusercontent.com/pkkid/python-plexapi/master/tests/data/cute_cat.jpg", 1000, 1000, upscale=False) + original_img = download( original_url, plex._token, savepath=str(tmpdir), filename="original_img", ) - resized_img = download( - resize_url, plex._token, savepath=str(tmpdir), filename="resize_image" + resized_jpeg_img = download( + resize_jpeg_url, plex._token, savepath=str(tmpdir), filename="resized_jpeg_img" + ) + no_minSize_png_img = download( + no_minSize_png_url, plex._token, savepath=str(tmpdir), filename="no_minSize_png_img" ) grayscale_img = download( grayscale_url, plex._token, savepath=str(tmpdir), filename="grayscale_img" ) - with Image.open(resized_img) as image: - assert width, height == image.size + opacity_background_img = download( + opacity_background_url, plex._token, savepath=str(tmpdir), filename="opacity_background_img" + ) + online_no_upscale_img = download( + online_no_upscale_url, plex._token, savepath=str(tmpdir), filename="online_no_upscale_img" + ) + with Image.open(original_img) as image: - assert width, height != image.size + assert image.size[0] != width + assert image.size[1] != height + with Image.open(resized_jpeg_img) as image: + assert image.size[0] == width + assert image.size[1] != height + assert image.format == "JPEG" + with Image.open(no_minSize_png_img) as image: + assert image.size[0] != width + assert image.size[1] == height + assert image.format == "PNG" assert _detect_color_image(grayscale_img, thumb_size=150) == "grayscale" + assert _detect_dominant_hexcolor(opacity_background_img) == background + with Image.open(online_no_upscale_img) as image1: + with Image.open(utils.STUB_IMAGE_PATH) as image2: + assert image1.size == image2.size def _detect_color_image(file, thumb_size=150, MSE_cutoff=22, adjust_color_bias=True): @@ -101,6 +129,15 @@ def _detect_color_image(file, thumb_size=150, MSE_cutoff=22, adjust_color_bias=T return "blackandwhite" +def _detect_dominant_hexcolor(file): + # https://stackoverflow.com/questions/3241929/python-find-dominant-most-common-color-in-an-image + pilimg = Image.open(file) + pilimg.convert("RGB") + pilimg.resize((1, 1), resample=0) + rgb_color = pilimg.getpixel((0, 0)) + return "{:02x}{:02x}{:02x}".format(*rgb_color) + + def test_server_fetchitem_notfound(plex): with pytest.raises(NotFound): plex.fetchItem(123456789)