yt 0.25.13 → 0.32.2
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 +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
|