/
channel.rb
302 lines (242 loc) · 10.9 KB
/
channel.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
require 'yt/models/resource'
module Yt
module Models
# Provides methods to interact with YouTube channels.
# @see https://developers.google.com/youtube/v3/docs/channels
class Channel < Resource
### SNIPPET ###
# @!attribute [r] title
# @return [String] the channel’s title.
delegate :title, to: :snippet
# @!attribute [r] description
# @return [String] the channel’s description.
delegate :description, to: :snippet
# @!method thumbnail_url(size = :default)
# Returns the URL of the channel’s thumbnail.
# @param [Symbol, String] size The size of the channel’s thumbnail.
# @return [String] if +size+ is +default+, the URL of a 88x88px image.
# @return [String] if +size+ is +medium+, the URL of a 240x240px image.
# @return [String] if +size+ is +high+, the URL of a 800x800px image.
# @return [nil] if the +size+ is not +default+, +medium+ or +high+.
delegate :thumbnail_url, to: :snippet
# @!attribute [r] published_at
# @return [Time] the date and time that the channel was created.
delegate :published_at, to: :snippet
### SUBSCRIPTION ###
has_one :subscription
# @return [Boolean] whether the account is subscribed to the channel.
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
# authenticated Yt::Account.
def subscribed?
sleep [(@subscriptions_updated_at || Time.now) - Time.now, 0].max
subscription.exists?
rescue Errors::NoItems
false
end
# Subscribes the authenticated account to the channel.
# Unlike {#subscribe!}, does not raise an error if already subscribed.
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
# authenticated Yt::Account.
def subscribe
subscriptions.insert(ignore_errors: true).tap do |subscription|
throttle_subscriptions
@subscription = subscription
end
end
# Subscribes the authenticated account to the channel.
# Unlike {#subscribe}, raises an error if already subscribed.
# @raise [Yt::Errors::RequestError] if already subscribed.
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
# authenticated Yt::Account.
def subscribe!
subscriptions.insert.tap do |subscription|
throttle_subscriptions
@subscription = subscription
end
end
# Unsubscribes the authenticated account from the channel.
# Unlike {#unsubscribe!}, does not raise an error if already unsubscribed.
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
# authenticated Yt::Account.
def unsubscribe
unsubscribe! if subscribed?
end
# Unsubscribes the authenticated account from the channel.
# Unlike {#unsubscribe}, raises an error if already unsubscribed.
#
# @raise [Yt::Errors::RequestError] if already unsubscribed.
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
# authenticated Yt::Account.
def unsubscribe!
subscription.delete.tap{ throttle_subscriptions }
end
### ASSOCIATIONS ###
# @!attribute [r] videos
# @return [Yt::Collections::Videos] the channel’s videos.
has_many :videos
# @!attribute [r] playlists
# @return [Yt::Collections::Playlists] the channel’s playlists.
has_many :playlists
# @!attribute [r] related_playlists
# @return [Yt::Collections::Playlists] the playlists associated with the
# channel, such as the playlist of uploaded or liked videos.
# @see https://developers.google.com/youtube/v3/docs/channels#contentDetails.relatedPlaylists
has_many :related_playlists
# @!attribute [r] subscribed_channels
# @return [Yt::Collections::SubscribedChannels] the channels that this
# channel is subscribed to.
# @raise [Yt::Errors::Forbidden] if the owner of the channel has
# explicitly select the option to keep all subscriptions private.
has_many :subscribed_channels
### ANALYTICS ###
# @macro reports
# @macro report_by_channel_dimensions
has_report :views, Integer
# @macro report_by_day
has_report :uniques, Integer
# @macro report_by_channel_dimensions
has_report :estimated_minutes_watched, Integer
# @macro report_by_gender_and_age_group
has_report :viewer_percentage, Float
# @macro report_by_day_and_country
has_report :comments, Integer
# @macro report_by_day_and_country
has_report :likes, Integer
# @macro report_by_day_and_country
has_report :dislikes, Integer
# @macro report_by_day_and_country
has_report :shares, Integer
# @macro report_by_day_and_country
has_report :subscribers_gained, Integer
# @macro report_by_day_and_country
has_report :subscribers_lost, Integer
# @macro report_by_day_and_country
has_report :favorites_added, Integer
# @macro report_by_day_and_country
has_report :favorites_removed, Integer
# @macro report_by_day_and_state
has_report :average_view_duration, Integer
# @macro report_by_day_and_state
has_report :average_view_percentage, Float
# @macro report_by_day_and_state
has_report :annotation_clicks, Integer
# @macro report_by_day_and_state
has_report :annotation_click_through_rate, Float
# @macro report_by_day_and_state
has_report :annotation_close_rate, Float
# @macro report_by_day_and_country
has_report :earnings, Float
# @macro report_by_day_and_country
has_report :impressions, Integer
# @macro report_by_day_and_country
has_report :monetized_playbacks, Integer
# @macro report_by_day_and_country
has_report :playback_based_cpm, Float
### STATISTICS ###
has_one :statistics_set
# @!attribute [r] view_count
# @return [Integer] the number of times the channel has been viewed.
delegate :view_count, to: :statistics_set
# @!attribute [r] comment_count
# @return [Integer] the number of comments for the channel.
delegate :comment_count, to: :statistics_set
# @!attribute [r] video_count
# @return [Integer] the number of videos uploaded to the channel.
delegate :video_count, to: :statistics_set
# @!attribute [r] subscriber_count
# @return [Integer] the number of subscriber the channel has.
delegate :subscriber_count, to: :statistics_set
# @return [Boolean] whether the number of subscribers is publicly visible.
def subscriber_count_visible?
statistics_set.hidden_subscriber_count == false
end
### CONTENT OWNER DETAILS ###
has_one :content_owner_detail
# @!attribute [r] content_owner
# The name of the content owner linked to the channel.
# @return [String] if the channel is partnered, its content owner’s name.
# @return [nil] if the channel is not partnered or if {Resource#auth auth}
# is a content owner without permissions to administer the channel.
# @raise [Yt::Errors::Forbidden] if {Resource#auth auth} does not
# return an authenticated content owner.
delegate :content_owner, to: :content_owner_detail
# Returns the time the channel was partnered to a content owner.
# @return [Time] if the channel is partnered, the time when it was linked
# to its content owner.
# @return [nil] if the channel is not partnered or if {Resource#auth auth}
# is a content owner without permissions to administer the channel.
# @raise [Yt::Errors::Forbidden] if {Resource#auth auth} does not
# return an authenticated content owner.
def linked_at
content_owner_detail.time_linked
end
### ACTIONS (UPLOAD, UPDATE, DELETE) ###
# Deletes the channel’s playlists matching all the given attributes.
# @return [Array<Boolean>] whether each playlist matching the given
# attributes was deleted.
# @raise [Yt::Errors::RequestError] if {Resource#auth auth} is not an
# authenticated Yt::Account with permissions to update the channel.
# @param [Hash] attributes the attributes to match the playlists by.
# @option attributes [<String, Regexp>] :title The playlist’s title.
# Pass a String for perfect match or a Regexp for advanced match.
# @option attributes [<String, Regexp>] :description The playlist’s
# description. Pass a String (perfect match) or a Regexp (advanced).
# @option attributes [Array<String>] :tags The playlist’s tags.
# All tags must match exactly.
# @option attributes [String] :privacy_status The playlist’s privacy
# status.
def delete_playlists(attributes = {})
playlists.delete_all attributes
end
### PRIVATE API ###
# @private
# Override Resource's new to set statistics as well
# if the response includes them
def initialize(options = {})
super options
if options[:statistics]
@statistics_set = StatisticsSet.new data: options[:statistics]
end
if options[:content_owner_details]
@content_owner_detail = ContentOwnerDetail.new data: options[:content_owner_details]
end
end
# @private
# Tells `has_many :videos` that channel.videos should return all the
# videos publicly available on the channel.
def videos_params
{channel_id: id}
end
# @private
# Tells `has_reports` to retrieve the reports from YouTube Analytics API
# either as a Channel or as a Content Owner.
# @see https://developers.google.com/youtube/analytics/v1/reports
def reports_params
{}.tap do |params|
if auth.owner_name
params[:ids] = "contentOwner==#{auth.owner_name}"
params[:filters] = "channel==#{id}"
else
params[:ids] = "channel==#{id}"
end
end
end
# @private
# Tells `has_one :content_owner_detail` to retrieve the content owner
# detail as the Content Owner, it the channel was authorized with one.
# If it was not, the call will fail, since YouTube only allows content
# owners to check who is the content owner of a channel.
def content_owner_details_params
{on_behalf_of_content_owner: auth.owner_name || auth.id}
end
# @private
# @note Google API must have some caching layer by which if we try to
# delete a subscription that we just created, we encounter an error.
# To overcome this, if we have just updated the subscription, we must
# wait some time before requesting it again.
def throttle_subscriptions(seconds = 10)
@subscriptions_updated_at = Time.now + seconds
end
end
end
end