googol 0.1.1 → 0.2.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/README.md +24 -7
- data/lib/googol/authenticable.rb +2 -4
- data/lib/googol/google_account.rb +2 -5
- data/lib/googol/readable.rb +11 -3
- data/lib/googol/requestable.rb +12 -4
- data/lib/googol/version.rb +1 -1
- data/lib/googol/youtube_account.rb +147 -26
- data/lib/googol/youtube_resource.rb +9 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4cced40105e7959c839fa4349625b98fddb0857b
|
4
|
+
data.tar.gz: ba65e22653877a3cf0aadc9d4ef81838ea09879e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69a0f394c1059aec6a06159f5e042aa7ec1f539a9646c0bbd06ae9b3e2362ffc9f115c10ad1887dcea477b993e069a7c21e007ee29605c0ca760d35f3962652f
|
7
|
+
data.tar.gz: 2be89f5a97ce5285ed09bf0fcb900879c437f656a8232c495b9ddfc9b073b80adb03630d375fb2f6d4509d1d5b9580cd647f325b1de3df9e210c3fd87ab4a8fd
|
data/README.md
CHANGED
@@ -31,8 +31,27 @@ account.email #=> 'user@google.com'
|
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
account = Googol::YoutubeAccount.new auth_params
|
34
|
-
account.
|
35
|
-
account.
|
34
|
+
account.like! video_id: 'Kd5M17e7Wek' # => adds 'Tongue' to your 'Liked videos'
|
35
|
+
account.subscribe_to! channel_id: 'UC7eaRqtonpyiYw0Pns0Au_g' # => subscribes to R.E.M.’s channel
|
36
|
+
playlist_id = account.find_or_create_playlist_by title: 'Favorite Music Videos'
|
37
|
+
account.add_to! playlist_id: playlist_id, video_id: 'Kd5M17e7Wek' # => adds 'Tongue' to your 'Favorite Music Videos' playlist
|
38
|
+
|
39
|
+
# Adds a video to a playlist as a Youtube account
|
40
|
+
#
|
41
|
+
# @param [Hash] target The target of the 'add_to' activity
|
42
|
+
# @option target [String] :video_id The ID of the video to add
|
43
|
+
# @option target [String] :playlist_id The ID of the playlist to add to
|
44
|
+
#
|
45
|
+
# @see https://developers.google.com/youtube/v3/docs/playlistItems/insert
|
46
|
+
#
|
47
|
+
def add_to!(target = {})
|
48
|
+
video_id, playlist_id = fetch! target, :video_id, :playlist_id
|
49
|
+
resource = {videoId: video_id, kind: 'youtube#video'}
|
50
|
+
youtube_request! path: '/playlistItems?part=snippet', json: true,
|
51
|
+
method: :post, snippet: {playlistId: playlist_id, resourceId: resource}
|
52
|
+
end
|
53
|
+
|
54
|
+
|
36
55
|
```
|
37
56
|
|
38
57
|
The full documentation is available at [rubydoc.info](http://rubydoc.info/github/fullscreeninc/googol/master/frames).
|
@@ -60,10 +79,8 @@ Youtube accounts
|
|
60
79
|
Use `Googol::YoutubeAccount` to send and retrieve data to Youtube,
|
61
80
|
impersonating an existing Youtube account.
|
62
81
|
|
63
|
-
Available instance methods are `id`, `title`, `description`,
|
64
|
-
|
65
|
-
Additionally, the `perform!` method lets you executes promotional actions as
|
66
|
-
a Youtube account, such as liking a video or subscribing to a channel.
|
82
|
+
Available instance methods are `id`, `title`, `description`, `thumbnail_url`,
|
83
|
+
`like!`, `subscribe_to!`, `find_or_create_playlist_by`, and `add_to!`.
|
67
84
|
|
68
85
|
These methods require user authentication (see below).
|
69
86
|
|
@@ -139,7 +156,7 @@ To install on your system, run
|
|
139
156
|
|
140
157
|
To use inside a bundled Ruby project, add this line to the Gemfile:
|
141
158
|
|
142
|
-
gem 'googol', '~> 0.
|
159
|
+
gem 'googol', '~> 0.2.0'
|
143
160
|
|
144
161
|
Since the gem follows [Semantic Versioning](http://semver.org),
|
145
162
|
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
|
data/lib/googol/authenticable.rb
CHANGED
@@ -33,10 +33,8 @@ module Googol
|
|
33
33
|
# * ...
|
34
34
|
def credentials
|
35
35
|
@credentials ||= request! method: :post,
|
36
|
-
host: 'https://accounts.google.com',
|
37
|
-
|
38
|
-
body: credentials_params,
|
39
|
-
valid_if: -> response, body {response.code == '200'}
|
36
|
+
host: 'https://accounts.google.com', path: '/o/oauth2/token',
|
37
|
+
body: credentials_params
|
40
38
|
end
|
41
39
|
|
42
40
|
private
|
@@ -35,11 +35,8 @@ module Googol
|
|
35
35
|
# * :locale [String] The account’s preferred locale.
|
36
36
|
# * :hd [String] The hosted domain name for the accounts’s Google Apps.
|
37
37
|
def info
|
38
|
-
@info ||= request!
|
39
|
-
|
40
|
-
host: 'https://www.googleapis.com',
|
41
|
-
path: '/oauth2/v2/userinfo',
|
42
|
-
valid_if: -> response, body {response.code == '200'}
|
38
|
+
@info ||= request! auth: credentials[:access_token],
|
39
|
+
host: 'https://www.googleapis.com', path: '/oauth2/v2/userinfo'
|
43
40
|
end
|
44
41
|
|
45
42
|
# Define a method to return each attribute of the profile separately.
|
data/lib/googol/readable.rb
CHANGED
@@ -17,9 +17,17 @@ module Googol
|
|
17
17
|
info[:snippet][:description]
|
18
18
|
end
|
19
19
|
|
20
|
-
# Return the URL of the Youtube
|
21
|
-
|
22
|
-
|
20
|
+
# Return the URL of the thumbnail image of the Youtube channel/videp.
|
21
|
+
#
|
22
|
+
# @option size [Symbol] :default The size of the thumbnail. Valid values are:
|
23
|
+
# :default (channel: 88px x 88px, video: 120px x 90px)
|
24
|
+
# :medium (channel: 240px x 240px, video: 320px x 180px)
|
25
|
+
# :high (channel: 800px x 800px, video: 480px x 360px)
|
26
|
+
#
|
27
|
+
# @return [String] The thumbnail URL
|
28
|
+
def thumbnail_url(size = :default)
|
29
|
+
size = :default unless [:medium, :high].include? size
|
30
|
+
info[:snippet][:thumbnails][size][:url]
|
23
31
|
end
|
24
32
|
|
25
33
|
# Return the kind of the Youtube object (either 'channel' or 'video')
|
data/lib/googol/requestable.rb
CHANGED
@@ -19,13 +19,13 @@ module Googol
|
|
19
19
|
http = Net::HTTP.new url.host, url.port
|
20
20
|
http.use_ssl = true
|
21
21
|
request = case params[:method]
|
22
|
-
when :get then Net::HTTP::Get.new params[:path]
|
23
22
|
when :post then
|
24
23
|
if params[:json]
|
25
24
|
Net::HTTP::Post.new params[:path], initheader = {'Content-Type' =>'application/json'}
|
26
25
|
else
|
27
26
|
Net::HTTP::Post.new params[:path]
|
28
27
|
end
|
28
|
+
else Net::HTTP::Get.new params[:path]
|
29
29
|
end
|
30
30
|
if params[:json]
|
31
31
|
request.body = params[:body].to_json
|
@@ -34,12 +34,12 @@ module Googol
|
|
34
34
|
end if params[:body]
|
35
35
|
|
36
36
|
request['Authorization'] = 'Bearer ' + params[:auth] if params[:auth]
|
37
|
+
|
37
38
|
response = http.request(request)
|
38
39
|
|
39
40
|
body = JSON.parse response.body if response.body
|
40
41
|
|
41
|
-
if
|
42
|
-
body = params[:extract].call body if params[:extract]
|
42
|
+
if response.code == params.fetch(:code, 200).to_s
|
43
43
|
body ? deep_symbolize_keys(body) : true
|
44
44
|
else
|
45
45
|
raise RequestError, body
|
@@ -49,10 +49,18 @@ module Googol
|
|
49
49
|
private
|
50
50
|
|
51
51
|
def deep_symbolize_keys(hash)
|
52
|
+
def symbolize(value)
|
53
|
+
case value
|
54
|
+
when Hash then deep_symbolize_keys value
|
55
|
+
when Array then value.map{|item| symbolize item}
|
56
|
+
else value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
52
60
|
{}.tap do |result|
|
53
61
|
hash.each do |k, v|
|
54
62
|
key = k.to_sym rescue k
|
55
|
-
result[key] =
|
63
|
+
result[key] = symbolize v
|
56
64
|
end
|
57
65
|
end
|
58
66
|
end
|
data/lib/googol/version.rb
CHANGED
@@ -14,7 +14,7 @@ module Googol
|
|
14
14
|
# * Use the authorization code to initialize the YoutubeAccount and like the video:
|
15
15
|
#
|
16
16
|
# account = Googol::YoutubeAccount.new code: code, redirect_url: redirect_url
|
17
|
-
# account.
|
17
|
+
# account.like! video_id: 'Kd5M17e7Wek' # => likes the video
|
18
18
|
#
|
19
19
|
class YoutubeAccount
|
20
20
|
include Authenticable
|
@@ -36,35 +36,156 @@ module Googol
|
|
36
36
|
# + :medium [Hash] Medium thumbnail URL (88px x 88px)
|
37
37
|
# + :high [Hash] High thumbnail URL (88px x 88px)
|
38
38
|
def info
|
39
|
-
@
|
40
|
-
|
41
|
-
host: 'https://www.googleapis.com',
|
42
|
-
path: '/youtube/v3/channels?part=id,snippet&mine=true',
|
43
|
-
valid_if: -> resp, body {resp.code == '200' && body['items'].any?},
|
44
|
-
extract: -> body {body['items'].first}
|
39
|
+
@info_response ||= youtube_request! path: '/channels?part=id,snippet&mine=true'
|
40
|
+
@info_response[:items].first
|
45
41
|
end
|
46
42
|
|
47
|
-
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
43
|
+
# Like a video as a Youtube account
|
44
|
+
#
|
45
|
+
# @param [Hash] target The target of the 'like' activity
|
46
|
+
# @option target [String] :video_id The ID of the video to like
|
47
|
+
#
|
48
|
+
# @see https://developers.google.com/youtube/v3/docs/videos/rate
|
49
|
+
#
|
50
|
+
# @note Liking a video does not also subscribe to its channel
|
51
|
+
#
|
52
|
+
def like!(target = {})
|
53
|
+
video_id = fetch! target, :video_id
|
54
|
+
path = "/videos/rate?rating=like&id=#{video_id}"
|
55
|
+
youtube_request! path: path, method: :post, code: 204
|
56
|
+
end
|
57
|
+
|
58
|
+
# Subscribe a Youtube account to a channel
|
59
|
+
#
|
60
|
+
# @param [Hash] target The target of the 'subscribe' activity
|
61
|
+
# @option target [String] :channel_id The ID of the channel to subscribe to
|
62
|
+
#
|
63
|
+
# @see https://developers.google.com/youtube/v3/docs/subscriptions/insert
|
64
|
+
#
|
65
|
+
def subscribe_to!(target = {})
|
66
|
+
channel_id = fetch! target, :channel_id
|
67
|
+
youtube_request! path: '/subscriptions?part=snippet', json: true,
|
68
|
+
method: :post, body: {snippet: {resourceId: {channelId: channel_id}}}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the first playlist of the Youtube account matching the attributes.
|
72
|
+
# If such a playlist is not found, it gets created and yielded to the block.
|
73
|
+
#
|
74
|
+
# @note Inspired by Ruby on Rails’ find_or_create_by
|
75
|
+
#
|
76
|
+
# @param [Hash] target The target of the 'add_to' activity
|
77
|
+
# @option target [String] :video_id The ID of the video to add
|
78
|
+
# @option target [String] :playlist_id The ID of the playlist to add to
|
79
|
+
#
|
80
|
+
# @see https://developers.google.com/youtube/v3/docs/playlistItems/insert
|
81
|
+
#
|
82
|
+
def find_or_create_playlist_by(attributes = {}, &block)
|
83
|
+
find_playlist_by(attributes) || create_playlist!(attributes, &block)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adds a video to a playlist as a Youtube account
|
87
|
+
#
|
88
|
+
# @param [Hash] target The target of the 'add_to' activity
|
89
|
+
# @option target [String] :video_id The ID of the video to add
|
90
|
+
# @option target [String] :playlist_id The ID of the playlist to add to
|
91
|
+
#
|
92
|
+
# @see https://developers.google.com/youtube/v3/docs/playlistItems/insert
|
93
|
+
#
|
94
|
+
def add_to!(target = {})
|
95
|
+
video_id, playlist_id = fetch! target, :video_id, :playlist_id
|
96
|
+
resource = {videoId: video_id, kind: 'youtube#video'}
|
97
|
+
youtube_request! method: :post, json: true,
|
98
|
+
path: '/playlistItems?part=snippet',
|
99
|
+
body: {snippet: {playlistId: playlist_id, resourceId: resource}}
|
100
|
+
end
|
54
101
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
102
|
+
private
|
103
|
+
|
104
|
+
# Creates a playlist for a Youtube account and yield it to the block
|
105
|
+
#
|
106
|
+
# @param [Hash] attributes The attributes of the new playlist
|
107
|
+
# @option attributes [String] :title Title of the playlist
|
108
|
+
# @option attributes [String] :description Description of the playlist
|
109
|
+
#
|
110
|
+
# @return [String] The ID of the playlist
|
111
|
+
#
|
112
|
+
# @see https://developers.google.com/youtube/v3/docs/playlists/insert
|
113
|
+
#
|
114
|
+
def create_playlist!(attrs = {}, &block)
|
115
|
+
snippet = {title: attrs[:title], description: attrs[:description]}
|
116
|
+
playlist = youtube_request! json: true, method: :post,
|
117
|
+
path: '/playlists?part=snippet,status',
|
118
|
+
body: {snippet: snippet, status: {privacyStatus: :public}}
|
119
|
+
yield playlist if block_given?
|
120
|
+
playlist[:id]
|
121
|
+
end
|
122
|
+
|
123
|
+
# Return the first playlist of the Youtube account matching the attributes.
|
124
|
+
#
|
125
|
+
# @param [Hash] filters The filter to query the list of playlists with.
|
126
|
+
# @option title [String or Regex] Filter on the title
|
127
|
+
# @option description [String or Regex] Filter on the description
|
128
|
+
#
|
129
|
+
# @example account.find_playlist_by title: 'Title' # => 'e45fXDsdsdsd'
|
130
|
+
# @example account.find_playlist_by description: 'xxxx' # => nil
|
131
|
+
#
|
132
|
+
# @note Google API does not have a "search" endpoint, therefore we have
|
133
|
+
# to scan the list of playlist page by page, limiting at 10 pages to
|
134
|
+
# prevent this function from running forever (50 playlists per page).
|
135
|
+
#
|
136
|
+
# @return [String or nil] The ID of the playlist (or nil if not found)
|
137
|
+
def find_playlist_by(filters = {})
|
138
|
+
page = filters.delete(:page) || 1
|
139
|
+
|
140
|
+
path = "/playlists?part=id,snippet&mine=true"
|
141
|
+
path << "&maxResults=#{filters.delete(:max) || 50 }"
|
142
|
+
path << "&pageToken=#{filters.delete :token}" if filters[:token]
|
143
|
+
|
144
|
+
response = youtube_request! path: path
|
145
|
+
|
146
|
+
if playlist = response[:items].find{|p| playlist_matches? p, filters}
|
147
|
+
playlist[:id]
|
148
|
+
elsif page < 10 && token = response[:nextPageToken]
|
149
|
+
find_playlist_by filters.merge page: page, token: token
|
67
150
|
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Checks if the playlist matches the given filters.
|
154
|
+
#
|
155
|
+
# @param [Hash] filters The filter to query the list of playlists with.
|
156
|
+
# @option title [String or Regex] Filter on the title
|
157
|
+
# @option description [String or Regex] Filter on the description
|
158
|
+
#
|
159
|
+
# @note String filters match when they are included in the specified field
|
160
|
+
# Regex filters match when they match as a Regex against the field
|
161
|
+
#
|
162
|
+
# @example Given a playlist x with title: 'A title', description: 'Example'
|
163
|
+
# playlist_matches? x, title: 'tit' # => true
|
164
|
+
# playlist_matches? x, title: 'TIT' # => false
|
165
|
+
# playlist_matches? x, title: /^TIT/i # => true
|
166
|
+
# playlist_matches? x, title: /^TIT/i, description: 'Ex' # => true
|
167
|
+
# playlist_matches? x, title: /^TIT/i, description: 'xxx' # => false
|
168
|
+
#
|
169
|
+
# @return [Boolean] Whether the playlist matches the filters
|
170
|
+
def playlist_matches?(playlist, filters = {})
|
171
|
+
filters.all? do |attribute, string_or_regex|
|
172
|
+
playlist[:snippet].fetch(attribute, '')[string_or_regex]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Fetch keys from a hash, raising a custom exception when not found
|
177
|
+
def fetch!(hash, *keys)
|
178
|
+
values = keys.map do |key|
|
179
|
+
hash.fetch(key) {raise RequestError, "Missing #{key} in #{hash}"}
|
180
|
+
end
|
181
|
+
keys.one? ? values.first : values
|
182
|
+
end
|
183
|
+
|
184
|
+
# Overrides the super.request! with Youtube-specific params
|
185
|
+
def youtube_request!(params = {})
|
186
|
+
params[:path] = "/youtube/v3#{params[:path]}"
|
187
|
+
params[:auth] = credentials[:access_token]
|
188
|
+
params[:host] = 'https://www.googleapis.com'
|
68
189
|
request! params
|
69
190
|
end
|
70
191
|
|
@@ -42,15 +42,16 @@ module Googol
|
|
42
42
|
# - :description [String] The channel's description.
|
43
43
|
# - :publishedAt [String] The date and time that the channel was created. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
|
44
44
|
# - :thumbnails [Hash]
|
45
|
-
# + :default [Hash] Default thumbnail URL (88px x 88px)
|
46
|
-
# + :medium [Hash] Medium thumbnail URL (
|
47
|
-
# + :high [Hash] High thumbnail URL (
|
45
|
+
# + :default [Hash] Default thumbnail URL (channel: 88px x 88px, video: 120px x 90px)
|
46
|
+
# + :medium [Hash] Medium thumbnail URL (channel: 240px x 240px, video: 320px x 180px)
|
47
|
+
# + :high [Hash] High thumbnail URL (channel: 800px x 800px, video: 480px x 360px)
|
48
48
|
def info
|
49
|
-
@
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
@info_response ||= request! host: 'https://www.googleapis.com', path: "/youtube/v3/#{info_path}"
|
50
|
+
if @info_response[:items].any?
|
51
|
+
@info_response[:items].first
|
52
|
+
else
|
53
|
+
raise RequestError, "Youtube resource not found at #{@url}"
|
54
|
+
end
|
54
55
|
end
|
55
56
|
|
56
57
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: googol
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claudio Baccigalupo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|