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,36 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/user_info'
3
+
4
+ module Yt
5
+ module Collections
6
+ # @private
7
+ class UserInfos < Base
8
+
9
+ private
10
+
11
+ # @return [Hash] the parameters to submit to YouTube to get the
12
+ # user info of an account
13
+ # @see https://developers.google.com/+/api/latest/people/getOpenIdConnect
14
+ def list_params
15
+ super.tap do |params|
16
+ params[:path] = '/oauth2/v2/userinfo'
17
+ params[:expected_response] = Net::HTTPOK
18
+ end
19
+ end
20
+
21
+ # next_page is overloaded here because, differently from the other
22
+ # endpoints, asking for the user info does not return a paginated result,
23
+ # so @page_token has to be explcitly set to nil, and the result wrapped
24
+ # in an Array.
25
+ def next_page
26
+ request = Yt::Request.new(list_params).tap do |request|
27
+ print "#{request.as_curl}\n" if Yt.configuration.developing?
28
+ end
29
+ response = request.run
30
+ @page_token = nil
31
+
32
+ Array.wrap response.body
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/video_category'
3
+
4
+ module Yt
5
+ module Collections
6
+ class VideoCategories < Base
7
+
8
+ private
9
+
10
+ def attributes_for_new_item(data)
11
+ {}.tap do |attributes|
12
+ attributes[:id] = data['id']
13
+ attributes[:snippet] = data['snippet']
14
+ attributes[:auth] = @auth
15
+ end
16
+ end
17
+
18
+ # @return [Hash] the parameters to submit to YouTube to list video categories.
19
+ # @see https://developers.google.com/youtube/v3/docs/videoCategories/list
20
+ def list_params
21
+ super.tap do |params|
22
+ params[:params] = video_categories_params
23
+ end
24
+ end
25
+
26
+ def video_categories_params
27
+ {}.tap do |params|
28
+ params[:part] = 'snippet'
29
+ params[:id] = @parent.category_id if @parent
30
+ apply_where_params! params
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,137 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/video'
3
+
4
+ module Yt
5
+ module Collections
6
+ # Provides methods to interact with a collection of YouTube videos.
7
+ #
8
+ # Resources with videos are: {Yt::Models::Channel channels} and
9
+ # {Yt::Models::Account accounts}.
10
+ class Videos < Base
11
+ def where(requirements = {})
12
+ @published_before = nil
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def attributes_for_new_item(data)
19
+ id = use_list_endpoint? ? data['id'] : data['id']['videoId']
20
+ snippet = data['snippet'].reverse_merge complete: false if data['snippet']
21
+ {}.tap do |attributes|
22
+ attributes[:id] = id
23
+ attributes[:snippet] = snippet
24
+ attributes[:status] = data['status']
25
+ attributes[:content_details] = data['contentDetails']
26
+ attributes[:statistics] = data['statistics']
27
+ attributes[:video_category] = data['videoCategory']
28
+ attributes[:auth] = @auth
29
+ end
30
+ end
31
+
32
+ def eager_load_items_from(items)
33
+ if included_relationships.any?
34
+ include_category = included_relationships.delete(:category)
35
+ included_relationships.append(:snippet).uniq! if include_category
36
+
37
+ ids = items.map{|item| item['id']['videoId']}
38
+ parts = included_relationships.map{|r| r.to_s.camelize(:lower)}
39
+ conditions = {id: ids.join(','), part: parts.join(',')}
40
+ videos = Collections::Videos.new(auth: @auth).where conditions
41
+
42
+ items.each do |item|
43
+ video = videos.find{|v| v.id == item['id']['videoId']}
44
+ parts.each do |part|
45
+ item[part] = case part
46
+ when 'snippet' then video.snippet.data.merge complete: true
47
+ when 'status' then video.status.data
48
+ when 'statistics' then video.statistics_set.data
49
+ when 'contentDetails' then video.content_detail.data
50
+ end
51
+ end if video
52
+ end
53
+
54
+ if include_category
55
+ category_ids = items.map{|item| item['snippet']['categoryId']}.uniq
56
+ conditions = {id: category_ids.join(',')}
57
+ video_categories = Collections::VideoCategories.new(auth: @auth).where conditions
58
+
59
+ items.each do |item|
60
+ video_category = video_categories.find{|v| v.id == item['snippet']['categoryId']}
61
+ item['videoCategory'] = video_category.data
62
+ end
63
+ end
64
+ end
65
+ super
66
+ end
67
+
68
+ # @return [Hash] the parameters to submit to YouTube to list videos.
69
+ # @see https://developers.google.com/youtube/v3/docs/search/list
70
+ def list_params
71
+ super.tap do |params|
72
+ params[:params] = videos_params
73
+ params[:path] = videos_path
74
+ end
75
+ end
76
+
77
+ def next_page
78
+ super.tap do |items|
79
+ halt_list if use_list_endpoint? && items.empty? && @page_token.nil?
80
+ add_offset_to(items) if !use_list_endpoint? && @page_token.nil? && videos_params[:order] == 'date'
81
+ end
82
+ end
83
+
84
+ # According to http://stackoverflow.com/a/23256768 YouTube does not
85
+ # provide more than 500 results for any query. In order to overcome
86
+ # that limit, the query is restarted with a publishedBefore filter in
87
+ # case there are more videos to be listed for a channel
88
+ def add_offset_to(items)
89
+ if items.count == videos_params[:max_results]
90
+ last_published = items.last['snippet']['publishedAt']
91
+ @page_token, @published_before = '', last_published
92
+ end
93
+ end
94
+
95
+ # If we ask for a list of videos matching specific IDs and no video is
96
+ # returned (e.g. they are all private/deleted), then we don’t want to
97
+ # switch from /videos to /search and keep on looking for videos, but
98
+ # simply return an empty array of items
99
+ def halt_list
100
+ @halt_list = true
101
+ end
102
+
103
+ def more_pages?
104
+ (@last_index.zero? && !@halt_list) || !@page_token.nil?
105
+ end
106
+
107
+ def videos_params
108
+ {}.tap do |params|
109
+ params[:type] = :video
110
+ params[:max_results] = 50
111
+ params[:part] = 'snippet'
112
+ params[:order] = 'date'
113
+ params.merge! @parent.videos_params if @parent
114
+ apply_where_params! params
115
+ params[:published_before] = @published_before if @published_before
116
+ params
117
+ end
118
+ end
119
+
120
+ def videos_path
121
+ use_list_endpoint? ? '/youtube/v3/videos' : '/youtube/v3/search'
122
+ end
123
+
124
+ # @private
125
+ # YouTube API provides two different endpoints to get a list of videos:
126
+ # /videos should be used when the query specifies video IDs or a chart,
127
+ # /search otherwise.
128
+ # @return [Boolean] whether to use the /videos endpoint.
129
+ # @todo: This is one of three places outside of base.rb where @where_params
130
+ # is accessed; it should be replaced with a filter on params instead.
131
+ def use_list_endpoint?
132
+ @where_params ||= {}
133
+ @parent.nil? && (@where_params.keys & [:id, :chart]).any?
134
+ end
135
+ end
136
+ end
137
+ end
data/lib/yt/config.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'yt/models/configuration'
2
+
3
+ module Yt
4
+ # Provides methods to read and write global configuration settings.
5
+ #
6
+ # A typical usage is to set the API keys retrieved from the
7
+ # {http://console.developers.google.com Google Developers Console}.
8
+ #
9
+ # @example Set the API key for a server-only YouTube app:
10
+ # Yt.configure do |config|
11
+ # config.api_key = 'ABCDEFGHIJ1234567890'
12
+ # end
13
+ #
14
+ # @example Set the API client id/secret for a web-client YouTube app:
15
+ # Yt.configure do |config|
16
+ # config.client_id = 'ABCDEFGHIJ1234567890'
17
+ # config.client_secret = 'ABCDEFGHIJ1234567890'
18
+ # end
19
+ #
20
+ # Note that Yt.configure has precedence over values through with
21
+ # environment variables (see {Yt::Models::Configuration}).
22
+ #
23
+ module Config
24
+ # Yields the global configuration to the given block.
25
+ #
26
+ # @example
27
+ # Yt.configure do |config|
28
+ # config.api_key = 'ABCDEFGHIJ1234567890'
29
+ # end
30
+ #
31
+ # @yield [Yt::Models::Configuration] The global configuration.
32
+ def configure
33
+ yield configuration if block_given?
34
+ end
35
+
36
+ # Returns the global {Yt::Models::Configuration} object.
37
+ #
38
+ # While this method _can_ be used to read and write configuration settings,
39
+ # it is easier to use {Yt::Config#configure} Yt.configure}.
40
+ #
41
+ # @example
42
+ # Yt.configuration.api_key = 'ABCDEFGHIJ1234567890'
43
+ #
44
+ # @return [Yt::Models::Configuration] The global configuration.
45
+ def configuration
46
+ @configuration ||= Yt::Configuration.new
47
+ end
48
+ end
49
+
50
+ # @note Config is the only module auto-loaded in the Yt module,
51
+ # in order to have a syntax as easy as Yt.configure
52
+
53
+ extend Config
54
+ end
@@ -0,0 +1,13 @@
1
+ require 'yt/errors/request_error'
2
+
3
+ module Yt
4
+ module Errors
5
+ class Forbidden < RequestError
6
+ private
7
+
8
+ def explanation
9
+ 'A request to YouTube API was considered forbidden by the server'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,81 @@
1
+ require 'yt/errors/request_error'
2
+
3
+ module Yt
4
+ module Errors
5
+ class MissingAuth < RequestError
6
+ def message
7
+ <<-MSG.gsub(/^ {8}/, '')
8
+ A request to YouTube API was sent without a valid authentication.
9
+
10
+ #{more_details}
11
+ MSG
12
+ end
13
+
14
+ def more_details
15
+ if scopes && authentication_url && redirect_uri
16
+ more_details_with_authentication_url
17
+ elsif scopes && user_code && verification_url
18
+ more_details_with_verification_url
19
+ else
20
+ more_details_without_url
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def more_details_with_authentication_url
27
+ <<-MSG.gsub(/^ {8}/, '')
28
+ You can ask YouTube accounts to authenticate your app for the scopes
29
+ #{scopes} by directing them to #{authentication_url}.
30
+
31
+ After they provide access to their account, they will be redirected to
32
+ #{redirect_uri} with a 'code' query parameter that you can read and use
33
+ to build an authorized account object by running:
34
+
35
+ Yt::Account.new authorization_code: code, redirect_uri: "#{redirect_uri}"
36
+ MSG
37
+ end
38
+
39
+ def more_details_with_verification_url
40
+ <<-MSG.gsub(/^ {8}/, '')
41
+ Please authenticate your app by visiting the page #{verification_url}
42
+ and entering the code #{user_code} before continuing.
43
+ MSG
44
+ end
45
+
46
+ def more_details_without_url
47
+ <<-MSG.gsub(/^ {8}/, '')
48
+ If you know the access token of the YouTube you want to authenticate
49
+ with, build an authorized account object by running:
50
+
51
+ Yt::Account.new access_token: access_token
52
+
53
+ If you know the refresh token of the YouTube you want to authenticate
54
+ with, build an authorized account object by running:
55
+
56
+ Yt::Account.new refresh_token: refresh_token
57
+ MSG
58
+ end
59
+
60
+ def scopes
61
+ @msg[:scopes]
62
+ end
63
+
64
+ def authentication_url
65
+ @msg[:authentication_url]
66
+ end
67
+
68
+ def redirect_uri
69
+ @msg[:redirect_uri]
70
+ end
71
+
72
+ def user_code
73
+ @msg[:user_code]
74
+ end
75
+
76
+ def verification_url
77
+ @msg[:verification_url]
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,13 @@
1
+ require 'yt/errors/request_error'
2
+
3
+ module Yt
4
+ module Errors
5
+ class NoItems < RequestError
6
+ private
7
+
8
+ def explanation
9
+ 'A request to YouTube API returned no items but some were expected'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,74 @@
1
+ require 'yt/config'
2
+
3
+ module Yt
4
+ module Errors
5
+ class RequestError < StandardError
6
+ def initialize(msg = {})
7
+ @msg = msg
8
+ super msg
9
+ end
10
+
11
+ def message
12
+ <<-MSG.gsub(/^ {8}/, '')
13
+ #{explanation}:
14
+ #{Yt.configuration.debugging? ? details : no_details }
15
+ MSG
16
+ end
17
+
18
+ def kind
19
+ response_body.fetch 'error', {}
20
+ end
21
+
22
+ def reasons
23
+ case kind
24
+ when Hash then kind.fetch('errors', []).map{|e| e['reason']}
25
+ else kind
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def explanation
32
+ 'A request to YouTube API failed'
33
+ end
34
+
35
+
36
+ def details
37
+ <<-MSG.gsub(/^ {8}/, '')
38
+ #{response_body}
39
+
40
+ You can retry the same request manually by running:
41
+ #{request_curl}
42
+ #{more_details}
43
+ MSG
44
+ end
45
+
46
+ def no_details
47
+ <<-MSG.gsub(/^ {8}/, '')
48
+ To display more verbose errors, change the configuration of Yt with:
49
+
50
+ Yt.configure do |config|
51
+ config.log_level = :debug
52
+ end
53
+ MSG
54
+ end
55
+
56
+ def more_details
57
+ end
58
+
59
+ def response_body
60
+ json['response_body'].is_a?(Hash) ? json['response_body'] : {}
61
+ end
62
+
63
+ def request_curl
64
+ json['request_curl']
65
+ end
66
+
67
+ def json
68
+ @json ||= JSON(@msg) rescue {}
69
+ end
70
+ end
71
+ end
72
+
73
+ Error = Errors::RequestError
74
+ end