sbscokr.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. from .common import InfoExtractor
  2. from ..utils import (
  3. clean_html,
  4. int_or_none,
  5. parse_iso8601,
  6. parse_resolution,
  7. url_or_none,
  8. )
  9. from ..utils.traversal import traverse_obj
  10. class SBSCoKrIE(InfoExtractor):
  11. IE_NAME = 'sbs.co.kr'
  12. _VALID_URL = [r'https?://allvod\.sbs\.co\.kr/allvod/vod(?:Package)?EndPage\.do\?(?:[^#]+&)?mdaId=(?P<id>\d+)',
  13. r'https?://programs\.sbs\.co\.kr/(?:enter|drama|culture|sports|plus|mtv|kth)/[a-z0-9]+/(?:vod|clip|movie)/\d+/(?P<id>(?:OC)?\d+)']
  14. _TESTS = [{
  15. 'url': 'https://programs.sbs.co.kr/enter/dongsang2/clip/52007/OC467706746?div=main_pop_clip',
  16. 'md5': 'c3f6d45e1fb5682039d94cda23c36f19',
  17. 'info_dict': {
  18. 'id': 'OC467706746',
  19. 'ext': 'mp4',
  20. 'title': '‘아슬아슬’ 박군♥한영의 새 집 인테리어 대첩♨',
  21. 'description': 'md5:6a71eb1979ee4a94ea380310068ccab4',
  22. 'thumbnail': 'https://img2.sbs.co.kr/ops_clip_img/2023/10/10/34c4c0f9-a9a5-4ff6-a92e-9bb4b5f6fa65915w1280.jpg',
  23. 'release_timestamp': 1696889400,
  24. 'release_date': '20231009',
  25. 'view_count': int,
  26. 'like_count': int,
  27. 'duration': 238,
  28. 'age_limit': 15,
  29. 'series': '동상이몽2_너는 내 운명',
  30. 'episode': '레이디제인, ‘혼전임신설’ ‘3개월’ 앞당긴 결혼식 비하인드 스토리 최초 공개!',
  31. 'episode_number': 311,
  32. },
  33. }, {
  34. 'url': 'https://allvod.sbs.co.kr/allvod/vodPackageEndPage.do?mdaId=22000489324&combiId=PA000000284&packageType=A&isFreeYN=',
  35. 'md5': 'bf46b2e89fda7ae7de01f5743cef7236',
  36. 'info_dict': {
  37. 'id': '22000489324',
  38. 'ext': 'mp4',
  39. 'title': '[다시보기] 트롤리 15회',
  40. 'description': 'md5:0e55d74bef1ac55c61ae90c73ac485f4',
  41. 'thumbnail': 'https://img2.sbs.co.kr/img/sbs_cms/WE/2023/02/14/arC1676333794938-1280-720.jpg',
  42. 'release_timestamp': 1676325600,
  43. 'release_date': '20230213',
  44. 'view_count': int,
  45. 'like_count': int,
  46. 'duration': 5931,
  47. 'age_limit': 15,
  48. 'series': '트롤리',
  49. 'episode': '이거 다 거짓말이야',
  50. 'episode_number': 15,
  51. },
  52. }, {
  53. 'url': 'https://programs.sbs.co.kr/enter/fourman/vod/69625/22000508948',
  54. 'md5': '41e8ae4cc6c8424f4e4d76661a4becbf',
  55. 'info_dict': {
  56. 'id': '22000508948',
  57. 'ext': 'mp4',
  58. 'title': '[다시보기] 신발 벗고 돌싱포맨 104회',
  59. 'description': 'md5:c6a247383c4dd661e4b956bf4d3b586e',
  60. 'thumbnail': 'https://img2.sbs.co.kr/img/sbs_cms/WE/2023/08/30/2vb1693355446261-1280-720.jpg',
  61. 'release_timestamp': 1693342800,
  62. 'release_date': '20230829',
  63. 'view_count': int,
  64. 'like_count': int,
  65. 'duration': 7036,
  66. 'age_limit': 15,
  67. 'series': '신발 벗고 돌싱포맨',
  68. 'episode': '돌싱포맨 저격수들 등장!',
  69. 'episode_number': 104,
  70. },
  71. }]
  72. def _call_api(self, video_id, rscuse=''):
  73. return self._download_json(
  74. f'https://api.play.sbs.co.kr/1.0/sbs_vodall/{video_id}', video_id,
  75. note=f'Downloading m3u8 information {rscuse}',
  76. query={
  77. 'platform': 'pcweb',
  78. 'protocol': 'download',
  79. 'absolute_show': 'Y',
  80. 'service': 'program',
  81. 'ssl': 'Y',
  82. 'rscuse': rscuse,
  83. })
  84. def _real_extract(self, url):
  85. video_id = self._match_id(url)
  86. details = self._call_api(video_id)
  87. source = traverse_obj(details, ('vod', 'source', 'mediasource', {dict})) or {}
  88. formats = []
  89. for stream in traverse_obj(details, (
  90. 'vod', 'source', 'mediasourcelist', lambda _, v: v['mediaurl'] or v['mediarscuse'],
  91. ), default=[source]):
  92. if not stream.get('mediaurl'):
  93. new_source = traverse_obj(
  94. self._call_api(video_id, rscuse=stream['mediarscuse']),
  95. ('vod', 'source', 'mediasource', {dict})) or {}
  96. if new_source.get('mediarscuse') == source.get('mediarscuse') or not new_source.get('mediaurl'):
  97. continue
  98. stream = new_source
  99. formats.append({
  100. 'url': stream['mediaurl'],
  101. 'format_id': stream.get('mediarscuse'),
  102. 'format_note': stream.get('medianame'),
  103. **parse_resolution(stream.get('quality')),
  104. 'preference': int_or_none(stream.get('mediarscuse')),
  105. })
  106. caption_url = traverse_obj(details, ('vod', 'source', 'subtitle', {url_or_none}))
  107. return {
  108. 'id': video_id,
  109. **traverse_obj(details, ('vod', {
  110. 'title': ('info', 'title'),
  111. 'duration': ('info', 'duration', {int_or_none}),
  112. 'view_count': ('info', 'viewcount', {int_or_none}),
  113. 'like_count': ('info', 'likecount', {int_or_none}),
  114. 'description': ('info', 'synopsis', {clean_html}),
  115. 'episode': ('info', 'content', ('contenttitle', 'title')),
  116. 'episode_number': ('info', 'content', 'number', {int_or_none}),
  117. 'series': ('info', 'program', 'programtitle'),
  118. 'age_limit': ('info', 'targetage', {int_or_none}),
  119. 'release_timestamp': ('info', 'broaddate', {parse_iso8601}),
  120. 'thumbnail': ('source', 'thumbnail', 'origin', {url_or_none}),
  121. }), get_all=False),
  122. 'formats': formats,
  123. 'subtitles': {'ko': [{'url': caption_url}]} if caption_url else None,
  124. }
  125. class SBSCoKrAllvodProgramIE(InfoExtractor):
  126. IE_NAME = 'sbs.co.kr:allvod_program'
  127. _VALID_URL = r'https?://allvod\.sbs\.co\.kr/allvod/vod(?:Free)?ProgramDetail\.do\?(?:[^#]+&)?pgmId=(?P<id>P?\d+)'
  128. _TESTS = [{
  129. 'url': 'https://allvod.sbs.co.kr/allvod/vodFreeProgramDetail.do?type=legend&pgmId=22000010159&listOrder=vodCntAsc',
  130. 'info_dict': {
  131. '_type': 'playlist',
  132. 'id': '22000010159',
  133. },
  134. 'playlist_count': 18,
  135. }, {
  136. 'url': 'https://allvod.sbs.co.kr/allvod/vodProgramDetail.do?pgmId=P460810577',
  137. 'info_dict': {
  138. '_type': 'playlist',
  139. 'id': 'P460810577',
  140. },
  141. 'playlist_count': 13,
  142. }]
  143. def _real_extract(self, url):
  144. program_id = self._match_id(url)
  145. details = self._download_json(
  146. 'https://allvod.sbs.co.kr/allvod/vodProgramDetail/vodProgramDetailAjax.do',
  147. program_id, note='Downloading program details',
  148. query={
  149. 'pgmId': program_id,
  150. 'currentCount': '10000',
  151. })
  152. return self.playlist_result(
  153. [self.url_result(f'https://allvod.sbs.co.kr/allvod/vodEndPage.do?mdaId={video_id}', SBSCoKrIE)
  154. for video_id in traverse_obj(details, ('list', ..., 'mdaId'))], program_id)
  155. class SBSCoKrProgramsVodIE(InfoExtractor):
  156. IE_NAME = 'sbs.co.kr:programs_vod'
  157. _VALID_URL = r'https?://programs\.sbs\.co\.kr/(?:enter|drama|culture|sports|plus|mtv)/(?P<id>[a-z0-9]+)/vods'
  158. _TESTS = [{
  159. 'url': 'https://programs.sbs.co.kr/culture/morningwide/vods/65007',
  160. 'info_dict': {
  161. '_type': 'playlist',
  162. 'id': '00000210215',
  163. },
  164. 'playlist_mincount': 9782,
  165. }, {
  166. 'url': 'https://programs.sbs.co.kr/enter/dongsang2/vods/52006',
  167. 'info_dict': {
  168. '_type': 'playlist',
  169. 'id': '22000010476',
  170. },
  171. 'playlist_mincount': 312,
  172. }]
  173. def _real_extract(self, url):
  174. program_slug = self._match_id(url)
  175. program_id = self._download_json(
  176. f'https://static.apis.sbs.co.kr/program-api/1.0/menu/{program_slug}', program_slug,
  177. note='Downloading program menu data')['program']['programid']
  178. return self.url_result(
  179. f'https://allvod.sbs.co.kr/allvod/vodProgramDetail.do?pgmId={program_id}', SBSCoKrAllvodProgramIE)