youtube_it 2.1.4 → 2.1.5

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.
@@ -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