slainer68_youtube_it 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/Gemfile +10 -0
  2. data/Gemfile.lock +27 -0
  3. data/Manifest.txt +37 -0
  4. data/README.rdoc +270 -0
  5. data/Rakefile +53 -0
  6. data/VERSION +1 -0
  7. data/lib/youtube_it/chain_io.rb +76 -0
  8. data/lib/youtube_it/client.rb +447 -0
  9. data/lib/youtube_it/middleware/faraday_authheader.rb +24 -0
  10. data/lib/youtube_it/middleware/faraday_oauth.rb +21 -0
  11. data/lib/youtube_it/middleware/faraday_oauth2.rb +13 -0
  12. data/lib/youtube_it/middleware/faraday_youtubeit.rb +30 -0
  13. data/lib/youtube_it/model/activity.rb +17 -0
  14. data/lib/youtube_it/model/author.rb +13 -0
  15. data/lib/youtube_it/model/category.rb +11 -0
  16. data/lib/youtube_it/model/comment.rb +16 -0
  17. data/lib/youtube_it/model/contact.rb +19 -0
  18. data/lib/youtube_it/model/content.rb +18 -0
  19. data/lib/youtube_it/model/message.rb +12 -0
  20. data/lib/youtube_it/model/playlist.rb +11 -0
  21. data/lib/youtube_it/model/rating.rb +23 -0
  22. data/lib/youtube_it/model/subscription.rb +7 -0
  23. data/lib/youtube_it/model/thumbnail.rb +17 -0
  24. data/lib/youtube_it/model/user.rb +27 -0
  25. data/lib/youtube_it/model/video.rb +239 -0
  26. data/lib/youtube_it/parser.rb +534 -0
  27. data/lib/youtube_it/record.rb +12 -0
  28. data/lib/youtube_it/request/base_search.rb +72 -0
  29. data/lib/youtube_it/request/error.rb +15 -0
  30. data/lib/youtube_it/request/standard_search.rb +43 -0
  31. data/lib/youtube_it/request/user_search.rb +47 -0
  32. data/lib/youtube_it/request/video_search.rb +102 -0
  33. data/lib/youtube_it/request/video_upload.rb +538 -0
  34. data/lib/youtube_it/response/video_search.rb +41 -0
  35. data/lib/youtube_it/version.rb +4 -0
  36. data/lib/youtube_it.rb +83 -0
  37. data/slainer68_youtube_it.gemspec +102 -0
  38. data/test/files/recorded_response.xml +1 -0
  39. data/test/files/youtube_video_response.xml +53 -0
  40. data/test/helper.rb +9 -0
  41. data/test/test.mov +0 -0
  42. data/test/test_chain_io.rb +63 -0
  43. data/test/test_client.rb +435 -0
  44. data/test/test_field_search.rb +48 -0
  45. data/test/test_video.rb +48 -0
  46. data/test/test_video_feed_parser.rb +271 -0
  47. data/test/test_video_search.rb +141 -0
  48. metadata +172 -0
@@ -0,0 +1,534 @@
1
+ class YouTubeIt
2
+ module Parser #:nodoc:
3
+ class FeedParser #:nodoc:
4
+ def initialize(content)
5
+ @content = (content =~ URI::regexp(%w(http https)) ? open(content).read : content)
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 rescue nil),
45
+ :uri => (entry.elements["author"].elements["uri"].text rescue nil)
46
+ )
47
+ YouTubeIt::Model::Comment.new(
48
+ :author => author,
49
+ :content => entry.elements["content"].text,
50
+ :published => entry.elements["published"].text,
51
+ :title => entry.elements["title"].text,
52
+ :updated => entry.elements["updated "].text,
53
+ :url => entry.elements["id"].text
54
+ )
55
+ end
56
+ end
57
+
58
+ class PlaylistFeedParser < FeedParser #:nodoc:
59
+
60
+ def parse_content(content)
61
+ xml = REXML::Document.new(content.body)
62
+ entry = xml.elements["entry"] || xml.elements["feed"]
63
+ YouTubeIt::Model::Playlist.new(
64
+ :title => entry.elements["title"].text,
65
+ :summary => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
66
+ :description => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
67
+ :playlist_id => entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':',''),
68
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil,
69
+ :response_code => content.status,
70
+ :xml => content.body)
71
+ end
72
+ end
73
+
74
+ class PlaylistsFeedParser < FeedParser #:nodoc:
75
+
76
+ # return array of playlist objects
77
+ def parse_content(content)
78
+ doc = REXML::Document.new(content.body)
79
+ feed = doc.elements["feed"]
80
+
81
+ playlists = []
82
+ feed.elements.each("entry") do |entry|
83
+ playlists << parse_entry(entry)
84
+ end
85
+ return playlists
86
+ end
87
+
88
+ protected
89
+
90
+ def parse_entry(entry)
91
+ YouTubeIt::Model::Playlist.new(
92
+ :title => entry.elements["title"].text,
93
+ :summary => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
94
+ :description => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
95
+ :playlist_id => entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':',''),
96
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil,
97
+ :response_code => nil,
98
+ :xml => nil)
99
+ end
100
+ end
101
+
102
+ # Returns an array of the user's activity
103
+ class ActivityParser < FeedParser
104
+ def parse_content(content)
105
+ doc = REXML::Document.new(content.body)
106
+ feed = doc.elements["feed"]
107
+
108
+ activity = []
109
+ feed.elements.each("entry") do |entry|
110
+ parsed_activity = parse_activity(entry)
111
+ if parsed_activity
112
+ activity << parsed_activity
113
+ end
114
+ end
115
+
116
+ return activity
117
+ end
118
+
119
+ protected
120
+
121
+ # Parses the user's activity feed.
122
+ def parse_activity(entry)
123
+ # Figure out what kind of activity we have
124
+ video_type = nil
125
+ parsed_activity = nil
126
+ entry.elements.each("category") do |category_tag|
127
+ if category_tag.attributes["scheme"]=="http://gdata.youtube.com/schemas/2007/userevents.cat"
128
+ video_type = category_tag.attributes["term"]
129
+ end
130
+ end
131
+
132
+ if video_type
133
+ case video_type
134
+ when "video_rated"
135
+ parsed_activity = YouTubeIt::Model::Activity.new(
136
+ :type => "video_rated",
137
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
138
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
139
+ :videos => parse_activity_videos(entry),
140
+ :video_id => entry.elements["yt:videoid"] ? entry.elements["yt:videoid"].text : nil
141
+ )
142
+ when "video_shared"
143
+ parsed_activity = YouTubeIt::Model::Activity.new(
144
+ :type => "video_shared",
145
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
146
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
147
+ :videos => parse_activity_videos(entry),
148
+ :video_id => entry.elements["yt:videoid"] ? entry.elements["yt:videoid"].text : nil
149
+ )
150
+ when "video_favorited"
151
+ parsed_activity = YouTubeIt::Model::Activity.new(
152
+ :type => "video_favorited",
153
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
154
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
155
+ :videos => parse_activity_videos(entry),
156
+ :video_id => entry.elements["yt:videoid"] ? entry.elements["yt:videoid"].text : nil
157
+ )
158
+ when "video_commented"
159
+ # Load the comment and video URL
160
+ comment_thread_url = nil
161
+ video_url = nil
162
+ entry.elements.each("link") do |link_tag|
163
+ case link_tag.attributes["rel"]
164
+ when "http://gdata.youtube.com/schemas/2007#comments"
165
+ comment_thread_url = link_tag.attributes["href"]
166
+ when "http://gdata.youtube.com/schemas/2007#video"
167
+ video_url = link_tag.attributes["href"]
168
+ else
169
+ # Invalid rel type, do nothing
170
+ end
171
+ end
172
+
173
+ parsed_activity = YouTubeIt::Model::Activity.new(
174
+ :type => "video_commented",
175
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
176
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
177
+ :videos => parse_activity_videos(entry),
178
+ :video_id => entry.elements["yt:videoid"] ? entry.elements["yt:videoid"].text : nil,
179
+ :comment_thread_url => comment_thread_url,
180
+ :video_url => video_url
181
+ )
182
+ when "video_uploaded"
183
+ parsed_activity = YouTubeIt::Model::Activity.new(
184
+ :type => "video_uploaded",
185
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
186
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
187
+ :videos => parse_activity_videos(entry),
188
+ :video_id => entry.elements["yt:videoid"] ? entry.elements["yt:videoid"].text : nil
189
+ )
190
+ when "friend_added"
191
+ parsed_activity = YouTubeIt::Model::Activity.new(
192
+ :type => "friend_added",
193
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
194
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
195
+ :username => entry.elements["yt:username"] ? entry.elements["yt:username"].text : nil
196
+ )
197
+ when "user_subscription_added"
198
+ parsed_activity = YouTubeIt::Model::Activity.new(
199
+ :type => "user_subscription_added",
200
+ :time => entry.elements["updated"] ? entry.elements["updated"].text : nil,
201
+ :author => entry.elements["author"].elements["name"] ? entry.elements["author"].elements["name"].text : nil,
202
+ :username => entry.elements["yt:username"] ? entry.elements["yt:username"].text : nil
203
+ )
204
+ else
205
+ # Invalid activity type, just let it return nil
206
+ end
207
+ end
208
+
209
+ return parsed_activity
210
+ end
211
+
212
+ # If a user enabled inline attribute videos may be included in results.
213
+ def parse_activity_videos(entry)
214
+ videos = []
215
+
216
+ entry.elements.each("link") do |link_tag|
217
+ if link_tag.attributes["rel"]=="http://gdata.youtube.com/schemas/2007#video"
218
+ videos << YouTubeIt::Parser::VideoFeedParser.new(link_tag).parse
219
+ end
220
+ end
221
+
222
+ if videos.size <= 0
223
+ videos = nil
224
+ end
225
+
226
+ return videos
227
+ end
228
+ end
229
+
230
+ # Returns an array of the user's contacts
231
+ class ContactsParser < FeedParser
232
+ def parse_content(content)
233
+ doc = REXML::Document.new(content.body)
234
+ feed = doc.elements["feed"]
235
+
236
+ contacts = []
237
+ feed.elements.each("entry") do |entry|
238
+ temp_contact = YouTubeIt::Model::Contact.new(
239
+ :title => entry.elements["title"] ? entry.elements["title"].text : nil,
240
+ :username => entry.elements["yt:username"] ? entry.elements["yt:username"].text : nil,
241
+ :status => entry.elements["yt:status"] ? entry.elements["yt:status"].text : nil
242
+ )
243
+
244
+ contacts << temp_contact
245
+ end
246
+
247
+ return contacts
248
+ end
249
+ end
250
+
251
+ # Returns an array of the user's messages
252
+ class MessagesParser < FeedParser
253
+ def parse_content(content)
254
+ doc = REXML::Document.new(content.body)
255
+ puts content.body
256
+ puts "doc..."
257
+ puts doc.inspect
258
+ feed = doc.elements["feed"]
259
+
260
+ messages = []
261
+ feed.elements.each("entry") do |entry|
262
+ author = entry.elements["author"]
263
+ temp_message = YouTubeIt::Model::Message.new(
264
+ :id => entry.elements["id"] ? entry.elements["id"].text.gsub(/.+:inbox:/, "") : nil,
265
+ :title => entry.elements["title"] ? entry.elements["title"].text : nil,
266
+ :name => author && author.elements["name"] ? author.elements["name"].text : nil,
267
+ :summary => entry.elements["summary"] ? entry.elements["summary"].text : nil,
268
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil
269
+ )
270
+
271
+ messages << temp_message
272
+ end
273
+
274
+ return messages
275
+ end
276
+ end
277
+
278
+ class ProfileFeedParser < FeedParser #:nodoc:
279
+ def parse_content(content)
280
+ xml = REXML::Document.new(content.body)
281
+ entry = xml.elements["entry"] || xml.elements["feed"]
282
+ YouTubeIt::Model::User.new(
283
+ :age => entry.elements["yt:age"] ? entry.elements["yt:age"].text : nil,
284
+ :username => entry.elements["yt:username"] ? entry.elements["yt:username"].text : nil,
285
+ :company => entry.elements["yt:company"] ? entry.elements["yt:company"].text : nil,
286
+ :gender => entry.elements["yt:gender"] ? entry.elements["yt:gender"].text : nil,
287
+ :hobbies => entry.elements["yt:hobbies"] ? entry.elements["yt:hobbies"].text : nil,
288
+ :hometown => entry.elements["yt:hometown"] ? entry.elements["yt:hometown"].text : nil,
289
+ :location => entry.elements["yt:location"] ? entry.elements["yt:location"].text : nil,
290
+ :last_login => entry.elements["yt:statistics"].attributes["lastWebAccess"],
291
+ :join_date => entry.elements["published"] ? entry.elements["published"].text : nil,
292
+ :movies => entry.elements["yt:movies"] ? entry.elements["yt:movies"].text : nil,
293
+ :music => entry.elements["yt:music"] ? entry.elements["yt:music"].text : nil,
294
+ :occupation => entry.elements["yt:occupation"] ? entry.elements["yt:occupation"].text : nil,
295
+ :relationship => entry.elements["yt:relationship"] ? entry.elements["yt:relationship"].text : nil,
296
+ :school => entry.elements["yt:school"] ? entry.elements["yt:school"].text : nil,
297
+ :avatar => entry.elements["media:thumbnail"] ? entry.elements["media:thumbnail"].attributes["url"] : nil,
298
+ :subscribers => entry.elements["yt:statistics"].attributes["subscriberCount"],
299
+ :videos_watched => entry.elements["yt:statistics"].attributes["videoWatchCount"],
300
+ :view_count => entry.elements["yt:statistics"].attributes["viewCount"],
301
+ :upload_views => entry.elements["yt:statistics"].attributes["totalUploadViews"]
302
+ )
303
+ end
304
+ end
305
+
306
+ class SubscriptionFeedParser < FeedParser #:nodoc:
307
+
308
+ def parse_content(content)
309
+ doc = REXML::Document.new(content.body)
310
+ feed = doc.elements["feed"]
311
+
312
+ subscriptions = []
313
+ feed.elements.each("entry") do |entry|
314
+ subscriptions << parse_entry(entry)
315
+ end
316
+ return subscriptions
317
+ end
318
+
319
+ protected
320
+
321
+ def parse_entry(entry)
322
+ YouTubeIt::Model::Subscription.new(
323
+ :title => entry.elements["title"].text,
324
+ :id => entry.elements["id"].text[/subscription([^<]+)/, 1].sub(':',''),
325
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil
326
+ )
327
+ end
328
+ end
329
+
330
+
331
+ class VideoFeedParser < FeedParser #:nodoc:
332
+
333
+ def parse_content(content)
334
+ doc = (content.is_a?(REXML::Element)) ? content : REXML::Document.new(content)
335
+
336
+ entry = doc.elements["entry"]
337
+ parse_entry(entry)
338
+ end
339
+
340
+ protected
341
+ def parse_entry(entry)
342
+ video_id = entry.elements["id"].text
343
+ published_at = entry.elements["published"] ? Time.parse(entry.elements["published"].text) : nil
344
+ updated_at = entry.elements["updated"] ? Time.parse(entry.elements["updated"].text) : nil
345
+
346
+ # parse the category and keyword lists
347
+ categories = []
348
+ keywords = []
349
+ entry.elements.each("category") do |category|
350
+ # determine if it's really a category, or just a keyword
351
+ scheme = category.attributes["scheme"]
352
+ if (scheme =~ /\/categories\.cat$/)
353
+ # it's a category
354
+ categories << YouTubeIt::Model::Category.new(
355
+ :term => category.attributes["term"],
356
+ :label => category.attributes["label"])
357
+
358
+ elsif (scheme =~ /\/keywords\.cat$/)
359
+ # it's a keyword
360
+ keywords << category.attributes["term"]
361
+ end
362
+ end
363
+
364
+ title = entry.elements["title"].text
365
+ html_content = entry.elements["content"] ? entry.elements["content"].text : nil
366
+
367
+ # parse the author
368
+ author_element = entry.elements["author"]
369
+ author = nil
370
+ if author_element
371
+ author = YouTubeIt::Model::Author.new(
372
+ :name => author_element.elements["name"].text,
373
+ :uri => author_element.elements["uri"].text)
374
+ end
375
+ media_group = entry.elements["media:group"]
376
+
377
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
378
+ description = ""
379
+ unless media_group.elements["media:description"].nil?
380
+ description = media_group.elements["media:description"].text
381
+ end
382
+
383
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
384
+ duration = 0
385
+ unless media_group.elements["yt:duration"].nil?
386
+ duration = media_group.elements["yt:duration"].attributes["seconds"].to_i
387
+ end
388
+
389
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
390
+ player_url = ""
391
+ unless media_group.elements["media:player"].nil?
392
+ player_url = media_group.elements["media:player"].attributes["url"]
393
+ end
394
+
395
+ unless media_group.elements["yt:aspectRatio"].nil?
396
+ widescreen = media_group.elements["yt:aspectRatio"].text == 'widescreen' ? true : false
397
+ end
398
+
399
+ media_content = []
400
+ media_group.elements.each("media:content") do |mce|
401
+ media_content << parse_media_content(mce)
402
+ end
403
+
404
+ # parse thumbnails
405
+ thumbnails = []
406
+ media_group.elements.each("media:thumbnail") do |thumb_element|
407
+ # TODO: convert time HH:MM:ss string to seconds?
408
+ thumbnails << YouTubeIt::Model::Thumbnail.new(
409
+ :url => thumb_element.attributes["url"],
410
+ :height => thumb_element.attributes["height"].to_i,
411
+ :width => thumb_element.attributes["width"].to_i,
412
+ :time => thumb_element.attributes["time"])
413
+ end
414
+
415
+ rating_element = entry.elements["gd:rating"]
416
+ extended_rating_element = entry.elements["yt:rating"]
417
+
418
+ rating = nil
419
+ if rating_element
420
+ rating_values = {
421
+ :min => rating_element.attributes["min"].to_i,
422
+ :max => rating_element.attributes["max"].to_i,
423
+ :rater_count => rating_element.attributes["numRaters"].to_i,
424
+ :average => rating_element.attributes["average"].to_f
425
+ }
426
+
427
+ if extended_rating_element
428
+ rating_values[:likes] = extended_rating_element.attributes["numLikes"].to_i
429
+ rating_values[:dislikes] = extended_rating_element.attributes["numDislikes"].to_i
430
+ end
431
+
432
+ rating = YouTubeIt::Model::Rating.new(rating_values)
433
+ end
434
+
435
+ if (el = entry.elements["yt:statistics"])
436
+ view_count, favorite_count = el.attributes["viewCount"].to_i, el.attributes["favoriteCount"].to_i
437
+ else
438
+ view_count, favorite_count = 0,0
439
+ end
440
+
441
+ noembed = entry.elements["yt:noembed"] ? true : false
442
+ racy = entry.elements["media:rating"] ? true : false
443
+
444
+ if where = entry.elements["georss:where"]
445
+ position = where.elements["gml:Point"].elements["gml:pos"].text
446
+ latitude, longitude = position.split(" ")
447
+ end
448
+
449
+ control = entry.elements["app:control"]
450
+ state = { :name => "published" }
451
+ if control && control.elements["yt:state"]
452
+ state = {
453
+ :name => control.elements["yt:state"].attributes["name"],
454
+ :reason_code => control.elements["yt:state"].attributes["reasonCode"],
455
+ :help_url => control.elements["yt:state"].attributes["helpUrl"],
456
+ :copy => control.elements["yt:state"].text
457
+ }
458
+
459
+ end
460
+
461
+ YouTubeIt::Model::Video.new(
462
+ :video_id => video_id,
463
+ :published_at => published_at,
464
+ :updated_at => updated_at,
465
+ :categories => categories,
466
+ :keywords => keywords,
467
+ :title => title,
468
+ :html_content => html_content,
469
+ :author => author,
470
+ :description => description,
471
+ :duration => duration,
472
+ :media_content => media_content,
473
+ :player_url => player_url,
474
+ :thumbnails => thumbnails,
475
+ :rating => rating,
476
+ :view_count => view_count,
477
+ :favorite_count => favorite_count,
478
+ :widescreen => widescreen,
479
+ :noembed => noembed,
480
+ :racy => racy,
481
+ :where => where,
482
+ :position => position,
483
+ :latitude => latitude,
484
+ :longitude => longitude,
485
+ :state => state)
486
+ end
487
+
488
+ def parse_media_content (media_content_element)
489
+ content_url = media_content_element.attributes["url"]
490
+ format_code = media_content_element.attributes["yt:format"].to_i
491
+ format = YouTubeIt::Model::Video::Format.by_code(format_code)
492
+ duration = media_content_element.attributes["duration"].to_i
493
+ mime_type = media_content_element.attributes["type"]
494
+ default = (media_content_element.attributes["isDefault"] == "true")
495
+
496
+ YouTubeIt::Model::Content.new(
497
+ :url => content_url,
498
+ :format => format,
499
+ :duration => duration,
500
+ :mime_type => mime_type,
501
+ :default => default)
502
+ end
503
+ end
504
+
505
+ class VideosFeedParser < VideoFeedParser #:nodoc:
506
+
507
+ private
508
+ def parse_content(content)
509
+ videos = []
510
+ doc = REXML::Document.new(content)
511
+ feed = doc.elements["feed"]
512
+ if feed
513
+ feed_id = feed.elements["id"].text
514
+ updated_at = Time.parse(feed.elements["updated"].text)
515
+ total_result_count = feed.elements["openSearch:totalResults"].text.to_i
516
+ offset = feed.elements["openSearch:startIndex"].text.to_i
517
+ max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i
518
+
519
+ feed.elements.each("entry") do |entry|
520
+ videos << parse_entry(entry)
521
+ end
522
+ end
523
+ YouTubeIt::Response::VideoSearch.new(
524
+ :feed_id => feed_id || nil,
525
+ :updated_at => updated_at || nil,
526
+ :total_result_count => total_result_count || nil,
527
+ :offset => offset || nil,
528
+ :max_result_count => max_result_count || nil,
529
+ :videos => videos)
530
+ end
531
+ end
532
+ end
533
+ end
534
+