yt 0.11.6 → 0.12.0

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 (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