summaryrefslogtreecommitdiffstats
path: root/yt_dlp/extractor/iheart.py
blob: fb6f51e2ca8f56c68fb9f10af913473aa8d862c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from .common import InfoExtractor
from ..utils import (
    clean_html,
    clean_podcast_url,
    int_or_none,
    str_or_none,
)


class IHeartRadioBaseIE(InfoExtractor):
    def _call_api(self, path, video_id, fatal=True, query=None):
        return self._download_json(
            'https://api.iheart.com/api/v3/podcast/' + path,
            video_id, fatal=fatal, query=query)

    def _extract_episode(self, episode):
        return {
            'thumbnail': episode.get('imageUrl'),
            'description': clean_html(episode.get('description')),
            'timestamp': int_or_none(episode.get('startDate'), 1000),
            'duration': int_or_none(episode.get('duration')),
        }


class IHeartRadioIE(IHeartRadioBaseIE):
    IE_NAME = 'iheartradio'
    _VALID_URL = r'(?:https?://(?:www\.)?iheart\.com/podcast/[^/]+/episode/(?P<display_id>[^/?&#]+)-|iheartradio:)(?P<id>\d+)'
    _TEST = {
        'url': 'https://www.iheart.com/podcast/105-behind-the-bastards-29236323/episode/part-one-alexander-lukashenko-the-dictator-70346499/?embed=true',
        'md5': 'c8609c92c8688dcb69d8541042b8abca',
        'info_dict': {
            'id': '70346499',
            'ext': 'mp3',
            'title': 'Part One: Alexander Lukashenko: The Dictator of Belarus',
            'description': 'md5:96cc7297b3a5a9ebae28643801c96fae',
            'timestamp': 1597741200,
            'upload_date': '20200818',
        }
    }

    def _real_extract(self, url):
        episode_id = self._match_id(url)
        episode = self._call_api(
            'episodes/' + episode_id, episode_id)['episode']
        info = self._extract_episode(episode)
        info.update({
            'id': episode_id,
            'title': episode['title'],
            'url': clean_podcast_url(episode['mediaUrl']),
        })
        return info


class IHeartRadioPodcastIE(IHeartRadioBaseIE):
    IE_NAME = 'iheartradio:podcast'
    _VALID_URL = r'https?://(?:www\.)?iheart(?:podcastnetwork)?\.com/podcast/[^/?&#]+-(?P<id>\d+)/?(?:[?#&]|$)'
    _TESTS = [{
        'url': 'https://www.iheart.com/podcast/1119-it-could-happen-here-30717896/',
        'info_dict': {
            'id': '30717896',
            'title': 'It Could Happen Here',
            'description': 'md5:5842117412a967eb0b01f8088eb663e2',
        },
        'playlist_mincount': 11,
    }, {
        'url': 'https://www.iheartpodcastnetwork.com/podcast/105-stuff-you-should-know-26940277',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        podcast_id = self._match_id(url)
        path = 'podcasts/' + podcast_id
        episodes = self._call_api(
            path + '/episodes', podcast_id, query={'limit': 1000000000})['data']

        entries = []
        for episode in episodes:
            episode_id = str_or_none(episode.get('id'))
            if not episode_id:
                continue
            info = self._extract_episode(episode)
            info.update({
                '_type': 'url',
                'id': episode_id,
                'title': episode.get('title'),
                'url': 'iheartradio:' + episode_id,
                'ie_key': IHeartRadioIE.ie_key(),
            })
            entries.append(info)

        podcast = self._call_api(path, podcast_id, False) or {}

        return self.playlist_result(
            entries, podcast_id, podcast.get('title'), podcast.get('description'))