265 lines
9.3 KiB
Python
265 lines
9.3 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Copyright (C) 2009 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.
|
|
|
|
"""Contains a client to communicate with the YouTube servers.
|
|
|
|
A quick and dirty port of the YouTube GDATA 1.0 Python client
|
|
libraries to version 2.0 of the GDATA library.
|
|
|
|
"""
|
|
|
|
# __author__ = 's.@google.com (John Skidgel)'
|
|
|
|
import logging
|
|
|
|
import gdata.client
|
|
import gdata.youtube.data
|
|
import atom.data
|
|
import atom.http_core
|
|
|
|
# Constants
|
|
# -----------------------------------------------------------------------------
|
|
YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL = 'https://www.google.com/youtube/accounts/ClientLogin'
|
|
YOUTUBE_SUPPORTED_UPLOAD_TYPES = ('mov', 'avi', 'wmv', 'mpg', 'quicktime',
|
|
'flv')
|
|
YOUTUBE_QUERY_VALID_TIME_PARAMETERS = ('today', 'this_week', 'this_month',
|
|
'all_time')
|
|
YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS = ('published', 'viewCount', 'rating',
|
|
'relevance')
|
|
YOUTUBE_QUERY_VALID_RACY_PARAMETERS = ('include', 'exclude')
|
|
YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS = ('1', '5', '6')
|
|
YOUTUBE_STANDARDFEEDS = ('most_recent', 'recently_featured',
|
|
'top_rated', 'most_viewed','watch_on_mobile')
|
|
|
|
YOUTUBE_UPLOAD_TOKEN_URI = 'http://gdata.youtube.com/action/GetUploadToken'
|
|
YOUTUBE_SERVER = 'gdata.youtube.com/feeds/api'
|
|
YOUTUBE_SERVICE = 'youtube'
|
|
YOUTUBE_VIDEO_FEED_URI = 'http://%s/videos' % YOUTUBE_SERVER
|
|
YOUTUBE_USER_FEED_URI = 'http://%s/users/' % YOUTUBE_SERVER
|
|
|
|
# Takes a youtube video ID.
|
|
YOUTUBE_CAPTION_FEED_URI = 'http://gdata.youtube.com/feeds/api/videos/%s/captions'
|
|
|
|
# Takes a youtube video ID and a caption track ID.
|
|
YOUTUBE_CAPTION_URI = 'http://gdata.youtube.com/feeds/api/videos/%s/captiondata/%s'
|
|
|
|
YOUTUBE_CAPTION_MIME_TYPE = 'application/vnd.youtube.timedtext; charset=UTF-8'
|
|
|
|
|
|
# Classes
|
|
# -----------------------------------------------------------------------------
|
|
class Error(Exception):
|
|
"""Base class for errors within the YouTube service."""
|
|
pass
|
|
|
|
|
|
class RequestError(Error):
|
|
"""Error class that is thrown in response to an invalid HTTP Request."""
|
|
pass
|
|
|
|
|
|
class YouTubeError(Error):
|
|
"""YouTube service specific error class."""
|
|
pass
|
|
|
|
|
|
class YouTubeClient(gdata.client.GDClient):
|
|
"""Client for the YouTube service.
|
|
|
|
Performs a partial list of Google Data YouTube API functions, such as
|
|
retrieving the videos feed for a user and the feed for a video.
|
|
YouTube Service requires authentication for any write, update or delete
|
|
actions.
|
|
"""
|
|
api_version = '2'
|
|
auth_service = YOUTUBE_SERVICE
|
|
auth_scopes = ['http://%s' % YOUTUBE_SERVER, 'https://%s' % YOUTUBE_SERVER]
|
|
|
|
def get_videos(self, uri=YOUTUBE_VIDEO_FEED_URI, auth_token=None,
|
|
desired_class=gdata.youtube.data.VideoFeed,
|
|
**kwargs):
|
|
"""Retrieves a YouTube video feed.
|
|
Args:
|
|
uri: A string representing the URI of the feed that is to be retrieved.
|
|
|
|
Returns:
|
|
A YouTubeVideoFeed if successfully retrieved.
|
|
"""
|
|
return self.get_feed(uri, auth_token=auth_token,
|
|
desired_class=desired_class,
|
|
**kwargs)
|
|
|
|
GetVideos = get_videos
|
|
|
|
|
|
def get_user_feed(self, uri=None, username=None):
|
|
"""Retrieve a YouTubeVideoFeed of user uploaded videos.
|
|
|
|
Either a uri or a username must be provided. This will retrieve list
|
|
of videos uploaded by specified user. The uri will be of format
|
|
"http://gdata.youtube.com/feeds/api/users/{username}/uploads".
|
|
|
|
Args:
|
|
uri: An optional string representing the URI of the user feed that is
|
|
to be retrieved.
|
|
username: An optional string representing the username.
|
|
|
|
Returns:
|
|
A YouTubeUserFeed if successfully retrieved.
|
|
|
|
Raises:
|
|
YouTubeError: You must provide at least a uri or a username to the
|
|
GetYouTubeUserFeed() method.
|
|
"""
|
|
if uri is None and username is None:
|
|
raise YouTubeError('You must provide at least a uri or a username '
|
|
'to the GetYouTubeUserFeed() method')
|
|
elif username and not uri:
|
|
uri = '%s%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'uploads')
|
|
return self.get_feed(uri, desired_class=gdata.youtube.data.VideoFeed)
|
|
|
|
GetUserFeed = get_user_feed
|
|
|
|
|
|
def get_video_entry(self, uri=None, video_id=None,
|
|
auth_token=None, **kwargs):
|
|
"""Retrieve a YouTubeVideoEntry.
|
|
|
|
Either a uri or a video_id must be provided.
|
|
|
|
Args:
|
|
uri: An optional string representing the URI of the entry that is to
|
|
be retrieved.
|
|
video_id: An optional string representing the ID of the video.
|
|
|
|
Returns:
|
|
A YouTubeVideoFeed if successfully retrieved.
|
|
|
|
Raises:
|
|
YouTubeError: You must provide at least a uri or a video_id to the
|
|
GetYouTubeVideoEntry() method.
|
|
"""
|
|
if uri is None and video_id is None:
|
|
raise YouTubeError('You must provide at least a uri or a video_id '
|
|
'to the get_youtube_video_entry() method')
|
|
elif video_id and uri is None:
|
|
uri = '%s/%s' % (YOUTUBE_VIDEO_FEED_URI, video_id)
|
|
return self.get_feed(uri,
|
|
desired_class=gdata.youtube.data.VideoEntry,
|
|
auth_token=auth_token,
|
|
**kwargs)
|
|
|
|
GetVideoEntry = get_video_entry
|
|
|
|
|
|
def get_caption_feed(self, uri):
|
|
"""Retrieve a Caption feed of tracks.
|
|
|
|
Args:
|
|
uri: A string representing the caption feed's URI to be retrieved.
|
|
|
|
Returns:
|
|
A YouTube CaptionFeed if successfully retrieved.
|
|
"""
|
|
return self.get_feed(uri, desired_class=gdata.youtube.data.CaptionFeed)
|
|
|
|
GetCaptionFeed = get_caption_feed
|
|
|
|
def get_caption_track(self, track_url, client_id,
|
|
developer_key, auth_token=None, **kwargs):
|
|
http_request = atom.http_core.HttpRequest(uri = track_url, method = 'GET')
|
|
dev_key = 'key=' + developer_key
|
|
authsub = 'AuthSub token="' + str(auth_token) + '"'
|
|
http_request.headers = {
|
|
'Authorization': authsub,
|
|
'X-GData-Client': client_id,
|
|
'X-GData-Key': dev_key
|
|
}
|
|
return self.request(http_request=http_request, **kwargs)
|
|
|
|
GetCaptionTrack = get_caption_track
|
|
|
|
def create_track(self, video_id, title, language, body, client_id,
|
|
developer_key, auth_token=None, title_type='text', **kwargs):
|
|
"""Creates a closed-caption track and adds to an existing YouTube video.
|
|
"""
|
|
new_entry = gdata.youtube.data.TrackEntry(
|
|
content = gdata.youtube.data.TrackContent(text = body, lang = language))
|
|
uri = YOUTUBE_CAPTION_FEED_URI % video_id
|
|
http_request = atom.http_core.HttpRequest(uri = uri, method = 'POST')
|
|
dev_key = 'key=' + developer_key
|
|
authsub = 'AuthSub token="' + str(auth_token) + '"'
|
|
http_request.headers = {
|
|
'Content-Type': YOUTUBE_CAPTION_MIME_TYPE,
|
|
'Content-Language': language,
|
|
'Slug': title,
|
|
'Authorization': authsub,
|
|
'GData-Version': self.api_version,
|
|
'X-GData-Client': client_id,
|
|
'X-GData-Key': dev_key
|
|
}
|
|
http_request.add_body_part(body, http_request.headers['Content-Type'])
|
|
return self.request(http_request = http_request,
|
|
desired_class = new_entry.__class__, **kwargs)
|
|
|
|
|
|
CreateTrack = create_track
|
|
|
|
def delete_track(self, video_id, track, client_id, developer_key,
|
|
auth_token=None, **kwargs):
|
|
"""Deletes a track."""
|
|
if isinstance(track, gdata.youtube.data.TrackEntry):
|
|
track_id_text_node = track.get_id().split(':')
|
|
track_id = track_id_text_node[3]
|
|
else:
|
|
track_id = track
|
|
uri = YOUTUBE_CAPTION_URI % (video_id, track_id)
|
|
http_request = atom.http_core.HttpRequest(uri = uri, method = 'DELETE')
|
|
dev_key = 'key=' + developer_key
|
|
authsub = 'AuthSub token="' + str(auth_token) + '"'
|
|
http_request.headers = {
|
|
'Authorization': authsub,
|
|
'GData-Version': self.api_version,
|
|
'X-GData-Client': client_id,
|
|
'X-GData-Key': dev_key
|
|
}
|
|
return self.request(http_request=http_request, **kwargs)
|
|
|
|
DeleteTrack = delete_track
|
|
|
|
def update_track(self, video_id, track, body, client_id, developer_key,
|
|
auth_token=None, **kwargs):
|
|
"""Updates a closed-caption track for an existing YouTube video.
|
|
"""
|
|
track_id_text_node = track.get_id().split(':')
|
|
track_id = track_id_text_node[3]
|
|
uri = YOUTUBE_CAPTION_URI % (video_id, track_id)
|
|
http_request = atom.http_core.HttpRequest(uri = uri, method = 'PUT')
|
|
dev_key = 'key=' + developer_key
|
|
authsub = 'AuthSub token="' + str(auth_token) + '"'
|
|
http_request.headers = {
|
|
'Content-Type': YOUTUBE_CAPTION_MIME_TYPE,
|
|
'Authorization': authsub,
|
|
'GData-Version': self.api_version,
|
|
'X-GData-Client': client_id,
|
|
'X-GData-Key': dev_key
|
|
}
|
|
http_request.add_body_part(body, http_request.headers['Content-Type'])
|
|
return self.request(http_request = http_request,
|
|
desired_class = track.__class__, **kwargs)
|
|
|
|
UpdateTrack = update_track
|