PageRenderTime 43ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/googlecl/youtube/service.py

http://googlecl.googlecode.com/
Python | 220 lines | 185 code | 10 blank | 25 comment | 0 complexity | 30bf333d26b1b66d2c81c2789fd2e5f0 MD5 | raw file
  1. # Copyright (C) 2010 Google Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Service details and instances for the YouTube service."""
  15. __author__ = 'tom.h.miller@gmail.com (Tom Miller)'
  16. import atom
  17. import gdata.youtube
  18. import logging
  19. import os
  20. import googlecl.base
  21. import googlecl.service
  22. from googlecl.youtube import SECTION_HEADER
  23. from gdata.youtube.service import YouTubeService
  24. from googlecl import safe_encode
  25. LOG = logging.getLogger(googlecl.youtube.LOGGER_NAME)
  26. class YouTubeServiceCL(YouTubeService, googlecl.service.BaseServiceCL):
  27. """Extends gdata.youtube.service.YouTubeService for the command line.
  28. This class adds some features focused on using YouTube via an installed app
  29. with a command line interface.
  30. """
  31. def __init__(self, config):
  32. """Constructor."""
  33. YouTubeService.__init__(self)
  34. googlecl.service.BaseServiceCL.__init__(self, SECTION_HEADER, config)
  35. def categorize_videos(self, video_entries, category):
  36. """Change the categories of a list of videos to a single category.
  37. If the update fails with a request error, a message is printed to screen.
  38. Usually, valid category strings are the first word of the category as seen
  39. on YouTube (e.g. "Film" for "Film & Animation")
  40. Keyword arguments:
  41. video_entries: List of YouTubeVideoEntry objects.
  42. category: String representation of category.
  43. """
  44. for video in video_entries:
  45. video.media.category = build_category(category)
  46. try:
  47. self.UpdateVideoEntry(video)
  48. except gdata.service.RequestError, err:
  49. if err.args[0]['body'].find('invalid_value') != -1:
  50. LOG.error('Category update failed, ' + category +
  51. ' is not a category.')
  52. else:
  53. raise
  54. CategorizeVideos = categorize_videos
  55. def get_videos(self, user='default', titles=None):
  56. """Get entries for videos uploaded by a user.
  57. Keyword arguments:
  58. user: The user whose videos are being retrieved. (Default 'default')
  59. title: list or string Title(s) that the video(s) should have.
  60. Default None, for all videos.
  61. Returns:
  62. List of videos that match parameters, or [] if none do.
  63. """
  64. uri = 'http://gdata.youtube.com/feeds/api/users/' + user + '/uploads'
  65. return self.GetEntries(uri,
  66. titles,
  67. converter=gdata.youtube.YouTubeVideoFeedFromString)
  68. GetVideos = get_videos
  69. def is_token_valid(self, test_uri='/feeds/api/users/default/uploads'):
  70. """Check that the token being used is valid."""
  71. return googlecl.service.BaseServiceCL.IsTokenValid(self, test_uri)
  72. IsTokenValid = is_token_valid
  73. def post_videos(self, paths, category, title=None, desc=None, tags=None,
  74. devtags=None, access=None):
  75. """Post video(s) to YouTube.
  76. Keyword arguments:
  77. paths: List of paths to videos.
  78. category: YouTube category for the video.
  79. title: Title of the video. (Default is the filename of the video).
  80. desc: Video summary (Default None).
  81. tags: Tags of the video as a string, separated by commas (Default None).
  82. devtags: Developer tags for the video (Default None).
  83. access: 'private' or 'unlisted', anything else = 'public'
  84. """
  85. from gdata.media import Group, Title, Description, Keywords, Private
  86. if isinstance(paths, basestring):
  87. paths = [paths]
  88. if access is None:
  89. access = 'public'
  90. access = access.lower()
  91. private = None
  92. if access == 'private':
  93. private = Private()
  94. for path in paths:
  95. filename = os.path.basename(path).split('.')[0]
  96. my_media_group = Group(title=Title(text=title or filename),
  97. description=Description(text=desc or 'A video'),
  98. keywords=Keywords(text=tags),
  99. category=build_category(category),
  100. private=private)
  101. if access == 'unlisted':
  102. extension_elements=[atom.ExtensionElement('accessControl',
  103. namespace=gdata.media.YOUTUBE_NAMESPACE,
  104. attributes={'action':'list', 'permission':'denied'})]
  105. video_entry = gdata.youtube.YouTubeVideoEntry(media=my_media_group,
  106. extension_elements=extension_elements)
  107. else:
  108. video_entry = gdata.youtube.YouTubeVideoEntry(media=my_media_group)
  109. if devtags:
  110. taglist = devtags.replace(', ', ',')
  111. taglist = taglist.split(',')
  112. video_entry.AddDeveloperTags(taglist)
  113. LOG.info(safe_encode('Loading ' + path))
  114. try:
  115. entry = self.InsertVideoEntry(video_entry, path)
  116. except gdata.service.RequestError, err:
  117. LOG.error('Failed to upload video: %s' % err)
  118. except gdata.youtube.service.YouTubeError, err:
  119. err_str = str(err)
  120. if err_str.find('path name or a file-like object') != -1:
  121. err_str = safe_encode('Could not find file ' + path)
  122. if (err.args[0]['body'].find('invalid_value') != -1 and
  123. err.args[0]['body'].find('media:category') != -1):
  124. err_str = 'Invalid category: %s' % category
  125. err_str += ('\nFor a list of valid categories, see '
  126. 'http://code.google.com/p/googlecl/wiki/Manual#YouTube')
  127. LOG.error(err_str)
  128. else:
  129. LOG.info('Video uploaded: %s' % entry.GetHtmlLink().href)
  130. PostVideos = post_videos
  131. def tag_videos(self, video_entries, tags):
  132. """Add or remove tags on a list of videos.
  133. Keyword arguments:
  134. video_entries: List of YouTubeVideoEntry objects.
  135. tags: String representation of tags in a comma separated list. For how
  136. tags are generated from the string, see
  137. googlecl.base.generate_tag_sets().
  138. """
  139. from gdata.media import Group, Keywords
  140. remove_set, add_set, replace_tags = googlecl.base.generate_tag_sets(tags)
  141. for video in video_entries:
  142. if not video.media:
  143. video.media = Group()
  144. if not video.media.keywords:
  145. video.media.keywords = Keywords()
  146. # No point removing tags if the video has no keywords,
  147. # or we're replacing the keywords.
  148. if video.media.keywords.text and remove_set and not replace_tags:
  149. current_tags = video.media.keywords.text.replace(', ', ',')
  150. current_set = set(current_tags.split(','))
  151. video.media.keywords.text = ','.join(current_set - remove_set)
  152. if replace_tags or not video.media.keywords.text:
  153. video.media.keywords.text = ','.join(add_set)
  154. elif add_set:
  155. video.media.keywords.text += ',' + ','.join(add_set)
  156. self.UpdateVideoEntry(video)
  157. TagVideos = tag_videos
  158. SERVICE_CLASS = YouTubeServiceCL
  159. def build_category(category):
  160. """Build a single-item list of a YouTube category.
  161. This refers to the Category of a video entry, such as "Film" or "Comedy",
  162. not the atom/gdata element. This does not check if the category provided
  163. is valid.
  164. Keyword arguments:
  165. category: String representing the category.
  166. Returns:
  167. A single-item list of a YouTube category (type gdata.media.Category).
  168. """
  169. from gdata.media import Category
  170. return [Category(
  171. text=category,
  172. scheme='http://gdata.youtube.com/schemas/2007/categories.cat',
  173. label=category)]