1
0
mirror of https://github.com/yt-dlp/yt-dlp.git synced 2025-07-19 19:58:30 +00:00
This commit is contained in:
gravesducking 2025-07-17 09:06:29 +08:00 committed by GitHub
commit 24f2a5c7ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 142 additions and 0 deletions

View File

@ -1048,6 +1048,7 @@
)
from .livestreamfails import LivestreamfailsIE
from .lnk import LnkIE
from .locipo import LocipoIE
from .loco import LocoIE
from .loom import (
LoomFolderIE,

141
yt_dlp/extractor/locipo.py Normal file
View File

@ -0,0 +1,141 @@
from .common import InfoExtractor
from ..utils import (
int_or_none,
parse_iso8601,
str_or_none,
traverse_obj,
url_or_none,
)
class LocipoIE(InfoExtractor):
IE_DESC = 'Locipo (ロキポ) Video/Playlist'
_VALID_URL = r'https?://locipo\.jp/creative/(?P<creative_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(\?.*list=(?P<playlist_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}))?'
_TESTS = [
{
'url': 'https://locipo.jp/creative/fb5ffeaa-398d-45ce-bb49-0e221b5f94f1',
'info_dict': {
'ext': 'mp4',
'id': 'fb5ffeaa-398d-45ce-bb49-0e221b5f94f1',
'series': 'リアルカレカノ',
'series_id': 'b865b972-99fe-41d5-a72c-8ed5c42132bd',
'duration': 3622,
'title': 'リアルカレカノ#4 ~伊達さゆりと勉強しよっ?~',
'description': 'TVアニメ「ラブライブスーパースター!!」澁谷かのん役などで\n活躍中の人気声優「伊達さゆり」さんと、恋人気分が味わえるコンテンツが登場!\n\n全てカレシ・カジョの1人称目線で撮影しているため\nこの動画でしか味わえない、ドキドキ感が満載!\n一緒に勉強したり…ご飯を食べたり…相談に乗ってもらったり…\nいろんなシチュエーションを楽しんでください!\n',
'uploader': 'thk',
'uploader_id': '1',
'thumbnail': 'https://dophkbxgy39ig.cloudfront.net/store/creatives/99190/large-51fec5367d73fc55dc364250885dfb2e.png',
'timestamp': 1711789200,
'modified_timestamp': 1725415481,
'upload_date': '20240330',
'modified_date': '20240904',
},
},
{
'url': 'https://locipo.jp/creative/8be557b9-5a97-4092-825e-5cb8c72b36ab?list=3058b313-3a7c-4d64-b067-d3d870b4b17d&noautoplay=&redirect=true',
'info_dict': {
'id': '3058b313-3a7c-4d64-b067-d3d870b4b17d',
'title': '達眼の戦士s ',
'description': '今注目のeスポーツで活躍するプロに密着!\n勝利への強いこだわりに迫るドキュメントバラエティ',
},
'playlist_count': 2,
},
{
'url': 'https://locipo.jp/creative/867176a9-cfd8-4807-b5f0-e41a549ba588?list=07738b35-6ce6-48b6-92f7-00167a95bb12',
'info_dict': {
'id': '07738b35-6ce6-48b6-92f7-00167a95bb12',
'title': 'チャント!特集',
},
'playlist_mincount': 30,
},
]
def _get_creative_metadata(self, creative_data):
return traverse_obj(creative_data, {
'id': ('id', {str}),
'duration': ('video', 'duration', {int_or_none}),
'title': ('title', {str}),
'description': ('description', {str}),
'uploader': ('station_cd', {str}),
'uploader_id': ('station_id', {str}),
'thumbnail': ('thumb', {url_or_none}),
'timestamp': ('broadcast_started_at', {parse_iso8601}),
'modified_timestamp': ('updated_at', {parse_iso8601}),
})
def _real_extract(self, url: str):
creative_id, playlist_id = self._match_valid_url(url).group('creative_id', 'playlist_id') # type: ignore
if not playlist_id:
creative_data = self._download_json(
f'https://api.locipo.jp/api/v1/creatives/{creative_id}',
creative_id,
headers={
'accept': 'application/json, text/plain, */*',
'origin': 'https://locipo.jp',
'referer': 'https://locipo.jp/',
},
)
return {
'formats': self._extract_m3u8_formats(m3u8_url=traverse_obj(creative_data, ('video', 'hls', {str})), video_id=creative_id), # type: ignore
'id': creative_id,
**self._get_creative_metadata(creative_data), # type: ignore
**traverse_obj(
creative_data,
{
'series': ('playlist', 'title', {str}),
'series_id': ('playlist', 'id', {str}),
},
), # type: ignore
}
playlist_data = self._download_json(
f'https://api.locipo.jp/api/v1/playlists/{playlist_id}',
playlist_id,
headers={
'accept': 'application/json, text/plain, */*',
'origin': 'https://locipo.jp',
'referer': 'https://locipo.jp/',
},
)
# NOTE: This API can return up to 1000 videos. Since there doesn't seem to be any playlist with more than 1000 items at the moment, pagination is currently not implemented.
playlist_creatives_data = self._download_json(
f'https://api.locipo.jp/api/v1/playlists/{playlist_id}/creatives',
None,
headers={
'accept': 'application/json, text/plain, */*',
'origin': 'https://locipo.jp',
'referer': 'https://locipo.jp/',
},
)
entries = []
for creative in playlist_creatives_data.get('items', []): # type: ignore
entries.append(
{
'formats': self._extract_m3u8_formats(
m3u8_url=traverse_obj(creative, ('video', 'hls', {str})), # type: ignore
video_id=traverse_obj(creative, ('id', {str})), # type: ignore
),
**self._get_creative_metadata(creative), # type: ignore
**traverse_obj(
playlist_data,
{
'series': ('title', {str}),
'series_id': ('id', {str}),
},
), # type: ignore
},
)
return self.playlist_result(
entries=entries,
playlist_id=playlist_id,
playlist_title=traverse_obj(playlist_data, ('title', {str})), # type: ignore
playlist_description=traverse_obj(playlist_data, ('description', {str_or_none})), # type: ignore
)