From 5e905db0f5a4cc282ea1030cf98450d7a0a396ba Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Thu, 11 Jul 2024 23:35:10 +0200 Subject: [PATCH] [youtube] Support for playlists and channels in search results. --- platypush/plugins/youtube/__init__.py | 17 +++++++- platypush/schemas/piped.py | 63 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/platypush/plugins/youtube/__init__.py b/platypush/plugins/youtube/__init__.py index c397facfb7..6dcee9b9c0 100644 --- a/platypush/plugins/youtube/__init__.py +++ b/platypush/plugins/youtube/__init__.py @@ -108,6 +108,19 @@ class YoutubePlugin(Plugin): return id_or_url + @staticmethod + def _dump_item(item: dict) -> dict: + if item.get('type') == 'stream': + return dict(PipedVideoSchema().dump(item)) + + if item.get('type') == 'channel': + return dict(PipedChannelSchema().dump(item)) + + if item.get('type') == 'playlist': + return dict(PipedPlaylistSchema().dump(item)) + + return item + @action def search(self, query: str, **_) -> List[dict]: """ @@ -118,9 +131,9 @@ class YoutubePlugin(Plugin): """ self.logger.info('Searching YouTube for "%s"', query) rs = self._request('search', auth=False, params={'q': query, 'filter': 'all'}) - results = PipedVideoSchema(many=True).dump(rs.get("items", [])) or [] + results = [self._dump_item(item) for item in rs.get('items', [])] self.logger.info( - '%d YouTube video results for the search query "%s"', + '%d YouTube results for the search query "%s"', len(results), query, ) diff --git a/platypush/schemas/piped.py b/platypush/schemas/piped.py index 98c28bf09b..d1f4167525 100644 --- a/platypush/schemas/piped.py +++ b/platypush/schemas/piped.py @@ -19,6 +19,14 @@ class PipedVideoSchema(Schema): unknown = EXCLUDE + item_type = fields.Constant( + 'video', + metadata={ + 'description': 'Item type', + 'example': 'video', + }, + ) + url = fields.Url( required=True, metadata={ @@ -119,6 +127,14 @@ class PipedPlaylistSchema(Schema): unknown = EXCLUDE + item_type = fields.Constant( + 'playlist', + metadata={ + 'description': 'Item type', + 'example': 'playlist', + }, + ) + id = fields.UUID( required=True, metadata={ @@ -159,6 +175,45 @@ class PipedPlaylistSchema(Schema): }, ) + channel = fields.String( + attribute='uploaderName', + metadata={ + 'description': 'Channel name', + 'example': 'My Channel', + }, + ) + + channel_url = fields.Url( + attribute='uploaderUrl', + metadata={ + 'description': 'Channel URL', + 'example': 'https://youtube.com/channel/1234567890', + }, + ) + + channel_image = fields.Url( + attribute='uploaderAvatar', + metadata={ + 'description': 'Channel image URL', + 'example': 'https://i.ytimg.com/vi/1234567890/hqdefault.jpg', + }, + ) + + @pre_dump + def fill_urls(self, data: dict, **_): + for attr in ('url', 'uploaderUrl'): + if data.get(attr) and not data[attr].startswith('https://'): + data[attr] = f'https://youtube.com{data[attr]}' + + return data + + @pre_dump + def normalize_timestamps(self, data: dict, **_): + if data.get('uploaded') and isinstance(data['uploaded'], int): + data['uploaded'] = datetime.fromtimestamp(data["uploaded"] / 1000) + + return data + class PipedChannelSchema(Schema): """ @@ -180,6 +235,14 @@ class PipedChannelSchema(Schema): }, ) + item_type = fields.Constant( + 'channel', + metadata={ + 'description': 'Item type', + 'example': 'channel', + }, + ) + url = fields.String( required=True, metadata={