yt 0.5.4 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/HISTORY.md +3 -0
- data/README.md +2 -4
- data/bin/yt +0 -7
- data/lib/yt/actions/delete.rb +3 -3
- data/lib/yt/actions/insert.rb +3 -3
- data/lib/yt/actions/list.rb +4 -6
- data/lib/yt/actions/update.rb +7 -5
- data/lib/yt/associations/authentications.rb +114 -0
- data/lib/yt/associations.rb +1 -0
- data/lib/yt/collections/annotations.rb +2 -2
- data/lib/yt/collections/authentications.rb +42 -0
- data/lib/yt/collections/base.rb +1 -1
- data/lib/yt/collections/playlist_items.rb +1 -1
- data/lib/yt/collections/snippets.rb +1 -2
- data/lib/yt/collections/subscriptions.rb +1 -1
- data/lib/yt/collections/user_infos.rb +2 -2
- data/lib/yt/config.rb +0 -2
- data/lib/yt/errors/missing_auth.rb +50 -0
- data/lib/yt/errors/no_items.rb +5 -9
- data/lib/yt/errors/request_error.rb +52 -0
- data/lib/yt/models/account.rb +17 -44
- data/lib/yt/models/annotation.rb +117 -115
- data/lib/yt/models/authentication.rb +27 -0
- data/lib/yt/models/base.rb +9 -5
- data/lib/yt/models/channel.rb +6 -4
- data/lib/yt/models/configuration.rb +27 -35
- data/lib/yt/models/description.rb +61 -59
- data/lib/yt/models/details_set.rb +26 -24
- data/lib/yt/models/id.rb +3 -1
- data/lib/yt/models/playlist.rb +38 -36
- data/lib/yt/models/playlist_item.rb +29 -27
- data/lib/yt/models/rating.rb +18 -16
- data/lib/yt/models/request.rb +95 -73
- data/lib/yt/models/resource.rb +19 -17
- data/lib/yt/models/snippet.rb +39 -37
- data/lib/yt/models/status.rb +20 -18
- data/lib/yt/models/subscription.rb +24 -22
- data/lib/yt/models/url.rb +68 -68
- data/lib/yt/models/user_info.rb +51 -49
- data/lib/yt/models/video.rb +6 -4
- data/lib/yt/version.rb +1 -1
- data/spec/associations/device_auth/authentications_spec.rb +78 -0
- data/spec/associations/device_auth/channels_spec.rb +2 -4
- data/spec/associations/device_auth/details_sets_spec.rb +4 -5
- data/spec/associations/device_auth/ids_spec.rb +2 -3
- data/spec/associations/device_auth/playlist_items_spec.rb +3 -4
- data/spec/associations/device_auth/playlists_spec.rb +14 -15
- data/spec/associations/device_auth/ratings_spec.rb +2 -4
- data/spec/associations/device_auth/snippets_spec.rb +5 -7
- data/spec/associations/device_auth/subscriptions_spec.rb +2 -4
- data/spec/associations/device_auth/user_infos_spec.rb +2 -5
- data/spec/associations/device_auth/videos_spec.rb +3 -5
- data/spec/associations/server_auth/details_sets_spec.rb +1 -1
- data/spec/associations/server_auth/ids_spec.rb +1 -1
- data/spec/associations/server_auth/playlist_items_spec.rb +1 -1
- data/spec/associations/server_auth/playlists_spec.rb +1 -1
- data/spec/associations/server_auth/snippets_spec.rb +1 -1
- data/spec/associations/server_auth/videos_spec.rb +1 -1
- data/spec/collections/playlist_items_spec.rb +3 -4
- data/spec/collections/subscriptions_spec.rb +2 -3
- data/spec/errors/missing_auth_spec.rb +10 -0
- data/spec/errors/no_items_spec.rb +2 -1
- data/spec/errors/request_error_spec.rb +18 -0
- data/spec/models/configuration_spec.rb +0 -17
- data/spec/models/description_spec.rb +5 -5
- data/spec/models/request_spec.rb +1 -7
- data/spec/models/subscription_spec.rb +2 -3
- data/spec/models/url_spec.rb +6 -6
- data/spec/support/fail_matcher.rb +1 -1
- data/spec/support/global_hooks.rb +33 -0
- metadata +15 -14
- data/lib/yt/errors/base.rb +0 -43
- data/lib/yt/errors/error.rb +0 -8
- data/lib/yt/errors/failed.rb +0 -17
- data/lib/yt/errors/unauthenticated.rb +0 -34
- data/spec/errors/failed_spec.rb +0 -9
- data/spec/errors/unauthenticated_spec.rb +0 -9
- data/spec/support/device_app.rb +0 -15
- data/spec/support/server_app.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c609731e55cd81b71dfbc3f53f47147dac54b51
|
4
|
+
data.tar.gz: 0993daf654fda172520af7d6d01e8620ff66fbf6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60d5bf311041934ec83bd36cfff50905d04e3ef8444f90e8be39ad3249da24618ef308bdee47688785a5bdc8d8024d91dc2a253a44d3cf204d331e6209eb66cb
|
7
|
+
data.tar.gz: fea7bc7fb323a51c21be93a0108e8d3350f808c8a4bb56cc956d2db5d4cf8c720fe6bdb8e492f39f65337d76d8843edf9898cd2464f9d354adc9ca089d0a834b
|
data/Gemfile.lock
CHANGED
data/HISTORY.md
CHANGED
@@ -7,6 +7,9 @@ v0.5 - 2014/05/16
|
|
7
7
|
* Add `has_one :id` to resources, to retrieve the ID of resources initialized by URL
|
8
8
|
* Raise an error if some `has_one` associations are not found (id, snippet, details set, user info)
|
9
9
|
* Don't check for the right :scope if Account is initialized with credentials
|
10
|
+
* Move models in Yt::Models but still auto-include them in the main namespace
|
11
|
+
* New Authentication model to separate `access_token` and `refresh_token` from Account
|
12
|
+
* New types of Errors that render more verbose errors and the failing request in cURL syntax
|
10
13
|
|
11
14
|
v0.4 - 2014/05/09
|
12
15
|
--------------------
|
data/README.md
CHANGED
@@ -8,6 +8,7 @@ Yt helps you write apps that need to interact with the YouTube API V3.
|
|
8
8
|
[![Build Status](https://travis-ci.org/Fullscreen/yt.png?branch=master)](https://travis-ci.org/Fullscreen/yt)
|
9
9
|
[![Coverage Status](https://coveralls.io/repos/Fullscreen/yt/badge.png?)](https://coveralls.io/r/Fullscreen/yt)
|
10
10
|
[![Code Climate](https://codeclimate.com/github/Fullscreen/yt.png)](https://codeclimate.com/github/Fullscreen/yt)
|
11
|
+
[![Inline docs](http://inch-pages.github.io/github/Fullscreen/yt.png)](http://inch-pages.github.io/github/Fullscreen/yt)
|
11
12
|
|
12
13
|
After [registering your app](#registering-your-app), you can run commands like:
|
13
14
|
|
@@ -169,7 +170,6 @@ Next, add the following snippet of code to the initializer of your app:
|
|
169
170
|
|
170
171
|
```ruby
|
171
172
|
Yt.configure do |config|
|
172
|
-
config.scenario = :server_app
|
173
173
|
config.api_key = '123456789012345678901234567890'
|
174
174
|
end
|
175
175
|
```
|
@@ -246,7 +246,6 @@ refresh token, then add the following snippet of code to the initializer of your
|
|
246
246
|
|
247
247
|
```ruby
|
248
248
|
Yt.configure do |config|
|
249
|
-
config.scenario = :device_app
|
250
249
|
config.client_id = '1234567890.apps.googleusercontent.com'
|
251
250
|
config.client_secret = '1234567890'
|
252
251
|
end
|
@@ -282,7 +281,6 @@ is equivalent to configuration your app with the initializer:
|
|
282
281
|
|
283
282
|
```ruby
|
284
283
|
Yt.configure do |config|
|
285
|
-
config.scenario = :device_app
|
286
284
|
config.client_id = '1234567890.apps.googleusercontent.com'
|
287
285
|
config.client_secret = '1234567890'
|
288
286
|
config.api_key = '123456789012345678901234567890'
|
@@ -302,7 +300,7 @@ To install on your system, run
|
|
302
300
|
|
303
301
|
To use inside a bundled Ruby project, add this line to the Gemfile:
|
304
302
|
|
305
|
-
gem 'yt', '~> 0.5.
|
303
|
+
gem 'yt', '~> 0.5.5'
|
306
304
|
|
307
305
|
Since the gem follows [Semantic Versioning](http://semver.org),
|
308
306
|
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
|
data/bin/yt
CHANGED
@@ -9,13 +9,6 @@ end
|
|
9
9
|
|
10
10
|
############################
|
11
11
|
|
12
|
-
Yt.configure do |config|
|
13
|
-
config.scenario = :server_app
|
14
|
-
config.api_key = ENV['YT_TEST_APP_SERVER_API_KEY']
|
15
|
-
end
|
16
|
-
|
17
|
-
############################
|
18
|
-
|
19
12
|
channel = Yt::Channel.new id: ARGV[0] || 'UCxO1tY8h1AhOz0T4ENwmpow'
|
20
13
|
|
21
14
|
puts "Title: #{channel.title}"
|
data/lib/yt/actions/delete.rb
CHANGED
@@ -7,16 +7,16 @@ module Yt
|
|
7
7
|
private
|
8
8
|
|
9
9
|
def do_delete(extra_delete_params = {})
|
10
|
-
request = Request.new delete_params.merge(extra_delete_params)
|
10
|
+
request = Yt::Request.new delete_params.merge(extra_delete_params)
|
11
11
|
response = request.run
|
12
|
-
raise unless response.is_a? Net::HTTPNoContent
|
13
12
|
yield response.body
|
14
13
|
end
|
15
14
|
|
16
15
|
def delete_params
|
17
|
-
|
16
|
+
{}.tap do |params|
|
18
17
|
params[:method] = :delete
|
19
18
|
params[:auth] = @auth
|
19
|
+
params[:expected_response] = Net::HTTPNoContent
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
data/lib/yt/actions/insert.rb
CHANGED
@@ -7,17 +7,17 @@ module Yt
|
|
7
7
|
private
|
8
8
|
|
9
9
|
def do_insert(extra_insert_params = {})
|
10
|
-
request = Request.new insert_params.merge(extra_insert_params)
|
10
|
+
request = Yt::Request.new insert_params.merge(extra_insert_params)
|
11
11
|
response = request.run
|
12
|
-
raise unless response.is_a? Net::HTTPOK
|
13
12
|
@items = []
|
14
13
|
new_item response.body
|
15
14
|
end
|
16
15
|
|
17
16
|
def insert_params
|
18
|
-
|
17
|
+
{}.tap do |params|
|
19
18
|
params[:method] = :post
|
20
19
|
params[:auth] = @auth
|
20
|
+
params[:expected_response] = Net::HTTPOK
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/yt/actions/list.rb
CHANGED
@@ -8,9 +8,7 @@ module Yt
|
|
8
8
|
alias size count
|
9
9
|
|
10
10
|
def first!
|
11
|
-
first.tap
|
12
|
-
raise Errors::NoItems unless item
|
13
|
-
end
|
11
|
+
first.tap{|item| raise Errors::NoItems unless item}
|
14
12
|
end
|
15
13
|
|
16
14
|
private
|
@@ -50,9 +48,8 @@ module Yt
|
|
50
48
|
end
|
51
49
|
|
52
50
|
def fetch_page(params = {})
|
53
|
-
request = Request.new params
|
51
|
+
request = Yt::Request.new params
|
54
52
|
response = request.run
|
55
|
-
raise unless response.is_a? Net::HTTPOK
|
56
53
|
token = response.body['nextPageToken']
|
57
54
|
items = response.body.fetch 'items', []
|
58
55
|
{items: items, token: token}
|
@@ -61,10 +58,11 @@ module Yt
|
|
61
58
|
def list_params
|
62
59
|
path = "/youtube/v3/#{self.class.to_s.demodulize.camelize :lower}"
|
63
60
|
|
64
|
-
|
61
|
+
{}.tap do |params|
|
65
62
|
params[:method] = :get
|
66
63
|
params[:auth] = @auth
|
67
64
|
params[:path] = path
|
65
|
+
params[:exptected_response] = Net::HTTPOK
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
data/lib/yt/actions/update.rb
CHANGED
@@ -3,18 +3,20 @@ require 'yt/models/request'
|
|
3
3
|
module Yt
|
4
4
|
module Actions
|
5
5
|
module Update
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def do_update(extra_update_params = {})
|
10
|
+
request = Yt::Request.new update_params.deep_merge(extra_update_params)
|
8
11
|
response = request.run
|
9
|
-
expected_response = options.fetch :expect, Net::HTTPNoContent
|
10
|
-
raise unless response.is_a? expected_response
|
11
12
|
yield response.body
|
12
13
|
end
|
13
14
|
|
14
15
|
def update_params
|
15
|
-
|
16
|
+
{}.tap do |params|
|
16
17
|
params[:method] = :put
|
17
18
|
params[:auth] = @auth
|
19
|
+
params[:expected_response] = Net::HTTPNoContent
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'yt/collections/authentications'
|
2
|
+
require 'yt/config'
|
3
|
+
require 'yt/errors/no_items'
|
4
|
+
require 'yt/errors/missing_auth'
|
5
|
+
|
6
|
+
module Yt
|
7
|
+
module Associations
|
8
|
+
# Provides the `has_one :access_token` method to YouTube resources, which
|
9
|
+
# allows to access to content detail set-specific methods like `access_token`.
|
10
|
+
# YouTube resources with access tokens are: accounts.
|
11
|
+
module Authentications
|
12
|
+
def authentication
|
13
|
+
@authentication = current_authentication
|
14
|
+
@authentication ||= new_authentication || refreshed_authentication!
|
15
|
+
end
|
16
|
+
|
17
|
+
def authentication_url
|
18
|
+
host = 'accounts.google.com'
|
19
|
+
path = '/o/oauth2/auth'
|
20
|
+
query = authentication_url_params.to_param
|
21
|
+
URI::HTTPS.build(host: host, path: path, query: query).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def current_authentication
|
27
|
+
@authentication ||= Yt::Authentication.new current_data if @access_token
|
28
|
+
@authentication unless @authentication.nil? || @authentication.expired?
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_data
|
32
|
+
{}.tap do |data|
|
33
|
+
data['access_token'] = @access_token
|
34
|
+
data['expires_at'] = @expires_at
|
35
|
+
data['refresh_token'] = @refresh_token
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Tries to obtain an access token using the authorization code (which
|
40
|
+
# can only be used once). On failure, does not raise an error because
|
41
|
+
# the access token might still be retrieved with a refresh token.
|
42
|
+
def new_authentication
|
43
|
+
new_authentications.first!
|
44
|
+
rescue Errors::NoItems => error
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# Tries to obtain an access token using the refresh token (which can
|
49
|
+
# be used multiple times). On failure, raise an error because there are
|
50
|
+
# no more options to obtain an access token.
|
51
|
+
def refreshed_authentication!
|
52
|
+
refreshed_authentications.first!
|
53
|
+
rescue Errors::NoItems => error
|
54
|
+
raise Errors::MissingAuth, error.to_param
|
55
|
+
end
|
56
|
+
|
57
|
+
def new_authentications
|
58
|
+
@new_authentications ||= Collections::Authentications.of(self).tap do |auth|
|
59
|
+
auth.auth_params = new_authentication_params
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def refreshed_authentications
|
64
|
+
@refreshed_authentications ||= Collections::Authentications.of(self).tap do |auth|
|
65
|
+
auth.auth_params = refreshed_authentication_params
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def authentication_url_params
|
70
|
+
{}.tap do |params|
|
71
|
+
params[:client_id] = client_id
|
72
|
+
params[:scope] = authentication_scope
|
73
|
+
params[:redirect_uri] = @redirect_uri
|
74
|
+
params[:response_type] = :code
|
75
|
+
params[:access_type] = :offline
|
76
|
+
# params[:include_granted_scopes] = true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def authentication_scope
|
81
|
+
@scopes.map do |scope|
|
82
|
+
"https://www.googleapis.com/auth/#{scope}"
|
83
|
+
end.join(' ') if @scopes.is_a?(Array)
|
84
|
+
end
|
85
|
+
|
86
|
+
def new_authentication_params
|
87
|
+
{}.tap do |params|
|
88
|
+
params[:client_id] = client_id
|
89
|
+
params[:client_secret] = client_secret
|
90
|
+
params[:code] = @authorization_code
|
91
|
+
params[:redirect_uri] = @redirect_uri
|
92
|
+
params[:grant_type] = :authorization_code
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def refreshed_authentication_params
|
97
|
+
{}.tap do |params|
|
98
|
+
params[:client_id] = client_id
|
99
|
+
params[:client_secret] = client_secret
|
100
|
+
params[:refresh_token] = @refresh_token
|
101
|
+
params[:grant_type] = :refresh_token
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def client_id
|
106
|
+
Yt.configuration.client_id
|
107
|
+
end
|
108
|
+
|
109
|
+
def client_secret
|
110
|
+
Yt.configuration.client_secret
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/yt/associations.rb
CHANGED
@@ -17,13 +17,13 @@ module Yt
|
|
17
17
|
params[:host] = 'www.youtube.com'
|
18
18
|
params[:path] = '/annotations_invideo'
|
19
19
|
params[:params] = {video_id: @parent.id}
|
20
|
+
params[:expected_response] = Net::HTTPOK
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
24
|
def next_page
|
24
|
-
request = Request.new list_params
|
25
|
+
request = Yt::Request.new list_params
|
25
26
|
response = request.run
|
26
|
-
raise unless response.is_a? Net::HTTPOK
|
27
27
|
@page_token = nil
|
28
28
|
|
29
29
|
document = response.body.fetch('document', {})['annotations'] || {}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'yt/collections/base'
|
2
|
+
require 'yt/models/authentication'
|
3
|
+
|
4
|
+
module Yt
|
5
|
+
module Collections
|
6
|
+
class Authentications < Base
|
7
|
+
attr_accessor :auth_params
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def new_item(data)
|
12
|
+
Yt::Authentication.new data
|
13
|
+
end
|
14
|
+
|
15
|
+
def list_params
|
16
|
+
super.tap do |params|
|
17
|
+
params[:host] = 'accounts.google.com'
|
18
|
+
params[:path] = '/o/oauth2/token'
|
19
|
+
params[:body_type] = :form
|
20
|
+
params[:method] = :post
|
21
|
+
params[:auth] = nil
|
22
|
+
params[:body] = auth_params
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def more_pages?
|
27
|
+
auth_params.values.all?
|
28
|
+
end
|
29
|
+
|
30
|
+
def next_page
|
31
|
+
request = Yt::Request.new list_params
|
32
|
+
Array.wrap request.run.body
|
33
|
+
rescue Yt::Error => error
|
34
|
+
expected?(error) ? [] : raise(error)
|
35
|
+
end
|
36
|
+
|
37
|
+
def expected?(error)
|
38
|
+
error.kind == 'invalid_grant'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/yt/collections/base.rb
CHANGED
@@ -11,7 +11,7 @@ module Yt
|
|
11
11
|
resource["#{attrs[:kind]}Id"] = attrs[:id]
|
12
12
|
snippet = {playlistId: @parent.id, resourceId: resource}
|
13
13
|
do_insert body: {snippet: snippet}, params: {part: 'snippet,status'}
|
14
|
-
rescue
|
14
|
+
rescue Yt::Error => error
|
15
15
|
ignorable_errors = error.reasons & ['videoNotFound', 'forbidden']
|
16
16
|
raise error unless options[:ignore_errors] && ignorable_errors.any?
|
17
17
|
end
|
@@ -12,10 +12,9 @@ module Yt
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def list_params
|
15
|
-
parents_path = @parent.class.to_s.demodulize.underscore.pluralize
|
16
15
|
super.tap do |params|
|
17
16
|
params[:params] = {id: @parent.id, part: 'snippet'}
|
18
|
-
params[:path] = "/youtube/v3/#{
|
17
|
+
params[:path] = "/youtube/v3/#{@parent.kind.pluralize}"
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -14,13 +14,13 @@ module Yt
|
|
14
14
|
def list_params
|
15
15
|
super.tap do |params|
|
16
16
|
params[:path] = '/oauth2/v2/userinfo'
|
17
|
+
params[:expected_response] = Net::HTTPOK
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
def next_page
|
21
|
-
request = Request.new list_params
|
22
|
+
request = Yt::Request.new list_params
|
22
23
|
response = request.run
|
23
|
-
raise unless response.is_a? Net::HTTPOK
|
24
24
|
@page_token = nil
|
25
25
|
|
26
26
|
Array.wrap response.body
|
data/lib/yt/config.rb
CHANGED
@@ -13,7 +13,6 @@ module Yt
|
|
13
13
|
# @example A server-to-server YouTube client app
|
14
14
|
#
|
15
15
|
# Yt.configure do |config|
|
16
|
-
# config.scenario = :server_app
|
17
16
|
# config.api_key = 'ABCDEFGHIJ1234567890'
|
18
17
|
# end
|
19
18
|
#
|
@@ -30,7 +29,6 @@ module Yt
|
|
30
29
|
#
|
31
30
|
# @example
|
32
31
|
# Yt.configure do |config|
|
33
|
-
# config.scenario = :server_app
|
34
32
|
# config.api_key = 'ABCDEFGHIJ1234567890'
|
35
33
|
# end
|
36
34
|
# @see Yt::Configuration
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'yt/errors/request_error'
|
2
|
+
require 'yt/config'
|
3
|
+
|
4
|
+
module Yt
|
5
|
+
module Errors
|
6
|
+
class MissingAuth < RequestError
|
7
|
+
private
|
8
|
+
|
9
|
+
def explanation
|
10
|
+
'A request to YouTube API was sent without a valid authentication'
|
11
|
+
end
|
12
|
+
|
13
|
+
def more_details
|
14
|
+
if [Yt.configuration.client_id, Yt.configuration.api_key].none?
|
15
|
+
<<-MSG.gsub(/^ {10}/, '')
|
16
|
+
In order to perform this request, you need to register your app with
|
17
|
+
Google Developers Console (https://console.developers.google.com).
|
18
|
+
|
19
|
+
Make sure your app has access to the Google+ and YouTube APIs.
|
20
|
+
|
21
|
+
If your app requires read-only access to public YouTube data, then
|
22
|
+
generate a server API key and set its value with the initializer:
|
23
|
+
|
24
|
+
Yt.configure do |config|
|
25
|
+
config.api_key = '123456789012345678901234567890'
|
26
|
+
end
|
27
|
+
|
28
|
+
or through an environment variable:
|
29
|
+
|
30
|
+
export YT_API_KEY="123456789012345678901234567890"
|
31
|
+
|
32
|
+
If your app needs to perform actions on behalf of YouTube accounts,
|
33
|
+
then generate a client ID and SECRET and set their values with the
|
34
|
+
initializer:
|
35
|
+
|
36
|
+
Yt.configure do |config|
|
37
|
+
config.client_id = '1234567890.apps.googleusercontent.com'
|
38
|
+
config.client_secret = '1234567890'
|
39
|
+
end
|
40
|
+
|
41
|
+
or through environment variables:
|
42
|
+
|
43
|
+
export YT_CLIENT_ID="1234567890.apps.googleusercontent.com"
|
44
|
+
export YT_CLIENT_SECRET="1234567890"
|
45
|
+
MSG
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/yt/errors/no_items.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
require 'yt/errors/
|
1
|
+
require 'yt/errors/request_error'
|
2
2
|
|
3
3
|
module Yt
|
4
4
|
module Errors
|
5
|
-
class NoItems <
|
6
|
-
|
7
|
-
<<-MSG.gsub(/^ {6}/, '')
|
8
|
-
A request to YouTube API V3 returned no items (but some were expected):
|
9
|
-
#{response_body}
|
5
|
+
class NoItems < RequestError
|
6
|
+
private
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
MSG
|
8
|
+
def explanation
|
9
|
+
'A request to YouTube API returned no items but some were expected'
|
14
10
|
end
|
15
11
|
end
|
16
12
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Yt
|
2
|
+
module Errors
|
3
|
+
class RequestError < StandardError
|
4
|
+
def initialize(msg = nil)
|
5
|
+
@msg = msg
|
6
|
+
super msg
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
<<-MSG.gsub(/^ {8}/, '')
|
11
|
+
#{explanation}:
|
12
|
+
#{response_body}
|
13
|
+
|
14
|
+
You can retry the same request manually by running:
|
15
|
+
#{request_curl}
|
16
|
+
#{more_details}
|
17
|
+
MSG
|
18
|
+
end
|
19
|
+
|
20
|
+
def kind
|
21
|
+
response_body.fetch 'error', {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def reasons
|
25
|
+
kind.fetch('errors', []).map{|e| e['reason']}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def explanation
|
31
|
+
'A request to YouTube API failed'
|
32
|
+
end
|
33
|
+
|
34
|
+
def more_details
|
35
|
+
end
|
36
|
+
|
37
|
+
def response_body
|
38
|
+
json['response_body']
|
39
|
+
end
|
40
|
+
|
41
|
+
def request_curl
|
42
|
+
json['request_curl']
|
43
|
+
end
|
44
|
+
|
45
|
+
def json
|
46
|
+
@json ||= JSON(@msg) rescue {}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Error = Errors::RequestError
|
52
|
+
end
|
data/lib/yt/models/account.rb
CHANGED
@@ -1,52 +1,25 @@
|
|
1
1
|
require 'yt/models/base'
|
2
|
-
require 'yt/config'
|
3
2
|
|
4
3
|
module Yt
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
:name, :given_name, :family_name, :profile_url, :avatar_url, :locale, :hd]
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@access_token ||= refresh_access_token || get_access_token
|
20
|
-
end
|
21
|
-
|
22
|
-
def auth
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
# Obtain a new access token using the refresh token
|
29
|
-
def refresh_access_token
|
30
|
-
if @refresh_token
|
31
|
-
body = {grant_type: 'refresh_token', refresh_token: @refresh_token}
|
32
|
-
request = Request.new auth_params.deep_merge(body: body)
|
33
|
-
response = request.run
|
34
|
-
response.body['access_token']
|
4
|
+
module Models
|
5
|
+
# Provides methods to access a YouTube account.
|
6
|
+
class Account < Base
|
7
|
+
has_one :authentication, delegate: [:access_token, :refresh_token, :expires_at]
|
8
|
+
has_one :channel, delegate: [:videos, :playlists, :create_playlist, :delete_playlists, :update_playlists]
|
9
|
+
has_one :user_info, delegate: [:id, :email, :has_verified_email?, :gender, :name, :given_name, :family_name, :profile_url, :avatar_url, :locale, :hd]
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@access_token = options[:access_token]
|
13
|
+
@refresh_token = options[:refresh_token]
|
14
|
+
@expires_at = options[:expires_at]
|
15
|
+
@authorization_code = options[:authorization_code]
|
16
|
+
@redirect_uri = options[:redirect_uri]
|
17
|
+
@scopes = options[:scopes]
|
35
18
|
end
|
36
|
-
end
|
37
19
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
path: '/o/oauth2/token',
|
42
|
-
format: :json,
|
43
|
-
body_type: :form,
|
44
|
-
method: :post,
|
45
|
-
body: {
|
46
|
-
client_id: Yt.configuration.client_id,
|
47
|
-
client_secret: Yt.configuration.client_secret
|
48
|
-
}
|
49
|
-
}
|
20
|
+
def auth
|
21
|
+
self
|
22
|
+
end
|
50
23
|
end
|
51
24
|
end
|
52
25
|
end
|