code:
import re

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import ExtractorError, int_or_none, urlencode_postdata

class CuriosityStreamBaseIE(InfoExtractor):
    _NETRC_MACHINE = 'curiositystream'
    _auth_token = None

    def _handle_errors(self, result):
        error = result.get('error', {}).get('message')
        if error:
            if isinstance(error, dict):
                error = ', '.join(error.values())
            raise ExtractorError(
                '%s said: %s' % (self.IE_NAME, error), expected=True)

    def _call_api(self, path, video_id, query=None):
        headers = {}
        if not self._auth_token:
            auth_cookie = self._get_cookies('https://curiositystream.com').get('auth_token')
            if auth_cookie:
                self.write_debug('Obtained auth_token cookie')
                self._auth_token = auth_cookie.value
        if self._auth_token:
            headers['X-Auth-Token'] = self._auth_token
        result = self._download_json(
            self._API_BASE_URL + path, video_id, headers=headers, query=query)
        self._handle_errors(result)
        return result['data']

    def _perform_login(self, username, password):
        result = self._download_json(
            'https://api.curiositystream.com/v1/login', None,
            note='Logging in', data=urlencode_postdata({
                'email': username,
                'password': password,
            }))
        self._handle_errors(result)
        CuriosityStreamBaseIE._auth_token = result['message']['auth_token']

class CuriosityStreamIE(CuriosityStreamBaseIE):
    IE_NAME = 'curiositystream'
    _VALID_URL = r'https?://(?:app\.)?curiositystream\.com/video/(?P<id>\d+)'
    _TESTS = [{
        'url': 'http://app.curiositystream.com/video/2',
        'info_dict': {
            'id': '2',
            'ext': 'mp4',
            'title': 'How Did You Develop The Internet?',
            'description': 'Vint Cerf, Google\'s Chief Internet Evangelist, describes how he and Bob Kahn created the internet.',
            'channel': 'Curiosity Stream',
            'categories': ['Technology', 'Interview'],
            'average_rating': 96.79,
            'series_id': '2',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }]

    _API_BASE_URL = 'https://api.curiositystream.com/v1/media/'

    def _real_extract(self, url):
        video_id = self._match_id(url)

        formats = []
        for encoding_format in ('m3u8', 'mpd'):
            media = self._call_api(video_id, video_id, query={
                'encodingsNew': 'true',
                'encodingsFormat': encoding_format,
            })
            for encoding in media.get('encodings', []):
                playlist_url = encoding.get('master_playlist_url')
                if encoding_format == 'm3u8':
                    # use `m3u8` entry_protocol until EXT-X-MAP is properly supported by `m3u8_native` entry_protocol
                    formats.extend(self._extract_m3u8_formats(
                        playlist_url, video_id, 'mp4',
                        m3u8_id='hls', fatal=False))
                elif encoding_format == 'mpd':
                    formats.extend(self._extract_mpd_formats(
                        playlist_url, video_id, mpd_id='dash', fatal=False))
                encoding_url = encoding.get('url')
                file_url = encoding.get('file_url')
                if not encoding_url and not file_url:
                    continue
                f = {
                    'width': int_or_none(encoding.get('width')),
                    'height': int_or_none(encoding.get('height')),
                    'vbr': int_or_none(encoding.get('video_bitrate')),
                    'abr': int_or_none(encoding.get('audio_bitrate')),
                    'filesize': int_or_none(encoding.get('size_in_bytes')),
                    'vcodec': encoding.get('video_codec'),
                    'acodec': encoding.get('audio_codec'),
                    'container': encoding.get('container_type'),
                }
                for f_url in (encoding_url, file_url):
                    if not f_url:
                        continue
                    fmt = f.copy()
                    rtmp = re.search(r'^(?P<url>rtmpe?://(?P<host>[^/]+)/(?P<app>.+))/(?P<playpath>mp[34]:.+)$', f_url)
                    if rtmp:
                        fmt.update({
                            'url': rtmp.group('url'),
                            'play_path': rtmp.group('playpath'),
                            'app': rtmp.group('app'),
                            'ext': 'flv',
                            'format_id': 'rtmp',
                        })
                    else:
                        fmt.update({
                            'url': f_url,
                            'format_id': 'http',
                        })
                    formats.append(fmt)
        self._sort_formats(formats)

        title = media['title']

        subtitles = {}
        for closed_caption in media.get('closed_captions', []):
            sub_url = closed_caption.get('file')
            if not sub_url:
                continue
            lang = closed_caption.get('code') or closed_caption.get('language') or 'en'
            subtitles.setdefault(lang, []).append({
                'url': sub_url,
            })

        return {
            'id': video_id,
            'formats': formats,
            'title': title,
            'description': media.get('description'),
            'thumbnail': media.get('image_large') or media.get('image_medium') or media.get('image_small'),
            'duration': int_or_none(media.get('duration')),
            'tags': media.get('tags'),
            'subtitles': subtitles,
            'channel': media.get('producer'),
            'categories': [media.get('primary_category'), media.get('type')],
            'average_rating': media.get('rating_percentage'),
            'series_id': str(media.get('collection_id') or '') or None,
        }

class CuriosityStreamCollectionBaseIE(CuriosityStreamBaseIE):

    def _real_extract(self, url):
        collection_id = self._match_id(url)
        collection = self._call_api(collection_id, collection_id)
        entries = []
        for media in collection.get('media', []):
            media_id = compat_str(media.get('id'))
            media_type, ie = ('series', CuriosityStreamSeriesIE) if media.get('is_collection') else ('video', CuriosityStreamIE)
            entries.append(self.url_result(
                'https://curiositystream.com/%s/%s' % (media_type, media_id),
                ie=ie.ie_key(), video_id=media_id))
        return self.playlist_result(
            entries, collection_id,
            collection.get('title'), collection.get('description'))

class CuriosityStreamCollectionsIE(CuriosityStreamCollectionBaseIE):
    IE_NAME = 'curiositystream:collections'
    _VALID_URL = r'https?://(?:app\.)?curiositystream\.com/collections/(?P<id>\d+)'
    _API_BASE_URL = 'https://api.curiositystream.com/v2/collections/'
    _TESTS = [{
        'url': 'https://curiositystream.com/collections/86',
        'info_dict': {
            'id': '86',
            'title': 'Staff Picks',
            'description': 'Wondering where to start? Here are a few of our favorite series and films... from our couch to yours.',
        },
        'playlist_mincount': 7,
    }, {
        'url': 'https://curiositystream.com/collections/36',
        'only_matching': True,
    }]

class CuriosityStreamSeriesIE(CuriosityStreamCollectionBaseIE):
    IE_NAME = 'curiositystream:series'
    _VALID_URL = r'https?://(?:app\.)?curiositystream\.com/(?:series|collection)/(?P<id>\d+)'
    _API_BASE_URL = 'https://api.curiositystream.com/v2/series/'
    _TESTS = [{
        'url': 'https://curiositystream.com/series/2',
        'info_dict': {
            'id': '2',
            'title': 'Curious Minds: The Internet',
            'description': 'How is the internet shaping our lives in the 21st Century?',
        },
        'playlist_mincount': 16,
    }, {
        'url': 'https://curiositystream.com/collection/2',
        'only_matching': True,
    }]


code:
from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    int_or_none,
    parse_age_limit,
    parse_iso8601,
    smuggle_url,
    str_or_none,
)

class CWTVIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?cw(?:tv(?:pr)?|seed)\.com/(?:shows/)?(?:[^/]+/)+[^?]*\?.*\b(?:play|watch)=(?P<id>[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})'
    _TESTS = [{
        'url': 'http://cwtv.com/shows/arrow/legends-of-yesterday/?play=6b15e985-9345-4f60-baf8-56e96be57c63',
        'info_dict': {
            'id': '6b15e985-9345-4f60-baf8-56e96be57c63',
            'ext': 'mp4',
            'title': 'Legends of Yesterday',
            'description': 'Oliver and Barry Allen take Kendra Saunders and Carter Hall to a remote location to keep them hidden from Vandal Savage while they figure out how to defeat him.',
            'duration': 2665,
            'series': 'Arrow',
            'season_number': 4,
            'season': '4',
            'episode_number': 8,
            'upload_date': '20151203',
            'timestamp': 1449122100,
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
        'skip': 'redirect to http://cwtv.com/shows/arrow/',
    }, {
        'url': 'http://www.cwseed.com/shows/whose-line-is-it-anyway/jeff-davis-4/?play=24282b12-ead2-42f2-95ad-26770c2c6088',
        'info_dict': {
            'id': '24282b12-ead2-42f2-95ad-26770c2c6088',
            'ext': 'mp4',
            'title': 'Jeff Davis 4',
            'description': 'Jeff Davis is back to make you laugh.',
            'duration': 1263,
            'series': 'Whose Line Is It Anyway?',
            'season_number': 11,
            'episode_number': 20,
            'upload_date': '20151006',
            'timestamp': 1444107300,
            'age_limit': 14,
            'uploader': 'CWTV',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'http://cwtv.com/thecw/chroniclesofcisco/?play=8adebe35-f447-465f-ab52-e863506ff6d6',
        'only_matching': True,
    }, {
        'url': 'http://cwtvpr.com/the-cw/video?watch=9eee3f60-ef4e-440b-b3b2-49428ac9c54e',
        'only_matching': True,
    }, {
        'url': 'http://cwtv.com/shows/arrow/legends-of-yesterday/?watch=6b15e985-9345-4f60-baf8-56e96be57c63',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        data = self._download_json(
            'http://images.cwtv.com/feed/mobileapp/video-meta/apiversion_8/guid_' + video_id,
            video_id)
        if data.get('result') != 'ok':
            raise ExtractorError(data['msg'], expected=True)
        video_data = data['video']
        title = video_data['title']
        mpx_url = video_data.get('mpx_url') or 'http://link.theplatform.com/s/cwtv/media/guid/2703454149/%s?formats=M3U' % video_id

        season = str_or_none(video_data.get('season'))
        episode = str_or_none(video_data.get('episode'))
        if episode and season:
            episode = episode[len(season):]

        return {
            '_type': 'url_transparent',
            'id': video_id,
            'title': title,
            'url': smuggle_url(mpx_url, {'force_smil_url': True}),
            'description': video_data.get('description_long'),
            'duration': int_or_none(video_data.get('duration_secs')),
            'series': video_data.get('series_name'),
            'season_number': int_or_none(season),
            'episode_number': int_or_none(episode),
            'timestamp': parse_iso8601(video_data.get('start_time')),
            'age_limit': parse_age_limit(video_data.get('rating')),
            'ie_key': 'ThePlatform',
            'thumbnail': video_data.get('large_thumbnail')
        }

repo_name: Lesmiscore/ytdl


code:
from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    smuggle_url,
    str_or_none,
    traverse_obj,
    urlencode_postdata,
)

class CybraryBaseIE(InfoExtractor):
    _API_KEY = 'AIzaSyCX9ru6j70PX2My1Eq6Q1zoMAhuTdXlzSw'
    _ENDPOINTS = {
        'course': 'https://app.cybrary.it/courses/api/catalog/browse/course/{}',
        'course_enrollment': 'https://app.cybrary.it/courses/api/catalog/{}/enrollment',
        'enrollment': 'https://app.cybrary.it/courses/api/enrollment/{}',
        'launch': 'https://app.cybrary.it/courses/api/catalog/{}/launch',
        'vimeo_oembed': 'https://vimeo.com/api/oembed.json?url=https://vimeo.com/{}',
    }
    _NETRC_MACHINE = 'cybrary'
    _TOKEN = None

    def _perform_login(self, username, password):
        CybraryBaseIE._TOKEN = self._download_json(
            f'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={self._API_KEY}',
            None, data=urlencode_postdata({'email': username, 'password': password, 'returnSecureToken': True}),
            note='Logging in')['idToken']

    def _real_initialize(self):
        if not self._TOKEN:
            self.raise_login_required(method='password')

    def _call_api(self, endpoint, item_id):
        return self._download_json(
            self._ENDPOINTS[endpoint].format(item_id), item_id,
            note=f'Downloading {endpoint} JSON metadata',
            headers={'Authorization': f'Bearer {self._TOKEN}'})

    def _get_vimeo_id(self, activity_id):
        launch_api = self._call_api('launch', activity_id)

        if launch_api.get('url'):
            return self._search_regex(r'https?://player\.vimeo\.com/video/(?P<vimeo_id>[0-9]+)', launch_api['url'], 'vimeo_id')
        return traverse_obj(launch_api, ('vendor_data', 'content', ..., 'videoId'), get_all=False)

class CybraryIE(CybraryBaseIE):
    _VALID_URL = r'https?://app.cybrary.it/immersive/(?P<enrollment>[0-9]+)/activity/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://app.cybrary.it/immersive/12487950/activity/63102',
        'md5': '9ae12d37e555cb2ed554223a71a701d0',
        'info_dict': {
            'id': '646609770',
            'ext': 'mp4',
            'title': 'Getting Started',
            'thumbnail': 'https://i.vimeocdn.com/video/1301817996-76a268f0c56cff18a5cecbbdc44131eb9dda0c80eb0b3a036_1280',
            'series_id': '63111',
            'uploader_url': 'https://vimeo.com/user30867300',
            'duration': 88,
            'uploader_id': 'user30867300',
            'series': 'Cybrary Orientation',
            'uploader': 'Cybrary',
            'chapter': 'Cybrary Orientation Series',
            'chapter_id': '63110'
        },
        'expected_warnings': ['No authenticators for vimeo']
    }, {
        'url': 'https://app.cybrary.it/immersive/12747143/activity/52686',
        'md5': '62f26547dccc59c44363e2a13d4ad08d',
        'info_dict': {
            'id': '445638073',
            'ext': 'mp4',
            'title': 'Azure Virtual Network IP Addressing',
            'thumbnail': 'https://i.vimeocdn.com/video/936667051-1647ace66c627d4a2382185e0dae8deb830309bfddd53f8b2367b2f91e92ed0e-d_1280',
            'series_id': '52733',
            'uploader_url': 'https://vimeo.com/user30867300',
            'duration': 426,
            'uploader_id': 'user30867300',
            'series': 'AZ-500: Microsoft Azure Security Technologies',
            'uploader': 'Cybrary',
            'chapter': 'Implement Network Security',
            'chapter_id': '52693'
        },
        'expected_warnings': ['No authenticators for vimeo']
    }]

    def _real_extract(self, url):
        activity_id, enrollment_id = self._match_valid_url(url).group('id', 'enrollment')
        course = self._call_api('enrollment', enrollment_id)['content']
        activity = traverse_obj(course, ('learning_modules', ..., 'activities', lambda _, v: int(activity_id) == v['id']), get_all=False)

        if activity.get('type') not in ['Video Activity', 'Lesson Activity']:
            raise ExtractorError('The activity is not a video', expected=True)

        module = next((m for m in course.get('learning_modules') or []
                      if int(activity_id) in traverse_obj(m, ('activities', ..., 'id') or [])), None)

        vimeo_id = self._get_vimeo_id(activity_id)

        return {
            '_type': 'url_transparent',
            'series': traverse_obj(course, ('content_description', 'title')),
            'series_id': str_or_none(traverse_obj(course, ('content_description', 'id'))),
            'id': vimeo_id,
            'chapter': module.get('title'),
            'chapter_id': str_or_none(module.get('id')),
            'title': activity.get('title'),
            'url': smuggle_url(f'https://player.vimeo.com/video/{vimeo_id}', {'http_headers': {'Referer': 'https://api.cybrary.it'}})
        }

class CybraryCourseIE(CybraryBaseIE):
    _VALID_URL = r'https://app.cybrary.it/browse/course/(?P<id>[\w-]+)/?(?:$|[#?])'
    _TESTS = [{
        'url': 'https://app.cybrary.it/browse/course/az-500-microsoft-azure-security-technologies',
        'info_dict': {
            'id': 898,
            'title': 'AZ-500: Microsoft Azure Security Technologies',
            'description': 'md5:69549d379c0fc1dec92926d4e8b6fbd4'
        },
        'playlist_count': 59
    }, {
        'url': 'https://app.cybrary.it/browse/course/cybrary-orientation',
        'info_dict': {
            'id': 1245,
            'title': 'Cybrary Orientation',
            'description': 'md5:9e69ff66b32fe78744e0ad4babe2e88e'
        },
        'playlist_count': 4
    }]

    def _real_extract(self, url):
        course_id = self._match_id(url)
        course = self._call_api('course', course_id)
        enrollment_info = self._call_api('course_enrollment', course['id'])

        entries = [self.url_result(
            f'https://app.cybrary.it/immersive/{enrollment_info["id"]}/activity/{activity["id"]}')
            for activity in traverse_obj(course, ('content_item', 'learning_modules', ..., 'activities', ...))]

        return self.playlist_result(
            entries,
            traverse_obj(course, ('content_item', 'id'), expected_type=str_or_none),
            course.get('title'), course.get('short_description'))


code:
from .common import InfoExtractor
from ..compat import compat_b64decode
from ..utils import (
    int_or_none,
    js_to_json,
    parse_count,
    parse_duration,
    traverse_obj,
    try_get,
    unified_timestamp,
)

class DaftsexIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?daftsex\.com/watch/(?P<id>-?\d+_\d+)'
    _TESTS = [{
        'url': 'https://daftsex.com/watch/-35370899_456246186',
        'md5': 'd95135e6cea2d905bea20dbe82cda64a',
        'info_dict': {
            'id': '-35370899_456246186',
            'ext': 'mp4',
            'title': 'just relaxing',
            'description': 'just relaxing - Watch video Watch video in high quality',
            'upload_date': '20201113',
            'timestamp': 1605261911,
            'thumbnail': r're:https://[^/]+/impf/-43BuMDIawmBGr3GLcZ93CYwWf2PBv_tVWoS1A/dnu41DnARU4\.jpg\?size=800x450&quality=96&keep_aspect_ratio=1&background=000000&sign=6af2c26ff4a45e55334189301c867384&type=video_thumb',
        },
    }, {
        'url': 'https://daftsex.com/watch/-156601359_456242791',
        'info_dict': {
            'id': '-156601359_456242791',
            'ext': 'mp4',
            'title': 'Skye Blue - Dinner And A Show',
            'description': 'Skye Blue - Dinner And A Show - Watch video Watch video in high quality',
            'upload_date': '20200916',
            'timestamp': 1600250735,
            'thumbnail': 'https://psv153-1.crazycloud.ru/videos/-156601359/456242791/thumb.jpg?extra=i3D32KaBbBFf9TqDRMAVmQ',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        title = self._html_search_meta('name', webpage, 'title')
        timestamp = unified_timestamp(self._html_search_meta('uploadDate', webpage, 'Upload Date', default=None))
        description = self._html_search_meta('description', webpage, 'Description', default=None)

        duration = parse_duration(self._search_regex(
            r'Duration: ((?:[0-9]{2}:){0,2}[0-9]{2})',
            webpage, 'duration', fatal=False))
        views = parse_count(self._search_regex(
            r'Views: ([0-9 ]+)',
            webpage, 'views', fatal=False))

        player_hash = self._search_regex(
            r'DaxabPlayer\.Init\({[\s\S]*hash:\s*"([0-9a-zA-Z_\-]+)"[\s\S]*}',
            webpage, 'player hash')
        player_color = self._search_regex(
            r'DaxabPlayer\.Init\({[\s\S]*color:\s*"([0-9a-z]+)"[\s\S]*}',
            webpage, 'player color', fatal=False) or ''

        embed_page = self._download_webpage(
            'https://daxab.com/player/%s?color=%s' % (player_hash, player_color),
            video_id, headers={'Referer': url})
        video_params = self._parse_json(
            self._search_regex(
                r'window\.globParams\s*=\s*({[\S\s]+})\s*;\s*<\/script>',
                embed_page, 'video parameters'),
            video_id, transform_source=js_to_json)

        server_domain = 'https://%s' % compat_b64decode(video_params['server'][::-1]).decode('utf-8')

        cdn_files = traverse_obj(video_params, ('video', 'cdn_files')) or {}
        if cdn_files:
            formats = []
            for format_id, format_data in cdn_files.items():
                ext, height = format_id.split('_')
                formats.append({
                    'format_id': format_id,
                    'url': f'{server_domain}/videos/{video_id.replace("_", "/")}/{height}.mp4?extra={format_data.split(".")[-1]}',
                    'height': int_or_none(height),
                    'ext': ext,
                })
            self._sort_formats(formats)

            return {
                'id': video_id,
                'title': title,
                'formats': formats,
                'description': description,
                'duration': duration,
                'thumbnail': try_get(video_params, lambda vi: 'https:' + compat_b64decode(vi['video']['thumb']).decode('utf-8')),
                'timestamp': timestamp,
                'view_count': views,
                'age_limit': 18,
            }

        item = self._download_json(
            f'{server_domain}/method/video.get/{video_id}', video_id,
            headers={'Referer': url}, query={
                'token': video_params['video']['access_token'],
                'videos': video_id,
                'ckey': video_params['c_key'],
                'credentials': video_params['video']['credentials'],
            })['response']['items'][0]

        formats = []
        for f_id, f_url in item.get('files', {}).items():
            if f_id == 'external':
                return self.url_result(f_url)
            ext, height = f_id.split('_')
            height_extra_key = traverse_obj(video_params, ('video', 'partial', 'quality', height))
            if height_extra_key:
                formats.append({
                    'format_id': f'{height}p',
                    'url': f'{server_domain}/{f_url[8:]}&videos={video_id}&extra_key={height_extra_key}',
                    'height': int_or_none(height),
                    'ext': ext,
                })
        self._sort_formats(formats)

        thumbnails = []
        for k, v in item.items():
            if k.startswith('photo_') and v:
                width = k.replace('photo_', '')
                thumbnails.append({
                    'id': width,
                    'url': v,
                    'width': int_or_none(width),
                })

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'comment_count': int_or_none(item.get('comments')),
            'description': description,
            'duration': duration,
            'thumbnails': thumbnails,
            'timestamp': timestamp,
            'view_count': views,
            'age_limit': 18,
        }


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    int_or_none,
    determine_protocol,
    try_get,
    unescapeHTML,
)

class DailyMailIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?dailymail\.co\.uk/(?:video/[^/]+/video-|embed/video/)(?P<id>[0-9]+)'
    _EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:www\.)?dailymail\.co\.uk/embed/video/\d+\.html)']
    _TESTS = [{
        'url': 'http://www.dailymail.co.uk/video/tvshowbiz/video-1295863/The-Mountain-appears-sparkling-water-ad-Heavy-Bubbles.html',
        'md5': 'f6129624562251f628296c3a9ffde124',
        'info_dict': {
            'id': '1295863',
            'ext': 'mp4',
            'title': 'The Mountain appears in sparkling water ad for \'Heavy Bubbles\'',
            'description': 'md5:a93d74b6da172dd5dc4d973e0b766a84',
        }
    }, {
        'url': 'http://www.dailymail.co.uk/embed/video/1295863.html',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        video_data = self._parse_json(self._search_regex(
            r"data-opts='({.+?})'", webpage, 'video data'), video_id)
        title = unescapeHTML(video_data['title'])

        sources_url = (try_get(
            video_data,
            (lambda x: x['plugins']['sources']['url'],
             lambda x: x['sources']['url']), compat_str)
            or 'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id)

        video_sources = self._download_json(sources_url, video_id)
        body = video_sources.get('body')
        if body:
            video_sources = body

        formats = []
        for rendition in video_sources['renditions']:
            rendition_url = rendition.get('url')
            if not rendition_url:
                continue
            tbr = int_or_none(rendition.get('encodingRate'), 1000)
            container = rendition.get('videoContainer')
            is_hls = container == 'M2TS'
            protocol = 'm3u8_native' if is_hls else determine_protocol({'url': rendition_url})
            formats.append({
                'format_id': ('hls' if is_hls else protocol) + ('-%d' % tbr if tbr else ''),
                'url': rendition_url,
                'width': int_or_none(rendition.get('frameWidth')),
                'height': int_or_none(rendition.get('frameHeight')),
                'tbr': tbr,
                'vcodec': rendition.get('videoCodec'),
                'container': container,
                'protocol': protocol,
                'ext': 'mp4' if is_hls else None,
            })
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'description': unescapeHTML(video_data.get('descr')),
            'thumbnail': video_data.get('poster') or video_data.get('thumbnail'),
            'formats': formats,
        }


code:
import functools
import json
import re

from .common import InfoExtractor
from ..compat import compat_HTTPError
from ..utils import (
    ExtractorError,
    OnDemandPagedList,
    age_restricted,
    clean_html,
    int_or_none,
    traverse_obj,
    try_get,
    unescapeHTML,
    unsmuggle_url,
    urlencode_postdata,
)

class DailymotionBaseInfoExtractor(InfoExtractor):
    _FAMILY_FILTER = None
    _HEADERS = {
        'Content-Type': 'application/json',
        'Origin': 'https://www.dailymotion.com',
    }
    _NETRC_MACHINE = 'dailymotion'

    def _get_dailymotion_cookies(self):
        return self._get_cookies('https://www.dailymotion.com/')

    @staticmethod
    def _get_cookie_value(cookies, name):
        cookie = cookies.get(name)
        if cookie:
            return cookie.value

    def _set_dailymotion_cookie(self, name, value):
        self._set_cookie('www.dailymotion.com', name, value)

    def _real_initialize(self):
        cookies = self._get_dailymotion_cookies()
        ff = self._get_cookie_value(cookies, 'ff')
        self._FAMILY_FILTER = ff == 'on' if ff else age_restricted(18, self.get_param('age_limit'))
        self._set_dailymotion_cookie('ff', 'on' if self._FAMILY_FILTER else 'off')

    def _call_api(self, object_type, xid, object_fields, note, filter_extra=None):
        if not self._HEADERS.get('Authorization'):
            cookies = self._get_dailymotion_cookies()
            token = self._get_cookie_value(cookies, 'access_token') or self._get_cookie_value(cookies, 'client_token')
            if not token:
                data = {
                    'client_id': 'f1a362d288c1b98099c7',
                    'client_secret': 'eea605b96e01c796ff369935357eca920c5da4c5',
                }
                username, password = self._get_login_info()
                if username:
                    data.update({
                        'grant_type': 'password',
                        'password': password,
                        'username': username,
                    })
                else:
                    data['grant_type'] = 'client_credentials'
                try:
                    token = self._download_json(
                        'https://graphql.api.dailymotion.com/oauth/token',
                        None, 'Downloading Access Token',
                        data=urlencode_postdata(data))['access_token']
                except ExtractorError as e:
                    if isinstance(e.cause, compat_HTTPError) and e.cause.code == 400:
                        raise ExtractorError(self._parse_json(
                            e.cause.read().decode(), xid)['error_description'], expected=True)
                    raise
                self._set_dailymotion_cookie('access_token' if username else 'client_token', token)
            self._HEADERS['Authorization'] = 'Bearer ' + token

        resp = self._download_json(
            'https://graphql.api.dailymotion.com/', xid, note, data=json.dumps({
                'query': '''{
  %s(xid: "%s"%s) {
    %s
  }
}''' % (object_type, xid, ', ' + filter_extra if filter_extra else '', object_fields),
            }).encode(), headers=self._HEADERS)
        obj = resp['data'][object_type]
        if not obj:
            raise ExtractorError(resp['errors'][0]['message'], expected=True)
        return obj

class DailymotionIE(DailymotionBaseInfoExtractor):
    _VALID_URL = r'''(?ix)
                    https?://
                        (?:
                            (?:(?:www|touch|geo)\.)?dailymotion\.[a-z]{2,3}/(?:(?:(?:(?:embed|swf|\#)/)|player\.html\?)?video|swf)|
                            (?:www\.)?lequipe\.fr/video
                        )
                        [/=](?P<id>[^/?_&]+)(?:.+?\bplaylist=(?P<playlist_id>x[0-9a-z]+))?
                    '''
    IE_NAME = 'dailymotion'
    _EMBED_REGEX = [r'<(?:(?:embed|iframe)[^>]+?src=|input[^>]+id=[\'"]dmcloudUrlEmissionSelect[\'"][^>]+value=)(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.com/(?:embed|swf)/video/.+?)\1']
    _TESTS = [{
        'url': 'http://www.dailymotion.com/video/x5kesuj_office-christmas-party-review-jason-bateman-olivia-munn-t-j-miller_news',
        'md5': '074b95bdee76b9e3654137aee9c79dfe',
        'info_dict': {
            'id': 'x5kesuj',
            'ext': 'mp4',
            'title': 'Office Christmas Party Review –  Jason Bateman, Olivia Munn, T.J. Miller',
            'description': 'Office Christmas Party Review -  Jason Bateman, Olivia Munn, T.J. Miller',
            'duration': 187,
            'timestamp': 1493651285,
            'upload_date': '20170501',
            'uploader': 'Deadline',
            'uploader_id': 'x1xm8ri',
            'age_limit': 0,
        },
    }, {
        'url': 'https://geo.dailymotion.com/player.html?video=x89eyek&mute=true',
        'md5': 'e2f9717c6604773f963f069ca53a07f8',
        'info_dict': {
            'id': 'x89eyek',
            'ext': 'mp4',
            'title': "En quête d'esprit du 27/03/2022",
            'description': 'md5:66542b9f4df2eb23f314fc097488e553',
            'duration': 2756,
            'timestamp': 1648383669,
            'upload_date': '20220327',
            'uploader': 'CNEWS',
            'uploader_id': 'x24vth',
            'age_limit': 0,
            'view_count': int,
            'like_count': int,
            'tags': ['en_quete_d_esprit'],
            'thumbnail': 'https://s2.dmcdn.net/v/Tncwi1YGKdvFbDuDY/x1080',
        }
    }, {
        'url': 'https://www.dailymotion.com/video/x2iuewm_steam-machine-models-pricing-listed-on-steam-store-ign-news_videogames',
        'md5': '2137c41a8e78554bb09225b8eb322406',
        'info_dict': {
            'id': 'x2iuewm',
            'ext': 'mp4',
            'title': 'Steam Machine Models, Pricing Listed on Steam Store - IGN News',
            'description': 'Several come bundled with the Steam Controller.',
            'thumbnail': r're:^https?:.*\.(?:jpg|png)$',
            'duration': 74,
            'timestamp': 1425657362,
            'upload_date': '20150306',
            'uploader': 'IGN',
            'uploader_id': 'xijv66',
            'age_limit': 0,
            'view_count': int,
        },
        'skip': 'video gone',
    }, {
        # Vevo video
        'url': 'http://www.dailymotion.com/video/x149uew_katy-perry-roar-official_musi',
        'info_dict': {
            'title': 'Roar (Official)',
            'id': 'USUV71301934',
            'ext': 'mp4',
            'uploader': 'Katy Perry',
            'upload_date': '20130905',
        },
        'params': {
            'skip_download': True,
        },
        'skip': 'VEVO is only available in some countries',
    }, {
        # age-restricted video
        'url': 'http://www.dailymotion.com/video/xyh2zz_leanna-decker-cyber-girl-of-the-year-desires-nude-playboy-plus_redband',
        'md5': '0d667a7b9cebecc3c89ee93099c4159d',
        'info_dict': {
            'id': 'xyh2zz',
            'ext': 'mp4',
            'title': 'Leanna Decker - Cyber Girl Of The Year Desires Nude [Playboy Plus]',
            'uploader': 'HotWaves1012',
            'age_limit': 18,
        },
        'skip': 'video gone',
    }, {
        # geo-restricted, player v5
        'url': 'http://www.dailymotion.com/video/xhza0o',
        'only_matching': True,
    }, {
        # with subtitles
        'url': 'http://www.dailymotion.com/video/x20su5f_the-power-of-nightmares-1-the-rise-of-the-politics-of-fear-bbc-2004_news',
        'only_matching': True,
    }, {
        'url': 'http://www.dailymotion.com/swf/video/x3n92nf',
        'only_matching': True,
    }, {
        'url': 'http://www.dailymotion.com/swf/x3ss1m_funny-magic-trick-barry-and-stuart_fun',
        'only_matching': True,
    }, {
        'url': 'https://www.lequipe.fr/video/x791mem',
        'only_matching': True,
    }, {
        'url': 'https://www.lequipe.fr/video/k7MtHciueyTcrFtFKA2',
        'only_matching': True,
    }, {
        'url': 'https://www.dailymotion.com/video/x3z49k?playlist=xv4bw',
        'only_matching': True,
    }]
    _GEO_BYPASS = False
    _COMMON_MEDIA_FIELDS = '''description
      geoblockedCountries {
        allowed
      }
      xid'''

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        # https://developer.dailymotion.com/player#player-parameters
        yield from super()._extract_embed_urls(url, webpage)
        for mobj in re.finditer(
                r'(?s)DM\.player\([^,]+,\s*{.*?video[\'"]?\s*:\s*["\']?(?P<id>[0-9a-zA-Z]+).+?}\s*\);', webpage):
            yield from 'https://www.dailymotion.com/embed/video/' + mobj.group('id')

    def _real_extract(self, url):
        url, smuggled_data = unsmuggle_url(url)
        video_id, playlist_id = self._match_valid_url(url).groups()

        if playlist_id:
            if self._yes_playlist(playlist_id, video_id):
                return self.url_result(
                    'http://www.dailymotion.com/playlist/' + playlist_id,
                    'DailymotionPlaylist', playlist_id)

        password = self.get_param('videopassword')
        media = self._call_api(
            'media', video_id, '''... on Video {
      %s
      stats {
        likes {
          total
        }
        views {
          total
        }
      }
    }
    ... on Live {
      %s
      audienceCount
      isOnAir
    }''' % (self._COMMON_MEDIA_FIELDS, self._COMMON_MEDIA_FIELDS), 'Downloading media JSON metadata',
            'password: "%s"' % self.get_param('videopassword') if password else None)
        xid = media['xid']

        metadata = self._download_json(
            'https://www.dailymotion.com/player/metadata/video/' + xid,
            xid, 'Downloading metadata JSON',
            query=traverse_obj(smuggled_data, 'query') or {'app': 'com.dailymotion.neon'})

        error = metadata.get('error')
        if error:
            title = error.get('title') or error['raw_message']
            # See https://developer.dailymotion.com/api#access-error
            if error.get('code') == 'DM007':
                allowed_countries = try_get(media, lambda x: x['geoblockedCountries']['allowed'], list)
                self.raise_geo_restricted(msg=title, countries=allowed_countries)
            raise ExtractorError(
                '%s said: %s' % (self.IE_NAME, title), expected=True)

        title = metadata['title']
        is_live = media.get('isOnAir')
        formats = []
        for quality, media_list in metadata['qualities'].items():
            for m in media_list:
                media_url = m.get('url')
                media_type = m.get('type')
                if not media_url or media_type == 'application/vnd.lumberjack.manifest':
                    continue
                if media_type == 'application/x-mpegURL':
                    formats.extend(self._extract_m3u8_formats(
                        media_url, video_id, 'mp4', live=is_live, m3u8_id='hls', fatal=False))
                else:
                    f = {
                        'url': media_url,
                        'format_id': 'http-' + quality,
                    }
                    m = re.search(r'/H264-(\d+)x(\d+)(?:-(60)/)?', media_url)
                    if m:
                        width, height, fps = map(int_or_none, m.groups())
                        f.update({
                            'fps': fps,
                            'height': height,
                            'width': width,
                        })
                    formats.append(f)
        for f in formats:
            f['url'] = f['url'].split('#')[0]
            if not f.get('fps') and f['format_id'].endswith('@60'):
                f['fps'] = 60
        self._sort_formats(formats)

        subtitles = {}
        subtitles_data = try_get(metadata, lambda x: x['subtitles']['data'], dict) or {}
        for subtitle_lang, subtitle in subtitles_data.items():
            subtitles[subtitle_lang] = [{
                'url': subtitle_url,
            } for subtitle_url in subtitle.get('urls', [])]

        thumbnails = []
        for height, poster_url in metadata.get('posters', {}).items():
            thumbnails.append({
                'height': int_or_none(height),
                'id': height,
                'url': poster_url,
            })

        owner = metadata.get('owner') or {}
        stats = media.get('stats') or {}
        get_count = lambda x: int_or_none(try_get(stats, lambda y: y[x + 's']['total']))

        return {
            'id': video_id,
            'title': title,
            'description': clean_html(media.get('description')),
            'thumbnails': thumbnails,
            'duration': int_or_none(metadata.get('duration')) or None,
            'timestamp': int_or_none(metadata.get('created_time')),
            'uploader': owner.get('screenname'),
            'uploader_id': owner.get('id') or metadata.get('screenname'),
            'age_limit': 18 if metadata.get('explicit') else 0,
            'tags': metadata.get('tags'),
            'view_count': get_count('view') or int_or_none(media.get('audienceCount')),
            'like_count': get_count('like'),
            'formats': formats,
            'subtitles': subtitles,
            'is_live': is_live,
        }

class DailymotionPlaylistBaseIE(DailymotionBaseInfoExtractor):
    _PAGE_SIZE = 100

    def _fetch_page(self, playlist_id, page):
        page += 1
        videos = self._call_api(
            self._OBJECT_TYPE, playlist_id,
            '''videos(allowExplicit: %s, first: %d, page: %d) {
      edges {
        node {
          xid
          url
        }
      }
    }''' % ('false' if self._FAMILY_FILTER else 'true', self._PAGE_SIZE, page),
            'Downloading page %d' % page)['videos']
        for edge in videos['edges']:
            node = edge['node']
            yield self.url_result(
                node['url'], DailymotionIE.ie_key(), node['xid'])

    def _real_extract(self, url):
        playlist_id = self._match_id(url)
        entries = OnDemandPagedList(functools.partial(
            self._fetch_page, playlist_id), self._PAGE_SIZE)
        return self.playlist_result(
            entries, playlist_id)

class DailymotionPlaylistIE(DailymotionPlaylistBaseIE):
    IE_NAME = 'dailymotion:playlist'
    _VALID_URL = r'(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/playlist/(?P<id>x[0-9a-z]+)'
    _TESTS = [{
        'url': 'http://www.dailymotion.com/playlist/xv4bw_nqtv_sport/1#video=xl8v3q',
        'info_dict': {
            'id': 'xv4bw',
        },
        'playlist_mincount': 20,
    }]
    _OBJECT_TYPE = 'collection'

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        # Look for embedded Dailymotion playlist player (#3822)
        for mobj in re.finditer(
                r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.[a-z]{2,3}/widget/jukebox\?.+?)\1',
                webpage):
            for p in re.findall(r'list\[\]=/playlist/([^/]+)/', unescapeHTML(mobj.group('url'))):
                yield '//dailymotion.com/playlist/%s' % p

class DailymotionUserIE(DailymotionPlaylistBaseIE):
    IE_NAME = 'dailymotion:user'
    _VALID_URL = r'https?://(?:www\.)?dailymotion\.[a-z]{2,3}/(?!(?:embed|swf|#|video|playlist)/)(?:(?:old/)?user/)?(?P<id>[^/]+)'
    _TESTS = [{
        'url': 'https://www.dailymotion.com/user/nqtv',
        'info_dict': {
            'id': 'nqtv',
        },
        'playlist_mincount': 152,
    }, {
        'url': 'http://www.dailymotion.com/user/UnderProject',
        'info_dict': {
            'id': 'UnderProject',
        },
        'playlist_mincount': 1000,
        'skip': 'Takes too long time',
    }, {
        'url': 'https://www.dailymotion.com/user/nqtv',
        'info_dict': {
            'id': 'nqtv',
        },
        'playlist_mincount': 148,
        'params': {
            'age_limit': 0,
        },
    }]
    _OBJECT_TYPE = 'channel'


code:
from .common import InfoExtractor
from ..utils import (
    determine_ext,
    float_or_none,
    join_nonempty,
    traverse_obj,
    url_or_none,
)

class DailyWireBaseIE(InfoExtractor):
    _JSON_PATH = {
        'episode': ('props', 'pageProps', 'episodeData', 'episode'),
        'videos': ('props', 'pageProps', 'videoData', 'video'),
        'podcasts': ('props', 'pageProps', 'episode'),
    }

    def _get_json(self, url):
        sites_type, slug = self._match_valid_url(url).group('sites_type', 'id')
        json_data = self._search_nextjs_data(self._download_webpage(url, slug), slug)
        return slug, traverse_obj(json_data, self._JSON_PATH[sites_type])

class DailyWireIE(DailyWireBaseIE):
    _VALID_URL = r'https?://(?:www\.)dailywire(?:\.com)/(?P<sites_type>episode|videos)/(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://www.dailywire.com/episode/1-fauci',
        'info_dict': {
            'id': 'ckzsl50xnqpy30850in3v4bu7',
            'ext': 'mp4',
            'display_id': '1-fauci',
            'title': '1. Fauci',
            'description': 'md5:9df630347ef85081b7e97dd30bc22853',
            'thumbnail': 'https://daily-wire-production.imgix.net/episodes/ckzsl50xnqpy30850in3v4bu7/ckzsl50xnqpy30850in3v4bu7-1648237399554.jpg',
            'creator': 'Caroline Roberts',
            'series_id': 'ckzplm0a097fn0826r2vc3j7h',
            'series': 'China: The Enemy Within',
        }
    }, {
        'url': 'https://www.dailywire.com/episode/ep-124-bill-maher',
        'info_dict': {
            'id': 'cl0ngbaalplc80894sfdo9edf',
            'ext': 'mp3',
            'display_id': 'ep-124-bill-maher',
            'title': 'Ep. 124 - Bill Maher',
            'thumbnail': 'https://daily-wire-production.imgix.net/episodes/cl0ngbaalplc80894sfdo9edf/cl0ngbaalplc80894sfdo9edf-1647065568518.jpg',
            'creator': 'Caroline Roberts',
            'description': 'md5:adb0de584bcfa9c41374999d9e324e98',
            'series_id': 'cjzvep7270hp00786l9hwccob',
            'series': 'The Sunday Special',
        }
    }, {
        'url': 'https://www.dailywire.com/videos/the-hyperions',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        slug, episode_info = self._get_json(url)
        urls = traverse_obj(
            episode_info, (('segments', 'videoUrl'), ..., ('video', 'audio')), expected_type=url_or_none)

        formats, subtitles = [], {}
        for url in urls:
            if determine_ext(url) != 'm3u8':
                formats.append({'url': url})
                continue
            format_, subs_ = self._extract_m3u8_formats_and_subtitles(url, slug)
            formats.extend(format_)
            self._merge_subtitles(subs_, target=subtitles)
        self._sort_formats(formats)
        return {
            'id': episode_info['id'],
            'display_id': slug,
            'title': traverse_obj(episode_info, 'title', 'name'),
            'description': episode_info.get('description'),
            'creator': join_nonempty(('createdBy', 'firstName'), ('createdBy', 'lastName'), from_dict=episode_info, delim=' '),
            'duration': float_or_none(episode_info.get('duration')),
            'is_live': episode_info.get('isLive'),
            'thumbnail': traverse_obj(episode_info, 'thumbnail', 'image', expected_type=url_or_none),
            'formats': formats,
            'subtitles': subtitles,
            'series_id': traverse_obj(episode_info, ('show', 'id')),
            'series': traverse_obj(episode_info, ('show', 'name')),
        }

class DailyWirePodcastIE(DailyWireBaseIE):
    _VALID_URL = r'https?://(?:www\.)dailywire(?:\.com)/(?P<sites_type>podcasts)/(?P<podcaster>[\w-]+/(?P<id>[\w-]+))'
    _TESTS = [{
        'url': 'https://www.dailywire.com/podcasts/morning-wire/get-ready-for-recession-6-15-22',
        'info_dict': {
            'id': 'cl4f01d0w8pbe0a98ydd0cfn1',
            'ext': 'm4a',
            'display_id': 'get-ready-for-recession-6-15-22',
            'title': 'Get Ready for Recession | 6.15.22',
            'description': 'md5:c4afbadda4e1c38a4496f6d62be55634',
            'thumbnail': 'https://daily-wire-production.imgix.net/podcasts/ckx4otgd71jm508699tzb6hf4-1639506575562.jpg',
            'duration': 900.117667,
        }
    }]

    def _real_extract(self, url):
        slug, episode_info = self._get_json(url)
        audio_id = traverse_obj(episode_info, 'audioMuxPlaybackId', 'VUsAipTrBVSgzw73SpC2DAJD401TYYwEp')

        return {
            'id': episode_info['id'],
            'url': f'https://stream.media.dailywire.com/{audio_id}/audio.m4a',
            'display_id': slug,
            'title': episode_info.get('title'),
            'duration': float_or_none(episode_info.get('duration')),
            'thumbnail': episode_info.get('thumbnail'),
            'description': episode_info.get('description'),
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    clean_html,
    int_or_none,
    try_get,
    unified_strdate)

class DamtomoBaseIE(InfoExtractor):
    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage, handle = self._download_webpage_handle(self._WEBPAGE_URL_TMPL % video_id, video_id, encoding='sjis')

        if handle.url == 'https://www.clubdam.com/sorry/':
            raise ExtractorError('You are rate-limited. Try again later.', expected=True)
        if '
予期せぬエラーが発生しました。
' in webpage:
            raise ExtractorError('There is an error on server-side. Try again later.', expected=True)

        description = self._search_regex(r'(?m)
\s*
\s*([^<]*?)\s*
', webpage, 'description', default=None)
        uploader_id = self._search_regex(r'<a href="https://www\.clubdam\.com/app/damtomo/member/info/Profile\.do\?damtomoId=([^"]+)"', webpage, 'uploader_id', default=None)

        data_dict = {
            mobj.group('class'): re.sub(r'\s+', ' ', clean_html(mobj.group('value')))
            for mobj in re.finditer(r'(?s)<(p|div)\s+class="(?P<class>[^" ]+?)">(?P<value>.+?)</\1>', webpage)}

        # since videos do not have title, give the name of song instead
        data_dict['user_name'] = re.sub(r'\s*さん\s*$', '', data_dict['user_name'])
        title = data_dict.get('song_title')

        stream_tree = self._download_xml(
            self._DKML_XML_URL % video_id, video_id, note='Requesting stream information', encoding='sjis',
            # doing this has no problem since there is no character outside ASCII,
            # and never likely to happen in the future
            transform_source=lambda x: re.sub(r'\s*encoding="[^"]+?"', '', x))

        m3u8_url = try_get(stream_tree, lambda x: x.find(
            './/d:streamingUrl', {'d': self._DKML_XML_NS}).text.strip(), str)
        if not m3u8_url:
            raise ExtractorError('Failed to obtain m3u8 URL')

        return {
            'id': video_id,
            'title': title,
            'uploader_id': uploader_id,
            'description': description,
            'formats': [{
                'format_id': 'hls',
                'url': m3u8_url,
                'ext': 'mp4',
                'protocol': 'm3u8_native',
            }],
            'uploader': data_dict.get('user_name'),
            'upload_date': unified_strdate(self._search_regex(r'(\d{4}/\d{2}/\d{2})', data_dict.get('date'), 'upload_date', default=None)),
            'view_count': int_or_none(self._search_regex(r'(\d+)', data_dict['audience'], 'view_count', default=None)),
            'like_count': int_or_none(self._search_regex(r'(\d+)', data_dict['nice'], 'like_count', default=None)),
            'track': title,
            'artist': data_dict.get('song_artist'),
        }

class DamtomoVideoIE(DamtomoBaseIE):
    IE_NAME = 'damtomo:video'
    _VALID_URL = r'https?://(?:www\.)?clubdam\.com/app/damtomo/(?:SP/)?karaokeMovie/StreamingDkm\.do\?karaokeMovieId=(?P<id>\d+)'
    _WEBPAGE_URL_TMPL = 'https://www.clubdam.com/app/damtomo/karaokeMovie/StreamingDkm.do?karaokeMovieId=%s'
    _DKML_XML_URL = 'https://www.clubdam.com/app/damtomo/karaokeMovie/GetStreamingDkmUrlXML.do?movieSelectFlg=2&karaokeMovieId=%s'
    _DKML_XML_NS = 'https://www.clubdam.com/app/damtomo/karaokeMovie/GetStreamingDkmUrlXML'
    _TESTS = [{
        'url': 'https://www.clubdam.com/app/damtomo/karaokeMovie/StreamingDkm.do?karaokeMovieId=2414316',
        'info_dict': {
            'id': '2414316',
            'title': 'Get Wild',
            'uploader': 'Ｋドロン',
            'uploader_id': 'ODk5NTQwMzQ',
            'track': 'Get Wild',
            'artist': 'TM NETWORK(TMN)',
            'upload_date': '20201226',
        }
    }]

class DamtomoRecordIE(DamtomoBaseIE):
    IE_NAME = 'damtomo:record'
    _VALID_URL = r'https?://(?:www\.)?clubdam\.com/app/damtomo/(?:SP/)?karaokePost/StreamingKrk\.do\?karaokeContributeId=(?P<id>\d+)'
    _WEBPAGE_URL_TMPL = 'https://www.clubdam.com/app/damtomo/karaokePost/StreamingKrk.do?karaokeContributeId=%s'
    _DKML_XML_URL = 'https://www.clubdam.com/app/damtomo/karaokePost/GetStreamingKrkUrlXML.do?karaokeContributeId=%s'
    _DKML_XML_NS = 'https://www.clubdam.com/app/damtomo/karaokePost/GetStreamingKrkUrlXML'
    _TESTS = [{
        'url': 'https://www.clubdam.com/app/damtomo/karaokePost/StreamingKrk.do?karaokeContributeId=27376862',
        'info_dict': {
            'id': '27376862',
            'title': 'イカSUMMER [良音]',
            'description': None,
            'uploader': 'ＮＡＮＡ',
            'uploader_id': 'MzAyMDExNTY',
            'upload_date': '20210721',
            'view_count': 4,
            'like_count': 1,
            'track': 'イカSUMMER [良音]',
            'artist': 'ORANGE RANGE',
        }
    }, {
        'url': 'https://www.clubdam.com/app/damtomo/karaokePost/StreamingKrk.do?karaokeContributeId=27489418',
        'info_dict': {
            'id': '27489418',
            'title': '心みだれて〜say it with flowers〜(生音)',
            'uploader_id': 'NjI1MjI2MjU',
            'description': 'やっぱりキーを下げて正解だった感じ。リベンジ成功ということで。',
            'uploader': '箱の「中の人」',
            'upload_date': '20210815',
            'view_count': 5,
            'like_count': 3,
            'track': '心みだれて〜say it with flowers〜(生音)',
            'artist': '小林明子',
        }
    }]


code:
import itertools

from .common import InfoExtractor
from ..compat import (
    compat_urllib_parse_unquote,
)
from ..utils import parse_qs

class DaumBaseIE(InfoExtractor):
    _KAKAO_EMBED_BASE = 'http://tv.kakao.com/embed/player/cliplink/'

class DaumIE(DaumBaseIE):
    _VALID_URL = r'https?://(?:(?:m\.)?tvpot\.daum\.net/v/|videofarm\.daum\.net/controller/player/VodPlayer\.swf\?vid=)(?P<id>[^?#&]+)'
    IE_NAME = 'daum.net'

    _TESTS = [{
        'url': 'http://tvpot.daum.net/v/vab4dyeDBysyBssyukBUjBz',
        'info_dict': {
            'id': 'vab4dyeDBysyBssyukBUjBz',
            'ext': 'mp4',
            'title': '마크 헌트 vs 안토니오 실바',
            'description': 'Mark Hunt vs Antonio Silva',
            'upload_date': '20131217',
            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
            'duration': 2117,
            'view_count': int,
            'comment_count': int,
            'uploader_id': 186139,
            'uploader': '콘간지',
            'timestamp': 1387310323,
        },
    }, {
        'url': 'http://m.tvpot.daum.net/v/65139429',
        'info_dict': {
            'id': '65139429',
            'ext': 'mp4',
            'title': '1297회, \'아빠 아들로 태어나길 잘 했어\' 민수, 감동의 눈물[아빠 어디가] 20150118',
            'description': 'md5:79794514261164ff27e36a21ad229fc5',
            'upload_date': '20150118',
            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
            'duration': 154,
            'view_count': int,
            'comment_count': int,
            'uploader': 'MBC 예능',
            'uploader_id': 132251,
            'timestamp': 1421604228,
        },
    }, {
        'url': 'http://tvpot.daum.net/v/07dXWRka62Y%24',
        'only_matching': True,
    }, {
        'url': 'http://videofarm.daum.net/controller/player/VodPlayer.swf?vid=vwIpVpCQsT8%24&ref=',
        'info_dict': {
            'id': 'vwIpVpCQsT8$',
            'ext': 'flv',
            'title': '01-Korean War ( Trouble on the horizon )',
            'description': 'Korean War 01\r\nTrouble on the horizon\r\n전쟁의 먹구름',
            'upload_date': '20080223',
            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
            'duration': 249,
            'view_count': int,
            'comment_count': int,
            'uploader': '까칠한 墮落始祖 황비홍님의',
            'uploader_id': 560824,
            'timestamp': 1203770745,
        },
    }, {
        # Requires dte_type=WEB (#9972)
        'url': 'http://tvpot.daum.net/v/s3794Uf1NZeZ1qMpGpeqeRU',
        'md5': 'a8917742069a4dd442516b86e7d66529',
        'info_dict': {
            'id': 's3794Uf1NZeZ1qMpGpeqeRU',
            'ext': 'mp4',
            'title': '러블리즈 - Destiny (나의 지구) (Lovelyz - Destiny)',
            'description': '러블리즈 - Destiny (나의 지구) (Lovelyz - Destiny)\r\n\r\n[쇼! 음악중심] 20160611, 507회',
            'upload_date': '20170129',
            'uploader': '쇼! 음악중심',
            'uploader_id': 2653210,
            'timestamp': 1485684628,
        },
    }]

    def _real_extract(self, url):
        video_id = compat_urllib_parse_unquote(self._match_id(url))
        if not video_id.isdigit():
            video_id += '@my'
        return self.url_result(
            self._KAKAO_EMBED_BASE + video_id, 'Kakao', video_id)

class DaumClipIE(DaumBaseIE):
    _VALID_URL = r'https?://(?:m\.)?tvpot\.daum\.net/(?:clip/ClipView.(?:do|tv)|mypot/View.do)\?.*?clipid=(?P<id>\d+)'
    IE_NAME = 'daum.net:clip'
    _URL_TEMPLATE = 'http://tvpot.daum.net/clip/ClipView.do?clipid=%s'

    _TESTS = [{
        'url': 'http://tvpot.daum.net/clip/ClipView.do?clipid=52554690',
        'info_dict': {
            'id': '52554690',
            'ext': 'mp4',
            'title': 'DOTA 2GETHER 시즌2 6회 - 2부',
            'description': 'DOTA 2GETHER 시즌2 6회 - 2부',
            'upload_date': '20130831',
            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
            'duration': 3868,
            'view_count': int,
            'uploader': 'GOMeXP',
            'uploader_id': 6667,
            'timestamp': 1377911092,
        },
    }, {
        'url': 'http://m.tvpot.daum.net/clip/ClipView.tv?clipid=54999425',
        'only_matching': True,
    }]

    @classmethod
    def suitable(cls, url):
        return False if DaumPlaylistIE.suitable(url) or DaumUserIE.suitable(url) else super(DaumClipIE, cls).suitable(url)

    def _real_extract(self, url):
        video_id = self._match_id(url)
        return self.url_result(
            self._KAKAO_EMBED_BASE + video_id, 'Kakao', video_id)

class DaumListIE(InfoExtractor):
    def _get_entries(self, list_id, list_id_type):
        name = None
        entries = []
        for pagenum in itertools.count(1):
            list_info = self._download_json(
                'http://tvpot.daum.net/mypot/json/GetClipInfo.do?size=48&init=true&order=date&page=%d&%s=%s' % (
                    pagenum, list_id_type, list_id), list_id, 'Downloading list info - %s' % pagenum)

            entries.extend([
                self.url_result(
                    'http://tvpot.daum.net/v/%s' % clip['vid'])
                for clip in list_info['clip_list']
            ])

            if not name:
                name = list_info.get('playlist_bean', {}).get('name') or \
                    list_info.get('potInfo', {}).get('name')

            if not list_info.get('has_more'):
                break

        return name, entries

    def _check_clip(self, url, list_id):
        query_dict = parse_qs(url)
        if 'clipid' in query_dict:
            clip_id = query_dict['clipid'][0]
            if not self._yes_playlist(list_id, clip_id):
                return self.url_result(DaumClipIE._URL_TEMPLATE % clip_id, 'DaumClip')

class DaumPlaylistIE(DaumListIE):
    _VALID_URL = r'https?://(?:m\.)?tvpot\.daum\.net/mypot/(?:View\.do|Top\.tv)\?.*?playlistid=(?P<id>[0-9]+)'
    IE_NAME = 'daum.net:playlist'
    _URL_TEMPLATE = 'http://tvpot.daum.net/mypot/View.do?playlistid=%s'

    _TESTS = [{
        'note': 'Playlist url with clipid',
        'url': 'http://tvpot.daum.net/mypot/View.do?playlistid=6213966&clipid=73806844',
        'info_dict': {
            'id': '6213966',
            'title': 'Woorissica Official',
        },
        'playlist_mincount': 181
    }, {
        'note': 'Playlist url with clipid - noplaylist',
        'url': 'http://tvpot.daum.net/mypot/View.do?playlistid=6213966&clipid=73806844',
        'info_dict': {
            'id': '73806844',
            'ext': 'mp4',
            'title': '151017 Airport',
            'upload_date': '20160117',
        },
        'params': {
            'noplaylist': True,
            'skip_download': True,
        }
    }]

    @classmethod
    def suitable(cls, url):
        return False if DaumUserIE.suitable(url) else super(DaumPlaylistIE, cls).suitable(url)

    def _real_extract(self, url):
        list_id = self._match_id(url)

        clip_result = self._check_clip(url, list_id)
        if clip_result:
            return clip_result

        name, entries = self._get_entries(list_id, 'playlistid')

        return self.playlist_result(entries, list_id, name)

class DaumUserIE(DaumListIE):
    _VALID_URL = r'https?://(?:m\.)?tvpot\.daum\.net/mypot/(?:View|Top)\.(?:do|tv)\?.*?ownerid=(?P<id>[0-9a-zA-Z]+)'
    IE_NAME = 'daum.net:user'

    _TESTS = [{
        'url': 'http://tvpot.daum.net/mypot/View.do?ownerid=o2scDLIVbHc0',
        'info_dict': {
            'id': 'o2scDLIVbHc0',
            'title': '마이 리틀 텔레비전',
        },
        'playlist_mincount': 213
    }, {
        'url': 'http://tvpot.daum.net/mypot/View.do?ownerid=o2scDLIVbHc0&clipid=73801156',
        'info_dict': {
            'id': '73801156',
            'ext': 'mp4',
            'title': '[미공개] 김구라, 오만석이 부릅니다 \'오케피\' - 마이 리틀 텔레비전 20160116',
            'upload_date': '20160117',
            'description': 'md5:5e91d2d6747f53575badd24bd62b9f36'
        },
        'params': {
            'noplaylist': True,
            'skip_download': True,
        }
    }, {
        'note': 'Playlist url has ownerid and playlistid, playlistid takes precedence',
        'url': 'http://tvpot.daum.net/mypot/View.do?ownerid=o2scDLIVbHc0&playlistid=6196631',
        'info_dict': {
            'id': '6196631',
            'title': '마이 리틀 텔레비전 - 20160109',
        },
        'playlist_count': 11
    }, {
        'url': 'http://tvpot.daum.net/mypot/Top.do?ownerid=o2scDLIVbHc0',
        'only_matching': True,
    }, {
        'url': 'http://m.tvpot.daum.net/mypot/Top.tv?ownerid=45x1okb1If50&playlistid=3569733',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        list_id = self._match_id(url)

        clip_result = self._check_clip(url, list_id)
        if clip_result:
            return clip_result

        query_dict = parse_qs(url)
        if 'playlistid' in query_dict:
            playlist_id = query_dict['playlistid'][0]
            return self.url_result(DaumPlaylistIE._URL_TEMPLATE % playlist_id, 'DaumPlaylist')

        name, entries = self._get_entries(list_id, 'ownerid')

        return self.playlist_result(entries, list_id, name)


code:
from .common import InfoExtractor
from ..utils import js_to_json, urljoin

class DaystarClipIE(InfoExtractor):
    IE_NAME = 'daystar:clip'
    _VALID_URL = r'https?://player\.daystar\.tv/(?P<id>\w+)'
    _TESTS = [{
        'url': 'https://player.daystar.tv/0MTO2ITM',
        'info_dict': {
            'id': '0MTO2ITM',
            'ext': 'mp4',
            'title': 'The Dark World of COVID Pt. 1 | Aaron Siri',
            'description': 'a420d320dda734e5f29458df3606c5f4',
            'thumbnail': r're:^https?://.+\.jpg',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)

        src_iframe = self._search_regex(r'\<iframe[^>]+src="([^"]+)"', webpage, 'src iframe')
        webpage_iframe = self._download_webpage(
            src_iframe.replace('player.php', 'config2.php'), video_id, headers={'Referer': src_iframe})

        sources = self._parse_json(self._search_regex(
            r'sources\:\s*(\[.*?\])', webpage_iframe, 'm3u8 source'), video_id, transform_source=js_to_json)

        formats, subtitles = [], {}
        for source in sources:
            file = source.get('file')
            if file and source.get('type') == 'm3u8':
                fmts, subs = self._extract_m3u8_formats_and_subtitles(
                    urljoin('https://www.lightcast.com/embed/', file),
                    video_id, 'mp4', fatal=False, headers={'Referer': src_iframe})
                formats.extend(fmts)
                subtitles = self._merge_subtitles(subtitles, subs)
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': self._html_search_meta(['og:title', 'twitter:title'], webpage),
            'description': self._html_search_meta(['og:description', 'twitter:description'], webpage),
            'thumbnail': self._search_regex(r'image:\s*"([^"]+)', webpage_iframe, 'thumbnail'),
            'formats': formats,
            'subtitles': subtitles,
        }


code:
from .common import InfoExtractor

class DBTVIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?dagbladet\.no/video/(?:(?:embed|(?P<display_id>[^/]+))/)?(?P<id>[0-9A-Za-z_-]{11}|[a-zA-Z0-9]{8})'
    _EMBED_REGEX = [r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dagbladet\.no/video/embed/(?:[0-9A-Za-z_-]{11}|[a-zA-Z0-9]{8}).*?)\1']
    _TESTS = [{
        'url': 'https://www.dagbladet.no/video/PynxJnNWChE/',
        'md5': 'b8f850ba1860adbda668d367f9b77699',
        'info_dict': {
            'id': 'PynxJnNWChE',
            'ext': 'mp4',
            'title': 'Skulle teste ut fornøyelsespark, men kollegaen var bare opptatt av bikinikroppen',
            'description': 'md5:49cc8370e7d66e8a2ef15c3b4631fd3f',
            'thumbnail': r're:https?://.*\.jpg',
            'upload_date': '20160916',
            'duration': 69,
            'uploader_id': 'UCk5pvsyZJoYJBd7_oFPTlRQ',
            'uploader': 'Dagbladet',
        },
        'add_ie': ['Youtube']
    }, {
        'url': 'https://www.dagbladet.no/video/embed/xlGmyIeN9Jo/?autoplay=false',
        'only_matching': True,
    }, {
        'url': 'https://www.dagbladet.no/video/truer-iran-bor-passe-dere/PalfB2Cw',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        display_id, video_id = self._match_valid_url(url).groups()
        info = {
            '_type': 'url_transparent',
            'id': video_id,
            'display_id': display_id,
        }
        if len(video_id) == 11:
            info.update({
                'url': video_id,
                'ie_key': 'Youtube',
            })
        else:
            info.update({
                'url': 'jwplatform:' + video_id,
                'ie_key': 'JWPlatform',
            })
        return info


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    float_or_none,
    int_or_none,
    unified_timestamp,
    url_or_none,
)

class DctpTvIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?dctp\.tv/(?:#/)?filme/(?P<id>[^/?#&]+)'
    _TESTS = [{
        # 4x3
        'url': 'http://www.dctp.tv/filme/videoinstallation-fuer-eine-kaufhausfassade/',
        'md5': '3ffbd1556c3fe210724d7088fad723e3',
        'info_dict': {
            'id': '95eaa4f33dad413aa17b4ee613cccc6c',
            'display_id': 'videoinstallation-fuer-eine-kaufhausfassade',
            'ext': 'm4v',
            'title': 'Videoinstallation für eine Kaufhausfassade',
            'description': 'Kurzfilm',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 71.24,
            'timestamp': 1302172322,
            'upload_date': '20110407',
        },
    }, {
        # 16x9
        'url': 'http://www.dctp.tv/filme/sind-youtuber-die-besseren-lehrer/',
        'only_matching': True,
    }]

    _BASE_URL = 'http://dctp-ivms2-restapi.s3.amazonaws.com'

    def _real_extract(self, url):
        display_id = self._match_id(url)

        version = self._download_json(
            '%s/version.json' % self._BASE_URL, display_id,
            'Downloading version JSON')

        restapi_base = '%s/%s/restapi' % (
            self._BASE_URL, version['version_name'])

        info = self._download_json(
            '%s/slugs/%s.json' % (restapi_base, display_id), display_id,
            'Downloading video info JSON')

        media = self._download_json(
            '%s/media/%s.json' % (restapi_base, compat_str(info['object_id'])),
            display_id, 'Downloading media JSON')

        uuid = media['uuid']
        title = media['title']
        is_wide = media.get('is_wide')
        formats = []

        def add_formats(suffix):
            templ = 'https://%%s/%s_dctp_%s.m4v' % (uuid, suffix)
            formats.extend([{
                'format_id': 'hls-' + suffix,
                'url': templ % 'cdn-segments.dctp.tv' + '/playlist.m3u8',
                'protocol': 'm3u8_native',
            }, {
                'format_id': 's3-' + suffix,
                'url': templ % 'completed-media.s3.amazonaws.com',
            }, {
                'format_id': 'http-' + suffix,
                'url': templ % 'cdn-media.dctp.tv',
            }])

        add_formats('0500_' + ('16x9' if is_wide else '4x3'))
        if is_wide:
            add_formats('720p')

        thumbnails = []
        images = media.get('images')
        if isinstance(images, list):
            for image in images:
                if not isinstance(image, dict):
                    continue
                image_url = url_or_none(image.get('url'))
                if not image_url:
                    continue
                thumbnails.append({
                    'url': image_url,
                    'width': int_or_none(image.get('width')),
                    'height': int_or_none(image.get('height')),
                })

        return {
            'id': uuid,
            'display_id': display_id,
            'title': title,
            'alt_title': media.get('subtitle'),
            'description': media.get('description') or media.get('teaser'),
            'timestamp': unified_timestamp(media.get('created')),
            'duration': float_or_none(media.get('duration_in_ms'), scale=1000),
            'thumbnails': thumbnails,
            'formats': formats,
        }


code:
import json

from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    int_or_none,
    orderedSet,
)

class DeezerBaseInfoExtractor(InfoExtractor):
    def get_data(self, url):
        if not self.get_param('test'):
            self.report_warning('For now, this extractor only supports the 30 second previews. Patches welcome!')

        mobj = self._match_valid_url(url)
        data_id = mobj.group('id')

        webpage = self._download_webpage(url, data_id)
        geoblocking_msg = self._html_search_regex(
            r'
(.*?)
', webpage, 'geoblocking message',
            default=None)
        if geoblocking_msg is not None:
            raise ExtractorError(
                'Deezer said: %s' % geoblocking_msg, expected=True)

        data_json = self._search_regex(
            (r'__DZR_APP_STATE__\s*=\s*({.+?})\s*</script>',
             r'naboo\.display\(\'[^\']+\',\s*(.*?)\);\n'),
            webpage, 'data JSON')
        data = json.loads(data_json)
        return data_id, webpage, data

class DeezerPlaylistIE(DeezerBaseInfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?deezer\.com/(../)?playlist/(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://www.deezer.com/playlist/176747451',
        'info_dict': {
            'id': '176747451',
            'title': 'Best!',
            'uploader': 'anonymous',
            'thumbnail': r're:^https?://(e-)?cdns-images\.dzcdn\.net/images/cover/.*\.jpg$',
        },
        'playlist_count': 29,
    }

    def _real_extract(self, url):
        playlist_id, webpage, data = self.get_data(url)

        playlist_title = data.get('DATA', {}).get('TITLE')
        playlist_uploader = data.get('DATA', {}).get('PARENT_USERNAME')
        playlist_thumbnail = self._search_regex(
            r'<img id="naboo_playlist_image".*?src="([^"]+)"', webpage,
            'playlist thumbnail')

        entries = []
        for s in data.get('SONGS', {}).get('data'):
            formats = [{
                'format_id': 'preview',
                'url': s.get('MEDIA', [{}])[0].get('HREF'),
                'preference': -100,  # Only the first 30 seconds
                'ext': 'mp3',
            }]
            self._sort_formats(formats)
            artists = ', '.join(
                orderedSet(a.get('ART_NAME') for a in s.get('ARTISTS')))
            entries.append({
                'id': s.get('SNG_ID'),
                'duration': int_or_none(s.get('DURATION')),
                'title': '%s - %s' % (artists, s.get('SNG_TITLE')),
                'uploader': s.get('ART_NAME'),
                'uploader_id': s.get('ART_ID'),
                'age_limit': 16 if s.get('EXPLICIT_LYRICS') == '1' else 0,
                'formats': formats,
            })

        return {
            '_type': 'playlist',
            'id': playlist_id,
            'title': playlist_title,
            'uploader': playlist_uploader,
            'thumbnail': playlist_thumbnail,
            'entries': entries,
        }

class DeezerAlbumIE(DeezerBaseInfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?deezer\.com/(../)?album/(?P<id>[0-9]+)'
    _TEST = {
        'url': 'https://www.deezer.com/fr/album/67505622',
        'info_dict': {
            'id': '67505622',
            'title': 'Last Week',
            'uploader': 'Home Brew',
            'thumbnail': r're:^https?://(e-)?cdns-images\.dzcdn\.net/images/cover/.*\.jpg$',
        },
        'playlist_count': 7,
    }

    def _real_extract(self, url):
        album_id, webpage, data = self.get_data(url)

        album_title = data.get('DATA', {}).get('ALB_TITLE')
        album_uploader = data.get('DATA', {}).get('ART_NAME')
        album_thumbnail = self._search_regex(
            r'<img id="naboo_album_image".*?src="([^"]+)"', webpage,
            'album thumbnail')

        entries = []
        for s in data.get('SONGS', {}).get('data'):
            formats = [{
                'format_id': 'preview',
                'url': s.get('MEDIA', [{}])[0].get('HREF'),
                'preference': -100,  # Only the first 30 seconds
                'ext': 'mp3',
            }]
            self._sort_formats(formats)
            artists = ', '.join(
                orderedSet(a.get('ART_NAME') for a in s.get('ARTISTS')))
            entries.append({
                'id': s.get('SNG_ID'),
                'duration': int_or_none(s.get('DURATION')),
                'title': '%s - %s' % (artists, s.get('SNG_TITLE')),
                'uploader': s.get('ART_NAME'),
                'uploader_id': s.get('ART_ID'),
                'age_limit': 16 if s.get('EXPLICIT_LYRICS') == '1' else 0,
                'formats': formats,
                'track': s.get('SNG_TITLE'),
                'track_number': int_or_none(s.get('TRACK_NUMBER')),
                'track_id': s.get('SNG_ID'),
                'artist': album_uploader,
                'album': album_title,
                'album_artist': album_uploader,
            })

        return {
            '_type': 'playlist',
            'id': album_id,
            'title': album_title,
            'uploader': album_uploader,
            'thumbnail': album_thumbnail,
            'entries': entries,
        }


code:
from .common import InfoExtractor

class DefenseGouvFrIE(InfoExtractor):
    IE_NAME = 'defense.gouv.fr'
    _VALID_URL = r'https?://.*?\.defense\.gouv\.fr/layout/set/ligthboxvideo/base-de-medias/webtv/(?P<id>[^/?#]*)'

    _TEST = {
        'url': 'http://www.defense.gouv.fr/layout/set/ligthboxvideo/base-de-medias/webtv/attaque-chimique-syrienne-du-21-aout-2013-1',
        'md5': '75bba6124da7e63d2d60b5244ec9430c',
        'info_dict': {
            'id': '11213',
            'ext': 'mp4',
            'title': 'attaque-chimique-syrienne-du-21-aout-2013-1'
        }
    }

    def _real_extract(self, url):
        title = self._match_id(url)
        webpage = self._download_webpage(url, title)

        video_id = self._search_regex(
            r"flashvars.pvg_id=\"(\d+)\";",
            webpage, 'ID')

        json_url = (
            'http://static.videos.gouv.fr/brightcovehub/export/json/%s' %
            video_id)
        info = self._download_json(json_url, title, 'Downloading JSON config')
        video_url = info['renditions'][0]['url']

        return {
            'id': video_id,
            'ext': 'mp4',
            'url': video_url,
            'title': title,
        }


code:
import re
import os.path

from .common import InfoExtractor
from ..compat import compat_urlparse
from ..utils import (
    url_basename,
    remove_start,
)

class DemocracynowIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?democracynow\.org/(?P<id>[^\?]*)'
    IE_NAME = 'democracynow'
    _TESTS = [{
        'url': 'http://www.democracynow.org/shows/2015/7/3',
        'md5': '3757c182d3d84da68f5c8f506c18c196',
        'info_dict': {
            'id': '2015-0703-001',
            'ext': 'mp4',
            'title': 'Daily Show for July 03, 2015',
            'description': 'md5:80eb927244d6749900de6072c7cc2c86',
        },
    }, {
        'url': 'http://www.democracynow.org/2015/7/3/this_flag_comes_down_today_bree',
        'info_dict': {
            'id': '2015-0703-001',
            'ext': 'mp4',
            'title': '"This Flag Comes Down Today": Bree Newsome Scales SC Capitol Flagpole, Takes Down Confederate Flag',
            'description': 'md5:4d2bc4f0d29f5553c2210a4bc7761a21',
        },
        'params': {
            'skip_download': True,
        },
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)

        webpage = self._download_webpage(url, display_id)

        json_data = self._parse_json(self._search_regex(
            r'<script[^>]+type="text/json"[^>]*>\s*({[^>]+})', webpage, 'json'),
            display_id)

        title = json_data['title']
        formats = []

        video_id = None

        for key in ('file', 'audio', 'video', 'high_res_video'):
            media_url = json_data.get(key, '')
            if not media_url:
                continue
            media_url = re.sub(r'\?.*', '', compat_urlparse.urljoin(url, media_url))
            video_id = video_id or remove_start(os.path.splitext(url_basename(media_url))[0], 'dn')
            formats.append({
                'url': media_url,
                'vcodec': 'none' if key == 'audio' else None,
            })

        self._sort_formats(formats)

        default_lang = 'en'
        subtitles = {}

        def add_subtitle_item(lang, info_dict):
            if lang not in subtitles:
                subtitles[lang] = []
            subtitles[lang].append(info_dict)

        # chapter_file are not subtitles
        if 'caption_file' in json_data:
            add_subtitle_item(default_lang, {
                'url': compat_urlparse.urljoin(url, json_data['caption_file']),
            })

        for subtitle_item in json_data.get('captions', []):
            lang = subtitle_item.get('language', '').lower() or default_lang
            add_subtitle_item(lang, {
                'url': compat_urlparse.urljoin(url, subtitle_item['url']),
            })

        description = self._og_search_description(webpage, default=None)

        return {
            'id': video_id or display_id,
            'title': title,
            'description': description,
            'thumbnail': json_data.get('image'),
            'subtitles': subtitles,
            'formats': formats,
        }


code:
from .common import InfoExtractor
from ..utils import merge_dicts, str_or_none

class Detik20IE(InfoExtractor):
    IE_NAME = '20.detik.com'
    _VALID_URL = r'https?://20\.detik\.com/((?!program)[\w-]+)/[\d-]+/(?P<id>[\w-]+)'
    _TESTS = [{
        # detikflash
        'url': 'https://20.detik.com/detikflash/20220705-220705098/zulhas-klaim-sukses-turunkan-harga-migor-jawa-bali',
        'info_dict': {
            'id': '220705098',
            'ext': 'mp4',
            'duration': 157,
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/07/05/bfe0384db04f4bbb9dd5efc869c5d4b1-20220705164334-0s.jpg?w=650&q=80',
            'description': 'md5:ac18dcee5b107abbec1ed46e0bf400e3',
            'title': 'Zulhas Klaim Sukses Turunkan Harga Migor Jawa-Bali',
            'tags': ['zulkifli hasan', 'menteri perdagangan', 'minyak goreng'],
            'timestamp': 1657039548,
            'upload_date': '20220705'
        }
    }, {
        # e-flash
        'url': 'https://20.detik.com/e-flash/20220705-220705109/ahli-level-ppkm-jadi-payung-strategi-protokol-kesehatan',
        'info_dict': {
            'id': '220705109',
            'ext': 'mp4',
            'tags': ['ppkm jabodetabek', 'dicky budiman', 'ppkm'],
            'upload_date': '20220705',
            'duration': 110,
            'title': 'Ahli: Level PPKM Jadi Payung Strategi Protokol Kesehatan',
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/07/05/Ahli-_Level_PPKM_Jadi_Payung_Strat_jOgUMCN-20220705182313-custom.jpg?w=650&q=80',
            'description': 'md5:4eb825a9842e6bdfefd66f47b364314a',
            'timestamp': 1657045255,
        }
    }, {
        # otobuzz
        'url': 'https://20.detik.com/otobuzz/20220704-220704093/mulai-rp-10-jutaan-ini-skema-kredit-mitsubishi-pajero-sport',
        'info_dict': {
            'id': '220704093',
            'ext': 'mp4',
            'tags': ['cicilan mobil', 'mitsubishi pajero sport', 'mitsubishi', 'pajero sport'],
            'timestamp': 1656951521,
            'duration': 83,
            'upload_date': '20220704',
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/07/04/5d6187e402ec4a91877755a5886ff5b6-20220704161859-0s.jpg?w=650&q=80',
            'description': 'md5:9b2257341b6f375cdcf90106146d5ffb',
            'title': 'Mulai Rp 10 Jutaan! Ini Skema Kredit Mitsubishi Pajero Sport',
        }
    }, {
        # sport-buzz
        'url': 'https://20.detik.com/sport-buzz/20220704-220704054/crash-crash-horor-di-paruh-pertama-motogp-2022',
        'info_dict': {
            'id': '220704054',
            'ext': 'mp4',
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/07/04/6b172c6fb564411996ea145128315630-20220704090746-0s.jpg?w=650&q=80',
            'title': 'Crash-crash Horor di Paruh Pertama MotoGP 2022',
            'description': 'md5:fbcc6687572ad7d16eb521b76daa50e4',
            'timestamp': 1656925591,
            'duration': 107,
            'tags': ['marc marquez', 'fabio quartararo', 'francesco bagnaia', 'motogp crash', 'motogp 2022'],
            'upload_date': '20220704',
        }
    }, {
        # adu-perspektif
        'url': 'https://20.detik.com/adu-perspektif/20220518-220518144/24-tahun-reformasi-dan-alarm-demokrasi-dari-filipina',
        'info_dict': {
            'id': '220518144',
            'ext': 'mp4',
            'title': '24 Tahun Reformasi dan Alarm Demokrasi dari Filipina',
            'upload_date': '20220518',
            'timestamp': 1652913823,
            'duration': 185.0,
            'tags': ['politik', 'adu perspektif', 'indonesia', 'filipina', 'demokrasi'],
            'description': 'md5:8eaaf440b839c3d02dca8c9bbbb099a9',
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/05/18/adpers_18_mei_compressed-20220518230458-custom.jpg?w=650&q=80',
        }
    }, {
        # sosok
        'url': 'https://20.detik.com/sosok/20220702-220703032/resa-boenard-si-princess-bantar-gebang',
        'info_dict': {
            'id': '220703032',
            'ext': 'mp4',
            'timestamp': 1656824438,
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/07/02/SOSOK_BGBJ-20220702191138-custom.jpg?w=650&q=80',
            'title': 'Resa Boenard Si \'Princess Bantar Gebang\'',
            'description': 'md5:84ea66306a0285330de6a13fc6218b78',
            'tags': ['sosok', 'sosok20d', 'bantar gebang', 'bgbj', 'resa boenard', 'bantar gebang bgbj', 'bgbj bantar gebang', 'sosok bantar gebang', 'sosok bgbj', 'bgbj resa boenard'],
            'upload_date': '20220703',
            'duration': 650,
        }
    }, {
        # viral
        'url': 'https://20.detik.com/viral/20220603-220603135/merasakan-bus-imut-tanpa-pengemudi-muter-muter-di-kawasan-bsd-city',
        'info_dict': {
            'id': '220603135',
            'ext': 'mp4',
            'description': 'md5:4771fe101aa303edb829c59c26f9e7c6',
            'timestamp': 1654304305,
            'title': 'Merasakan Bus Imut Tanpa Pengemudi, Muter-muter di Kawasan BSD City',
            'tags': ['viral', 'autonomous vehicle', 'electric', 'shuttle bus'],
            'thumbnail': 'https://cdnv.detik.com/videoservice/AdminTV/2022/06/03/VIRAL_BUS_NO_SUPIR-20220604004707-custom.jpg?w=650&q=80',
            'duration': 593,
            'upload_date': '20220604',
        }
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        json_ld_data = self._search_json_ld(webpage, display_id)

        video_url = self._html_search_regex(
            r'videoUrl\s*:\s*"(?P<video_url>[^"]+)', webpage, 'videoUrl')
        formats, subtitles = self._extract_m3u8_formats_and_subtitles(video_url, display_id, ext='mp4')

        return merge_dicts(json_ld_data, {
            'id': self._html_search_meta('video_id', webpage),
            'formats': formats,
            'subtitles': subtitles,
            'tags': str_or_none(self._html_search_meta(['keywords', 'keyword', 'dtk:keywords'], webpage), '').split(','),
        })


code:
from .common import InfoExtractor
from ..utils import unified_strdate

class DFBIE(InfoExtractor):
    IE_NAME = 'tv.dfb.de'
    _VALID_URL = r'https?://tv\.dfb\.de/video/(?P<display_id>[^/]+)/(?P<id>\d+)'

    _TEST = {
        'url': 'http://tv.dfb.de/video/u-19-em-stimmen-zum-spiel-gegen-russland/11633/',
        'md5': 'ac0f98a52a330f700b4b3034ad240649',
        'info_dict': {
            'id': '11633',
            'display_id': 'u-19-em-stimmen-zum-spiel-gegen-russland',
            'ext': 'mp4',
            'title': 'U 19-EM: Stimmen zum Spiel gegen Russland',
            'upload_date': '20150714',
        },
    }

    def _real_extract(self, url):
        display_id, video_id = self._match_valid_url(url).groups()

        player_info = self._download_xml(
            'http://tv.dfb.de/server/hd_video.php?play=%s' % video_id,
            display_id)
        video_info = player_info.find('video')
        stream_access_url = self._proto_relative_url(video_info.find('url').text.strip())

        formats = []
        # see http://tv.dfb.de/player/js/ajax.js for the method to extract m3u8 formats
        for sa_url in (stream_access_url, stream_access_url + '&area=&format=iphone'):
            stream_access_info = self._download_xml(sa_url, display_id)
            token_el = stream_access_info.find('token')
            manifest_url = token_el.attrib['url'] + '?' + 'hdnea=' + token_el.attrib['auth']
            if '.f4m' in manifest_url:
                formats.extend(self._extract_f4m_formats(
                    manifest_url + '&hdcore=3.2.0',
                    display_id, f4m_id='hds', fatal=False))
            else:
                formats.extend(self._extract_m3u8_formats(
                    manifest_url, display_id, 'mp4',
                    'm3u8_native', m3u8_id='hls', fatal=False))
        self._sort_formats(formats)

        return {
            'id': video_id,
            'display_id': display_id,
            'title': video_info.find('title').text,
            'thumbnail': 'http://tv.dfb.de/images/%s_640x360.jpg' % video_id,
            'upload_date': unified_strdate(video_info.find('time_date').text),
            'formats': formats,
        }


code:
from .common import InfoExtractor
from ..utils import parse_duration

class DHMIE(InfoExtractor):
    IE_DESC = 'Filmarchiv - Deutsches Historisches Museum'
    _VALID_URL = r'https?://(?:www\.)?dhm\.de/filmarchiv/(?:[^/]+/)+(?P<id>[^/]+)'

    _TESTS = [{
        'url': 'http://www.dhm.de/filmarchiv/die-filme/the-marshallplan-at-work-in-west-germany/',
        'md5': '11c475f670209bf6acca0b2b7ef51827',
        'info_dict': {
            'id': 'the-marshallplan-at-work-in-west-germany',
            'ext': 'flv',
            'title': 'MARSHALL PLAN AT WORK IN WESTERN GERMANY, THE',
            'description': 'md5:1fabd480c153f97b07add61c44407c82',
            'duration': 660,
            'thumbnail': r're:^https?://.*\.jpg$',
        },
    }, {
        'url': 'http://www.dhm.de/filmarchiv/02-mapping-the-wall/peter-g/rolle-1/',
        'md5': '09890226332476a3e3f6f2cb74734aa5',
        'info_dict': {
            'id': 'rolle-1',
            'ext': 'flv',
            'title': 'ROLLE 1',
            'thumbnail': r're:^https?://.*\.jpg$',
        },
    }]

    def _real_extract(self, url):
        playlist_id = self._match_id(url)

        webpage = self._download_webpage(url, playlist_id)

        playlist_url = self._search_regex(
            r"file\s*:\s*'([^']+)'", webpage, 'playlist url')

        entries = self._extract_xspf_playlist(playlist_url, playlist_id)

        title = self._search_regex(
            [r'dc:title="([^"]+)"', r'<title> »([^<]+)</title>'],
            webpage, 'title').strip()
        description = self._html_search_regex(
            r'
Description:(.+?)
',
            webpage, 'description', default=None)
        duration = parse_duration(self._search_regex(
            r'Length\s*\s*:\s*([^<]+)',
            webpage, 'duration', default=None))

        entries[0].update({
            'title': title,
            'description': description,
            'duration': duration,
        })

        return self.playlist_result(entries, playlist_id)


code:
from .common import InfoExtractor
from ..utils import js_to_json

class DiggIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?digg\.com/video/(?P<id>[^/?#&]+)'
    _TESTS = [{
        # JWPlatform via provider
        'url': 'http://digg.com/video/sci-fi-short-jonah-daniel-kaluuya-get-out',
        'info_dict': {
            'id': 'LcqvmS0b',
            'ext': 'mp4',
            'title': "'Get Out' Star Daniel Kaluuya Goes On 'Moby Dick'-Like Journey In Sci-Fi Short 'Jonah'",
            'description': 'md5:541bb847648b6ee3d6514bc84b82efda',
            'upload_date': '20180109',
            'timestamp': 1515530551,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # Youtube via provider
        'url': 'http://digg.com/video/dog-boat-seal-play',
        'only_matching': True,
    }, {
        # vimeo as regular embed
        'url': 'http://digg.com/video/dream-girl-short-film',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)

        webpage = self._download_webpage(url, display_id)

        info = self._parse_json(
            self._search_regex(
                r'(?s)video_info\s*=\s*({.+?});\n', webpage, 'video info',
                default='{}'), display_id, transform_source=js_to_json,
            fatal=False)

        video_id = info.get('video_id')

        if video_id:
            provider = info.get('provider_name')
            if provider == 'youtube':
                return self.url_result(
                    video_id, ie='Youtube', video_id=video_id)
            elif provider == 'jwplayer':
                return self.url_result(
                    'jwplatform:%s' % video_id, ie='JWPlatform',
                    video_id=video_id)

        return self.url_result(url, 'Generic')


code:
from .common import InfoExtractor

from ..utils import (
    ExtractorError,
    parse_resolution,
    traverse_obj,
    try_get,
    urlencode_postdata,
)

class DigitalConcertHallIE(InfoExtractor):
    IE_DESC = 'DigitalConcertHall extractor'
    _VALID_URL = r'https?://(?:www\.)?digitalconcerthall\.com/(?P<language>[a-z]+)/concert/(?P<id>[0-9]+)'
    _OAUTH_URL = 'https://api.digitalconcerthall.com/v2/oauth2/token'
    _ACCESS_TOKEN = None
    _NETRC_MACHINE = 'digitalconcerthall'
    _TESTS = [{
        'note': 'Playlist with only one video',
        'url': 'https://www.digitalconcerthall.com/en/concert/53201',
        'info_dict': {
            'id': '53201-1',
            'ext': 'mp4',
            'composer': 'Kurt Weill',
            'title': '[Magic Night]',
            'thumbnail': r're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
            'upload_date': '20210624',
            'timestamp': 1624548600,
            'duration': 2798,
            'album_artist': 'Members of the Berliner Philharmoniker / Simon Rössler',
        },
        'params': {'skip_download': 'm3u8'},
    }, {
        'note': 'Concert with several works and an interview',
        'url': 'https://www.digitalconcerthall.com/en/concert/53785',
        'info_dict': {
            'id': '53785',
            'album_artist': 'Berliner Philharmoniker / Kirill Petrenko',
            'title': 'Kirill Petrenko conducts Mendelssohn and Shostakovich',
        },
        'params': {'skip_download': 'm3u8'},
        'playlist_count': 3,
    }]

    def _perform_login(self, username, password):
        token_response = self._download_json(
            self._OAUTH_URL,
            None, 'Obtaining token', errnote='Unable to obtain token', data=urlencode_postdata({
                'affiliate': 'none',
                'grant_type': 'device',
                'device_vendor': 'unknown',
                'app_id': 'dch.webapp',
                'app_version': '1.0.0',
                'client_secret': '2ySLN+2Fwb',
            }), headers={
                'Content-Type': 'application/x-www-form-urlencoded',
            })
        self._ACCESS_TOKEN = token_response['access_token']
        try:
            self._download_json(
                self._OAUTH_URL,
                None, note='Logging in', errnote='Unable to login', data=urlencode_postdata({
                    'grant_type': 'password',
                    'username': username,
                    'password': password,
                }), headers={
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Referer': 'https://www.digitalconcerthall.com',
                    'Authorization': f'Bearer {self._ACCESS_TOKEN}'
                })
        except ExtractorError:
            self.raise_login_required(msg='Login info incorrect')

    def _real_initialize(self):
        if not self._ACCESS_TOKEN:
            self.raise_login_required(method='password')

    def _entries(self, items, language, **kwargs):
        for item in items:
            video_id = item['id']
            stream_info = self._download_json(
                self._proto_relative_url(item['_links']['streams']['href']), video_id, headers={
                    'Accept': 'application/json',
                    'Authorization': f'Bearer {self._ACCESS_TOKEN}',
                    'Accept-Language': language
                })

            m3u8_url = traverse_obj(
                stream_info, ('channel', lambda k, _: k.startswith('vod_mixed'), 'stream', 0, 'url'), get_all=False)
            formats = self._extract_m3u8_formats(m3u8_url, video_id, 'mp4', 'm3u8_native', fatal=False)
            self._sort_formats(formats)

            yield {
                'id': video_id,
                'title': item.get('title'),
                'composer': item.get('name_composer'),
                'url': m3u8_url,
                'formats': formats,
                'duration': item.get('duration_total'),
                'timestamp': traverse_obj(item, ('date', 'published')),
                'description': item.get('short_description') or stream_info.get('short_description'),
                **kwargs,
                'chapters': [{
                    'start_time': chapter.get('time'),
                    'end_time': try_get(chapter, lambda x: x['time'] + x['duration']),
                    'title': chapter.get('text'),
                } for chapter in item['cuepoints']] if item.get('cuepoints') else None,
            }

    def _real_extract(self, url):
        language, video_id = self._match_valid_url(url).group('language', 'id')
        if not language:
            language = 'en'

        thumbnail_url = self._html_search_regex(
            r'(https?://images\.digitalconcerthall\.com/cms/thumbnails/.*\.jpg)',
            self._download_webpage(url, video_id), 'thumbnail')
        thumbnails = [{
            'url': thumbnail_url,
            **parse_resolution(thumbnail_url)
        }]

        vid_info = self._download_json(
            f'https://api.digitalconcerthall.com/v2/concert/{video_id}', video_id, headers={
                'Accept': 'application/json',
                'Accept-Language': language
            })
        album_artist = ' / '.join(traverse_obj(vid_info, ('_links', 'artist', ..., 'name')) or '')

        return {
            '_type': 'playlist',
            'id': video_id,
            'title': vid_info.get('title'),
            'entries': self._entries(traverse_obj(vid_info, ('_embedded', ..., ...)), language,
                                     thumbnails=thumbnails, album_artist=album_artist),
            'thumbnails': thumbnails,
            'album_artist': album_artist,
        }