yt-andrewroth 0.25.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +27 -0
- data/.rspec +3 -0
- data/.travis.yml +9 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +732 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +489 -0
- data/Rakefile +11 -0
- data/YOUTUBE_IT.md +835 -0
- data/bin/yt +30 -0
- data/gemfiles/Gemfile.activesupport-3.x +4 -0
- data/gemfiles/Gemfile.activesupport-4.x +4 -0
- data/lib/yt.rb +21 -0
- data/lib/yt/actions/base.rb +32 -0
- data/lib/yt/actions/delete.rb +19 -0
- data/lib/yt/actions/delete_all.rb +32 -0
- data/lib/yt/actions/insert.rb +42 -0
- data/lib/yt/actions/list.rb +139 -0
- data/lib/yt/actions/modify.rb +37 -0
- data/lib/yt/actions/patch.rb +19 -0
- data/lib/yt/actions/update.rb +19 -0
- data/lib/yt/associations/has_attribute.rb +55 -0
- data/lib/yt/associations/has_authentication.rb +214 -0
- data/lib/yt/associations/has_many.rb +22 -0
- data/lib/yt/associations/has_one.rb +22 -0
- data/lib/yt/associations/has_reports.rb +320 -0
- data/lib/yt/collections/advertising_options_sets.rb +34 -0
- data/lib/yt/collections/annotations.rb +62 -0
- data/lib/yt/collections/assets.rb +58 -0
- data/lib/yt/collections/authentications.rb +47 -0
- data/lib/yt/collections/base.rb +62 -0
- data/lib/yt/collections/channels.rb +31 -0
- data/lib/yt/collections/claim_histories.rb +34 -0
- data/lib/yt/collections/claims.rb +56 -0
- data/lib/yt/collections/content_details.rb +30 -0
- data/lib/yt/collections/content_owner_details.rb +34 -0
- data/lib/yt/collections/content_owners.rb +32 -0
- data/lib/yt/collections/device_flows.rb +23 -0
- data/lib/yt/collections/file_details.rb +30 -0
- data/lib/yt/collections/ids.rb +27 -0
- data/lib/yt/collections/live_streaming_details.rb +30 -0
- data/lib/yt/collections/ownerships.rb +34 -0
- data/lib/yt/collections/partnered_channels.rb +28 -0
- data/lib/yt/collections/players.rb +30 -0
- data/lib/yt/collections/playlist_items.rb +53 -0
- data/lib/yt/collections/playlists.rb +28 -0
- data/lib/yt/collections/policies.rb +28 -0
- data/lib/yt/collections/ratings.rb +23 -0
- data/lib/yt/collections/references.rb +46 -0
- data/lib/yt/collections/related_playlists.rb +43 -0
- data/lib/yt/collections/reports.rb +161 -0
- data/lib/yt/collections/resources.rb +57 -0
- data/lib/yt/collections/resumable_sessions.rb +51 -0
- data/lib/yt/collections/snippets.rb +27 -0
- data/lib/yt/collections/statistics_sets.rb +30 -0
- data/lib/yt/collections/statuses.rb +27 -0
- data/lib/yt/collections/subscribed_channels.rb +46 -0
- data/lib/yt/collections/subscribers.rb +33 -0
- data/lib/yt/collections/subscriptions.rb +50 -0
- data/lib/yt/collections/user_infos.rb +36 -0
- data/lib/yt/collections/video_categories.rb +35 -0
- data/lib/yt/collections/videos.rb +137 -0
- data/lib/yt/config.rb +54 -0
- data/lib/yt/errors/forbidden.rb +13 -0
- data/lib/yt/errors/missing_auth.rb +81 -0
- data/lib/yt/errors/no_items.rb +13 -0
- data/lib/yt/errors/request_error.rb +74 -0
- data/lib/yt/errors/server_error.rb +13 -0
- data/lib/yt/errors/unauthorized.rb +50 -0
- data/lib/yt/models/account.rb +216 -0
- data/lib/yt/models/advertising_options_set.rb +38 -0
- data/lib/yt/models/annotation.rb +132 -0
- data/lib/yt/models/asset.rb +111 -0
- data/lib/yt/models/asset_metadata.rb +38 -0
- data/lib/yt/models/asset_snippet.rb +46 -0
- data/lib/yt/models/authentication.rb +83 -0
- data/lib/yt/models/base.rb +32 -0
- data/lib/yt/models/channel.rb +302 -0
- data/lib/yt/models/claim.rb +156 -0
- data/lib/yt/models/claim_event.rb +67 -0
- data/lib/yt/models/claim_history.rb +29 -0
- data/lib/yt/models/configuration.rb +70 -0
- data/lib/yt/models/content_detail.rb +65 -0
- data/lib/yt/models/content_owner.rb +48 -0
- data/lib/yt/models/content_owner_detail.rb +18 -0
- data/lib/yt/models/description.rb +58 -0
- data/lib/yt/models/device_flow.rb +16 -0
- data/lib/yt/models/file_detail.rb +21 -0
- data/lib/yt/models/id.rb +9 -0
- data/lib/yt/models/iterator.rb +16 -0
- data/lib/yt/models/live_streaming_detail.rb +23 -0
- data/lib/yt/models/match_policy.rb +34 -0
- data/lib/yt/models/ownership.rb +75 -0
- data/lib/yt/models/player.rb +18 -0
- data/lib/yt/models/playlist.rb +218 -0
- data/lib/yt/models/playlist_item.rb +112 -0
- data/lib/yt/models/policy.rb +36 -0
- data/lib/yt/models/policy_rule.rb +124 -0
- data/lib/yt/models/rating.rb +37 -0
- data/lib/yt/models/reference.rb +172 -0
- data/lib/yt/models/resource.rb +136 -0
- data/lib/yt/models/resumable_session.rb +52 -0
- data/lib/yt/models/right_owner.rb +58 -0
- data/lib/yt/models/snippet.rb +50 -0
- data/lib/yt/models/statistics_set.rb +26 -0
- data/lib/yt/models/status.rb +32 -0
- data/lib/yt/models/subscription.rb +38 -0
- data/lib/yt/models/timestamp.rb +13 -0
- data/lib/yt/models/url.rb +90 -0
- data/lib/yt/models/user_info.rb +26 -0
- data/lib/yt/models/video.rb +630 -0
- data/lib/yt/models/video_category.rb +12 -0
- data/lib/yt/request.rb +278 -0
- data/lib/yt/version.rb +3 -0
- data/spec/collections/claims_spec.rb +30 -0
- data/spec/collections/playlist_items_spec.rb +44 -0
- data/spec/collections/playlists_spec.rb +27 -0
- data/spec/collections/policies_spec.rb +30 -0
- data/spec/collections/references_spec.rb +30 -0
- data/spec/collections/reports_spec.rb +30 -0
- data/spec/collections/subscriptions_spec.rb +25 -0
- data/spec/collections/videos_spec.rb +43 -0
- data/spec/errors/forbidden_spec.rb +10 -0
- data/spec/errors/missing_auth_spec.rb +24 -0
- data/spec/errors/no_items_spec.rb +10 -0
- data/spec/errors/request_error_spec.rb +44 -0
- data/spec/errors/server_error_spec.rb +10 -0
- data/spec/errors/unauthorized_spec.rb +10 -0
- data/spec/models/account_spec.rb +138 -0
- data/spec/models/annotation_spec.rb +180 -0
- data/spec/models/asset_spec.rb +20 -0
- data/spec/models/channel_spec.rb +127 -0
- data/spec/models/claim_event_spec.rb +62 -0
- data/spec/models/claim_history_spec.rb +27 -0
- data/spec/models/claim_spec.rb +211 -0
- data/spec/models/configuration_spec.rb +44 -0
- data/spec/models/content_detail_spec.rb +45 -0
- data/spec/models/content_owner_detail_spec.rb +6 -0
- data/spec/models/description_spec.rb +94 -0
- data/spec/models/file_detail_spec.rb +13 -0
- data/spec/models/live_streaming_detail_spec.rb +6 -0
- data/spec/models/ownership_spec.rb +59 -0
- data/spec/models/player_spec.rb +13 -0
- data/spec/models/playlist_item_spec.rb +120 -0
- data/spec/models/playlist_spec.rb +138 -0
- data/spec/models/policy_rule_spec.rb +63 -0
- data/spec/models/policy_spec.rb +41 -0
- data/spec/models/rating_spec.rb +12 -0
- data/spec/models/reference_spec.rb +249 -0
- data/spec/models/request_spec.rb +163 -0
- data/spec/models/resource_spec.rb +57 -0
- data/spec/models/right_owner_spec.rb +71 -0
- data/spec/models/snippet_spec.rb +13 -0
- data/spec/models/statistics_set_spec.rb +13 -0
- data/spec/models/status_spec.rb +13 -0
- data/spec/models/subscription_spec.rb +30 -0
- data/spec/models/url_spec.rb +78 -0
- data/spec/models/video_category_spec.rb +21 -0
- data/spec/models/video_spec.rb +669 -0
- data/spec/requests/as_account/account_spec.rb +125 -0
- data/spec/requests/as_account/authentications_spec.rb +139 -0
- data/spec/requests/as_account/channel_spec.rb +259 -0
- data/spec/requests/as_account/channels_spec.rb +18 -0
- data/spec/requests/as_account/playlist_item_spec.rb +56 -0
- data/spec/requests/as_account/playlist_spec.rb +244 -0
- data/spec/requests/as_account/resource_spec.rb +18 -0
- 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 +408 -0
- data/spec/requests/as_content_owner/account_spec.rb +25 -0
- data/spec/requests/as_content_owner/advertising_options_set_spec.rb +15 -0
- data/spec/requests/as_content_owner/asset_spec.rb +20 -0
- data/spec/requests/as_content_owner/channel_spec.rb +1934 -0
- data/spec/requests/as_content_owner/claim_history_spec.rb +20 -0
- data/spec/requests/as_content_owner/content_owner_spec.rb +241 -0
- data/spec/requests/as_content_owner/match_policy_spec.rb +17 -0
- data/spec/requests/as_content_owner/ownership_spec.rb +25 -0
- data/spec/requests/as_content_owner/playlist_spec.rb +782 -0
- data/spec/requests/as_content_owner/video_spec.rb +1239 -0
- data/spec/requests/as_server_app/channel_spec.rb +74 -0
- data/spec/requests/as_server_app/playlist_item_spec.rb +30 -0
- data/spec/requests/as_server_app/playlist_spec.rb +53 -0
- data/spec/requests/as_server_app/video_spec.rb +58 -0
- data/spec/requests/as_server_app/videos_spec.rb +40 -0
- data/spec/requests/unauthenticated/video_spec.rb +22 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/fail_matcher.rb +15 -0
- data/spec/support/global_hooks.rb +48 -0
- data/yt.gemspec +32 -0
- metadata +416 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'yt/models/base'
|
2
|
+
require 'yt/models/asset_metadata'
|
3
|
+
|
4
|
+
module Yt
|
5
|
+
module Models
|
6
|
+
# Provides methods to interact with YouTube ContentID assets.
|
7
|
+
# @see https://developers.google.com/youtube/partner/docs/v1/assets
|
8
|
+
class Asset < Base
|
9
|
+
attr_reader :auth
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@data = options.fetch(:data, {})
|
13
|
+
@id = options[:id]
|
14
|
+
@auth = options[:auth]
|
15
|
+
end
|
16
|
+
|
17
|
+
def update(attributes = {})
|
18
|
+
underscore_keys! attributes
|
19
|
+
do_patch body: attributes
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
# @!attribute [r] ownership
|
24
|
+
# @return [Yt::Models::Ownership] the asset’s ownership.
|
25
|
+
has_one :ownership
|
26
|
+
delegate :general_owners, :performance_owners, :synchronization_owners,
|
27
|
+
:mechanical_owners, to: :ownership
|
28
|
+
|
29
|
+
def metadata_mine
|
30
|
+
@metadata_mine ||= Yt::Models::AssetMetadata.new data: @data.fetch('metadataMine', {})
|
31
|
+
end
|
32
|
+
|
33
|
+
def metadata_effective
|
34
|
+
@metadata_effective ||= Yt::Models::AssetMetadata.new data: @data.fetch('metadataEffective', {})
|
35
|
+
end
|
36
|
+
|
37
|
+
# Soft-deletes the asset.
|
38
|
+
# @note YouTube API does not provide a +delete+ method for the Asset
|
39
|
+
# resource, but only an +update+ method. Updating the +status+ of a
|
40
|
+
# Asset to "inactive" can be considered a soft-deletion.
|
41
|
+
# @note Despite what the documentation says, YouTube API never returns
|
42
|
+
# the status of an asset, so it’s impossible to update, although the
|
43
|
+
# documentation says this should be the case. If YouTube ever fixes
|
44
|
+
# the API, then the following code can be uncommented.
|
45
|
+
# @return [Boolean] whether the asset is inactive.
|
46
|
+
# def delete
|
47
|
+
# body = {id: id, status: :inactive}
|
48
|
+
# do_patch(body: body) {|data| @data = data}
|
49
|
+
# inactive?
|
50
|
+
# end
|
51
|
+
|
52
|
+
# @return [String] the ID that YouTube assigns and uses to uniquely
|
53
|
+
# identify the asset.
|
54
|
+
has_attribute :id
|
55
|
+
|
56
|
+
# Returns the asset’s type.
|
57
|
+
# @return [String] the asset’s type. This value determines the metadata
|
58
|
+
# fields that you can set for the asset. In addition, certain API
|
59
|
+
# functions may only be supported for specific types of assets. For
|
60
|
+
# example, composition assets may have more complex ownership data than
|
61
|
+
# other types of assets.
|
62
|
+
# Possible values are: +'art_track_video'+, +'composition'+,
|
63
|
+
# +'episode'+, +'general'+, +'movie'+, +'music_video'+, +'season'+,
|
64
|
+
# +'show'+, +'sound_recording'+, +'video_game'+, +'web'+.
|
65
|
+
has_attribute :type
|
66
|
+
|
67
|
+
# @return [Array<Yt::Models::Tag>] the list of asset labels associated
|
68
|
+
# with the asset. You can apply a label to multiple assets to group
|
69
|
+
# them. You can use the labels as search filters to perform bulk updates,
|
70
|
+
# to download reports, or to filter YouTube Analytics.
|
71
|
+
has_attribute :label
|
72
|
+
|
73
|
+
# Status
|
74
|
+
|
75
|
+
# Returns the asset’s status.
|
76
|
+
# @return [String] the asset’s status. Possible values are: +'active'+,
|
77
|
+
# +'inactive'+, +'pending'+.
|
78
|
+
# @note Despite what the documentation says, YouTube API never returns
|
79
|
+
# the status of an asset, so it’s impossible to update, although the
|
80
|
+
# documentation says this should be the case. If YouTube ever fixes
|
81
|
+
# the API, then the following code can be uncommented.
|
82
|
+
# has_attribute :status
|
83
|
+
#
|
84
|
+
# # @return [Boolean] whether the asset is active.
|
85
|
+
# def active?
|
86
|
+
# status == 'active'
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# # @return [Boolean] whether the asset is inactive.
|
90
|
+
# def inactive?
|
91
|
+
# status == 'inactive'
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# # @return [Boolean] whether the asset is pending.
|
95
|
+
# def pending?
|
96
|
+
# status == 'pending'
|
97
|
+
# end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# @see https://developers.google.com/youtube/partner/docs/v1/assets/patch
|
102
|
+
def patch_params
|
103
|
+
super.tap do |params|
|
104
|
+
params[:expected_response] = Net::HTTPOK
|
105
|
+
params[:path] = "/youtube/partner/v1/assets/#{@id}"
|
106
|
+
params[:params] = {on_behalf_of_content_owner: @auth.owner_name}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'yt/models/base'
|
2
|
+
|
3
|
+
module Yt
|
4
|
+
module Models
|
5
|
+
# The AssetMetadata object specifies the metadata for an asset.
|
6
|
+
# @see https://developers.google.com/youtube/partner/docs/v1/assets#metadataMine
|
7
|
+
class AssetMetadata < Base
|
8
|
+
def initialize(options = {})
|
9
|
+
@data = options[:data]
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] A unique value that you, the metadata provider,
|
13
|
+
# use to identify an asset. The value could be a unique ID that
|
14
|
+
# you created for the asset or a standard identifier, such as an
|
15
|
+
# ISRC.
|
16
|
+
def custom_id
|
17
|
+
@data['customId']
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] The asset's title or name.
|
21
|
+
def title
|
22
|
+
@data['title']
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] A description of the asset. The description may be
|
26
|
+
# displayed on YouTube or in CMS.
|
27
|
+
def notes
|
28
|
+
@data['notes']
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [String] the ID that YouTube assigns and uses to uniquely
|
32
|
+
# identify the asset.
|
33
|
+
def description
|
34
|
+
@data['description']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'yt/models/base'
|
2
|
+
|
3
|
+
module Yt
|
4
|
+
module Models
|
5
|
+
# Provides methods to interact with YouTube ContentID assetSnippets.
|
6
|
+
# @see https://developers.google.com/youtube/partner/docs/v1/assetSearch
|
7
|
+
class AssetSnippet < Base
|
8
|
+
attr_reader :auth
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@data = options.fetch(:data, {})
|
12
|
+
@auth = options[:auth]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String] the ID that YouTube assigns and uses to uniquely
|
16
|
+
# identify the asset.
|
17
|
+
has_attribute :id
|
18
|
+
|
19
|
+
# Returns the asset’s type.
|
20
|
+
# @return [String] the asset’s type. This value determines the metadata
|
21
|
+
# fields that you can set for the asset. In addition, certain API
|
22
|
+
# functions may only be supported for specific types of assets. For
|
23
|
+
# example, composition assets may have more complex ownership data than
|
24
|
+
# other types of assets.
|
25
|
+
# Possible values are: +'art_track_video'+, +'composition'+,
|
26
|
+
# +'episode'+, +'general'+, +'movie'+, +'music_video'+, +'season'+,
|
27
|
+
# +'show'+, +'sound_recording'+, +'video_game'+, +'web'+.
|
28
|
+
has_attribute :type
|
29
|
+
|
30
|
+
# @return [String] the title of this asset.
|
31
|
+
has_attribute :title
|
32
|
+
|
33
|
+
# @return [String] the Custom ID assigned by the content owner to
|
34
|
+
# this asset.
|
35
|
+
has_attribute :custom_id
|
36
|
+
|
37
|
+
# @return [String] the ISRC (International Standard Recording Code)
|
38
|
+
# for this asset.
|
39
|
+
has_attribute :isrc
|
40
|
+
|
41
|
+
# @return [String] the ISWC (International Standard Musical Work Code)
|
42
|
+
# for this asset.
|
43
|
+
has_attribute :iswc
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Yt
|
2
|
+
module Models
|
3
|
+
# Provides methods to authenticate with YouTube (and Google) API.
|
4
|
+
# @see https://developers.google.com/accounts/docs/OAuth2
|
5
|
+
class Authentication
|
6
|
+
|
7
|
+
# Before your application can access private data using a Google API,
|
8
|
+
# it must obtain an access token that grants access to that API.
|
9
|
+
#
|
10
|
+
# A single access token can grant varying degrees of access to multiple
|
11
|
+
# APIs. A variable parameter called scope controls the set of resources
|
12
|
+
# and operations that an access token permits.
|
13
|
+
#
|
14
|
+
# After an application obtains an access token, it sends the token to a
|
15
|
+
# Google API in an HTTP authorization header.
|
16
|
+
#
|
17
|
+
# Access tokens are valid only for the set of operations and resources
|
18
|
+
# described in the scope of the token request. For example, if an access
|
19
|
+
# token is issued for the Google+ API, it does not grant access to the
|
20
|
+
# Google Contacts API.
|
21
|
+
#
|
22
|
+
# @return [String] the OAuth2 Google access token.
|
23
|
+
attr_reader :access_token
|
24
|
+
|
25
|
+
# Access tokens have limited lifetimes. If your application needs access
|
26
|
+
# to a Google API beyond the lifetime of a single access token, it can
|
27
|
+
# obtain a refresh token. A refresh token allows your application to
|
28
|
+
# obtain new access tokens.
|
29
|
+
#
|
30
|
+
# Save refresh tokens in secure long-term storage and continue to
|
31
|
+
# use them as long as they remain valid. Limits apply to the number of
|
32
|
+
# refresh tokens that are issued per client-user combination, and per
|
33
|
+
# user across all clients, and these limits are different. If your
|
34
|
+
# application requests enough refresh tokens to go over one of the
|
35
|
+
# limits, older refresh tokens stop working.
|
36
|
+
#
|
37
|
+
# There is currently a 25-token limit per Google user account.
|
38
|
+
# If a user account has 25 valid tokens, the next authentication request
|
39
|
+
# succeeds, but quietly invalidates the oldest outstanding token without
|
40
|
+
# any user-visible warning.
|
41
|
+
#
|
42
|
+
# @return [String] the OAuth2 Google refresh token.
|
43
|
+
attr_reader :refresh_token
|
44
|
+
|
45
|
+
# Access tokens have limited lifetimes. If your application needs access
|
46
|
+
# to a Google API beyond the lifetime of a single access token, it can
|
47
|
+
# obtain a refresh token.
|
48
|
+
#
|
49
|
+
# A refresh token allows your application to
|
50
|
+
# obtain new access tokens.
|
51
|
+
#
|
52
|
+
# @return [Time] the time when access token no longer works.
|
53
|
+
attr_reader :expires_at
|
54
|
+
|
55
|
+
def initialize(data = {})
|
56
|
+
@access_token = data['access_token']
|
57
|
+
@refresh_token = data['refresh_token']
|
58
|
+
@error = data['error']
|
59
|
+
@expires_at = expiration_date data.slice('expires_at', 'expires_in')
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Boolean] whether the access token has expired.
|
63
|
+
def expired?
|
64
|
+
@expires_at && @expires_at.past?
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Boolean] whether the device auth is pending
|
68
|
+
def pending?
|
69
|
+
@error == 'authorization_pending'
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def expiration_date(options = {})
|
75
|
+
if options['expires_in']
|
76
|
+
Time.now + options['expires_in'].seconds
|
77
|
+
else
|
78
|
+
Time.parse options['expires_at'] rescue nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'yt/actions/delete'
|
2
|
+
require 'yt/actions/update'
|
3
|
+
require 'yt/actions/patch'
|
4
|
+
|
5
|
+
require 'yt/associations/has_attribute'
|
6
|
+
require 'yt/associations/has_authentication'
|
7
|
+
require 'yt/associations/has_many'
|
8
|
+
require 'yt/associations/has_one'
|
9
|
+
require 'yt/associations/has_reports'
|
10
|
+
|
11
|
+
require 'yt/errors/request_error'
|
12
|
+
|
13
|
+
module Yt
|
14
|
+
module Models
|
15
|
+
# @private
|
16
|
+
class Base
|
17
|
+
include Actions::Delete
|
18
|
+
include Actions::Update
|
19
|
+
include Actions::Patch
|
20
|
+
|
21
|
+
include Associations::HasAttribute
|
22
|
+
extend Associations::HasReports
|
23
|
+
extend Associations::HasOne
|
24
|
+
extend Associations::HasMany
|
25
|
+
extend Associations::HasAuthentication
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# By including Models in the main namespace, models can be initialized with
|
30
|
+
# the shorter notation Yt::Video.new, rather than Yt::Models::Video.new.
|
31
|
+
include Models
|
32
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
require 'yt/models/resource'
|
2
|
+
|
3
|
+
module Yt
|
4
|
+
module Models
|
5
|
+
# Provides methods to interact with YouTube channels.
|
6
|
+
# @see https://developers.google.com/youtube/v3/docs/channels
|
7
|
+
class Channel < Resource
|
8
|
+
|
9
|
+
### SNIPPET ###
|
10
|
+
|
11
|
+
# @!attribute [r] title
|
12
|
+
# @return [String] the channel’s title.
|
13
|
+
delegate :title, to: :snippet
|
14
|
+
|
15
|
+
# @!attribute [r] description
|
16
|
+
# @return [String] the channel’s description.
|
17
|
+
delegate :description, to: :snippet
|
18
|
+
|
19
|
+
# @!method thumbnail_url(size = :default)
|
20
|
+
# Returns the URL of the channel’s thumbnail.
|
21
|
+
# @param [Symbol, String] size The size of the channel’s thumbnail.
|
22
|
+
# @return [String] if +size+ is +default+, the URL of a 88x88px image.
|
23
|
+
# @return [String] if +size+ is +medium+, the URL of a 240x240px image.
|
24
|
+
# @return [String] if +size+ is +high+, the URL of a 800x800px image.
|
25
|
+
# @return [nil] if the +size+ is not +default+, +medium+ or +high+.
|
26
|
+
delegate :thumbnail_url, to: :snippet
|
27
|
+
|
28
|
+
# @!attribute [r] published_at
|
29
|
+
# @return [Time] the date and time that the channel was created.
|
30
|
+
delegate :published_at, to: :snippet
|
31
|
+
|
32
|
+
### SUBSCRIPTION ###
|
33
|
+
|
34
|
+
has_one :subscription
|
35
|
+
|
36
|
+
# @return [Boolean] whether the account is subscribed to the channel.
|
37
|
+
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
|
38
|
+
# authenticated Yt::Account.
|
39
|
+
def subscribed?
|
40
|
+
sleep [(@subscriptions_updated_at || Time.now) - Time.now, 0].max
|
41
|
+
subscription.exists?
|
42
|
+
rescue Errors::NoItems
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
# Subscribes the authenticated account to the channel.
|
47
|
+
# Unlike {#subscribe!}, does not raise an error if already subscribed.
|
48
|
+
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
|
49
|
+
# authenticated Yt::Account.
|
50
|
+
def subscribe
|
51
|
+
subscriptions.insert(ignore_errors: true).tap do |subscription|
|
52
|
+
throttle_subscriptions
|
53
|
+
@subscription = subscription
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Subscribes the authenticated account to the channel.
|
58
|
+
# Unlike {#subscribe}, raises an error if already subscribed.
|
59
|
+
# @raise [Yt::Errors::RequestError] if already subscribed.
|
60
|
+
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
|
61
|
+
# authenticated Yt::Account.
|
62
|
+
def subscribe!
|
63
|
+
subscriptions.insert.tap do |subscription|
|
64
|
+
throttle_subscriptions
|
65
|
+
@subscription = subscription
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Unsubscribes the authenticated account from the channel.
|
70
|
+
# Unlike {#unsubscribe!}, does not raise an error if already unsubscribed.
|
71
|
+
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
|
72
|
+
# authenticated Yt::Account.
|
73
|
+
def unsubscribe
|
74
|
+
unsubscribe! if subscribed?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Unsubscribes the authenticated account from the channel.
|
78
|
+
# Unlike {#unsubscribe}, raises an error if already unsubscribed.
|
79
|
+
#
|
80
|
+
# @raise [Yt::Errors::RequestError] if already unsubscribed.
|
81
|
+
# @raise [Yt::Errors::Unauthorized] if {Resource#auth auth} is not an
|
82
|
+
# authenticated Yt::Account.
|
83
|
+
def unsubscribe!
|
84
|
+
subscription.delete.tap{ throttle_subscriptions }
|
85
|
+
end
|
86
|
+
|
87
|
+
### ASSOCIATIONS ###
|
88
|
+
|
89
|
+
# @!attribute [r] videos
|
90
|
+
# @return [Yt::Collections::Videos] the channel’s videos.
|
91
|
+
has_many :videos
|
92
|
+
|
93
|
+
# @!attribute [r] playlists
|
94
|
+
# @return [Yt::Collections::Playlists] the channel’s playlists.
|
95
|
+
has_many :playlists
|
96
|
+
|
97
|
+
# @!attribute [r] related_playlists
|
98
|
+
# @return [Yt::Collections::Playlists] the playlists associated with the
|
99
|
+
# channel, such as the playlist of uploaded or liked videos.
|
100
|
+
# @see https://developers.google.com/youtube/v3/docs/channels#contentDetails.relatedPlaylists
|
101
|
+
has_many :related_playlists
|
102
|
+
|
103
|
+
# @!attribute [r] subscribed_channels
|
104
|
+
# @return [Yt::Collections::SubscribedChannels] the channels that this
|
105
|
+
# channel is subscribed to.
|
106
|
+
# @raise [Yt::Errors::Forbidden] if the owner of the channel has
|
107
|
+
# explicitly select the option to keep all subscriptions private.
|
108
|
+
has_many :subscribed_channels
|
109
|
+
|
110
|
+
### ANALYTICS ###
|
111
|
+
|
112
|
+
# @macro reports
|
113
|
+
|
114
|
+
# @macro report_by_channel_dimensions
|
115
|
+
has_report :views, Integer
|
116
|
+
|
117
|
+
# @macro report_by_day
|
118
|
+
has_report :uniques, Integer
|
119
|
+
|
120
|
+
# @macro report_by_channel_dimensions
|
121
|
+
has_report :estimated_minutes_watched, Integer
|
122
|
+
|
123
|
+
# @macro report_by_gender_and_age_group
|
124
|
+
has_report :viewer_percentage, Float
|
125
|
+
|
126
|
+
# @macro report_by_day_and_country
|
127
|
+
has_report :comments, Integer
|
128
|
+
|
129
|
+
# @macro report_by_day_and_country
|
130
|
+
has_report :likes, Integer
|
131
|
+
|
132
|
+
# @macro report_by_day_and_country
|
133
|
+
has_report :dislikes, Integer
|
134
|
+
|
135
|
+
# @macro report_by_day_and_country
|
136
|
+
has_report :shares, Integer
|
137
|
+
|
138
|
+
# @macro report_by_day_and_country
|
139
|
+
has_report :subscribers_gained, Integer
|
140
|
+
|
141
|
+
# @macro report_by_day_and_country
|
142
|
+
has_report :subscribers_lost, Integer
|
143
|
+
|
144
|
+
# @macro report_by_day_and_country
|
145
|
+
has_report :favorites_added, Integer
|
146
|
+
|
147
|
+
# @macro report_by_day_and_country
|
148
|
+
has_report :favorites_removed, Integer
|
149
|
+
|
150
|
+
# @macro report_by_day_and_state
|
151
|
+
has_report :average_view_duration, Integer
|
152
|
+
|
153
|
+
# @macro report_by_day_and_state
|
154
|
+
has_report :average_view_percentage, Float
|
155
|
+
|
156
|
+
# @macro report_by_day_and_state
|
157
|
+
has_report :annotation_clicks, Integer
|
158
|
+
|
159
|
+
# @macro report_by_day_and_state
|
160
|
+
has_report :annotation_click_through_rate, Float
|
161
|
+
|
162
|
+
# @macro report_by_day_and_state
|
163
|
+
has_report :annotation_close_rate, Float
|
164
|
+
|
165
|
+
# @macro report_by_day_and_country
|
166
|
+
has_report :earnings, Float
|
167
|
+
|
168
|
+
# @macro report_by_day_and_country
|
169
|
+
has_report :impressions, Integer
|
170
|
+
|
171
|
+
# @macro report_by_day_and_country
|
172
|
+
has_report :monetized_playbacks, Integer
|
173
|
+
|
174
|
+
# @macro report_by_day_and_country
|
175
|
+
has_report :playback_based_cpm, Float
|
176
|
+
|
177
|
+
### STATISTICS ###
|
178
|
+
|
179
|
+
has_one :statistics_set
|
180
|
+
|
181
|
+
# @!attribute [r] view_count
|
182
|
+
# @return [Integer] the number of times the channel has been viewed.
|
183
|
+
delegate :view_count, to: :statistics_set
|
184
|
+
|
185
|
+
# @!attribute [r] comment_count
|
186
|
+
# @return [Integer] the number of comments for the channel.
|
187
|
+
delegate :comment_count, to: :statistics_set
|
188
|
+
|
189
|
+
# @!attribute [r] video_count
|
190
|
+
# @return [Integer] the number of videos uploaded to the channel.
|
191
|
+
delegate :video_count, to: :statistics_set
|
192
|
+
|
193
|
+
# @!attribute [r] subscriber_count
|
194
|
+
# @return [Integer] the number of subscriber the channel has.
|
195
|
+
delegate :subscriber_count, to: :statistics_set
|
196
|
+
|
197
|
+
# @return [Boolean] whether the number of subscribers is publicly visible.
|
198
|
+
def subscriber_count_visible?
|
199
|
+
statistics_set.hidden_subscriber_count == false
|
200
|
+
end
|
201
|
+
|
202
|
+
### CONTENT OWNER DETAILS ###
|
203
|
+
|
204
|
+
has_one :content_owner_detail
|
205
|
+
|
206
|
+
# @!attribute [r] content_owner
|
207
|
+
# The name of the content owner linked to the channel.
|
208
|
+
# @return [String] if the channel is partnered, its content owner’s name.
|
209
|
+
# @return [nil] if the channel is not partnered or if {Resource#auth auth}
|
210
|
+
# is a content owner without permissions to administer the channel.
|
211
|
+
# @raise [Yt::Errors::Forbidden] if {Resource#auth auth} does not
|
212
|
+
# return an authenticated content owner.
|
213
|
+
delegate :content_owner, to: :content_owner_detail
|
214
|
+
|
215
|
+
# Returns the time the channel was partnered to a content owner.
|
216
|
+
# @return [Time] if the channel is partnered, the time when it was linked
|
217
|
+
# to its content owner.
|
218
|
+
# @return [nil] if the channel is not partnered or if {Resource#auth auth}
|
219
|
+
# is a content owner without permissions to administer the channel.
|
220
|
+
# @raise [Yt::Errors::Forbidden] if {Resource#auth auth} does not
|
221
|
+
# return an authenticated content owner.
|
222
|
+
def linked_at
|
223
|
+
content_owner_detail.time_linked
|
224
|
+
end
|
225
|
+
|
226
|
+
### ACTIONS (UPLOAD, UPDATE, DELETE) ###
|
227
|
+
|
228
|
+
# Deletes the channel’s playlists matching all the given attributes.
|
229
|
+
# @return [Array<Boolean>] whether each playlist matching the given
|
230
|
+
# attributes was deleted.
|
231
|
+
# @raise [Yt::Errors::RequestError] if {Resource#auth auth} is not an
|
232
|
+
# authenticated Yt::Account with permissions to update the channel.
|
233
|
+
# @param [Hash] attributes the attributes to match the playlists by.
|
234
|
+
# @option attributes [<String, Regexp>] :title The playlist’s title.
|
235
|
+
# Pass a String for perfect match or a Regexp for advanced match.
|
236
|
+
# @option attributes [<String, Regexp>] :description The playlist’s
|
237
|
+
# description. Pass a String (perfect match) or a Regexp (advanced).
|
238
|
+
# @option attributes [Array<String>] :tags The playlist’s tags.
|
239
|
+
# All tags must match exactly.
|
240
|
+
# @option attributes [String] :privacy_status The playlist’s privacy
|
241
|
+
# status.
|
242
|
+
def delete_playlists(attributes = {})
|
243
|
+
playlists.delete_all attributes
|
244
|
+
end
|
245
|
+
|
246
|
+
### PRIVATE API ###
|
247
|
+
|
248
|
+
# @private
|
249
|
+
# Override Resource's new to set statistics as well
|
250
|
+
# if the response includes them
|
251
|
+
def initialize(options = {})
|
252
|
+
super options
|
253
|
+
if options[:statistics]
|
254
|
+
@statistics_set = StatisticsSet.new data: options[:statistics]
|
255
|
+
end
|
256
|
+
if options[:content_owner_details]
|
257
|
+
@content_owner_detail = ContentOwnerDetail.new data: options[:content_owner_details]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# @private
|
262
|
+
# Tells `has_many :videos` that channel.videos should return all the
|
263
|
+
# videos publicly available on the channel.
|
264
|
+
def videos_params
|
265
|
+
{channel_id: id}
|
266
|
+
end
|
267
|
+
|
268
|
+
# @private
|
269
|
+
# Tells `has_reports` to retrieve the reports from YouTube Analytics API
|
270
|
+
# either as a Channel or as a Content Owner.
|
271
|
+
# @see https://developers.google.com/youtube/analytics/v1/reports
|
272
|
+
def reports_params
|
273
|
+
{}.tap do |params|
|
274
|
+
if auth.owner_name
|
275
|
+
params[:ids] = "contentOwner==#{auth.owner_name}"
|
276
|
+
params[:filters] = "channel==#{id}"
|
277
|
+
else
|
278
|
+
params[:ids] = "channel==#{id}"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# @private
|
284
|
+
# Tells `has_one :content_owner_detail` to retrieve the content owner
|
285
|
+
# detail as the Content Owner, it the channel was authorized with one.
|
286
|
+
# If it was not, the call will fail, since YouTube only allows content
|
287
|
+
# owners to check who is the content owner of a channel.
|
288
|
+
def content_owner_details_params
|
289
|
+
{on_behalf_of_content_owner: auth.owner_name || auth.id}
|
290
|
+
end
|
291
|
+
|
292
|
+
# @private
|
293
|
+
# @note Google API must have some caching layer by which if we try to
|
294
|
+
# delete a subscription that we just created, we encounter an error.
|
295
|
+
# To overcome this, if we have just updated the subscription, we must
|
296
|
+
# wait some time before requesting it again.
|
297
|
+
def throttle_subscriptions(seconds = 10)
|
298
|
+
@subscriptions_updated_at = Time.now + seconds
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|