From 9716b1da35b1795844cea546f2f3f62438a4f1f0 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Mon, 14 Oct 2024 23:00:50 +0200 Subject: [PATCH] [media.jellyfin] Added support for photo items. --- platypush/plugins/media/jellyfin/__init__.py | 10 +-- platypush/schemas/media/jellyfin.py | 95 +++++++++++++++----- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/platypush/plugins/media/jellyfin/__init__.py b/platypush/plugins/media/jellyfin/__init__.py index 15e23fa99d..ca338f7a3b 100644 --- a/platypush/plugins/media/jellyfin/__init__.py +++ b/platypush/plugins/media/jellyfin/__init__.py @@ -10,6 +10,7 @@ from platypush.schemas.media.jellyfin import ( JellyfinCollectionSchema, JellyfinEpisodeSchema, JellyfinMovieSchema, + JellyfinPhotoSchema, JellyfinTrackSchema, JellyfinVideoSchema, ) @@ -153,29 +154,24 @@ class MediaJellyfinPlugin(Plugin): for result in search_results: if result['Type'] == 'Movie': result = JellyfinMovieSchema().dump(result) - result['type'] = 'movie' # type: ignore elif result['Type'] == 'Video': result = JellyfinVideoSchema().dump(result) - result['type'] = 'video' # type: ignore + elif result['Type'] == 'Photo': + result = JellyfinPhotoSchema().dump(result) elif result['Type'] == 'Episode': result = JellyfinEpisodeSchema().dump(result) - result['type'] = 'episode' # type: ignore elif result['Type'] == 'Audio': result = JellyfinTrackSchema().dump(result) - result['type'] = 'audio' # type: ignore elif result['Type'] == 'MusicArtist': result = JellyfinArtistSchema().dump(result) - result['type'] = 'artist' # type: ignore elif result['Type'] == 'MusicAlbum': result = JellyfinAlbumSchema().dump(result) - result['type'] = 'album' # type: ignore elif result['Type'] == 'Series': serialized_results += self._flatten_series_result(result) for r in serialized_results: r['type'] = 'episode' elif result.get('IsFolder'): result = JellyfinCollectionSchema().dump(result) - result['type'] = 'collection' # type: ignore if isinstance(result, dict) and result.get('type'): serialized_results.append(result) diff --git a/platypush/schemas/media/jellyfin.py b/platypush/schemas/media/jellyfin.py index eac5f363d4..99efd560ce 100644 --- a/platypush/schemas/media/jellyfin.py +++ b/platypush/schemas/media/jellyfin.py @@ -17,27 +17,28 @@ class JellyfinSchema(Schema): self.fields['id'].attribute = 'Id' if 'path' in self.fields: self.fields['path'].attribute = 'Path' - if 'created_at' in self.fields: - self.fields['created_at'].attribute = 'DateCreated' if 'name' in self.fields: self.fields['name'].attribute = 'Name' - elif 'title' in self.fields: + if 'title' in self.fields: self.fields['title'].attribute = 'Name' - @post_dump - def gen_img_url(self, data: dict, **_) -> dict: - if 'image' in self.fields and data.get('id') and not data.get('image'): - plugin = get_plugin('media.jellyfin') - assert plugin, 'The media.jellyfin plugin is not configured' - data['image'] = ( - plugin.server + f'/Items/{data["id"]}' # type: ignore - '/Images/Primary?fillHeight=333&fillWidth=222&quality=96' - ) + @property + def _plugin(self): + p = get_plugin('media.jellyfin') + assert p, 'The media.jellyfin plugin is not configured' + return p - return data + @property + def _server(self): + return self._plugin.server + + @property + def _api_key(self): + return self._plugin._api_key # pylint: disable=protected-access @pre_dump def _gen_video_url(self, data, **_): + data = data or {} if data.get('MediaType') != 'Video': return data @@ -59,30 +60,38 @@ class JellyfinSchema(Schema): video_format = list(available_containers)[0] - plugin = get_plugin('media.jellyfin') - assert plugin, 'The media.jellyfin plugin is not configured' data['url'] = ( - f'{plugin.server}/Videos/{data["Id"]}' + f'{self._server}/Videos/{data["Id"]}' f'/stream.{video_format}' - f'?Static=true&api_key={plugin._api_key}' + f'?Static=true&api_key={self._api_key}' ) return data @pre_dump def _gen_audio_url(self, data, **_): + data = data or {} if data.get('MediaType') != 'Audio': return data - plugin = get_plugin('media.jellyfin') - assert plugin, 'The media.jellyfin plugin is not configured' data['url'] = ( - f'{plugin.server}/Audio/{data["Id"]}' - f'/stream?Static=true&api_key={plugin._api_key}' + f'{self._server}/Audio/{data["Id"]}' + f'/stream?Static=true&api_key={self._api_key}' ) return data + @post_dump + def gen_img_url(self, data: dict, **_) -> dict: + data = data or {} + if 'image' in self.fields and data.get('id') and not data.get('image'): + data['image'] = ( + self._server + f'/Items/{data["id"]}' # type: ignore + '/Images/Primary?fillHeight=333&fillWidth=222&quality=96' + ) + + return data + @post_dump def _normalize_duration(self, data: dict, **_) -> dict: if data.get('duration'): @@ -91,11 +100,13 @@ class JellyfinSchema(Schema): class JellyfinArtistSchema(JellyfinSchema, MediaArtistSchema, MediaCollectionSchema): + type = fields.Constant('artist') item_type = fields.Constant('artist') collection_type = fields.Constant('music') class JellyfinTrackSchema(JellyfinSchema): + type = fields.Constant('audio') item_type = fields.Constant('track') id = fields.String(attribute='Id') url = fields.URL() @@ -138,6 +149,7 @@ class JellyfinTrackSchema(JellyfinSchema): class JellyfinAlbumSchema(JellyfinSchema, MediaCollectionSchema): id = fields.String(attribute='Id') + type = fields.Constant('album') item_type = fields.Constant('album') collection_type = fields.Constant('music') name = fields.String(attribute='Name') @@ -163,12 +175,16 @@ class JellyfinCollectionSchema(JellyfinSchema, MediaCollectionSchema): super().__init__(*args, **kwargs) self.fields['type'].attribute = 'CollectionType' + type = fields.Constant('collection') + item_type = fields.Constant('collection') collection_type = fields.String(attribute='CollectionType') image = fields.String() created_at = DateTime(attribute='DateCreated') class JellyfinVideoSchema(JellyfinSchema, MediaVideoSchema): + type = fields.Constant('video') + item_type = fields.Constant('video') path = fields.String(attribute='Path') duration = fields.Number(attribute='RunTimeTicks') community_rating = fields.Number(attribute='CommunityRating') @@ -233,10 +249,14 @@ class JellyfinVideoSchema(JellyfinSchema, MediaVideoSchema): class JellyfinMovieSchema(JellyfinVideoSchema): - pass + type = fields.Constant('movie') + item_type = fields.Constant('movie') class JellyfinEpisodeSchema(JellyfinVideoSchema): + type = fields.Constant('episode') + item_type = fields.Constant('episode') + @pre_dump def _normalize_episode_name(self, data: dict, **_) -> dict: prefix = '' @@ -254,3 +274,34 @@ class JellyfinEpisodeSchema(JellyfinVideoSchema): data['Name'] = prefix + data.get('Name', '') return data + + +class JellyfinPhotoSchema(JellyfinSchema): + id = fields.String(attribute='Id') + name = fields.String(attribute='Name') + url = fields.URL() + type = fields.Constant('photo') + item_type = fields.Constant('photo') + path = fields.String(attribute='Path') + created_at = DateTime(attribute='PremiereDate') + width = fields.Number(attribute='Width') + height = fields.Number(attribute='Height') + camera_make = fields.String(attribute='CameraMake') + camera_model = fields.String(attribute='CameraModel') + software = fields.String(attribute='Software') + exposure_time = fields.Float(attribute='ExposureTime') + focal_length = fields.Float(attribute='FocalLength') + image_orientation = fields.String(attribute='ImageOrientation') + aperture = fields.Float(attribute='Aperture') + iso = fields.Number(attribute='IsoSpeedRating') + + @pre_dump + def _gen_photo_url(self, data, **_): + data = data or {} + base_url = f'{self._server}/Items/{data["Id"]}' + data['preview_url'] = ( + f'{base_url}/Images/Primary?api_key={self._api_key}' + f'&fillHeight=489&fillWidth=367&quality=96' + ) + data['url'] = f'{base_url}/Download?api_key={self._api_key}' + return data