yt 0.25.13 → 0.32.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +305 -1
- data/MIT-LICENSE +1 -1
- data/README.md +86 -5
- data/YOUTUBE_IT.md +3 -3
- data/lib/yt.rb +5 -2
- data/lib/yt/actions/list.rb +3 -3
- data/lib/yt/associations/has_authentication.rb +33 -1
- data/lib/yt/associations/has_reports.rb +13 -18
- data/lib/yt/collections/assets.rb +2 -2
- data/lib/yt/collections/authentications.rb +9 -2
- data/lib/yt/collections/base.rb +3 -3
- data/lib/yt/collections/bulk_report_jobs.rb +28 -0
- data/lib/yt/collections/bulk_reports.rb +24 -0
- data/lib/yt/collections/claims.rb +22 -1
- data/lib/yt/collections/comment_threads.rb +41 -0
- data/lib/yt/collections/content_owners.rb +1 -1
- data/lib/yt/collections/group_infos.rb +27 -0
- data/lib/yt/collections/group_items.rb +45 -0
- data/lib/yt/collections/reports.rb +75 -13
- data/lib/yt/collections/revocations.rb +30 -0
- data/lib/yt/collections/video_groups.rb +29 -0
- data/lib/yt/collections/videos.rb +34 -9
- data/lib/yt/constants/geography.rb +326 -0
- data/lib/yt/errors/forbidden.rb +1 -3
- data/lib/yt/errors/no_items.rb +1 -3
- data/lib/yt/errors/request_error.rb +10 -7
- data/lib/yt/errors/server_error.rb +1 -3
- data/lib/yt/errors/unauthorized.rb +3 -3
- data/lib/yt/models/account.rb +12 -0
- data/lib/yt/models/advertising_options_set.rb +4 -4
- data/lib/yt/models/bulk_report.rb +23 -0
- data/lib/yt/models/bulk_report_job.rb +23 -0
- data/lib/yt/models/channel.rb +21 -12
- data/lib/yt/models/claim.rb +13 -2
- data/lib/yt/models/comment.rb +37 -0
- data/lib/yt/models/comment_thread.rb +50 -0
- data/lib/yt/models/content_detail.rb +6 -0
- data/lib/yt/models/content_owner.rb +31 -1
- data/lib/yt/models/group_info.rb +16 -0
- data/lib/yt/models/group_item.rb +15 -0
- data/lib/yt/models/resource.rb +3 -10
- data/lib/yt/models/revocation.rb +12 -0
- data/lib/yt/models/right_owner.rb +0 -2
- data/lib/yt/models/snippet.rb +24 -3
- data/lib/yt/models/video.rb +42 -11
- data/lib/yt/models/video_group.rb +186 -0
- data/lib/yt/request.rb +5 -3
- data/lib/yt/version.rb +2 -2
- data/spec/collections/comment_threads_spec.rb +46 -0
- data/spec/collections/playlist_items_spec.rb +1 -1
- data/spec/collections/reports_spec.rb +2 -2
- data/spec/constants/geography_spec.rb +16 -0
- data/spec/models/annotation_spec.rb +1 -1
- data/spec/models/claim_spec.rb +15 -3
- data/spec/models/comment_spec.rb +40 -0
- data/spec/models/comment_thread_spec.rb +93 -0
- data/spec/models/content_detail_spec.rb +7 -0
- data/spec/models/reference_spec.rb +2 -2
- data/spec/models/request_spec.rb +21 -0
- data/spec/models/resource_spec.rb +0 -15
- data/spec/models/video_spec.rb +1 -1
- data/spec/requests/as_account/account_spec.rb +16 -4
- data/spec/requests/as_account/authentications_spec.rb +1 -13
- data/spec/requests/as_account/channel_spec.rb +15 -45
- data/spec/requests/as_account/playlist_item_spec.rb +3 -3
- data/spec/requests/as_account/playlist_spec.rb +5 -32
- data/spec/requests/as_account/video_spec.rb +2022 -21
- data/spec/requests/as_content_owner/account_spec.rb +4 -0
- data/spec/requests/as_content_owner/bulk_report_job_spec.rb +19 -0
- data/spec/requests/as_content_owner/channel_spec.rb +59 -270
- data/spec/requests/as_content_owner/content_owner_spec.rb +89 -1
- data/spec/requests/as_content_owner/playlist_spec.rb +0 -15
- data/spec/requests/as_content_owner/video_group_spec.rb +112 -0
- data/spec/requests/as_content_owner/video_spec.rb +72 -146
- data/spec/requests/as_server_app/channel_spec.rb +1 -21
- data/spec/requests/as_server_app/comment_spec.rb +22 -0
- data/spec/requests/as_server_app/comment_thread_spec.rb +27 -0
- data/spec/requests/as_server_app/comment_threads_spec.rb +41 -0
- data/spec/requests/as_server_app/playlist_item_spec.rb +2 -2
- data/spec/requests/as_server_app/playlist_spec.rb +1 -22
- data/spec/requests/as_server_app/video_spec.rb +21 -19
- data/spec/requests/as_server_app/videos_spec.rb +5 -5
- data/spec/requests/unauthenticated/video_spec.rb +1 -9
- data/spec/spec_helper.rb +1 -1
- data/yt.gemspec +2 -1
- metadata +51 -17
- data/lib/yt/collections/ids.rb +0 -27
- data/lib/yt/config.rb +0 -54
- data/lib/yt/models/configuration.rb +0 -70
- data/lib/yt/models/description.rb +0 -58
- data/lib/yt/models/url.rb +0 -91
- data/spec/models/configuration_spec.rb +0 -44
- data/spec/models/description_spec.rb +0 -94
- data/spec/models/url_spec.rb +0 -84
- data/spec/requests/as_account/resource_spec.rb +0 -18
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'yt/collections/base'
|
2
|
+
require 'yt/models/group_item'
|
3
|
+
|
4
|
+
module Yt
|
5
|
+
module Collections
|
6
|
+
# @private
|
7
|
+
class GroupItems < Base
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def attributes_for_new_item(data)
|
12
|
+
super(data).tap do |attributes|
|
13
|
+
attributes[:video] = data['video']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def list_params
|
18
|
+
super.tap do |params|
|
19
|
+
params[:host] = 'youtubeanalytics.googleapis.com'
|
20
|
+
params[:path] = "/v2/groupItems"
|
21
|
+
params[:params] = {group_id: @parent.id}
|
22
|
+
if @auth.owner_name
|
23
|
+
params[:params][:on_behalf_of_content_owner] = @auth.owner_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def eager_load_items_from(items)
|
29
|
+
if included_relationships.include?(:video)
|
30
|
+
all_video_ids = items.map{|item| item['resource']['id']}.uniq
|
31
|
+
all_video_ids.each_slice(50) do |video_ids|
|
32
|
+
conditions = {id: video_ids.join(',')}
|
33
|
+
conditions[:part] = 'snippet,status,statistics,contentDetails'
|
34
|
+
videos = Collections::Videos.new(auth: @auth).where conditions
|
35
|
+
items.each do |item|
|
36
|
+
video = videos.find{|v| v.id == item['resource']['id']}
|
37
|
+
item['video'] = video if video
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -11,12 +11,15 @@ module Yt
|
|
11
11
|
hash[:traffic_source] = {name: 'insightTrafficSourceType', parse: ->(source, *values) { @metrics.keys.zip(values.map{|v| {TRAFFIC_SOURCES.key(source) => v}}).to_h} }
|
12
12
|
hash[:playback_location] = {name: 'insightPlaybackLocationType', parse: ->(location, *values) { @metrics.keys.zip(values.map{|v| {PLAYBACK_LOCATIONS.key(location) => v}}).to_h} }
|
13
13
|
hash[:embedded_player_location] = {name: 'insightPlaybackLocationDetail', parse: ->(url, *values) {@metrics.keys.zip(values.map{|v| {url => v}}).to_h} }
|
14
|
+
hash[:subscribed_status] = {name: 'subscribedStatus', parse: ->(status, *values) {@metrics.keys.zip(values.map{|v| {SUBSCRIBED_STATUSES.key(status) => v}}).to_h} }
|
14
15
|
hash[:related_video] = {name: 'insightTrafficSourceDetail', parse: ->(video_id, *values) { @metrics.keys.zip(values.map{|v| {video_id => v}}).to_h} }
|
15
16
|
hash[:search_term] = {name: 'insightTrafficSourceDetail', parse: ->(search_term, *values) {@metrics.keys.zip(values.map{|v| {search_term => v}}).to_h} }
|
16
17
|
hash[:referrer] = {name: 'insightTrafficSourceDetail', parse: ->(url, *values) {@metrics.keys.zip(values.map{|v| {url => v}}).to_h} }
|
17
18
|
hash[:video] = {name: 'video', parse: ->(video_id, *values) { @metrics.keys.zip(values.map{|v| {video_id => v}}).to_h} }
|
18
19
|
hash[:playlist] = {name: 'playlist', parse: ->(playlist_id, *values) { @metrics.keys.zip(values.map{|v| {playlist_id => v}}).to_h} }
|
19
|
-
hash[:device_type] = {name: 'deviceType', parse: ->(type, *values) {@metrics.keys.zip(values.map{|v| {type
|
20
|
+
hash[:device_type] = {name: 'deviceType', parse: ->(type, *values) {@metrics.keys.zip(values.map{|v| {DEVICE_TYPES.key(type) => v}}).to_h} }
|
21
|
+
hash[:operating_system] = {name: 'operatingSystem', parse: ->(os, *values) {@metrics.keys.zip(values.map{|v| {OPERATING_SYSTEMS.key(os) => v}}).to_h} }
|
22
|
+
hash[:youtube_product] = {name: 'youtubeProduct', parse: ->(product, *values) {@metrics.keys.zip(values.map{|v| {YOUTUBE_PRODUCTS.key(product) => v}}).to_h} }
|
20
23
|
hash[:country] = {name: 'country', parse: ->(country_code, *values) { @metrics.keys.zip(values.map{|v| {country_code => v}}).to_h} }
|
21
24
|
hash[:state] = {name: 'province', parse: ->(country_and_state_code, *values) { @metrics.keys.zip(values.map{|v| {country_and_state_code[3..-1] => v}}).to_h} }
|
22
25
|
hash[:gender_age_group] = {name: 'gender,ageGroup', parse: ->(gender, *values) { [gender.downcase.to_sym, *values] }}
|
@@ -43,6 +46,8 @@ module Yt
|
|
43
46
|
google: 'GOOGLE_SEARCH',
|
44
47
|
notification: 'NOTIFICATION',
|
45
48
|
playlist_page: 'YT_PLAYLIST_PAGE',
|
49
|
+
campaign_card: 'CAMPAIGN_CARD',
|
50
|
+
end_screen: 'END_SCREEN',
|
46
51
|
info_card: 'INFO_CARD'
|
47
52
|
}
|
48
53
|
|
@@ -53,17 +58,72 @@ module Yt
|
|
53
58
|
embedded: 'EMBEDDED',
|
54
59
|
other: 'YT_OTHER',
|
55
60
|
external_app: 'EXTERNAL_APP',
|
61
|
+
search: 'SEARCH', # undocumented but returned by the API
|
62
|
+
browse: 'BROWSE', # undocumented but returned by the API
|
56
63
|
mobile: 'MOBILE' # only present for data < September 10, 2013
|
57
64
|
}
|
58
65
|
|
66
|
+
# @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#Playback_Detail_Dimensions
|
67
|
+
SUBSCRIBED_STATUSES = {
|
68
|
+
subscribed: 'SUBSCRIBED',
|
69
|
+
unsubscribed: 'UNSUBSCRIBED'
|
70
|
+
}
|
71
|
+
|
72
|
+
# @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#youtubeProduct
|
73
|
+
YOUTUBE_PRODUCTS = {
|
74
|
+
core: 'CORE',
|
75
|
+
gaming: 'GAMING',
|
76
|
+
kids: 'KIDS',
|
77
|
+
unknown: 'UNKNOWN'
|
78
|
+
}
|
79
|
+
|
80
|
+
# @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#Device_Dimensions
|
81
|
+
DEVICE_TYPES = {
|
82
|
+
desktop: 'DESKTOP',
|
83
|
+
game_console: 'GAME_CONSOLE',
|
84
|
+
mobile: 'MOBILE',
|
85
|
+
tablet: 'TABLET',
|
86
|
+
tv: 'TV',
|
87
|
+
unknown_platform: 'UNKNOWN_PLATFORM'
|
88
|
+
}
|
89
|
+
|
90
|
+
# @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#Device_Dimensions
|
91
|
+
OPERATING_SYSTEMS = {
|
92
|
+
android: 'ANDROID',
|
93
|
+
bada: 'BADA',
|
94
|
+
blackberry: 'BLACKBERRY',
|
95
|
+
chromecast: 'CHROMECAST',
|
96
|
+
docomo: 'DOCOMO',
|
97
|
+
firefox: 'FIREFOX',
|
98
|
+
hiptop: 'HIPTOP',
|
99
|
+
ios: 'IOS',
|
100
|
+
linux: 'LINUX',
|
101
|
+
macintosh: 'MACINTOSH',
|
102
|
+
meego: 'MEEGO',
|
103
|
+
nintendo_3ds: 'NINTENDO_3DS',
|
104
|
+
other: 'OTHER',
|
105
|
+
playstation: 'PLAYSTATION',
|
106
|
+
playstation_vita: 'PLAYSTATION_VITA',
|
107
|
+
realmedia: 'REALMEDIA',
|
108
|
+
smart_tv: 'SMART_TV',
|
109
|
+
symbian: 'SYMBIAN',
|
110
|
+
tizen: 'TIZEN',
|
111
|
+
webos: 'WEBOS',
|
112
|
+
wii: 'WII',
|
113
|
+
windows: 'WINDOWS',
|
114
|
+
windows_mobile: 'WINDOWS_MOBILE',
|
115
|
+
xbox: 'XBOX'
|
116
|
+
}
|
117
|
+
|
59
118
|
attr_writer :metrics
|
60
119
|
|
61
|
-
def within(days_range, country, state, dimension, videos,
|
120
|
+
def within(days_range, country, state, dimension, videos, historical, max_retries = 3)
|
62
121
|
@days_range = days_range
|
63
|
-
@dimension = dimension
|
64
122
|
@country = country
|
65
123
|
@state = state
|
124
|
+
@dimension = dimension
|
66
125
|
@videos = videos
|
126
|
+
@historical = historical
|
67
127
|
if dimension == :gender_age_group # array of array
|
68
128
|
Hash.new{|h,k| h[k] = Hash.new 0.0}.tap do |hash|
|
69
129
|
each{|gender, age_group, value| hash[gender][age_group[3..-1]] = value}
|
@@ -86,10 +146,10 @@ module Yt
|
|
86
146
|
end
|
87
147
|
elsif dimension == :day
|
88
148
|
hash = hash.transform_values{|h| h.sort_by{|day, v| day}.to_h}
|
89
|
-
elsif dimension.in? [:traffic_source, :country, :state, :playback_location, :device_type]
|
149
|
+
elsif dimension.in? [:traffic_source, :country, :state, :playback_location, :device_type, :operating_system, :subscribed_status]
|
90
150
|
hash = hash.transform_values{|h| h.sort_by{|range, v| -v}.to_h}
|
91
151
|
end
|
92
|
-
(@metrics.one? || @metrics.keys == [:
|
152
|
+
(@metrics.one? || @metrics.keys == [:estimated_revenue, :estimated_minutes_watched]) ? hash[@metrics.keys.first] : hash
|
93
153
|
end
|
94
154
|
# NOTE: Once in a while, YouTube responds with 400 Error and the message
|
95
155
|
# "Invalid query. Query did not conform to the expectations."; in this
|
@@ -98,7 +158,7 @@ module Yt
|
|
98
158
|
# same query is a workaround that works and can hardly cause any damage.
|
99
159
|
# Similarly, once in while YouTube responds with a random 503 error.
|
100
160
|
rescue Yt::Error => e
|
101
|
-
|
161
|
+
(max_retries > 0) && rescue?(e) ? sleep(3) && within(days_range, country, state, dimension, videos, historical, max_retries - 1) : raise
|
102
162
|
end
|
103
163
|
|
104
164
|
private
|
@@ -117,7 +177,8 @@ module Yt
|
|
117
177
|
# @see https://developers.google.com/youtube/analytics/v1/content_owner_reports
|
118
178
|
def list_params
|
119
179
|
super.tap do |params|
|
120
|
-
params[:
|
180
|
+
params[:host] = 'youtubeanalytics.googleapis.com'
|
181
|
+
params[:path] = '/v2/reports'
|
121
182
|
params[:params] = reports_params
|
122
183
|
params[:camelize_params] = false
|
123
184
|
end
|
@@ -125,15 +186,16 @@ module Yt
|
|
125
186
|
|
126
187
|
def reports_params
|
127
188
|
@parent.reports_params.tap do |params|
|
128
|
-
params['
|
129
|
-
params['
|
189
|
+
params['startDate'] = @days_range.begin
|
190
|
+
params['endDate'] = @days_range.end
|
130
191
|
params['metrics'] = @metrics.keys.join(',').to_s.camelize(:lower)
|
131
192
|
params['dimensions'] = DIMENSIONS[@dimension][:name] unless @dimension == :range
|
132
|
-
params['
|
133
|
-
params['
|
193
|
+
params['includeHistoricalChannelData'] = @historical if @historical
|
194
|
+
params['maxResults'] = 50 if @dimension.in? [:playlist, :video]
|
195
|
+
params['maxResults'] = 25 if @dimension.in? [:embedded_player_location, :related_video, :search_term, :referrer]
|
134
196
|
if @dimension.in? [:video, :playlist, :embedded_player_location, :related_video, :search_term, :referrer]
|
135
|
-
if @metrics.keys == [:
|
136
|
-
params['sort'] = '-
|
197
|
+
if @metrics.keys == [:estimated_revenue, :estimated_minutes_watched]
|
198
|
+
params['sort'] = '-estimatedRevenue'
|
137
199
|
else
|
138
200
|
params['sort'] = "-#{@metrics.keys.join(',').to_s.camelize(:lower)}"
|
139
201
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'yt/collections/authentications'
|
2
|
+
require 'yt/models/revocation'
|
3
|
+
|
4
|
+
module Yt
|
5
|
+
module Collections
|
6
|
+
class Revocations < Authentications
|
7
|
+
attr_accessor :auth_params
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# This overrides the parent mehthod defined in Authentications
|
12
|
+
def attributes_for_new_item(data)
|
13
|
+
{data: data}
|
14
|
+
end
|
15
|
+
|
16
|
+
def list_params
|
17
|
+
super.tap do |params|
|
18
|
+
params[:host] = 'accounts.google.com'
|
19
|
+
params[:path] = '/o/oauth2/revoke'
|
20
|
+
params[:request_format] = nil
|
21
|
+
params[:method] = :get
|
22
|
+
params[:auth] = nil
|
23
|
+
params[:body] = nil
|
24
|
+
params[:camelize_body] = false
|
25
|
+
params[:params] = auth_params
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'yt/collections/base'
|
2
|
+
require 'yt/models/video_group'
|
3
|
+
require 'yt/models/group_info'
|
4
|
+
|
5
|
+
module Yt
|
6
|
+
module Collections
|
7
|
+
# @private
|
8
|
+
class VideoGroups < Base
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def attributes_for_new_item(data)
|
13
|
+
{id: data['id'], auth: @auth, group_info: Yt::GroupInfo.new(data: data)}
|
14
|
+
end
|
15
|
+
|
16
|
+
def new_item(data)
|
17
|
+
super if data['contentDetails']['itemType'] == 'youtube#video'
|
18
|
+
end
|
19
|
+
|
20
|
+
def list_params
|
21
|
+
super.tap do |params|
|
22
|
+
params[:host] = 'youtubeanalytics.googleapis.com'
|
23
|
+
params[:path] = "/v2/groups"
|
24
|
+
params[:params] = @parent.video_groups_params
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -10,6 +10,7 @@ module Yt
|
|
10
10
|
class Videos < Base
|
11
11
|
def where(requirements = {})
|
12
12
|
@published_before = nil
|
13
|
+
@halt_list = false
|
13
14
|
super
|
14
15
|
end
|
15
16
|
|
@@ -25,18 +26,23 @@ module Yt
|
|
25
26
|
attributes[:content_details] = data['contentDetails']
|
26
27
|
attributes[:statistics] = data['statistics']
|
27
28
|
attributes[:video_category] = data['videoCategory']
|
29
|
+
attributes[:claim] = data['claim']
|
28
30
|
attributes[:auth] = @auth
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
34
|
def eager_load_items_from(items)
|
33
35
|
if included_relationships.any?
|
34
|
-
|
35
|
-
included_relationships
|
36
|
+
associations = [:claim, :category]
|
37
|
+
if (included_relationships & associations).any?
|
38
|
+
included_relationships.append(:snippet).uniq!
|
39
|
+
end
|
36
40
|
|
37
41
|
ids = items.map{|item| item['id']['videoId']}
|
38
|
-
parts = included_relationships.map
|
39
|
-
|
42
|
+
parts = (included_relationships - associations).map do |r|
|
43
|
+
r.to_s.camelize(:lower)
|
44
|
+
end
|
45
|
+
conditions = { id: ids.join(','), part: parts.join(',') }
|
40
46
|
videos = Collections::Videos.new(auth: @auth).where conditions
|
41
47
|
|
42
48
|
items.each do |item|
|
@@ -51,7 +57,20 @@ module Yt
|
|
51
57
|
end if video
|
52
58
|
end
|
53
59
|
|
54
|
-
if
|
60
|
+
if included_relationships.include? :claim
|
61
|
+
video_ids = items.map{|item| item['id']['videoId']}.uniq
|
62
|
+
conditions = {
|
63
|
+
video_id: video_ids.join(','),
|
64
|
+
include_third_party_claims: false
|
65
|
+
}
|
66
|
+
claims = @parent.claims.includes(:asset).where conditions
|
67
|
+
items.each do |item|
|
68
|
+
claim = claims.find { |c| c.video_id == item['id']['videoId']}
|
69
|
+
item['claim'] = claim
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if included_relationships.include? :category
|
55
74
|
category_ids = items.map{|item| item['snippet']['categoryId']}.uniq
|
56
75
|
conditions = {id: category_ids.join(',')}
|
57
76
|
video_categories = Collections::VideoCategories.new(auth: @auth).where conditions
|
@@ -77,7 +96,7 @@ module Yt
|
|
77
96
|
def next_page
|
78
97
|
super.tap do |items|
|
79
98
|
halt_list if use_list_endpoint? && items.empty? && @page_token.nil?
|
80
|
-
add_offset_to(items) if !use_list_endpoint? &&
|
99
|
+
add_offset_to(items) if !use_list_endpoint? && videos_params[:order] == 'date' && !(videos_params[:for_mine] || videos_params[:for_content_owner])
|
81
100
|
end
|
82
101
|
end
|
83
102
|
|
@@ -86,9 +105,15 @@ module Yt
|
|
86
105
|
# that limit, the query is restarted with a publishedBefore filter in
|
87
106
|
# case there are more videos to be listed for a channel
|
88
107
|
def add_offset_to(items)
|
89
|
-
|
108
|
+
@fetched_items ||= 0
|
109
|
+
if (@fetched_items += items.count) >= 500
|
90
110
|
last_published = items.last['snippet']['publishedAt']
|
91
|
-
|
111
|
+
last_published = DateTime.rfc3339(last_published) - 1.second
|
112
|
+
last_published = last_published.strftime '%FT%T.999Z'
|
113
|
+
@page_token, @published_before, @fetched_items = '', last_published, 0
|
114
|
+
elsif (1...50) === @last_index % 50
|
115
|
+
@halt_list, @page_token = true, nil
|
116
|
+
@last_index += items.size
|
92
117
|
end
|
93
118
|
end
|
94
119
|
|
@@ -134,4 +159,4 @@ module Yt
|
|
134
159
|
end
|
135
160
|
end
|
136
161
|
end
|
137
|
-
end
|
162
|
+
end
|
@@ -0,0 +1,326 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
|
4
|
+
module Yt
|
5
|
+
# The list of country codes and names used by the YouTube Analytics API.
|
6
|
+
# @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#country
|
7
|
+
COUNTRIES = {
|
8
|
+
AC: 'Ascension',
|
9
|
+
AD: 'Andorra',
|
10
|
+
AE: 'United Arab Emirates',
|
11
|
+
AF: 'Afghanistan',
|
12
|
+
AG: 'Antigua and Barbuda',
|
13
|
+
AI: 'Anguilla',
|
14
|
+
AL: 'Albania',
|
15
|
+
AM: 'Armenia',
|
16
|
+
AN: 'Netherlands Antilles',
|
17
|
+
AO: 'Angola',
|
18
|
+
AQ: 'Antarctic Territory',
|
19
|
+
AR: 'Argentina',
|
20
|
+
AS: 'American Samoa',
|
21
|
+
AT: 'Austria',
|
22
|
+
AU: 'Australia',
|
23
|
+
AW: 'Aruba',
|
24
|
+
AX: 'Aland',
|
25
|
+
AZ: 'Azerbaijan',
|
26
|
+
BA: 'Bosnia and Herzegovina',
|
27
|
+
BB: 'Barbados',
|
28
|
+
BD: 'Bangladesh',
|
29
|
+
BE: 'Belgium',
|
30
|
+
BF: 'Burkina Faso',
|
31
|
+
BG: 'Bulgaria',
|
32
|
+
BH: 'Bahrain',
|
33
|
+
BI: 'Burundi',
|
34
|
+
BJ: 'Benin',
|
35
|
+
BL: 'Saint Barthélemy',
|
36
|
+
BM: 'Bermuda',
|
37
|
+
BN: 'Brunei',
|
38
|
+
BO: 'Bolivia',
|
39
|
+
BQ: 'Bonaire, Sint Eustatius and Saba',
|
40
|
+
BR: 'Brazil',
|
41
|
+
BS: 'Bahamas',
|
42
|
+
BT: 'Bhutan',
|
43
|
+
BV: 'Bouvet Island',
|
44
|
+
BW: 'Botswana',
|
45
|
+
BY: 'Belarus',
|
46
|
+
BZ: 'Belize',
|
47
|
+
CA: 'Canada',
|
48
|
+
CC: 'Cocos (Keeling) Islands',
|
49
|
+
CD: 'Congo',
|
50
|
+
CF: 'Central African Republic',
|
51
|
+
CG: 'Congo',
|
52
|
+
CH: 'Switzerland',
|
53
|
+
CI: 'Ivory Coast',
|
54
|
+
CK: 'Cook Islands',
|
55
|
+
CL: 'Chile',
|
56
|
+
CM: 'Cameroon',
|
57
|
+
CN: 'China',
|
58
|
+
CO: 'Colombia',
|
59
|
+
CR: 'Costa Rica',
|
60
|
+
CU: 'Cuba',
|
61
|
+
CV: 'Cape Verde',
|
62
|
+
CW: 'Curaçao',
|
63
|
+
CX: 'Christmas Island',
|
64
|
+
CY: 'Cyprus',
|
65
|
+
CZ: 'Czech Republic',
|
66
|
+
DE: 'Germany',
|
67
|
+
DJ: 'Djibouti',
|
68
|
+
DK: 'Denmark',
|
69
|
+
DM: 'Dominica',
|
70
|
+
DO: 'Dominican Republic',
|
71
|
+
DZ: 'Algeria',
|
72
|
+
EC: 'Ecuador',
|
73
|
+
EE: 'Estonia',
|
74
|
+
EG: 'Egypt',
|
75
|
+
EH: 'Western Sahara',
|
76
|
+
ER: 'Eritrea',
|
77
|
+
ES: 'Spain',
|
78
|
+
ET: 'Ethiopia',
|
79
|
+
FI: 'Finland',
|
80
|
+
FJ: 'Fiji',
|
81
|
+
FK: 'Falkland Islands',
|
82
|
+
FM: 'Micronesia',
|
83
|
+
FO: 'Faroe Islands',
|
84
|
+
FR: 'France',
|
85
|
+
GA: 'Gabon',
|
86
|
+
GB: 'United Kingdom',
|
87
|
+
GD: 'Grenada',
|
88
|
+
GE: 'Georgia',
|
89
|
+
GF: 'French Guiana',
|
90
|
+
GG: 'Guernsey',
|
91
|
+
GH: 'Ghana',
|
92
|
+
GI: 'Gibraltar',
|
93
|
+
GL: 'Greenland',
|
94
|
+
GM: 'Gambia',
|
95
|
+
GN: 'Guinea',
|
96
|
+
GP: 'Guadeloupe',
|
97
|
+
GQ: 'Equatorial Guinea',
|
98
|
+
GR: 'Greece',
|
99
|
+
GS: 'South Georgia & South Sandwich Islands',
|
100
|
+
GT: 'Guatemala',
|
101
|
+
GU: 'Guam',
|
102
|
+
GW: 'Guinea-Bissau',
|
103
|
+
GY: 'Guyana',
|
104
|
+
HK: 'Hong Kong',
|
105
|
+
HM: 'Heard Island and McDonald Islands',
|
106
|
+
HN: 'Honduras',
|
107
|
+
HR: 'Croatia',
|
108
|
+
HT: 'Haiti',
|
109
|
+
HU: 'Hungary',
|
110
|
+
ID: 'Indonesia',
|
111
|
+
IE: 'Ireland',
|
112
|
+
IL: 'Israel',
|
113
|
+
IM: 'Isle of Man',
|
114
|
+
IN: 'India',
|
115
|
+
IO: 'British Indian Ocean Territory',
|
116
|
+
IQ: 'Iraq',
|
117
|
+
IR: 'Iran',
|
118
|
+
IS: 'Iceland',
|
119
|
+
IT: 'Italy',
|
120
|
+
JE: 'Jersey',
|
121
|
+
JM: 'Jamaica',
|
122
|
+
JO: 'Jordan',
|
123
|
+
JP: 'Japan',
|
124
|
+
KE: 'Kenya',
|
125
|
+
KG: 'Kyrgyzstan',
|
126
|
+
KH: 'Cambodia',
|
127
|
+
KI: 'Kiribati',
|
128
|
+
KM: 'Comoros',
|
129
|
+
KN: 'Saint Kitts and Nevis',
|
130
|
+
KP: 'North Korea',
|
131
|
+
KR: 'South Korea',
|
132
|
+
KW: 'Kuwait',
|
133
|
+
KY: 'Cayman Islands',
|
134
|
+
KZ: 'Kazakhstan',
|
135
|
+
LA: 'Laos',
|
136
|
+
LB: 'Lebanon',
|
137
|
+
LC: 'Saint Lucia',
|
138
|
+
LI: 'Liechtenstein',
|
139
|
+
LK: 'Sri Lanka',
|
140
|
+
LR: 'Liberia',
|
141
|
+
LS: 'Lesotho',
|
142
|
+
LT: 'Lithuania',
|
143
|
+
LU: 'Luxembourg',
|
144
|
+
LV: 'Latvia',
|
145
|
+
LY: 'Libya',
|
146
|
+
MA: 'Morocco',
|
147
|
+
MC: 'Monaco',
|
148
|
+
MD: 'Moldova',
|
149
|
+
ME: 'Montenegro',
|
150
|
+
MF: 'Saint Martin',
|
151
|
+
MG: 'Madagascar',
|
152
|
+
MH: 'Marshall Islands',
|
153
|
+
MK: 'Macedonia',
|
154
|
+
ML: 'Mali',
|
155
|
+
MM: 'Myanmar',
|
156
|
+
MN: 'Mongolia',
|
157
|
+
MO: 'Macau',
|
158
|
+
MP: 'Northern Mariana Islands',
|
159
|
+
MQ: 'Martinique',
|
160
|
+
MR: 'Mauritania',
|
161
|
+
MS: 'Montserrat',
|
162
|
+
MT: 'Malta',
|
163
|
+
MU: 'Mauritius',
|
164
|
+
MV: 'Maldives',
|
165
|
+
MW: 'Malawi',
|
166
|
+
MX: 'Mexico',
|
167
|
+
MY: 'Malaysia',
|
168
|
+
MZ: 'Mozambique',
|
169
|
+
NA: 'Namibia',
|
170
|
+
NC: 'New Caledonia',
|
171
|
+
NE: 'Niger',
|
172
|
+
NF: 'Norfolk Island',
|
173
|
+
NG: 'Nigeria',
|
174
|
+
NI: 'Nicaragua',
|
175
|
+
NL: 'Netherlands',
|
176
|
+
NO: 'Norway',
|
177
|
+
NP: 'Nepal',
|
178
|
+
NR: 'Nauru',
|
179
|
+
NU: 'Niue',
|
180
|
+
NZ: 'New Zealand',
|
181
|
+
OM: 'Oman',
|
182
|
+
PA: 'Panama',
|
183
|
+
PE: 'Peru',
|
184
|
+
PF: 'French Polynesia',
|
185
|
+
PG: 'Papua New Guinea',
|
186
|
+
PH: 'Philippines',
|
187
|
+
PK: 'Pakistan',
|
188
|
+
PL: 'Poland',
|
189
|
+
PM: 'Saint Pierre and Miquelon',
|
190
|
+
PN: 'Pitcairn Islands',
|
191
|
+
PR: 'Puerto Rico',
|
192
|
+
PS: 'Palestinian Territory',
|
193
|
+
PT: 'Portugal',
|
194
|
+
PW: 'Palau',
|
195
|
+
PY: 'Paraguay',
|
196
|
+
QA: 'Qatar',
|
197
|
+
RE: 'Reunion',
|
198
|
+
RO: 'Romania',
|
199
|
+
RS: 'Serbia',
|
200
|
+
RU: 'Russia',
|
201
|
+
RW: 'Rwanda',
|
202
|
+
SA: 'Saudi Arabia',
|
203
|
+
SB: 'Solomon Islands',
|
204
|
+
SC: 'Seychelles',
|
205
|
+
SD: 'Sudan',
|
206
|
+
SE: 'Sweden',
|
207
|
+
SG: 'Singapore',
|
208
|
+
SH: 'Saint Helena',
|
209
|
+
SI: 'Slovenia',
|
210
|
+
SJ: 'Svalbard',
|
211
|
+
SK: 'Slovakia',
|
212
|
+
SL: 'Sierra Leone',
|
213
|
+
SM: 'San Marino',
|
214
|
+
SN: 'Senegal',
|
215
|
+
SO: 'Somalia',
|
216
|
+
SR: 'Suriname',
|
217
|
+
SS: 'South Sudan',
|
218
|
+
ST: 'Sao Tome and Principe',
|
219
|
+
SV: 'El Salvador',
|
220
|
+
SX: 'Sint Maarten',
|
221
|
+
SY: 'Syria',
|
222
|
+
SZ: 'Swaziland',
|
223
|
+
TA: 'Tristan da Cunha',
|
224
|
+
TC: 'Turks and Caicos Islands',
|
225
|
+
TD: 'Chad',
|
226
|
+
TF: 'French Southern and Antarctic Lands',
|
227
|
+
TG: 'Togo',
|
228
|
+
TH: 'Thailand',
|
229
|
+
TJ: 'Tajikistan',
|
230
|
+
TK: 'Tokelau',
|
231
|
+
TL: 'East Timor',
|
232
|
+
TM: 'Turkmenistan',
|
233
|
+
TN: 'Tunisia',
|
234
|
+
TO: 'Tonga',
|
235
|
+
TR: 'Turkey',
|
236
|
+
TT: 'Trinidad and Tobago',
|
237
|
+
TV: 'Tuvalu',
|
238
|
+
TW: 'Taiwan',
|
239
|
+
TZ: 'Tanzania',
|
240
|
+
UA: 'Ukraine',
|
241
|
+
UG: 'Uganda',
|
242
|
+
UM: 'Midway Islands',
|
243
|
+
US: 'United States',
|
244
|
+
UY: 'Uruguay',
|
245
|
+
UZ: 'Uzbekistan',
|
246
|
+
VA: 'Vatican City',
|
247
|
+
VC: 'Saint Vincent and the Grenadines',
|
248
|
+
VE: 'Venezuela',
|
249
|
+
VG: 'British Virgin Islands',
|
250
|
+
VI: 'U.S. Virgin Islands',
|
251
|
+
VN: 'Vietnam',
|
252
|
+
VU: 'Vanuatu',
|
253
|
+
XK: 'Kosovo',
|
254
|
+
WF: 'Wallis and Futuna',
|
255
|
+
WS: 'Samoa',
|
256
|
+
YE: 'Yemen',
|
257
|
+
YT: 'Mayotte',
|
258
|
+
ZA: 'South Africa',
|
259
|
+
ZM: 'Zambia',
|
260
|
+
ZW: 'Zimbabwe',
|
261
|
+
ZZ: 'Unknown Region'
|
262
|
+
}.with_indifferent_access
|
263
|
+
|
264
|
+
# The list of U.S. state codes and names used by the YouTube Analytics API.
|
265
|
+
# @see https://developers.google.com/youtube/analytics/v1/dimsmets/dims#province
|
266
|
+
US_STATES = {
|
267
|
+
AL: 'Alabama',
|
268
|
+
AK: 'Alaska',
|
269
|
+
AZ: 'Arizona',
|
270
|
+
AR: 'Arkansas',
|
271
|
+
CA: 'California',
|
272
|
+
CO: 'Colorado',
|
273
|
+
CT: 'Connecticut',
|
274
|
+
DE: 'Delaware',
|
275
|
+
FL: 'Florida',
|
276
|
+
GA: 'Georgia',
|
277
|
+
HI: 'Hawaii',
|
278
|
+
ID: 'Idaho',
|
279
|
+
IL: 'Illinois',
|
280
|
+
IN: 'Indiana',
|
281
|
+
IA: 'Iowa',
|
282
|
+
KS: 'Kansas',
|
283
|
+
KY: 'Kentucky',
|
284
|
+
LA: 'Louisiana',
|
285
|
+
ME: 'Maine',
|
286
|
+
MD: 'Maryland',
|
287
|
+
MA: 'Massachusetts',
|
288
|
+
MI: 'Michigan',
|
289
|
+
MN: 'Minnesota',
|
290
|
+
MS: 'Mississippi',
|
291
|
+
MO: 'Missouri',
|
292
|
+
MT: 'Montana',
|
293
|
+
NE: 'Nebraska',
|
294
|
+
NV: 'Nevada',
|
295
|
+
NH: 'New Hampshire',
|
296
|
+
NJ: 'New Jersey',
|
297
|
+
NM: 'New Mexico',
|
298
|
+
NY: 'New York',
|
299
|
+
NC: 'North Carolina',
|
300
|
+
ND: 'North Dakota',
|
301
|
+
OH: 'Ohio',
|
302
|
+
OK: 'Oklahoma',
|
303
|
+
OR: 'Oregon',
|
304
|
+
PA: 'Pennsylvania',
|
305
|
+
RI: 'Rhode Island',
|
306
|
+
SC: 'South Carolina',
|
307
|
+
SD: 'South Dakota',
|
308
|
+
TN: 'Tennessee',
|
309
|
+
TX: 'Texas',
|
310
|
+
UT: 'Utah',
|
311
|
+
VT: 'Vermont',
|
312
|
+
VA: 'Virginia',
|
313
|
+
WA: 'Washington',
|
314
|
+
WV: 'West Virginia',
|
315
|
+
WI: 'Wisconsin',
|
316
|
+
WY: 'Wyoming',
|
317
|
+
DC: 'District of Columbia',
|
318
|
+
AS: 'American Samoa',
|
319
|
+
GU: 'Guam',
|
320
|
+
MP: 'Northern Mariana Islands',
|
321
|
+
PR: 'Puerto Rico',
|
322
|
+
UM: 'United States Minor Outlying Islands',
|
323
|
+
VI: 'Virgin Islands, U.S.',
|
324
|
+
ZZ: 'Unknown Region'
|
325
|
+
}.with_indifferent_access
|
326
|
+
end
|