yt 0.14.7 → 0.15.0

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: e26f7e87270dc0509123f5ea88302a280e54da66
4
- data.tar.gz: 73f82aca406690352fb266e2129bc4288be40f4a
3
+ metadata.gz: 54209e7c3dedc665ad4d6b0d93bcf6c6d9d6f546
4
+ data.tar.gz: 6e09959e44cbf2aad7bcf29df8dfa25f6cc903c6
5
5
  SHA512:
6
- metadata.gz: 7c113de8d650081766da2ba86cbc85e2e038e02cf2e8f358ab76feabeacae8ca2fc5d29447e68608a0a31d2466177fe44722b4bfe2d25a40cb1004e7edfa2a50
7
- data.tar.gz: 6600285bcf6beb7fb56d38bdb555cba51e745a0237c2a21059f9b3905faeec6487866b1deb3b028751b67a4d31fb2a27684c9f9bd63ea2f5eb12bdbdd7538c3b
6
+ metadata.gz: 3a83c419303aa9980ecc4769c711798742aa0a3a77202ef04e18300ba32d414994f9e2b3fa6dd5042489e5b9caaca75966c4bdcfe1a3e1a995f641d176f5a89f
7
+ data.tar.gz: 5f31bea6b6e1d473a42f73f2971efd7567220d0283c645b488ea6f35870be5e460352346996469179408f11fc9190ce61e17ce2ac435499ae89e732ead1e65b0
data/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ For more information about changelogs, check
6
6
  [Keep a Changelog](http://keepachangelog.com) and
7
7
  [Vandamme](http://tech-angels.github.io/vandamme).
8
8
 
9
+ ## 0.15.0 - 2015-04-19
10
+
11
+ **How to upgrade**
12
+
13
+ If your code never calls the `viewer_percentage(gender: [:female|:male])` method
14
+ on a Channel or Video object, then you are good to go.
15
+
16
+ If it does, then replace your calls to `viewer_percentage(gender: :female)`
17
+ with `viewer_percentage(by: gender)[:female]`, and do the same for `:male`.
18
+
19
+ Note that the _plural_ `viewer_percentages` method still works but it’s
20
+ deprecated: you should use `viewer_percentage` instead.
21
+
22
+ * [ENHANCEMENT] Remove `:gender` option in `viewer_percentage` in favor of a more generic `:by`
23
+ * [FEATURE] New `by: :gender` option for reports, to return viewer percentage by gender
24
+ * [FEATURE] New `by: :age_group` option for reports, to return viewer percentage by age group
25
+ * [ENHANCEMENT] The viewer percentage report now accepts start/end date options (like any other report)
26
+ * [DEPRECATION] Deprecate `viewer_percentages` in favor of `viewer_percentage`.
27
+
9
28
  ## 0.14.7 - 2015-04-17
10
29
 
11
30
  * [FEATURE] New `by: :device_type` option for reports, to return views and estimated watched minutes (channels) by device
data/README.md CHANGED
@@ -41,7 +41,7 @@ To install on your system, run
41
41
 
42
42
  To use inside a bundled Ruby project, add this line to the Gemfile:
43
43
 
44
- gem 'yt', '~> 0.14.7'
44
+ gem 'yt', '~> 0.15.0'
45
45
 
46
46
  Since the gem follows [Semantic Versioning](http://semver.org),
47
47
  indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
@@ -156,7 +156,7 @@ Use [Yt::Channel](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Chan
156
156
  * retrieve the views and estimated minutes watched by video
157
157
  * retrieve the views and estimated minutes watched by playlist
158
158
  * retrieve the views and estimated minutes watched by device type
159
- * retrieve the viewer percentage of a channel by gender and age group
159
+ * retrieve the viewer percentage of a channel by gender, by age group or by both
160
160
 
161
161
  ```ruby
162
162
  # Channels can be initialized with ID or URL
@@ -215,8 +215,7 @@ channel.favorites_removed from: '2014-08-30', to: '2014-08-31' #=> {Sat, 30 Aug
215
215
  channel.estimated_minutes_watched #=> {Sun, 22 Feb 2015=>2433258.0, Mon, 23 Feb 2015=>2634360.0, …}
216
216
  channel.average_view_duration #=> {Sun, 22 Feb 2015=>329.0, Mon, 23 Feb 2015=>326.0, …}
217
217
  channel.average_view_percentage # {Sun, 22 Feb 2015=>38.858253094977265, Mon, 23 Feb 2015=>37.40014235438217, …}
218
- channel.viewer_percentages #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
219
- channel.viewer_percentage(gender: :male) #=> 49.12
218
+ channel.viewer_percentage #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
220
219
 
221
220
  channel.views since: 7.days.ago, by: :traffic_source #=> {advertising: 10.0, related_video: 20.0, promoted: 5.0, subscriber: 1.0, channel: 3.0, other: 7.0}
222
221
  channel.estimated_minutes_watched since: 7.days.ago, by: :traffic_source #=> {annotation: 10.0, external_app: 20.0, external_url: 5.0, embedded: 1.0, search: 3.0}
@@ -258,8 +257,8 @@ channel.subscribers_lost from: '2014-08-30', to: '2014-08-31' #=> {Sat, 30 Aug 2
258
257
  channel.estimated_minutes_watched #=> {Sun, 22 Feb 2015=>2433258.0, Mon, 23 Feb 2015=>2634360.0, …}
259
258
  channel.average_view_duration #=> {Sun, 22 Feb 2015=>329.0, Mon, 23 Feb 2015=>326.0, …}
260
259
  channel.average_view_percentage # {Sun, 22 Feb 2015=>38.858253094977265, Mon, 23 Feb 2015=>37.40014235438217, …}
261
- channel.viewer_percentages #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
262
- channel.viewer_percentage(gender: :female) #=> 49.12
260
+ channel.viewer_percentage since: 7.days.ago #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
261
+
263
262
  channel.views since: 7.days.ago, by: :traffic_source #=> {advertising: 10.0, related_video: 20.0, promoted: 5.0, subscriber: 1.0, channel: 3.0, other: 7.0}
264
263
  channel.estimated_minutes_watched since: 7.days.ago, by: :traffic_source #=> {annotation: 10.0, external_app: 20.0, external_url: 5.0, embedded: 1.0, search: 3.0}
265
264
  channel.views since: 7.days.ago, by: :playback_location #=> {embedded: 34.0, watch: 467.0, channel: 462.0, other: 3.0}
@@ -275,6 +274,8 @@ channel.estimated_minutes_watched since: 7.days.ago, by: :playlist #=> {#<Yt::Mo
275
274
  channel.views since: 7.days.ago, by: :device_type #=> {mobile: 144473.0, unknown_platform: 840.0, game_console: 4940.0, desktop: 102889.0, tv: 4134.0, tablet: 50189.0}
276
275
  channel.estimated_minutes_watched since: 7.days.ago, by: :device_type #=> {mobile: 144473.0, unknown_platform: 840.0, game_console: 4940.0, desktop: 102889.0, tv: 4134.0, tablet: 50189.0}
277
276
  channel.monetized_playbacks_on 5.days.ago #=> 123.0
277
+ channel.viewer_percentage since: 7.days.ago, by: :gender #=> {female: 49.12, male: 50.88}
278
+ channel.viewer_percentage since: 7.days.ago, by: :age_group #=> {'18-24' => 12.12, '25-34' => 16.16,…}
278
279
 
279
280
  channel.content_owner #=> 'CMSname'
280
281
  channel.linked_at #=> Wed, 28 May 2014
@@ -299,7 +300,7 @@ Use [Yt::Video](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Video)
299
300
  * retrieve the views of a video by embedded player location
300
301
  * retrieve the views of a video by related video
301
302
  * retrieve the views of a video by device type
302
- * retrieve the viewer percentage of a video by gender and age group
303
+ * retrieve the viewer percentage of a video by gender, by age group or by both
303
304
  * retrieve data about live-streaming videos
304
305
  * retrieve the advertising options of a video
305
306
 
@@ -396,14 +397,15 @@ video.favorites_removed from: '2014-08-30', to: '2014-08-31' #=> {Sat, 30 Aug 20
396
397
  video.average_view_duration #=> {Sun, 22 Feb 2015=>329.0, Mon, 23 Feb 2015=>326.0, …}
397
398
  video.average_view_percentage # {Sun, 22 Feb 2015=>38.858253094977265, Mon, 23 Feb 2015=>37.40014235438217, …}
398
399
  video.estimated_minutes_watched #=> {Sun, 22 Feb 2015=>2433258.0, Mon, 23 Feb 2015=>2634360.0, …}
399
- video.viewer_percentages #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
400
- video.viewer_percentage(gender: :female) #=> 49.12
400
+ video.viewer_percentage #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
401
401
 
402
402
  video.views since: 7.days.ago, by: :traffic_source #=> {advertising: 10.0, related_video: 20.0, promoted: 5.0, subscriber: 1.0, channel: 3.0, other: 7.0}
403
403
  video.views since: 7.days.ago, by: :playback_location #=> {:embedded=>6.0, :watch=>11.0}
404
404
  video.views since: 7.days.ago, by: :embedded_player_location #=> {"fullscreen.net"=>45.0, "linkedin.com"=>5.0, "mashable.com"=>1.0, "unknown"=>1.0}
405
405
  video.views since: 7.days.ago, by: :related_video #=> {#<Yt::Models::Video @id=...>: 10.0, #<Yt::Models::Video @id=...>: 20.0, …}
406
406
  video.views since: 7.days.ago, by: :device_type #=> {mobile: 144473.0, unknown_platform: 840.0, game_console: 4940.0, desktop: 102889.0, tv: 4134.0, tablet: 50189.0}
407
+ video.viewer_percentage since: 7.days.ago, by: :gender #=> {female: 49.12, male: 50.88}
408
+ video.viewer_percentage since: 7.days.ago, by: :age_group #=> {'18-24' => 12.12, '25-34' => 16.16,…}
407
409
 
408
410
  video.delete #=> true
409
411
  ```
@@ -429,12 +431,15 @@ video.average_view_percentage # {Sun, 22 Feb 2015=>38.858253094977265, Mon, 23 F
429
431
  video.estimated_minutes_watched #=> {Sun, 22 Feb 2015=>2433258.0, Mon, 23 Feb 2015=>2634360.0, …}
430
432
  video.impressions_on 5.days.ago #=> 157.0
431
433
  video.monetized_playbacks_on 5.days.ago #=> 123.0
432
-
433
- video.viewer_percentages #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
434
- video.viewer_percentage(gender: :female) #=> 49.12
434
+ video.viewer_percentage #=> {female: {'18-24' => 12.12, '25-34' => 16.16,…}…}
435
435
 
436
436
  video.views since: 7.days.ago, by: :traffic_source #=> {advertising: 10.0, related_video: 20.0, promoted: 5.0, subscriber: 1.0, channel: 3.0, other: 7.0}
437
437
  video.views since: 7.days.ago, by: :playback_location #=> {:embedded=>6.0, :watch=>11.0}
438
+ video.views since: 7.days.ago, by: :embedded_player_location #=> {"fullscreen.net"=>45.0, "linkedin.com"=>5.0, "mashable.com"=>1.0, "unknown"=>1.0}
439
+ video.views since: 7.days.ago, by: :related_video #=> {#<Yt::Models::Video @id=...>: 10.0, #<Yt::Models::Video @id=...>: 20.0, …}
440
+ video.views since: 7.days.ago, by: :device_type #=> {mobile: 144473.0, unknown_platform: 840.0, game_console: 4940.0, desktop: 102889.0, tv: 4134.0, tablet: 50189.0}
441
+ video.viewer_percentage since: 7.days.ago, by: :gender #=> {female: 49.12, male: 50.88}
442
+ video.viewer_percentage since: 7.days.ago, by: :age_group #=> {'18-24' => 12.12, '25-34' => 16.16,…}
438
443
 
439
444
  video.ad_formats #=> ["standard_instream", "trueview_instream"]
440
445
  ```
@@ -43,10 +43,10 @@ module Yt
43
43
 
44
44
  def define_metric_method(metric)
45
45
  define_method metric do |options = {}|
46
- from = options[:since] || options[:from] || 5.days.ago
47
- to = options[:until] || options[:to] || 1.day.ago
46
+ from = options[:since] || options[:from] || (metric == :viewer_percentage ? 3.months.ago : 5.days.ago)
47
+ to = options[:until] || options[:to] || (metric == :viewer_percentage ? Date.today : 1.day.ago)
48
48
  range = Range.new *[from, to].map(&:to_date)
49
- dimension = options[:by] || :day
49
+ dimension = options[:by] || (metric == :viewer_percentage ? :gender_age_group : :day)
50
50
 
51
51
  ivar = instance_variable_get "@#{metric}_#{dimension}"
52
52
  instance_variable_set "@#{metric}_#{dimension}", ivar || {}
@@ -4,8 +4,10 @@ module Yt
4
4
  #
5
5
  # YouTube resources with viewer percentage reports are:
6
6
  # {Yt::Models::Channel channels} and {Yt::Models::Channel videos}.
7
+ # @deprecated Use {#viewer_percentage} instead.
7
8
  module HasViewerPercentages
8
9
  # @!macro has_viewer_percentages
10
+ # @!deprecated Use {#viewer_percentage} instead.
9
11
  # @!method viewer_percentages
10
12
  # @return [Hash<Symbol,Hash<String,Float>>] the viewer percentages.
11
13
  # The first-level hash identifies the genres (:female, :male).
@@ -20,7 +22,7 @@ module Yt
20
22
  # @example Return the % of male viewers of a video
21
23
  # channel.viewer_percentage(gender: :male) #=> 52.02
22
24
 
23
- # Defines two public instance methods to access the viewer percentages of
25
+ # Defines one public instance methods to access the viewer percentages of
24
26
  # a resource for a specific metric.
25
27
  # @example Adds +viewer_percentages+ and +viewer_percentage+ on Channel.
26
28
  # class Channel < Resource
@@ -30,23 +32,15 @@ module Yt
30
32
  require 'yt/collections/viewer_percentages'
31
33
 
32
34
  define_viewer_percentages_method
33
- define_viewer_percentage_method
34
35
  end
35
36
 
36
37
  private
37
38
 
38
39
  def define_viewer_percentages_method
39
- # @todo: add options like start and end date
40
40
  define_method :viewer_percentages do
41
41
  @viewer_percentages ||= Collections::ViewerPercentages.of(self)[id]
42
42
  end
43
43
  end
44
-
45
- def define_viewer_percentage_method
46
- define_method :viewer_percentage do |filters = {}|
47
- viewer_percentages[filters[:gender]].values.sum.round(2)
48
- end
49
- end
50
44
  end
51
45
  end
52
46
  end
@@ -13,6 +13,12 @@ module Yt
13
13
 
14
14
  private
15
15
 
16
+ def attributes_for_new_item(data)
17
+ snippet = data.fetch 'snippet', {}
18
+ data['snippet'].reverse_merge! complete: snippet.key?('position')
19
+ super(data)
20
+ end
21
+
16
22
  # @return [Hash] the parameters to submit to YouTube to list playlist items.
17
23
  # @see https://developers.google.com/youtube/v3/docs/playlistItems/list
18
24
  def list_params
@@ -11,6 +11,9 @@ module Yt
11
11
  hash[:video] = {name: 'video', parse: ->(video_id) { Yt::Video.new id: video_id, auth: @auth } }
12
12
  hash[:playlist] = {name: 'playlist', parse: ->(playlist_id) { Yt::Playlist.new id: playlist_id, auth: @auth } }
13
13
  hash[:device_type] = {name: 'deviceType', parse: ->(type) { type.downcase.to_sym } }
14
+ hash[:gender_age_group] = {name: 'gender,ageGroup', parse: ->(gender) { gender.downcase.to_sym }}
15
+ hash[:gender] = {name: 'gender', parse: ->(gender) { gender.downcase.to_sym } }
16
+ hash[:age_group] = {name: 'ageGroup', parse: ->(age_group) { age_group[3..-1] } }
14
17
  end
15
18
 
16
19
  # @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#Traffic_Source_Dimensions
@@ -46,7 +49,13 @@ module Yt
46
49
  def within(days_range, dimension, try_again = true)
47
50
  @days_range = days_range
48
51
  @dimension = dimension
49
- Hash[*flat_map{|daily_value| daily_value}]
52
+ if dimension == :gender_age_group # array of array
53
+ Hash.new{|h,k| h[k] = Hash.new 0.0}.tap do |hash|
54
+ each{|gender, age_group, value| hash[gender][age_group[3..-1]] = value}
55
+ end
56
+ else
57
+ Hash[*flat_map{|value| [value.first, value.last]}]
58
+ end
50
59
  # NOTE: Once in a while, YouTube responds with 400 Error and the message
51
60
  # "Invalid query. Query did not conform to the expectations."; in this
52
61
  # case running the same query after one second fixes the issue. This is
@@ -60,7 +69,7 @@ module Yt
60
69
  private
61
70
 
62
71
  def new_item(data)
63
- [instance_exec(data.first, &DIMENSIONS[@dimension][:parse]), data.last]
72
+ [instance_exec(data.first, &DIMENSIONS[@dimension][:parse]), *data[1..-1]]
64
73
  end
65
74
 
66
75
  # @see https://developers.google.com/youtube/analytics/v1/content_owner_reports
@@ -17,7 +17,7 @@ module Yt
17
17
 
18
18
  def attributes_for_new_item(data)
19
19
  id = use_list_endpoint? ? data['id'] : data['id']['videoId']
20
- snippet = data['snippet'].reverse_merge includes_tags: false if data['snippet']
20
+ snippet = data['snippet'].reverse_merge complete: false if data['snippet']
21
21
  {}.tap do |attributes|
22
22
  attributes[:id] = id
23
23
  attributes[:snippet] = snippet
@@ -39,7 +39,7 @@ module Yt
39
39
  video = videos.find{|v| v.id == item['id']['videoId']}
40
40
  parts.each do |part|
41
41
  item[part] = case part
42
- when 'snippet' then video.snippet.data.merge includes_tags: true
42
+ when 'snippet' then video.snippet.data.merge complete: true
43
43
  when 'status' then video.status.data
44
44
  when 'statistics' then video.statistics_set.data
45
45
  when 'contentDetails' then video.content_detail.data
@@ -2,6 +2,7 @@ require 'yt/collections/base'
2
2
 
3
3
  module Yt
4
4
  module Collections
5
+ # @deprecated Use {Yt::Collections::Reports} instead.
5
6
  class ViewerPercentages < Base
6
7
  delegate :[], to: :all
7
8
 
@@ -58,6 +58,10 @@ module Yt
58
58
  # @macro has_report
59
59
  has_report :monetized_playbacks
60
60
 
61
+ # @macro has_report
62
+ has_report :viewer_percentage
63
+
64
+ # @deprecated Use {#has_report :viewer_percentage}.
61
65
  # @macro has_viewer_percentages
62
66
  has_viewer_percentages
63
67
 
@@ -5,7 +5,7 @@ module Yt
5
5
  # Provides methods to interact with YouTube playlist items.
6
6
  # @see https://developers.google.com/youtube/v3/docs/playlistItems
7
7
  class PlaylistItem < Resource
8
- delegate :channel_id, :channel_title, :playlist_id, :position, :video_id,
8
+ delegate :channel_id, :channel_title, :playlist_id, :video_id,
9
9
  :video, to: :snippet
10
10
 
11
11
  def delete(options = {})
@@ -17,6 +17,20 @@ module Yt
17
17
  !@id.nil?
18
18
  end
19
19
 
20
+ # Returns the position of the item in the playlist.
21
+ # Since YouTube API does not return the position on PlaylistItem#create,
22
+ # the memoized @snippet is erased if the video was instantiated like that,
23
+ # so that the full snippet (with position) is loaded, rather than the
24
+ # partial one.
25
+ # @see https://developers.google.com/youtube/v3/docs/playlistItems
26
+ # @return [Integer] the order in which the item appears in a playlist.
27
+ def position
28
+ unless snippet.position || snippet.complete? || @auth.nil?
29
+ @snippet = nil
30
+ end
31
+ snippet.position
32
+ end
33
+
20
34
  private
21
35
 
22
36
  def resource_id
@@ -146,16 +146,17 @@ module Yt
146
146
  @video ||= Video.new id: video_id, auth: @auth if video_id
147
147
  end
148
148
 
149
- # Returns whether YouTube API includes tags in this snippet.
150
- # YouTube API only returns tags on Videos#list, not on Videos#search.
149
+ # Returns whether YouTube API includes all attributes in this snippet.
150
+ # For instance, YouTube API only returns tags on Videos#list, not on
151
+ # Videos#search. And returns position on PlaylistItems#list, not on
152
+ # PlaylistItems#insert.
151
153
  # This method is used to guarantee that, when a video was instantiated
152
- # by calling account.videos or channel.videos, then video.tags is not
153
- # simply returned as empty, but an additional call to Videos#list is
154
- # made to retrieve the correct tags.
154
+ # by one of the methods above, an additional call to is made to retrieve
155
+ # the full snippet in case an attribute is missing.
155
156
  # @see https://developers.google.com/youtube/v3/docs/videos
156
- # @return [Boolean] whether YouTube API includes tags in this snippet.
157
- def includes_tags
158
- @includes_tags ||= data.fetch :includes_tags, true
157
+ # @return [Boolean] whether YouTube API includes the complete snippet.
158
+ def complete?
159
+ @complete ||= data.fetch :complete, true
159
160
  end
160
161
 
161
162
  private
@@ -89,6 +89,10 @@ module Yt
89
89
  # @macro has_report
90
90
  has_report :monetized_playbacks
91
91
 
92
+ # @macro has_report
93
+ has_report :viewer_percentage
94
+
95
+ # @deprecated Use {#has_report :viewer_percentage}.
92
96
  # @macro has_viewer_percentages
93
97
  has_viewer_percentages
94
98
 
@@ -124,7 +128,7 @@ module Yt
124
128
  # @return [Array<Yt::Models::Tag>] the list of keyword tags associated
125
129
  # with the video.
126
130
  def tags
127
- unless snippet.tags.any? || snippet.includes_tags || @auth.nil?
131
+ unless snippet.tags.any? || snippet.complete? || @auth.nil?
128
132
  @snippet = nil
129
133
  end
130
134
  snippet.tags
data/lib/yt/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.14.7'
2
+ VERSION = '0.15.0'
3
3
  end
@@ -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) }
12
+ let(:result) { reports.within Range.new(5.days.ago, 4.days.ago), :day }
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' }
@@ -22,7 +22,7 @@ describe Yt::Collections::Reports do
22
22
  end
23
23
 
24
24
  context 'but returns a success code 2XX the second time' do
25
- before { try_again.and_return [1,2] }
25
+ before { try_again.and_return [[1,2]] }
26
26
  it { expect{result}.not_to fail }
27
27
  end
28
28
  end
@@ -51,12 +51,12 @@ describe Yt::Account, :device_app do
51
51
  end
52
52
 
53
53
  describe '.includes(:snippet)' do
54
- let(:video) { $account.videos.includes(:snippet).first }
54
+ let(:video) { $account.videos.first }
55
55
 
56
56
  specify 'eager-loads the *full* snippet of each video' do
57
57
  expect(video.instance_variable_defined? :@snippet).to be true
58
58
  expect(video.channel_title).to be
59
- expect(video.snippet.includes_tags).to be true
59
+ expect(video.snippet).to be_complete
60
60
  end
61
61
  end
62
62
 
@@ -109,10 +109,13 @@ describe Yt::Account, :device_app do
109
109
  it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
110
110
  end
111
111
 
112
- context 'and an invalid device code' do
113
- before { attrs[:device_code] = '--not-a-valid-device-code--' }
114
- it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
115
- end
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
116
119
  end
117
120
 
118
121
  context 'given no token or code' do
@@ -26,7 +26,7 @@ describe Yt::Channel, :device_app do
26
26
 
27
27
  specify 'returns the videos in the channel without their tags' do
28
28
  expect(video).to be_a Yt::Video
29
- expect(video.snippet.includes_tags).to be false
29
+ expect(video.snippet).not_to be_complete
30
30
  end
31
31
 
32
32
  describe '.where(id: *anything*)' do
@@ -175,6 +175,7 @@ describe Yt::Channel, :device_app do
175
175
  expect{channel.estimated_minutes_watched}.not_to raise_error
176
176
  expect{channel.average_view_duration}.not_to raise_error
177
177
  expect{channel.average_view_percentage}.not_to raise_error
178
+ expect{channel.viewer_percentage}.not_to raise_error
178
179
  expect{channel.earnings}.to raise_error Yt::Errors::Unauthorized
179
180
  expect{channel.impressions}.to raise_error Yt::Errors::Unauthorized
180
181
  expect{channel.monetized_playbacks}.to raise_error Yt::Errors::Unauthorized
@@ -195,9 +196,9 @@ describe Yt::Channel, :device_app do
195
196
  expect{channel.impressions_on 3.days.ago}.to raise_error Yt::Errors::Unauthorized
196
197
  end
197
198
 
199
+ # @deprecated, use channel.viewer_percentage instead
198
200
  it 'returns valid reports for channel-related demographics' do
199
201
  expect{channel.viewer_percentages}.not_to raise_error
200
- expect{channel.viewer_percentage}.not_to raise_error
201
202
  end
202
203
 
203
204
  it 'cannot give information about its content owner' do
@@ -5,7 +5,7 @@ describe Yt::PlaylistItem, :device_app do
5
5
  subject(:item) { Yt::PlaylistItem.new id: id, auth: $account }
6
6
 
7
7
  context 'given an existing playlist item' do
8
- let(:id) { 'PLjW_GNR5Ir0GWEP_oveGBNjTvKkYyZfsna1TZDCBP-Z8' }
8
+ let(:id) { 'PLjW_GNR5Ir0GMlbJzA-aW0UV8TchJFb8p3uzrLNcZKPY' }
9
9
 
10
10
  it 'returns valid metadata' do
11
11
  expect(item.title).to be_a String
@@ -7,7 +7,7 @@ describe Yt::Playlist, :device_app do
7
7
  subject(:playlist) { Yt::Playlist.new id: id, auth: $account }
8
8
 
9
9
  context 'given an existing playlist' do
10
- let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
10
+ let(:id) { 'PLSWYkYzOrPMT9pJG5St5G0WDalhRzGkU4' }
11
11
 
12
12
  it 'returns valid metadata' do
13
13
  expect(playlist.title).to be_a String
@@ -21,6 +21,8 @@ describe Yt::Playlist, :device_app do
21
21
  end
22
22
 
23
23
  it { expect(playlist.playlist_items.first).to be_a Yt::PlaylistItem }
24
+ it { expect(playlist.playlist_items.first.snippet).to be_complete }
25
+ it { expect(playlist.playlist_items.first.position).not_to be_nil }
24
26
  end
25
27
 
26
28
  context 'given an unknown playlist' do
@@ -31,7 +33,7 @@ describe Yt::Playlist, :device_app do
31
33
  end
32
34
 
33
35
  context 'given someone else’s playlist' do
34
- let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
36
+ let(:id) { 'PLSWYkYzOrPMT9pJG5St5G0WDalhRzGkU4' }
35
37
  let(:video_id) { 'MESycYJytkU' }
36
38
 
37
39
  it { expect{playlist.delete}.to fail.with 'forbidden' }
@@ -137,6 +139,20 @@ describe Yt::Playlist, :device_app do
137
139
  it { expect(playlist.add_video(video_id, position: 0).position).to be 0 }
138
140
  end
139
141
 
142
+ # NOTE: This test sounds redundant, but it’s actually a reflection of
143
+ # another irrational behavior of YouTube API. In short, if you add a new
144
+ # video to a playlist, the returned item does not have the "position"
145
+ # information. You need an extra call to get it. When YouTube fixes this
146
+ # behavior, this test (and related code) will go away.
147
+ describe 'adding the video' do
148
+ let(:item) { playlist.add_video video_id }
149
+
150
+ specify 'returns an item without its position' do
151
+ expect(item.snippet).not_to be_complete
152
+ expect(item.position).not_to be_nil # after reloading
153
+ end
154
+ end
155
+
140
156
  describe 'can be removed' do
141
157
  before { playlist.add_video video_id }
142
158
 
@@ -292,6 +292,7 @@ describe Yt::Video, :device_app do
292
292
  expect{video.estimated_minutes_watched}.not_to raise_error
293
293
  expect{video.average_view_duration}.not_to raise_error
294
294
  expect{video.average_view_percentage}.not_to raise_error
295
+ expect{video.viewer_percentage}.not_to raise_error
295
296
  expect{video.earnings}.to raise_error Yt::Errors::Unauthorized
296
297
  expect{video.impressions}.to raise_error Yt::Errors::Unauthorized
297
298
  expect{video.monetized_playbacks}.to raise_error Yt::Errors::Unauthorized
@@ -313,9 +314,9 @@ describe Yt::Video, :device_app do
313
314
  expect{video.impressions_on 3.days.ago}.to raise_error Yt::Errors::Unauthorized
314
315
  end
315
316
 
317
+ # @deprecated, use video.viewer_percentage instead
316
318
  it 'returns valid reports for video-related demographics' do
317
319
  expect{video.viewer_percentages}.not_to raise_error
318
- expect{video.viewer_percentage}.not_to raise_error
319
320
  end
320
321
  end
321
322
 
@@ -311,7 +311,7 @@ describe Yt::Channel, :partner do
311
311
 
312
312
  describe 'shares can be retrieved for a specific day' do
313
313
  context 'in which the channel was partnered' do
314
- let(:shares) { channel.shares_on 5.days.ago}
314
+ let(:shares) { channel.shares_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
315
315
  it { expect(shares).to be_a Float }
316
316
  end
317
317
 
@@ -845,6 +845,58 @@ describe Yt::Channel, :partner do
845
845
  end
846
846
  end
847
847
 
848
+ describe 'viewer percentage can be retrieved for a range of days' do
849
+ let(:viewer_percentage) { channel.viewer_percentage since: 1.year.ago, until: 10.days.ago}
850
+ it { expect(viewer_percentage).to be_a Hash }
851
+ end
852
+
853
+ describe 'viewer_percentage can be grouped by gender and age group' do
854
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
855
+ let(:keys) { range.values }
856
+
857
+ specify 'without a :by option (default)' do
858
+ viewer_percentage = channel.viewer_percentage range
859
+ expect(viewer_percentage.keys).to match_array [:female, :male]
860
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
861
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
862
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
863
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
864
+ end
865
+
866
+ specify 'with the :by option set to :gender_age_group' do
867
+ viewer_percentage = channel.viewer_percentage range.merge by: :gender_age_group
868
+ expect(viewer_percentage.keys).to match_array [:female, :male]
869
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
870
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
871
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
872
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
873
+ end
874
+ end
875
+
876
+ describe 'viewer_percentage can be grouped by gender' do
877
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
878
+ let(:keys) { range.values }
879
+
880
+ specify 'with the :by option set to :gender' do
881
+ viewer_percentage = channel.viewer_percentage range.merge by: :gender
882
+ expect(viewer_percentage.keys).to match_array [:female, :male]
883
+ expect(viewer_percentage[:female]).to be_a Float
884
+ expect(viewer_percentage[:male]).to be_a Float
885
+ end
886
+ end
887
+
888
+ describe 'viewer_percentage can be grouped by age group' do
889
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
890
+ let(:keys) { range.values }
891
+
892
+ specify 'with the :by option set to :age_group' do
893
+ viewer_percentage = channel.viewer_percentage range.merge by: :age_group
894
+ expect(viewer_percentage.keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
895
+ expect(viewer_percentage.values).to all(be_instance_of Float)
896
+ end
897
+ end
898
+
899
+ # @deprecated, use channel.viewer_percentage instead
848
900
  specify 'viewer percentages by gender and age range can be retrieved' do
849
901
  expect(channel.viewer_percentages[:female]['18-24']).to be_a Float
850
902
  expect(channel.viewer_percentages[:female]['25-34']).to be_a Float
@@ -858,9 +910,6 @@ describe Yt::Channel, :partner do
858
910
  expect(channel.viewer_percentages[:male]['45-54']).to be_a Float
859
911
  expect(channel.viewer_percentages[:male]['55-64']).to be_a Float
860
912
  expect(channel.viewer_percentages[:male]['65-']).to be_a Float
861
-
862
- expect(channel.viewer_percentage(gender: :male)).to be_a Float
863
- expect(channel.viewer_percentage(gender: :female)).to be_a Float
864
913
  end
865
914
 
866
915
  specify 'information about its content owner can be retrieved' do
@@ -15,7 +15,7 @@ describe Yt::Video, :partner do
15
15
 
16
16
  describe 'earnings can be retrieved for a specific day' do
17
17
  context 'in which the video made any money' do
18
- let(:earnings) {video.earnings_on 5.days.ago}
18
+ let(:earnings) {video.earnings_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
19
19
  it { expect(earnings).to be_a Float }
20
20
  end
21
21
 
@@ -26,22 +26,14 @@ describe Yt::Video, :partner do
26
26
  end
27
27
 
28
28
  describe 'earnings can be retrieved for a range of days' do
29
- let(:date) { 4.days.ago }
30
-
31
- specify 'with a given start (:since option)' do
32
- expect(video.earnings(since: date).keys.min).to eq date.to_date
33
- end
29
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
34
30
 
35
- specify 'with a given end (:until option)' do
36
- expect(video.earnings(until: date).keys.max).to eq date.to_date
37
- end
38
-
39
- specify 'with a given start (:from option)' do
40
- expect(video.earnings(from: date).keys.min).to eq date.to_date
31
+ specify 'with a given start and end (:since / :until option)' do
32
+ expect(video.earnings(since: date, until: date).keys.min).to eq date.to_date
41
33
  end
42
34
 
43
- specify 'with a given end (:to option)' do
44
- expect(video.earnings(to: date).keys.max).to eq date.to_date
35
+ specify 'with a given start and end (:from / :to option)' do
36
+ expect(video.earnings(from: date, to: date).keys.min).to eq date.to_date
45
37
  end
46
38
  end
47
39
 
@@ -113,7 +105,7 @@ describe Yt::Video, :partner do
113
105
  end
114
106
 
115
107
  describe 'views can be grouped by embedded player location' do
116
- let(:range) { {since: 4.days.ago, until: 3.days.ago} }
108
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: 3.days.ago} }
117
109
 
118
110
  specify 'with the :by option set to :embedded_player_location' do
119
111
  views = video.views range.merge by: :embedded_player_location
@@ -142,7 +134,7 @@ describe Yt::Video, :partner do
142
134
 
143
135
  describe 'comments can be retrieved for a specific day' do
144
136
  context 'in which the video was partnered' do
145
- let(:comments) { video.comments_on 5.days.ago}
137
+ let(:comments) { video.comments_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
146
138
  it { expect(comments).to be_a Float }
147
139
  end
148
140
 
@@ -189,7 +181,7 @@ describe Yt::Video, :partner do
189
181
 
190
182
  describe 'likes can be retrieved for a specific day' do
191
183
  context 'in which the video was partnered' do
192
- let(:likes) { video.likes_on 5.days.ago}
184
+ let(:likes) { video.likes_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
193
185
  it { expect(likes).to be_a Float }
194
186
  end
195
187
 
@@ -236,7 +228,7 @@ describe Yt::Video, :partner do
236
228
 
237
229
  describe 'dislikes can be retrieved for a specific day' do
238
230
  context 'in which the video was partnered' do
239
- let(:dislikes) { video.dislikes_on 5.days.ago}
231
+ let(:dislikes) { video.dislikes_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
240
232
  it { expect(dislikes).to be_a Float }
241
233
  end
242
234
 
@@ -283,7 +275,7 @@ describe Yt::Video, :partner do
283
275
 
284
276
  describe 'shares can be retrieved for a specific day' do
285
277
  context 'in which the video was partnered' do
286
- let(:shares) { video.shares_on 5.days.ago}
278
+ let(:shares) { video.shares_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
287
279
  it { expect(shares).to be_a Float }
288
280
  end
289
281
 
@@ -330,7 +322,7 @@ describe Yt::Video, :partner do
330
322
 
331
323
  describe 'gained subscribers can be retrieved for a specific day' do
332
324
  context 'in which the video was partnered' do
333
- let(:subscribers_gained) { video.subscribers_gained_on 5.days.ago}
325
+ let(:subscribers_gained) { video.subscribers_gained_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
334
326
  it { expect(subscribers_gained).to be_a Float }
335
327
  end
336
328
 
@@ -377,7 +369,7 @@ describe Yt::Video, :partner do
377
369
 
378
370
  describe 'lost subscribers can be retrieved for a specific day' do
379
371
  context 'in which the video was partnered' do
380
- let(:subscribers_lost) { video.subscribers_lost_on 5.days.ago}
372
+ let(:subscribers_lost) { video.subscribers_lost_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
381
373
  it { expect(subscribers_lost).to be_a Float }
382
374
  end
383
375
 
@@ -424,7 +416,7 @@ describe Yt::Video, :partner do
424
416
 
425
417
  describe 'added favorites can be retrieved for a specific day' do
426
418
  context 'in which the video was partnered' do
427
- let(:favorites_added) { video.favorites_added_on 5.days.ago}
419
+ let(:favorites_added) { video.favorites_added_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
428
420
  it { expect(favorites_added).to be_a Float }
429
421
  end
430
422
 
@@ -471,7 +463,7 @@ describe Yt::Video, :partner do
471
463
 
472
464
  describe 'removed favorites can be retrieved for a specific day' do
473
465
  context 'in which the video was partnered' do
474
- let(:favorites_removed) { video.favorites_removed_on 5.days.ago}
466
+ let(:favorites_removed) { video.favorites_removed_on ENV['YT_TEST_PARTNER_VIDEO_DATE']}
475
467
  it { expect(favorites_removed).to be_a Float }
476
468
  end
477
469
 
@@ -761,6 +753,58 @@ describe Yt::Video, :partner do
761
753
  end
762
754
  end
763
755
 
756
+ describe 'viewer percentage can be retrieved for a range of days' do
757
+ let(:viewer_percentage) { video.viewer_percentage since: 1.year.ago, until: 10.days.ago}
758
+ it { expect(viewer_percentage).to be_a Hash }
759
+ end
760
+
761
+ describe 'viewer_percentage can be grouped by gender and age group' do
762
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
763
+ let(:keys) { range.values }
764
+
765
+ specify 'without a :by option (default)' do
766
+ viewer_percentage = video.viewer_percentage range
767
+ expect(viewer_percentage.keys).to match_array [:female, :male]
768
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
769
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
770
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
771
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
772
+ end
773
+
774
+ specify 'with the :by option set to :gender_age_group' do
775
+ viewer_percentage = video.viewer_percentage range.merge by: :gender_age_group
776
+ expect(viewer_percentage.keys).to match_array [:female, :male]
777
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
778
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
779
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
780
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
781
+ end
782
+ end
783
+
784
+ describe 'viewer_percentage can be grouped by gender' do
785
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
786
+ let(:keys) { range.values }
787
+
788
+ specify 'with the :by option set to :gender' do
789
+ viewer_percentage = video.viewer_percentage range.merge by: :gender
790
+ expect(viewer_percentage.keys).to match_array [:female, :male]
791
+ expect(viewer_percentage[:female]).to be_a Float
792
+ expect(viewer_percentage[:male]).to be_a Float
793
+ end
794
+ end
795
+
796
+ describe 'viewer_percentage can be grouped by age group' do
797
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
798
+ let(:keys) { range.values }
799
+
800
+ specify 'with the :by option set to :age_group' do
801
+ viewer_percentage = video.viewer_percentage range.merge by: :age_group
802
+ expect(viewer_percentage.keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
803
+ expect(viewer_percentage.values).to all(be_instance_of Float)
804
+ end
805
+ end
806
+
807
+ # @deprecated, use video.viewer_percentage instead
764
808
  specify 'viewer percentages by gender and age range can be retrieved' do
765
809
  expect(video.viewer_percentages[:female]['18-24']).to be_a Float
766
810
  expect(video.viewer_percentages[:female]['25-34']).to be_a Float
@@ -774,9 +818,6 @@ describe Yt::Video, :partner do
774
818
  expect(video.viewer_percentages[:male]['45-54']).to be_a Float
775
819
  expect(video.viewer_percentages[:male]['55-64']).to be_a Float
776
820
  expect(video.viewer_percentages[:male]['65-']).to be_a Float
777
-
778
- expect(video.viewer_percentage(gender: :male)).to be_a Float
779
- expect(video.viewer_percentage(gender: :female)).to be_a Float
780
821
  end
781
822
  end
782
823
 
@@ -5,7 +5,7 @@ describe Yt::PlaylistItem, :server_app do
5
5
  subject(:item) { Yt::PlaylistItem.new id: id }
6
6
 
7
7
  context 'given an existing playlist item' do
8
- let(:id) { 'PLjW_GNR5Ir0GWEP_oveGBNjTvKkYyZfsna1TZDCBP-Z8' }
8
+ let(:id) { 'PLjW_GNR5Ir0GMlbJzA-aW0UV8TchJFb8p3uzrLNcZKPY' }
9
9
 
10
10
  it 'returns valid snippet data' do
11
11
  expect(item.snippet).to be_a Yt::Snippet
@@ -5,7 +5,7 @@ describe Yt::Playlist, :server_app do
5
5
  subject(:playlist) { Yt::Playlist.new id: id }
6
6
 
7
7
  context 'given an existing playlist' do
8
- let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
8
+ let(:id) { 'PLSWYkYzOrPMT9pJG5St5G0WDalhRzGkU4' }
9
9
 
10
10
  it 'returns valid snippet data' do
11
11
  expect(playlist.snippet).to be_a Yt::Snippet
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.14.7
4
+ version: 0.15.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: 2015-04-17 00:00:00.000000000 Z
11
+ date: 2015-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport