PageRenderTime 22ms CodeModel.GetById 2ms app.highlight 16ms RepoModel.GetById 1ms 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
 15
 16"""Service details and instances for the YouTube service."""
 17
 18
 19__author__ = 'tom.h.miller@gmail.com (Tom Miller)'
 20import atom
 21import gdata.youtube
 22import logging
 23import os
 24import googlecl.base
 25import googlecl.service
 26from googlecl.youtube import SECTION_HEADER
 27from gdata.youtube.service import YouTubeService
 28from googlecl import safe_encode
 29
 30LOG = logging.getLogger(googlecl.youtube.LOGGER_NAME)
 31
 32
 33class YouTubeServiceCL(YouTubeService, googlecl.service.BaseServiceCL):
 34
 35  """Extends gdata.youtube.service.YouTubeService for the command line.
 36
 37  This class adds some features focused on using YouTube via an installed app
 38  with a command line interface.
 39
 40  """
 41
 42  def __init__(self, config):
 43    """Constructor."""
 44    YouTubeService.__init__(self)
 45    googlecl.service.BaseServiceCL.__init__(self, SECTION_HEADER, config)
 46
 47  def categorize_videos(self, video_entries, category):
 48    """Change the categories of a list of videos to a single category.
 49
 50    If the update fails with a request error, a message is printed to screen.
 51    Usually, valid category strings are the first word of the category as seen
 52    on YouTube (e.g. "Film" for "Film & Animation")
 53
 54    Keyword arguments:
 55      video_entries: List of YouTubeVideoEntry objects.
 56      category: String representation of category.
 57
 58    """
 59    for video in video_entries:
 60      video.media.category = build_category(category)
 61      try:
 62        self.UpdateVideoEntry(video)
 63      except gdata.service.RequestError, err:
 64        if err.args[0]['body'].find('invalid_value') != -1:
 65          LOG.error('Category update failed, ' + category +
 66                    ' is not a category.')
 67        else:
 68          raise
 69
 70  CategorizeVideos = categorize_videos
 71
 72  def get_videos(self, user='default', titles=None):
 73    """Get entries for videos uploaded by a user.
 74
 75    Keyword arguments:
 76      user: The user whose videos are being retrieved. (Default 'default')
 77      title: list or string Title(s) that the video(s) should have.
 78             Default None, for all videos.
 79
 80    Returns:
 81      List of videos that match parameters, or [] if none do.
 82
 83    """
 84    uri = 'http://gdata.youtube.com/feeds/api/users/' + user + '/uploads'
 85    return self.GetEntries(uri,
 86                           titles,
 87                           converter=gdata.youtube.YouTubeVideoFeedFromString)
 88
 89  GetVideos = get_videos
 90
 91  def is_token_valid(self, test_uri='/feeds/api/users/default/uploads'):
 92    """Check that the token being used is valid."""
 93    return googlecl.service.BaseServiceCL.IsTokenValid(self, test_uri)
 94
 95  IsTokenValid = is_token_valid
 96
 97  def post_videos(self, paths, category, title=None, desc=None, tags=None,
 98                 devtags=None, access=None):
 99    """Post video(s) to YouTube.
100
101    Keyword arguments:
102      paths: List of paths to videos.
103      category: YouTube category for the video.
104      title: Title of the video. (Default is the filename of the video).
105      desc: Video summary (Default None).
106      tags: Tags of the video as a string, separated by commas (Default None).
107      devtags: Developer tags for the video (Default None).
108      access: 'private' or 'unlisted', anything else = 'public'
109
110    """
111    from gdata.media import Group, Title, Description, Keywords, Private
112    if isinstance(paths, basestring):
113      paths = [paths]
114
115    if access is None:
116      access = 'public'
117
118    access = access.lower()
119    private = None
120    if access == 'private':
121      private = Private()
122
123    for path in paths:
124      filename = os.path.basename(path).split('.')[0]
125      my_media_group = Group(title=Title(text=title or filename),
126                             description=Description(text=desc or 'A video'),
127                             keywords=Keywords(text=tags),
128                             category=build_category(category),
129                             private=private)
130
131      if access == 'unlisted':
132        extension_elements=[atom.ExtensionElement('accessControl',
133            namespace=gdata.media.YOUTUBE_NAMESPACE,
134            attributes={'action':'list', 'permission':'denied'})]
135        video_entry = gdata.youtube.YouTubeVideoEntry(media=my_media_group,
136            extension_elements=extension_elements)
137      else:
138        video_entry = gdata.youtube.YouTubeVideoEntry(media=my_media_group)
139
140      if devtags:
141        taglist = devtags.replace(', ', ',')
142        taglist = taglist.split(',')
143        video_entry.AddDeveloperTags(taglist)
144      LOG.info(safe_encode('Loading ' + path))
145      try:
146        entry = self.InsertVideoEntry(video_entry, path)
147      except gdata.service.RequestError, err:
148        LOG.error('Failed to upload video: %s' % err)
149      except gdata.youtube.service.YouTubeError, err:
150        err_str = str(err)
151        if err_str.find('path name or a file-like object') != -1:
152          err_str = safe_encode('Could not find file ' + path)
153        if (err.args[0]['body'].find('invalid_value') != -1 and
154            err.args[0]['body'].find('media:category') != -1):
155          err_str = 'Invalid category: %s' % category
156          err_str += ('\nFor a list of valid categories, see '
157                      'http://code.google.com/p/googlecl/wiki/Manual#YouTube')
158        LOG.error(err_str)
159      else:
160        LOG.info('Video uploaded: %s' % entry.GetHtmlLink().href)
161
162  PostVideos = post_videos
163
164  def tag_videos(self, video_entries, tags):
165    """Add or remove tags on a list of videos.
166
167    Keyword arguments:
168      video_entries: List of YouTubeVideoEntry objects.
169      tags: String representation of tags in a comma separated list. For how
170            tags are generated from the string, see
171            googlecl.base.generate_tag_sets().
172
173    """
174    from gdata.media import Group, Keywords
175    remove_set, add_set, replace_tags = googlecl.base.generate_tag_sets(tags)
176    for video in video_entries:
177      if not video.media:
178        video.media = Group()
179      if not video.media.keywords:
180        video.media.keywords = Keywords()
181
182      # No point removing tags if the video has no keywords,
183      # or we're replacing the keywords.
184      if video.media.keywords.text and remove_set and not replace_tags:
185        current_tags = video.media.keywords.text.replace(', ', ',')
186        current_set = set(current_tags.split(','))
187        video.media.keywords.text = ','.join(current_set - remove_set)
188
189      if replace_tags or not video.media.keywords.text:
190        video.media.keywords.text = ','.join(add_set)
191      elif add_set:
192        video.media.keywords.text += ',' + ','.join(add_set)
193
194      self.UpdateVideoEntry(video)
195
196  TagVideos = tag_videos
197
198
199SERVICE_CLASS = YouTubeServiceCL
200
201
202def build_category(category):
203  """Build a single-item list of a YouTube category.
204
205  This refers to the Category of a video entry, such as "Film" or "Comedy",
206  not the atom/gdata element. This does not check if the category provided
207  is valid.
208
209  Keyword arguments:
210    category: String representing the category.
211
212  Returns:
213    A single-item list of a YouTube category (type gdata.media.Category).
214
215  """
216  from gdata.media import Category
217  return [Category(
218                text=category,
219                scheme='http://gdata.youtube.com/schemas/2007/categories.cat',
220                label=category)]