yt 0.5.4 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/HISTORY.md +3 -0
  4. data/README.md +2 -4
  5. data/bin/yt +0 -7
  6. data/lib/yt/actions/delete.rb +3 -3
  7. data/lib/yt/actions/insert.rb +3 -3
  8. data/lib/yt/actions/list.rb +4 -6
  9. data/lib/yt/actions/update.rb +7 -5
  10. data/lib/yt/associations/authentications.rb +114 -0
  11. data/lib/yt/associations.rb +1 -0
  12. data/lib/yt/collections/annotations.rb +2 -2
  13. data/lib/yt/collections/authentications.rb +42 -0
  14. data/lib/yt/collections/base.rb +1 -1
  15. data/lib/yt/collections/playlist_items.rb +1 -1
  16. data/lib/yt/collections/snippets.rb +1 -2
  17. data/lib/yt/collections/subscriptions.rb +1 -1
  18. data/lib/yt/collections/user_infos.rb +2 -2
  19. data/lib/yt/config.rb +0 -2
  20. data/lib/yt/errors/missing_auth.rb +50 -0
  21. data/lib/yt/errors/no_items.rb +5 -9
  22. data/lib/yt/errors/request_error.rb +52 -0
  23. data/lib/yt/models/account.rb +17 -44
  24. data/lib/yt/models/annotation.rb +117 -115
  25. data/lib/yt/models/authentication.rb +27 -0
  26. data/lib/yt/models/base.rb +9 -5
  27. data/lib/yt/models/channel.rb +6 -4
  28. data/lib/yt/models/configuration.rb +27 -35
  29. data/lib/yt/models/description.rb +61 -59
  30. data/lib/yt/models/details_set.rb +26 -24
  31. data/lib/yt/models/id.rb +3 -1
  32. data/lib/yt/models/playlist.rb +38 -36
  33. data/lib/yt/models/playlist_item.rb +29 -27
  34. data/lib/yt/models/rating.rb +18 -16
  35. data/lib/yt/models/request.rb +95 -73
  36. data/lib/yt/models/resource.rb +19 -17
  37. data/lib/yt/models/snippet.rb +39 -37
  38. data/lib/yt/models/status.rb +20 -18
  39. data/lib/yt/models/subscription.rb +24 -22
  40. data/lib/yt/models/url.rb +68 -68
  41. data/lib/yt/models/user_info.rb +51 -49
  42. data/lib/yt/models/video.rb +6 -4
  43. data/lib/yt/version.rb +1 -1
  44. data/spec/associations/device_auth/authentications_spec.rb +78 -0
  45. data/spec/associations/device_auth/channels_spec.rb +2 -4
  46. data/spec/associations/device_auth/details_sets_spec.rb +4 -5
  47. data/spec/associations/device_auth/ids_spec.rb +2 -3
  48. data/spec/associations/device_auth/playlist_items_spec.rb +3 -4
  49. data/spec/associations/device_auth/playlists_spec.rb +14 -15
  50. data/spec/associations/device_auth/ratings_spec.rb +2 -4
  51. data/spec/associations/device_auth/snippets_spec.rb +5 -7
  52. data/spec/associations/device_auth/subscriptions_spec.rb +2 -4
  53. data/spec/associations/device_auth/user_infos_spec.rb +2 -5
  54. data/spec/associations/device_auth/videos_spec.rb +3 -5
  55. data/spec/associations/server_auth/details_sets_spec.rb +1 -1
  56. data/spec/associations/server_auth/ids_spec.rb +1 -1
  57. data/spec/associations/server_auth/playlist_items_spec.rb +1 -1
  58. data/spec/associations/server_auth/playlists_spec.rb +1 -1
  59. data/spec/associations/server_auth/snippets_spec.rb +1 -1
  60. data/spec/associations/server_auth/videos_spec.rb +1 -1
  61. data/spec/collections/playlist_items_spec.rb +3 -4
  62. data/spec/collections/subscriptions_spec.rb +2 -3
  63. data/spec/errors/missing_auth_spec.rb +10 -0
  64. data/spec/errors/no_items_spec.rb +2 -1
  65. data/spec/errors/request_error_spec.rb +18 -0
  66. data/spec/models/configuration_spec.rb +0 -17
  67. data/spec/models/description_spec.rb +5 -5
  68. data/spec/models/request_spec.rb +1 -7
  69. data/spec/models/subscription_spec.rb +2 -3
  70. data/spec/models/url_spec.rb +6 -6
  71. data/spec/support/fail_matcher.rb +1 -1
  72. data/spec/support/global_hooks.rb +33 -0
  73. metadata +15 -14
  74. data/lib/yt/errors/base.rb +0 -43
  75. data/lib/yt/errors/error.rb +0 -8
  76. data/lib/yt/errors/failed.rb +0 -17
  77. data/lib/yt/errors/unauthenticated.rb +0 -34
  78. data/spec/errors/failed_spec.rb +0 -9
  79. data/spec/errors/unauthenticated_spec.rb +0 -9
  80. data/spec/support/device_app.rb +0 -15
  81. data/spec/support/server_app.rb +0 -10
@@ -1,142 +1,144 @@
1
1
  module Yt
2
- # Provides methods to access and analyze a single YouTube annotation.
3
- class Annotation
4
- # Instantiate an Annotation object from its YouTube XML representation.
5
- #
6
- # @note There is no documented way to access annotations through API.
7
- # There is an endpoint that returns an XML in an undocumented format,
8
- # which is here parsed into a comprehensible set of attributes.
9
- #
10
- # @param [String] xml_data The YouTube XML representation of an annotation
11
- def initialize(options = {})
12
- @data = options[:data]
13
- end
2
+ module Models
3
+ # Provides methods to access and analyze a single YouTube annotation.
4
+ class Annotation
5
+ # Instantiate an Annotation object from its YouTube XML representation.
6
+ #
7
+ # @note There is no documented way to access annotations through API.
8
+ # There is an endpoint that returns an XML in an undocumented format,
9
+ # which is here parsed into a comprehensible set of attributes.
10
+ #
11
+ # @param [String] xml_data The YouTube XML representation of an annotation
12
+ def initialize(options = {})
13
+ @data = options[:data]
14
+ end
14
15
 
15
- # Checks whether the entire annotation box remains above y
16
- #
17
- # @param [Integer] y Vertical position in the Youtube video (0 to 100)
18
- #
19
- # @return [Boolean] Whether the box remains above y
20
- def above?(y)
21
- top && top < y
22
- end
16
+ # Checks whether the entire annotation box remains above y
17
+ #
18
+ # @param [Integer] y Vertical position in the Youtube video (0 to 100)
19
+ #
20
+ # @return [Boolean] Whether the box remains above y
21
+ def above?(y)
22
+ top && top < y
23
+ end
23
24
 
24
- # Checks whether the entire annotation box remains below y
25
- #
26
- # @param [Integer] y Vertical position in the Youtube video (0 to 100)
27
- #
28
- # @return [Boolean] Whether the box remains below y
29
- def below?(y)
30
- bottom && bottom > y
31
- end
25
+ # Checks whether the entire annotation box remains below y
26
+ #
27
+ # @param [Integer] y Vertical position in the Youtube video (0 to 100)
28
+ #
29
+ # @return [Boolean] Whether the box remains below y
30
+ def below?(y)
31
+ bottom && bottom > y
32
+ end
32
33
 
33
- # Checks whether there is a link to subscribe.
34
- # Should a branding watermark also counts, because it links to the channel?
35
- #
36
- # @return [Boolean] Whether there is a link to subscribe in the annotation
37
- def has_link_to_subscribe?(options = {}) # TODO: options for which videos
38
- link_class == '5'
39
- end
34
+ # Checks whether there is a link to subscribe.
35
+ # Should a branding watermark also counts, because it links to the channel?
36
+ #
37
+ # @return [Boolean] Whether there is a link to subscribe in the annotation
38
+ def has_link_to_subscribe?(options = {}) # TODO: options for which videos
39
+ link_class == '5'
40
+ end
40
41
 
41
- # Checks whether there is a link to a video.
42
- # An Invideo featured video also counts
43
- #
44
- # @return [Boolean] Whether there is a link to a video in the annotation
45
- def has_link_to_video?(options = {}) # TODO: options for which videos
46
- link_class == '1' || type == 'promotion'
47
- end
42
+ # Checks whether there is a link to a video.
43
+ # An Invideo featured video also counts
44
+ #
45
+ # @return [Boolean] Whether there is a link to a video in the annotation
46
+ def has_link_to_video?(options = {}) # TODO: options for which videos
47
+ link_class == '1' || type == 'promotion'
48
+ end
48
49
 
49
- # Checks whether there is a link to a playlist.
50
- # A link to a video with the playlist in the URL also counts
51
- #
52
- # @return [Boolean] Whether there is a link to a playlist in the annotation
53
- def has_link_to_playlist?
54
- link_class == '2' || text.include?('&list=')
55
- end
50
+ # Checks whether there is a link to a playlist.
51
+ # A link to a video with the playlist in the URL also counts
52
+ #
53
+ # @return [Boolean] Whether there is a link to a playlist in the annotation
54
+ def has_link_to_playlist?
55
+ link_class == '2' || text.include?('&list=')
56
+ end
56
57
 
57
- # Checks whether the link opens in the same window.
58
- #
59
- # @return [Boolean] Whether the link opens in the same window
60
- def has_link_to_same_window?
61
- link_target == 'current'
62
- end
58
+ # Checks whether the link opens in the same window.
59
+ #
60
+ # @return [Boolean] Whether the link opens in the same window
61
+ def has_link_to_same_window?
62
+ link_target == 'current'
63
+ end
63
64
 
64
- # Checks whether the annotation comes from InVideo Programming
65
- #
66
- # @return [Boolean] Whether the annotation comes from InVideo Programming
67
- def has_invideo_programming?
68
- type == 'promotion' || type == 'branding'
69
- end
65
+ # Checks whether the annotation comes from InVideo Programming
66
+ #
67
+ # @return [Boolean] Whether the annotation comes from InVideo Programming
68
+ def has_invideo_programming?
69
+ type == 'promotion' || type == 'branding'
70
+ end
70
71
 
71
- # @return [Boolean] Whether the annotation starts after the number of seconds
72
- # @note This is broken for invideo programming, because they do not
73
- # have the timestamp in the region, but in the "data" field
74
- def starts_after?(seconds)
75
- timestamps.first > seconds if timestamps.any?
76
- end
72
+ # @return [Boolean] Whether the annotation starts after the number of seconds
73
+ # @note This is broken for invideo programming, because they do not
74
+ # have the timestamp in the region, but in the "data" field
75
+ def starts_after?(seconds)
76
+ timestamps.first > seconds if timestamps.any?
77
+ end
77
78
 
78
- # @return [Boolean] Whether the annotation starts before the number of seconds
79
- # @note This is broken for invideo programming, because they do not
80
- # have the timestamp in the region, but in the "data" field
81
- def starts_before?(seconds)
82
- timestamps.first < seconds if timestamps.any?
83
- end
79
+ # @return [Boolean] Whether the annotation starts before the number of seconds
80
+ # @note This is broken for invideo programming, because they do not
81
+ # have the timestamp in the region, but in the "data" field
82
+ def starts_before?(seconds)
83
+ timestamps.first < seconds if timestamps.any?
84
+ end
84
85
 
85
- private
86
+ private
86
87
 
87
- def text
88
- @text ||= @data.fetch 'TEXT', ''
89
- end
88
+ def text
89
+ @text ||= @data.fetch 'TEXT', ''
90
+ end
90
91
 
91
- def type
92
- @type ||= @data.fetch 'type', ''
93
- end
92
+ def type
93
+ @type ||= @data.fetch 'type', ''
94
+ end
94
95
 
95
- def link_class
96
- @link_class ||= url['link_class']
97
- end
96
+ def link_class
97
+ @link_class ||= url['link_class']
98
+ end
98
99
 
99
- def link_target
100
- @link_target ||= url['target']
101
- end
100
+ def link_target
101
+ @link_target ||= url['target']
102
+ end
102
103
 
103
- def url
104
- @url ||= action.fetch 'url', {}
105
- end
104
+ def url
105
+ @url ||= action.fetch 'url', {}
106
+ end
106
107
 
107
- def action
108
- @action ||= @data.fetch 'action', {}
109
- end
108
+ def action
109
+ @action ||= @data.fetch 'action', {}
110
+ end
110
111
 
111
- def top
112
- @top ||= positions.map{|pos| pos['y'].to_f}.max
113
- end
112
+ def top
113
+ @top ||= positions.map{|pos| pos['y'].to_f}.max
114
+ end
114
115
 
115
- def bottom
116
- @bottom ||= positions.map{|pos| pos['y'].to_f + pos['h'].to_f}.max
117
- end
116
+ def bottom
117
+ @bottom ||= positions.map{|pos| pos['y'].to_f + pos['h'].to_f}.max
118
+ end
118
119
 
119
- def timestamps
120
- @timestamps ||= positions.map do |pos|
121
- regex = %r{(?:|(?<hours>\d*):)(?:|(?<min>\d*):)(?<sec>\d*)\.(?<ms>\d*)}
122
- match = pos['t'].match regex
123
- hours = (match[:hours] || '0').to_i
124
- minutes = (match[:min] || '0').to_i
125
- seconds = (match[:sec]).to_i
126
- (hours * 60 + minutes) * 60 + seconds
120
+ def timestamps
121
+ @timestamps ||= positions.map do |pos|
122
+ regex = %r{(?:|(?<hours>\d*):)(?:|(?<min>\d*):)(?<sec>\d*)\.(?<ms>\d*)}
123
+ match = pos['t'].match regex
124
+ hours = (match[:hours] || '0').to_i
125
+ minutes = (match[:min] || '0').to_i
126
+ seconds = (match[:sec]).to_i
127
+ (hours * 60 + minutes) * 60 + seconds
128
+ end
127
129
  end
128
- end
129
130
 
130
- def positions
131
- @positions ||= region['rectRegion'] || region['anchoredRegion'] || []
132
- end
131
+ def positions
132
+ @positions ||= region['rectRegion'] || region['anchoredRegion'] || []
133
+ end
133
134
 
134
- def region
135
- @region ||= segment.fetch 'movingRegion', {}
136
- end
135
+ def region
136
+ @region ||= segment.fetch 'movingRegion', {}
137
+ end
137
138
 
138
- def segment
139
- @segment ||= (@data['segment'] || {})
139
+ def segment
140
+ @segment ||= (@data['segment'] || {})
141
+ end
140
142
  end
141
143
  end
142
144
  end
@@ -0,0 +1,27 @@
1
+ module Yt
2
+ module Models
3
+ class Authentication
4
+ attr_reader :access_token, :refresh_token, :expires_at
5
+
6
+ def initialize(data = {})
7
+ @access_token = data['access_token']
8
+ @refresh_token = data['refresh_token']
9
+ @expires_at = expiration_date data.slice('expires_at', 'expires_in')
10
+ end
11
+
12
+ def expired?
13
+ @expires_at && @expires_at.past?
14
+ end
15
+
16
+ private
17
+
18
+ def expiration_date(options = {})
19
+ if options['expires_in']
20
+ Time.now + options['expires_in'].seconds
21
+ else
22
+ Time.parse options['expires_at'] rescue nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,12 +1,16 @@
1
1
  require 'yt/associations'
2
2
  require 'yt/actions/delete'
3
3
  require 'yt/actions/update'
4
- require 'yt/errors/error'
4
+ require 'yt/errors/request_error'
5
5
 
6
6
  module Yt
7
- class Base
8
- extend Associations
9
- include Actions::Delete
10
- include Actions::Update
7
+ module Models
8
+ class Base
9
+ extend Associations
10
+ include Actions::Delete
11
+ include Actions::Update
12
+ end
11
13
  end
14
+
15
+ include Models
12
16
  end
@@ -1,9 +1,11 @@
1
1
  require 'yt/models/resource'
2
2
 
3
3
  module Yt
4
- class Channel < Resource
5
- has_many :subscriptions
6
- has_many :videos
7
- has_many :playlists
4
+ module Models
5
+ class Channel < Resource
6
+ has_many :subscriptions
7
+ has_many :videos
8
+ has_many :playlists
9
+ end
8
10
  end
9
11
  end
@@ -1,40 +1,32 @@
1
1
  module Yt
2
- # Stores runtime configuration information.
3
- #
4
- # Configuration options are loaded from `~/.yt`, `.yt`, command line
5
- # switches, and the `YT_OPTS` environment variable (listed in lowest to
6
- # highest precedence).
7
- #
8
- # @example A server-to-server YouTube client app
9
- #
10
- # Yt.configure do |config|
11
- # config.scenario = :server_app
12
- # config.api_key = 'ABCDEFGHIJ1234567890'
13
- # end
14
- #
15
- # @example A web YouTube client app
16
- #
17
- # Yt.configure do |config|
18
- # config.client_id = 'ABCDEFGHIJ1234567890'
19
- # config.client_secret = 'ABCDEFGHIJ1234567890'
20
- # end
21
- #
22
- class Configuration
23
- attr_accessor :scenario, :api_key, :client_id, :client_secret, :account
2
+ module Models
3
+ # Stores runtime configuration information.
4
+ #
5
+ # Configuration options are loaded from `~/.yt`, `.yt`, command line
6
+ # switches, and the `YT_OPTS` environment variable (listed in lowest to
7
+ # highest precedence).
8
+ #
9
+ # @example A server-to-server YouTube client app
10
+ #
11
+ # Yt.configure do |config|
12
+ # config.api_key = 'ABCDEFGHIJ1234567890'
13
+ # end
14
+ #
15
+ # @example A web YouTube client app
16
+ #
17
+ # Yt.configure do |config|
18
+ # config.client_id = 'ABCDEFGHIJ1234567890'
19
+ # config.client_secret = 'ABCDEFGHIJ1234567890'
20
+ # end
21
+ #
22
+ class Configuration
23
+ attr_accessor :api_key, :client_id, :client_secret
24
24
 
25
- def initialize
26
- @scenario = set_scenario ENV['YT_CLIENT_SCENARIO']
27
- @client_id = ENV['YT_CLIENT_ID']
28
- @client_secret = ENV['YT_CLIENT_SECRET']
29
- @api_key = ENV['YT_API_KEY']
30
- end
31
-
32
- private
33
-
34
- def set_scenario(value)
35
- valid_scenarios = [:web_app, :device_app, :server_app]
36
- scenario = valid_scenarios.find{|scenario| scenario.to_s == value}
37
- scenario || :web_app
25
+ def initialize
26
+ @client_id = ENV['YT_CLIENT_ID']
27
+ @client_secret = ENV['YT_CLIENT_SECRET']
28
+ @api_key = ENV['YT_API_KEY']
29
+ end
38
30
  end
39
31
  end
40
32
  end
@@ -1,74 +1,76 @@
1
1
  require 'yt/models/url'
2
2
 
3
3
  module Yt
4
- # Provides read-only access to the description of a YouTube resource.
5
- # Resources with descriptions are: videos and channels.
6
- #
7
- # @example
8
- #
9
- # description = Yt::Description.new 'Fullscreen provides a suite of end-to-end YouTube tools and services to many of the world’s leading brands and media companies.'
10
- # description.to_s.slice(0,19) # => 'Fullscreen provides'
11
- # description.length # => 127
12
- #
13
- class Description < String
14
- # Returns whether the description includes a YouTube video URL
4
+ module Models
5
+ # Provides read-only access to the description of a YouTube resource.
6
+ # Resources with descriptions are: videos and channels.
15
7
  #
16
8
  # @example
17
9
  #
18
- # description = Yt::Description.new 'Link to video: youtube.com/watch?v=MESycYJytkU'
19
- # description.has_link_to_video? #=> true
10
+ # description = Yt::Description.new 'Fullscreen provides a suite of end-to-end YouTube tools and services to many of the world’s leading brands and media companies.'
11
+ # description.to_s.slice(0,19) # => 'Fullscreen provides'
12
+ # description.length # => 127
20
13
  #
21
- # @return [Boolean] Whether the description includes a link to a video
22
- def has_link_to_video?
23
- # TODO: might take as an option WHICH video to link to
24
- # in order to check if it's my own video
25
- links.any?{|link| link.kind == :video}
26
- end
14
+ class Description < String
15
+ # Returns whether the description includes a YouTube video URL
16
+ #
17
+ # @example
18
+ #
19
+ # description = Yt::Description.new 'Link to video: youtube.com/watch?v=MESycYJytkU'
20
+ # description.has_link_to_video? #=> true
21
+ #
22
+ # @return [Boolean] Whether the description includes a link to a video
23
+ def has_link_to_video?
24
+ # TODO: might take as an option WHICH video to link to
25
+ # in order to check if it's my own video
26
+ links.any?{|link| link.kind == :video}
27
+ end
27
28
 
28
- # Returns whether the description includes a YouTube channel URL
29
- #
30
- # @example
31
- #
32
- # description = Yt::Description.new 'Link to channel: youtube.com/fullscreen'
33
- # description.has_link_to_channel? #=> true
34
- #
35
- # @return [Boolean] Whether the description includes a link to a channel
36
- def has_link_to_channel?(options = {}) # TODO: which channel
37
- # TODO: might take as an option WHICH channel to link to
38
- # in order to check if it's my own channel
39
- links.any?{|link| link.kind == :channel}
40
- end
29
+ # Returns whether the description includes a YouTube channel URL
30
+ #
31
+ # @example
32
+ #
33
+ # description = Yt::Description.new 'Link to channel: youtube.com/fullscreen'
34
+ # description.has_link_to_channel? #=> true
35
+ #
36
+ # @return [Boolean] Whether the description includes a link to a channel
37
+ def has_link_to_channel?(options = {}) # TODO: which channel
38
+ # TODO: might take as an option WHICH channel to link to
39
+ # in order to check if it's my own channel
40
+ links.any?{|link| link.kind == :channel}
41
+ end
41
42
 
42
- # Returns whether the description includes a YouTube subscription URL
43
- #
44
- # @example
45
- #
46
- # description = Yt::Description.new 'Link to subscribe: youtube.com/subscription_center?add_user=fullscreen'
47
- # description.has_link_to_subscribe? #=> true
48
- #
49
- # @return [Boolean] Whether the description includes a link to subscribe
50
- def has_link_to_subscribe?(options = {}) # TODO: which channel
51
- # TODO: might take as an option WHICH channel to subscribe to
52
- # in order to check if it's my own channel
53
- links.any?{|link| link.kind == :subscription}
54
- end
43
+ # Returns whether the description includes a YouTube subscription URL
44
+ #
45
+ # @example
46
+ #
47
+ # description = Yt::Description.new 'Link to subscribe: youtube.com/subscription_center?add_user=fullscreen'
48
+ # description.has_link_to_subscribe? #=> true
49
+ #
50
+ # @return [Boolean] Whether the description includes a link to subscribe
51
+ def has_link_to_subscribe?(options = {}) # TODO: which channel
52
+ # TODO: might take as an option WHICH channel to subscribe to
53
+ # in order to check if it's my own channel
54
+ links.any?{|link| link.kind == :subscription}
55
+ end
55
56
 
56
- # Returns whether the description includes a YouTube playlist URL
57
- #
58
- # @example
59
- #
60
- # description = Yt::Description.new 'Link to playlist: youtube.com/playlist?list=LLxO1tY8h1AhOz0T4ENwmpow'
61
- # description.has_link_to_playlist? #=> true
62
- #
63
- # @return [Boolean] Whether the description includes a link to a playlist
64
- def has_link_to_playlist?
65
- links.any?{|link| link.kind == :playlist}
66
- end
57
+ # Returns whether the description includes a YouTube playlist URL
58
+ #
59
+ # @example
60
+ #
61
+ # description = Yt::Description.new 'Link to playlist: youtube.com/playlist?list=LLxO1tY8h1AhOz0T4ENwmpow'
62
+ # description.has_link_to_playlist? #=> true
63
+ #
64
+ # @return [Boolean] Whether the description includes a link to a playlist
65
+ def has_link_to_playlist?
66
+ links.any?{|link| link.kind == :playlist}
67
+ end
67
68
 
68
- private
69
+ private
69
70
 
70
- def links
71
- @links ||= self.split(' ').map{|word| URL.new word}
71
+ def links
72
+ @links ||= self.split(' ').map{|word| URL.new word}
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -1,34 +1,36 @@
1
1
  require 'yt/models/base'
2
2
 
3
3
  module Yt
4
- class DetailsSet < Base
4
+ module Models
5
+ class DetailsSet < Base
5
6
 
6
- def initialize(options = {})
7
- @data = options[:data]
8
- end
7
+ def initialize(options = {})
8
+ @data = options[:data]
9
+ end
9
10
 
10
- # Return the duration of the YouTube video.
11
- #
12
- # @return [Integer] Duration in seconds of the YouTube video
13
- def duration
14
- @duration = to_seconds @data.fetch('duration', 0)
15
- end
16
- # also available: dimension, definition, caption, licensed_content?
11
+ # Return the duration of the YouTube video.
12
+ #
13
+ # @return [Integer] Duration in seconds of the YouTube video
14
+ def duration
15
+ @duration = to_seconds @data.fetch('duration', 0)
16
+ end
17
+ # also available: dimension, definition, caption, licensed_content?
17
18
 
18
- private
19
+ private
19
20
 
20
- # The length of the video. The tag value is an ISO 8601 duration in the format PT#M#S,
21
- # in which the letters PT indicate that the value specifies a period of time, and
22
- # the letters M and S refer to length in minutes and seconds, respectively. The #
23
- # characters preceding the M and S letters are both integers that specify the number
24
- # of minutes (or seconds) of the video. For example, a value of PT15M51S indicates
25
- # that the video is 15 minutes and 51 seconds long.
26
- def to_seconds(iso8601_duration)
27
- match = iso8601_duration.match %r{^PT(?:|(?<hours>\d*?)H)(?:|(?<min>\d*?)M)(?:|(?<sec>\d*?)S)$}
28
- hours = (match[:hours] || '0').to_i
29
- minutes = (match[:min] || '0').to_i
30
- seconds = (match[:sec]).to_i
31
- (hours * 60 + minutes) * 60 + seconds
21
+ # The length of the video. The tag value is an ISO 8601 duration in the format PT#M#S,
22
+ # in which the letters PT indicate that the value specifies a period of time, and
23
+ # the letters M and S refer to length in minutes and seconds, respectively. The #
24
+ # characters preceding the M and S letters are both integers that specify the number
25
+ # of minutes (or seconds) of the video. For example, a value of PT15M51S indicates
26
+ # that the video is 15 minutes and 51 seconds long.
27
+ def to_seconds(iso8601_duration)
28
+ match = iso8601_duration.match %r{^PT(?:|(?<hours>\d*?)H)(?:|(?<min>\d*?)M)(?:|(?<sec>\d*?)S)$}
29
+ hours = (match[:hours] || '0').to_i
30
+ minutes = (match[:min] || '0').to_i
31
+ seconds = (match[:sec]).to_i
32
+ (hours * 60 + minutes) * 60 + seconds
33
+ end
32
34
  end
33
35
  end
34
36
  end
data/lib/yt/models/id.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  module Yt
2
- class Id < String
2
+ module Models
3
+ class Id < String
4
+ end
3
5
  end
4
6
  end