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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +13 -2
- data/lib/yt/actions/list.rb +11 -1
- data/lib/yt/associations/has_attribute.rb +51 -0
- data/lib/yt/associations/has_many.rb +0 -15
- data/lib/yt/collections/content_owners.rb +0 -4
- data/lib/yt/collections/playlist_items.rb +1 -1
- data/lib/yt/collections/resources.rb +1 -1
- data/lib/yt/collections/subscribed_channels.rb +46 -0
- data/lib/yt/collections/subscribers.rb +33 -0
- data/lib/yt/collections/subscriptions.rb +0 -23
- data/lib/yt/models/account.rb +6 -1
- data/lib/yt/models/annotation.rb +4 -10
- data/lib/yt/models/asset.rb +3 -10
- data/lib/yt/models/base.rb +2 -0
- data/lib/yt/models/channel.rb +53 -25
- data/lib/yt/models/claim.rb +13 -25
- data/lib/yt/models/content_detail.rb +9 -11
- data/lib/yt/models/content_owner_detail.rb +2 -8
- data/lib/yt/models/device_flow.rb +3 -11
- data/lib/yt/models/live_streaming_detail.rb +5 -25
- data/lib/yt/models/ownership.rb +8 -8
- data/lib/yt/models/playlist.rb +14 -10
- data/lib/yt/models/playlist_item.rb +13 -0
- data/lib/yt/models/policy.rb +10 -14
- data/lib/yt/models/policy_rule.rb +15 -21
- data/lib/yt/models/reference.rb +13 -37
- data/lib/yt/models/right_owner.rb +6 -17
- data/lib/yt/models/snippet.rb +19 -33
- data/lib/yt/models/statistics_set.rb +9 -23
- data/lib/yt/models/status.rb +20 -25
- data/lib/yt/models/subscription.rb +2 -8
- data/lib/yt/models/user_info.rb +11 -33
- data/lib/yt/version.rb +1 -1
- data/spec/collections/subscriptions_spec.rb +0 -7
- data/spec/errors/forbidden_spec.rb +10 -0
- data/spec/errors/server_error_spec.rb +10 -0
- data/spec/models/subscription_spec.rb +0 -9
- data/spec/requests/as_account/account_spec.rb +8 -0
- data/spec/requests/as_account/channel_spec.rb +40 -6
- data/spec/requests/as_account/playlist_item_spec.rb +26 -0
- data/spec/requests/as_account/playlist_spec.rb +1 -0
- data/spec/requests/as_server_app/channel_spec.rb +9 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
51
|
-
|
36
|
+
has_attribute :subscriber_count_visible?, from: :hidden_subscriber_count do |hidden|
|
37
|
+
hidden == false
|
52
38
|
end
|
53
39
|
end
|
54
40
|
end
|
data/lib/yt/models/status.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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? &&
|
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
|
-
|
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
|
-
|
299
|
-
|
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
|
-
|
313
|
-
|
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
|
-
|
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
|
|
data/lib/yt/models/user_info.rb
CHANGED
@@ -10,61 +10,39 @@ module Yt
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# @return [String] the user’s ID.
|
13
|
-
|
14
|
-
@id ||= @data.fetch 'id', ''
|
15
|
-
end
|
13
|
+
has_attribute :id, default: ''
|
16
14
|
|
17
15
|
# @return [String] the user’s email address.
|
18
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
55
|
-
@gender ||= @data.fetch 'gender', ''
|
56
|
-
end
|
38
|
+
has_attribute :gender, default: ''
|
57
39
|
|
58
40
|
# @return [String] the user’s preferred locale.
|
59
|
-
|
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
|
-
|
66
|
-
@hd ||= @data.fetch 'hd', ''
|
67
|
-
end
|
45
|
+
has_attribute :hd, default: ''
|
68
46
|
end
|
69
47
|
end
|
70
48
|
end
|
data/lib/yt/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
42
|
+
let(:id) { 'UCCj956IF62FbT7Gouszaj9w' }
|
43
|
+
before { channel.throttle_subscriptions }
|
44
|
+
|
35
45
|
it { expect(channel.subscribed?).to be false }
|
36
|
-
it { expect(channel.
|
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
|
-
|
59
|
+
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
60
|
+
before { channel.throttle_subscriptions }
|
61
|
+
|
41
62
|
it { expect(channel.subscribed?).to be true }
|
42
|
-
it { expect(channel.
|
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
|