diff --git a/yt_dlp/extractor/rule34video.py b/yt_dlp/extractor/rule34video.py
index 3630f5e40..bfcd7110e 100644
--- a/yt_dlp/extractor/rule34video.py
+++ b/yt_dlp/extractor/rule34video.py
@@ -9,6 +9,7 @@
get_element_html_by_class,
get_elements_by_class,
int_or_none,
+ merge_dicts,
parse_count,
parse_duration,
unescapeHTML,
@@ -72,45 +73,58 @@ def _real_extract(self, url):
formats = []
- for mobj in re.finditer(r']+href="(?P[^"]+download=true[^"]+)".*>(?P[^\s]+) (?P[^<]+)p', webpage):
- url, ext, quality = mobj.groups()
+ for mobj in re.finditer(r']+href="(?P[^"]+download=true[^"]+)".*>(?P[^\s]+) (?P[^<]+)p', webpage):
+ url, ext, height = mobj.groups()
formats.append({
'url': url,
'ext': ext.lower(),
- 'quality': quality,
+ 'height': int_or_none(height),
})
categories, creators, uploader, uploader_url = [None] * 4
for col in get_elements_by_class('col', webpage):
label = clean_html(get_element_by_class('label', col))
- if label == 'Categories:':
+ if label == 'Categories':
categories = list(map(clean_html, get_elements_by_class('item', col)))
- elif label == 'Artist:':
+ elif label == 'Artist':
creators = list(map(clean_html, get_elements_by_class('item', col)))
- elif label == 'Uploaded By:':
- uploader = clean_html(get_element_by_class('name', col))
- uploader_url = extract_attributes(get_element_html_by_class('name', col) or '').get('href')
+ elif label == 'Uploaded by':
+ uploader_link = get_element_html_by_class('btn_link', col)
+ uploader = clean_html(uploader_link)
+ uploader_url = extract_attributes(uploader_link or '').get('href')
- return {
- **traverse_obj(self._search_json_ld(webpage, video_id, default={}), ({
- 'title': 'title',
- 'view_count': 'view_count',
- 'like_count': 'like_count',
- 'duration': 'duration',
- 'timestamp': 'timestamp',
- 'description': 'description',
- 'thumbnail': ('thumbnails', 0, 'url'),
- })),
+ view_count, duration = [None] * 2
+ for item_info in get_elements_by_class('item_info', webpage):
+ item_info_text = clean_html(item_info)
+ if get_element_by_class('custom-eye', item_info) is not None:
+ precise_view_count = re.search(r'\(([\d,]+)\)', item_info_text)
+ view_count = parse_count(precise_view_count.group(1) if precise_view_count is not None else item_info_text)
+ elif get_element_by_class('custom-time', item_info) is not None:
+ duration = parse_duration(clean_html(item_info))
+
+ like_count = None
+ if like_count_text := re.search(r'\(([\d,]+)\)', get_element_by_class('voters count', webpage)):
+ like_count = parse_count(like_count_text.group(1))
+
+ json_ld = traverse_obj(self._search_json_ld(webpage, video_id, default={}), ({
+ 'title': 'title',
+ 'view_count': 'view_count',
+ 'like_count': 'like_count',
+ 'duration': 'duration',
+ 'timestamp': 'timestamp',
+ 'description': 'description',
+ 'thumbnail': ('thumbnails', 0, 'url'),
+ }))
+
+ return merge_dicts({
'id': video_id,
'formats': formats,
'title': self._html_extract_title(webpage),
'thumbnail': self._html_search_regex(
r'preview_url:\s+\'([^\']+)\'', webpage, 'thumbnail', default=None),
- 'duration': parse_duration(self._html_search_regex(
- r'"icon-clock">\s+((?:\d+:?)+)', webpage, 'duration', default=None)),
- 'view_count': int_or_none(self._html_search_regex(
- r'"icon-eye">\s+([ \d]+)', webpage, 'views', default='').replace(' ', '')),
- 'like_count': parse_count(get_element_by_class('voters count', webpage)),
+ 'duration': duration,
+ 'view_count': view_count,
+ 'like_count': like_count,
'comment_count': int_or_none(self._search_regex(
r'[^(]+\((\d+)\)', get_element_by_attribute('href', '#tab_comments', webpage), 'comment count', fatal=False)),
'age_limit': 18,
@@ -120,4 +134,4 @@ def _real_extract(self, url):
'categories': categories,
'tags': list(map(unescapeHTML, re.findall(
r']+\bhref="https://rule34video\.com/tags/\d+/"[^>]*>(?P[^>]*)', webpage))),
- }
+ }, json_ld)