yt 0.11.6 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|