tmm1-youtube-g 0.4.5 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +10 -1
- data/Manifest.txt +3 -0
- data/README.txt +2 -0
- data/Rakefile +5 -0
- data/TODO.txt +0 -2
- data/lib/youtube_g.rb +11 -1
- data/lib/youtube_g/client.rb +58 -13
- data/lib/youtube_g/logger.rb +0 -2
- data/lib/youtube_g/model/author.rb +3 -0
- data/lib/youtube_g/model/category.rb +4 -1
- data/lib/youtube_g/model/contact.rb +8 -0
- data/lib/youtube_g/model/content.rb +5 -0
- data/lib/youtube_g/model/playlist.rb +1 -0
- data/lib/youtube_g/model/rating.rb +7 -0
- data/lib/youtube_g/model/thumbnail.rb +7 -0
- data/lib/youtube_g/model/video.rb +105 -27
- data/lib/youtube_g/parser.rb +8 -13
- data/lib/youtube_g/record.rb +1 -1
- data/lib/youtube_g/request/video_search.rb +40 -115
- data/lib/youtube_g/request/video_upload.rb +37 -19
- data/lib/youtube_g/response/video_search.rb +7 -7
- data/test/test_client.rb +44 -14
- data/test/test_video.rb +13 -1
- data/test/test_video_search.rb +15 -5
- metadata +1 -1
data/History.txt
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
== trunk
|
2
|
-
|
2
|
+
* Added :page and :per_page options, this allows easier usage of the will_paginate
|
3
|
+
plugin with the library. The :offset and :max_results options are no longer available. [Daniel Insley]
|
4
|
+
* Added ability to get video responses on the instances of the YouTube::Model::Video object. [Daniel Insley]
|
5
|
+
* Added and improved the existing documentation [Daniel Insley]
|
6
|
+
* Fixed usage of deprecated yt:racy, now using media:rating [Daniel Insley]
|
7
|
+
* Renamed can_embed? method to embeddable? [Daniel Insley]
|
8
|
+
* Added ability for padingation and ordering on standard feeds. [Daniel Insley]
|
9
|
+
* Add error-handling for video upload errors. [FiXato]
|
10
|
+
* Add error-handling for authentication errors from YouTube during video upload. [FiXato]
|
11
|
+
* Add support for making videos private upon video upload. [FiXato]
|
3
12
|
* Fix issue with REXML parsing of video upload response. [FiXato]
|
4
13
|
* Fix issue with response code comparison. [FiXato]
|
5
14
|
* Authcode is now retrieved for video uploads. [FiXato]
|
data/Manifest.txt
CHANGED
@@ -17,6 +17,9 @@ lib/youtube_g/model/user.rb
|
|
17
17
|
lib/youtube_g/model/video.rb
|
18
18
|
lib/youtube_g/parser.rb
|
19
19
|
lib/youtube_g/record.rb
|
20
|
+
lib/youtube_g/request/base_search.rb
|
21
|
+
lib/youtube_g/request/standard_search.rb
|
22
|
+
lib/youtube_g/request/user_search.rb
|
20
23
|
lib/youtube_g/request/video_search.rb
|
21
24
|
lib/youtube_g/request/video_upload.rb
|
22
25
|
lib/youtube_g/response/video_search.rb
|
data/README.txt
CHANGED
@@ -32,6 +32,7 @@ Create a client:
|
|
32
32
|
Basic queries:
|
33
33
|
|
34
34
|
client.videos_by(:query => "penguin")
|
35
|
+
client.videos_by(:query => "penguin", :page => 2, :per_page => 15)
|
35
36
|
client.videos_by(:tags => ['tiger', 'leopard'])
|
36
37
|
client.videos_by(:categories => [:news, :sports])
|
37
38
|
client.videos_by(:categories => [:news, :sports], :tags => ['soccer', 'football'])
|
@@ -40,6 +41,7 @@ Basic queries:
|
|
40
41
|
Standard feeds:
|
41
42
|
|
42
43
|
client.videos_by(:most_viewed)
|
44
|
+
client.videos_by(:most_linked, :page => 3)
|
43
45
|
client.videos_by(:top_rated, :time => :today)
|
44
46
|
|
45
47
|
Advanced queries (with boolean operators OR (either), AND (include), NOT (exclude)):
|
data/Rakefile
CHANGED
@@ -17,4 +17,9 @@ desc 'Tag release'
|
|
17
17
|
task :tag do
|
18
18
|
svn_root = 'svn+ssh://drummr77@rubyforge.org/var/svn/youtube-g'
|
19
19
|
sh %(svn cp #{svn_root}/trunk #{svn_root}/tags/release-#{YouTubeG::VERSION} -m "Tag YouTubeG release #{YouTubeG::VERSION}")
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Load the library in an IRB session'
|
23
|
+
task :console do
|
24
|
+
sh %(irb -r lib/youtube_g.rb)
|
20
25
|
end
|
data/TODO.txt
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
[ ] stub out http request/response cycle for tests
|
2
|
-
[ ] consider defaulting the client to no-logger, rather than outputting to STDOUT.
|
3
2
|
[ ] allow specifying values as single items where you don't need to wrap in a list, e.g. :tags => :chickens instead of :tags => [ 'chickens' ]
|
4
3
|
[ ] make sure symbols will work as well as tags everywhere (again, :tags => :chickens is same as :tags => 'chickens')
|
5
4
|
[ ] figure out better structure for class/file (either rename request/video_search.rb or split into one class per file again)
|
6
5
|
[ ] restore spaces after method def names
|
7
6
|
[ ] use a proxy for testing with static sample result xml so we have repeatable tests
|
8
7
|
[ ] Clean up tests using Shoulda to define contexts
|
9
|
-
[ ] Consolidate requires
|
10
8
|
[ ] Allow :category and :categories for query DSL
|
11
9
|
[ ] Exception handling
|
12
10
|
|
data/lib/youtube_g.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'net/https'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'rexml/document'
|
6
|
+
require 'cgi'
|
7
|
+
|
1
8
|
require File.dirname(__FILE__) + '/youtube_g/client'
|
2
9
|
require File.dirname(__FILE__) + '/youtube_g/record'
|
3
10
|
require File.dirname(__FILE__) + '/youtube_g/parser'
|
@@ -10,10 +17,13 @@ require File.dirname(__FILE__) + '/youtube_g/model/rating'
|
|
10
17
|
require File.dirname(__FILE__) + '/youtube_g/model/thumbnail'
|
11
18
|
require File.dirname(__FILE__) + '/youtube_g/model/user'
|
12
19
|
require File.dirname(__FILE__) + '/youtube_g/model/video'
|
20
|
+
require File.dirname(__FILE__) + '/youtube_g/request/base_search'
|
21
|
+
require File.dirname(__FILE__) + '/youtube_g/request/user_search'
|
22
|
+
require File.dirname(__FILE__) + '/youtube_g/request/standard_search'
|
13
23
|
require File.dirname(__FILE__) + '/youtube_g/request/video_upload'
|
14
24
|
require File.dirname(__FILE__) + '/youtube_g/request/video_search'
|
15
25
|
require File.dirname(__FILE__) + '/youtube_g/response/video_search'
|
16
26
|
|
17
|
-
class YouTubeG
|
27
|
+
class YouTubeG #:nodoc:
|
18
28
|
VERSION = '0.4.5'
|
19
29
|
end
|
data/lib/youtube_g/client.rb
CHANGED
@@ -1,36 +1,81 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
1
|
class YouTubeG
|
4
2
|
class Client
|
5
3
|
attr_accessor :logger
|
6
4
|
|
7
|
-
def initialize(logger=
|
8
|
-
@logger = logger
|
5
|
+
def initialize(logger=false)
|
6
|
+
@logger = Logger.new(STDOUT) if logger
|
9
7
|
end
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
8
|
+
|
9
|
+
# Retrieves an array of standard feed, custom query, or user videos.
|
10
|
+
#
|
11
|
+
# === Parameters
|
12
|
+
# If fetching videos for a standard feed:
|
13
|
+
# params<Symbol>:: Accepts a symbol of :top_rated, :top_favorites, :most_viewed,
|
14
|
+
# :most_popular, :most_recent, :most_discussed, :most_linked,
|
15
|
+
# :most_responded, :recently_featured, and :watch_on_mobile.
|
16
|
+
#
|
17
|
+
# You can find out more specific information about what each standard feed provides
|
18
|
+
# by visiting: http://code.google.com/apis/youtube/reference.html#Standard_feeds
|
19
|
+
#
|
20
|
+
# options<Hash> (optional):: Accepts the options of :time, :page (default is 1),
|
21
|
+
# and :per_page (default is 25). :offset and :max_results
|
22
|
+
# can also be passed for a custom offset.
|
23
|
+
#
|
24
|
+
# If fetching videos by tags, categories, query:
|
25
|
+
# params<Hash>:: Accepts the keys :tags, :categories, :query, :order_by,
|
26
|
+
# :author, :racy, :response_format, :video_format, :page (default is 1),
|
27
|
+
# and :per_page(default is 25)
|
28
|
+
#
|
29
|
+
# options<Hash>:: Not used. (Optional)
|
30
|
+
#
|
31
|
+
# If fetching videos for a particular user:
|
32
|
+
# params<Hash>:: Key of :user with a value of the username.
|
33
|
+
# options<Hash>:: Not used. (Optional)
|
34
|
+
# === Returns
|
35
|
+
# YouTubeG::Response::VideoSearch
|
13
36
|
def videos_by(params, options={})
|
37
|
+
request_params = params.respond_to?(:to_hash) ? params : options
|
38
|
+
request_params[:page] ||= 1
|
39
|
+
|
40
|
+
unless request_params[:max_results]
|
41
|
+
request_params[:max_results] = request_params[:per_page] || 25
|
42
|
+
end
|
43
|
+
|
44
|
+
unless request_params[:offset]
|
45
|
+
request_params[:offset] = calculate_offset(request_params[:page], request_params[:max_results] )
|
46
|
+
end
|
47
|
+
|
14
48
|
if params.respond_to?(:to_hash) and not params[:user]
|
15
|
-
request = YouTubeG::Request::VideoSearch.new(
|
16
|
-
|
49
|
+
request = YouTubeG::Request::VideoSearch.new(request_params)
|
17
50
|
elsif (params.respond_to?(:to_hash) && params[:user]) || (params == :favorites)
|
18
|
-
request = YouTubeG::Request::UserSearch.new(
|
19
|
-
|
51
|
+
request = YouTubeG::Request::UserSearch.new(request_params, options)
|
20
52
|
else
|
21
|
-
request = YouTubeG::Request::StandardSearch.new(params,
|
53
|
+
request = YouTubeG::Request::StandardSearch.new(params, request_params)
|
22
54
|
end
|
23
55
|
|
24
|
-
logger.debug "Submitting request [url=#{request.url}]."
|
56
|
+
logger.debug "Submitting request [url=#{request.url}]." if logger
|
25
57
|
parser = YouTubeG::Parser::VideosFeedParser.new(request.url)
|
26
58
|
parser.parse
|
27
59
|
end
|
28
60
|
|
61
|
+
# Retrieves a single YouTube video.
|
62
|
+
#
|
63
|
+
# === Parameters
|
64
|
+
# vid<String>:: The ID or URL of the video that you'd like to retrieve.
|
65
|
+
#
|
66
|
+
# === Returns
|
67
|
+
# YouTubeG::Model::Video
|
29
68
|
def video_by(vid)
|
30
69
|
video_id = vid =~ /^http/ ? vid : "http://gdata.youtube.com/feeds/videos/#{vid}"
|
31
70
|
parser = YouTubeG::Parser::VideoFeedParser.new(video_id)
|
32
71
|
parser.parse
|
33
72
|
end
|
34
73
|
|
74
|
+
private
|
75
|
+
|
76
|
+
def calculate_offset(page, per_page)
|
77
|
+
page == 1 ? 1 : ((per_page * page) - per_page + 1)
|
78
|
+
end
|
79
|
+
|
35
80
|
end
|
36
81
|
end
|
data/lib/youtube_g/logger.rb
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
class YouTubeG
|
2
2
|
module Model
|
3
3
|
class Contact < YouTubeG::Record
|
4
|
+
# *String*:: Identifies the status of a contact.
|
5
|
+
#
|
6
|
+
# * The tag's value will be accepted if the authenticated user and the contact have marked each other as friends.
|
7
|
+
# * The tag's value will be requested if the contact has asked to be added to the authenticated user's contact list, but the request has not yet been accepted (or rejected).
|
8
|
+
# * The tag's value will be pending if the authenticated user has asked to be added to the contact's contact list, but the request has not yet been accepted or rejected.
|
9
|
+
#
|
4
10
|
attr_reader :status
|
11
|
+
|
12
|
+
# *String*:: The Youtube username of the contact.
|
5
13
|
attr_reader :username
|
6
14
|
end
|
7
15
|
end
|
@@ -1,10 +1,15 @@
|
|
1
1
|
class YouTubeG
|
2
2
|
module Model
|
3
3
|
class Content < YouTubeG::Record
|
4
|
+
# *Boolean*:: Description of the video.
|
4
5
|
attr_reader :default
|
6
|
+
# *Fixnum*:: Length of the video in seconds.
|
5
7
|
attr_reader :duration
|
8
|
+
# YouTubeG::Model::Video::Format:: Specifies the video format of the video object
|
6
9
|
attr_reader :format
|
10
|
+
# *String*:: Specifies the MIME type of the media object.
|
7
11
|
attr_reader :mime_type
|
12
|
+
# *String*:: Specifies the URL for the media object.
|
8
13
|
attr_reader :url
|
9
14
|
|
10
15
|
alias :is_default? :default
|
@@ -1,9 +1,16 @@
|
|
1
1
|
class YouTubeG
|
2
2
|
module Model
|
3
3
|
class Rating < YouTubeG::Record
|
4
|
+
# *Float*:: Average rating given to the video
|
4
5
|
attr_reader :average
|
6
|
+
|
7
|
+
# *Fixnum*:: Maximum rating that can be assigned to the video
|
5
8
|
attr_reader :max
|
9
|
+
|
10
|
+
# *Fixnum*:: Minimum rating that can be assigned to the video
|
6
11
|
attr_reader :min
|
12
|
+
|
13
|
+
# *Fixnum*:: Indicates how many people have rated the video
|
7
14
|
attr_reader :rater_count
|
8
15
|
end
|
9
16
|
end
|
@@ -1,9 +1,16 @@
|
|
1
1
|
class YouTubeG
|
2
2
|
module Model
|
3
3
|
class Thumbnail < YouTubeG::Record
|
4
|
+
# *String*:: URL for the thumbnail image.
|
4
5
|
attr_reader :url
|
6
|
+
|
7
|
+
# *Fixnum*:: Height of the thumbnail image.
|
5
8
|
attr_reader :height
|
9
|
+
|
10
|
+
# *Fixnum*:: Width of the thumbnail image.
|
6
11
|
attr_reader :width
|
12
|
+
|
13
|
+
# *String*:: Specifies the time offset at which the frame shown in the thumbnail image appears in the video.
|
7
14
|
attr_reader :time
|
8
15
|
end
|
9
16
|
end
|
@@ -1,91 +1,165 @@
|
|
1
|
+
# TODO
|
2
|
+
# * self atom feed
|
3
|
+
# * alternate youtube watch url
|
4
|
+
# * comments feedLink
|
5
|
+
|
1
6
|
class YouTubeG
|
2
7
|
module Model
|
3
8
|
class Video < YouTubeG::Record
|
4
9
|
# Describes the various file formats in which a Youtube video may be
|
5
10
|
# made available and allows looking them up by format code number.
|
6
|
-
#
|
7
11
|
class Format
|
8
12
|
@@formats = Hash.new
|
9
13
|
|
14
|
+
# Instantiates a new video format object.
|
15
|
+
#
|
16
|
+
# == Parameters
|
17
|
+
# :format_code<Fixnum>:: The Youtube Format code of the object.
|
18
|
+
# :name<Symbol>:: The name of the format
|
19
|
+
#
|
20
|
+
# == Returns
|
21
|
+
# YouTubeG::Model::Video::Format: Video format object
|
10
22
|
def initialize(format_code, name)
|
11
23
|
@format_code = format_code
|
12
24
|
@name = name
|
13
25
|
|
14
26
|
@@formats[format_code] = self
|
15
27
|
end
|
16
|
-
|
28
|
+
|
29
|
+
# Allows you to get the video format for a specific format code.
|
30
|
+
#
|
31
|
+
# A full list of format codes is available at:
|
32
|
+
#
|
33
|
+
# http://code.google.com/apis/youtube/reference.html#youtube_data_api_tag_media:content
|
34
|
+
#
|
35
|
+
# == Parameters
|
36
|
+
# :format_code<Fixnum>:: The Youtube Format code of the object.
|
37
|
+
#
|
38
|
+
# == Returns
|
39
|
+
# YouTubeG::Model::Video::Format: Video format object
|
17
40
|
def self.by_code(format_code)
|
18
41
|
@@formats[format_code]
|
19
42
|
end
|
20
43
|
|
21
|
-
# Flash format on YouTube site. All videos are available in this
|
22
|
-
# format.
|
23
|
-
#
|
44
|
+
# Flash format on YouTube site. All videos are available in this format.
|
24
45
|
FLASH = YouTubeG::Model::Video::Format.new(0, :flash)
|
25
46
|
|
26
|
-
# RTSP streaming URL for mobile video playback. H.263 video (176x144)
|
27
|
-
# and AMR audio.
|
28
|
-
#
|
47
|
+
# RTSP streaming URL for mobile video playback. H.263 video (176x144) and AMR audio.
|
29
48
|
RTSP = YouTubeG::Model::Video::Format.new(1, :rtsp)
|
30
49
|
|
31
50
|
# HTTP URL to the embeddable player (SWF) for this video. This format
|
32
51
|
# is not available for a video that is not embeddable.
|
33
|
-
#
|
34
52
|
SWF = YouTubeG::Model::Video::Format.new(5, :swf)
|
35
53
|
|
54
|
+
# RTSP streaming URL for mobile video playback. MPEG-4 SP video (up to 176x144) and AAC audio.
|
36
55
|
THREE_GPP = YouTubeG::Model::Video::Format.new(6, :three_gpp)
|
37
56
|
end
|
38
|
-
|
57
|
+
|
58
|
+
# *Fixnum*:: Duration of a video in seconds.
|
39
59
|
attr_reader :duration
|
60
|
+
|
61
|
+
# *Boolean*:: Specifies that a video may or may not be embedded on other websites.
|
40
62
|
attr_reader :noembed
|
63
|
+
|
64
|
+
# *Fixnum*:: Specifies the order in which the video appears in a playlist.
|
41
65
|
attr_reader :position
|
66
|
+
|
67
|
+
# *Boolean*:: Specifies that a video is flagged as adult or not.
|
42
68
|
attr_reader :racy
|
43
|
-
attr_reader :statistics
|
44
69
|
|
70
|
+
# *String*: Specifies a URI that uniquely and permanently identifies the video.
|
45
71
|
attr_reader :video_id
|
72
|
+
|
73
|
+
# *Time*:: When the video was published on Youtube.
|
46
74
|
attr_reader :published_at
|
75
|
+
|
76
|
+
# *Time*:: When the video's data was last updated.
|
47
77
|
attr_reader :updated_at
|
78
|
+
|
79
|
+
# *Array*:: A array of YouTubeG::Model::Category objects that describe the videos categories.
|
48
80
|
attr_reader :categories
|
81
|
+
|
82
|
+
# *Array*:: An array of words associated with the video.
|
49
83
|
attr_reader :keywords
|
84
|
+
|
85
|
+
# *String*:: Description of the video.
|
50
86
|
attr_reader :description
|
87
|
+
|
88
|
+
# *String*:: Title for the video.
|
51
89
|
attr_reader :title
|
90
|
+
|
91
|
+
# *String*:: Description of the video.
|
52
92
|
attr_reader :html_content
|
93
|
+
|
94
|
+
# YouTubeG::Model::Author:: Information about the YouTube user who owns a piece of video content.
|
53
95
|
attr_reader :author
|
54
|
-
|
55
|
-
# YouTubeG::Model::Content
|
56
|
-
# data available for this video. Most, but not all, videos offer this.
|
96
|
+
|
97
|
+
# *Array*:: An array of YouTubeG::Model::Content objects describing the individual media content data available for this video. Most, but not all, videos offer this.
|
57
98
|
attr_reader :media_content
|
58
|
-
|
59
|
-
|
99
|
+
|
100
|
+
# *Array*:: An array of YouTubeG::Model::Thumbnail objects that contain information regarding the videos thumbnail images.
|
101
|
+
attr_reader :thumbnails
|
102
|
+
|
103
|
+
# *String*:: The link to watch the URL on YouTubes website.
|
60
104
|
attr_reader :player_url
|
105
|
+
|
106
|
+
# YouTubeG::Model::Rating:: Information about the videos rating.
|
61
107
|
attr_reader :rating
|
108
|
+
|
109
|
+
# *Fixnum*:: Number of times that the video has been viewed
|
62
110
|
attr_reader :view_count
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
|
111
|
+
|
112
|
+
attr_reader :statistics
|
113
|
+
|
114
|
+
# Videos related to the current video.
|
115
|
+
#
|
116
|
+
# === Returns
|
117
|
+
# YouTubeG::Response::VideoSearch
|
70
118
|
def related
|
71
119
|
YouTubeG::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/related").parse
|
72
120
|
end
|
73
121
|
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
122
|
+
# Video responses to the current video.
|
123
|
+
#
|
124
|
+
# === Returns
|
125
|
+
# YouTubeG::Response::VideoSearch
|
126
|
+
def responses
|
127
|
+
YouTubeG::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/responses").parse
|
128
|
+
end
|
129
|
+
|
130
|
+
# The ID of the video, useful for searching for the video again without having to store it anywhere.
|
131
|
+
# A regular query search, with this id will return the same video.
|
132
|
+
#
|
133
|
+
# === Example
|
134
|
+
# >> video.unique_id
|
135
|
+
# => "ZTUVgYoeN_o"
|
136
|
+
#
|
137
|
+
# === Returns
|
138
|
+
# String: The Youtube video id.
|
77
139
|
def unique_id
|
78
140
|
video_id[/videos\/([^<]+)/, 1]
|
79
141
|
end
|
80
142
|
|
81
|
-
|
143
|
+
# Allows you to check whether the video can be embedded on a webpage.
|
144
|
+
#
|
145
|
+
# === Returns
|
146
|
+
# Boolean: True if the video can be embedded, false if not.
|
147
|
+
def embeddable?
|
82
148
|
not @noembed
|
83
149
|
end
|
84
150
|
|
151
|
+
# Provides a URL and various other types of information about a video.
|
152
|
+
#
|
153
|
+
# === Returns
|
154
|
+
# YouTubeG::Model::Content: Data about the embeddable video.
|
85
155
|
def default_media_content
|
86
156
|
@media_content.find { |c| c.is_default? }
|
87
157
|
end
|
88
158
|
|
159
|
+
# Gives you the HTML to embed the video on your website.
|
160
|
+
#
|
161
|
+
# === Returns
|
162
|
+
# String: The HTML for embedding the video on your website.
|
89
163
|
def embed_html(width = 425, height = 350)
|
90
164
|
<<EDOC
|
91
165
|
<object width="#{width}" height="#{height}">
|
@@ -97,6 +171,10 @@ class YouTubeG
|
|
97
171
|
EDOC
|
98
172
|
end
|
99
173
|
|
174
|
+
# The URL needed for embedding the video in a page.
|
175
|
+
#
|
176
|
+
# === Returns
|
177
|
+
# String: Absolute URL for embedding video
|
100
178
|
def embed_url
|
101
179
|
@player_url.sub('watch?', '').sub('=', '/')
|
102
180
|
end
|
data/lib/youtube_g/parser.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
require 'open-uri'
|
3
|
-
require 'rexml/document'
|
4
|
-
|
5
1
|
class YouTubeG
|
6
|
-
module Parser
|
7
|
-
class FeedParser
|
2
|
+
module Parser #:nodoc:
|
3
|
+
class FeedParser #:nodoc:
|
8
4
|
def initialize(url)
|
9
5
|
@url = url
|
10
6
|
end
|
@@ -14,17 +10,16 @@ class YouTubeG
|
|
14
10
|
end
|
15
11
|
end
|
16
12
|
|
17
|
-
class VideoFeedParser < FeedParser
|
13
|
+
class VideoFeedParser < FeedParser #:nodoc:
|
18
14
|
|
19
15
|
def parse_content(content)
|
20
16
|
doc = REXML::Document.new(content)
|
21
17
|
entry = doc.elements["entry"]
|
22
|
-
|
23
18
|
parse_entry(entry)
|
24
19
|
end
|
25
20
|
|
26
21
|
protected
|
27
|
-
def parse_entry(entry)
|
22
|
+
def parse_entry(entry)
|
28
23
|
video_id = entry.elements["id"].text
|
29
24
|
published_at = Time.parse(entry.elements["published"].text)
|
30
25
|
updated_at = Time.parse(entry.elements["updated"].text)
|
@@ -94,7 +89,7 @@ class YouTubeG
|
|
94
89
|
view_count = (el = entry.elements["yt:statistics"]) ? el.attributes["viewCount"].to_i : 0
|
95
90
|
|
96
91
|
noembed = entry.elements["yt:noembed"] ? true : false
|
97
|
-
racy = entry.elements["
|
92
|
+
racy = entry.elements["media:rating"] ? true : false
|
98
93
|
|
99
94
|
YouTubeG::Model::Video.new(
|
100
95
|
:video_id => video_id,
|
@@ -116,7 +111,7 @@ class YouTubeG
|
|
116
111
|
:racy => racy)
|
117
112
|
end
|
118
113
|
|
119
|
-
def parse_media_content (media_content_element)
|
114
|
+
def parse_media_content (media_content_element)
|
120
115
|
content_url = media_content_element.attributes["url"]
|
121
116
|
format_code = media_content_element.attributes["yt:format"].to_i
|
122
117
|
format = YouTubeG::Model::Video::Format.by_code(format_code)
|
@@ -133,10 +128,10 @@ class YouTubeG
|
|
133
128
|
end
|
134
129
|
end
|
135
130
|
|
136
|
-
class VideosFeedParser < VideoFeedParser
|
131
|
+
class VideosFeedParser < VideoFeedParser #:nodoc:
|
137
132
|
|
138
133
|
private
|
139
|
-
def parse_content(content)
|
134
|
+
def parse_content(content) #:nodoc:
|
140
135
|
doc = REXML::Document.new(content)
|
141
136
|
feed = doc.elements["feed"]
|
142
137
|
|
data/lib/youtube_g/record.rb
CHANGED
@@ -1,48 +1,6 @@
|
|
1
1
|
class YouTubeG
|
2
|
-
|
3
|
-
|
4
|
-
module Request
|
5
|
-
|
6
|
-
class BaseSearch
|
7
|
-
attr_reader :url
|
8
|
-
|
9
|
-
def base_url
|
10
|
-
"http://gdata.youtube.com/feeds/api/"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class UserSearch < BaseSearch
|
15
|
-
|
16
|
-
def initialize(params, options={})
|
17
|
-
@url = base_url
|
18
|
-
return @url << "#{options[:user]}/favorites" if params == :favorites
|
19
|
-
@url << "#{params[:user]}/uploads" if params[:user]
|
20
|
-
end
|
21
|
-
|
22
|
-
def base_url
|
23
|
-
super << "users/"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class StandardSearch < BaseSearch
|
28
|
-
TYPES = [ :most_viewed, :top_rated, :recently_featured, :watch_on_mobile ]
|
29
|
-
TIMES = [ :all_time, :today, :this_week, :this_month ]
|
30
|
-
|
31
|
-
def initialize(type, options={})
|
32
|
-
if TYPES.include?(type)
|
33
|
-
@url = base_url << type.to_s
|
34
|
-
@url << "?time=#{CGI.escape(options.delete(:time).to_s)}" if TIMES.include?(options[:time])
|
35
|
-
else
|
36
|
-
raise "Invalid type, must be one of: #{ TYPES.map { |t| t.to_s }.join(", ") }"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def base_url
|
41
|
-
super << "standardfeeds/"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class VideoSearch < BaseSearch
|
2
|
+
module Request #:nodoc:
|
3
|
+
class VideoSearch < BaseSearch #:nodoc:
|
46
4
|
# From here: http://code.google.com/apis/youtube/reference.html#yt_format
|
47
5
|
ONLY_EMBEDDABLE = 5
|
48
6
|
|
@@ -58,34 +16,22 @@ class YouTubeG
|
|
58
16
|
attr_reader :author
|
59
17
|
|
60
18
|
def initialize(params={})
|
61
|
-
#
|
62
|
-
return if params.nil?
|
63
|
-
|
64
|
-
# initialize our various member data to avoid warnings and so we'll
|
19
|
+
# Initialize our various member data to avoid warnings and so we'll
|
65
20
|
# automatically fall back to the youtube api defaults
|
66
|
-
@max_results
|
67
|
-
@
|
68
|
-
@
|
69
|
-
@
|
70
|
-
@response_format = nil
|
71
|
-
@video_format = nil
|
72
|
-
@racy = nil
|
73
|
-
@author = nil
|
74
|
-
|
75
|
-
# build up the url corresponding to this request
|
21
|
+
@max_results, @order_by,
|
22
|
+
@offset, @query,
|
23
|
+
@response_format, @video_format,
|
24
|
+
@racy, @author = nil
|
76
25
|
@url = base_url
|
77
26
|
|
78
|
-
#
|
27
|
+
# Return a single video (base_url + /T7YazwP8GtY)
|
79
28
|
return @url << "/" << params[:video_id] if params[:video_id]
|
80
29
|
|
81
30
|
@url << "/-/" if (params[:categories] || params[:tags])
|
82
31
|
@url << categories_to_params(params.delete(:categories)) if params[:categories]
|
83
32
|
@url << tags_to_params(params.delete(:tags)) if params[:tags]
|
84
33
|
|
85
|
-
params
|
86
|
-
name = key.to_s
|
87
|
-
instance_variable_set("@#{name}", value) if respond_to?(name)
|
88
|
-
end
|
34
|
+
set_instance_variables(params)
|
89
35
|
|
90
36
|
if( params[ :only_embeddable ] )
|
91
37
|
@video_format = ONLY_EMBEDDABLE
|
@@ -94,11 +40,13 @@ class YouTubeG
|
|
94
40
|
@url << build_query_params(to_youtube_params)
|
95
41
|
end
|
96
42
|
|
97
|
-
|
43
|
+
private
|
44
|
+
|
45
|
+
def base_url #:nodoc:
|
98
46
|
super << "videos"
|
99
47
|
end
|
100
48
|
|
101
|
-
def to_youtube_params
|
49
|
+
def to_youtube_params #:nodoc:
|
102
50
|
{
|
103
51
|
'max-results' => @max_results,
|
104
52
|
'orderby' => @order_by,
|
@@ -110,58 +58,35 @@ class YouTubeG
|
|
110
58
|
'author' => @author
|
111
59
|
}
|
112
60
|
end
|
113
|
-
|
114
|
-
private
|
115
|
-
# Convert category symbols into strings and build the URL. GData requires categories to be capitalized.
|
116
|
-
# Categories defined like: categories => { :include => [:news], :exclude => [:sports], :either => [..] }
|
117
|
-
# or like: categories => [:news, :sports]
|
118
|
-
def categories_to_params(categories)
|
119
|
-
if categories.respond_to?(:keys) and categories.respond_to?(:[])
|
120
|
-
s = ""
|
121
|
-
s << categories[:either].map { |c| c.to_s.capitalize }.join("%7C") << '/' if categories[:either]
|
122
|
-
s << categories[:include].map { |c| c.to_s.capitalize }.join("/") << '/' if categories[:include]
|
123
|
-
s << ("-" << categories[:exclude].map { |c| c.to_s.capitalize }.join("/-")) << '/' if categories[:exclude]
|
124
|
-
s
|
125
|
-
else
|
126
|
-
categories.map { |c| c.to_s.capitalize }.join("/") << '/'
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# Tags defined like: tags => { :include => [:football], :exclude => [:soccer], :either => [:polo, :tennis] }
|
131
|
-
# or tags => [:football, :soccer]
|
132
|
-
def tags_to_params(tags)
|
133
|
-
if tags.respond_to?(:keys) and tags.respond_to?(:[])
|
134
|
-
s = ""
|
135
|
-
s << tags[:either].map { |t| CGI.escape(t.to_s) }.join("%7C") << '/' if tags[:either]
|
136
|
-
s << tags[:include].map { |t| CGI.escape(t.to_s) }.join("/") << '/' if tags[:include]
|
137
|
-
s << ("-" << tags[:exclude].map { |t| CGI.escape(t.to_s) }.join("/-")) << '/' if tags[:exclude]
|
138
|
-
s
|
139
|
-
else
|
140
|
-
tags.map { |t| CGI.escape(t.to_s) }.join("/") << '/'
|
141
|
-
end
|
142
|
-
end
|
143
61
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
u << '&' if (item_count > 0)
|
157
|
-
u << "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
158
|
-
item_count += 1
|
159
|
-
end
|
160
|
-
|
161
|
-
# if we found no non-nil values, we've got no params so just
|
162
|
-
# return an empty string
|
163
|
-
(item_count == 0) ? '' : u
|
62
|
+
# Convert category symbols into strings and build the URL. GData requires categories to be capitalized.
|
63
|
+
# Categories defined like: categories => { :include => [:news], :exclude => [:sports], :either => [..] }
|
64
|
+
# or like: categories => [:news, :sports]
|
65
|
+
def categories_to_params(categories) #:nodoc:
|
66
|
+
if categories.respond_to?(:keys) and categories.respond_to?(:[])
|
67
|
+
s = ""
|
68
|
+
s << categories[:either].map { |c| c.to_s.capitalize }.join("%7C") << '/' if categories[:either]
|
69
|
+
s << categories[:include].map { |c| c.to_s.capitalize }.join("/") << '/' if categories[:include]
|
70
|
+
s << ("-" << categories[:exclude].map { |c| c.to_s.capitalize }.join("/-")) << '/' if categories[:exclude]
|
71
|
+
s
|
72
|
+
else
|
73
|
+
categories.map { |c| c.to_s.capitalize }.join("/") << '/'
|
164
74
|
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Tags defined like: tags => { :include => [:football], :exclude => [:soccer], :either => [:polo, :tennis] }
|
78
|
+
# or tags => [:football, :soccer]
|
79
|
+
def tags_to_params(tags) #:nodoc:
|
80
|
+
if tags.respond_to?(:keys) and tags.respond_to?(:[])
|
81
|
+
s = ""
|
82
|
+
s << tags[:either].map { |t| CGI.escape(t.to_s) }.join("%7C") << '/' if tags[:either]
|
83
|
+
s << tags[:include].map { |t| CGI.escape(t.to_s) }.join("/") << '/' if tags[:include]
|
84
|
+
s << ("-" << tags[:exclude].map { |t| CGI.escape(t.to_s) }.join("/-")) << '/' if tags[:exclude]
|
85
|
+
s
|
86
|
+
else
|
87
|
+
tags.map { |t| CGI.escape(t.to_s) }.join("/") << '/'
|
88
|
+
end
|
89
|
+
end
|
165
90
|
|
166
91
|
end
|
167
92
|
end
|
@@ -1,12 +1,8 @@
|
|
1
|
-
require 'net/https'
|
2
|
-
require 'digest/md5'
|
3
|
-
require 'rexml/document'
|
4
|
-
require 'cgi'
|
5
|
-
|
6
1
|
class YouTubeG
|
7
2
|
|
8
3
|
module Upload
|
9
4
|
class UploadError < Exception; end
|
5
|
+
class AuthenticationError < Exception; end
|
10
6
|
|
11
7
|
# require 'youtube_g'
|
12
8
|
#
|
@@ -32,8 +28,14 @@ class YouTubeG
|
|
32
28
|
# :description
|
33
29
|
# :category
|
34
30
|
# :keywords
|
31
|
+
# :private
|
32
|
+
# Specifying :private will make the video private, otherwise it will be public.
|
35
33
|
#
|
36
|
-
|
34
|
+
# When one of the fields is invalid according to YouTube,
|
35
|
+
# an UploadError will be returned. Its message contains a list of newline separated
|
36
|
+
# errors, containing the key and its error code.
|
37
|
+
#
|
38
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
37
39
|
def upload data, opts = {}
|
38
40
|
data = data.respond_to?(:read) ? data.read : data
|
39
41
|
@opts = { :mime_type => 'video/mp4',
|
@@ -56,6 +58,19 @@ class YouTubeG
|
|
56
58
|
|
57
59
|
Net::HTTP.start(base_url) do |upload|
|
58
60
|
response = upload.post('/feeds/api/users/' << @user << '/uploads', uploadBody, uploadHeader)
|
61
|
+
if response.code.to_i == 403
|
62
|
+
raise AuthenticationError, response.body[/<TITLE>(.+)<\/TITLE>/, 1]
|
63
|
+
elsif response.code.to_i != 201
|
64
|
+
upload_error = ''
|
65
|
+
xml = REXML::Document.new(response.body)
|
66
|
+
errors = xml.elements["//errors"]
|
67
|
+
errors.each do |error|
|
68
|
+
location = error.elements["location"].text[/media:group\/media:(.*)\/text\(\)/,1]
|
69
|
+
code = error.elements["code"].text
|
70
|
+
upload_error << sprintf("%s: %s\r\n", location, code)
|
71
|
+
end
|
72
|
+
raise UploadError, upload_error
|
73
|
+
end
|
59
74
|
xml = REXML::Document.new(response.body)
|
60
75
|
return xml.elements["//id"].text[/videos\/(.+)/, 1]
|
61
76
|
end
|
@@ -64,15 +79,15 @@ class YouTubeG
|
|
64
79
|
|
65
80
|
private
|
66
81
|
|
67
|
-
def base_url
|
82
|
+
def base_url #:nodoc:
|
68
83
|
"uploads.gdata.youtube.com"
|
69
84
|
end
|
70
85
|
|
71
|
-
def boundary
|
86
|
+
def boundary #:nodoc:
|
72
87
|
"An43094fu"
|
73
88
|
end
|
74
89
|
|
75
|
-
def auth_token
|
90
|
+
def auth_token #:nodoc:
|
76
91
|
unless @auth_token
|
77
92
|
http = Net::HTTP.new("www.google.com", 443)
|
78
93
|
http.use_ssl = true
|
@@ -85,18 +100,21 @@ class YouTubeG
|
|
85
100
|
@auth_token
|
86
101
|
end
|
87
102
|
|
88
|
-
def video_xml
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
103
|
+
def video_xml #:nodoc:
|
104
|
+
video_xml = ''
|
105
|
+
video_xml << '<?xml version="1.0"?>'
|
106
|
+
video_xml << '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007">'
|
107
|
+
video_xml << '<media:group>'
|
108
|
+
video_xml << '<media:title type="plain">%s</media:title>' % @opts[:title]
|
109
|
+
video_xml << '<media:description type="plain">%s</media:description>' % @opts[:description]
|
110
|
+
video_xml << '<media:keywords>%s</media:keywords>' % @opts[:keywords].join(",")
|
111
|
+
video_xml << '<media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">%s</media:category>' % @opts[:category]
|
112
|
+
video_xml << '<yt:private/>' if @opts[:private]
|
113
|
+
video_xml << '</media:group>'
|
114
|
+
video_xml << '</entry>'
|
97
115
|
end
|
98
116
|
|
99
|
-
def generate_upload_body(boundary, video_xml, data)
|
117
|
+
def generate_upload_body(boundary, video_xml, data) #:nodoc:
|
100
118
|
uploadBody = ""
|
101
119
|
uploadBody << "--#{boundary}\r\n"
|
102
120
|
uploadBody << "Content-Type: application/atom+xml; charset=UTF-8\r\n\r\n"
|
@@ -1,22 +1,22 @@
|
|
1
1
|
class YouTubeG
|
2
2
|
module Response
|
3
3
|
class VideoSearch < YouTubeG::Record
|
4
|
-
#
|
5
|
-
attr_reader :feed_id
|
4
|
+
# *String*:: Unique feed identifying url.
|
5
|
+
attr_reader :feed_id
|
6
6
|
|
7
|
-
#
|
7
|
+
# *Fixnum*:: Number of results per page.
|
8
8
|
attr_reader :max_result_count
|
9
9
|
|
10
|
-
#
|
10
|
+
# *Fixnum*:: 1-based offset index into the full result set.
|
11
11
|
attr_reader :offset
|
12
12
|
|
13
|
-
#
|
13
|
+
# *Fixnum*:: Total number of results available for the original request.
|
14
14
|
attr_reader :total_result_count
|
15
15
|
|
16
|
-
#
|
16
|
+
# *Time*:: Date and time at which the feed was last updated
|
17
17
|
attr_reader :updated_at
|
18
18
|
|
19
|
-
#
|
19
|
+
# *Array*:: Array of YouTubeG::Model::Video records
|
20
20
|
attr_reader :videos
|
21
21
|
end
|
22
22
|
end
|
data/test/test_client.rb
CHANGED
@@ -12,7 +12,7 @@ class TestClient < Test::Unit::TestCase
|
|
12
12
|
def test_should_respond_to_a_basic_query
|
13
13
|
response = @client.videos_by(:query => "penguin")
|
14
14
|
|
15
|
-
assert_equal "http://gdata.youtube.com/feeds/api/videos
|
15
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos", response.feed_id
|
16
16
|
assert_equal 25, response.max_result_count
|
17
17
|
assert_equal 25, response.videos.length
|
18
18
|
assert_equal 1, response.offset
|
@@ -22,10 +22,41 @@ class TestClient < Test::Unit::TestCase
|
|
22
22
|
response.videos.each { |v| assert_valid_video v }
|
23
23
|
end
|
24
24
|
|
25
|
+
def test_should_respond_to_a_basic_query_with_offset_and_max_results
|
26
|
+
response = @client.videos_by(:query => "penguin", :offset => 15, :max_results => 30)
|
27
|
+
|
28
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos", response.feed_id
|
29
|
+
assert_equal 30, response.max_result_count
|
30
|
+
assert_equal 30, response.videos.length
|
31
|
+
assert_equal 15, response.offset
|
32
|
+
assert(response.total_result_count > 100)
|
33
|
+
assert_instance_of Time, response.updated_at
|
34
|
+
|
35
|
+
response.videos.each { |v| assert_valid_video v }
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_respond_to_a_basic_query_with_paging
|
39
|
+
response = @client.videos_by(:query => "penguin")
|
40
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos", response.feed_id
|
41
|
+
assert_equal 25, response.max_result_count
|
42
|
+
assert_equal 1, response.offset
|
43
|
+
|
44
|
+
response = @client.videos_by(:query => "penguin", :page => 2)
|
45
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos", response.feed_id
|
46
|
+
assert_equal 25, response.max_result_count
|
47
|
+
assert_equal 26, response.offset
|
48
|
+
|
49
|
+
response2 = @client.videos_by(:query => "penguin", :page => 3)
|
50
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos", response2.feed_id
|
51
|
+
assert_equal 25, response2.max_result_count
|
52
|
+
assert_equal 51, response2.offset
|
53
|
+
end
|
54
|
+
|
55
|
+
|
25
56
|
def test_should_get_videos_for_multiword_metasearch_query
|
26
57
|
response = @client.videos_by(:query => 'christina ricci')
|
27
58
|
|
28
|
-
assert_equal "http://gdata.youtube.com/feeds/api/videos
|
59
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos", response.feed_id
|
29
60
|
assert_equal 25, response.max_result_count
|
30
61
|
assert_equal 25, response.videos.length
|
31
62
|
assert_equal 1, response.offset
|
@@ -109,19 +140,18 @@ class TestClient < Test::Unit::TestCase
|
|
109
140
|
# end
|
110
141
|
|
111
142
|
def test_should_get_videos_for_query_search_with_categories_excluded
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
response.videos.each { |v| assert_valid_video v }
|
116
|
-
end
|
117
|
-
|
118
|
-
def test_should_be_able_to_pass_in_logger
|
119
|
-
@client = YouTubeG::Client.new(Logger.new(STDOUT))
|
120
|
-
assert_not_nil @client.logger
|
143
|
+
video = @client.video_by("EkF4JD2rO3Q")
|
144
|
+
assert_equal "<object width=\"425\" height=\"350\">\n <param name=\"movie\" value=\"http://www.youtube.com/v/EkF4JD2rO3Q\"></param>\n <param name=\"wmode\" value=\"transparent\"></param>\n <embed src=\"http://www.youtube.com/v/EkF4JD2rO3Q\" type=\"application/x-shockwave-flash\" \n wmode=\"transparent\" width=\"425\" height=\"350\"></embed>\n</object>\n", video.embed_html
|
145
|
+
assert_valid_video video
|
121
146
|
end
|
122
147
|
|
123
|
-
def
|
148
|
+
def test_should_disable_debug_if_debug_is_set_to_false
|
124
149
|
@client = YouTubeG::Client.new
|
150
|
+
assert_nil @client.logger
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_should_enable_logger_if_debug_is_true
|
154
|
+
@client = YouTubeG::Client.new(true)
|
125
155
|
assert_not_nil @client.logger
|
126
156
|
end
|
127
157
|
|
@@ -129,14 +159,14 @@ class TestClient < Test::Unit::TestCase
|
|
129
159
|
response = @client.videos_by(:query => "avril lavigne girlfriend")
|
130
160
|
|
131
161
|
video = response.videos.first
|
132
|
-
assert !video.
|
162
|
+
assert !video.embeddable?
|
133
163
|
end
|
134
164
|
|
135
165
|
def test_should_determine_if_embeddable_video_is_embeddable
|
136
166
|
response = @client.videos_by(:query => "strongbad")
|
137
167
|
|
138
168
|
video = response.videos.first
|
139
|
-
assert video.
|
169
|
+
assert video.embeddable?
|
140
170
|
end
|
141
171
|
|
142
172
|
def test_should_retrieve_video_by_id
|
data/test/test_video.rb
CHANGED
@@ -23,8 +23,20 @@ class TestVideo < Test::Unit::TestCase
|
|
23
23
|
assert_equal 25, response.max_result_count
|
24
24
|
assert_equal 25, response.videos.length
|
25
25
|
assert_equal 1, response.offset
|
26
|
-
puts response.total_result_count
|
27
26
|
assert(response.total_result_count > 0)
|
28
27
|
assert_instance_of Time, response.updated_at
|
29
28
|
end
|
29
|
+
|
30
|
+
def test_should_have_response_videos
|
31
|
+
video = YouTubeG::Model::Video.new(:video_id => "http://gdata.youtube.com/feeds/videos/BDqs-OZWw9o")
|
32
|
+
response = video.responses
|
33
|
+
|
34
|
+
assert_equal "http://gdata.youtube.com/feeds/api/videos/BDqs-OZWw9o/responses", response.feed_id
|
35
|
+
assert_equal 25, response.max_result_count
|
36
|
+
assert_equal 25, response.videos.length
|
37
|
+
assert_equal 1, response.offset
|
38
|
+
assert(response.total_result_count > 0)
|
39
|
+
assert_instance_of Time, response.updated_at
|
40
|
+
end
|
41
|
+
|
30
42
|
end
|
data/test/test_video_search.rb
CHANGED
@@ -61,17 +61,27 @@ class TestVideoSearch < Test::Unit::TestCase
|
|
61
61
|
request = YouTubeG::Request::StandardSearch.new(:most_viewed)
|
62
62
|
assert_equal "http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed", request.url
|
63
63
|
end
|
64
|
+
|
65
|
+
def test_should_build_url_for_top_rated_for_today
|
66
|
+
request = YouTubeG::Request::StandardSearch.new(:top_rated, :time => :today)
|
67
|
+
assert_equal "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated?time=today", request.url
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_build_url_for_most_viewed_offset_and_max_results_without_time
|
71
|
+
request = YouTubeG::Request::StandardSearch.new(:top_rated, :offset => 5, :max_results => 10)
|
72
|
+
assert_equal "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated?max-results=10&start-index=5", request.url
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_should_build_url_for_most_viewed_offset_and_max_results_with_time
|
76
|
+
request = YouTubeG::Request::StandardSearch.new(:top_rated, :offset => 5, :max_results => 10, :time => :today)
|
77
|
+
assert_equal "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated?time=today&max-results=10&start-index=5", request.url
|
78
|
+
end
|
64
79
|
|
65
80
|
def test_should_raise_exception_for_invalid_type
|
66
81
|
assert_raise RuntimeError do
|
67
82
|
request = YouTubeG::Request::StandardSearch.new(:most_viewed_yo)
|
68
83
|
end
|
69
84
|
end
|
70
|
-
|
71
|
-
def test_should_build_url_for_top_rated_for_today
|
72
|
-
request = YouTubeG::Request::StandardSearch.new(:top_rated, :time => :today)
|
73
|
-
assert_equal "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated?time=today", request.url
|
74
|
-
end
|
75
85
|
|
76
86
|
# -- Complex Video Queries -------------------------------------------------------------------------
|
77
87
|
|