yt 0.11.6 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/README.md +13 -2
  4. data/lib/yt/actions/list.rb +11 -1
  5. data/lib/yt/associations/has_attribute.rb +51 -0
  6. data/lib/yt/associations/has_many.rb +0 -15
  7. data/lib/yt/collections/content_owners.rb +0 -4
  8. data/lib/yt/collections/playlist_items.rb +1 -1
  9. data/lib/yt/collections/resources.rb +1 -1
  10. data/lib/yt/collections/subscribed_channels.rb +46 -0
  11. data/lib/yt/collections/subscribers.rb +33 -0
  12. data/lib/yt/collections/subscriptions.rb +0 -23
  13. data/lib/yt/models/account.rb +6 -1
  14. data/lib/yt/models/annotation.rb +4 -10
  15. data/lib/yt/models/asset.rb +3 -10
  16. data/lib/yt/models/base.rb +2 -0
  17. data/lib/yt/models/channel.rb +53 -25
  18. data/lib/yt/models/claim.rb +13 -25
  19. data/lib/yt/models/content_detail.rb +9 -11
  20. data/lib/yt/models/content_owner_detail.rb +2 -8
  21. data/lib/yt/models/device_flow.rb +3 -11
  22. data/lib/yt/models/live_streaming_detail.rb +5 -25
  23. data/lib/yt/models/ownership.rb +8 -8
  24. data/lib/yt/models/playlist.rb +14 -10
  25. data/lib/yt/models/playlist_item.rb +13 -0
  26. data/lib/yt/models/policy.rb +10 -14
  27. data/lib/yt/models/policy_rule.rb +15 -21
  28. data/lib/yt/models/reference.rb +13 -37
  29. data/lib/yt/models/right_owner.rb +6 -17
  30. data/lib/yt/models/snippet.rb +19 -33
  31. data/lib/yt/models/statistics_set.rb +9 -23
  32. data/lib/yt/models/status.rb +20 -25
  33. data/lib/yt/models/subscription.rb +2 -8
  34. data/lib/yt/models/user_info.rb +11 -33
  35. data/lib/yt/version.rb +1 -1
  36. data/spec/collections/subscriptions_spec.rb +0 -7
  37. data/spec/errors/forbidden_spec.rb +10 -0
  38. data/spec/errors/server_error_spec.rb +10 -0
  39. data/spec/models/subscription_spec.rb +0 -9
  40. data/spec/requests/as_account/account_spec.rb +8 -0
  41. data/spec/requests/as_account/channel_spec.rb +40 -6
  42. data/spec/requests/as_account/playlist_item_spec.rb +26 -0
  43. data/spec/requests/as_account/playlist_spec.rb +1 -0
  44. data/spec/requests/as_server_app/channel_spec.rb +9 -0
  45. metadata +9 -2
@@ -11,44 +11,30 @@ module Yt
11
11
  end
12
12
 
13
13
  # @return [Integer] the number of times the resource has been viewed.
14
- def view_count
15
- @view_count ||= @data['viewCount'].to_i
16
- end
14
+ has_attribute :view_count, type: Integer
17
15
 
18
16
  # @return [Integer] the number of comments for the resource.
19
- def comment_count
20
- @comment_count ||= @data['commentCount'].to_i
21
- end
17
+ has_attribute :comment_count, type: Integer
22
18
 
23
19
  # @return [Integer] the number of users who liked the resource.
24
- def like_count
25
- @like_count ||= @data['likeCount'].to_i
26
- end
20
+ has_attribute :like_count, type: Integer
27
21
 
28
22
  # @return [Integer] the number of users who disliked the resource.
29
- def dislike_count
30
- @dislike_count ||= @data['dislikeCount'].to_i
31
- end
23
+ has_attribute :dislike_count, type: Integer
32
24
 
33
25
  # @return [Integer] the number of users who currently have the resource
34
26
  # marked as a favorite resource.
35
- def favorite_count
36
- @favorite_count ||= @data['favoriteCount'].to_i
37
- end
27
+ has_attribute :favorite_count, type: Integer
38
28
 
39
29
  # @return [Integer] the number of videos updated to the resource.
40
- def video_count
41
- @video_count ||= @data['videoCount'].to_i
42
- end
30
+ has_attribute :video_count, type: Integer
43
31
 
44
32
  # @return [Integer] the number of subscriber the resource has.
45
- def subscriber_count
46
- @subscriber_count ||= @data['subscriberCount'].to_i
47
- end
33
+ has_attribute :subscriber_count, type: Integer
48
34
 
49
35
  # @return [Boolean] whether the number of subscribers is publicly visible.
50
- def subscriber_count_visible?
51
- @subscriber_count_visible ||= @data['hiddenSubscriberCount'] == false
36
+ has_attribute :subscriber_count_visible?, from: :hidden_subscriber_count do |hidden|
37
+ hidden == false
52
38
  end
53
39
  end
54
40
  end
@@ -11,7 +11,7 @@ module Yt
11
11
  # @see https://developers.google.com/youtube/v3/docs/videos#resource
12
12
  # @see https://developers.google.com/youtube/v3/docs/playlists#resource
13
13
  # @see https://developers.google.com/youtube/v3/docs/playlistItems#resource
14
- class Status
14
+ class Status < Base
15
15
  def initialize(options = {})
16
16
  @data = options[:data]
17
17
  end
@@ -22,9 +22,7 @@ module Yt
22
22
 
23
23
  # @return [String] the privacy status of the resource. Valid values are:
24
24
  # private, public, unlisted.
25
- def privacy_status
26
- @privacy_status ||= @data['privacyStatus']
27
- end
25
+ has_attribute :privacy_status
28
26
 
29
27
  # @return [Boolean] whether the resource is public.
30
28
  def public?
@@ -48,9 +46,7 @@ module Yt
48
46
  # uploaded video. Valid values are: deleted, failed, processed,
49
47
  # rejected, uploaded.
50
48
  # @return [nil] if the resource is not a video.
51
- def upload_status
52
- @upload_status ||= @data['uploadStatus']
53
- end
49
+ has_attribute :upload_status
54
50
 
55
51
  # Returns whether an uploaded video was deleted.
56
52
  # @return [Boolean] if the resource is a video, whether the uploaded
@@ -101,9 +97,7 @@ module Yt
101
97
  # the reason why the video failed to upload. Valid values are: codec,
102
98
  # conversion, emptyFile, invalidFile, tooSmall, uploadAborted.
103
99
  # @return [nil] if the resource is not a video or upload has not failed.
104
- def failure_reason
105
- @failure_reason ||= @data['failureReason']
106
- end
100
+ has_attribute :failure_reason
107
101
 
108
102
  # Returns whether a video upload failed because of the codec.
109
103
  # @return [Boolean] if the resource is a video, whether the video uses
@@ -163,9 +157,7 @@ module Yt
163
157
  # claim, copyright, duplicate, inappropriate, length, termsOfUse,
164
158
  # trademark, uploaderAccountClosed, uploaderAccountSuspended.
165
159
  # @return [nil] if the resource is not a rejected video.
166
- def rejection_reason
167
- @rejection_reason ||= @data['rejectionReason']
168
- end
160
+ has_attribute :rejection_reason
169
161
 
170
162
  # Returns whether a video was rejected because it was claimed.
171
163
  # @return [Boolean] if the resource is a rejected video, whether the
@@ -249,16 +241,15 @@ module Yt
249
241
  # @return [nil] if the resource is not a private video scheduled to be
250
242
  # published.
251
243
  def scheduled_at
252
- @scheduled_at ||= Yt::Timestamp.parse @data['publishAt'] if scheduled?
244
+ publish_at if scheduled?
253
245
  end
254
- alias publish_at scheduled_at
255
246
 
256
247
  # Returns whether the video is scheduled to be published.
257
248
  # @return [Boolean] if the resource is a video, whether it is currently
258
249
  # private and is scheduled to become public in the future.
259
250
  # @return [nil] if the resource is not a video.
260
251
  def scheduled?
261
- private? && @data['publishAt'] if video?
252
+ private? && publish_at if video?
262
253
  end
263
254
 
264
255
  # License (Video only)
@@ -267,9 +258,7 @@ module Yt
267
258
  # @return [String] if resource is a video, its license. Valid values are:
268
259
  # creativeCommon, youtube.
269
260
  # @return [nil] if the resource is not a video.
270
- def license
271
- @license ||= @data['license']
272
- end
261
+ has_attribute :license
273
262
 
274
263
  # Returns whether the video uses a Creative Commons license.
275
264
  # @return [Boolean] if the resource is a video, whether it uses a
@@ -295,10 +284,12 @@ module Yt
295
284
  # @return [Boolean] if the resource is a video, whether it can be
296
285
  # embedded on another website.
297
286
  # @return [nil] if the resource is not a video.
298
- def embeddable?
299
- @embeddable ||= @data['embeddable']
287
+ has_attribute :embeddable?, from: :embeddable
288
+
289
+ # @deprecated Use {#embeddable?} instead.
290
+ def embeddable
291
+ embeddable?
300
292
  end
301
- alias embeddable embeddable?
302
293
 
303
294
  # Public stats (Video only)
304
295
 
@@ -309,13 +300,17 @@ module Yt
309
300
  # video’s viewcount and ratings will still be publicly visible even
310
301
  # if this property’s value is set to false.
311
302
  # @return [nil] if the resource is not a video.
312
- def has_public_stats_viewable?
313
- @public_stats_viewable ||= @data['publicStatsViewable']
303
+ has_attribute :has_public_stats_viewable?, from: :public_stats_viewable
304
+
305
+ # @deprecated Use {#has_public_stats_viewable?} instead.
306
+ def public_stats_viewable
307
+ has_public_stats_viewable?
314
308
  end
315
- alias public_stats_viewable has_public_stats_viewable?
316
309
 
317
310
  private
318
311
 
312
+ has_attribute :publish_at, type: Time
313
+
319
314
  def video?
320
315
  upload_status.present?
321
316
  end
@@ -13,14 +13,8 @@ module Yt
13
13
  @auth = options[:auth]
14
14
  end
15
15
 
16
- def delete(options = {})
17
- begin
18
- do_delete {@id = nil}
19
- rescue Yt::Error => error
20
- ignorable_errors = error.reasons & ['subscriptionNotFound']
21
- raise error unless options[:ignore_errors] && ignorable_errors.any?
22
- @id = nil
23
- end
16
+ def delete(options = {}, &block)
17
+ do_delete {@id = nil}
24
18
  !exists?
25
19
  end
26
20
 
@@ -10,61 +10,39 @@ module Yt
10
10
  end
11
11
 
12
12
  # @return [String] the user’s ID.
13
- def id
14
- @id ||= @data.fetch 'id', ''
15
- end
13
+ has_attribute :id, default: ''
16
14
 
17
15
  # @return [String] the user’s email address.
18
- def email
19
- @email ||= @data.fetch 'email', ''
20
- end
16
+ has_attribute :email, default: ''
21
17
 
22
18
  # @return [Boolean] whether the email address is verified.
23
- def has_verified_email?
24
- @verified_email ||= @data.fetch 'verified_email', false
25
- end
19
+ has_attribute :has_verified_email?, from: :verified_email, default: false, camelize: false
26
20
 
27
21
  # @return [String] the user's full name.
28
- def name
29
- @name ||= @data.fetch 'name', ''
30
- end
22
+ has_attribute :name, default: ''
31
23
 
32
24
  # @return [String] the user’s given (first) name.
33
- def given_name
34
- @given_name ||= @data.fetch 'given_name', ''
35
- end
25
+ has_attribute :given_name, default: '', camelize: false
36
26
 
37
27
  # @return [String] the user’s family (last) name.
38
- def family_name
39
- @family_name ||= @data.fetch 'family_name', ''
40
- end
28
+ has_attribute :family_name, default: '', camelize: false
41
29
 
42
30
  # @return [String] the URL of the user’s profile page.
43
- def profile_url
44
- @profile_url ||= @data.fetch 'link', ''
45
- end
31
+ has_attribute :profile_url, from: :link, default: ''
46
32
 
47
33
  # @return [String] the URL of the user’s profile picture.
48
- def avatar_url
49
- @avatar_url ||= @data.fetch 'picture', ''
50
- end
34
+ has_attribute :avatar_url, from: :picture, default: ''
51
35
 
52
36
  # @return [String] the person’s gender. Possible values include, but
53
37
  # are not limited to, "male", "female", "other".
54
- def gender
55
- @gender ||= @data.fetch 'gender', ''
56
- end
38
+ has_attribute :gender, default: ''
57
39
 
58
40
  # @return [String] the user’s preferred locale.
59
- def locale
60
- @locale ||= @data.fetch 'locale', ''
61
- end
41
+ has_attribute :locale, default: ''
62
42
 
63
43
  # @return [String] the hosted domain name for the user’s Google Apps
64
44
  # account. For instance, example.com.
65
- def hd
66
- @hd ||= @data.fetch 'hd', ''
67
- end
45
+ has_attribute :hd, default: ''
68
46
  end
69
47
  end
70
48
  end
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.11.6'
2
+ VERSION = '0.12.0'
3
3
  end
@@ -4,7 +4,6 @@ require 'yt/collections/subscriptions'
4
4
  describe Yt::Collections::Subscriptions do
5
5
  subject(:collection) { Yt::Collections::Subscriptions.new }
6
6
  let(:msg) { {response_body: {error: {errors: [{reason: reason}]}}}.to_json }
7
- before { expect(collection).to receive :throttle }
8
7
  before { expect(collection).to behave }
9
8
 
10
9
  describe '#insert' do
@@ -23,10 +22,4 @@ describe Yt::Collections::Subscriptions do
23
22
  it { expect{collection.insert ignore_errors: true}.not_to fail }
24
23
  end
25
24
  end
26
-
27
- describe '#delete_all' do
28
- let(:behave) { receive(:do_delete_all).and_return [true] }
29
-
30
- it { expect(collection.delete_all).to eq [true] }
31
- end
32
25
  end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'yt/errors/forbidden'
3
+
4
+ describe Yt::Errors::Forbidden do
5
+ let(:msg) { %r{^A request to YouTube API was considered forbidden by the server} }
6
+
7
+ describe '#exception' do
8
+ it { expect{raise Yt::Errors::Forbidden}.to raise_error msg }
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'yt/errors/server_error'
3
+
4
+ describe Yt::Errors::ServerError do
5
+ let(:msg) { %r{^A request to YouTube API caused an unexpected server error} }
6
+
7
+ describe '#exception' do
8
+ it { expect{raise Yt::Errors::ServerError}.to raise_error msg }
9
+ end
10
+ end
@@ -3,7 +3,6 @@ require 'yt/models/subscription'
3
3
 
4
4
  describe Yt::Subscription do
5
5
  subject(:subscription) { Yt::Subscription.new id: id }
6
- let(:msg) { {response_body: {error: {errors: [{reason: reason}]}}}.to_json }
7
6
 
8
7
  describe '#exists?' do
9
8
  context 'given a subscription with an id' do
@@ -27,13 +26,5 @@ describe Yt::Subscription do
27
26
  it { expect(subscription.delete).to be true }
28
27
  it { expect{subscription.delete}.to change{subscription.exists?} }
29
28
  end
30
-
31
- context 'given an unknown subscription' do
32
- let(:reason) { 'subscriptionNotFound' }
33
- let(:behave) { receive(:do_delete).and_raise Yt::Error, msg }
34
-
35
- it { expect{subscription.delete}.to fail.with 'subscriptionNotFound' }
36
- it { expect{subscription.delete ignore_errors: true}.not_to fail }
37
- end
38
29
  end
39
30
  end
@@ -61,4 +61,12 @@ describe Yt::Account, :device_app do
61
61
  it { expect(video).to be_a Yt::Video }
62
62
  end
63
63
  end
64
+
65
+ describe '.subscribers' do
66
+ let(:subscriber) { $account.subscribers.first }
67
+
68
+ specify 'returns the channels who are subscribed to me' do
69
+ expect(subscriber).to be_a Yt::Channel
70
+ end
71
+ end
64
72
  end
@@ -25,21 +25,51 @@ describe Yt::Channel, :device_app do
25
25
  it { expect(channel.playlists.first).to be_a Yt::Playlist }
26
26
  it { expect{channel.create_playlist}.to raise_error Yt::Errors::RequestError }
27
27
  it { expect{channel.delete_playlists}.to raise_error Yt::Errors::RequestError }
28
- it { expect(channel.subscriptions).to be_a Yt::Collections::Subscriptions }
28
+
29
+ specify 'with a public list of subscriptions' do
30
+ expect(channel.subscribed_channels.first).to be_a Yt::Channel
31
+ end
32
+
33
+ context 'with a hidden list of subscriptions' do
34
+ let(:id) { 'UCG0hw7n_v0sr8MXgb6oel6w' }
35
+ it { expect{channel.subscribed_channels.size}.to raise_error Yt::Errors::Forbidden }
36
+ end
29
37
 
30
38
  # NOTE: These tests are slow because we *must* wait some seconds between
31
39
  # subscribing and unsubscribing to a channel, otherwise YouTube will show
32
40
  # wrong (cached) data, such as a user is subscribed when he is not.
33
41
  context 'that I am not subscribed to', :slow do
34
- before { channel.unsubscribe }
42
+ let(:id) { 'UCCj956IF62FbT7Gouszaj9w' }
43
+ before { channel.throttle_subscriptions }
44
+
35
45
  it { expect(channel.subscribed?).to be false }
36
- it { expect(channel.subscribe!).to be_truthy }
46
+ it { expect(channel.unsubscribe).to be_falsey }
47
+ it { expect{channel.unsubscribe!}.to raise_error Yt::Errors::RequestError }
48
+
49
+ context 'when I subscribe' do
50
+ before { channel.subscribe }
51
+ after { channel.unsubscribe }
52
+
53
+ it { expect(channel.subscribed?).to be true }
54
+ it { expect(channel.unsubscribe!).to be_truthy }
55
+ end
37
56
  end
38
57
 
39
58
  context 'that I am subscribed to', :slow do
40
- before { channel.subscribe }
59
+ let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
60
+ before { channel.throttle_subscriptions }
61
+
41
62
  it { expect(channel.subscribed?).to be true }
42
- it { expect(channel.unsubscribe!).to be_truthy }
63
+ it { expect(channel.subscribe).to be_falsey }
64
+ it { expect{channel.subscribe!}.to raise_error Yt::Errors::RequestError }
65
+
66
+ context 'when I unsubscribe' do
67
+ before { channel.unsubscribe }
68
+ after { channel.subscribe }
69
+
70
+ it { expect(channel.subscribed?).to be false }
71
+ it { expect(channel.subscribe!).to be_truthy }
72
+ end
43
73
  end
44
74
 
45
75
  describe 'filtering by ID is ignored when listing videos' do
@@ -71,6 +101,10 @@ describe Yt::Channel, :device_app do
71
101
  let(:privacy_status) { 'unlisted' }
72
102
  let(:params) { {title: title, description: description, tags: tags, privacy_status: privacy_status} }
73
103
 
104
+ specify 'subscriptions can be listed (hidden or public)' do
105
+ expect(channel.subscriptions.size).to be
106
+ end
107
+
74
108
  describe 'playlists can be added' do
75
109
  after { channel.delete_playlists params }
76
110
  it { expect(channel.create_playlist params).to be_a Yt::Playlist }
@@ -141,4 +175,4 @@ describe Yt::Channel, :device_app do
141
175
  it { expect(channel.videos.size).to be_zero }
142
176
  end
143
177
  end
144
- end
178
+ end
@@ -27,4 +27,30 @@ describe Yt::PlaylistItem, :device_app do
27
27
 
28
28
  it { expect{item.snippet}.to raise_error Yt::Errors::RequestError }
29
29
  end
30
+
31
+
32
+ context 'given one of my own playlist items that I want to update' do
33
+ before(:all) do
34
+ @my_playlist = $account.create_playlist title: "Yt Test Update Playlist Item #{rand}"
35
+ @my_playlist.add_video 'MESycYJytkU'
36
+ @my_playlist_item = @my_playlist.add_video 'MESycYJytkU'
37
+ end
38
+ after(:all) { @my_playlist.delete }
39
+
40
+ let(:id) { @my_playlist_item.id }
41
+ let!(:old_title) { @my_playlist_item.title }
42
+ let!(:old_privacy_status) { @my_playlist_item.privacy_status }
43
+ let(:update) { @my_playlist_item.update attrs }
44
+
45
+ context 'given I update the position' do
46
+ let(:attrs) { {position: 0} }
47
+
48
+ specify 'only updates the position' do
49
+ expect(update).to be true
50
+ expect(@my_playlist_item.position).to be 0
51
+ expect(@my_playlist_item.title).to eq old_title
52
+ expect(@my_playlist_item.privacy_status).to eq old_privacy_status
53
+ end
54
+ end
55
+ end
30
56
  end