yt 0.11.6 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|