vibedeck-youtube_it 0.0.1

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.
Files changed (37) hide show
  1. data/README.rdoc +234 -0
  2. data/Rakefile +35 -0
  3. data/lib/youtube_it/chain_io.rb +76 -0
  4. data/lib/youtube_it/client.rb +367 -0
  5. data/lib/youtube_it/middleware/faraday_authheader.rb +24 -0
  6. data/lib/youtube_it/middleware/faraday_oauth.rb +21 -0
  7. data/lib/youtube_it/middleware/faraday_youtubeit.rb +30 -0
  8. data/lib/youtube_it/model/author.rb +13 -0
  9. data/lib/youtube_it/model/category.rb +11 -0
  10. data/lib/youtube_it/model/comment.rb +16 -0
  11. data/lib/youtube_it/model/contact.rb +16 -0
  12. data/lib/youtube_it/model/content.rb +18 -0
  13. data/lib/youtube_it/model/playlist.rb +11 -0
  14. data/lib/youtube_it/model/rating.rb +23 -0
  15. data/lib/youtube_it/model/subscription.rb +7 -0
  16. data/lib/youtube_it/model/thumbnail.rb +17 -0
  17. data/lib/youtube_it/model/user.rb +26 -0
  18. data/lib/youtube_it/model/video.rb +225 -0
  19. data/lib/youtube_it/parser.rb +357 -0
  20. data/lib/youtube_it/record.rb +12 -0
  21. data/lib/youtube_it/request/base_search.rb +72 -0
  22. data/lib/youtube_it/request/error.rb +15 -0
  23. data/lib/youtube_it/request/standard_search.rb +43 -0
  24. data/lib/youtube_it/request/user_search.rb +47 -0
  25. data/lib/youtube_it/request/video_search.rb +102 -0
  26. data/lib/youtube_it/request/video_upload.rb +415 -0
  27. data/lib/youtube_it/response/video_search.rb +41 -0
  28. data/lib/youtube_it/version.rb +4 -0
  29. data/lib/youtube_it.rb +75 -0
  30. data/test/helper.rb +10 -0
  31. data/test/test_chain_io.rb +63 -0
  32. data/test/test_client.rb +418 -0
  33. data/test/test_field_search.rb +48 -0
  34. data/test/test_video.rb +43 -0
  35. data/test/test_video_feed_parser.rb +271 -0
  36. data/test/test_video_search.rb +141 -0
  37. metadata +150 -0
@@ -0,0 +1,225 @@
1
+ # TODO
2
+ # * self atom feed
3
+ # * alternate youtube watch url
4
+ # * comments feedLink
5
+
6
+ class YouTubeIt
7
+ module Model
8
+ class Video < YouTubeIt::Record
9
+ # Describes the various file formats in which a Youtube video may be
10
+ # made available and allows looking them up by format code number.
11
+ class Format
12
+ @@formats = Hash.new
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
+ # YouTubeIt::Model::Video::Format: Video format object
22
+ def initialize(format_code, name)
23
+ @format_code = format_code
24
+ @name = name
25
+
26
+ @@formats[format_code] = self
27
+ end
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
+ # YouTubeIt::Model::Video::Format: Video format object
40
+ def self.by_code(format_code)
41
+ @@formats[format_code]
42
+ end
43
+
44
+ # Flash format on YouTube site. All videos are available in this format.
45
+ FLASH = YouTubeIt::Model::Video::Format.new(0, :flash)
46
+
47
+ # RTSP streaming URL for mobile video playback. H.263 video (176x144) and AMR audio.
48
+ RTSP = YouTubeIt::Model::Video::Format.new(1, :rtsp)
49
+
50
+ # HTTP URL to the embeddable player (SWF) for this video. This format
51
+ # is not available for a video that is not embeddable.
52
+ SWF = YouTubeIt::Model::Video::Format.new(5, :swf)
53
+
54
+ # RTSP streaming URL for mobile video playback. MPEG-4 SP video (up to 176x144) and AAC audio.
55
+ THREE_GPP = YouTubeIt::Model::Video::Format.new(6, :three_gpp)
56
+ end
57
+
58
+ # *Fixnum*:: Duration of a video in seconds.
59
+ attr_reader :duration
60
+
61
+ # *Boolean*:: Specifies that a video may or may not be 16:9 ratio.
62
+ attr_reader :widescreen
63
+
64
+ # *Boolean*:: Specifies that a video may or may not be embedded on other websites.
65
+ attr_reader :noembed
66
+
67
+ # *Fixnum*:: Specifies the order in which the video appears in a playlist.
68
+ attr_reader :position
69
+
70
+ # *Boolean*:: Specifies that a video is flagged as adult or not.
71
+ attr_reader :racy
72
+
73
+ # *String*: Specifies a URI that uniquely and permanently identifies the video.
74
+ attr_reader :video_id
75
+
76
+ # *Time*:: When the video was published on Youtube.
77
+ attr_reader :published_at
78
+
79
+ # *Time*:: When the video's data was last updated.
80
+ attr_reader :updated_at
81
+
82
+ # *Array*:: A array of YouTubeIt::Model::Category objects that describe the videos categories.
83
+ attr_reader :categories
84
+
85
+ # *Array*:: An array of words associated with the video.
86
+ attr_reader :keywords
87
+
88
+ # *String*:: Description of the video.
89
+ attr_reader :description
90
+
91
+ # *String*:: Title for the video.
92
+ attr_reader :title
93
+
94
+ # *String*:: Description of the video.
95
+ attr_reader :html_content
96
+
97
+ # YouTubeIt::Model::Author:: Information about the YouTube user who owns a piece of video content.
98
+ attr_reader :author
99
+
100
+ # *Array*:: An array of YouTubeIt::Model::Content objects describing the individual media content data available for this video. Most, but not all, videos offer this.
101
+ attr_reader :media_content
102
+
103
+ # *Array*:: An array of YouTubeIt::Model::Thumbnail objects that contain information regarding the videos thumbnail images.
104
+ attr_reader :thumbnails
105
+
106
+ # *String*:: The link to watch the URL on YouTubes website.
107
+ attr_reader :player_url
108
+
109
+ # YouTubeIt::Model::Rating:: Information about the videos rating.
110
+ attr_reader :rating
111
+
112
+ # *Fixnum*:: Number of times that the video has been viewed
113
+ attr_reader :view_count
114
+
115
+ # *Fixnum*:: Number of times that the video has been favorited
116
+ attr_reader :favorite_count
117
+
118
+ # *String*:: State of the video (processing, restricted, deleted, rejected and failed)
119
+ attr_reader :state
120
+
121
+
122
+ # Geodata
123
+ attr_reader :where
124
+ attr_reader :position
125
+ attr_reader :latitude
126
+ attr_reader :longitude
127
+
128
+ # Videos related to the current video.
129
+ #
130
+ # === Returns
131
+ # YouTubeIt::Response::VideoSearch
132
+ def related
133
+ YouTubeIt::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/related").parse
134
+ end
135
+
136
+ # Video responses to the current video.
137
+ #
138
+ # === Returns
139
+ # YouTubeIt::Response::VideoSearch
140
+ def responses
141
+ YouTubeIt::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/responses").parse
142
+ end
143
+
144
+ # The ID of the video, useful for searching for the video again without having to store it anywhere.
145
+ # A regular query search, with this id will return the same video.
146
+ #
147
+ # === Example
148
+ # >> video.unique_id
149
+ # => "ZTUVgYoeN_o"
150
+ #
151
+ # === Returns
152
+ # String: The Youtube video id.
153
+ def unique_id
154
+ video_id[/videos\/([^<]+)/, 1] || video_id[/video\:([^<]+)/, 1]
155
+ end
156
+
157
+ # Allows you to check whether the video can be embedded on a webpage.
158
+ #
159
+ # === Returns
160
+ # Boolean: True if the video can be embedded, false if not.
161
+ def embeddable?
162
+ not @noembed
163
+ end
164
+
165
+ # Allows you to check whether the video is widescreen (16:9) or not.
166
+ #
167
+ # === Returns
168
+ # Boolean: True if the video is (approximately) 16:9, false if not.
169
+ def widescreen?
170
+ @widescreen
171
+ end
172
+
173
+ # Provides a URL and various other types of information about a video.
174
+ #
175
+ # === Returns
176
+ # YouTubeIt::Model::Content: Data about the embeddable video.
177
+ def default_media_content
178
+ @media_content.find { |c| c.is_default? }
179
+ end
180
+
181
+ # Gives you the HTML to embed the video on your website.
182
+ #
183
+ # === Returns
184
+ # String: The HTML for embedding the video on your website.
185
+ def embed_html(width = 425, height = 350)
186
+ <<EDOC
187
+ <object width="#{width}" height="#{height}">
188
+ <param name="movie" value="#{embed_url}"></param>
189
+ <param name="wmode" value="transparent"></param>
190
+ <embed src="#{embed_url}" type="application/x-shockwave-flash"
191
+ wmode="transparent" width="#{width}" height="#{height}"></embed>
192
+ </object>
193
+ EDOC
194
+ end
195
+
196
+ # Gives you the HTML to embed the video on your website.
197
+ #
198
+ # === Returns
199
+ # String: The HTML for embedding the video on your website.
200
+ def embed_html_with_width(width = 1280)
201
+ height = (widescreen? ? width * 9/16 : width * 3/4) + 25
202
+
203
+ <<EDOC
204
+ <object width="#{width}" height="#{height}">
205
+ <param name="movie" value="#{embed_url}"></param>
206
+ <param name="wmode" value="transparent"></param>
207
+ <embed src="#{embed_url}" type="application/x-shockwave-flash"
208
+ wmode="transparent" width="#{width}" height="#{height}"></embed>
209
+ </object>
210
+ EDOC
211
+ end
212
+
213
+ # The URL needed for embedding the video in a page.
214
+ #
215
+ # === Returns
216
+ # String: Absolute URL for embedding video
217
+ def embed_url
218
+ @player_url.sub('watch?', '').sub('=', '/').sub('feature/', 'feature=')
219
+ end
220
+
221
+
222
+ end
223
+ end
224
+ end
225
+
@@ -0,0 +1,357 @@
1
+ class YouTubeIt
2
+ module Parser #:nodoc:
3
+ class FeedParser #:nodoc:
4
+ def initialize(content)
5
+ @content = open(content).read
6
+
7
+ rescue OpenURI::HTTPError => e
8
+ raise OpenURI::HTTPError.new(e.io.status[0],e)
9
+ rescue
10
+ @content = content
11
+
12
+ end
13
+
14
+ def parse
15
+ parse_content @content
16
+ end
17
+
18
+ def parse_videos
19
+ doc = REXML::Document.new(@content)
20
+ videos = []
21
+ doc.elements.each("*/entry") do |video|
22
+ videos << parse_entry(video)
23
+ end
24
+ videos
25
+ end
26
+ end
27
+
28
+ class CommentsFeedParser < FeedParser #:nodoc:
29
+ # return array of comments
30
+ def parse_content(content)
31
+ doc = REXML::Document.new(content.body)
32
+ feed = doc.elements["feed"]
33
+
34
+ comments = []
35
+ feed.elements.each("entry") do |entry|
36
+ comments << parse_entry(entry)
37
+ end
38
+ return comments
39
+ end
40
+
41
+ protected
42
+ def parse_entry(entry)
43
+ author = YouTubeIt::Model::Author.new(
44
+ :name => entry.elements["author"].elements["name"].text,
45
+ :uri => entry.elements["author"].elements["uri"].text,
46
+ :thumbnail_url => entry.elements["media:thumbnail"].attributes["url"]
47
+ )
48
+ YouTubeIt::Model::Comment.new(
49
+ :author => author,
50
+ :content => entry.elements["content"].text,
51
+ :published => entry.elements["published"].text,
52
+ :title => entry.elements["title"].text,
53
+ :updated => entry.elements["updated "].text,
54
+ :url => entry.elements["id"].text
55
+ )
56
+ end
57
+ end
58
+
59
+ class PlaylistFeedParser < FeedParser #:nodoc:
60
+
61
+ def parse_content(content)
62
+ xml = REXML::Document.new(content.body)
63
+ entry = xml.elements["entry"] || xml.elements["feed"]
64
+ YouTubeIt::Model::Playlist.new(
65
+ :title => entry.elements["title"].text,
66
+ :summary => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
67
+ :description => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
68
+ :playlist_id => entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':',''),
69
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil,
70
+ :response_code => content.status,
71
+ :xml => content.body)
72
+ end
73
+ end
74
+
75
+ class PlaylistsFeedParser < FeedParser #:nodoc:
76
+
77
+ # return array of playlist objects
78
+ def parse_content(content)
79
+ doc = REXML::Document.new(content.body)
80
+ feed = doc.elements["feed"]
81
+
82
+ playlists = []
83
+ feed.elements.each("entry") do |entry|
84
+ playlists << parse_entry(entry)
85
+ end
86
+ return playlists
87
+ end
88
+
89
+ protected
90
+
91
+ def parse_entry(entry)
92
+ YouTubeIt::Model::Playlist.new(
93
+ :title => entry.elements["title"].text,
94
+ :summary => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
95
+ :description => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
96
+ :playlist_id => entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':',''),
97
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil,
98
+ :response_code => nil,
99
+ :xml => nil)
100
+ end
101
+ end
102
+
103
+ class ProfileFeedParser < FeedParser #:nodoc:
104
+ def parse_content(content)
105
+ xml = REXML::Document.new(content.body)
106
+ entry = xml.elements["entry"] || xml.elements["feed"]
107
+ YouTubeIt::Model::User.new(
108
+ :age => entry.elements["yt:age"] ? entry.elements["yt:age"].text : nil,
109
+ :username => entry.elements["yt:username"] ? entry.elements["yt:username"].text : nil,
110
+ :company => entry.elements["yt:company"] ? entry.elements["yt:company"].text : nil,
111
+ :gender => entry.elements["yt:gender"] ? entry.elements["yt:gender"].text : nil,
112
+ :hobbies => entry.elements["yt:hobbies"] ? entry.elements["yt:hobbies"].text : nil,
113
+ :hometown => entry.elements["yt:hometown"] ? entry.elements["yt:hometown"].text : nil,
114
+ :location => entry.elements["yt:location"] ? entry.elements["yt:location"].text : nil,
115
+ :last_login => entry.elements["yt:statistics"].attributes["lastWebAccess"],
116
+ :join_date => entry.elements["published"] ? entry.elements["published"].text : nil,
117
+ :movies => entry.elements["yt:movies"] ? entry.elements["yt:movies"].text : nil,
118
+ :music => entry.elements["yt:music"] ? entry.elements["yt:music"].text : nil,
119
+ :occupation => entry.elements["yt:occupation"] ? entry.elements["yt:occupation"].text : nil,
120
+ :relationship => entry.elements["yt:relationship"] ? entry.elements["yt:relationship"].text : nil,
121
+ :school => entry.elements["yt:school"] ? entry.elements["yt:school"].text : nil,
122
+ :subscribers => entry.elements["yt:statistics"].attributes["subscriberCount"],
123
+ :videos_watched => entry.elements["yt:statistics"].attributes["videoWatchCount"],
124
+ :view_count => entry.elements["yt:statistics"].attributes["viewCount"],
125
+ :upload_views => entry.elements["yt:statistics"].attributes["totalUploadViews"]
126
+ )
127
+ end
128
+ end
129
+
130
+ class SubscriptionFeedParser < FeedParser #:nodoc:
131
+
132
+ def parse_content(content)
133
+ doc = REXML::Document.new(content.body)
134
+ feed = doc.elements["feed"]
135
+
136
+ subscriptions = []
137
+ feed.elements.each("entry") do |entry|
138
+ subscriptions << parse_entry(entry)
139
+ end
140
+ return subscriptions
141
+ end
142
+
143
+ protected
144
+
145
+ def parse_entry(entry)
146
+ YouTubeIt::Model::Subscription.new(
147
+ :title => entry.elements["title"].text,
148
+ :id => entry.elements["id"].text[/subscription([^<]+)/, 1].sub(':',''),
149
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil
150
+ )
151
+ end
152
+ end
153
+
154
+
155
+ class VideoFeedParser < FeedParser #:nodoc:
156
+
157
+ def parse_content(content)
158
+ doc = REXML::Document.new(content)
159
+ entry = doc.elements["entry"]
160
+ parse_entry(entry)
161
+ end
162
+
163
+ protected
164
+ def parse_entry(entry)
165
+ video_id = entry.elements["id"].text
166
+ published_at = entry.elements["published"] ? Time.parse(entry.elements["published"].text) : nil
167
+ updated_at = entry.elements["updated"] ? Time.parse(entry.elements["updated"].text) : nil
168
+
169
+ # parse the category and keyword lists
170
+ categories = []
171
+ keywords = []
172
+ entry.elements.each("category") do |category|
173
+ # determine if it's really a category, or just a keyword
174
+ scheme = category.attributes["scheme"]
175
+ if (scheme =~ /\/categories\.cat$/)
176
+ # it's a category
177
+ categories << YouTubeIt::Model::Category.new(
178
+ :term => category.attributes["term"],
179
+ :label => category.attributes["label"])
180
+
181
+ elsif (scheme =~ /\/keywords\.cat$/)
182
+ # it's a keyword
183
+ keywords << category.attributes["term"]
184
+ end
185
+ end
186
+
187
+ title = entry.elements["title"].text
188
+ html_content = entry.elements["content"] ? entry.elements["content"].text : nil
189
+
190
+ # parse the author
191
+ author_element = entry.elements["author"]
192
+ author = nil
193
+ if author_element
194
+ author = YouTubeIt::Model::Author.new(
195
+ :name => author_element.elements["name"].text,
196
+ :uri => author_element.elements["uri"].text)
197
+ end
198
+ media_group = entry.elements["media:group"]
199
+
200
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
201
+ description = ""
202
+ unless media_group.elements["media:description"].nil?
203
+ description = media_group.elements["media:description"].text
204
+ end
205
+
206
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
207
+ duration = 0
208
+ unless media_group.elements["yt:duration"].nil?
209
+ duration = media_group.elements["yt:duration"].attributes["seconds"].to_i
210
+ end
211
+
212
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
213
+ player_url = ""
214
+ unless media_group.elements["media:player"].nil?
215
+ player_url = media_group.elements["media:player"].attributes["url"]
216
+ end
217
+
218
+ unless media_group.elements["yt:aspectRatio"].nil?
219
+ widescreen = media_group.elements["yt:aspectRatio"].text == 'widescreen' ? true : false
220
+ end
221
+
222
+ media_content = []
223
+ media_group.elements.each("media:content") do |mce|
224
+ media_content << parse_media_content(mce)
225
+ end
226
+
227
+ # parse thumbnails
228
+ thumbnails = []
229
+ media_group.elements.each("media:thumbnail") do |thumb_element|
230
+ # TODO: convert time HH:MM:ss string to seconds?
231
+ thumbnails << YouTubeIt::Model::Thumbnail.new(
232
+ :url => thumb_element.attributes["url"],
233
+ :height => thumb_element.attributes["height"].to_i,
234
+ :width => thumb_element.attributes["width"].to_i,
235
+ :time => thumb_element.attributes["time"])
236
+ end
237
+
238
+ rating_element = entry.elements["gd:rating"]
239
+ extended_rating_element = entry.elements["yt:rating"]
240
+
241
+ rating = nil
242
+ if rating_element
243
+ rating_values = {
244
+ :min => rating_element.attributes["min"].to_i,
245
+ :max => rating_element.attributes["max"].to_i,
246
+ :rater_count => rating_element.attributes["numRaters"].to_i,
247
+ :average => rating_element.attributes["average"].to_f
248
+ }
249
+
250
+ if extended_rating_element
251
+ rating_values[:likes] = extended_rating_element.attributes["numLikes"].to_i
252
+ rating_values[:dislikes] = extended_rating_element.attributes["numDislikes"].to_i
253
+ end
254
+
255
+ rating = YouTubeIt::Model::Rating.new(rating_values)
256
+ end
257
+
258
+ if (el = entry.elements["yt:statistics"])
259
+ view_count, favorite_count = el.attributes["viewCount"].to_i, el.attributes["favoriteCount"].to_i
260
+ else
261
+ view_count, favorite_count = 0,0
262
+ end
263
+
264
+ noembed = entry.elements["yt:noembed"] ? true : false
265
+ racy = entry.elements["media:rating"] ? true : false
266
+
267
+ if where = entry.elements["georss:where"]
268
+ position = where.elements["gml:Point"].elements["gml:pos"].text
269
+ latitude, longitude = position.split(" ")
270
+ end
271
+
272
+ control = entry.elements["app:control"]
273
+ state = { :name => "published" }
274
+ if control && control.elements["yt:state"]
275
+ state = {
276
+ :name => control.elements["yt:state"].attributes["name"],
277
+ :reason_code => control.elements["yt:state"].attributes["reasonCode"],
278
+ :help_url => control.elements["yt:state"].attributes["helpUrl"],
279
+ :copy => control.elements["yt:state"].text
280
+ }
281
+
282
+ end
283
+
284
+ YouTubeIt::Model::Video.new(
285
+ :video_id => video_id,
286
+ :published_at => published_at,
287
+ :updated_at => updated_at,
288
+ :categories => categories,
289
+ :keywords => keywords,
290
+ :title => title,
291
+ :html_content => html_content,
292
+ :author => author,
293
+ :description => description,
294
+ :duration => duration,
295
+ :media_content => media_content,
296
+ :player_url => player_url,
297
+ :thumbnails => thumbnails,
298
+ :rating => rating,
299
+ :view_count => view_count,
300
+ :favorite_count => favorite_count,
301
+ :widescreen => widescreen,
302
+ :noembed => noembed,
303
+ :racy => racy,
304
+ :where => where,
305
+ :position => position,
306
+ :latitude => latitude,
307
+ :longitude => longitude,
308
+ :state => state)
309
+ end
310
+
311
+ def parse_media_content (media_content_element)
312
+ content_url = media_content_element.attributes["url"]
313
+ format_code = media_content_element.attributes["yt:format"].to_i
314
+ format = YouTubeIt::Model::Video::Format.by_code(format_code)
315
+ duration = media_content_element.attributes["duration"].to_i
316
+ mime_type = media_content_element.attributes["type"]
317
+ default = (media_content_element.attributes["isDefault"] == "true")
318
+
319
+ YouTubeIt::Model::Content.new(
320
+ :url => content_url,
321
+ :format => format,
322
+ :duration => duration,
323
+ :mime_type => mime_type,
324
+ :default => default)
325
+ end
326
+ end
327
+
328
+ class VideosFeedParser < VideoFeedParser #:nodoc:
329
+
330
+ private
331
+ def parse_content(content)
332
+ videos = []
333
+ doc = REXML::Document.new(content)
334
+ feed = doc.elements["feed"]
335
+ if feed
336
+ feed_id = feed.elements["id"].text
337
+ updated_at = Time.parse(feed.elements["updated"].text)
338
+ total_result_count = feed.elements["openSearch:totalResults"].text.to_i
339
+ offset = feed.elements["openSearch:startIndex"].text.to_i
340
+ max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i
341
+
342
+ feed.elements.each("entry") do |entry|
343
+ videos << parse_entry(entry)
344
+ end
345
+ end
346
+ YouTubeIt::Response::VideoSearch.new(
347
+ :feed_id => feed_id || nil,
348
+ :updated_at => updated_at || nil,
349
+ :total_result_count => total_result_count || nil,
350
+ :offset => offset || nil,
351
+ :max_result_count => max_result_count || nil,
352
+ :videos => videos)
353
+ end
354
+ end
355
+ end
356
+ end
357
+
@@ -0,0 +1,12 @@
1
+ class YouTubeIt
2
+ class Record #:nodoc:
3
+ def initialize (params)
4
+ return if params.nil?
5
+
6
+ params.each do |key, value|
7
+ name = key.to_s
8
+ instance_variable_set("@#{name}", value) if respond_to?(name)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,72 @@
1
+ class YouTubeIt
2
+ module Request #:nodoc:
3
+ class BaseSearch #:nodoc:
4
+ attr_reader :url
5
+
6
+ private
7
+
8
+ def base_url
9
+ "http://gdata.youtube.com/feeds/api/"
10
+ end
11
+
12
+ def set_instance_variables( variables )
13
+ variables.each do |key, value|
14
+ name = key.to_s
15
+ instance_variable_set("@#{name}", value) if respond_to?(name)
16
+ end
17
+ end
18
+
19
+ def build_query_params(params)
20
+ qs = params.to_a.map { | k, v | v.nil? ? nil : "#{YouTubeIt.esc(k)}=#{YouTubeIt.esc(v)}" }.compact.sort.join('&')
21
+ qs.empty? ? '' : (@dev_key ? "?#{qs}&key=#{@dev_key}" : "?#{qs}")
22
+ end
23
+ end
24
+
25
+
26
+ module FieldSearch
27
+ def default_fields
28
+ "id,updated,openSearch:totalResults,openSearch:startIndex,openSearch:itemsPerPage"
29
+ end
30
+
31
+ def fields_to_params(fields)
32
+ return "" unless fields
33
+
34
+ fields_param = [default_fields]
35
+
36
+ if fields[:recorded]
37
+ if fields[:recorded].is_a? Range
38
+ fields_param << "entry[xs:date(yt:recorded) > xs:date('#{formatted_date(fields[:recorded].first)}') and xs:date(yt:recorded) < xs:date('#{formatted_date(fields[:recorded].last)}')]"
39
+ else
40
+ fields_param << "entry[xs:date(yt:recorded) = xs:date('#{formatted_date(fields[:recorded])}')]"
41
+ end
42
+ end
43
+
44
+ if fields[:published]
45
+ if fields[:published].is_a? Range
46
+ fields_param << "entry[xs:dateTime(published) > xs:dateTime('#{formatted_date(fields[:published].first)}T00:00:00') and xs:dateTime(published) < xs:dateTime('#{formatted_date(fields[:published].last)}T00:00:00')]"
47
+ else
48
+ fields_param << "entry[xs:date(published) = xs:date('#{formatted_date(fields[:published])}')]"
49
+ end
50
+ end
51
+
52
+ if fields[:view_count]
53
+ fields_param << "entry[yt:statistics/@viewCount > #{fields[:view_count]}]"
54
+ end
55
+
56
+
57
+ return "&fields=#{URI.escape(fields_param.join(","))}"
58
+ end
59
+
60
+ #youtube taked dates that look like 'YYYY-MM-DD'
61
+ def formatted_date(date)
62
+ return date if date.is_a? String
63
+ if date.respond_to? :strftime
64
+ date.strftime("%Y-%m-%d")
65
+ else
66
+ ""
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,15 @@
1
+ class UploadError < YouTubeIt::Error
2
+ attr_reader :code
3
+ def initialize(msg, code = 0)
4
+ super(msg)
5
+ @code = code
6
+ end
7
+ end
8
+
9
+ class AuthenticationError < YouTubeIt::Error
10
+ attr_reader :code
11
+ def initialize(msg, code = 0)
12
+ super(msg)
13
+ @code = code
14
+ end
15
+ end