camdemy.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import re
  2. import urllib.parse
  3. from .common import InfoExtractor
  4. from ..utils import (
  5. clean_html,
  6. parse_duration,
  7. str_to_int,
  8. unified_strdate,
  9. )
  10. class CamdemyIE(InfoExtractor):
  11. _VALID_URL = r'https?://(?:www\.)?camdemy\.com/media/(?P<id>\d+)'
  12. _TESTS = [{
  13. # single file
  14. 'url': 'http://www.camdemy.com/media/5181/',
  15. 'md5': '5a5562b6a98b37873119102e052e311b',
  16. 'info_dict': {
  17. 'id': '5181',
  18. 'ext': 'mp4',
  19. 'title': 'Ch1-1 Introduction, Signals (02-23-2012)',
  20. 'thumbnail': r're:^https?://.*\.jpg$',
  21. 'creator': 'ss11spring',
  22. 'duration': 1591,
  23. 'upload_date': '20130114',
  24. 'view_count': int,
  25. },
  26. }, {
  27. # With non-empty description
  28. # webpage returns "No permission or not login"
  29. 'url': 'http://www.camdemy.com/media/13885',
  30. 'md5': '4576a3bb2581f86c61044822adbd1249',
  31. 'info_dict': {
  32. 'id': '13885',
  33. 'ext': 'mp4',
  34. 'title': 'EverCam + Camdemy QuickStart',
  35. 'thumbnail': r're:^https?://.*\.jpg$',
  36. 'description': 'md5:2a9f989c2b153a2342acee579c6e7db6',
  37. 'creator': 'evercam',
  38. 'duration': 318,
  39. },
  40. }, {
  41. # External source (YouTube)
  42. 'url': 'http://www.camdemy.com/media/14842',
  43. 'info_dict': {
  44. 'id': '2vsYQzNIsJo',
  45. 'ext': 'mp4',
  46. 'title': 'Excel 2013 Tutorial - How to add Password Protection',
  47. 'description': 'Excel 2013 Tutorial for Beginners - How to add Password Protection',
  48. 'upload_date': '20130211',
  49. 'uploader': 'Hun Kim',
  50. 'uploader_id': 'hunkimtutorials',
  51. },
  52. 'params': {
  53. 'skip_download': True,
  54. },
  55. }]
  56. def _real_extract(self, url):
  57. video_id = self._match_id(url)
  58. webpage = self._download_webpage(url, video_id)
  59. src_from = self._html_search_regex(
  60. r"class=['\"]srcFrom['\"][^>]*>Sources?(?:\s+from)?\s*:\s*<a[^>]+(?:href|title)=(['\"])(?P<url>(?:(?!\1).)+)\1",
  61. webpage, 'external source', default=None, group='url')
  62. if src_from:
  63. return self.url_result(src_from)
  64. oembed_obj = self._download_json(
  65. 'http://www.camdemy.com/oembed/?format=json&url=' + url, video_id)
  66. title = oembed_obj['title']
  67. thumb_url = oembed_obj['thumbnail_url']
  68. video_folder = urllib.parse.urljoin(thumb_url, 'video/')
  69. file_list_doc = self._download_xml(
  70. urllib.parse.urljoin(video_folder, 'fileList.xml'),
  71. video_id, 'Downloading filelist XML')
  72. file_name = file_list_doc.find('./video/item/fileName').text
  73. video_url = urllib.parse.urljoin(video_folder, file_name)
  74. # Some URLs return "No permission or not login" in a webpage despite being
  75. # freely available via oembed JSON URL (e.g. http://www.camdemy.com/media/13885)
  76. upload_date = unified_strdate(self._search_regex(
  77. r'>published on ([^<]+)<', webpage,
  78. 'upload date', default=None))
  79. view_count = str_to_int(self._search_regex(
  80. r'role=["\']viewCnt["\'][^>]*>([\d,.]+) views',
  81. webpage, 'view count', default=None))
  82. description = self._html_search_meta(
  83. 'description', webpage, default=None) or clean_html(
  84. oembed_obj.get('description'))
  85. return {
  86. 'id': video_id,
  87. 'url': video_url,
  88. 'title': title,
  89. 'thumbnail': thumb_url,
  90. 'description': description,
  91. 'creator': oembed_obj.get('author_name'),
  92. 'duration': parse_duration(oembed_obj.get('duration')),
  93. 'upload_date': upload_date,
  94. 'view_count': view_count,
  95. }
  96. class CamdemyFolderIE(InfoExtractor):
  97. _VALID_URL = r'https?://(?:www\.)?camdemy\.com/folder/(?P<id>\d+)'
  98. _TESTS = [{
  99. # links with trailing slash
  100. 'url': 'http://www.camdemy.com/folder/450',
  101. 'info_dict': {
  102. 'id': '450',
  103. 'title': '信號與系統 2012 & 2011 (Signals and Systems)',
  104. },
  105. 'playlist_mincount': 145,
  106. }, {
  107. # links without trailing slash
  108. # and multi-page
  109. 'url': 'http://www.camdemy.com/folder/853',
  110. 'info_dict': {
  111. 'id': '853',
  112. 'title': '科學計算 - 使用 Matlab',
  113. },
  114. 'playlist_mincount': 20,
  115. }, {
  116. # with displayMode parameter. For testing the codes to add parameters
  117. 'url': 'http://www.camdemy.com/folder/853/?displayMode=defaultOrderByOrg',
  118. 'info_dict': {
  119. 'id': '853',
  120. 'title': '科學計算 - 使用 Matlab',
  121. },
  122. 'playlist_mincount': 20,
  123. }]
  124. def _real_extract(self, url):
  125. folder_id = self._match_id(url)
  126. # Add displayMode=list so that all links are displayed in a single page
  127. parsed_url = list(urllib.parse.urlparse(url))
  128. query = dict(urllib.parse.parse_qsl(parsed_url[4]))
  129. query.update({'displayMode': 'list'})
  130. parsed_url[4] = urllib.parse.urlencode(query)
  131. final_url = urllib.parse.urlunparse(parsed_url)
  132. page = self._download_webpage(final_url, folder_id)
  133. matches = re.findall(r"href='(/media/\d+/?)'", page)
  134. entries = [self.url_result('http://www.camdemy.com' + media_path)
  135. for media_path in matches]
  136. folder_title = self._html_search_meta('keywords', page)
  137. return self.playlist_result(entries, folder_id, folder_title)