mirror of
https://github.com/yt-dlp/yt-dlp
synced 2025-01-19 15:23:05 +01:00
BoomPlayGenericPlaylistIE, BoomPlaySearchIE
This commit is contained in:
parent
a8769f672b
commit
6d2de79b7a
2 changed files with 146 additions and 8 deletions
|
@ -282,9 +282,11 @@ from .bokecc import BokeCCIE
|
||||||
from .bongacams import BongaCamsIE
|
from .bongacams import BongaCamsIE
|
||||||
from .boomplay import (
|
from .boomplay import (
|
||||||
BoomPlayEpisodeIE,
|
BoomPlayEpisodeIE,
|
||||||
|
BoomPlayGenericPlaylistIE,
|
||||||
BoomPlayMusicIE,
|
BoomPlayMusicIE,
|
||||||
BoomPlayPlaylistIE,
|
BoomPlayPlaylistIE,
|
||||||
BoomPlayPodcastIE,
|
BoomPlayPodcastIE,
|
||||||
|
BoomPlaySearchIE,
|
||||||
BoomPlayVideoIE,
|
BoomPlayVideoIE,
|
||||||
)
|
)
|
||||||
from .boosty import BoostyIE
|
from .boosty import BoostyIE
|
||||||
|
|
|
@ -2,22 +2,29 @@ import base64
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor, SearchInfoExtractor
|
||||||
from ..aes import aes_cbc_decrypt_bytes, aes_cbc_encrypt_bytes, unpad_pkcs7
|
from ..aes import aes_cbc_decrypt_bytes, aes_cbc_encrypt_bytes, unpad_pkcs7
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
clean_html,
|
clean_html,
|
||||||
|
extract_attributes,
|
||||||
get_element_by_attribute,
|
get_element_by_attribute,
|
||||||
get_element_by_class,
|
get_element_by_class,
|
||||||
get_elements_by_attribute,
|
get_elements_by_attribute,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
merge_dicts,
|
merge_dicts,
|
||||||
|
orderedSet,
|
||||||
|
parse_count,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
strip_or_none,
|
strip_or_none,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
url_or_none,
|
url_or_none,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
|
urljoin,
|
||||||
|
variadic,
|
||||||
)
|
)
|
||||||
from ..utils.traversal import traverse_obj
|
from ..utils.traversal import traverse_obj
|
||||||
|
|
||||||
|
@ -27,6 +34,14 @@ class BoomPlayBaseIE(InfoExtractor):
|
||||||
# Note that the real key/iv differs from `lhx.AESUtils.key`/`lhx.AESUtils.iv`
|
# Note that the real key/iv differs from `lhx.AESUtils.key`/`lhx.AESUtils.iv`
|
||||||
_KEY = b'boomplayVr3xopAM'
|
_KEY = b'boomplayVr3xopAM'
|
||||||
_IV = b'boomplay8xIsKTn9'
|
_IV = b'boomplay8xIsKTn9'
|
||||||
|
_BASE = 'https://www.boomplay.com'
|
||||||
|
_MEDIA_TYPES = ('songs', 'video', 'episode', 'podcasts', 'playlists', 'artists', 'albums')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _urljoin(cls, path):
|
||||||
|
if not hasattr(path, 'startswith') or path.startswith('javascript:'):
|
||||||
|
return None
|
||||||
|
return url_or_none(urljoin(base=cls._BASE, path=path))
|
||||||
|
|
||||||
def _get_playurl(self, item_id, item_type):
|
def _get_playurl(self, item_id, item_type):
|
||||||
resp = self._download_json(
|
resp = self._download_json(
|
||||||
|
@ -49,7 +64,6 @@ class BoomPlayBaseIE(InfoExtractor):
|
||||||
if url := url_or_none(self._get_playurl(_id, item_type)):
|
if url := url_or_none(self._get_playurl(_id, item_type)):
|
||||||
return [{
|
return [{
|
||||||
'format_id': '0',
|
'format_id': '0',
|
||||||
'vcodec': 'none' if item_type == 'MUSIC' else None,
|
|
||||||
'url': url,
|
'url': url,
|
||||||
'http_headers': {
|
'http_headers': {
|
||||||
'Origin': 'https://www.boomplay.com',
|
'Origin': 'https://www.boomplay.com',
|
||||||
|
@ -79,12 +93,12 @@ class BoomPlayBaseIE(InfoExtractor):
|
||||||
metadata_entries.extend(re.findall(r'(?s)<li>(?P<entry>.*?)</li>', details_section) or [])
|
metadata_entries.extend(re.findall(r'(?s)<li>(?P<entry>.*?)</li>', details_section) or [])
|
||||||
page_metadata = {
|
page_metadata = {
|
||||||
'id': _id,
|
'id': _id,
|
||||||
'title': self._html_search_regex(r'<h1>([^<]+)</h1>', metadata_div, 'title', default=''),
|
'title': self._html_search_regex(r'<h1>([^<]+)</h1>', metadata_div, 'title', default=None),
|
||||||
'thumbnail': self._html_search_meta(['og:image', 'twitter:image'],
|
'thumbnail': self._html_search_meta(['og:image', 'twitter:image'],
|
||||||
webpage, 'thumbnail', default=''),
|
webpage, 'thumbnail', default=''),
|
||||||
'like_count': int_or_none(get_element_by_class('btn_favorite', metadata_div)),
|
'like_count': parse_count(get_element_by_class('btn_favorite', metadata_div)),
|
||||||
'repost_count': int_or_none(get_element_by_class('btn_share', metadata_div)),
|
'repost_count': parse_count(get_element_by_class('btn_share', metadata_div)),
|
||||||
'comment_count': int_or_none(get_element_by_class('btn_comment', metadata_div)),
|
'comment_count': parse_count(get_element_by_class('btn_comment', metadata_div)),
|
||||||
'duration': parse_duration(get_element_by_class('btn_duration', metadata_div)),
|
'duration': parse_duration(get_element_by_class('btn_duration', metadata_div)),
|
||||||
'upload_date': unified_strdate(strip_or_none(get_element_by_class('btn_pubDate', metadata_div))),
|
'upload_date': unified_strdate(strip_or_none(get_element_by_class('btn_pubDate', metadata_div))),
|
||||||
'description': description,
|
'description': description,
|
||||||
|
@ -104,7 +118,55 @@ class BoomPlayBaseIE(InfoExtractor):
|
||||||
page_metadata['release_year'] = int_or_none(v)
|
page_metadata['release_year'] = int_or_none(v)
|
||||||
return page_metadata
|
return page_metadata
|
||||||
|
|
||||||
extract = lambda self, url: self.write_debug(json.dumps(a := super().extract(url), indent=2)) or a # rm
|
def _extract_suitable_links(self, webpage, media_types):
|
||||||
|
if not media_types:
|
||||||
|
media_types = self._MEDIA_TYPES
|
||||||
|
media_types = list(variadic(media_types))
|
||||||
|
|
||||||
|
for idx, v in enumerate(media_types):
|
||||||
|
media_types[idx] = re.escape(v) if v in self._MEDIA_TYPES else ''
|
||||||
|
media_types = join_nonempty(*media_types, delim='|')
|
||||||
|
return orderedSet(traverse_obj(re.finditer(
|
||||||
|
rf'''(?x)
|
||||||
|
<a
|
||||||
|
(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?
|
||||||
|
(?<=\s)href\s*=\s*(?P<_q>['"])
|
||||||
|
(?:
|
||||||
|
(?!javascript:)(?P<link>/(?:{media_types})/\d+?)
|
||||||
|
)
|
||||||
|
(?P=_q)
|
||||||
|
(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?
|
||||||
|
''', webpage), (..., 'link', {self._urljoin}, {self.url_result})))
|
||||||
|
|
||||||
|
def _extract_playlist_entries(self, webpage, media_types, warn=True):
|
||||||
|
song_list = strip_or_none(
|
||||||
|
get_element_by_attribute(
|
||||||
|
'class', r'[^\'"]*(?<=[\'"\s])morePart_musics(?=[\'"\s])[^\'"]*', webpage,
|
||||||
|
tag='ol', escape_value=False)
|
||||||
|
or get_element_by_attribute(
|
||||||
|
'class', r'[^\'"]*(?<=[\'"\s])morePart(?=[\'"\s])[^\'"]*', webpage,
|
||||||
|
tag='ol', escape_value=False)
|
||||||
|
or '')
|
||||||
|
|
||||||
|
entries = traverse_obj(re.finditer(
|
||||||
|
r'''(?x)
|
||||||
|
<a
|
||||||
|
(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?
|
||||||
|
(?<=\s)class\s*=\s*(?P<_q>['"])
|
||||||
|
(?:
|
||||||
|
[^\'"]*(?<=[\'"\s])songName(?=[\'"\s])[^\'"]*
|
||||||
|
)
|
||||||
|
(?P=_q)
|
||||||
|
(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?
|
||||||
|
>
|
||||||
|
''', song_list),
|
||||||
|
(..., 0, {extract_attributes}, 'href', {self._urljoin}, {self.url_result}))
|
||||||
|
if not entries:
|
||||||
|
if warn:
|
||||||
|
self.report_warning('Failed to extract playlist entries, finding suitable links instead!')
|
||||||
|
return self._extract_suitable_links(webpage, media_types)
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
class BoomPlayMusicIE(BoomPlayBaseIE):
|
class BoomPlayMusicIE(BoomPlayBaseIE):
|
||||||
|
@ -143,7 +205,7 @@ class BoomPlayMusicIE(BoomPlayBaseIE):
|
||||||
'artists': ('byArtist', ..., 'name'),
|
'artists': ('byArtist', ..., 'name'),
|
||||||
'duration': ('duration', {parse_duration}),
|
'duration': ('duration', {parse_duration}),
|
||||||
}), {
|
}), {
|
||||||
'formats': self._extract_formats(song_id, 'MUSIC'),
|
'formats': self._extract_formats(song_id, 'MUSIC', vcodec='none'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,6 +279,7 @@ class BoomPlayPodcastIE(BoomPlayBaseIE):
|
||||||
'thumbnail': 'https://source.boomplaymusic.com/group10/M00/12/22/6f9cf97ad6f846a0a7882c98dfcf4f8c_320_320.jpg',
|
'thumbnail': 'https://source.boomplaymusic.com/group10/M00/12/22/6f9cf97ad6f846a0a7882c98dfcf4f8c_320_320.jpg',
|
||||||
'repost_count': int,
|
'repost_count': int,
|
||||||
'comment_count': int,
|
'comment_count': int,
|
||||||
|
'like_count': int,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +316,7 @@ class BoomPlayPlaylistIE(BoomPlayBaseIE):
|
||||||
'thumbnail': 'https://source.boomplaymusic.com/group10/M00/08/19/d05d431ee616412caeacd7f78f4f68f5_320_320.jpeg',
|
'thumbnail': 'https://source.boomplaymusic.com/group10/M00/08/19/d05d431ee616412caeacd7f78f4f68f5_320_320.jpeg',
|
||||||
'repost_count': int,
|
'repost_count': int,
|
||||||
'comment_count': int,
|
'comment_count': int,
|
||||||
|
'like_count': int,
|
||||||
'description': 'md5:7ebdffc5137c77acb62acb3c89248445',
|
'description': 'md5:7ebdffc5137c77acb62acb3c89248445',
|
||||||
},
|
},
|
||||||
'playlist_count': 10,
|
'playlist_count': 10,
|
||||||
|
@ -281,3 +345,75 @@ class BoomPlayPlaylistIE(BoomPlayBaseIE):
|
||||||
'artists': ('byArtist', ..., 'name'),
|
'artists': ('byArtist', ..., 'name'),
|
||||||
'channel_url': ('byArtist', 0, '@id'),
|
'channel_url': ('byArtist', 0, '@id'),
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
|
||||||
|
class BoomPlayGenericPlaylistIE(BoomPlayBaseIE):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?boomplay\.com/.+'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.boomplay.com/search/default/Rise%20of%20the%20Fallen%20Heroes',
|
||||||
|
'md5': 'c5fb4f23e6aae98064230ef3c39c2178',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '165481965',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': 'Rise of the Fallen Heroes',
|
||||||
|
'duration': 125.0,
|
||||||
|
'genres': ['Metal'],
|
||||||
|
'artists': ['fatbunny'],
|
||||||
|
'thumbnail': 'https://source.boomplaymusic.com/group10/M00/04/29/375ecda38f6f48179a93c72ab909118f_464_464.jpg',
|
||||||
|
'channel_url': 'https://www.boomplay.com/artists/52723101',
|
||||||
|
'comment_count': int,
|
||||||
|
'repost_count': int,
|
||||||
|
'album': 'Legendary Battle',
|
||||||
|
'release_year': 2024,
|
||||||
|
'like_count': int,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.boomplay.com/search/video/%20Autumn%20blues',
|
||||||
|
'md5': 'd9b67ad333d2292a82922062d065352d',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1154892',
|
||||||
|
'title': 'Autumn blues',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'timestamp': 1728599214,
|
||||||
|
'view_count': int,
|
||||||
|
'thumbnail': 'https://source.boomplaymusic.com/group10/M00/10/10/2171dee9e1f8452e84021560729edb88.jpg',
|
||||||
|
'description': 'Autumn blues by Lugo',
|
||||||
|
'upload_date': '20241010',
|
||||||
|
'duration': 177.0,
|
||||||
|
},
|
||||||
|
'expected_warnings': ['Failed to extract playlist entries, finding suitable links instead!'],
|
||||||
|
'params': {'playlist_items': '1'},
|
||||||
|
}]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
if not any(ie.suitable(url) for ie in (
|
||||||
|
BoomPlayEpisodeIE,
|
||||||
|
BoomPlayMusicIE,
|
||||||
|
BoomPlayPlaylistIE,
|
||||||
|
BoomPlayPodcastIE,
|
||||||
|
BoomPlayVideoIE,
|
||||||
|
)):
|
||||||
|
return super().suitable(url)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
_id = self._generic_id(url)
|
||||||
|
webpage = self._download_webpage(url, _id)
|
||||||
|
# TODO: pass media types based on search types
|
||||||
|
return self.playlist_result(
|
||||||
|
self._extract_playlist_entries(webpage, self._MEDIA_TYPES),
|
||||||
|
**self._extract_page_metadata(webpage, _id))
|
||||||
|
|
||||||
|
|
||||||
|
class BoomPlaySearchIE(SearchInfoExtractor):
|
||||||
|
_SEARCH_KEY = 'boomplaysearch'
|
||||||
|
_RETURN_TYPE = 'url'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'boomplaysearch:rise of the fallen heroes',
|
||||||
|
'only_matching': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _search_results(self, query):
|
||||||
|
yield self.url_result(
|
||||||
|
f'https://www.boomplay.com/search/default/{urllib.parse.quote(query)}')
|
||||||
|
|
Loading…
Add table
Reference in a new issue