youtube_it 2.1.4 → 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -52,6 +52,10 @@ class YouTubeIt
52
52
  if fields[:view_count]
53
53
  fields_param << "entry[yt:statistics/@viewCount > #{fields[:view_count]}]"
54
54
  end
55
+
56
+ if fields[:entry]
57
+ fields_param << "entry[#{fields[:entry]}]"
58
+ end
55
59
 
56
60
 
57
61
  return "&fields=#{URI.escape(fields_param.join(","))}"
@@ -14,10 +14,17 @@ class YouTubeIt
14
14
  attr_reader :tags # /-/tag1/tag2
15
15
  attr_reader :categories # /-/Category1/Category2
16
16
  attr_reader :video_format # format (1=mobile devices)
17
- attr_reader :racy # racy ([exclude], include)
17
+ attr_reader :safe_search # safeSearch (none, [moderate], strict)
18
18
  attr_reader :author
19
19
  attr_reader :lang # lt
20
20
  attr_reader :restriction
21
+ attr_reader :duration
22
+ attr_reader :time
23
+ attr_reader :hd
24
+ attr_reader :caption
25
+ attr_reader :uploader
26
+ attr_reader :region
27
+ attr_reader :paid_content
21
28
 
22
29
 
23
30
  def initialize(params={})
@@ -26,7 +33,9 @@ class YouTubeIt
26
33
  @max_results, @order_by,
27
34
  @offset, @query,
28
35
  @response_format, @video_format,
29
- @racy, @author, @lang = nil
36
+ @safe_search, @author, @lang,
37
+ @duration, @time, @hd, @caption,
38
+ @uploader, @region, @paid_content = nil
30
39
  @url = base_url
31
40
  @dev_key = params[:dev_key] if params[:dev_key]
32
41
 
@@ -62,10 +71,17 @@ class YouTubeIt
62
71
  'q' => @query,
63
72
  'alt' => @response_format,
64
73
  'format' => @video_format,
65
- 'racy' => @racy,
74
+ 'safeSearch' => @safe_search,
66
75
  'author' => @author,
67
76
  'restriction' => @restriction,
68
- 'lr' => @lang
77
+ 'lr' => @lang,
78
+ 'duration' => @duration,
79
+ 'time' => @time,
80
+ 'hd' => @hd,
81
+ 'caption' => @caption,
82
+ 'region' => @region,
83
+ 'paid-content' => @paid_content,
84
+ 'uploader' => @uploader
69
85
  }
70
86
  end
71
87
 
@@ -107,39 +107,39 @@ class YouTubeIt
107
107
  :description => '',
108
108
  :category => 'People',
109
109
  :keywords => [] }.merge(options)
110
-
110
+
111
111
  update_body = video_xml
112
112
  update_url = "/feeds/api/users/default/uploads/%s" % video_id
113
113
  response = yt_session.put(update_url, update_body)
114
-
114
+
115
115
  return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
116
116
  end
117
-
117
+
118
118
  # Fetches the currently authenticated user's contacts (i.e. friends).
119
119
  # When the authentication credentials are incorrect, an AuthenticationError will be raised.
120
120
  def get_my_contacts(opts)
121
121
  contacts_url = "/feeds/api/users/default/contacts?v=2"
122
122
  contacts_url << opts.collect { |k,p| [k,p].join '=' }.join('&')
123
123
  response = yt_session.get(contacts_url)
124
-
124
+
125
125
  return YouTubeIt::Parser::ContactsParser.new(response).parse
126
126
  end
127
-
127
+
128
128
  def send_message(opts)
129
129
  message_body = message_xml_for(opts)
130
130
  message_url = "/feeds/api/users/%s/inbox" % opts[:recipient_id]
131
131
  response = yt_session.post(message_url, message_body)
132
-
132
+
133
133
  return {:code => response.status, :body => response.body}
134
134
  end
135
-
135
+
136
136
  # Fetches the currently authenticated user's messages (i.e. inbox).
137
137
  # When the authentication credentials are incorrect, an AuthenticationError will be raised.
138
138
  def get_my_messages(opts)
139
139
  messages_url = "/feeds/api/users/default/inbox"
140
140
  messages_url << opts.collect { |k,p| [k,p].join '=' }.join('&')
141
141
  response = yt_session.get(messages_url)
142
-
142
+
143
143
  return YouTubeIt::Parser::MessagesParser.new(response).parse
144
144
  end
145
145
 
@@ -148,11 +148,11 @@ class YouTubeIt
148
148
  def get_my_video(video_id)
149
149
  get_url = "/feeds/api/users/default/uploads/%s" % video_id
150
150
  response = yt_session.get(get_url)
151
-
151
+
152
152
  return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
153
153
  end
154
154
 
155
- # Fetches the data of the videos of the current user, which may be private.
155
+ # Fetches the data of the videos of the current user, which may be private.
156
156
  # When the authentication credentials are incorrect, an AuthenticationError will be raised.
157
157
  def get_my_videos(opts)
158
158
  max_results = opts[:per_page] || 50
@@ -170,7 +170,7 @@ class YouTubeIt
170
170
 
171
171
  return true
172
172
  end
173
-
173
+
174
174
  # Delete a video message
175
175
  def delete_message(message_id)
176
176
  delete_url = "/feeds/api/users/default/inbox/%s" % message_id
@@ -184,19 +184,29 @@ class YouTubeIt
184
184
  token_body = video_xml
185
185
  token_url = "/action/GetUploadToken"
186
186
  response = yt_session.post(token_url, token_body)
187
-
187
+
188
188
  return {:url => "#{response.body[/<url>(.+)<\/url>/, 1]}?nexturl=#{nexturl}",
189
189
  :token => response.body[/<token>(.+)<\/token>/, 1]}
190
190
  end
191
191
 
192
- def add_comment(video_id, comment)
193
- comment_body = video_xml_for(:comment => comment)
192
+ def add_comment(video_id, comment, opts = {})
193
+ reply_to = opts.delete :reply_to
194
+ reply_to = reply_to.unique_id if reply_to.is_a? YouTubeIt::Model::Comment
195
+ comment_body = comment_xml_for(:comment => comment, :reply_to => reply_to_url(video_id, reply_to))
194
196
  comment_url = "/feeds/api/videos/%s/comments" % video_id
195
197
  response = yt_session.post(comment_url, comment_body)
196
-
198
+
197
199
  return {:code => response.status, :body => response.body}
198
200
  end
199
201
 
202
+ def delete_comment(video_id, comment_id)
203
+ comment_id = comment_id.unique_id if comment_id.is_a? YouTubeIt::Model::Comment
204
+ url = "/feeds/api/videos/%s/comments/%s" % [video_id, comment_id]
205
+ response = yt_session.delete(url)
206
+
207
+ return response.status == 200
208
+ end
209
+
200
210
  def comments(video_id, opts = {})
201
211
  comment_url = "/feeds/api/videos/%s/comments?" % video_id
202
212
  comment_url << opts.collect { |k,p| [k,p].join '=' }.join('&')
@@ -208,55 +218,106 @@ class YouTubeIt
208
218
  favorite_body = video_xml_for(:favorite => video_id)
209
219
  favorite_url = "/feeds/api/users/default/favorites"
210
220
  response = yt_session.post(favorite_url, favorite_body)
211
-
212
- return {:code => response.status, :body => response.body}
221
+
222
+ return {:code => response.status, :body => response.body, :favorite_entry_id => get_entry_id(response.body)}
213
223
  end
214
224
 
215
225
  def delete_favorite(video_id)
216
- favorite_header = {
217
- "GData-Version" => "1",
218
- }
219
226
  favorite_url = "/feeds/api/users/default/favorites/%s" % video_id
220
- response = yt_session.delete(favorite_url, favorite_header)
221
-
227
+ response = yt_session.delete(favorite_url)
228
+
222
229
  return true
223
230
  end
224
231
 
225
- def profile(user)
226
- profile_url = "/feeds/api/users/%s?v=2" % (user ? user : "default")
227
- response = yt_session.get(profile_url)
232
+ def profile(user=nil)
233
+ response = yt_session.get(profile_url(user))
228
234
 
229
235
  return YouTubeIt::Parser::ProfileFeedParser.new(response).parse
230
236
  end
231
-
237
+
238
+ def profiles(usernames_to_fetch)
239
+ usernames_to_fetch.each_slice(50).map do |usernames|
240
+ post = Nokogiri::XML <<-BATCH
241
+ <?xml version="1.0" encoding="UTF-8"?>
242
+ <feed xmlns='http://www.w3.org/2005/Atom'
243
+ xmlns:media='http://search.yahoo.com/mrss/'
244
+ xmlns:batch='http://schemas.google.com/gdata/batch'
245
+ xmlns:yt='http://gdata.youtube.com/schemas/2007'>
246
+ <batch:operation type="query" />
247
+ </feed>
248
+ BATCH
249
+ usernames.each do |username|
250
+ post.at('feed').add_child <<-ENTRY
251
+ <entry xmlns:batch='http://schemas.google.com/gdata/batch'>
252
+ <id>#{profile_url(username)}</id>
253
+ <batch:id>#{username}</batch:id>
254
+ </entry>
255
+ ENTRY
256
+ end
257
+
258
+ post_body = ''
259
+ post.write_to( post_body, :indent => 2 )
260
+ post_body_io = StringIO.new(post_body)
261
+
262
+ response = yt_session.post('feeds/api/users/batch', post_body_io )
263
+ YouTubeIt::Parser::BatchProfileFeedParser.new(response).parse
264
+ end.reduce({},:merge)
265
+ end
266
+
267
+ def profile_url(user=nil)
268
+ "/feeds/api/users/%s?v=2" % (user || "default")
269
+ end
270
+
232
271
  # Return's a user's activity feed.
233
272
  def get_activity(user, opts)
234
273
  activity_url = "/feeds/api/events?author=%s&v=2&" % (user ? user : "default")
235
274
  activity_url << opts.collect { |k,p| [k,p].join '=' }.join('&')
236
275
  response = yt_session.get(activity_url)
237
-
276
+
238
277
  return YouTubeIt::Parser::ActivityParser.new(response).parse
239
278
  end
240
279
 
241
- def playlist(playlist_id)
242
- playlist_url = "/feeds/api/playlists/%s?v=2" % playlist_id
280
+ def watchlater(user)
281
+ watchlater_url = "/feeds/api/users/%s/watch_later?v=2" % (user ? user : "default")
282
+ response = yt_session.get(watchlater_url)
283
+
284
+ return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
285
+ end
286
+
287
+ def add_video_to_watchlater(video_id)
288
+ playlist_body = video_xml_for(:playlist => video_id)
289
+ playlist_url = "/feeds/api/users/default/watch_later"
290
+ response = yt_session.post(playlist_url, playlist_body)
291
+
292
+ return {:code => response.status, :body => response.body, :watchlater_entry_id => get_entry_id(response.body)}
293
+ end
294
+
295
+ def delete_video_from_watchlater(video_id)
296
+ playlist_url = "/feeds/api/users/default/watch_later/%s" % video_id
297
+ response = yt_session.delete(playlist_url)
298
+
299
+ return true
300
+ end
301
+
302
+ def playlist(playlist_id, order_by = :position)
303
+ playlist_url = "/feeds/api/playlists/%s?v=2&orderby=%s" % [playlist_id, order_by]
243
304
  response = yt_session.get(playlist_url)
244
-
305
+
245
306
  return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
246
307
  end
247
308
 
248
309
  def playlists(user)
249
310
  playlist_url = "/feeds/api/users/%s/playlists?v=2" % (user ? user : "default")
250
311
  response = yt_session.get(playlist_url)
251
-
312
+
252
313
  return YouTubeIt::Parser::PlaylistsFeedParser.new(response).parse
253
314
  end
254
-
315
+
255
316
  def add_playlist(options)
256
317
  playlist_body = video_xml_for_playlist(options)
257
318
  playlist_url = "/feeds/api/users/default/playlists"
258
319
  response = yt_session.post(playlist_url, playlist_body)
259
-
320
+
260
321
  return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
261
322
  end
262
323
 
@@ -264,29 +325,29 @@ class YouTubeIt
264
325
  playlist_body = video_xml_for(:playlist => video_id)
265
326
  playlist_url = "/feeds/api/playlists/%s" % playlist_id
266
327
  response = yt_session.post(playlist_url, playlist_body)
267
-
268
- return {:code => response.status, :body => response.body, :playlist_entry_id => playlist_entry_id_from_playlist(response.body)}
328
+
329
+ return {:code => response.status, :body => response.body, :playlist_entry_id => get_entry_id(response.body)}
269
330
  end
270
331
 
271
332
  def update_playlist(playlist_id, options)
272
333
  playlist_body = video_xml_for_playlist(options)
273
334
  playlist_url = "/feeds/api/users/default/playlists/%s" % playlist_id
274
335
  response = yt_session.put(playlist_url, playlist_body)
275
-
336
+
276
337
  return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
277
338
  end
278
339
 
279
340
  def delete_video_from_playlist(playlist_id, playlist_entry_id)
280
341
  playlist_url = "/feeds/api/playlists/%s/%s" % [playlist_id, playlist_entry_id]
281
342
  response = yt_session.delete(playlist_url)
282
-
343
+
283
344
  return true
284
345
  end
285
346
 
286
347
  def delete_playlist(playlist_id)
287
348
  playlist_url = "/feeds/api/users/default/playlists/%s" % playlist_id
288
349
  response = yt_session.delete(playlist_url)
289
-
350
+
290
351
  return true
291
352
  end
292
353
 
@@ -294,32 +355,32 @@ class YouTubeIt
294
355
  rating_body = video_xml_for(:rating => rating)
295
356
  rating_url = "/feeds/api/videos/#{video_id}/ratings"
296
357
  response = yt_session.post(rating_url, rating_body)
297
-
358
+
298
359
  return {:code => response.status, :body => response.body}
299
360
  end
300
-
361
+
301
362
  def subscriptions(user)
302
363
  subscription_url = "/feeds/api/users/%s/subscriptions?v=2" % (user ? user : "default")
303
364
  response = yt_session.get(subscription_url)
304
-
365
+
305
366
  return YouTubeIt::Parser::SubscriptionFeedParser.new(response).parse
306
367
  end
307
-
368
+
308
369
  def subscribe_channel(channel_name)
309
370
  subscribe_body = video_xml_for(:subscribe => channel_name)
310
371
  subscribe_url = "/feeds/api/users/default/subscriptions"
311
372
  response = yt_session.post(subscribe_url, subscribe_body)
312
-
373
+
313
374
  return {:code => response.status, :body => response.body}
314
375
  end
315
-
376
+
316
377
  def unsubscribe_channel(subscription_id)
317
378
  unsubscribe_url = "/feeds/api/users/default/subscriptions/%s" % subscription_id
318
379
  response = yt_session.delete(unsubscribe_url)
319
-
380
+
320
381
  return {:code => response.status, :body => response.body}
321
382
  end
322
-
383
+
323
384
  def favorites(user, opts = {})
324
385
  favorite_url = "/feeds/api/users/%s/favorites#{opts.empty? ? '' : '?#{opts.to_param}'}" % (user ? user : "default")
325
386
  response = yt_session.get(favorite_url)
@@ -331,24 +392,37 @@ class YouTubeIt
331
392
  current_user_url = "/feeds/api/users/default"
332
393
  response = yt_session.get(current_user_url)
333
394
 
334
- return REXML::Document.new(response.body).elements["entry"].elements['author'].elements['name'].text
395
+ return Nokogiri::XML(response.body).at("entry/author/name").text
335
396
  end
336
-
397
+
337
398
  def add_response(original_video_id, response_video_id)
338
399
  response_body = video_xml_for(:response => response_video_id)
339
400
  response_url = "/feeds/api/videos/%s/responses" % original_video_id
340
401
  response = yt_session.post(response_url, response_body)
341
-
402
+
342
403
  return {:code => response.status, :body => response.body}
343
404
  end
344
405
 
345
406
  def delete_response(original_video_id, response_video_id)
346
407
  response_url = "/feeds/api/videos/%s/responses/%s" % [original_video_id, response_video_id]
347
408
  response = yt_session.delete(response_url)
348
-
409
+
349
410
  return {:code => response.status, :body => response.body}
350
411
  end
351
412
 
413
+ def get_watch_history
414
+ watch_history_url = "/feeds/api/users/default/watch_history?v=2"
415
+ response = yt_session.get(watch_history_url)
416
+
417
+ return YouTubeIt::Parser::VideosFeedParser.new(response.body).parse
418
+ end
419
+
420
+ def new_subscription_videos(user)
421
+ subscription_url = "/feeds/api/users/%s/newsubscriptionvideos?v=2" % (user ? user : "default")
422
+ response = yt_session.get(subscription_url)
423
+
424
+ return YouTubeIt::Parser::VideosFeedParser.new(response.body).parse
425
+ end
352
426
 
353
427
  private
354
428
 
@@ -377,15 +451,15 @@ class YouTubeIt
377
451
 
378
452
  def parse_upload_error_from(string)
379
453
  begin
380
- REXML::Document.new(string).elements["//errors"].inject('') do | all_faults, error|
381
- if error.elements["internalReason"]
382
- msg_error = error.elements["internalReason"].text
383
- elsif error.elements["location"]
384
- msg_error = error.elements["location"].text[/media:group\/media:(.*)\/text\(\)/,1]
454
+ Nokogiri::XML(string).xpath("//errors").inject('') do | all_faults, error|
455
+ if error.at("internalReason")
456
+ msg_error = error.at("internalReason").text
457
+ elsif error.at("location")
458
+ msg_error = error.at("location").text[/media:group\/media:(.*)\/text\(\)/,1]
385
459
  else
386
460
  msg_error = "Unspecified error"
387
461
  end
388
- code = error.elements["code"].text if error.elements["code"]
462
+ code = error.at("code").text if error.at("code")
389
463
  all_faults + sprintf("%s: %s\n", msg_error, code)
390
464
  end
391
465
  rescue
@@ -406,14 +480,13 @@ class YouTubeIt
406
480
  end
407
481
 
408
482
  def uploaded_video_id_from(string)
409
- xml = REXML::Document.new(string)
410
- xml.elements["//id"].text[/videos\/(.+)/, 1]
483
+ xml = Nokogiri::XML(string)
484
+ xml.at("id").text[/videos\/(.+)/, 1]
411
485
  end
412
486
 
413
487
  def playlist_id_from(string)
414
- xml = REXML::Document.new(string)
415
- entry = xml.elements["entry"]
416
- entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':','')
488
+ xml = Nokogiri::XML(string)
489
+ xml.at("entry/id").text[/playlist([^<]+)/, 1].sub(':','')
417
490
  end
418
491
 
419
492
  # If data can be read, use the first 1024 bytes as filename. If data
@@ -444,7 +517,11 @@ class YouTubeIt
444
517
  def video_xml
445
518
  b = Builder::XmlMarkup.new
446
519
  b.instruct!
447
- b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:media' => "http://search.yahoo.com/mrss/", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
520
+ b.entry(:xmlns => "http://www.w3.org/2005/Atom",
521
+ 'xmlns:media' => "http://search.yahoo.com/mrss/",
522
+ 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007",
523
+ 'xmlns:gml' => 'http://www.opengis.net/gml',
524
+ 'xmlns:georss' => 'http://www.georss.org/georss') do | m |
448
525
  m.tag!("media:group") do | mg |
449
526
  mg.tag!("media:title", @opts[:title], :type => "plain")
450
527
  mg.tag!("media:description", @opts[:description], :type => "plain")
@@ -460,6 +537,13 @@ class YouTubeIt
460
537
  m.tag!("yt:accessControl", :action => "list", :permission => @opts[:list]) if @opts[:list]
461
538
  m.tag!("yt:accessControl", :action => "embed", :permission => @opts[:embed]) if @opts[:embed]
462
539
  m.tag!("yt:accessControl", :action => "syndicate", :permission => @opts[:syndicate]) if @opts[:syndicate]
540
+ if @opts[:latitude] and @opts[:longitude]
541
+ m.tag!("georss:where") do |geo|
542
+ geo.tag!("gml:Point") do |point|
543
+ point.tag!("gml:pos", @opts.values_at(:latitude, :longitude).join(' '))
544
+ end
545
+ end
546
+ end
463
547
  end.to_s
464
548
  end
465
549
 
@@ -467,7 +551,6 @@ class YouTubeIt
467
551
  b = Builder::XmlMarkup.new
468
552
  b.instruct!
469
553
  b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
470
- m.content(data[:comment]) if data[:comment]
471
554
  m.id(data[:favorite] || data[:playlist] || data[:response]) if data[:favorite] || data[:playlist] || data[:response]
472
555
  m.tag!("yt:rating", :value => data[:rating]) if data[:rating]
473
556
  if(data[:subscribe])
@@ -476,7 +559,20 @@ class YouTubeIt
476
559
  end
477
560
  end.to_s
478
561
  end
479
-
562
+
563
+ def reply_to_url video_id, reply_to
564
+ 'https://gdata.youtube.com/feeds/api/videos/%s/comments/%s' % [video_id, reply_to] if reply_to
565
+ end
566
+
567
+ def comment_xml_for(data)
568
+ b = Builder::XmlMarkup.new
569
+ b.instruct!
570
+ b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
571
+ m.link(:rel => 'http://gdata.youtube.com/schemas/2007#in-reply-to', :type => 'application/atom+xml', :href => data[:reply_to]) if data[:reply_to]
572
+ m.content(data[:comment]) if data[:comment]
573
+ end.to_s
574
+ end
575
+
480
576
  def message_xml_for(data)
481
577
  b = Builder::XmlMarkup.new
482
578
  b.instruct!
@@ -512,10 +608,10 @@ class YouTubeIt
512
608
  YouTubeIt::GreedyChainIO.new(post_body)
513
609
  end
514
610
 
515
- def playlist_entry_id_from_playlist(string)
516
- playlist_xml = REXML::Document.new(string)
517
- playlist_xml.elements.each("/entry") do |item|
518
- return item.elements["id"].text[/^.*:([^:]+)$/,1]
611
+ def get_entry_id(string)
612
+ entry_xml = Nokogiri::XML(string)
613
+ entry_xml.css("entry").each do |item|
614
+ return item.at("id").text[/^.*:([^:]+)$/,1]
519
615
  end
520
616
  end
521
617
 
@@ -529,9 +625,9 @@ class YouTubeIt
529
625
  end
530
626
  end
531
627
  builder.use Faraday::Request::AuthHeader, authorization_headers
532
- builder.use Faraday::Response::YouTubeIt
628
+ builder.use Faraday::Response::YouTubeIt
533
629
  builder.adapter YouTubeIt.adapter
534
-
630
+
535
631
  end
536
632
  end
537
633
  end