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 +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +17 -12
- data/lib/yt/associations/has_reports.rb +3 -3
- data/lib/yt/associations/has_viewer_percentages.rb +3 -9
- data/lib/yt/collections/playlist_items.rb +6 -0
- data/lib/yt/collections/reports.rb +11 -2
- data/lib/yt/collections/videos.rb +2 -2
- data/lib/yt/collections/viewer_percentages.rb +1 -0
- data/lib/yt/models/channel.rb +4 -0
- data/lib/yt/models/playlist_item.rb +15 -1
- data/lib/yt/models/snippet.rb +9 -8
- data/lib/yt/models/video.rb +5 -1
- data/lib/yt/version.rb +1 -1
- data/spec/collections/reports_spec.rb +2 -2
- data/spec/requests/as_account/account_spec.rb +2 -2
- data/spec/requests/as_account/authentications_spec.rb +7 -4
- data/spec/requests/as_account/channel_spec.rb +3 -2
- data/spec/requests/as_account/playlist_item_spec.rb +1 -1
- data/spec/requests/as_account/playlist_spec.rb +18 -2
- data/spec/requests/as_account/video_spec.rb +2 -1
- data/spec/requests/as_content_owner/channel_spec.rb +53 -4
- data/spec/requests/as_content_owner/video_spec.rb +67 -26
- data/spec/requests/as_server_app/playlist_item_spec.rb +1 -1
- data/spec/requests/as_server_app/playlist_spec.rb +1 -1
- 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: 54209e7c3dedc665ad4d6b0d93bcf6c6d9d6f546
|
4
|
+
data.tar.gz: 6e09959e44cbf2aad7bcf29df8dfa25f6cc903c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
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.
|
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.
|
262
|
-
|
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
|
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.
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
data/lib/yt/models/channel.rb
CHANGED
@@ -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, :
|
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
|
data/lib/yt/models/snippet.rb
CHANGED
@@ -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
|
150
|
-
# YouTube API only returns tags on Videos#list, not on
|
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
|
153
|
-
#
|
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
|
157
|
-
def
|
158
|
-
@
|
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
|
data/lib/yt/models/video.rb
CHANGED
@@ -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.
|
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
@@ -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.
|
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
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
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) { '
|
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) { '
|
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) { '
|
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
|
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
|
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) {
|
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.
|
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.
|
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:
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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) { '
|
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) { '
|
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.
|
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-
|
11
|
+
date: 2015-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|