forked from platypush/platypush
[media.jellyfin] Added more metadata to the returned items.
This commit is contained in:
parent
cad864f220
commit
585c2f733f
2 changed files with 186 additions and 27 deletions
|
@ -1,52 +1,72 @@
|
||||||
from marshmallow import fields
|
from marshmallow import fields
|
||||||
from marshmallow.schema import Schema
|
from marshmallow.schema import Schema
|
||||||
|
|
||||||
|
from platypush.schemas import DateTime
|
||||||
|
|
||||||
|
|
||||||
class MediaCollectionSchema(Schema):
|
class MediaCollectionSchema(Schema):
|
||||||
id = fields.String(
|
id = fields.String(
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Collection ID',
|
'description': 'Collection ID',
|
||||||
)
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
name = fields.String(
|
name = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Collection name',
|
'description': 'Collection name',
|
||||||
)
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
type = fields.String(
|
type = fields.String(
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Collection type (movies, music, series etc.)',
|
'description': 'Collection type (movies, music, series etc.)',
|
||||||
)
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
image = fields.URL(
|
image = fields.URL(
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Collection image (URL)',
|
'description': 'Collection image (URL)',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
path = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Path to collection',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
created_at = DateTime(
|
||||||
|
metadata={
|
||||||
|
'description': 'Creation date',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MediaArtistSchema(Schema):
|
class MediaArtistSchema(Schema):
|
||||||
id = fields.String(
|
id = fields.String(
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Artist ID',
|
'description': 'Artist ID',
|
||||||
)
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
name = fields.String(
|
name = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Artist name',
|
'description': 'Artist name',
|
||||||
)
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
image = fields.URL(
|
image = fields.URL(
|
||||||
metadata=dict(
|
metadata={
|
||||||
description='Artist main image (URL)',
|
'description': 'Artist main image (URL)',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
created_at = DateTime(
|
||||||
|
metadata={
|
||||||
|
'description': 'Creation date',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +76,12 @@ class MediaItemSchema(Schema):
|
||||||
url = fields.URL()
|
url = fields.URL()
|
||||||
file = fields.String()
|
file = fields.String()
|
||||||
image = fields.URL()
|
image = fields.URL()
|
||||||
|
path = fields.String()
|
||||||
|
created_at = DateTime(
|
||||||
|
metadata={
|
||||||
|
'description': 'Creation date',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MediaVideoSchema(MediaItemSchema):
|
class MediaVideoSchema(MediaItemSchema):
|
||||||
|
@ -69,4 +95,3 @@ class MediaMovieSchema(MediaItemSchema):
|
||||||
|
|
||||||
class MediaEpisodeSchema(MediaItemSchema):
|
class MediaEpisodeSchema(MediaItemSchema):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
||||||
from marshmallow import Schema, fields, pre_dump, post_dump
|
from marshmallow import Schema, fields, pre_dump, post_dump
|
||||||
|
|
||||||
from platypush.context import get_plugin
|
from platypush.context import get_plugin
|
||||||
|
from platypush.schemas import DateTime
|
||||||
|
|
||||||
from . import MediaArtistSchema, MediaCollectionSchema, MediaVideoSchema
|
from . import MediaArtistSchema, MediaCollectionSchema, MediaVideoSchema
|
||||||
|
|
||||||
|
@ -14,6 +15,10 @@ class JellyfinSchema(Schema):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
if 'id' in self.fields:
|
if 'id' in self.fields:
|
||||||
self.fields['id'].attribute = 'Id'
|
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:
|
if 'name' in self.fields:
|
||||||
self.fields['name'].attribute = 'Name'
|
self.fields['name'].attribute = 'Name'
|
||||||
elif 'title' in self.fields:
|
elif 'title' in self.fields:
|
||||||
|
@ -21,7 +26,7 @@ class JellyfinSchema(Schema):
|
||||||
|
|
||||||
@post_dump
|
@post_dump
|
||||||
def gen_img_url(self, data: dict, **_) -> dict:
|
def gen_img_url(self, data: dict, **_) -> dict:
|
||||||
if 'image' in self.fields:
|
if 'image' in self.fields and data.get('id') and not data.get('image'):
|
||||||
plugin = get_plugin('media.jellyfin')
|
plugin = get_plugin('media.jellyfin')
|
||||||
assert plugin, 'The media.jellyfin plugin is not configured'
|
assert plugin, 'The media.jellyfin plugin is not configured'
|
||||||
data['image'] = (
|
data['image'] = (
|
||||||
|
@ -38,7 +43,7 @@ class JellyfinSchema(Schema):
|
||||||
|
|
||||||
video_format = None
|
video_format = None
|
||||||
containers_priority = ['mp4', 'mkv', 'm4a', 'mov', 'avi']
|
containers_priority = ['mp4', 'mkv', 'm4a', 'mov', 'avi']
|
||||||
available_containers = data.get('Container', '').split(',')
|
available_containers = set(data.get('Container', '').split(','))
|
||||||
for container in containers_priority:
|
for container in containers_priority:
|
||||||
if container in available_containers:
|
if container in available_containers:
|
||||||
video_format = container
|
video_format = container
|
||||||
|
@ -52,7 +57,7 @@ class JellyfinSchema(Schema):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
video_format = available_containers[0]
|
video_format = list(available_containers)[0]
|
||||||
|
|
||||||
plugin = get_plugin('media.jellyfin')
|
plugin = get_plugin('media.jellyfin')
|
||||||
assert plugin, 'The media.jellyfin plugin is not configured'
|
assert plugin, 'The media.jellyfin plugin is not configured'
|
||||||
|
@ -64,9 +69,93 @@ class JellyfinSchema(Schema):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@pre_dump
|
||||||
|
def _gen_audio_url(self, data, **_):
|
||||||
|
if data.get('MediaType') != 'Audio':
|
||||||
|
return data
|
||||||
|
|
||||||
class JellyfinArtistSchema(JellyfinSchema, MediaArtistSchema):
|
plugin = get_plugin('media.jellyfin')
|
||||||
pass
|
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}'
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@post_dump
|
||||||
|
def _normalize_duration(self, data: dict, **_) -> dict:
|
||||||
|
if data.get('duration'):
|
||||||
|
data['duration'] //= 1e7
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class JellyfinArtistSchema(JellyfinSchema, MediaArtistSchema, MediaCollectionSchema):
|
||||||
|
item_type = fields.Constant('artist')
|
||||||
|
collection_type = fields.Constant('music')
|
||||||
|
|
||||||
|
|
||||||
|
class JellyfinTrackSchema(JellyfinSchema):
|
||||||
|
item_type = fields.Constant('track')
|
||||||
|
id = fields.String(attribute='Id')
|
||||||
|
url = fields.URL()
|
||||||
|
duration = fields.Number(attribute='RunTimeTicks')
|
||||||
|
artist = fields.String()
|
||||||
|
album = fields.String(attribute='Album')
|
||||||
|
name = fields.String(attribute='Name')
|
||||||
|
track_number = fields.Number(attribute='IndexNumber')
|
||||||
|
disc_number = fields.Number(attribute='ParentIndexNumber')
|
||||||
|
year = fields.Number(attribute='ProductionYear')
|
||||||
|
image = fields.String()
|
||||||
|
created_at = DateTime(attribute='DateCreated')
|
||||||
|
|
||||||
|
@pre_dump
|
||||||
|
def _normalize_artist(self, data: dict, **_) -> dict:
|
||||||
|
artists = data.get('Artists', [])
|
||||||
|
if artists:
|
||||||
|
data['artist'] = ', '.join(artists)
|
||||||
|
return data
|
||||||
|
|
||||||
|
@post_dump
|
||||||
|
def _normalize_community_rating(self, data: dict, **_) -> dict:
|
||||||
|
if data.get('community_rating'):
|
||||||
|
data['community_rating'] *= 10
|
||||||
|
return data
|
||||||
|
|
||||||
|
@post_dump
|
||||||
|
def _normalize_duration(self, data: dict, **_) -> dict:
|
||||||
|
if data.get('duration'):
|
||||||
|
data['duration'] //= 1e7
|
||||||
|
return data
|
||||||
|
|
||||||
|
@post_dump
|
||||||
|
def _add_title(self, data: dict, **_) -> dict:
|
||||||
|
if not data.get('title'):
|
||||||
|
data['title'] = data.get('name')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class JellyfinAlbumSchema(JellyfinSchema, MediaCollectionSchema):
|
||||||
|
id = fields.String(attribute='Id')
|
||||||
|
item_type = fields.Constant('album')
|
||||||
|
collection_type = fields.Constant('music')
|
||||||
|
name = fields.String(attribute='Name')
|
||||||
|
artist = fields.Nested(JellyfinArtistSchema, attribute='AlbumArtist')
|
||||||
|
duration = fields.Number(attribute='RunTimeTicks')
|
||||||
|
year = fields.Number(attribute='ProductionYear')
|
||||||
|
|
||||||
|
@pre_dump
|
||||||
|
def _expand_artist(self, data: dict, **_) -> dict:
|
||||||
|
artists = data.get('AlbumArtists', [])
|
||||||
|
if not artists:
|
||||||
|
return data
|
||||||
|
|
||||||
|
data['AlbumArtist'] = {
|
||||||
|
'Id': artists[0].get('Id'),
|
||||||
|
'Name': artists[0].get('Name'),
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class JellyfinCollectionSchema(JellyfinSchema, MediaCollectionSchema):
|
class JellyfinCollectionSchema(JellyfinSchema, MediaCollectionSchema):
|
||||||
|
@ -74,11 +163,36 @@ class JellyfinCollectionSchema(JellyfinSchema, MediaCollectionSchema):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['type'].attribute = 'CollectionType'
|
self.fields['type'].attribute = 'CollectionType'
|
||||||
|
|
||||||
|
collection_type = fields.String(attribute='CollectionType')
|
||||||
|
image = fields.String()
|
||||||
|
created_at = DateTime(attribute='DateCreated')
|
||||||
|
|
||||||
|
|
||||||
class JellyfinVideoSchema(JellyfinSchema, MediaVideoSchema):
|
class JellyfinVideoSchema(JellyfinSchema, MediaVideoSchema):
|
||||||
|
path = fields.String(attribute='Path')
|
||||||
duration = fields.Number(attribute='RunTimeTicks')
|
duration = fields.Number(attribute='RunTimeTicks')
|
||||||
community_rating = fields.Number(attribute='CommunityRating')
|
community_rating = fields.Number(attribute='CommunityRating')
|
||||||
|
container = fields.String(
|
||||||
|
attribute='Container',
|
||||||
|
metadata={
|
||||||
|
'description': 'Available video containers',
|
||||||
|
'example': 'mp4',
|
||||||
|
},
|
||||||
|
)
|
||||||
critic_rating = fields.Number(attribute='CriticRating')
|
critic_rating = fields.Number(attribute='CriticRating')
|
||||||
|
created_at = DateTime(attribute='DateCreated')
|
||||||
|
imdb_url = fields.URL(
|
||||||
|
attribute='ExternalUrl',
|
||||||
|
metadata={
|
||||||
|
'description': 'IMDb URL',
|
||||||
|
'example': 'https://www.imdb.com/title/tt1234567/',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
overview = fields.String(attribute='Overview')
|
||||||
|
genres = fields.List(fields.String, attribute='Genres')
|
||||||
|
tags = fields.List(fields.String, attribute='Tags')
|
||||||
|
trailer_url = fields.URL(attribute='TrailerUrl')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -97,6 +211,26 @@ class JellyfinVideoSchema(JellyfinSchema, MediaVideoSchema):
|
||||||
data['duration'] //= 1e7
|
data['duration'] //= 1e7
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@pre_dump
|
||||||
|
def _extract_imdb_url(self, data: dict, **_) -> dict:
|
||||||
|
external_urls = data.get('ExternalUrls', [])
|
||||||
|
for url in external_urls:
|
||||||
|
if url.get('Name') == 'IMDb':
|
||||||
|
data['ExternalUrl'] = url.get('Url')
|
||||||
|
break
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@pre_dump
|
||||||
|
def _extract_trailer_url(self, data: dict, **_) -> dict:
|
||||||
|
trailers = data.get('RemoteTrailers', [])
|
||||||
|
for trailer in trailers:
|
||||||
|
if trailer.get('Type') == 'Trailer':
|
||||||
|
data['TrailerUrl'] = trailer.get('Url')
|
||||||
|
break
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class JellyfinMovieSchema(JellyfinVideoSchema):
|
class JellyfinMovieSchema(JellyfinVideoSchema):
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in a new issue