yt 0.32.6 → 0.33.0
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 +19 -0
- data/README.md +22 -32
- data/YOUTUBE_IT.md +4 -4
- data/lib/yt.rb +0 -1
- data/lib/yt/associations/has_reports.rb +9 -14
- data/lib/yt/collections/reports.rb +5 -7
- data/lib/yt/models/resource.rb +69 -3
- data/lib/yt/models/url.rb +2 -60
- data/lib/yt/request.rb +6 -2
- data/lib/yt/version.rb +1 -1
- data/yt.gemspec +5 -2
- metadata +31 -169
- 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: 7f3d794d74e724d49a7a96cf3d27dcfbeabcc0c42a7a9766deaae7e01be367c0
|
4
|
+
data.tar.gz: bf836aa2984cb6c6903881d56a6f60aad68504a5cb7013c276e6466ee075cf71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74662b9a898592295377a2e3bd51295682a67d07c773abc27945706c858860e74ce4ce4c77ca20455a674a746090603ef49261bce21b40418984430f93f5d18c
|
7
|
+
data.tar.gz: 74f82bc61b9bde908766aa6c1e19afe7da9fbeb42faeead0a37d2cc31def646b71f333c0e3dde43589027b28cf3e781d04d1824b58be7db99af3b31914debc0d
|
data/.rspec
CHANGED
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.33.0 - 2020-04-10
|
10
|
+
|
11
|
+
If your code calls reports methods such as `views`, `likes`, or `reports`,
|
12
|
+
do not include `by: :week` option since `7DayTotals` dimension will no longer be
|
13
|
+
supported by YouTube API as of [April 15, 2020](https://developers.google.com/youtube/analytics/revision_history#october-15,-2019).
|
14
|
+
|
15
|
+
Use `by: :day` option instead and add up the numbers from each day.
|
16
|
+
|
17
|
+
If you keep using `by: :week` option after this change it will raise an error
|
18
|
+
(before the gem upgrade) or it will run with `day` dimension instead (after
|
19
|
+
the gem upgrade, like any other random input).
|
20
|
+
|
21
|
+
* [REMOVAL] Remove `by: :week` option for reports.
|
22
|
+
* [FEATURE] Add back the option of initializing a resource by its URL.
|
23
|
+
* [BUGFIX] Limit retries on refreshing tokens
|
24
|
+
|
25
|
+
**Breaking change**
|
26
|
+
|
27
|
+
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
28
|
|
10
29
|
## 0.32.6 - 2020-02-07
|
11
30
|
|
data/README.md
CHANGED
@@ -526,44 +526,34 @@ If a variable is set in both places, then `Yt.configure` takes precedence.
|
|
526
526
|
How to test
|
527
527
|
===========
|
528
528
|
|
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:
|
529
|
+
To run tests:
|
535
530
|
|
536
531
|
```bash
|
537
|
-
rspec
|
532
|
+
rspec
|
538
533
|
```
|
539
534
|
|
540
|
-
|
535
|
+
We recommend RSpec >= 3.8.
|
541
536
|
|
542
|
-
|
543
|
-
|
544
|
-
|
537
|
+
Yt comes with two different sets of tests:
|
538
|
+
|
539
|
+
1. Unit tests in `spec/models`, `spec/collections` and `spec/errors`
|
540
|
+
2. Legacy integration tests in `spec/requests`
|
541
|
+
|
542
|
+
Coming soon will be a new set of high-level integration tests.
|
543
|
+
|
544
|
+
Integration tests are recorded with VCR. Some of the tests refer to
|
545
|
+
fixture data that an arbitrary account may not have access to. If you
|
546
|
+
need to modify one of these tests or re-record the cassette, we'd
|
547
|
+
suggest working against your own version of the testing setup. Then in
|
548
|
+
your pull request, we can help canonize your test/fixtures.
|
549
|
+
|
550
|
+
Some of the integration tests require authentication. These can be set
|
551
|
+
with the following environment variables:
|
545
552
|
|
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.
|
553
|
+
* `YT_TEST_CLIENT_ID`
|
554
|
+
* `YT_TEST_CLIENT_SECRET`
|
555
|
+
* `YT_TEST_API_KEY`
|
556
|
+
* `YT_TEST_REFRESH_TOKEN`
|
567
557
|
|
568
558
|
How to release new versions
|
569
559
|
===========================
|
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
@@ -13,7 +13,6 @@ require 'yt/models/video_group'
|
|
13
13
|
require 'yt/models/comment_thread'
|
14
14
|
require 'yt/models/ownership'
|
15
15
|
require 'yt/models/advertising_options_set'
|
16
|
-
require 'yt/models/url'
|
17
16
|
|
18
17
|
# An object-oriented Ruby client for YouTube.
|
19
18
|
# Helps creating applications that need to interact with YouTube objects.
|
@@ -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/resource.rb
CHANGED
@@ -12,7 +12,9 @@ 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
|
+
@id ||= @match['id'] || fetch_channel_id
|
17
|
+
end
|
16
18
|
|
17
19
|
### STATUS ###
|
18
20
|
|
@@ -42,7 +44,12 @@ module Yt
|
|
42
44
|
|
43
45
|
# @private
|
44
46
|
def initialize(options = {})
|
45
|
-
|
47
|
+
if options[:url]
|
48
|
+
@url = options[:url]
|
49
|
+
@match = find_pattern_match
|
50
|
+
else
|
51
|
+
@id = options[:id]
|
52
|
+
end
|
46
53
|
@auth = options[:auth]
|
47
54
|
@snippet = Snippet.new(data: options[:snippet]) if options[:snippet]
|
48
55
|
@status = Status.new(data: options[:status]) if options[:status]
|
@@ -50,7 +57,11 @@ module Yt
|
|
50
57
|
|
51
58
|
# @private
|
52
59
|
def kind
|
53
|
-
|
60
|
+
if @url
|
61
|
+
@match[:kind].to_s
|
62
|
+
else
|
63
|
+
self.class.to_s.demodulize.underscore
|
64
|
+
end
|
54
65
|
end
|
55
66
|
|
56
67
|
# @private
|
@@ -66,8 +77,63 @@ module Yt
|
|
66
77
|
end
|
67
78
|
end
|
68
79
|
|
80
|
+
# @return [Array<Regexp>] patterns matching URLs of YouTube playlists.
|
81
|
+
PLAYLIST_PATTERNS = [
|
82
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/playlist/?\?list=(?<id>[a-zA-Z0-9_-]+)},
|
83
|
+
]
|
84
|
+
|
85
|
+
# @return [Array<Regexp>] patterns matching URLs of YouTube videos.
|
86
|
+
VIDEO_PATTERNS = [
|
87
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/watch\?v=(?<id>[a-zA-Z0-9_-]{11})},
|
88
|
+
%r{^(?:https?://)?(?:www\.)?youtu\.be/(?<id>[a-zA-Z0-9_-]{11})},
|
89
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/embed/(?<id>[a-zA-Z0-9_-]{11})},
|
90
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/v/(?<id>[a-zA-Z0-9_-]{11})},
|
91
|
+
]
|
92
|
+
|
93
|
+
# @return [Array<Regexp>] patterns matching URLs of YouTube channels.
|
94
|
+
CHANNEL_PATTERNS = [
|
95
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/channel/(?<id>UC[a-zA-Z0-9_-]{22})},
|
96
|
+
%r{^(?:https?://)?(?:www\.)?youtube\.com/(?<format>c/|user/)?(?<name>[a-zA-Z0-9_-]+)}
|
97
|
+
]
|
98
|
+
|
69
99
|
private
|
70
100
|
|
101
|
+
def find_pattern_match
|
102
|
+
patterns.find do |kind, regex|
|
103
|
+
if data = @url.match(regex)
|
104
|
+
# Note: With Ruby 2.4, the following is data.named_captures
|
105
|
+
break data.names.zip(data.captures).to_h.merge kind: kind
|
106
|
+
end
|
107
|
+
end || {kind: :unknown}
|
108
|
+
end
|
109
|
+
|
110
|
+
def patterns
|
111
|
+
# @note: :channel *must* be the last since one of its regex eats the
|
112
|
+
# remaining patterns. In short, don't change the following order.
|
113
|
+
Enumerator.new do |patterns|
|
114
|
+
VIDEO_PATTERNS.each {|regex| patterns << [:video, regex]}
|
115
|
+
PLAYLIST_PATTERNS.each {|regex| patterns << [:playlist, regex]}
|
116
|
+
CHANNEL_PATTERNS.each {|regex| patterns << [:channel, regex]}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def fetch_channel_id
|
121
|
+
response = Net::HTTP.start 'www.youtube.com', 443, use_ssl: true do |http|
|
122
|
+
http.request Net::HTTP::Get.new("/#{@match['format']}#{@match['name']}")
|
123
|
+
end
|
124
|
+
if response.is_a?(Net::HTTPRedirection)
|
125
|
+
response = Net::HTTP.start 'www.youtube.com', 443, use_ssl: true do |http|
|
126
|
+
http.request Net::HTTP::Get.new(response['location'])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
regex = %r{<meta itemprop="channelId" content="(?<id>UC[a-zA-Z0-9_-]{22})">}
|
130
|
+
if data = response.body.match(regex)
|
131
|
+
data[:id]
|
132
|
+
else
|
133
|
+
raise Yt::Errors::NoItems
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
71
137
|
# Since YouTube API only returns tags on Videos#list, the memoized
|
72
138
|
# `@snippet` is erased if the video was instantiated through Video#search
|
73
139
|
# (e.g., by calling account.videos or channel.videos), so that the full
|
data/lib/yt/models/url.rb
CHANGED
@@ -14,18 +14,17 @@ module Yt
|
|
14
14
|
# @param [String] text the name or URL of a YouTube resource (in any form).
|
15
15
|
def initialize(text)
|
16
16
|
@text = text.to_s.strip
|
17
|
-
@match = find_pattern_match
|
18
17
|
end
|
19
18
|
|
20
19
|
# @return [Symbol] the kind of YouTube resource matching the URL.
|
21
20
|
# Possible values are: +:playlist+, +:video+, +:channel+, and +:unknown:.
|
22
21
|
def kind
|
23
|
-
@
|
22
|
+
Resource.new(url: @text).kind.to_sym
|
24
23
|
end
|
25
24
|
|
26
25
|
# @return [<String, nil>] the ID of the YouTube resource matching the URL.
|
27
26
|
def id
|
28
|
-
@
|
27
|
+
Resource.new(url: @text).id
|
29
28
|
end
|
30
29
|
|
31
30
|
# @return [<Yt::Channel>] the resource associated with the URL
|
@@ -37,63 +36,6 @@ module Yt
|
|
37
36
|
else raise Yt::Errors::NoItems
|
38
37
|
end.new options.merge(id: id)
|
39
38
|
end
|
40
|
-
|
41
|
-
# @return [Array<Regexp>] patterns matching URLs of YouTube playlists.
|
42
|
-
PLAYLIST_PATTERNS = [
|
43
|
-
%r{^(?:https?://)?(?:www\.)?youtube\.com/playlist/?\?list=(?<id>[a-zA-Z0-9_-]+)},
|
44
|
-
]
|
45
|
-
|
46
|
-
# @return [Array<Regexp>] patterns matching URLs of YouTube videos.
|
47
|
-
VIDEO_PATTERNS = [
|
48
|
-
%r{^(?:https?://)?(?:www\.)?youtube\.com/watch\?v=(?<id>[a-zA-Z0-9_-]{11})},
|
49
|
-
%r{^(?:https?://)?(?:www\.)?youtu\.be/(?<id>[a-zA-Z0-9_-]{11})},
|
50
|
-
%r{^(?:https?://)?(?:www\.)?youtube\.com/embed/(?<id>[a-zA-Z0-9_-]{11})},
|
51
|
-
%r{^(?:https?://)?(?:www\.)?youtube\.com/v/(?<id>[a-zA-Z0-9_-]{11})},
|
52
|
-
]
|
53
|
-
|
54
|
-
# @return [Array<Regexp>] patterns matching URLs of YouTube channels.
|
55
|
-
CHANNEL_PATTERNS = [
|
56
|
-
%r{^(?:https?://)?(?:www\.)?youtube\.com/channel/(?<id>UC[a-zA-Z0-9_-]{22})},
|
57
|
-
%r{^(?:https?://)?(?:www\.)?youtube\.com/(?<format>c/|user/)?(?<name>[a-zA-Z0-9_-]+)}
|
58
|
-
]
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def find_pattern_match
|
63
|
-
patterns.find(-> {{kind: :unknown}}) do |kind, regex|
|
64
|
-
if data = @text.match(regex)
|
65
|
-
# Note: With Ruby 2.4, the following is data.named_captures
|
66
|
-
break data.names.zip(data.captures).to_h.merge kind: kind
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def patterns
|
72
|
-
# @note: :channel *must* be the last since one of its regex eats the
|
73
|
-
# remaining patterns. In short, don't change the following order.
|
74
|
-
Enumerator.new do |patterns|
|
75
|
-
VIDEO_PATTERNS.each {|regex| patterns << [:video, regex]}
|
76
|
-
PLAYLIST_PATTERNS.each {|regex| patterns << [:playlist, regex]}
|
77
|
-
CHANNEL_PATTERNS.each {|regex| patterns << [:channel, regex]}
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def fetch_id
|
82
|
-
response = Net::HTTP.start 'www.youtube.com', 443, use_ssl: true do |http|
|
83
|
-
http.request Net::HTTP::Get.new("/#{@match['format']}#{@match['name']}")
|
84
|
-
end
|
85
|
-
if response.is_a?(Net::HTTPRedirection)
|
86
|
-
response = Net::HTTP.start 'www.youtube.com', 443, use_ssl: true do |http|
|
87
|
-
http.request Net::HTTP::Get.new(response['location'])
|
88
|
-
end
|
89
|
-
end
|
90
|
-
regex = %r{<meta itemprop="channelId" content="(?<id>UC[a-zA-Z0-9_-]{22})">}
|
91
|
-
if data = response.body.match(regex)
|
92
|
-
data[:id]
|
93
|
-
else
|
94
|
-
raise Yt::Errors::NoItems
|
95
|
-
end
|
96
|
-
end
|
97
39
|
end
|
98
40
|
end
|
99
41
|
end
|