yt 0.25.13 → 0.32.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +305 -1
- data/MIT-LICENSE +1 -1
- data/README.md +86 -5
- data/YOUTUBE_IT.md +3 -3
- data/lib/yt.rb +5 -2
- data/lib/yt/actions/list.rb +3 -3
- data/lib/yt/associations/has_authentication.rb +33 -1
- data/lib/yt/associations/has_reports.rb +13 -18
- data/lib/yt/collections/assets.rb +2 -2
- data/lib/yt/collections/authentications.rb +9 -2
- data/lib/yt/collections/base.rb +3 -3
- data/lib/yt/collections/bulk_report_jobs.rb +28 -0
- data/lib/yt/collections/bulk_reports.rb +24 -0
- data/lib/yt/collections/claims.rb +22 -1
- data/lib/yt/collections/comment_threads.rb +41 -0
- data/lib/yt/collections/content_owners.rb +1 -1
- data/lib/yt/collections/group_infos.rb +27 -0
- data/lib/yt/collections/group_items.rb +45 -0
- data/lib/yt/collections/reports.rb +75 -13
- data/lib/yt/collections/revocations.rb +30 -0
- data/lib/yt/collections/video_groups.rb +29 -0
- data/lib/yt/collections/videos.rb +34 -9
- data/lib/yt/constants/geography.rb +326 -0
- data/lib/yt/errors/forbidden.rb +1 -3
- data/lib/yt/errors/no_items.rb +1 -3
- data/lib/yt/errors/request_error.rb +10 -7
- data/lib/yt/errors/server_error.rb +1 -3
- data/lib/yt/errors/unauthorized.rb +3 -3
- data/lib/yt/models/account.rb +12 -0
- data/lib/yt/models/advertising_options_set.rb +4 -4
- data/lib/yt/models/bulk_report.rb +23 -0
- data/lib/yt/models/bulk_report_job.rb +23 -0
- data/lib/yt/models/channel.rb +21 -12
- data/lib/yt/models/claim.rb +13 -2
- data/lib/yt/models/comment.rb +37 -0
- data/lib/yt/models/comment_thread.rb +50 -0
- data/lib/yt/models/content_detail.rb +6 -0
- data/lib/yt/models/content_owner.rb +31 -1
- data/lib/yt/models/group_info.rb +16 -0
- data/lib/yt/models/group_item.rb +15 -0
- data/lib/yt/models/resource.rb +3 -10
- data/lib/yt/models/revocation.rb +12 -0
- data/lib/yt/models/right_owner.rb +0 -2
- data/lib/yt/models/snippet.rb +24 -3
- data/lib/yt/models/video.rb +42 -11
- data/lib/yt/models/video_group.rb +186 -0
- data/lib/yt/request.rb +5 -3
- data/lib/yt/version.rb +2 -2
- data/spec/collections/comment_threads_spec.rb +46 -0
- data/spec/collections/playlist_items_spec.rb +1 -1
- data/spec/collections/reports_spec.rb +2 -2
- data/spec/constants/geography_spec.rb +16 -0
- data/spec/models/annotation_spec.rb +1 -1
- data/spec/models/claim_spec.rb +15 -3
- data/spec/models/comment_spec.rb +40 -0
- data/spec/models/comment_thread_spec.rb +93 -0
- data/spec/models/content_detail_spec.rb +7 -0
- data/spec/models/reference_spec.rb +2 -2
- data/spec/models/request_spec.rb +21 -0
- data/spec/models/resource_spec.rb +0 -15
- data/spec/models/video_spec.rb +1 -1
- data/spec/requests/as_account/account_spec.rb +16 -4
- data/spec/requests/as_account/authentications_spec.rb +1 -13
- data/spec/requests/as_account/channel_spec.rb +15 -45
- data/spec/requests/as_account/playlist_item_spec.rb +3 -3
- data/spec/requests/as_account/playlist_spec.rb +5 -32
- data/spec/requests/as_account/video_spec.rb +2022 -21
- data/spec/requests/as_content_owner/account_spec.rb +4 -0
- data/spec/requests/as_content_owner/bulk_report_job_spec.rb +19 -0
- data/spec/requests/as_content_owner/channel_spec.rb +59 -270
- data/spec/requests/as_content_owner/content_owner_spec.rb +89 -1
- data/spec/requests/as_content_owner/playlist_spec.rb +0 -15
- data/spec/requests/as_content_owner/video_group_spec.rb +112 -0
- data/spec/requests/as_content_owner/video_spec.rb +72 -146
- data/spec/requests/as_server_app/channel_spec.rb +1 -21
- data/spec/requests/as_server_app/comment_spec.rb +22 -0
- data/spec/requests/as_server_app/comment_thread_spec.rb +27 -0
- data/spec/requests/as_server_app/comment_threads_spec.rb +41 -0
- data/spec/requests/as_server_app/playlist_item_spec.rb +2 -2
- data/spec/requests/as_server_app/playlist_spec.rb +1 -22
- data/spec/requests/as_server_app/video_spec.rb +21 -19
- data/spec/requests/as_server_app/videos_spec.rb +5 -5
- data/spec/requests/unauthenticated/video_spec.rb +1 -9
- data/spec/spec_helper.rb +1 -1
- data/yt.gemspec +2 -1
- metadata +51 -17
- data/lib/yt/collections/ids.rb +0 -27
- data/lib/yt/config.rb +0 -54
- data/lib/yt/models/configuration.rb +0 -70
- data/lib/yt/models/description.rb +0 -58
- data/lib/yt/models/url.rb +0 -91
- data/spec/models/configuration_spec.rb +0 -44
- data/spec/models/description_spec.rb +0 -94
- data/spec/models/url_spec.rb +0 -84
- data/spec/requests/as_account/resource_spec.rb +0 -18
data/lib/yt/request.rb
CHANGED
@@ -197,7 +197,7 @@ module Yt
|
|
197
197
|
# for a couple of seconds might solve the connection issues.
|
198
198
|
def run_again?
|
199
199
|
refresh_token_and_retry? ||
|
200
|
-
server_error? && sleep_and_retry? ||
|
200
|
+
server_error? && sleep_and_retry?(3) ||
|
201
201
|
exceeded_quota? && sleep_and_retry?(3)
|
202
202
|
end
|
203
203
|
|
@@ -209,6 +209,8 @@ module Yt
|
|
209
209
|
Errno::EHOSTUNREACH,
|
210
210
|
Errno::ENETUNREACH,
|
211
211
|
Errno::ECONNRESET,
|
212
|
+
Net::OpenTimeout,
|
213
|
+
SocketError,
|
212
214
|
Net::HTTPServerError
|
213
215
|
] + extra_server_errors
|
214
216
|
end
|
@@ -232,7 +234,7 @@ module Yt
|
|
232
234
|
@retries_so_far += 1
|
233
235
|
if (@retries_so_far < max_retries)
|
234
236
|
@response = @http_request = @uri = nil
|
235
|
-
sleep 3
|
237
|
+
sleep 3 + (10 * @retries_so_far)
|
236
238
|
end
|
237
239
|
end
|
238
240
|
|
@@ -267,7 +269,7 @@ module Yt
|
|
267
269
|
|
268
270
|
# @return [Boolean] whether the request exceeds the YouTube quota
|
269
271
|
def exceeded_quota?
|
270
|
-
response_error == Errors::Forbidden && response.body =~ /
|
272
|
+
response_error == Errors::Forbidden && response.body =~ /Exceeded/i
|
271
273
|
end
|
272
274
|
|
273
275
|
# @return [Boolean] whether the request lacks proper authorization.
|
data/lib/yt/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Yt
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
2
|
+
VERSION = '0.32.2'
|
3
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yt/collections/comment_threads'
|
3
|
+
require 'yt/models/video'
|
4
|
+
require 'yt/models/channel'
|
5
|
+
|
6
|
+
describe Yt::Collections::CommentThreads do
|
7
|
+
subject(:collection) { Yt::Collections::CommentThreads.new parent: parent}
|
8
|
+
|
9
|
+
describe '#size', :ruby2 do
|
10
|
+
describe 'sends only one request and return the total results' do
|
11
|
+
let(:total_results) { 1234 }
|
12
|
+
let(:parent) { Yt::Video.new id: 'any-id' }
|
13
|
+
|
14
|
+
before do
|
15
|
+
expect_any_instance_of(Yt::Request).to receive(:run).once do
|
16
|
+
double(body: {'pageInfo'=>{'totalResults'=>total_results}})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
it { expect(collection.size).to be total_results }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#count' do
|
24
|
+
let(:query) { {q: 'search string'} }
|
25
|
+
let(:parent) { Yt::Video.new id: 'any-id' }
|
26
|
+
let(:page) { {items: [], token: 'any-token'} }
|
27
|
+
|
28
|
+
context 'called once with .where(query) and once without' do
|
29
|
+
after do
|
30
|
+
collection.where(query).count
|
31
|
+
collection.count
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'only applies the query on the first call' do
|
35
|
+
expect(collection).to receive(:fetch_page) do |options|
|
36
|
+
expect(options[:params]).to include query
|
37
|
+
page
|
38
|
+
end
|
39
|
+
expect(collection).to receive(:fetch_page) do |options|
|
40
|
+
expect(options[:params]).not_to include query
|
41
|
+
page
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -6,7 +6,7 @@ require 'yt/collections/playlist_items'
|
|
6
6
|
describe Yt::Collections::PlaylistItems do
|
7
7
|
subject(:collection) { Yt::Collections::PlaylistItems.new parent: playlist }
|
8
8
|
let(:playlist) { Yt::Playlist.new id: 'LLxO1tY8h1AhOz0T4ENwmpow' }
|
9
|
-
let(:attrs) { {id: '
|
9
|
+
let(:attrs) { {id: '9bZkp7q19f0', kind: :video} }
|
10
10
|
let(:msg) { {response_body: {error: {errors: [{reason: reason}]}}}.to_json }
|
11
11
|
before { expect(collection).to behave }
|
12
12
|
|
@@ -9,7 +9,7 @@ describe Yt::Collections::Reports do
|
|
9
9
|
let(:msg) { {response_body: {error: {errors: [error]}}}.to_json }
|
10
10
|
|
11
11
|
describe '#within' do
|
12
|
-
let(:result) { reports.within Range.new(5.days.ago, 4.days.ago), nil, nil, :day, nil }
|
12
|
+
let(:result) { reports.within Range.new(5.days.ago, 4.days.ago), nil, nil, :day, nil, nil }
|
13
13
|
context 'given the request raises error 400 with "Invalid Query" message' do
|
14
14
|
let(:reason) { 'badRequest' }
|
15
15
|
let(:message) { 'Invalid query. Query did not conform to the expectations' }
|
@@ -27,4 +27,4 @@ describe Yt::Collections::Reports do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yt/constants/geography'
|
3
|
+
|
4
|
+
describe 'Yt::COUNTRIES' do
|
5
|
+
it 'returns all country codes and names' do
|
6
|
+
expect(Yt::COUNTRIES[:US]).to eq 'United States'
|
7
|
+
expect(Yt::COUNTRIES['IT']).to eq 'Italy'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'Yt::US_STATES' do
|
12
|
+
it 'returns all U.S. state codes and names' do
|
13
|
+
expect(Yt::US_STATES[:CA]).to eq 'California'
|
14
|
+
expect(Yt::US_STATES['CO']).to eq 'Colorado'
|
15
|
+
end
|
16
|
+
end
|
@@ -86,7 +86,7 @@ describe Yt::Annotation do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
context 'given an annotation with an embedded playlist link' do
|
89
|
-
let(:xml) { '<TEXT>https://www.youtube.com/watch?v=
|
89
|
+
let(:xml) { '<TEXT>https://www.youtube.com/watch?v=9bZkp7q19f0&list=LLxO1tY8h1AhOz0T4ENwmpow"</TEXT>' }
|
90
90
|
it { expect(annotation).to have_link_to_playlist }
|
91
91
|
end
|
92
92
|
|
data/spec/models/claim_spec.rb
CHANGED
@@ -20,8 +20,8 @@ describe Yt::Claim do
|
|
20
20
|
|
21
21
|
describe '#video_id' do
|
22
22
|
context 'given fetching a claim returns an videoId' do
|
23
|
-
let(:data) { {"videoId"=>"
|
24
|
-
it { expect(claim.video_id).to eq '
|
23
|
+
let(:data) { {"videoId"=>"9bZkp7q19f0"} }
|
24
|
+
it { expect(claim.video_id).to eq '9bZkp7q19f0' }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -208,4 +208,16 @@ describe Yt::Claim do
|
|
208
208
|
it { expect(claim.third_party?).to be false }
|
209
209
|
end
|
210
210
|
end
|
211
|
-
|
211
|
+
|
212
|
+
describe '#source' do
|
213
|
+
context 'given fetching a claim returns a source' do
|
214
|
+
let(:data) { {"origin"=>{"source"=>"webUpload"}} }
|
215
|
+
it { expect(claim.source).to eq 'webUpload' }
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'given fetching a claim does not return a source' do
|
219
|
+
let(:data) { {"origin"=>{}} }
|
220
|
+
it { expect(claim.source).to eq nil }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yt/models/comment'
|
3
|
+
|
4
|
+
describe Yt::Comment do
|
5
|
+
subject(:comment) { Yt::Comment.new attrs }
|
6
|
+
|
7
|
+
describe '#snippet' do
|
8
|
+
context 'given fetching a comment returns a snippet' do
|
9
|
+
let(:attrs) { {snippet: {"videoId" => "12345"}} }
|
10
|
+
it { expect(comment.snippet).to be_a Yt::Snippet }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'attributes' do
|
15
|
+
examples = {
|
16
|
+
video_id: {with: 'xyz123', without: nil},
|
17
|
+
parent_id: {with: 'abc123', without: nil},
|
18
|
+
text_display: {with: 'awesome', without: nil},
|
19
|
+
author_display_name: {with: 'John', without: nil},
|
20
|
+
like_count: {with: 10, without: nil},
|
21
|
+
updated_at: {input: '2016-03-22T12:56:56.3Z', with: Time.parse('2016-03-22T12:56:56.3Z'), without: nil},
|
22
|
+
}
|
23
|
+
|
24
|
+
examples.each do |attr, cases|
|
25
|
+
describe "##{attr}" do
|
26
|
+
context "given a snippet with a #{attr}" do
|
27
|
+
let(:attrs) {
|
28
|
+
{snippet: {"#{attr.to_s.camelize(:lower)}" => cases[:input] || cases[:with]}}}
|
29
|
+
it { expect(comment.send(attr)).to eq cases[:with] }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "given a snippet without a #{attr}" do
|
33
|
+
let(:attrs) { {snippet: {}} }
|
34
|
+
it { expect(comment.send(attr)).to eq cases[:without] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yt/models/comment_thread'
|
3
|
+
|
4
|
+
describe Yt::CommentThread do
|
5
|
+
subject(:comment_thread) { Yt::CommentThread.new attrs }
|
6
|
+
|
7
|
+
describe '#snippet' do
|
8
|
+
context 'given fetching a comment thread returns a snippet' do
|
9
|
+
let(:attrs) { {snippet: {"videoId" => "12345"}} }
|
10
|
+
it { expect(comment_thread.snippet).to be_a Yt::Snippet }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#video_id' do
|
15
|
+
context 'given a snippet with a video id' do
|
16
|
+
let(:attrs) { {snippet: {"videoId"=>"12345"}} }
|
17
|
+
it { expect(comment_thread.video_id).to eq '12345' }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'given a snippet without a video id' do
|
21
|
+
let(:attrs) { {snippet: {}} }
|
22
|
+
it { expect(comment_thread.video_id).to be_nil }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#top_level_comment' do
|
27
|
+
context 'given a snippet with a top level comment' do
|
28
|
+
let(:attrs) { {snippet: {"topLevelComment"=> {}}} }
|
29
|
+
it { expect(comment_thread.top_level_comment).to be_a Yt::Comment }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'attributes from #top_level_comment delegations' do
|
34
|
+
context 'with values' do
|
35
|
+
let(:attrs) { {snippet: {"topLevelComment"=> {"id" => "xyz123", "snippet" => {
|
36
|
+
"textDisplay" => "funny video!",
|
37
|
+
"authorDisplayName" => "fullscreen",
|
38
|
+
"likeCount" => 99,
|
39
|
+
"updatedAt" => "2016-03-22T12:56:56.3Z"}}}} }
|
40
|
+
|
41
|
+
it { expect(comment_thread.top_level_comment.id).to eq 'xyz123' }
|
42
|
+
it { expect(comment_thread.text_display).to eq 'funny video!' }
|
43
|
+
it { expect(comment_thread.author_display_name).to eq 'fullscreen' }
|
44
|
+
it { expect(comment_thread.like_count).to eq 99 }
|
45
|
+
it { expect(comment_thread.updated_at).to eq Time.parse('2016-03-22T12:56:56.3Z') }
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'without values' do
|
49
|
+
let(:attrs) { {snippet: {"topLevelComment"=> {"snippet" => {}}}} }
|
50
|
+
|
51
|
+
it { expect(comment_thread.text_display).to be_nil }
|
52
|
+
it { expect(comment_thread.author_display_name).to be_nil }
|
53
|
+
it { expect(comment_thread.like_count).to be_nil }
|
54
|
+
it { expect(comment_thread.updated_at).to be_nil }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#total_reply_count' do
|
59
|
+
context 'given a snippet with a total reply count' do
|
60
|
+
let(:attrs) { {snippet: {"totalReplyCount"=>1}} }
|
61
|
+
it { expect(comment_thread.total_reply_count).to eq 1 }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'given a snippet without a total reply count' do
|
65
|
+
let(:attrs) { {snippet: {}} }
|
66
|
+
it { expect(comment_thread.total_reply_count).to be_nil }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#can_reply?' do
|
71
|
+
context 'given a snippet with canReply set' do
|
72
|
+
let(:attrs) { {snippet: {"canReply"=>true}} }
|
73
|
+
it { expect(comment_thread.can_reply?).to be true }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'given a snippet without canReply set' do
|
77
|
+
let(:attrs) { {snippet: {}} }
|
78
|
+
it { expect(comment_thread.can_reply?).to be false }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#is_public?' do
|
83
|
+
context 'given a snippet with isPublic set' do
|
84
|
+
let(:attrs) { {snippet: {"isPublic"=>true}} }
|
85
|
+
it { expect(comment_thread).to be_public }
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'given a snippet without isPublic set' do
|
89
|
+
let(:attrs) { {snippet: {}} }
|
90
|
+
it { expect(comment_thread).to_not be_public }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -42,4 +42,11 @@ describe Yt::ContentDetail do
|
|
42
42
|
it { expect(content_detail.duration).to eq 51 }
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
describe '#length' do
|
47
|
+
context 'returns the duration in HH:MM:SS' do
|
48
|
+
let(:data) { {"duration"=>"PT1H18M52S"} }
|
49
|
+
it { expect(content_detail.length).to eq '01:18:52' }
|
50
|
+
end
|
51
|
+
end
|
45
52
|
end
|
@@ -156,8 +156,8 @@ describe Yt::Reference do
|
|
156
156
|
|
157
157
|
describe '#video_id' do
|
158
158
|
context 'given fetching a reference returns an videoId' do
|
159
|
-
let(:data) { {"videoId"=>"
|
160
|
-
it { expect(reference.video_id).to eq '
|
159
|
+
let(:data) { {"videoId"=>"9bZkp7q19f0"} }
|
160
|
+
it { expect(reference.video_id).to eq '9bZkp7q19f0' }
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
data/spec/models/request_spec.rb
CHANGED
@@ -178,6 +178,27 @@ describe Yt::Request do
|
|
178
178
|
it { expect{request.run}.not_to fail }
|
179
179
|
end
|
180
180
|
end
|
181
|
+
|
182
|
+
# NOTE: This test is just a reflection of YouTube irrational behavior of
|
183
|
+
# being unavailable once in a while, and therefore causing Net::HTTP to
|
184
|
+
# fail, although retrying after some seconds works.
|
185
|
+
context 'a SocketError', ruby21: true do
|
186
|
+
let(:http_error) { SocketError.new }
|
187
|
+
|
188
|
+
context 'every time' do
|
189
|
+
before { expect(Net::HTTP).to receive(:start).at_least(:once).and_raise http_error }
|
190
|
+
|
191
|
+
it { expect{request.run}.to fail }
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'but works the second time' do
|
195
|
+
before { expect(Net::HTTP).to receive(:start).at_least(:once).and_return retry_response }
|
196
|
+
before { allow(retry_response).to receive(:body) }
|
197
|
+
let(:retry_response) { Net::HTTPOK.new nil, nil, nil }
|
198
|
+
|
199
|
+
it { expect{request.run}.not_to fail }
|
200
|
+
end
|
201
|
+
end
|
181
202
|
end
|
182
203
|
end
|
183
204
|
end
|
@@ -4,21 +4,6 @@ require 'yt/models/resource'
|
|
4
4
|
describe Yt::Resource do
|
5
5
|
subject(:resource) { Yt::Resource.new attrs }
|
6
6
|
|
7
|
-
context 'given a resource initialized with a URL (containing an ID)' do
|
8
|
-
let(:attrs) { {url: 'youtu.be/MESycYJytkU'} }
|
9
|
-
|
10
|
-
it { expect(resource.id).to eq 'MESycYJytkU' }
|
11
|
-
it { expect(resource.kind).to eq 'video' }
|
12
|
-
it { expect(resource.username).to be_nil }
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'given a resource initialized with a URL (containing a username)' do
|
16
|
-
let(:attrs) { {url: 'youtube.com/fullscreen'} }
|
17
|
-
|
18
|
-
it { expect(resource.kind).to eq 'channel' }
|
19
|
-
it { expect(resource.username).to eq 'fullscreen' }
|
20
|
-
end
|
21
|
-
|
22
7
|
describe '#public?' do
|
23
8
|
context 'given fetching a status returns privacyStatus "public"' do
|
24
9
|
let(:attrs) { {status: {"privacyStatus"=>"public"}} }
|
data/spec/models/video_spec.rb
CHANGED
@@ -649,7 +649,7 @@ describe Yt::Video do
|
|
649
649
|
end
|
650
650
|
|
651
651
|
describe '#update' do
|
652
|
-
let(:attrs) { {id: '
|
652
|
+
let(:attrs) { {id: '9bZkp7q19f0', snippet: {'title'=>'old'}} }
|
653
653
|
before { expect(video).to receive(:do_update).and_yield 'snippet'=>{'title'=>'new'} }
|
654
654
|
|
655
655
|
it { expect(video.update title: 'new').to be true }
|
@@ -27,10 +27,7 @@ describe Yt::Account, :device_app do
|
|
27
27
|
expect(uploads).not_to be_empty
|
28
28
|
end
|
29
29
|
|
30
|
-
specify 'includes private playlists (such as
|
31
|
-
watch_later = related_playlists.select{|p| p.title == 'Watch Later'}
|
32
|
-
expect(watch_later).not_to be_empty
|
33
|
-
|
30
|
+
specify 'includes private playlists (such as History)' do
|
34
31
|
history = related_playlists.select{|p| p.title == 'History'}
|
35
32
|
expect(history).not_to be_empty
|
36
33
|
end
|
@@ -97,6 +94,21 @@ describe Yt::Account, :device_app do
|
|
97
94
|
end
|
98
95
|
end
|
99
96
|
|
97
|
+
describe '.video_groups' do
|
98
|
+
let(:video_group) { $account.video_groups.first }
|
99
|
+
|
100
|
+
specify 'returns the first video-group created by the account' do
|
101
|
+
expect(video_group).to be_a Yt::VideoGroup
|
102
|
+
expect(video_group.title).to be_a String
|
103
|
+
expect(video_group.item_count).to be_an Integer
|
104
|
+
expect(video_group.published_at).to be_a Time
|
105
|
+
end
|
106
|
+
|
107
|
+
specify 'allows to run reports against each video-group' do
|
108
|
+
expect(video_group.views).to be
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
100
112
|
describe '.upload_video' do
|
101
113
|
let(:video_params) { {title: 'Test Yt upload', privacy_status: 'private', category_id: 17} }
|
102
114
|
let(:video) { $account.upload_video path_or_url, video_params }
|
@@ -41,7 +41,7 @@ describe Yt::Account, :device_app do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
context 'that is invalid' do
|
44
|
-
let(:authorization_code) {
|
44
|
+
let(:authorization_code) { rand(36**20).to_s(36) }
|
45
45
|
it { expect{account.authentication}.to raise_error Yt::Errors::Unauthorized }
|
46
46
|
end
|
47
47
|
end
|
@@ -104,18 +104,6 @@ describe Yt::Account, :device_app do
|
|
104
104
|
|
105
105
|
it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
|
106
106
|
end
|
107
|
-
|
108
|
-
context 'and no device token' do
|
109
|
-
it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
|
110
|
-
end
|
111
|
-
|
112
|
-
# NOTE: This test is commented out because of YouTube irrational behavior
|
113
|
-
# of using to return "MissingAuth" when passing a wrong device code, and
|
114
|
-
# now randomly returning `{"error"=>"internal_failure"}` instead.
|
115
|
-
# context 'and an invalid device code' do
|
116
|
-
# before { attrs[:device_code] = '--not-a-valid-device-code--' }
|
117
|
-
# it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
|
118
|
-
# end
|
119
107
|
end
|
120
108
|
|
121
109
|
context 'given no token or code' do
|