yt 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29f2a9daff36933af1c9fafa394009a4660697cd
4
- data.tar.gz: b9cb80c044040fc195eac01bad264d1fb65fea69
3
+ metadata.gz: 5c6e034f7f20ce13ac90b4156e7e6845ef96730a
4
+ data.tar.gz: 95f4e7115e31707a6fbf64ea7d671f6c9dd34b19
5
5
  SHA512:
6
- metadata.gz: 02a7be4f86dd4fe2801edf57ffa79f05a2b16321d3d3e5a7043893b3eb40d81bf4173049de53443bd816fe5cdf177b64f5e9150140a1b25d4f30bb5ee3523f24
7
- data.tar.gz: d9a7005315e12337e16d89e19f8112d3b484594d984fdd7315f6fb5a3c34193f21ed1758fd9bf65487a2c849422b0763abc4b5b50406f3e733344b59c6561c5c
6
+ metadata.gz: 363c34358726c126c90b7fab58747d3df03a1fc0f1711e524d28cc35c954859865fbbde3a0e9207a6f6d49f2cc1235cb12ec5dc62632cc29699df0b4908fd271
7
+ data.tar.gz: 5d2436157647425de284a1fbb918f12c9e7f6f510b9c0dab9a8e97b2193468994cb4a53e2f8ffcdd3052c2d520e75cb3bed15e0aa57bd2c139383090158c96be
data/.travis.yml CHANGED
@@ -15,4 +15,9 @@ env:
15
15
  global:
16
16
  - secure: ApPj5c9h6xk+AbHHf4KXL10QnleYCWwcp2+qMFolrS6RWR5vhrnKq1UBo0h59HuvXeOQIgZ/GmocuSEZovS9c1hQP3n0PHSNEnGUNJyn6CS3BiPQSWmC3p2pONo1Xv+hVWfzDoEv80b82GX5P5x/l1BSqhpxLA9geITJprWsoLg=
17
17
  - secure: CkLyqIq/yTV5h/41b0wxIhMN0LvTk51IBht1+l678YSK7zT9emo9DcwnZVQ9f9KluDD8SyUGRORD40eQs8XmqpDCx7gj7i5e3TEVBofM9YnyFMR1lsWRiMr8jf2eksuP10kEkV7S7E1KH4LI2K1ZcOVqfN0G3Vs0C5D8GwpEcQ8=
18
- - secure: XzuEz0AV6eDeT3+VaodXRMOh+fDvtG1X/VDfnh3f3gmVsnW1d8gdZY9Gw9rkSixK0SwL58BcSu+SU9BlDAsa0asv2XF8h51Q7s3x0ld9I60ssV/4DXSXqicHYcq13Vd86JMmURUuLiJM3y5lXb0bMGXNOkgYZ19gd5X9No+Tkww=
18
+ - secure: XzuEz0AV6eDeT3+VaodXRMOh+fDvtG1X/VDfnh3f3gmVsnW1d8gdZY9Gw9rkSixK0SwL58BcSu+SU9BlDAsa0asv2XF8h51Q7s3x0ld9I60ssV/4DXSXqicHYcq13Vd86JMmURUuLiJM3y5lXb0bMGXNOkgYZ19gd5X9No+Tkww=
19
+ - secure: GvFLpteuIhPHtNT41VBR49sJf5yAWpwwGA24Ov92e5FaVzZD8eiBQZYwjx3Whyo5nkSN5nDzYeKvEdkHaE+sIhSjhoeSY6Q85YK9o/4hSSxjo71OepZXfawKH0xtpkbI+N1fMuUCZR0ustSCLx1snLLZtFRQR2YWU6qg+Yj48Ds=
20
+ - secure: d7yJZSz7Xk0LgJJSTbiK+yb12fdu9101wGXR56sSaRb5BoulBO12uKQgMYMa1kAh6imrNL4WzGOQQGC3SXDAEL6riHcuQJQOmM5zRJtZOrgitVe4cUm9Lv7jo4OyoJNd65EqpOaFPlI0Z3hp0xy3tOuTXemjie44ReKsCUp7z2Y=
21
+ - secure: Hpv62bpHo2zNHwB6VYNKxXLv12GHC0fiGUZ/uDv/4ncukifR9zEeucp104o3f4jqRpauy533kNw1JmucXI6Y+IEyHoEcqA99/i/HirS5WlB7vRH1ATX20Jj3kQ2JaFBIzidX9gNXvSNwpI2XK267eqfZUDFU8zp6aUPnJ6tgpq0=
22
+ - secure: Ejj8tsuwyrRVmCc/R9ubKWCHWhCGpe0Dy6fc1UuPCkcMZyXq9ZC02v2obWsTQQ7epEgsCYZAO4v/gWpuv1b1huGcWdfJzMW7RCoY87cEf9HnAK0lSwGx4+/pYkEMe8y5p149C3vAR8nqczvEavN1fUq/WwPUqp+JyDP7kwFTs2Y=
23
+ - secure: gE5kAT1R54hmS+W3YYGcUtlD8ZskvTctVR3sr+C5CUjVPdq6Ktx5Q/a6EJyAVVrhxpaCOuk3LG+VkzdQIVFUNRiDPcOulkond4HkSQDoy+IJ/wTXvUS+lIJ1ERUnWega+APrQUjH5s2WayPGZUBqWt/u8Tt9EmSUZfuKZSEXqZk=
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yt (0.5.6)
4
+ yt (0.5.7)
5
5
  activesupport
6
6
 
7
7
  GEM
data/HISTORY.md CHANGED
@@ -11,6 +11,7 @@ v0.5 - 2014/05/16
11
11
  * New Authentication model to separate `access_token` and `refresh_token` from Account
12
12
  * New types of Errors that render more verbose errors and the failing request in cURL syntax
13
13
  * Separate Error class for 500 error, so they can be easily found in app logs
14
+ * New Earning collection to retrieve estimated earning for YouTube-partnered channels
14
15
 
15
16
  v0.4 - 2014/05/09
16
17
  --------------------
data/README.md CHANGED
@@ -41,9 +41,9 @@ Use [Yt::Account](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Account) to
41
41
  * access the YouTube channel of the account
42
42
 
43
43
  ```ruby
44
- account = Yt::Account.new
44
+ # An account can be initialized with access token, refresh token or an authorization code
45
+ account = Yt::Account.new access_token: 'ya29.1.ABCDEFGHIJ'
45
46
 
46
- # An OAuth2 prompt will appear before the following commands
47
47
  account.email #=> .. your e-mail address..
48
48
  account.channel #=> #<Yt::Channel @id=...>
49
49
  ```
@@ -60,6 +60,7 @@ Use [Yt::Channel](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Channel) to
60
60
  * access the playlists of a channel
61
61
  * subscribe to and unsubscribe from a channel
62
62
  * create and delete playlists from a channel
63
+ * retrieve the estimated daily earnings of a channel
63
64
 
64
65
  ```ruby
65
66
  channel = Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow'
@@ -71,8 +72,14 @@ channel.videos.first #=> #<Yt::Video @id=...>
71
72
 
72
73
  channel.playlists.count #=> 2
73
74
  channel.playlists.first #=> #<Yt::Playlist @id=...>
75
+ ```
76
+
77
+ *The methods above do not require authentication.*
78
+
79
+ ```ruby
80
+ account = Yt::Account.new access_token: 'ya29.1.ABCDEFGHIJ'
81
+ channel = Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow', auth: account
74
82
 
75
- # An OAuth2 prompt will appear before the following commands
76
83
  channel.subscribed? #=> false
77
84
  channel.subscribe #=> true
78
85
 
@@ -81,7 +88,18 @@ channel.delete_playlists title: 'New playlist' #=> [true]
81
88
 
82
89
  ```
83
90
 
84
- *Subscribing to and unsubscribing from a channel requires authentication (see below).*
91
+ *The methods above require to be authenticated as a YouTube account (see below).*
92
+
93
+ ```ruby
94
+ content_owner = Yt::Account.new owner_name: 'CMSname', access_token: 'ya29.1.ABCDEFGHIJ'
95
+ channel = Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow', auth: content_owner
96
+
97
+ channel.earning 5.days.ago #=> 12.23
98
+ channel.earnings since: 3.days.ago, until: 2.days.ago #=> {Wed, 28 May 2014 => 1.34, Thu, 29 May 2014 => 0.47}
99
+ ```
100
+
101
+ *The methods above require to be authenticated as the channel’s content owner (see below).*
102
+
85
103
 
86
104
  Yt::Video
87
105
  -----------
@@ -100,13 +118,19 @@ video.description.has_link_to_subscribe? #=> false
100
118
 
101
119
  video.annotations.count #=> 1
102
120
  video.annotations.first #=> #<Yt::Annotation @id=...>
121
+ ```
122
+
123
+ *The methods above do not require authentication.*
124
+
125
+ ```ruby
126
+ account = Yt::Account.new access_token: 'ya29.1.ABCDEFGHIJ'
127
+ video = Yt::Video.new id: 'MESycYJytkU', auth: account
103
128
 
104
- # An OAuth2 prompt will appear before the following commands
105
129
  video.liked? #=> false
106
130
  video.like #=> true
107
131
  ```
108
132
 
109
- *Liking and disliking a video requires authentication (see below).*
133
+ *The methods above require to be authenticated as a YouTube account (see below).*
110
134
 
111
135
  Yt::Playlist
112
136
  ------------
@@ -127,14 +151,17 @@ playlist.playlist_items.count #=> 1
127
151
  playlist.playlist_items.first #=> #<Yt::PlaylistItem @id=...>
128
152
  playlist.playlist_items.first.position #=> 0
129
153
  playlist.playlist_items.first.video.title #=> "Fullscreen Creator Platform"
154
+ ```
155
+
156
+ *The methods above do not require authentication.*
130
157
 
131
- # An OAuth2 prompt will appear before the following commands
158
+ ```ruby
132
159
  playlist.add_video 'MESycYJytkU'
133
160
  playlist.add_videos ['MESycYJytkU', 'MESycYJytkU']
134
161
  playlist.delete_playlist_items title: 'Fullscreen Creator Platform' #=> [true]
135
162
  ```
136
163
 
137
- *Adding and removing videos/items requires authentication (see below).*
164
+ *The methods above require to be authenticated as the playlist’s owner (see below).*
138
165
 
139
166
 
140
167
  Yt::Annotation
@@ -219,8 +246,28 @@ account.email #=> (retrieves the account’s e-mail address)
219
246
  account.playlists.first.add_video 'MESycYJytkU' #=> (adds a video to an account’s playlist)
220
247
  ```
221
248
 
222
- Scenario 3. If you don’t have the account’s refresh token, then [..TODO..]
249
+ Scenario 3. If you don’t have any account’s token, then you can get one by
250
+ having the user authorize your app through the Google OAuth page. First,
251
+ build the the Google OAuth page URL with the following code:
223
252
 
253
+ ```ruby
254
+ Yt::Account.new(scopes: scopes, redirect_uri: redirect_uri).authentication_url
255
+ ```
256
+
257
+ where `scopes` is the list of scopes you want the user to authorize, and
258
+ `redirect_uri` is the page of your web app the user should come back after
259
+ authorizing (remember, this must be added to the Google Console as well).
260
+ Sample scopes are: `youtube`, `youtube.readonly` `userinfo.email`.
261
+
262
+ After authorizing your app, the user will be redirected to `redirect_uri`
263
+ with an extra `code` parameter that looks something like `4/Ja60jJ7_Kw0`.
264
+ Just pass the code to the following method to authenticate and initialize the account:
265
+
266
+ ```ruby
267
+ account = Yt::Account.new authorization_code: '4/Ja60jJ7_Kw0'
268
+ account.email #=> (retrieves the account’s e-mail address)
269
+ account.playlists.first.add_video 'MESycYJytkU' #=> (adds a video to an account’s playlist)
270
+ ```
224
271
 
225
272
  Device apps that do require user interactions
226
273
  ---------------------------------------------
@@ -300,7 +347,7 @@ To install on your system, run
300
347
 
301
348
  To use inside a bundled Ruby project, add this line to the Gemfile:
302
349
 
303
- gem 'yt', '~> 0.5.6'
350
+ gem 'yt', '~> 0.5.7'
304
351
 
305
352
  Since the gem follows [Semantic Versioning](http://semver.org),
306
353
  indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
@@ -4,7 +4,7 @@ require 'yt/errors/no_items'
4
4
  module Yt
5
5
  module Actions
6
6
  module List
7
- delegate :count, :first, :any?, :each, :map, :find, to: :list
7
+ delegate :count, :first, :any?, :each, :map, :flat_map, :find, to: :list
8
8
  alias size count
9
9
 
10
10
  def first!
@@ -51,7 +51,7 @@ module Yt
51
51
  request = Yt::Request.new params
52
52
  response = request.run
53
53
  token = response.body['nextPageToken']
54
- items = response.body.fetch 'items', []
54
+ items = response.body.fetch items_key, []
55
55
  {items: items, token: token}
56
56
  end
57
57
 
@@ -65,6 +65,10 @@ module Yt
65
65
  params[:exptected_response] = Net::HTTPOK
66
66
  end
67
67
  end
68
+
69
+ def items_key
70
+ 'items'
71
+ end
68
72
  end
69
73
  end
70
74
  end
@@ -0,0 +1,52 @@
1
+ require 'yt/collections/earnings'
2
+
3
+ module Yt
4
+ module Associations
5
+ # Provides the `has_many :earnings` method to YouTube resources, which
6
+ # allows to invoke earning-related methods, such as .earnings.
7
+ # YouTube resources with earning are: channels.
8
+ module Earnings
9
+ # Return the estimated earning for one specific day.
10
+ #
11
+ # @param [Date or Time or DateTime or String] date The date to obtain
12
+ # the estimated earnings for. If String, must be Date-parseable.
13
+ #
14
+ # @return [Float] The estimated earnings in USD.
15
+ def earning(date)
16
+ earnings(from: date, to: date).values.first
17
+ end
18
+
19
+ # Return the estimated earning for a range of days.
20
+ #
21
+ # @param [Hash] options the range of days to get the earnings for.
22
+ # @option options [Date or Time or DateTime or String] :since The start
23
+ # of the days range. If String, must be Date-parseable.
24
+ # @note options[:since] is aliased as options[:from]
25
+ # @option options [Date or Time or DateTime or String] :until The end
26
+ # of the days range. If String, must be Date-parseable.
27
+ # @note options[:until] is aliased as options[:to]
28
+ #
29
+ # @return [Hash] The estimated earnings by day. Each :key is a Date
30
+ # and each :value is a Float, representing estimated earnings in USD.
31
+ def earnings(options = {})
32
+ from = options[:since] || options[:from] || 1.week.ago
33
+ to = options[:until] || options[:to] || 1.day.ago
34
+ range = Range.new *[from, to].map(&:to_date)
35
+
36
+ Hash[*range.flat_map do |date|
37
+ [date, (@earnings ||= {})[date] ||= range_earnings(range)[date]]
38
+ end]
39
+ end
40
+
41
+ private
42
+
43
+ def range_earnings(date_range)
44
+ (@range_earnings ||= {})[date_range] ||= all_earnings.within date_range
45
+ end
46
+
47
+ def all_earnings
48
+ Collections::Earnings.of self
49
+ end
50
+ end
51
+ end
52
+ end
@@ -14,6 +14,7 @@ module Yt
14
14
  autoload :Authentications
15
15
  autoload :Channels
16
16
  autoload :DetailsSets
17
+ autoload :Earnings
17
18
  autoload :Ids
18
19
  autoload :PlaylistItems
19
20
  autoload :Playlists
@@ -0,0 +1,38 @@
1
+ require 'yt/collections/base'
2
+
3
+ module Yt
4
+ module Collections
5
+ class Earnings < Base
6
+
7
+ def within(days_range)
8
+ @days_range = days_range
9
+ Hash[*flat_map{|daily_earning| daily_earning}]
10
+ end
11
+
12
+ private
13
+
14
+ def new_item(data)
15
+ # NOTE: could use column headers to be more precise
16
+ [Date.iso8601(data.first), data.last]
17
+ end
18
+
19
+ def list_params
20
+ super.tap do |params|
21
+ params[:path] = '/youtube/analytics/v1/reports'
22
+ params[:params] = {}.tap do |params|
23
+ params['ids'] = "contentOwner==#{@auth.owner_name}"
24
+ params['filters'] = "channel==#{@parent.id}"
25
+ params['start-date'] = @days_range.begin
26
+ params['end-date'] = @days_range.end
27
+ params['metrics'] = :earnings
28
+ params['dimensions'] = :day
29
+ end
30
+ end
31
+ end
32
+
33
+ def items_key
34
+ 'rows'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -4,9 +4,10 @@ module Yt
4
4
  module Models
5
5
  # Provides methods to access a YouTube account.
6
6
  class Account < Base
7
- has_one :authentication, delegate: [:access_token, :refresh_token, :expires_at]
8
7
  has_one :channel, delegate: [:videos, :playlists, :create_playlist, :delete_playlists, :update_playlists]
9
8
  has_one :user_info, delegate: [:id, :email, :has_verified_email?, :gender, :name, :given_name, :family_name, :profile_url, :avatar_url, :locale, :hd]
9
+ has_one :authentication, delegate: [:access_token, :refresh_token, :expires_at]
10
+ attr_reader :owner_name
10
11
 
11
12
  def initialize(options = {})
12
13
  @access_token = options[:access_token]
@@ -15,6 +16,7 @@ module Yt
15
16
  @authorization_code = options[:authorization_code]
16
17
  @redirect_uri = options[:redirect_uri]
17
18
  @scopes = options[:scopes]
19
+ @owner_name = options[:owner_name]
18
20
  end
19
21
 
20
22
  def auth
@@ -6,6 +6,7 @@ module Yt
6
6
  has_many :subscriptions
7
7
  has_many :videos
8
8
  has_many :playlists
9
+ has_many :earnings # requires auth with an account with 'yt-analytics-monetary.readonly'
9
10
  end
10
11
  end
11
12
  end
data/lib/yt/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.5.6'
2
+ VERSION = '0.5.7'
3
3
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Yt::Associations::Earnings, :partner do
4
+ context 'given a Youtube Partner channel (with a content owner)' do
5
+ let(:channel) { $partner_channel }
6
+
7
+ describe '#earning' do
8
+ context 'given a date for which YouTube has estimated earnings' do
9
+ let(:earning) { channel.earning 5.days.ago}
10
+ it { expect(earning).to be_a Float }
11
+ end
12
+
13
+ context 'given a date for which YouTube does not have estimated earnings' do
14
+ let(:earning) { channel.earning 5.days.from_now}
15
+ it { expect(earning).to be_nil }
16
+ end
17
+ end
18
+
19
+ describe '#earning' do
20
+ let(:date) { 4.days.ago }
21
+
22
+ context 'given a :since option' do
23
+ let(:earnings) { channel.earnings since: date}
24
+ it { expect(earnings.keys.min).to eq date.to_date }
25
+ end
26
+
27
+ context 'given a :from option' do
28
+ let(:earnings) { channel.earnings from: date}
29
+ it { expect(earnings.keys.min).to eq date.to_date }
30
+ end
31
+
32
+ context 'given a :until option' do
33
+ let(:earnings) { channel.earnings until: date}
34
+ it { expect(earnings.keys.max).to eq date.to_date }
35
+ end
36
+
37
+ context 'given a :to option' do
38
+ let(:earnings) { channel.earnings to: date}
39
+ it { expect(earnings.keys.max).to eq date.to_date }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -30,4 +30,17 @@ RSpec.configure do |config|
30
30
  config.api_key = ENV['YT_TEST_SERVER_API_KEY']
31
31
  end
32
32
  end
33
- end
33
+
34
+ config.before :all, partner: true do
35
+ Yt.configure do |config|
36
+ config.client_id = ENV['YT_TEST_PARTNER_CLIENT_ID']
37
+ config.client_secret = ENV['YT_TEST_PARTNER_CLIENT_SECRET']
38
+ end
39
+ # Create one Youtube Partner channel, authenticated as the content owner
40
+ attrs = {refresh_token: ENV['YT_TEST_CONTENT_OWNER_REFRESH_TOKEN']}
41
+ attrs[:owner_name] = ENV['YT_TEST_CONTENT_OWNER_NAME']
42
+ content_owner = Yt::Account.new attrs
43
+ attrs = {id: ENV['YT_TEST_PARTNER_CHANNEL_ID'], auth: content_owner}
44
+ $partner_channel = Yt::Channel.new attrs
45
+ end
46
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
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-05-23 00:00:00.000000000 Z
11
+ date: 2014-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -127,6 +127,7 @@ files:
127
127
  - lib/yt/associations/authentications.rb
128
128
  - lib/yt/associations/channels.rb
129
129
  - lib/yt/associations/details_sets.rb
130
+ - lib/yt/associations/earnings.rb
130
131
  - lib/yt/associations/ids.rb
131
132
  - lib/yt/associations/playlist_items.rb
132
133
  - lib/yt/associations/playlists.rb
@@ -141,6 +142,7 @@ files:
141
142
  - lib/yt/collections/base.rb
142
143
  - lib/yt/collections/channels.rb
143
144
  - lib/yt/collections/details_sets.rb
145
+ - lib/yt/collections/earnings.rb
144
146
  - lib/yt/collections/ids.rb
145
147
  - lib/yt/collections/playlist_items.rb
146
148
  - lib/yt/collections/playlists.rb
@@ -178,6 +180,7 @@ files:
178
180
  - spec/associations/device_auth/authentications_spec.rb
179
181
  - spec/associations/device_auth/channels_spec.rb
180
182
  - spec/associations/device_auth/details_sets_spec.rb
183
+ - spec/associations/device_auth/earnings_spec.rb
181
184
  - spec/associations/device_auth/ids_spec.rb
182
185
  - spec/associations/device_auth/playlist_items_spec.rb
183
186
  - spec/associations/device_auth/playlists_spec.rb
@@ -260,6 +263,7 @@ test_files:
260
263
  - spec/associations/device_auth/authentications_spec.rb
261
264
  - spec/associations/device_auth/channels_spec.rb
262
265
  - spec/associations/device_auth/details_sets_spec.rb
266
+ - spec/associations/device_auth/earnings_spec.rb
263
267
  - spec/associations/device_auth/ids_spec.rb
264
268
  - spec/associations/device_auth/playlist_items_spec.rb
265
269
  - spec/associations/device_auth/playlists_spec.rb