yt 0.11.6 → 0.12.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 +35 -0
- data/README.md +13 -2
- data/lib/yt/actions/list.rb +11 -1
- data/lib/yt/associations/has_attribute.rb +51 -0
- data/lib/yt/associations/has_many.rb +0 -15
- data/lib/yt/collections/content_owners.rb +0 -4
- data/lib/yt/collections/playlist_items.rb +1 -1
- data/lib/yt/collections/resources.rb +1 -1
- data/lib/yt/collections/subscribed_channels.rb +46 -0
- data/lib/yt/collections/subscribers.rb +33 -0
- data/lib/yt/collections/subscriptions.rb +0 -23
- data/lib/yt/models/account.rb +6 -1
- data/lib/yt/models/annotation.rb +4 -10
- data/lib/yt/models/asset.rb +3 -10
- data/lib/yt/models/base.rb +2 -0
- data/lib/yt/models/channel.rb +53 -25
- data/lib/yt/models/claim.rb +13 -25
- data/lib/yt/models/content_detail.rb +9 -11
- data/lib/yt/models/content_owner_detail.rb +2 -8
- data/lib/yt/models/device_flow.rb +3 -11
- data/lib/yt/models/live_streaming_detail.rb +5 -25
- data/lib/yt/models/ownership.rb +8 -8
- data/lib/yt/models/playlist.rb +14 -10
- data/lib/yt/models/playlist_item.rb +13 -0
- data/lib/yt/models/policy.rb +10 -14
- data/lib/yt/models/policy_rule.rb +15 -21
- data/lib/yt/models/reference.rb +13 -37
- data/lib/yt/models/right_owner.rb +6 -17
- data/lib/yt/models/snippet.rb +19 -33
- data/lib/yt/models/statistics_set.rb +9 -23
- data/lib/yt/models/status.rb +20 -25
- data/lib/yt/models/subscription.rb +2 -8
- data/lib/yt/models/user_info.rb +11 -33
- data/lib/yt/version.rb +1 -1
- data/spec/collections/subscriptions_spec.rb +0 -7
- data/spec/errors/forbidden_spec.rb +10 -0
- data/spec/errors/server_error_spec.rb +10 -0
- data/spec/models/subscription_spec.rb +0 -9
- data/spec/requests/as_account/account_spec.rb +8 -0
- data/spec/requests/as_account/channel_spec.rb +40 -6
- data/spec/requests/as_account/playlist_item_spec.rb +26 -0
- data/spec/requests/as_account/playlist_spec.rb +1 -0
- data/spec/requests/as_server_app/channel_spec.rb +9 -0
- metadata +9 -2
data/lib/yt/models/base.rb
CHANGED
@@ -2,6 +2,7 @@ require 'yt/actions/delete'
|
|
2
2
|
require 'yt/actions/update'
|
3
3
|
require 'yt/actions/patch'
|
4
4
|
|
5
|
+
require 'yt/associations/has_attribute'
|
5
6
|
require 'yt/associations/has_authentication'
|
6
7
|
require 'yt/associations/has_many'
|
7
8
|
require 'yt/associations/has_one'
|
@@ -17,6 +18,7 @@ module Yt
|
|
17
18
|
include Actions::Update
|
18
19
|
include Actions::Patch
|
19
20
|
|
21
|
+
include Associations::HasAttribute
|
20
22
|
extend Associations::HasReports
|
21
23
|
extend Associations::HasViewerPercentages
|
22
24
|
extend Associations::HasOne
|
data/lib/yt/models/channel.rb
CHANGED
@@ -5,10 +5,6 @@ module Yt
|
|
5
5
|
# A channel resource contains information about a YouTube channel.
|
6
6
|
# @see https://developers.google.com/youtube/v3/docs/channels
|
7
7
|
class Channel < Resource
|
8
|
-
# @!attribute [r] subscriptions
|
9
|
-
# @return [Yt::Collections::Subscriptions] the channel’s subscriptions.
|
10
|
-
has_many :subscriptions
|
11
|
-
|
12
8
|
# @!attribute [r] videos
|
13
9
|
# @return [Yt::Collections::Videos] the channel’s videos.
|
14
10
|
has_many :videos
|
@@ -53,6 +49,20 @@ module Yt
|
|
53
49
|
has_one :content_owner_detail
|
54
50
|
delegate :content_owner, :linked_at, to: :content_owner_detail
|
55
51
|
|
52
|
+
# @!attribute [r] subscribed_channels
|
53
|
+
# @return [Yt::Collections::SubscribedChannels] the channels that the channel is subscribed to.
|
54
|
+
# @raise [Yt::Errors::Forbidden] if the owner of the channel has
|
55
|
+
# explicitly select the option to keep all subscriptions private.
|
56
|
+
has_many :subscribed_channels
|
57
|
+
|
58
|
+
# @!attribute [r] subscription
|
59
|
+
# @return [Yt::Models::Subscription] the channel’s subscription by auth.
|
60
|
+
# @raise [Yt::Errors::NoItems] if {Resource#auth auth} is not
|
61
|
+
# subscribed to the channel.
|
62
|
+
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
63
|
+
# return an authenticated account.
|
64
|
+
has_one :subscription
|
65
|
+
|
56
66
|
# Returns whether the authenticated account is subscribed to the channel.
|
57
67
|
#
|
58
68
|
# This method requires {Resource#auth auth} to return an
|
@@ -61,55 +71,64 @@ module Yt
|
|
61
71
|
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
62
72
|
# return an authenticated account.
|
63
73
|
def subscribed?
|
64
|
-
|
74
|
+
sleep [(@subscriptions_updated_at || Time.now) - Time.now, 0].max
|
75
|
+
subscription.exists?
|
76
|
+
rescue Errors::NoItems
|
77
|
+
false
|
65
78
|
end
|
66
79
|
|
67
|
-
#
|
68
|
-
#
|
80
|
+
# Unsubscribes the authenticated account from the channel.
|
81
|
+
# Raises an error if the account was not subscribed.
|
69
82
|
#
|
70
83
|
# This method requires {Resource#auth auth} to return an
|
71
84
|
# authenticated instance of {Yt::Account}.
|
85
|
+
# @raise [Yt::Errors::RequestError] if {Resource#auth auth} was not
|
86
|
+
# subscribed to the channel.
|
72
87
|
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
73
88
|
# return an authenticated account.
|
74
|
-
def
|
75
|
-
|
89
|
+
def unsubscribe!
|
90
|
+
subscription.delete.tap{ throttle_subscriptions }
|
76
91
|
end
|
77
92
|
|
78
|
-
#
|
79
|
-
#
|
93
|
+
# Unsubscribes the authenticated account from the channel.
|
94
|
+
# Does not raise an error if the account was not subscribed.
|
80
95
|
#
|
81
96
|
# This method requires {Resource#auth auth} to return an
|
82
97
|
# authenticated instance of {Yt::Account}.
|
83
|
-
# @raise [Yt::Errors::RequestError] if {Resource#auth auth} was already
|
84
|
-
# subscribed to the channel.
|
85
98
|
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
86
99
|
# return an authenticated account.
|
87
|
-
def
|
88
|
-
|
100
|
+
def unsubscribe
|
101
|
+
unsubscribe! if subscribed?
|
89
102
|
end
|
90
103
|
|
91
|
-
#
|
92
|
-
#
|
104
|
+
# Subscribes the authenticated account to the channel.
|
105
|
+
# Raises an error if the account was already subscribed.
|
93
106
|
#
|
94
107
|
# This method requires {Resource#auth auth} to return an
|
95
108
|
# authenticated instance of {Yt::Account}.
|
109
|
+
# @raise [Yt::Errors::RequestError] if {Resource#auth auth} was already
|
110
|
+
# subscribed to the channel.
|
96
111
|
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
97
112
|
# return an authenticated account.
|
98
|
-
def
|
99
|
-
subscriptions.
|
113
|
+
def subscribe!
|
114
|
+
subscriptions.insert.tap do |subscription|
|
115
|
+
throttle_subscriptions
|
116
|
+
@subscription = subscription
|
117
|
+
end
|
100
118
|
end
|
101
119
|
|
102
|
-
#
|
103
|
-
#
|
120
|
+
# Subscribes the authenticated account to the channel.
|
121
|
+
# Does not raise an error if the account was already subscribed.
|
104
122
|
#
|
105
123
|
# This method requires {Resource#auth auth} to return an
|
106
124
|
# authenticated instance of {Yt::Account}.
|
107
|
-
# @raise [Yt::Errors::RequestError] if {Resource#auth auth} was not
|
108
|
-
# subscribed to the channel.
|
109
125
|
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
110
126
|
# return an authenticated account.
|
111
|
-
def
|
112
|
-
subscriptions.
|
127
|
+
def subscribe
|
128
|
+
subscriptions.insert(ignore_errors: true).tap do |subscription|
|
129
|
+
throttle_subscriptions
|
130
|
+
@subscription = subscription
|
131
|
+
end
|
113
132
|
end
|
114
133
|
|
115
134
|
def create_playlist(params = {})
|
@@ -150,6 +169,15 @@ module Yt
|
|
150
169
|
def content_owner_details_params
|
151
170
|
{on_behalf_of_content_owner: auth.owner_name || auth.id}
|
152
171
|
end
|
172
|
+
|
173
|
+
# @private
|
174
|
+
# @note Google API must have some caching layer by which if we try to
|
175
|
+
# delete a subscription that we just created, we encounter an error.
|
176
|
+
# To overcome this, if we have just updated the subscription, we must
|
177
|
+
# wait some time before requesting it again.
|
178
|
+
def throttle_subscriptions(seconds = 10)
|
179
|
+
@subscriptions_updated_at = Time.now + seconds
|
180
|
+
end
|
153
181
|
end
|
154
182
|
end
|
155
183
|
end
|
data/lib/yt/models/claim.rb
CHANGED
@@ -24,21 +24,15 @@ module Yt
|
|
24
24
|
|
25
25
|
# @return [String] the ID that YouTube assigns and uses to uniquely
|
26
26
|
# identify the claim.
|
27
|
-
|
28
|
-
@id ||= @data['id']
|
29
|
-
end
|
27
|
+
has_attribute :id
|
30
28
|
|
31
29
|
# @return [String] the unique YouTube asset ID that identifies the asset
|
32
30
|
# associated with the claim.
|
33
|
-
|
34
|
-
@asset_id ||= @data["assetId"]
|
35
|
-
end
|
31
|
+
has_attribute :asset_id
|
36
32
|
|
37
33
|
# @return [String] the unique YouTube video ID that identifies the video
|
38
34
|
# associated with the claim.
|
39
|
-
|
40
|
-
@video_id ||= @data["videoId"]
|
41
|
-
end
|
35
|
+
has_attribute :video_id
|
42
36
|
|
43
37
|
# Status
|
44
38
|
|
@@ -50,9 +44,7 @@ module Yt
|
|
50
44
|
# @note When updating a claim, you can update its status from active to
|
51
45
|
# inactive to effectively release the claim, but the API does not
|
52
46
|
# support other updates to a claim’s status.
|
53
|
-
|
54
|
-
@status ||= @data["status"]
|
55
|
-
end
|
47
|
+
has_attribute :status
|
56
48
|
|
57
49
|
# @return [Boolean] whether the claim is active.
|
58
50
|
def active?
|
@@ -101,9 +93,7 @@ module Yt
|
|
101
93
|
# @return [String] whether the claim covers the audio, video, or
|
102
94
|
# audiovisual portion of the claimed content. Valid values are: audio,
|
103
95
|
# audiovisual, video.
|
104
|
-
|
105
|
-
@content_type ||= @data["contentType"]
|
106
|
-
end
|
96
|
+
has_attribute :content_type
|
107
97
|
|
108
98
|
# @return [Boolean] whether the covers the audio of the content.
|
109
99
|
def audio?
|
@@ -121,13 +111,11 @@ module Yt
|
|
121
111
|
end
|
122
112
|
|
123
113
|
# @return [Time] the date and time that the claim was created.
|
124
|
-
|
125
|
-
@created_at ||= Time.parse @data["timeCreated"]
|
126
|
-
end
|
114
|
+
has_attribute :created_at, type: Time, from: :time_created
|
127
115
|
|
128
116
|
# @return [Boolean] whether a third party created the claim.
|
129
|
-
|
130
|
-
|
117
|
+
has_attribute :third_party?, from: :third_party_claim do |value|
|
118
|
+
value == true
|
131
119
|
end
|
132
120
|
|
133
121
|
# Return whether the video should be blocked where not explicitly owned.
|
@@ -141,16 +129,16 @@ module Yt
|
|
141
129
|
# though it will not be monetized in those countries. However, if you
|
142
130
|
# set this property to true, then the video will be monetized in the
|
143
131
|
# United States and Canada and blocked in all other countries.
|
144
|
-
|
145
|
-
@block_outside_ownership ||= @data["blockOutsideOwnership"]
|
146
|
-
end
|
132
|
+
has_attribute :block_outside_ownership?, from: :block_outside_ownership
|
147
133
|
|
148
134
|
# @return [String] The unique ID that YouTube uses to identify the
|
149
135
|
# reference that generated the match.
|
150
|
-
|
151
|
-
|
136
|
+
has_attribute :match_reference_id, from: :match_info do |match_info|
|
137
|
+
(match_info || {})['referenceId']
|
152
138
|
end
|
153
139
|
|
140
|
+
private
|
141
|
+
|
154
142
|
# @see https://developers.google.com/youtube/partner/docs/v1/claims/update
|
155
143
|
def patch_params
|
156
144
|
super.tap do |params|
|
@@ -12,30 +12,28 @@ module Yt
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# @return [Integer] the duration of the video (in seconds).
|
15
|
-
|
16
|
-
|
15
|
+
has_attribute :duration, default: 0 do |value|
|
16
|
+
to_seconds value
|
17
17
|
end
|
18
18
|
|
19
19
|
# @return [Boolean] whether the video is available in 3D.
|
20
|
-
|
21
|
-
|
20
|
+
has_attribute :stereoscopic?, from: :dimension do |dimension|
|
21
|
+
dimension == '3d'
|
22
22
|
end
|
23
23
|
|
24
24
|
# @return [Boolean] whether the video is available in high definition.
|
25
|
-
|
26
|
-
|
25
|
+
has_attribute :hd?, from: :definition do |definition|
|
26
|
+
definition == 'hd'
|
27
27
|
end
|
28
28
|
|
29
29
|
# @return [Boolean] whether captions are available for the video.
|
30
|
-
|
31
|
-
|
30
|
+
has_attribute :captioned?, from: :caption do |caption|
|
31
|
+
caption == 'true'
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Boolean] whether the video represents licensed content, which
|
35
35
|
# means that the content has been claimed by a YouTube content partner.
|
36
|
-
|
37
|
-
@licensed ||= @data.fetch 'licensedContent', false
|
38
|
-
end
|
36
|
+
has_attribute :licensed?, default: false, from: :licensed_content
|
39
37
|
|
40
38
|
private
|
41
39
|
|
@@ -21,9 +21,7 @@ module Yt
|
|
21
21
|
# permissions to administer the channel.
|
22
22
|
# @raise [Yt::Errors::Forbidden] if {Resource#auth auth} does not
|
23
23
|
# return an authenticated content owner.
|
24
|
-
|
25
|
-
@content_owner ||= @data['contentOwner']
|
26
|
-
end
|
24
|
+
has_attribute :content_owner
|
27
25
|
|
28
26
|
# Returns the date and time of when the channel was linked to the content
|
29
27
|
# owner.
|
@@ -37,11 +35,7 @@ module Yt
|
|
37
35
|
# permissions to administer the channel.
|
38
36
|
# @raise [Yt::Errors::Forbidden] if {Resource#auth auth} does not
|
39
37
|
# return an authenticated content owner.
|
40
|
-
|
41
|
-
@linked_at ||= if @data['timeLinked']
|
42
|
-
Time.parse @data['timeLinked']
|
43
|
-
end
|
44
|
-
end
|
38
|
+
has_attribute :linked_at, type: Time, from: :time_linked
|
45
39
|
end
|
46
40
|
end
|
47
41
|
end
|
@@ -7,17 +7,9 @@ module Yt
|
|
7
7
|
@data = options[:data]
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def user_code
|
15
|
-
@user_code ||= @data['user_code']
|
16
|
-
end
|
17
|
-
|
18
|
-
def verification_url
|
19
|
-
@verification_url ||= @data['verification_url']
|
20
|
-
end
|
10
|
+
has_attribute :device_code, camelize: false
|
11
|
+
has_attribute :user_code, camelize: false
|
12
|
+
has_attribute :verification_url, camelize: false
|
21
13
|
end
|
22
14
|
end
|
23
15
|
end
|
@@ -15,47 +15,27 @@ module Yt
|
|
15
15
|
# @return [Time] if the broadcast has begun, the time that the broadcast
|
16
16
|
# actually started.
|
17
17
|
# @return [nil] if the broadcast has not begun.
|
18
|
-
|
19
|
-
@actual_start_time ||= if @data['actualStartTime']
|
20
|
-
Time.parse @data['actualStartTime']
|
21
|
-
end
|
22
|
-
end
|
18
|
+
has_attribute :actual_start_time, type: Time
|
23
19
|
|
24
20
|
# @return [Time] if the broadcast is over, the time that the broadcast
|
25
21
|
# actually ended.
|
26
22
|
# @return [nil] if the broadcast is not over.
|
27
|
-
|
28
|
-
@actual_end_time ||= if @data['actualEndTime']
|
29
|
-
Time.parse @data['actualEndTime']
|
30
|
-
end
|
31
|
-
end
|
23
|
+
has_attribute :actual_end_time, type: Time
|
32
24
|
|
33
25
|
# @return [Time] the time that the broadcast is scheduled to begin.
|
34
|
-
|
35
|
-
@scheduled_start_time ||= if @data['scheduledStartTime']
|
36
|
-
Time.parse @data['scheduledStartTime']
|
37
|
-
end
|
38
|
-
end
|
26
|
+
has_attribute :scheduled_start_time, type: Time
|
39
27
|
|
40
28
|
# @return [Time] if the broadcast is scheduled to end, the time that the
|
41
29
|
# broadcast is scheduled to end.
|
42
30
|
# @return [nil] if the broadcast is scheduled to continue indefinitely.
|
43
|
-
|
44
|
-
@scheduled_end_time ||= if @data['scheduledEndTime']
|
45
|
-
Time.parse @data['scheduledEndTime']
|
46
|
-
end
|
47
|
-
end
|
31
|
+
has_attribute :scheduled_end_time, type: Time
|
48
32
|
|
49
33
|
# @return [Integer] if the broadcast has current viewers and the
|
50
34
|
# broadcast owner has not hidden the viewcount for the video, the
|
51
35
|
# number of viewers currently watching the broadcast.
|
52
36
|
# @return [nil] if the broadcast has ended or the broadcast owner has
|
53
37
|
# hidden the viewcount for the video.
|
54
|
-
|
55
|
-
@concurrent_viewers ||= if @data['concurrentViewers']
|
56
|
-
@data['concurrentViewers'].to_i
|
57
|
-
end
|
58
|
-
end
|
38
|
+
has_attribute :concurrent_viewers, type: Integer
|
59
39
|
end
|
60
40
|
end
|
61
41
|
end
|
data/lib/yt/models/ownership.rb
CHANGED
@@ -29,26 +29,26 @@ module Yt
|
|
29
29
|
# General asset ownership is used for all types of assets and is the
|
30
30
|
# only type of ownership data that can be provided for assets that are
|
31
31
|
# not compositions.
|
32
|
-
|
33
|
-
|
32
|
+
has_attribute :general_owners, from: :general do |data|
|
33
|
+
as_owners data
|
34
34
|
end
|
35
35
|
|
36
36
|
# @return [Array<RightOwner>] a list that identifies owners of the
|
37
37
|
# performance rights for a composition asset.
|
38
|
-
|
39
|
-
|
38
|
+
has_attribute :performance_owners, from: :performance do |data|
|
39
|
+
as_owners data
|
40
40
|
end
|
41
41
|
|
42
42
|
# @return [Array<RightOwner>] a list that identifies owners of the
|
43
43
|
# synchronization rights for a composition asset.
|
44
|
-
|
45
|
-
|
44
|
+
has_attribute :synchronization_owners, from: :synchronization do |data|
|
45
|
+
as_owners data
|
46
46
|
end
|
47
47
|
|
48
48
|
# @return [Array<RightOwner>] a list that identifies owners of the
|
49
49
|
# mechanical rights for a composition asset.
|
50
|
-
|
51
|
-
|
50
|
+
has_attribute :mechanical_owners, from: :mechanical do |data|
|
51
|
+
as_owners data
|
52
52
|
end
|
53
53
|
|
54
54
|
private
|
data/lib/yt/models/playlist.rb
CHANGED
@@ -35,20 +35,22 @@ module Yt
|
|
35
35
|
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} does not
|
36
36
|
# return an account with permissions to update the playlist.
|
37
37
|
# @return [Yt::PlaylistItem] the item added to the playlist.
|
38
|
-
def add_video(video_id)
|
39
|
-
|
38
|
+
def add_video(video_id, attributes = {})
|
39
|
+
playlist_item_params = playlist_item_params(video_id, attributes)
|
40
|
+
playlist_items.insert playlist_item_params, ignore_errors: true
|
40
41
|
end
|
41
42
|
|
42
|
-
def add_video!(video_id)
|
43
|
-
|
43
|
+
def add_video!(video_id, attributes = {})
|
44
|
+
playlist_item_params = playlist_item_params(video_id, attributes)
|
45
|
+
playlist_items.insert playlist_item_params
|
44
46
|
end
|
45
47
|
|
46
|
-
def add_videos(video_ids = [])
|
47
|
-
video_ids.map{|video_id| add_video video_id}
|
48
|
+
def add_videos(video_ids = [], attributes = {})
|
49
|
+
video_ids.map{|video_id| add_video video_id, attributes}
|
48
50
|
end
|
49
51
|
|
50
|
-
def add_videos!(video_ids = [])
|
51
|
-
video_ids.map{|video_id| add_video! video_id}
|
52
|
+
def add_videos!(video_ids = [], attributes = {})
|
53
|
+
video_ids.map{|video_id| add_video! video_id, attributes}
|
52
54
|
end
|
53
55
|
|
54
56
|
def delete_playlist_items(attrs = {})
|
@@ -67,8 +69,10 @@ module Yt
|
|
67
69
|
|
68
70
|
# @todo: extend camelize to also camelize the nested hashes, so we
|
69
71
|
# don’t have to write videoId
|
70
|
-
def
|
71
|
-
|
72
|
+
def playlist_item_params(video_id, params = {})
|
73
|
+
params.dup.tap do |params|
|
74
|
+
params[:resource_id] ||= {kind: 'youtube#video', videoId: video_id}
|
75
|
+
end
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|