yt 0.32.6 → 0.33.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -4
- data/CHANGELOG.md +36 -0
- data/README.md +22 -33
- data/YOUTUBE_IT.md +4 -4
- data/lib/yt.rb +1 -1
- data/lib/yt/actions/list.rb +1 -1
- data/lib/yt/associations/has_reports.rb +9 -14
- data/lib/yt/collections/reports.rb +5 -7
- data/lib/yt/models/account.rb +13 -3
- data/lib/yt/models/channel.rb +19 -4
- data/lib/yt/models/resource.rb +74 -3
- data/lib/yt/models/status.rb +3 -1
- data/lib/yt/models/url.rb +2 -60
- data/lib/yt/models/video.rb +16 -1
- data/lib/yt/request.rb +7 -4
- data/lib/yt/version.rb +1 -1
- data/yt.gemspec +5 -2
- metadata +32 -170
- data/spec/collections/claims_spec.rb +0 -62
- data/spec/collections/comment_threads_spec.rb +0 -46
- data/spec/collections/playlist_items_spec.rb +0 -44
- data/spec/collections/playlists_spec.rb +0 -27
- data/spec/collections/policies_spec.rb +0 -30
- data/spec/collections/references_spec.rb +0 -30
- data/spec/collections/reports_spec.rb +0 -30
- data/spec/collections/subscriptions_spec.rb +0 -25
- data/spec/collections/videos_spec.rb +0 -43
- data/spec/constants/geography_spec.rb +0 -16
- data/spec/errors/forbidden_spec.rb +0 -10
- data/spec/errors/missing_auth_spec.rb +0 -24
- data/spec/errors/no_items_spec.rb +0 -10
- data/spec/errors/request_error_spec.rb +0 -44
- data/spec/errors/server_error_spec.rb +0 -10
- data/spec/errors/unauthorized_spec.rb +0 -10
- data/spec/models/account_spec.rb +0 -138
- data/spec/models/annotation_spec.rb +0 -180
- data/spec/models/asset_spec.rb +0 -32
- data/spec/models/channel_spec.rb +0 -127
- data/spec/models/claim_event_spec.rb +0 -62
- data/spec/models/claim_history_spec.rb +0 -27
- data/spec/models/claim_spec.rb +0 -223
- data/spec/models/comment_spec.rb +0 -40
- data/spec/models/comment_thread_spec.rb +0 -93
- data/spec/models/configuration_spec.rb +0 -44
- data/spec/models/content_detail_spec.rb +0 -52
- data/spec/models/content_owner_detail_spec.rb +0 -6
- data/spec/models/file_detail_spec.rb +0 -13
- data/spec/models/live_streaming_detail_spec.rb +0 -6
- data/spec/models/ownership_spec.rb +0 -59
- data/spec/models/player_spec.rb +0 -13
- data/spec/models/playlist_item_spec.rb +0 -120
- data/spec/models/playlist_spec.rb +0 -138
- data/spec/models/policy_rule_spec.rb +0 -63
- data/spec/models/policy_spec.rb +0 -41
- data/spec/models/rating_spec.rb +0 -12
- data/spec/models/reference_spec.rb +0 -249
- data/spec/models/request_spec.rb +0 -204
- data/spec/models/resource_spec.rb +0 -42
- data/spec/models/right_owner_spec.rb +0 -71
- data/spec/models/snippet_spec.rb +0 -13
- data/spec/models/statistics_set_spec.rb +0 -13
- data/spec/models/status_spec.rb +0 -13
- data/spec/models/subscription_spec.rb +0 -30
- data/spec/models/url_spec.rb +0 -78
- data/spec/models/video_category_spec.rb +0 -21
- data/spec/models/video_spec.rb +0 -669
- data/spec/requests/as_account/account_spec.rb +0 -143
- data/spec/requests/as_account/authentications_spec.rb +0 -127
- data/spec/requests/as_account/channel_spec.rb +0 -246
- data/spec/requests/as_account/channels_spec.rb +0 -18
- data/spec/requests/as_account/playlist_item_spec.rb +0 -55
- data/spec/requests/as_account/playlist_spec.rb +0 -218
- data/spec/requests/as_account/thumbnail.jpg +0 -0
- data/spec/requests/as_account/video.mp4 +0 -0
- data/spec/requests/as_account/video_spec.rb +0 -408
- data/spec/requests/as_content_owner/account_spec.rb +0 -29
- data/spec/requests/as_content_owner/advertising_options_set_spec.rb +0 -15
- data/spec/requests/as_content_owner/asset_spec.rb +0 -31
- data/spec/requests/as_content_owner/bulk_report_job_spec.rb +0 -19
- data/spec/requests/as_content_owner/channel_spec.rb +0 -1836
- data/spec/requests/as_content_owner/claim_history_spec.rb +0 -20
- data/spec/requests/as_content_owner/claim_spec.rb +0 -17
- data/spec/requests/as_content_owner/content_owner_spec.rb +0 -370
- data/spec/requests/as_content_owner/match_policy_spec.rb +0 -17
- data/spec/requests/as_content_owner/ownership_spec.rb +0 -25
- data/spec/requests/as_content_owner/playlist_spec.rb +0 -767
- data/spec/requests/as_content_owner/video_group_spec.rb +0 -112
- data/spec/requests/as_content_owner/video_spec.rb +0 -1223
- data/spec/requests/as_server_app/channel_spec.rb +0 -54
- data/spec/requests/as_server_app/comment_spec.rb +0 -22
- data/spec/requests/as_server_app/comment_thread_spec.rb +0 -27
- data/spec/requests/as_server_app/comment_threads_spec.rb +0 -41
- data/spec/requests/as_server_app/playlist_item_spec.rb +0 -30
- data/spec/requests/as_server_app/playlist_spec.rb +0 -33
- data/spec/requests/as_server_app/url_spec.rb +0 -94
- data/spec/requests/as_server_app/video_spec.rb +0 -60
- data/spec/requests/as_server_app/videos_spec.rb +0 -40
- data/spec/requests/unauthenticated/video_spec.rb +0 -14
- data/spec/spec_helper.rb +0 -20
- data/spec/support/fail_matcher.rb +0 -15
- data/spec/support/global_hooks.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c89bfab3bb32975237aa1b79a1f0897a240ea494f3d3928a03b423c734b71f6
|
4
|
+
data.tar.gz: 6c58ab20408d9fdf79265fc499d1ee0f88623295de50592a25948e8029aaf91a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bcd96aa69c14456e067f0cad222148743334e8d8f89f3a052923f687e2d06e3a53db772898ec43399d1cdff20352fc9e56f63745bdbbebe69641511255cf5a9
|
7
|
+
data.tar.gz: 52245373b815ad681f7516bea9157597c7b5b118db206a50c7ca2746cf61b8c591f2fb4765c63787427cac26fb803ce11053c1d12855e4d213fdbfb54b372e0c
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,42 @@ 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.33.4 - 2021-01-15
|
10
|
+
|
11
|
+
* [REMOVAL] remove retry for quota errors
|
12
|
+
|
13
|
+
## 0.33.3 - 2020-11-17
|
14
|
+
|
15
|
+
* [BUGFIX] require the `URL` model when requiring `yt`
|
16
|
+
* [BUGFIX] handle passing in a `nil` id
|
17
|
+
|
18
|
+
## 0.33.2 - 2020-11-11
|
19
|
+
|
20
|
+
* [BUGFIX] No more pages when page token is an empty string, per YouTube change.
|
21
|
+
|
22
|
+
## 0.33.1 - 2020-10-19
|
23
|
+
|
24
|
+
* [BUGFIX] Only retry once when exchanging a refresh token
|
25
|
+
|
26
|
+
## 0.33.0 - 2020-04-10
|
27
|
+
|
28
|
+
If your code calls reports methods such as `views`, `likes`, or `reports`,
|
29
|
+
do not include `by: :week` option since `7DayTotals` dimension will no longer be
|
30
|
+
supported by YouTube API as of [April 15, 2020](https://developers.google.com/youtube/analytics/revision_history#october-15,-2019).
|
31
|
+
|
32
|
+
Use `by: :day` option instead and add up the numbers from each day.
|
33
|
+
|
34
|
+
If you keep using `by: :week` option after this change it will raise an error
|
35
|
+
(before the gem upgrade) or it will run with `day` dimension instead (after
|
36
|
+
the gem upgrade, like any other random input).
|
37
|
+
|
38
|
+
* [REMOVAL] Remove `by: :week` option for reports.
|
39
|
+
* [FEATURE] Add back the option of initializing a resource by its URL.
|
40
|
+
* [BUGFIX] Limit retries on refreshing tokens
|
41
|
+
|
42
|
+
**Breaking change**
|
43
|
+
|
44
|
+
If your code is using constant `Yt::URL::CHANNEL_PATTERNS` etc then it's moved to `Yt::Resource::CHANNEL_PATTERNS`, `Yt::Resource::VIDEO_PATTERNS`, and `Yt::Resource::PLAYLIST_PATTERNS`.
|
9
45
|
|
10
46
|
## 0.32.6 - 2020-02-07
|
11
47
|
|
data/README.md
CHANGED
@@ -18,7 +18,6 @@ After [registering your app](#configuring-your-app), you can run commands like:
|
|
18
18
|
channel = Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow'
|
19
19
|
channel.title #=> "Fullscreen"
|
20
20
|
channel.public? #=> true
|
21
|
-
channel.comment_count #=> 773
|
22
21
|
channel.videos.count #=> 12
|
23
22
|
```
|
24
23
|
|
@@ -526,44 +525,34 @@ If a variable is set in both places, then `Yt.configure` takes precedence.
|
|
526
525
|
How to test
|
527
526
|
===========
|
528
527
|
|
529
|
-
|
530
|
-
|
531
|
-
1. tests in `spec/models`, `spec/collections` and `spec/errors` **do not hit** the YouTube API
|
532
|
-
1. tests in `spec/requests` **hit** the YouTube API and require authentication
|
533
|
-
|
534
|
-
To only run tests against models, collections and errors (which do not hit the API), type:
|
528
|
+
To run tests:
|
535
529
|
|
536
530
|
```bash
|
537
|
-
rspec
|
531
|
+
rspec
|
538
532
|
```
|
539
533
|
|
540
|
-
|
534
|
+
We recommend RSpec >= 3.8.
|
541
535
|
|
542
|
-
|
543
|
-
|
544
|
-
|
536
|
+
Yt comes with two different sets of tests:
|
537
|
+
|
538
|
+
1. Unit tests in `spec/models`, `spec/collections` and `spec/errors`
|
539
|
+
2. Legacy integration tests in `spec/requests`
|
540
|
+
|
541
|
+
Coming soon will be a new set of high-level integration tests.
|
542
|
+
|
543
|
+
Integration tests are recorded with VCR. Some of the tests refer to
|
544
|
+
fixture data that an arbitrary account may not have access to. If you
|
545
|
+
need to modify one of these tests or re-record the cassette, we'd
|
546
|
+
suggest working against your own version of the testing setup. Then in
|
547
|
+
your pull request, we can help canonize your test/fixtures.
|
548
|
+
|
549
|
+
Some of the integration tests require authentication. These can be set
|
550
|
+
with the following environment variables:
|
545
551
|
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
* Account-based tests, which require a valid refresh token along with
|
552
|
-
the application-level credentials the refresh token was created with
|
553
|
-
(`YT_TEST_DEVICE_REFRESH_TOKEN`, `YT_TEST_DEVICE_CLIENT_ID`, and
|
554
|
-
`YT_TEST_DEVICE_CLIENT_SECRET` respectively).
|
555
|
-
* Server application tests, which use a server API key
|
556
|
-
(`YT_TEST_SERVER_API_KEY`).
|
557
|
-
* Tests that excercise YouTube's partner functionality. This requires an
|
558
|
-
a partner channel id (`YT_TEST_CONTENT_OWNER_NAME`), a refresh token
|
559
|
-
that's authenticated with that channel
|
560
|
-
(`YT_TEST_CONTENT_OWNER_REFRESH_TOKEN`), and the corresponding
|
561
|
-
application (`YT_TEST_PARTNER_CLIENT_ID` and
|
562
|
-
(`YT_TEST_PARTNER_CLIENT_SECRET`).
|
563
|
-
|
564
|
-
The refresh tokens need to be generated with the `youtube`,
|
565
|
-
`yt-analytics` and `userinfo.profile` permissions in order for tests to
|
566
|
-
pass.
|
552
|
+
* `YT_TEST_CLIENT_ID`
|
553
|
+
* `YT_TEST_CLIENT_SECRET`
|
554
|
+
* `YT_TEST_API_KEY`
|
555
|
+
* `YT_TEST_REFRESH_TOKEN`
|
567
556
|
|
568
557
|
How to release new versions
|
569
558
|
===========================
|
data/YOUTUBE_IT.md
CHANGED
@@ -141,7 +141,7 @@ client = YouTubeIt::Client.new
|
|
141
141
|
client.videos_by(:query => "penguin", :author => "liz")
|
142
142
|
# with yt: the 'author' filter was removed from YouTube API V3, so the
|
143
143
|
# request must be done using the channel of the requested author
|
144
|
-
channel = Yt::Channel.new
|
144
|
+
channel = Yt::Channel.new url: 'youtube.com/liz'
|
145
145
|
channel.videos.where(q: 'penguin')
|
146
146
|
```
|
147
147
|
|
@@ -176,7 +176,7 @@ client = YouTubeIt::Client.new
|
|
176
176
|
client.videos_by(:user => 'liz')
|
177
177
|
# with yt: the 'author' filter was removed from YouTube API V3, so the
|
178
178
|
# request must be done using the channel of the requested author
|
179
|
-
channel = Yt::Channel.new
|
179
|
+
channel = Yt::Channel.new url: 'youtube.com/liz'
|
180
180
|
channel.videos.where(q: 'penguin')
|
181
181
|
```
|
182
182
|
|
@@ -188,7 +188,7 @@ client = YouTubeIt::Client.new
|
|
188
188
|
client.videos_by(:favorites, :user => 'liz')
|
189
189
|
# with yt: note that only *old* channels have a "Favorites" playlist, since
|
190
190
|
# "Favorites" has been deprecated by YouTube in favor of "Liked Videos".
|
191
|
-
channel = Yt::Channel.new
|
191
|
+
channel = Yt::Channel.new url: 'youtube.com/liz'
|
192
192
|
channel.related_playlists.find{|p| p.title == 'Favorites'}
|
193
193
|
```
|
194
194
|
|
@@ -832,4 +832,4 @@ TODO
|
|
832
832
|
|
833
833
|
$ client.activity(user) #default current user
|
834
834
|
|
835
|
-
-->
|
835
|
+
-->
|
data/lib/yt.rb
CHANGED
@@ -8,12 +8,12 @@ require 'yt/models/content_owner'
|
|
8
8
|
require 'yt/models/match_policy'
|
9
9
|
require 'yt/models/playlist'
|
10
10
|
require 'yt/models/playlist_item'
|
11
|
+
require 'yt/models/url'
|
11
12
|
require 'yt/models/video'
|
12
13
|
require 'yt/models/video_group'
|
13
14
|
require 'yt/models/comment_thread'
|
14
15
|
require 'yt/models/ownership'
|
15
16
|
require 'yt/models/advertising_options_set'
|
16
|
-
require 'yt/models/url'
|
17
17
|
|
18
18
|
# An object-oriented Ruby client for YouTube.
|
19
19
|
# Helps creating applications that need to interact with YouTube objects.
|
data/lib/yt/actions/list.rb
CHANGED
@@ -10,7 +10,7 @@ module Yt
|
|
10
10
|
# @option options [Array<Symbol>] :only The metrics to generate reports
|
11
11
|
# for.
|
12
12
|
# @option options [Symbol] :by (:day) The dimension to collect metrics
|
13
|
-
# by. Accepted values are: +:day+, +:
|
13
|
+
# by. Accepted values are: +:day+, +:month+.
|
14
14
|
# @option options [#to_date] :since The first day of the time-range.
|
15
15
|
# Also aliased as +:from+.
|
16
16
|
# @option options [#to_date] :until The last day of the time-range.
|
@@ -40,11 +40,6 @@ module Yt
|
|
40
40
|
# @example Get the $1 for this and last month:
|
41
41
|
# resource.$1 since: 1.month.ago, by: :month
|
42
42
|
# # => {Wed, 01 Apr 2014..Thu, 30 Apr 2014 => 12.0, Fri, 01 May 2014..Sun, 31 May 2014 => 34.0, …}
|
43
|
-
# @return [Hash<Range<Date, Date>, $2>] if grouped by week, the $1
|
44
|
-
# for each week in the time-range.
|
45
|
-
# @example Get the $1 for this and last week:
|
46
|
-
# resource.$1 since: 1.week.ago, by: :week
|
47
|
-
# # => {Wed, 01 Apr 2014..Tue, 07 Apr 2014 => 20.0, Wed, 08 Apr 2014..Tue, 14 Apr 2014 => 13.0, …}
|
48
43
|
# @macro report
|
49
44
|
|
50
45
|
# @!macro [new] report_with_range
|
@@ -75,19 +70,19 @@ module Yt
|
|
75
70
|
|
76
71
|
# @!macro [new] report_by_day
|
77
72
|
# @option options [Symbol] :by (:day) The dimension to collect $1 by.
|
78
|
-
# Accepted values are: +:day+, +:
|
73
|
+
# Accepted values are: +:day+, +:month+.
|
79
74
|
# @macro report_with_day
|
80
75
|
|
81
76
|
# @!macro [new] report_by_day_and_country
|
82
77
|
# @option options [Symbol] :by (:day) The dimension to collect $1 by.
|
83
|
-
# Accepted values are: +:day+, +:
|
78
|
+
# Accepted values are: +:day+, +:month+, :+range+.
|
84
79
|
# @macro report_with_day
|
85
80
|
# @macro report_with_range
|
86
81
|
# @macro report_with_country
|
87
82
|
|
88
83
|
# @!macro [new] report_by_day_and_state
|
89
84
|
# @option options [Symbol] :by (:day) The dimension to collect $1 by.
|
90
|
-
# Accepted values are: +:day+, +:
|
85
|
+
# Accepted values are: +:day+, +:month+, :+range+.
|
91
86
|
# @macro report_with_day
|
92
87
|
# @macro report_with_range
|
93
88
|
# @macro report_with_country_and_state
|
@@ -128,7 +123,7 @@ module Yt
|
|
128
123
|
|
129
124
|
# @!macro [new] report_by_video_dimensions
|
130
125
|
# @option options [Symbol] :by (:day) The dimension to collect $1 by.
|
131
|
-
# Accepted values are: +:day+, +:
|
126
|
+
# Accepted values are: +:day+, +:month+, +:range+,
|
132
127
|
# +:traffic_source+,+:search_term+, +:playback_location+,
|
133
128
|
# +:related_video+, +:embedded_player_location+.
|
134
129
|
# @option options [Array<Symbol>] :includes ([:id]) if grouped by
|
@@ -162,7 +157,7 @@ module Yt
|
|
162
157
|
|
163
158
|
# @!macro [new] report_by_channel_dimensions
|
164
159
|
# @option options [Symbol] :by (:day) The dimension to collect $1 by.
|
165
|
-
# Accepted values are: +:day+, +:
|
160
|
+
# Accepted values are: +:day+, +:month+, +:range+,
|
166
161
|
# +:traffic_source+, +:search_term+, +:playback_location+, +:video+,
|
167
162
|
# +:related_video+, +:playlist+, +:embedded_player_location+.
|
168
163
|
# @return [Hash<Symbol, $2>] if grouped by embedded player location,
|
@@ -175,7 +170,7 @@ module Yt
|
|
175
170
|
|
176
171
|
# @!macro [new] report_by_playlist_dimensions
|
177
172
|
# @option options [Symbol] :by (:day) The dimension to collect $1 by.
|
178
|
-
# Accepted values are: +:day+, +:
|
173
|
+
# Accepted values are: +:day+, +:month+, +:range+,
|
179
174
|
# +:traffic_source+, +:playback_location+, +:related_video+, +:video+,
|
180
175
|
# +:playlist+.
|
181
176
|
# @macro report_with_channel_dimensions
|
@@ -228,7 +223,7 @@ module Yt
|
|
228
223
|
def define_reports_method(metric, type)
|
229
224
|
(@metrics ||= {})[metric] = type
|
230
225
|
define_method :reports do |options = {}|
|
231
|
-
from = options[:since] || options[:from] || (options[:by].in?([:day, :
|
226
|
+
from = options[:since] || options[:from] || (options[:by].in?([:day, :month]) ? 5.days.ago : '2005-02-01')
|
232
227
|
to = options[:until] || options[:to] || Date.today
|
233
228
|
location = options[:in]
|
234
229
|
country = location.is_a?(Hash) ? location[:country] : location
|
@@ -252,7 +247,7 @@ module Yt
|
|
252
247
|
|
253
248
|
def define_metric_method(metric)
|
254
249
|
define_method metric do |options = {}|
|
255
|
-
from = options[:since] || options[:from] || (options[:by].in?([:day, :
|
250
|
+
from = options[:since] || options[:from] || (options[:by].in?([:day, :month]) ? 5.days.ago : '2005-02-01')
|
256
251
|
to = options[:until] || options[:to] || Date.today
|
257
252
|
location = options[:in]
|
258
253
|
country = location.is_a?(Hash) ? location[:country] : location
|
@@ -6,7 +6,6 @@ module Yt
|
|
6
6
|
class Reports < Base
|
7
7
|
DIMENSIONS = Hash.new({name: 'day', parse: ->(day, *values) { @metrics.keys.zip(values.map{|v| {Date.iso8601(day) => v}}).to_h} }).tap do |hash|
|
8
8
|
hash[:month] = {name: 'month', parse: ->(month, *values) { @metrics.keys.zip(values.map{|v| {Range.new(Date.strptime(month, '%Y-%m').beginning_of_month, Date.strptime(month, '%Y-%m').end_of_month) => v} }).to_h} }
|
9
|
-
hash[:week] = {name: '7DayTotals', parse: ->(last_day_of_week, *values) { @metrics.keys.zip(values.map{|v| {Range.new(Date.strptime(last_day_of_week) - 6, Date.strptime(last_day_of_week)) => v} }).to_h} }
|
10
9
|
hash[:range] = {parse: ->(*values) { @metrics.keys.zip(values.map{|v| {total: v}}).to_h } }
|
11
10
|
hash[:traffic_source] = {name: 'insightTrafficSourceType', parse: ->(source, *values) { @metrics.keys.zip(values.map{|v| {TRAFFIC_SOURCES.key(source) => v}}).to_h} }
|
12
11
|
hash[:playback_location] = {name: 'insightPlaybackLocationType', parse: ->(location, *values) { @metrics.keys.zip(values.map{|v| {PLAYBACK_LOCATIONS.key(location) => v}}).to_h} }
|
@@ -140,11 +139,6 @@ module Yt
|
|
140
139
|
end
|
141
140
|
if dimension == :month
|
142
141
|
hash = hash.transform_values{|h| h.sort_by{|range, v| range.first}.to_h}
|
143
|
-
elsif dimension == :week
|
144
|
-
hash = hash.transform_values do |h|
|
145
|
-
h.select{|range, v| range.last.wday == days_range.last.wday}.
|
146
|
-
sort_by{|range, v| range.first}.to_h
|
147
|
-
end
|
148
142
|
elsif dimension == :day
|
149
143
|
hash = hash.transform_values{|h| h.sort_by{|day, v| day}.to_h}
|
150
144
|
elsif dimension.in? [:traffic_source, :country, :state, :playback_location, :device_type, :operating_system, :subscribed_status]
|
@@ -159,11 +153,15 @@ module Yt
|
|
159
153
|
# same query is a workaround that works and can hardly cause any damage.
|
160
154
|
# Similarly, once in while YouTube responds with a random 503 error.
|
161
155
|
rescue Yt::Error => e
|
162
|
-
(max_retries > 0) && rescue?(e) ? sleep(
|
156
|
+
(max_retries > 0) && rescue?(e) ? sleep(retry_time) && within(days_range, country, state, dimension, videos, historical, max_retries - 1) : raise
|
163
157
|
end
|
164
158
|
|
165
159
|
private
|
166
160
|
|
161
|
+
def retry_time
|
162
|
+
3
|
163
|
+
end
|
164
|
+
|
167
165
|
def type_cast(value, type)
|
168
166
|
case [type]
|
169
167
|
when [Integer] then value.to_i if value
|
data/lib/yt/models/account.rb
CHANGED
@@ -67,13 +67,19 @@ module Yt
|
|
67
67
|
# @option params [String] :description The video’s description.
|
68
68
|
# @option params [Array<String>] :tags The video’s tags.
|
69
69
|
# @option params [String] :privacy_status The video’s privacy status.
|
70
|
+
# @option params [Boolean] :self_declared_made_for_kids The video’s made for kids self-declaration.
|
70
71
|
# @return [Yt::Models::Video] the newly uploaded video.
|
71
72
|
def upload_video(path_or_url, params = {})
|
72
73
|
file = open path_or_url, 'rb'
|
73
74
|
session = resumable_sessions.insert file.size, upload_body(params)
|
74
75
|
|
75
76
|
session.update(body: file) do |data|
|
76
|
-
Yt::Video.new
|
77
|
+
Yt::Video.new(
|
78
|
+
id: data['id'],
|
79
|
+
snippet: data['snippet'],
|
80
|
+
status: data['status'],
|
81
|
+
auth: self
|
82
|
+
)
|
77
83
|
end
|
78
84
|
end
|
79
85
|
|
@@ -217,8 +223,12 @@ module Yt
|
|
217
223
|
snippet[:categoryId] = snippet.delete(:category_id) if snippet[:category_id]
|
218
224
|
body[:snippet] = snippet if snippet.any?
|
219
225
|
|
220
|
-
|
221
|
-
|
226
|
+
privacy_status = params[:privacy_status]
|
227
|
+
self_declared_made_for_kids = params[:self_declared_made_for_kids]
|
228
|
+
|
229
|
+
body[:status] = {}
|
230
|
+
body[:status][:privacyStatus] = privacy_status if privacy_status
|
231
|
+
body[:status][:selfDeclaredMadeForKids] = self_declared_made_for_kids unless self_declared_made_for_kids.nil?
|
222
232
|
end
|
223
233
|
end
|
224
234
|
|
data/lib/yt/models/channel.rb
CHANGED
@@ -29,6 +29,25 @@ module Yt
|
|
29
29
|
# @return [Time] the date and time that the channel was created.
|
30
30
|
delegate :published_at, to: :snippet
|
31
31
|
|
32
|
+
### STATUS ###
|
33
|
+
|
34
|
+
# @!attribute [r] made_for_kids?
|
35
|
+
# @return [Boolean, nil] This value indicates whether the channel is
|
36
|
+
# designated as child-directed, and it contains the current "made for
|
37
|
+
# kids" status of the channel.
|
38
|
+
def made_for_kids?
|
39
|
+
status.made_for_kids
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!attribute [r] self_declared_made_for_kids?
|
43
|
+
# @return [Boolean, nil] In a channels.update request, this property
|
44
|
+
# allows the channel owner to designate the channel as
|
45
|
+
# child-directed. The property value is only returned if the channel
|
46
|
+
# owner authorized the API request.
|
47
|
+
def self_declared_made_for_kids?
|
48
|
+
status.self_declared_made_for_kids
|
49
|
+
end
|
50
|
+
|
32
51
|
### SUBSCRIPTION ###
|
33
52
|
|
34
53
|
has_one :subscription
|
@@ -197,10 +216,6 @@ module Yt
|
|
197
216
|
# @return [Integer] the number of times the channel has been viewed.
|
198
217
|
delegate :view_count, to: :statistics_set
|
199
218
|
|
200
|
-
# @!attribute [r] comment_count
|
201
|
-
# @return [Integer] the number of comments for the channel.
|
202
|
-
delegate :comment_count, to: :statistics_set
|
203
|
-
|
204
219
|
# @!attribute [r] video_count
|
205
220
|
# @return [Integer] the number of videos uploaded to the channel.
|
206
221
|
delegate :video_count, to: :statistics_set
|
data/lib/yt/models/resource.rb
CHANGED
@@ -12,7 +12,13 @@ module Yt
|
|
12
12
|
|
13
13
|
# @!attribute [r] id
|
14
14
|
# @return [String] the ID that YouTube uses to identify each resource.
|
15
|
-
|
15
|
+
def id
|
16
|
+
if @id.nil? && @match && @match[:kind] == :channel
|
17
|
+
@id ||= fetch_channel_id
|
18
|
+
else
|
19
|
+
@id
|
20
|
+
end
|
21
|
+
end
|
16
22
|
|
17
23
|
### STATUS ###
|
18
24
|
|
@@ -42,7 +48,13 @@ module Yt
|
|
42
48
|
|
43
49
|
# @private
|
44
50
|
def initialize(options = {})
|
45
|
-
|
51
|
+
if options[:url]
|
52
|
+
@url = options[:url]
|
53
|
+
@match = find_pattern_match
|
54
|
+
@id = @match['id']
|
55
|
+
else
|
56
|
+
@id = options[:id]
|
57
|
+
end
|
46
58
|
@auth = options[:auth]
|
47
59
|
@snippet = Snippet.new(data: options[:snippet]) if options[:snippet]
|
48
60
|
@status = Status.new(data: options[:status]) if options[:status]
|
@@ -50,7 +62,11 @@ module Yt
|
|
50
62
|
|
51
63
|
# @private
|
52
64
|
def kind
|
53
|
-
|
65
|
+
if @url
|
66
|
+
@match[:kind].to_s
|
67
|
+
else
|
68
|
+
self.class.to_s.demodulize.underscore
|
69
|
+
end
|
54
70
|
end
|
55
71
|
|
56
72
|
# @private
|
@@ -66,8 +82,63 @@ module Yt
|
|
66
82
|
end
|
67
83
|
end
|
68
84
|
|
85
|
+
# @return [Array<Regexp>] patterns matching URLs of YouTube playlists.
|
86
|
+
PLAYLIST_PATTERNS = [
|
87
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/playlist/?\?list=(?<id>[a-zA-Z0-9_-]+)},
|
88
|
+
]
|
89
|
+
|
90
|
+
# @return [Array<Regexp>] patterns matching URLs of YouTube videos.
|
91
|
+
VIDEO_PATTERNS = [
|
92
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/watch\?v=(?<id>[a-zA-Z0-9_-]{11})},
|
93
|
+
%r{^(?:https?://)?(?:www\.)?youtu\.be/(?<id>[a-zA-Z0-9_-]{11})},
|
94
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/embed/(?<id>[a-zA-Z0-9_-]{11})},
|
95
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/v/(?<id>[a-zA-Z0-9_-]{11})},
|
96
|
+
]
|
97
|
+
|
98
|
+
# @return [Array<Regexp>] patterns matching URLs of YouTube channels.
|
99
|
+
CHANNEL_PATTERNS = [
|
100
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/channel/(?<id>UC[a-zA-Z0-9_-]{22})},
|
101
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/(?<format>c/|user/)?(?<name>[a-zA-Z0-9_-]+)}
|
102
|
+
]
|
103
|
+
|
69
104
|
private
|
70
105
|
|
106
|
+
def find_pattern_match
|
107
|
+
patterns.find do |kind, regex|
|
108
|
+
if data = @url.match(regex)
|
109
|
+
# Note: With Ruby 2.4, the following is data.named_captures
|
110
|
+
break data.names.zip(data.captures).to_h.merge kind: kind
|
111
|
+
end
|
112
|
+
end || {kind: :unknown}
|
113
|
+
end
|
114
|
+
|
115
|
+
def patterns
|
116
|
+
# @note: :channel *must* be the last since one of its regex eats the
|
117
|
+
# remaining patterns. In short, don't change the following order.
|
118
|
+
Enumerator.new do |patterns|
|
119
|
+
VIDEO_PATTERNS.each {|regex| patterns << [:video, regex]}
|
120
|
+
PLAYLIST_PATTERNS.each {|regex| patterns << [:playlist, regex]}
|
121
|
+
CHANNEL_PATTERNS.each {|regex| patterns << [:channel, regex]}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def fetch_channel_id
|
126
|
+
response = Net::HTTP.start 'www.youtube.com', 443, use_ssl: true do |http|
|
127
|
+
http.request Net::HTTP::Get.new("/#{@match['format']}#{@match['name']}")
|
128
|
+
end
|
129
|
+
if response.is_a?(Net::HTTPRedirection)
|
130
|
+
response = Net::HTTP.start 'www.youtube.com', 443, use_ssl: true do |http|
|
131
|
+
http.request Net::HTTP::Get.new(response['location'])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
regex = %r{<meta itemprop="channelId" content="(?<id>UC[a-zA-Z0-9_-]{22})">}
|
135
|
+
if data = response.body.match(regex)
|
136
|
+
data[:id]
|
137
|
+
else
|
138
|
+
raise Yt::Errors::NoItems
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
71
142
|
# Since YouTube API only returns tags on Videos#list, the memoized
|
72
143
|
# `@snippet` is erased if the video was instantiated through Video#search
|
73
144
|
# (e.g., by calling account.videos or channel.videos), so that the full
|