From 5d7516373849cc2c62dff11a63257fb7e44abe9b Mon Sep 17 00:00:00 2001 From: bashonly Date: Tue, 1 Apr 2025 16:53:21 -0500 Subject: [PATCH] Apply suggestions from code review Authored by: bashonly --- yt_dlp/extractor/manyvids.py | 120 ++++++++++++++--------------------- 1 file changed, 49 insertions(+), 71 deletions(-) diff --git a/yt_dlp/extractor/manyvids.py b/yt_dlp/extractor/manyvids.py index 661e5e8cc1..1356169bfd 100644 --- a/yt_dlp/extractor/manyvids.py +++ b/yt_dlp/extractor/manyvids.py @@ -1,40 +1,29 @@ from .common import InfoExtractor -from .. import traverse_obj from ..utils import ( + clean_html, determine_ext, int_or_none, + join_nonempty, parse_count, parse_duration, parse_iso8601, url_or_none, ) +from ..utils.traversal import traverse_obj class ManyVidsIE(InfoExtractor): _VALID_URL = r'(?i)https?://(?:www\.)?manyvids\.com/video/(?P\d+)' _TESTS = [{ - # Dead preview video - 'skip': True, - 'url': 'https://www.manyvids.com/Video/133957/everthing-about-me/', - 'md5': '03f11bb21c52dd12a05be21a5c7dcc97', - 'info_dict': { - 'id': '133957', - 'ext': 'mp4', - 'title': 'everthing about me (Preview)', - 'uploader': 'ellyxxix', - 'view_count': int, - 'like_count': int, - }, - }, { # preview video 'url': 'https://www.manyvids.com/Video/530341/mv-tips-tricks', 'md5': '738dc723f7735ee9602f7ea352a6d058', 'info_dict': { - 'id': '530341', + 'id': '530341-preview', 'ext': 'mp4', - 'title': 'MV Tips & Tricks (Preview)', - 'description': 'md5:c3bae98c0f9453237c28b0f8795d9f83', - 'thumbnail': 'https://cdn5.manyvids.com/php_uploads/video_images/DestinyDiaz/thumbs/thumb_Hs26ATOO7fcZaI9sx3XT_screenshot_001.jpg', + 'title': 'MV Tips & Tricks (Preview)', + 'description': r're:I will take you on a tour around .{1313}$', + 'thumbnail': r're:https://cdn5\.manyvids\.com/php_uploads/video_images/DestinyDiaz/.+\.jpg', 'uploader': 'DestinyDiaz', 'view_count': int, 'like_count': int, @@ -43,6 +32,7 @@ class ManyVidsIE(InfoExtractor): 'release_date': '20171019', 'duration': 3167.0, }, + 'expected_warnings': ['Only extracting preview'], }, { # full video 'url': 'https://www.manyvids.com/Video/935718/MY-FACE-REVEAL/', @@ -51,8 +41,8 @@ class ManyVidsIE(InfoExtractor): 'id': '935718', 'ext': 'mp4', 'title': 'MY FACE REVEAL', - 'description': 'md5:ec5901d41808b3746fed90face161612', - 'thumbnail': 'https://ods.manyvids.com/1001061960/3aa5397f2a723ec4597e344df66ab845/screenshots/thumbs/custom_1_180_5be09c1dcce03.jpg', + 'description': r're:Today is the day!! I am finally taking off my mask .{445}$', + 'thumbnail': r're:https://ods\.manyvids\.com/1001061960/3aa5397f2a723ec4597e344df66ab845/screenshots/.+\.jpg', 'uploader': 'Sarah Calanthe', 'view_count': int, 'like_count': int, @@ -62,69 +52,57 @@ class ManyVidsIE(InfoExtractor): 'duration': 224.0, }, }] + _API_BASE = 'https://www.manyvids.com/bff/store/video' def _real_extract(self, url): video_id = self._match_id(url) + video_data = self._download_json(f'{self._API_BASE}/{video_id}/private', video_id)['data'] + formats, preview_only = [], True - info = traverse_obj( - self._download_json(f'https://www.manyvids.com/bff/store/video/{video_id}', video_id), - ('data', {dict})) or {} + for format_id, path in [ + ('preview', ['teaser', 'filepath']), + ('transcoded', ['transcodedFilepath']), + ('filepath', ['filepath']), + ]: + format_url = traverse_obj(video_data, (*path, {url_or_none})) + if not format_url: + continue + if determine_ext(format_url) == 'm3u8': + formats.extend(self._extract_m3u8_formats(format_url, video_id, 'mp4', m3u8_id=format_id)) + else: + formats.append({ + 'url': format_url, + 'format_id': format_id, + 'preference': -10 if format_id == 'preview' else None, + 'quality': 10 if format_id == 'filepath' else None, + 'height': int_or_none( + self._search_regex(r'_(\d{2,3}[02468])_', format_url, 'height', default=None)), + }) + if format_id != 'preview': + preview_only = False - video_urls = self._download_json(f'https://www.manyvids.com/bff/store/video/{video_id}/private', video_id)[ - 'data'] + metadata = traverse_obj( + self._download_json(f'{self._API_BASE}/{video_id}', video_id, fatal=False), 'data') + title = traverse_obj(metadata, ('title', {clean_html})) - video_urls_and_ids = ( - (traverse_obj(video_urls, ('teaser', 'filepath')), 'preview'), - (video_urls.get('transcodedFilepath'), 'transcoded'), - (video_urls.get('filepath'), 'filepath'), - ) - - title = traverse_obj(info, 'title') - - # If the video formats JSON only contains a teaser object, then it is a preview - if video_urls.get('teaser') and not video_urls.get('filepath'): - title += ' (Preview)' + if preview_only: + title = join_nonempty(title, '(Preview)', delim=' ') video_id += '-preview' self.report_warning( f'Only extracting preview. Video may be paid or subscription only. {self._login_hint()}') - formats = [] - for v_url, fmt in video_urls_and_ids: - v_url = url_or_none(v_url) - if not v_url: - continue - if determine_ext(v_url) == 'm3u8': - formats.extend(self._extract_m3u8_formats( - v_url, video_id, 'mp4', entry_protocol='m3u8_native', - m3u8_id='hls')) - else: - formats.append({ - 'url': v_url, - 'format_id': fmt, - }) - - self._remove_duplicate_formats(formats) - - for f in formats: - if f.get('height') is None: - f['height'] = int_or_none( - self._search_regex(r'_(\d{2,3}[02468])_', f['url'], 'video height', default=None)) - if 'preview' in f['format_id']: - f['preference'] = -10 - if 'transcoded' in f['format_id']: - f['preference'] = f.get('preference', -1) - 1 - return { 'id': video_id, 'title': title, 'formats': formats, - 'description': (traverse_obj(info, 'description')), - 'uploader': (traverse_obj(info, ('model', 'displayName'))), - 'thumbnail': ( - url_or_none(traverse_obj(info, 'screenshot')) or url_or_none(traverse_obj(info, 'thumbnail'))), - 'view_count': (parse_count(traverse_obj(info, 'views'))), - 'like_count': (parse_count(traverse_obj(info, 'likes'))), - 'release_timestamp': (parse_iso8601(traverse_obj(info, 'launchDate'))), - 'duration': (parse_duration(traverse_obj(info, 'videoDuration'))), - 'tags': [t.get('label') for t in traverse_obj(info, 'tagList')], + **traverse_obj(metadata, { + 'description': ('description', {clean_html}), + 'uploader': ('model', 'displayName', {clean_html}), + 'thumbnail': (('screenshot', 'thumbnail'), {url_or_none}, any), + 'view_count': ('views', {parse_count}), + 'like_count': ('likes', {parse_count}), + 'release_timestamp': ('launchDate', {parse_iso8601}), + 'duration': ('videoDuration', {parse_duration}), + 'tags': ('tagList', ..., 'label', {str}, filter, all, filter), + }), }