youtube-g-es 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/History.txt +45 -0
- data/Manifest.txt +28 -0
- data/README.txt +86 -0
- data/TODO.txt +16 -0
- data/lib/youtube_g.rb +29 -0
- data/lib/youtube_g/client.rb +85 -0
- data/lib/youtube_g/logger.rb +25 -0
- data/lib/youtube_g/model/author.rb +11 -0
- data/lib/youtube_g/model/category.rb +11 -0
- data/lib/youtube_g/model/contact.rb +16 -0
- data/lib/youtube_g/model/content.rb +18 -0
- data/lib/youtube_g/model/playlist.rb +8 -0
- data/lib/youtube_g/model/rating.rb +17 -0
- data/lib/youtube_g/model/thumbnail.rb +17 -0
- data/lib/youtube_g/model/user.rb +20 -0
- data/lib/youtube_g/model/video.rb +190 -0
- data/lib/youtube_g/parser.rb +169 -0
- data/lib/youtube_g/record.rb +12 -0
- data/lib/youtube_g/request/base_search.rb +43 -0
- data/lib/youtube_g/request/standard_search.rb +40 -0
- data/lib/youtube_g/request/user_search.rb +42 -0
- data/lib/youtube_g/request/video_search.rb +93 -0
- data/lib/youtube_g/request/video_upload.rb +130 -0
- data/lib/youtube_g/response/video_search.rb +41 -0
- data/test/test_client.rb +268 -0
- data/test/test_video.rb +42 -0
- data/test/test_video_search.rb +138 -0
- data/youtube-g-es.gemspec +52 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTJhYTYzZjE2NWFlOGJlMzhkMjRkMjkyOGU5M2U2MmFjMjhlYzY1MA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YzEwMzdjMjRlNzYyZjYwYThjYzBiNjMyYjkzM2Q3NTVjYTllMjZhMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZTQ5ZDhkZWY1NDFlN2FlNzJiYjYwNmVlZTJlODFlYzk0NGFjMDUzMWI2MjU0
|
10
|
+
NTJlOThiNWVmZmQyZmRmMWU0ZGE0ODExNmU0OTA4NGE1NDk5ZWJhZWY2ODA1
|
11
|
+
OTc4ZDJkNzNkZmY0ZmE3MmYwNTQ0YzRjZDAyMDIwMDFiMmY3MDU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZmJkOWY3NWIxYTkyM2M2ODg0ZmZiN2U3OGI5YTFiZDk4M2NjY2VlM2VkYzBk
|
14
|
+
NTY0MjY5MmI2OTlhMWE1NWVjZGNlODE4NTE2NGJiODk5MmJhNmM5Mzg1MmZk
|
15
|
+
ODEwZTc2YTUyMDk0NjBlMGNlNzE5Yjg2MTNmOGJjODk4OGE3YWI=
|
data/History.txt
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
== 0.5.0 / 2009-01-07
|
2
|
+
|
3
|
+
* Fixed bug in user favorites (thanks Pius Uzamere)
|
4
|
+
|
5
|
+
== 0.4.9.9 / 2008-09-01
|
6
|
+
|
7
|
+
* Add Geodata information (thanks Jose Galisteo)
|
8
|
+
* Added :page and :per_page options, this allows easier usage of the will_paginate
|
9
|
+
plugin with the library. The :offset and :max_results options are no longer available. [Daniel Insley]
|
10
|
+
* Added ability to get video responses on the instances of the YouTube::Model::Video object. [Daniel Insley]
|
11
|
+
* Added and improved the existing documentation [Daniel Insley]
|
12
|
+
* Fixed usage of deprecated yt:racy, now using media:rating [Daniel Insley]
|
13
|
+
* Renamed can_embed? method to embeddable? [Daniel Insley]
|
14
|
+
* Added ability for padingation and ordering on standard feeds. [Daniel Insley]
|
15
|
+
* Add error-handling for video upload errors. [FiXato]
|
16
|
+
* Add error-handling for authentication errors from YouTube during video upload. [FiXato]
|
17
|
+
* Add support for making videos private upon video upload. [FiXato]
|
18
|
+
* Fix issue with REXML parsing of video upload response. [FiXato]
|
19
|
+
* Fix issue with response code comparison. [FiXato]
|
20
|
+
* Authcode is now retrieved for video uploads. [FiXato]
|
21
|
+
* Add basic support for uploading videos [thanks Joe Damato]
|
22
|
+
* Add basic support for related videos [tmm1]
|
23
|
+
* Improve docs for order_by attribute [thanks Jason Arora]
|
24
|
+
* Added support for the "racy" parameter (choices are "include" or "exclude") [thanks Jason Arora]
|
25
|
+
* Add missing attribute reader for description [tmm1]
|
26
|
+
* Fix issue with missing yt:statistics and viewCount [tmm1]
|
27
|
+
* Allow Client#video_by to take either a url or a video id [tmm1]
|
28
|
+
|
29
|
+
== 0.4.1 / 2008-02-11
|
30
|
+
|
31
|
+
* Added 3GPP video format [shane]
|
32
|
+
* Fixed tests [shane]
|
33
|
+
|
34
|
+
== 0.4.0 / 2007-12-18
|
35
|
+
|
36
|
+
* Fixed API projection in search URL [Pete Higgins]
|
37
|
+
* Fixed embeddable video searching [Pete Higgins]
|
38
|
+
* Fixed video embeddable detection [Pete Higgins]
|
39
|
+
* Fixed unique id hyphen detection [Pete Higgins, Chris Taggart]
|
40
|
+
|
41
|
+
== 0.3.0 / 2007-09-17
|
42
|
+
|
43
|
+
* Initial public release
|
44
|
+
* Birthday!
|
45
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
TODO.txt
|
6
|
+
lib/youtube_g.rb
|
7
|
+
lib/youtube_g/client.rb
|
8
|
+
lib/youtube_g/logger.rb
|
9
|
+
lib/youtube_g/model/author.rb
|
10
|
+
lib/youtube_g/model/category.rb
|
11
|
+
lib/youtube_g/model/contact.rb
|
12
|
+
lib/youtube_g/model/content.rb
|
13
|
+
lib/youtube_g/model/playlist.rb
|
14
|
+
lib/youtube_g/model/rating.rb
|
15
|
+
lib/youtube_g/model/thumbnail.rb
|
16
|
+
lib/youtube_g/model/user.rb
|
17
|
+
lib/youtube_g/model/video.rb
|
18
|
+
lib/youtube_g/parser.rb
|
19
|
+
lib/youtube_g/record.rb
|
20
|
+
lib/youtube_g/request/base_search.rb
|
21
|
+
lib/youtube_g/request/standard_search.rb
|
22
|
+
lib/youtube_g/request/user_search.rb
|
23
|
+
lib/youtube_g/request/video_search.rb
|
24
|
+
lib/youtube_g/request/video_upload.rb
|
25
|
+
lib/youtube_g/response/video_search.rb
|
26
|
+
test/test_client.rb
|
27
|
+
test/test_video.rb
|
28
|
+
test/test_video_search.rb
|
data/README.txt
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
youtube-g-es : Correzione Euro Servizi per gemma youtube-g, tolto break su riga 18 del file lib/youtube_g/request/user_search.rb.
|
2
|
+
|
3
|
+
youtube-g
|
4
|
+
by Shane Vitarana and Walter Korman
|
5
|
+
|
6
|
+
Rubyforge: http://rubyforge.org/projects/youtube-g/
|
7
|
+
RDoc: http://youtube-g.rubyforge.org/
|
8
|
+
Google Group: http://groups.google.com/group/ruby-youtube-library
|
9
|
+
|
10
|
+
== DESCRIPTION:
|
11
|
+
|
12
|
+
youtube-g is a pure Ruby client for the YouTube GData API. It provides an easy
|
13
|
+
way to access the latest YouTube video search results from your own programs.
|
14
|
+
In comparison with the earlier Youtube search interfaces, this new API and
|
15
|
+
library offers much-improved flexibility around executing complex search
|
16
|
+
queries to obtain well-targeted video search results.
|
17
|
+
|
18
|
+
More detail on the underlying source Google-provided API is available at:
|
19
|
+
|
20
|
+
http://code.google.com/apis/youtube/overview.html
|
21
|
+
|
22
|
+
== FEATURES/PROBLEMS:
|
23
|
+
|
24
|
+
* Aims to be in parity with Google's YouTube GData API. Core functionality
|
25
|
+
is currently present -- work is in progress to fill in the rest.
|
26
|
+
|
27
|
+
== SYNOPSIS:
|
28
|
+
|
29
|
+
Create a client:
|
30
|
+
|
31
|
+
require 'youtube_g'
|
32
|
+
client = YouTubeG::Client.new
|
33
|
+
|
34
|
+
Basic queries:
|
35
|
+
|
36
|
+
client.videos_by(:query => "penguin")
|
37
|
+
client.videos_by(:query => "penguin", :page => 2, :per_page => 15)
|
38
|
+
client.videos_by(:tags => ['tiger', 'leopard'])
|
39
|
+
client.videos_by(:categories => [:news, :sports])
|
40
|
+
client.videos_by(:categories => [:news, :sports], :tags => ['soccer', 'football'])
|
41
|
+
client.videos_by(:user => 'liz')
|
42
|
+
client.videos_by(:favorites, :user => 'liz')
|
43
|
+
|
44
|
+
Standard feeds:
|
45
|
+
|
46
|
+
client.videos_by(:most_viewed)
|
47
|
+
client.videos_by(:most_linked, :page => 3)
|
48
|
+
client.videos_by(:top_rated, :time => :today)
|
49
|
+
|
50
|
+
Advanced queries (with boolean operators OR (either), AND (include), NOT (exclude)):
|
51
|
+
|
52
|
+
client.videos_by(:categories => { :either => [:news, :sports], :exclude => [:comedy] }, :tags => { :include => ['football'], :exclude => ['soccer'] })
|
53
|
+
|
54
|
+
|
55
|
+
== REQUIREMENTS:
|
56
|
+
|
57
|
+
* None
|
58
|
+
|
59
|
+
== INSTALL:
|
60
|
+
|
61
|
+
* sudo gem install youtube-g
|
62
|
+
|
63
|
+
== LICENSE:
|
64
|
+
|
65
|
+
MIT License
|
66
|
+
|
67
|
+
Copyright (c) 2007 Shane Vitarana and Walter Korman
|
68
|
+
|
69
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
70
|
+
a copy of this software and associated documentation files (the
|
71
|
+
'Software'), to deal in the Software without restriction, including
|
72
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
73
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
74
|
+
permit persons to whom the Software is furnished to do so, subject to
|
75
|
+
the following conditions:
|
76
|
+
|
77
|
+
The above copyright notice and this permission notice shall be
|
78
|
+
included in all copies or substantial portions of the Software.
|
79
|
+
|
80
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
81
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
82
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
83
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
84
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
85
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
86
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/TODO.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
[ ] stub out http request/response cycle for tests
|
2
|
+
[ ] allow specifying values as single items where you don't need to wrap in a list, e.g. :tags => :chickens instead of :tags => [ 'chickens' ]
|
3
|
+
[ ] make sure symbols will work as well as tags everywhere (again, :tags => :chickens is same as :tags => 'chickens')
|
4
|
+
[ ] figure out better structure for class/file (either rename request/video_search.rb or split into one class per file again)
|
5
|
+
[ ] restore spaces after method def names
|
6
|
+
[ ] use a proxy for testing with static sample result xml so we have repeatable tests
|
7
|
+
[ ] Clean up tests using Shoulda to define contexts
|
8
|
+
[ ] Allow :category and :categories for query DSL
|
9
|
+
[ ] Exception handling
|
10
|
+
|
11
|
+
== API Features TODO
|
12
|
+
|
13
|
+
[ ] Profile feed parsing
|
14
|
+
[ ] Playlist feeds
|
15
|
+
[ ] User subscriptions
|
16
|
+
[ ] Video comments
|
data/lib/youtube_g.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'net/https'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'rexml/document'
|
6
|
+
require 'cgi'
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + '/youtube_g/client'
|
9
|
+
require File.dirname(__FILE__) + '/youtube_g/record'
|
10
|
+
require File.dirname(__FILE__) + '/youtube_g/parser'
|
11
|
+
require File.dirname(__FILE__) + '/youtube_g/model/author'
|
12
|
+
require File.dirname(__FILE__) + '/youtube_g/model/category'
|
13
|
+
require File.dirname(__FILE__) + '/youtube_g/model/contact'
|
14
|
+
require File.dirname(__FILE__) + '/youtube_g/model/content'
|
15
|
+
require File.dirname(__FILE__) + '/youtube_g/model/playlist'
|
16
|
+
require File.dirname(__FILE__) + '/youtube_g/model/rating'
|
17
|
+
require File.dirname(__FILE__) + '/youtube_g/model/thumbnail'
|
18
|
+
require File.dirname(__FILE__) + '/youtube_g/model/user'
|
19
|
+
require File.dirname(__FILE__) + '/youtube_g/model/video'
|
20
|
+
require File.dirname(__FILE__) + '/youtube_g/request/base_search'
|
21
|
+
require File.dirname(__FILE__) + '/youtube_g/request/user_search'
|
22
|
+
require File.dirname(__FILE__) + '/youtube_g/request/standard_search'
|
23
|
+
require File.dirname(__FILE__) + '/youtube_g/request/video_upload'
|
24
|
+
require File.dirname(__FILE__) + '/youtube_g/request/video_search'
|
25
|
+
require File.dirname(__FILE__) + '/youtube_g/response/video_search'
|
26
|
+
|
27
|
+
class YouTubeG #:nodoc:
|
28
|
+
VERSION = '0.5.0'
|
29
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class YouTubeG
|
2
|
+
class Client
|
3
|
+
attr_accessor :logger
|
4
|
+
|
5
|
+
def initialize(logger=false)
|
6
|
+
@logger = Logger.new(STDOUT) if logger
|
7
|
+
end
|
8
|
+
|
9
|
+
# Retrieves an array of standard feed, custom query, or user videos.
|
10
|
+
#
|
11
|
+
# === Parameters
|
12
|
+
# If fetching videos for a standard feed:
|
13
|
+
# params<Symbol>:: Accepts a symbol of :top_rated, :top_favorites, :most_viewed,
|
14
|
+
# :most_popular, :most_recent, :most_discussed, :most_linked,
|
15
|
+
# :most_responded, :recently_featured, and :watch_on_mobile.
|
16
|
+
#
|
17
|
+
# You can find out more specific information about what each standard feed provides
|
18
|
+
# by visiting: http://code.google.com/apis/youtube/reference.html#Standard_feeds
|
19
|
+
#
|
20
|
+
# options<Hash> (optional):: Accepts the options of :time, :page (default is 1),
|
21
|
+
# and :per_page (default is 25). :offset and :max_results
|
22
|
+
# can also be passed for a custom offset.
|
23
|
+
#
|
24
|
+
# If fetching videos by tags, categories, query:
|
25
|
+
# params<Hash>:: Accepts the keys :tags, :categories, :query, :order_by,
|
26
|
+
# :author, :racy, :response_format, :video_format, :page (default is 1),
|
27
|
+
# and :per_page(default is 25)
|
28
|
+
#
|
29
|
+
# options<Hash>:: Not used. (Optional)
|
30
|
+
#
|
31
|
+
# If fetching videos for a particular user:
|
32
|
+
# params<Hash>:: Key of :user with a value of the username.
|
33
|
+
# options<Hash>:: Not used. (Optional)
|
34
|
+
# === Returns
|
35
|
+
# YouTubeG::Response::VideoSearch
|
36
|
+
def videos_by(params, options={})
|
37
|
+
request_params = params.respond_to?(:to_hash) ? params : options
|
38
|
+
request_params[:page] = integer_or_default(request_params[:page], 1)
|
39
|
+
|
40
|
+
unless request_params[:max_results]
|
41
|
+
request_params[:max_results] = integer_or_default(request_params[:per_page], 25)
|
42
|
+
end
|
43
|
+
|
44
|
+
unless request_params[:offset]
|
45
|
+
request_params[:offset] = calculate_offset(request_params[:page], request_params[:max_results] )
|
46
|
+
end
|
47
|
+
|
48
|
+
if params.respond_to?(:to_hash) and not params[:user]
|
49
|
+
request = YouTubeG::Request::VideoSearch.new(request_params)
|
50
|
+
elsif (params.respond_to?(:to_hash) && params[:user]) || (params == :favorites)
|
51
|
+
request = YouTubeG::Request::UserSearch.new(params, request_params)
|
52
|
+
else
|
53
|
+
request = YouTubeG::Request::StandardSearch.new(params, request_params)
|
54
|
+
end
|
55
|
+
|
56
|
+
logger.debug "Submitting request [url=#{request.url}]." if logger
|
57
|
+
parser = YouTubeG::Parser::VideosFeedParser.new(request.url)
|
58
|
+
parser.parse
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieves a single YouTube video.
|
62
|
+
#
|
63
|
+
# === Parameters
|
64
|
+
# vid<String>:: The ID or URL of the video that you'd like to retrieve.
|
65
|
+
#
|
66
|
+
# === Returns
|
67
|
+
# YouTubeG::Model::Video
|
68
|
+
def video_by(vid)
|
69
|
+
video_id = vid =~ /^http/ ? vid : "http://gdata.youtube.com/feeds/videos/#{vid}"
|
70
|
+
parser = YouTubeG::Parser::VideoFeedParser.new(video_id)
|
71
|
+
parser.parse
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def calculate_offset(page, per_page)
|
77
|
+
page == 1 ? 1 : ((per_page * page) - per_page + 1)
|
78
|
+
end
|
79
|
+
|
80
|
+
def integer_or_default(value, default)
|
81
|
+
value = value.to_i
|
82
|
+
value > 0 ? value : default
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class YouTubeG
|
2
|
+
|
3
|
+
# TODO: Why is this needed? Does this happen if running standalone w/o Rails?
|
4
|
+
# Anyway, isn't it easier to debug w/o the really long timestamp & log level?
|
5
|
+
# How often do you look at the timestamp and log level? Wouldn't it be nice to
|
6
|
+
# see your logger output first?
|
7
|
+
|
8
|
+
# Extension of the base ruby Logger class to restore the default log
|
9
|
+
# level and timestamp formatting which is so rudely taken forcibly
|
10
|
+
# away from us by the Rails app's use of the ActiveSupport library
|
11
|
+
# that wholesale-ly modifies the Logger's format_message method.
|
12
|
+
#
|
13
|
+
class Logger < ::Logger
|
14
|
+
private
|
15
|
+
begin
|
16
|
+
# restore original log formatting to un-screw the screwage that is
|
17
|
+
# foisted upon us by the activesupport library's clean_logger.rb
|
18
|
+
alias format_message old_format_message
|
19
|
+
|
20
|
+
rescue NameError
|
21
|
+
# nothing for now -- this means we didn't need to alias since the
|
22
|
+
# method wasn't overridden
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class YouTubeG
|
2
|
+
module Model
|
3
|
+
class Contact < YouTubeG::Record
|
4
|
+
# *String*:: Identifies the status of a contact.
|
5
|
+
#
|
6
|
+
# * The tag's value will be accepted if the authenticated user and the contact have marked each other as friends.
|
7
|
+
# * The tag's value will be requested if the contact has asked to be added to the authenticated user's contact list, but the request has not yet been accepted (or rejected).
|
8
|
+
# * The tag's value will be pending if the authenticated user has asked to be added to the contact's contact list, but the request has not yet been accepted or rejected.
|
9
|
+
#
|
10
|
+
attr_reader :status
|
11
|
+
|
12
|
+
# *String*:: The Youtube username of the contact.
|
13
|
+
attr_reader :username
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class YouTubeG
|
2
|
+
module Model
|
3
|
+
class Content < YouTubeG::Record
|
4
|
+
# *Boolean*:: Description of the video.
|
5
|
+
attr_reader :default
|
6
|
+
# *Fixnum*:: Length of the video in seconds.
|
7
|
+
attr_reader :duration
|
8
|
+
# YouTubeG::Model::Video::Format:: Specifies the video format of the video object
|
9
|
+
attr_reader :format
|
10
|
+
# *String*:: Specifies the MIME type of the media object.
|
11
|
+
attr_reader :mime_type
|
12
|
+
# *String*:: Specifies the URL for the media object.
|
13
|
+
attr_reader :url
|
14
|
+
|
15
|
+
alias :is_default? :default
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class YouTubeG
|
2
|
+
module Model
|
3
|
+
class Rating < YouTubeG::Record
|
4
|
+
# *Float*:: Average rating given to the video
|
5
|
+
attr_reader :average
|
6
|
+
|
7
|
+
# *Fixnum*:: Maximum rating that can be assigned to the video
|
8
|
+
attr_reader :max
|
9
|
+
|
10
|
+
# *Fixnum*:: Minimum rating that can be assigned to the video
|
11
|
+
attr_reader :min
|
12
|
+
|
13
|
+
# *Fixnum*:: Indicates how many people have rated the video
|
14
|
+
attr_reader :rater_count
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class YouTubeG
|
2
|
+
module Model
|
3
|
+
class Thumbnail < YouTubeG::Record
|
4
|
+
# *String*:: URL for the thumbnail image.
|
5
|
+
attr_reader :url
|
6
|
+
|
7
|
+
# *Fixnum*:: Height of the thumbnail image.
|
8
|
+
attr_reader :height
|
9
|
+
|
10
|
+
# *Fixnum*:: Width of the thumbnail image.
|
11
|
+
attr_reader :width
|
12
|
+
|
13
|
+
# *String*:: Specifies the time offset at which the frame shown in the thumbnail image appears in the video.
|
14
|
+
attr_reader :time
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|