Skip to content

Instantly share code, notes, and snippets.

@dot-mike
Created July 9, 2024 19:30
Show Gist options
  • Save dot-mike/ebf485a36d35f2c7938c29575be520f6 to your computer and use it in GitHub Desktop.
Save dot-mike/ebf485a36d35f2c7938c29575be520f6 to your computer and use it in GitHub Desktop.
Yt-DLP plugin for detandreteatret / 23video.com
# coding: utf-8
from __future__ import unicode_literals
import re
# ⚠ Don't use relative imports
from yt_dlp.extractor.common import InfoExtractor
from yt_dlp.utils import int_or_none, try_get, clean_html, unescapeHTML
class TwentyThreeVideoExIE(InfoExtractor):
IE_NAME = '23videoEx'
_VALID_URL = r'https?://(?P<domain>[^.]+\.(?:twentythree\.net|23video\.com))/video/(?P<id>\d+)(?:/[A-Za-z0-9\-_]*)?'
_TESTS = [{
'url': 'https://detandreteatret.23video.com/video/7742017/murder-she-improvised-en-ganske',
'info_dict': {
'id': '7742017',
'ext': 'mp4',
'title': 'Murder, she Improvised - en ganske grundig forklaring',
'upload_date': '20130212',
}
}]
def _real_extract(self, url):
domain, photo_id = self._match_valid_url(url).groups()
base_url = 'https://%s' % domain
photo_data = self._download_json(
base_url + '/api/photo/list', photo_id, query={
'format': 'json',
'photo_id': photo_id
}, transform_source=lambda s: self._search_regex(r'(?s)({.+})', s, 'photo data'))['photo']
title = photo_data['title']
formats = []
audio_path = photo_data.get('audio_download')
if audio_path:
formats.append({
'format_id': 'audio',
'url': base_url + audio_path,
'filesize': int_or_none(photo_data.get('audio_size')),
'vcodec': 'none',
})
def add_common_info_to_list(l, template, id_field, id_value):
f_base = template % id_value
f_path = photo_data.get(f_base + 'download')
if not f_path:
return
l.append({
id_field: id_value,
'url': base_url + f_path,
'width': int_or_none(photo_data.get(f_base + 'width')),
'height': int_or_none(photo_data.get(f_base + 'height')),
'filesize': int_or_none(photo_data.get(f_base + 'size')),
})
for f in ('mobile_high', 'medium', 'hd', '1080p', '4k'):
add_common_info_to_list(formats, 'video_%s_', 'format_id', f)
thumbnails = []
for t in ('quad16', 'quad50', 'quad75', 'quad100', 'small', 'portrait', 'standard', 'medium', 'large', 'original'):
add_common_info_to_list(thumbnails, '%s_', 'id', t)
tags = try_get(photo_data, lambda x: x['tags'], list)
description = clean_html(unescapeHTML(photo_data.get('content_text')))
return {
'id': photo_id,
'title': title,
'description': description,
'timestamp': int_or_none(photo_data.get('creation_date_epoch')),
'duration': int_or_none(photo_data.get('video_length')),
'view_count': int_or_none(photo_data.get('view_count')),
'comment_count': int_or_none(photo_data.get('number_of_comments')),
'uploader_id': photo_data.get('user_id'),
'uploader': photo_data.get('display_name'),
'thumbnails': thumbnails,
'formats': formats,
'tags': tags
}
class TwentyThreeVideoAlbumExIE(InfoExtractor):
IE_NAME = '23VideoAlbumEx'
_VALID_URL = r'https?://(?P<domain>[^.]+\.(?:twentythree\.net|23video\.com))/channel/(?P<id>\d+)(?:/[A-Za-z0-9\-_]*)?'
_TESTS = [{
'url': 'https://detandreteatret.23video.com/channel/7742261/forestillinger',
'info_dict': {
'id': '7742261',
'title': 'Forestillinger'
}
}]
def _real_extract(self, url):
domain, album_id = self._match_valid_url(url).groups()
base_url = 'https://%s' % domain
album_info = self._download_json(
base_url + '/api/album/list', album_id, query={
'format': 'json',
'album_id': album_id
}, transform_source=lambda s: self._search_regex(r'(?s)({.+})', s, 'album data'))['album']
album_data = self._download_json(
base_url + '/api/photo/list', album_id, query={
'format': 'json',
'album_id': album_id,
'size': 100,
}, transform_source=lambda s: self._search_regex(r'(?s)({.+})', s, 'album data'))['photos']
title = album_info['title']
entries = [self.url_result(base_url + item['one']) for item in album_data]
return self.playlist_result(entries, album_id, title)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment