style(extractor/applepodcasts): apply various coding conventions

pull/13864/head
Florent DELAHAYE 3 weeks ago
parent ebea243ea2
commit 6488e3f36e

@ -83,10 +83,9 @@ class ApplePodcastsIE(ApplePodcastsBaseIE):
} }
class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE): class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE):
# Apple podcast items are partially described (last episodes only) in the embedded json from main page therefore API calls are mandatory to get a full list # Apple podcast items are partially described in the embedded json from main page (last episodes only) therefore API calls are mandatory to get a full list
_VALID_URL = ApplePodcastsBaseIE._BASE_URL_REGEX + r'/id(?P<id>\d+)' _VALID_URL = ApplePodcastsBaseIE._BASE_URL_REGEX + r'/id(?P<id>\d+)'
_TESTS = [{ _TESTS = [{
'url': 'https://podcasts.apple.com/fr/podcast/id1691740320', 'url': 'https://podcasts.apple.com/fr/podcast/id1691740320',
'info_dict': { 'info_dict': {
@ -95,27 +94,27 @@ class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE):
'playlist_uploader': 'Guillaume Pley' 'playlist_uploader': 'Guillaume Pley'
}, },
'playlist_mincount': 400, 'playlist_mincount': 400,
'playlist_entries': [ 'playlist_entries': [
{ {
'id': '1000718966711', 'id': '1000718966711',
'title': 'MICHAEL YOUN : LES MOMENTS LES PLUS FOUS DE SES 25 ANS DE CARRIÈRE (MORNING LIVE, FATAL 2…)', 'title': 'MICHAEL YOUN : LES MOMENTS LES PLUS FOUS DE SES 25 ANS DE CARRIÈRE (MORNING LIVE, FATAL 2…)',
'uploader': 'Guillaume Pley', 'uploader': 'Guillaume Pley',
'description': "Retrouvez la boutique LEGEND ➡️: https://shop.legend-group.fr/\nMerci à Michaël Youn d'être venu nous voir sur LEGEND. Il est venu nous raconter plus de 25 ans de carrière, en tant quacteur, réalisateur et artiste. Il a été révélé par l'émission Morning Live sur M6, il nous a livré les anecdotes les plus folles quil a vécues à cette époque. Il est aussi venu nous raconter comment il a rencontré sa femme et ce que ses enfants ont changé dans sa vie.\nPour voir la bande annonce du film « Certains laiment chauve » déjà disponible au cinéma ➡️ https://www.allocine.fr/film/fichefilm_gen_cfilm=1000007354.html\nRetrouvez l'interview complète sur YouTube ➡ https://youtu.be/_TXBz1dSfBw\nPour toutes demandes de partenariats : legend@influxcrew.com\nRetrouvez-nous sur tous les réseaux LEGEND !\nFacebook : https://www.facebook.com/legendmediafr\nInstagram : https://www.instagram.com/legendmedia/\nTikTok : https://www.tiktok.com/@legend\nTwitter : https://twitter.com/legendmediafr\nSnapchat : https://t.snapchat.com/CgEvsbWV\n Hébergé par Acast. Visitez acast.com/privacy pour plus d'informations.", 'description': 'Retrouvez la boutique LEGEND ➡️: https://shop.legend-group.fr/\nMerci à Michaël Youn d\'être venu nous voir sur LEGEND. Il est venu nous raconter plus de 25 ans de carrière, en tant quacteur, réalisateur et artiste. Il a été révélé par l\'émission Morning Live sur M6, il nous a livré les anecdotes les plus folles quil a vécues à cette époque. Il est aussi venu nous raconter comment il a rencontré sa femme et ce que ses enfants ont changé dans sa vie.\nPour voir la bande annonce du film « Certains laiment chauve » déjà disponible au cinéma ➡️ https://www.allocine.fr/film/fichefilm_gen_cfilm=1000007354.html\nRetrouvez l\'interview complète sur YouTube ➡ https://youtu.be/_TXBz1dSfBw\nPour toutes demandes de partenariats : legend@influxcrew.com\nRetrouvez-nous sur tous les réseaux LEGEND !\nFacebook : https://www.facebook.com/legendmediafr\nInstagram : https://www.instagram.com/legendmedia/\nTikTok : https://www.tiktok.com/@legend\nTwitter : https://twitter.com/legendmediafr\nSnapchat : https://t.snapchat.com/CgEvsbWV\n Hébergé par Acast. Visitez acast.com/privacy pour plus d\'informations.',
'release_timestamp': 1753434168, 'release_timestamp': 1753434168,
'duration': 6856, 'duration': 6856,
'url': 'https://podcasts.apple.com/fr/podcast/michael-youn-les-moments-les-plus-fous-de-ses-25-ans/id1691740320?i=1000718966711' 'url': 'https://podcasts.apple.com/fr/podcast/michael-youn-les-moments-les-plus-fous-de-ses-25-ans/id1691740320?i=1000718966711'
}, },
{ {
'id': '1000718672235', 'id': '1000718672235',
'title': 'AMBULANCIER DU SAMU: SES INTERVENTIONS IMPROBABLES (SUIC*DES, FAUX MALADES, ENFANTS DR0GUÉS)', 'title': 'AMBULANCIER DU SAMU: SES INTERVENTIONS IMPROBABLES (SUIC*DES, FAUX MALADES, ENFANTS DR0GUÉS)',
'uploader': 'Guillaume Pley', 'uploader': 'Guillaume Pley',
'description': "Retrouvez la boutique LEGEND ➡️: https://shop.legend-group.fr/\nMerci à Thomas dêtre passé nous voir chez LEGEND ! Thomas est ambulancier et urgentiste au SMUR depuis 10 ans. Il est venu partager avec nous ses anecdotes les plus marquantes.\nIl a vécu des interventions difficiles, comme sur une scène de crime où une mère avait tué ses deux enfants, ou encore ce jour où il a pris en charge une victime coupée en deux par un hachoir.\nMais son métier, cest aussi des moments plus légers, parfois même drôles, comme cette fois où il a dû intervenir sur le tournage dun film X pour secourir des acteurs.\nPour toutes demandes de partenariats : legend@influxcrew.com\nRetrouvez-nous sur tous les réseaux LEGEND !\nRetrouvez l'interview complète sur YouTube ➡ https://youtu.be/ye5cVoc7hIc\nFacebook : https://www.facebook.com/legendmediafr\nInstagram : https://www.instagram.com/legendmedia/\nTikTok : https://www.tiktok.com/@legend\nTwitter : https://twitter.com/legendmediafr\nSnapchat : https://t.snapchat.com/CgEvsbWV\n Hébergé par Acast. Visitez acast.com/privacy pour plus d'informations.", 'description': 'Retrouvez la boutique LEGEND ➡️: https://shop.legend-group.fr/\nMerci à Thomas dêtre passé nous voir chez LEGEND ! Thomas est ambulancier et urgentiste au SMUR depuis 10 ans. Il est venu partager avec nous ses anecdotes les plus marquantes.\nIl a vécu des interventions difficiles, comme sur une scène de crime où une mère avait tué ses deux enfants, ou encore ce jour où il a pris en charge une victime coupée en deux par un hachoir.\nMais son métier, cest aussi des moments plus légers, parfois même drôles, comme cette fois où il a dû intervenir sur le tournage dun film X pour secourir des acteurs.\nPour toutes demandes de partenariats : legend@influxcrew.com\nRetrouvez-nous sur tous les réseaux LEGEND !\nRetrouvez l\'interview complète sur YouTube ➡ https://youtu.be/ye5cVoc7hIc\nFacebook : https://www.facebook.com/legendmediafr\nInstagram : https://www.instagram.com/legendmedia/\nTikTok : https://www.tiktok.com/@legend\nTwitter : https://twitter.com/legendmediafr\nSnapchat : https://t.snapchat.com/CgEvsbWV\n Hébergé par Acast. Visitez acast.com/privacy pour plus d\'informations.',
'release_timestamp': 1753272000, 'release_timestamp': 1753272000,
'duration': 4165, 'duration': 4165,
'url': 'https://podcasts.apple.com/fr/podcast/ambulancier-du-samu-ses-interventions-improbables-suic/id1691740320?i=1000718672235' 'url': 'https://podcasts.apple.com/fr/podcast/ambulancier-du-samu-ses-interventions-improbables-suic/id1691740320?i=1000718672235'
} }
] ]
}] }]
# Extract token (supposedly JWT) from javascript # Extract token (supposedly JWT) from javascript
# Note: javascript file number/names and token variable name may change # Note: javascript file number/names and token variable name may change
@ -125,7 +124,7 @@ class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE):
auth_token = None auth_token = None
for js_url in js_urls: for js_url in js_urls:
js_code = self._download_webpage(js_url, 'Generic authentification token', fatal=False, note=f'Scanning {js_url}') js_code = self._download_webpage(js_url, 'Generic authorization token', fatal=False, note=f'Scanning {js_url}')
if not js_code: if not js_code:
continue continue
match = re.search(r'const\s+Ml\s*=\s*"((?:eyJ)[^"]+)"', js_code) match = re.search(r'const\s+Ml\s*=\s*"((?:eyJ)[^"]+)"', js_code)
@ -139,11 +138,11 @@ class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE):
return auth_token return auth_token
# Call backend API pages and merge them as a single list # Call backend API pages and merge them as a single list
def _paginate_episodes(self, playlist_id, token): def _unpaginate_episodes(self, playlist_id, token):
base_url = 'https://amp-api.podcasts.apple.com/v1/catalog/fr/podcasts/' base_url = 'https://amp-api.podcasts.apple.com/v1/catalog/fr/podcasts/'
headers = { headers = {
'Authorization': f'Bearer {token}', 'Authorization': f'Bearer {token}',
'Origin': 'https://podcasts.apple.com' 'Origin': 'https://podcasts.apple.com'
} }
all_episodes = [] all_episodes = []
@ -151,10 +150,7 @@ class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE):
limit = 25 # Limit in use by website but other values seem to be accepted limit = 25 # Limit in use by website but other values seem to be accepted
while True: while True:
episodes_url = ( episodes_url = f'{base_url}{playlist_id}/episodes?l=fr-FR&offset={offset}&limit={limit}'
f'{base_url}{playlist_id}/episodes?'
f'l=fr-FR&offset={offset}&limit={limit}'
)
episodes_json = self._download_json(episodes_url, playlist_id, headers=headers, note=f'Downloading episodes offset {offset}') episodes_json = self._download_json(episodes_url, playlist_id, headers=headers, note=f'Downloading episodes offset {offset}')
all_episodes.extend(episodes_json.get('data', [])) all_episodes.extend(episodes_json.get('data', []))
if 'next' not in episodes_json: if 'next' not in episodes_json:
@ -174,38 +170,34 @@ class ApplePodcastsPlaylistIE(ApplePodcastsBaseIE):
(..., lambda _, v: v.get('contentType') == 'showHeaderRegular', 'items', 0), (..., lambda _, v: v.get('contentType') == 'showHeaderRegular', 'items', 0),
expected_type=dict, get_all=False) expected_type=dict, get_all=False)
token = self._extract_token(webpage)
episodes = self._paginate_episodes(playlist_id, token)
entries = [] entries = []
for e in episodes: for e in self._unpaginate_episodes(playlist_id, self._extract_token(webpage)):
episode_data = traverse_obj(e, { episode_data = traverse_obj(e, {
'id': ('id', {str}), 'id': ('id', {str}),
'title': ('attributes', 'name', {str}), 'title': ('attributes', 'name', {str}),
'uploader': ('attributes', 'artistName', {str}), 'uploader': ('attributes', 'artistName', {str}),
'description': ('attributes', 'description', 'standard', {str}), 'description': ('attributes', 'description', 'standard', {str}),
'url': ('attributes', 'url', {clean_podcast_url}), 'url': ('attributes', 'url', {clean_podcast_url}),
'release_timestamp': ('attributes', 'releaseDateTime', {parse_iso8601}), 'release_timestamp': ('attributes', 'releaseDateTime', {parse_iso8601}),
'duration': ('attributes', 'durationInMilliseconds', {lambda x: int(x) // 1000}), 'duration': ('attributes', 'durationInMilliseconds', {lambda x: int(x) // 1000}),
'thumbnail_template': ('artwork', 'url', {str}), 'thumbnail_template': ('artwork', 'url', {str}),
'thumb_width': ('artwork', 'width', {int}), 'thumb_width': ('artwork', 'width', {int}),
'thumb_height': ('artwork', 'height', {int}), 'thumb_height': ('artwork', 'height', {int}),
}) })
if not episode_data.get('url'): if not episode_data.get('url'):
continue continue
entries.append({ entries.append({
'_type': 'url', '_type': 'url',
'ie_key': 'ApplePodcasts', 'ie_key': 'ApplePodcasts',
**episode_data, **episode_data,
}) })
return self.playlist_result(entries, return self.playlist_result(entries,
playlist_id, playlist_id,
**traverse_obj(playlist_data, { **traverse_obj(playlist_data, {
'playlist_title': ('title', {str}), 'playlist_title': ('title', {str}),
'playlist_description': ('description', {str}), 'playlist_description': ('description', {str}),
'playlist_uploader': ('providerTitle', {str}), 'playlist_uploader': ('providerTitle', {str}),
}) }))
)

Loading…
Cancel
Save