yt-andrewroth 0.25.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. data/.gitignore +27 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +732 -0
  6. data/Gemfile +4 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +489 -0
  9. data/Rakefile +11 -0
  10. data/YOUTUBE_IT.md +835 -0
  11. data/bin/yt +30 -0
  12. data/gemfiles/Gemfile.activesupport-3.x +4 -0
  13. data/gemfiles/Gemfile.activesupport-4.x +4 -0
  14. data/lib/yt.rb +21 -0
  15. data/lib/yt/actions/base.rb +32 -0
  16. data/lib/yt/actions/delete.rb +19 -0
  17. data/lib/yt/actions/delete_all.rb +32 -0
  18. data/lib/yt/actions/insert.rb +42 -0
  19. data/lib/yt/actions/list.rb +139 -0
  20. data/lib/yt/actions/modify.rb +37 -0
  21. data/lib/yt/actions/patch.rb +19 -0
  22. data/lib/yt/actions/update.rb +19 -0
  23. data/lib/yt/associations/has_attribute.rb +55 -0
  24. data/lib/yt/associations/has_authentication.rb +214 -0
  25. data/lib/yt/associations/has_many.rb +22 -0
  26. data/lib/yt/associations/has_one.rb +22 -0
  27. data/lib/yt/associations/has_reports.rb +320 -0
  28. data/lib/yt/collections/advertising_options_sets.rb +34 -0
  29. data/lib/yt/collections/annotations.rb +62 -0
  30. data/lib/yt/collections/assets.rb +58 -0
  31. data/lib/yt/collections/authentications.rb +47 -0
  32. data/lib/yt/collections/base.rb +62 -0
  33. data/lib/yt/collections/channels.rb +31 -0
  34. data/lib/yt/collections/claim_histories.rb +34 -0
  35. data/lib/yt/collections/claims.rb +56 -0
  36. data/lib/yt/collections/content_details.rb +30 -0
  37. data/lib/yt/collections/content_owner_details.rb +34 -0
  38. data/lib/yt/collections/content_owners.rb +32 -0
  39. data/lib/yt/collections/device_flows.rb +23 -0
  40. data/lib/yt/collections/file_details.rb +30 -0
  41. data/lib/yt/collections/ids.rb +27 -0
  42. data/lib/yt/collections/live_streaming_details.rb +30 -0
  43. data/lib/yt/collections/ownerships.rb +34 -0
  44. data/lib/yt/collections/partnered_channels.rb +28 -0
  45. data/lib/yt/collections/players.rb +30 -0
  46. data/lib/yt/collections/playlist_items.rb +53 -0
  47. data/lib/yt/collections/playlists.rb +28 -0
  48. data/lib/yt/collections/policies.rb +28 -0
  49. data/lib/yt/collections/ratings.rb +23 -0
  50. data/lib/yt/collections/references.rb +46 -0
  51. data/lib/yt/collections/related_playlists.rb +43 -0
  52. data/lib/yt/collections/reports.rb +161 -0
  53. data/lib/yt/collections/resources.rb +57 -0
  54. data/lib/yt/collections/resumable_sessions.rb +51 -0
  55. data/lib/yt/collections/snippets.rb +27 -0
  56. data/lib/yt/collections/statistics_sets.rb +30 -0
  57. data/lib/yt/collections/statuses.rb +27 -0
  58. data/lib/yt/collections/subscribed_channels.rb +46 -0
  59. data/lib/yt/collections/subscribers.rb +33 -0
  60. data/lib/yt/collections/subscriptions.rb +50 -0
  61. data/lib/yt/collections/user_infos.rb +36 -0
  62. data/lib/yt/collections/video_categories.rb +35 -0
  63. data/lib/yt/collections/videos.rb +137 -0
  64. data/lib/yt/config.rb +54 -0
  65. data/lib/yt/errors/forbidden.rb +13 -0
  66. data/lib/yt/errors/missing_auth.rb +81 -0
  67. data/lib/yt/errors/no_items.rb +13 -0
  68. data/lib/yt/errors/request_error.rb +74 -0
  69. data/lib/yt/errors/server_error.rb +13 -0
  70. data/lib/yt/errors/unauthorized.rb +50 -0
  71. data/lib/yt/models/account.rb +216 -0
  72. data/lib/yt/models/advertising_options_set.rb +38 -0
  73. data/lib/yt/models/annotation.rb +132 -0
  74. data/lib/yt/models/asset.rb +111 -0
  75. data/lib/yt/models/asset_metadata.rb +38 -0
  76. data/lib/yt/models/asset_snippet.rb +46 -0
  77. data/lib/yt/models/authentication.rb +83 -0
  78. data/lib/yt/models/base.rb +32 -0
  79. data/lib/yt/models/channel.rb +302 -0
  80. data/lib/yt/models/claim.rb +156 -0
  81. data/lib/yt/models/claim_event.rb +67 -0
  82. data/lib/yt/models/claim_history.rb +29 -0
  83. data/lib/yt/models/configuration.rb +70 -0
  84. data/lib/yt/models/content_detail.rb +65 -0
  85. data/lib/yt/models/content_owner.rb +48 -0
  86. data/lib/yt/models/content_owner_detail.rb +18 -0
  87. data/lib/yt/models/description.rb +58 -0
  88. data/lib/yt/models/device_flow.rb +16 -0
  89. data/lib/yt/models/file_detail.rb +21 -0
  90. data/lib/yt/models/id.rb +9 -0
  91. data/lib/yt/models/iterator.rb +16 -0
  92. data/lib/yt/models/live_streaming_detail.rb +23 -0
  93. data/lib/yt/models/match_policy.rb +34 -0
  94. data/lib/yt/models/ownership.rb +75 -0
  95. data/lib/yt/models/player.rb +18 -0
  96. data/lib/yt/models/playlist.rb +218 -0
  97. data/lib/yt/models/playlist_item.rb +112 -0
  98. data/lib/yt/models/policy.rb +36 -0
  99. data/lib/yt/models/policy_rule.rb +124 -0
  100. data/lib/yt/models/rating.rb +37 -0
  101. data/lib/yt/models/reference.rb +172 -0
  102. data/lib/yt/models/resource.rb +136 -0
  103. data/lib/yt/models/resumable_session.rb +52 -0
  104. data/lib/yt/models/right_owner.rb +58 -0
  105. data/lib/yt/models/snippet.rb +50 -0
  106. data/lib/yt/models/statistics_set.rb +26 -0
  107. data/lib/yt/models/status.rb +32 -0
  108. data/lib/yt/models/subscription.rb +38 -0
  109. data/lib/yt/models/timestamp.rb +13 -0
  110. data/lib/yt/models/url.rb +90 -0
  111. data/lib/yt/models/user_info.rb +26 -0
  112. data/lib/yt/models/video.rb +630 -0
  113. data/lib/yt/models/video_category.rb +12 -0
  114. data/lib/yt/request.rb +278 -0
  115. data/lib/yt/version.rb +3 -0
  116. data/spec/collections/claims_spec.rb +30 -0
  117. data/spec/collections/playlist_items_spec.rb +44 -0
  118. data/spec/collections/playlists_spec.rb +27 -0
  119. data/spec/collections/policies_spec.rb +30 -0
  120. data/spec/collections/references_spec.rb +30 -0
  121. data/spec/collections/reports_spec.rb +30 -0
  122. data/spec/collections/subscriptions_spec.rb +25 -0
  123. data/spec/collections/videos_spec.rb +43 -0
  124. data/spec/errors/forbidden_spec.rb +10 -0
  125. data/spec/errors/missing_auth_spec.rb +24 -0
  126. data/spec/errors/no_items_spec.rb +10 -0
  127. data/spec/errors/request_error_spec.rb +44 -0
  128. data/spec/errors/server_error_spec.rb +10 -0
  129. data/spec/errors/unauthorized_spec.rb +10 -0
  130. data/spec/models/account_spec.rb +138 -0
  131. data/spec/models/annotation_spec.rb +180 -0
  132. data/spec/models/asset_spec.rb +20 -0
  133. data/spec/models/channel_spec.rb +127 -0
  134. data/spec/models/claim_event_spec.rb +62 -0
  135. data/spec/models/claim_history_spec.rb +27 -0
  136. data/spec/models/claim_spec.rb +211 -0
  137. data/spec/models/configuration_spec.rb +44 -0
  138. data/spec/models/content_detail_spec.rb +45 -0
  139. data/spec/models/content_owner_detail_spec.rb +6 -0
  140. data/spec/models/description_spec.rb +94 -0
  141. data/spec/models/file_detail_spec.rb +13 -0
  142. data/spec/models/live_streaming_detail_spec.rb +6 -0
  143. data/spec/models/ownership_spec.rb +59 -0
  144. data/spec/models/player_spec.rb +13 -0
  145. data/spec/models/playlist_item_spec.rb +120 -0
  146. data/spec/models/playlist_spec.rb +138 -0
  147. data/spec/models/policy_rule_spec.rb +63 -0
  148. data/spec/models/policy_spec.rb +41 -0
  149. data/spec/models/rating_spec.rb +12 -0
  150. data/spec/models/reference_spec.rb +249 -0
  151. data/spec/models/request_spec.rb +163 -0
  152. data/spec/models/resource_spec.rb +57 -0
  153. data/spec/models/right_owner_spec.rb +71 -0
  154. data/spec/models/snippet_spec.rb +13 -0
  155. data/spec/models/statistics_set_spec.rb +13 -0
  156. data/spec/models/status_spec.rb +13 -0
  157. data/spec/models/subscription_spec.rb +30 -0
  158. data/spec/models/url_spec.rb +78 -0
  159. data/spec/models/video_category_spec.rb +21 -0
  160. data/spec/models/video_spec.rb +669 -0
  161. data/spec/requests/as_account/account_spec.rb +125 -0
  162. data/spec/requests/as_account/authentications_spec.rb +139 -0
  163. data/spec/requests/as_account/channel_spec.rb +259 -0
  164. data/spec/requests/as_account/channels_spec.rb +18 -0
  165. data/spec/requests/as_account/playlist_item_spec.rb +56 -0
  166. data/spec/requests/as_account/playlist_spec.rb +244 -0
  167. data/spec/requests/as_account/resource_spec.rb +18 -0
  168. data/spec/requests/as_account/thumbnail.jpg +0 -0
  169. data/spec/requests/as_account/video.mp4 +0 -0
  170. data/spec/requests/as_account/video_spec.rb +408 -0
  171. data/spec/requests/as_content_owner/account_spec.rb +25 -0
  172. data/spec/requests/as_content_owner/advertising_options_set_spec.rb +15 -0
  173. data/spec/requests/as_content_owner/asset_spec.rb +20 -0
  174. data/spec/requests/as_content_owner/channel_spec.rb +1934 -0
  175. data/spec/requests/as_content_owner/claim_history_spec.rb +20 -0
  176. data/spec/requests/as_content_owner/content_owner_spec.rb +241 -0
  177. data/spec/requests/as_content_owner/match_policy_spec.rb +17 -0
  178. data/spec/requests/as_content_owner/ownership_spec.rb +25 -0
  179. data/spec/requests/as_content_owner/playlist_spec.rb +782 -0
  180. data/spec/requests/as_content_owner/video_spec.rb +1239 -0
  181. data/spec/requests/as_server_app/channel_spec.rb +74 -0
  182. data/spec/requests/as_server_app/playlist_item_spec.rb +30 -0
  183. data/spec/requests/as_server_app/playlist_spec.rb +53 -0
  184. data/spec/requests/as_server_app/video_spec.rb +58 -0
  185. data/spec/requests/as_server_app/videos_spec.rb +40 -0
  186. data/spec/requests/unauthenticated/video_spec.rb +22 -0
  187. data/spec/spec_helper.rb +20 -0
  188. data/spec/support/fail_matcher.rb +15 -0
  189. data/spec/support/global_hooks.rb +48 -0
  190. data/yt.gemspec +32 -0
  191. 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