# service.py

#

Copyright (C) 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Service details and instances for the YouTube service.

#

    __author__ = 'tom.h.miller@gmail.com (Tom Miller)'
    import gdata.youtube
    import logging
    import os
    import googlecl.base
    import googlecl.service
    from googlecl.youtube import SECTION_HEADER
    from gdata.youtube.service import YouTubeService
    from googlecl import safe_encode

    LOG = logging.getLogger(googlecl.youtube.LOGGER_NAME)

#

Extends gdata.youtube.service.YouTubeService for the command line.

    class YouTubeServiceCL(YouTubeService, googlecl.service.BaseServiceCL):

#

This class adds some features focused on using YouTube via an installed app with a command line interface.

#

Constructor.

      def __init__(self, config):

#

        YouTubeService.__init__(self)
        googlecl.service.BaseServiceCL.__init__(self, SECTION_HEADER, config)

#

Change the categories of a list of videos to a single category.

      def categorize_videos(self, video_entries, category):

#

If the update fails with a request error, a message is printed to screen. Usually, valid category strings are the first word of the category as seen on YouTube (e.g. "Film" for "Film & Animation")

Keyword arguments: video_entries: List of YouTubeVideoEntry objects. category: String representation of category.

        for video in video_entries:
          video.media.category = build_category(category)
          try:
            self.UpdateVideoEntry(video)
          except gdata.service.RequestError, err:
            if err.args[0]['body'].find('invalid_value') != -1:
              LOG.error('Category update failed, ' + category +
                        ' is not a category.')
            else:
              raise

      CategorizeVideos = categorize_videos

#

Get entries for videos uploaded by a user.

      def get_videos(self, user='default', titles=None):

#

Keyword arguments: user: The user whose videos are being retrieved. (Default 'default') title: list or string Title(s) that the video(s) should have. Default None, for all videos.

Returns: List of videos that match parameters, or [] if none do.

        uri = 'http://gdata.youtube.com/feeds/api/users/' + user + '/uploads'
        return self.GetEntries(uri,
                               titles,
                               converter=gdata.youtube.YouTubeVideoFeedFromString)

      GetVideos = get_videos

#

Check that the token being used is valid.

      def is_token_valid(self, test_uri='/feeds/api/users/default/uploads'):

#

        return googlecl.service.BaseServiceCL.IsTokenValid(self, test_uri)

      IsTokenValid = is_token_valid

#

Post video(s) to YouTube.

      def post_videos(self, paths, category, title=None, desc=None, tags=None,
                     devtags=None, is_private=None):

#

Keyword arguments: paths: List of paths to videos. category: YouTube category for the video. title: Title of the video. (Default is the filename of the video). desc: Video summary (Default None). tags: Tags of the video as a string, separated by commas (Default None). devtags: Developer tags for the video (Default None).

        from gdata.media import Group, Title, Description, Keywords, Private
        if isinstance(paths, basestring):
          paths = [paths]
        set_private = lambda private: Private() if private else None
        for path in paths:
          filename = os.path.basename(path).split('.')[0]
          my_media_group = Group(title=Title(text=title or filename),
                                 description=Description(text=desc or 'A video'),
                                 keywords=Keywords(text=tags),
                                 category=build_category(category),
                                 private=set_private(is_private))

          video_entry = gdata.youtube.YouTubeVideoEntry(media=my_media_group)
          if devtags:
            taglist = devtags.replace(', ', ',')
            taglist = taglist.split(',')
            video_entry.AddDeveloperTags(taglist)
          LOG.info(safe_encode('Loading ' + path))
          try:
            entry = self.InsertVideoEntry(video_entry, path)
          except gdata.service.RequestError, err:
            LOG.error('Failed to upload video: %s' % err)
          except gdata.youtube.service.YouTubeError, err:
            err_str = str(err)
            if err_str.find('path name or a file-like object') != -1:
              err_str = safe_encode('Could not find file ' + path)
            if (err.args[0]['body'].find('invalid_value') != -1 and
                err.args[0]['body'].find('media:category') != -1):
              err_str = 'Invalid category: %s' % category
              err_str += ('\nFor a list of valid categories, see '
                          'http://code.google.com/p/googlecl/wiki/Manual#YouTube')
            LOG.error(err_str)
          else:
            LOG.info('Video uploaded: %s' % entry.GetHtmlLink().href)

      PostVideos = post_videos

#

Add or remove tags on a list of videos.

      def tag_videos(self, video_entries, tags):

#

Keyword arguments: video_entries: List of YouTubeVideoEntry objects. tags: String representation of tags in a comma separated list. For how tags are generated from the string, see googlecl.base.generate_tag_sets().

        from gdata.media import Group, Keywords
        remove_set, add_set, replace_tags = googlecl.base.generate_tag_sets(tags)
        for video in video_entries:
          if not video.media:
            video.media = Group()
          if not video.media.keywords:
            video.media.keywords = Keywords()

#

No point removing tags if the video has no keywords, or we're replacing the keywords.

          if video.media.keywords.text and remove_set and not replace_tags:
            current_tags = video.media.keywords.text.replace(', ', ',')
            current_set = set(current_tags.split(','))
            video.media.keywords.text = ','.join(current_set - remove_set)

          if replace_tags or not video.media.keywords.text:
            video.media.keywords.text = ','.join(add_set)
          elif add_set:
            video.media.keywords.text += ',' + ','.join(add_set)

          self.UpdateVideoEntry(video)

      TagVideos = tag_videos

    SERVICE_CLASS = YouTubeServiceCL

#

Build a single-item list of a YouTube category.

    def build_category(category):

#

This refers to the Category of a video entry, such as "Film" or "Comedy", not the atom/gdata element. This does not check if the category provided is valid.

Keyword arguments: category: String representing the category.

Returns: A single-item list of a YouTube category (type gdata.media.Category).

      from gdata.media import Category
      return [Category(
                    text=category,
                    scheme='http://gdata.youtube.com/schemas/2007/categories.cat',
                    label=category)]