yt 0.5.6 → 0.5.7

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 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