agiley-youtube-g 0.6.2

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.
@@ -0,0 +1,11 @@
1
+ class YouTubeG
2
+ module Model
3
+ class Author < YouTubeG::Record
4
+ # *String*: Author's YouTube username.
5
+ attr_reader :name
6
+
7
+ # *String*: Feed URL of the author.
8
+ attr_reader :uri
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class YouTubeG
2
+ module Model
3
+ class Category < YouTubeG::Record
4
+ # *String*:: Name of the YouTube category
5
+ attr_reader :label
6
+
7
+ # *String*:: Identifies the type of item described.
8
+ attr_reader :term
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ class YouTubeG
2
+ module Model
3
+ class Comment < YouTubeG::Record
4
+ # *Time*:: When the video was published on Youtube.
5
+ attr_reader :published_at
6
+
7
+ # *Time*:: When the video's data was last updated.
8
+ attr_reader :updated_at
9
+
10
+ # *String*:: Title for the video.
11
+ attr_reader :title
12
+
13
+ # *String*:: Description of the video.
14
+ attr_reader :content
15
+
16
+ # YouTubeG::Model::Author:: Information about the YouTube user who owns a piece of video content.
17
+ attr_reader :author
18
+ end
19
+ end
20
+ 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,8 @@
1
+ class YouTubeG
2
+ module Model
3
+ class Playlist < YouTubeG::Record
4
+ # *String*:: User entered description for the playlist.
5
+ attr_reader :description
6
+ end
7
+ end
8
+ 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
@@ -0,0 +1,20 @@
1
+ class YouTubeG
2
+ module Model
3
+ class User < YouTubeG::Record
4
+ attr_reader :age
5
+ attr_reader :books
6
+ attr_reader :company
7
+ attr_reader :gender
8
+ attr_reader :hobbies
9
+ attr_reader :hometown
10
+ attr_reader :location
11
+ attr_reader :movies
12
+ attr_reader :music
13
+ attr_reader :occupation
14
+ attr_reader :relationship
15
+ attr_reader :school
16
+ attr_reader :description
17
+ attr_reader :username
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,204 @@
1
+ # TODO
2
+ # * self atom feed
3
+ # * alternate youtube watch url
4
+ # * comments feedLink
5
+
6
+ class YouTubeG
7
+ module Model
8
+ class Video < YouTubeG::Record
9
+ # Describes the various file formats in which a Youtube video may be
10
+ # made available and allows looking them up by format code number.
11
+ class Format
12
+ @@formats = Hash.new
13
+
14
+ # Instantiates a new video format object.
15
+ #
16
+ # == Parameters
17
+ # :format_code<Fixnum>:: The Youtube Format code of the object.
18
+ # :name<Symbol>:: The name of the format
19
+ #
20
+ # == Returns
21
+ # YouTubeG::Model::Video::Format: Video format object
22
+ def initialize(format_code, name)
23
+ @format_code = format_code
24
+ @name = name
25
+
26
+ @@formats[format_code] = self
27
+ end
28
+
29
+ # Allows you to get the video format for a specific format code.
30
+ #
31
+ # A full list of format codes is available at:
32
+ #
33
+ # http://code.google.com/apis/youtube/reference.html#youtube_data_api_tag_media:content
34
+ #
35
+ # == Parameters
36
+ # :format_code<Fixnum>:: The Youtube Format code of the object.
37
+ #
38
+ # == Returns
39
+ # YouTubeG::Model::Video::Format: Video format object
40
+ def self.by_code(format_code)
41
+ @@formats[format_code]
42
+ end
43
+
44
+ # Flash format on YouTube site. All videos are available in this format.
45
+ FLASH = YouTubeG::Model::Video::Format.new(0, :flash)
46
+
47
+ # RTSP streaming URL for mobile video playback. H.263 video (176x144) and AMR audio.
48
+ RTSP = YouTubeG::Model::Video::Format.new(1, :rtsp)
49
+
50
+ # HTTP URL to the embeddable player (SWF) for this video. This format
51
+ # is not available for a video that is not embeddable.
52
+ SWF = YouTubeG::Model::Video::Format.new(5, :swf)
53
+
54
+ # RTSP streaming URL for mobile video playback. MPEG-4 SP video (up to 176x144) and AAC audio.
55
+ THREE_GPP = YouTubeG::Model::Video::Format.new(6, :three_gpp)
56
+ end
57
+
58
+ # *Fixnum*:: Duration of a video in seconds.
59
+ attr_reader :duration
60
+
61
+ # *Boolean*:: Specifies that a video may or may not be embedded on other websites.
62
+ attr_reader :noembed
63
+
64
+ # *Fixnum*:: Specifies the order in which the video appears in a playlist.
65
+ attr_reader :position
66
+
67
+ # *Boolean*:: Specifies that a video is flagged as adult or not.
68
+ attr_reader :racy
69
+
70
+ # *String*: Specifies a URI that uniquely and permanently identifies the video.
71
+ attr_reader :video_id
72
+
73
+ # *Time*:: When the video was published on Youtube.
74
+ attr_reader :published_at
75
+
76
+ # *Time*:: When the video's data was last updated.
77
+ attr_reader :updated_at
78
+
79
+ # *Array*:: A array of YouTubeG::Model::Category objects that describe the videos categories.
80
+ attr_reader :categories
81
+
82
+ # *Array*:: An array of words associated with the video.
83
+ attr_reader :keywords
84
+
85
+ # *String*:: Description of the video.
86
+ attr_reader :description
87
+
88
+ # *String*:: Title for the video.
89
+ attr_reader :title
90
+
91
+ # *String*:: Description of the video.
92
+ attr_reader :html_content
93
+
94
+ # YouTubeG::Model::Author:: Information about the YouTube user who owns a piece of video content.
95
+ attr_reader :author
96
+
97
+ # *Array*:: An array of YouTubeG::Model::Content objects describing the individual media content data available for this video. Most, but not all, videos offer this.
98
+ attr_reader :media_content
99
+
100
+ # *Array*:: An array of YouTubeG::Model::Thumbnail objects that contain information regarding the videos thumbnail images.
101
+ attr_reader :thumbnails
102
+
103
+ # *String*:: The link to watch the URL on YouTubes website.
104
+ attr_reader :player_url
105
+
106
+ # YouTubeG::Model::Rating:: Information about the videos rating.
107
+ attr_reader :rating
108
+
109
+ # *Fixnum*:: Number of times that the video has been viewed
110
+ attr_reader :view_count
111
+
112
+ # *Fixnum*:: Number of times that the video has been favorited
113
+ attr_reader :favorite_count
114
+
115
+
116
+ # Geodata
117
+ attr_reader :where
118
+ attr_reader :position
119
+ attr_reader :latitude
120
+ attr_reader :longitude
121
+
122
+ attr_reader :statistics
123
+
124
+ # Videos related to the current video.
125
+ #
126
+ # === Returns
127
+ # YouTubeG::Response::VideoSearch
128
+ def related
129
+ YouTubeG::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/related").parse
130
+ end
131
+
132
+ # Video responses to the current video.
133
+ #
134
+ # === Returns
135
+ # YouTubeG::Response::VideoSearch
136
+ def responses
137
+ YouTubeG::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/responses").parse
138
+ end
139
+
140
+ # Comments on the current video.
141
+ #
142
+ # === Returns
143
+ # YouTubeG::Response::CommentsSearch
144
+ def comments_search(options={})
145
+ url = YouTubeG::Request::CommentsSearch.new(unique_id,options).url
146
+ YouTubeG::Parser::CommentsFeedParser.new(url).parse
147
+ end
148
+
149
+
150
+ # The ID of the video, useful for searching for the video again without having to store it anywhere.
151
+ # A regular query search, with this id will return the same video.
152
+ #
153
+ # === Example
154
+ # >> video.unique_id
155
+ # => "ZTUVgYoeN_o"
156
+ #
157
+ # === Returns
158
+ # String: The Youtube video id.
159
+ def unique_id
160
+ video_id[/videos\/([^<]+)/, 1]
161
+ end
162
+
163
+ # Allows you to check whether the video can be embedded on a webpage.
164
+ #
165
+ # === Returns
166
+ # Boolean: True if the video can be embedded, false if not.
167
+ def embeddable?
168
+ not @noembed
169
+ end
170
+
171
+ # Provides a URL and various other types of information about a video.
172
+ #
173
+ # === Returns
174
+ # YouTubeG::Model::Content: Data about the embeddable video.
175
+ def default_media_content
176
+ @media_content.find { |c| c.is_default? }
177
+ end
178
+
179
+ # Gives you the HTML to embed the video on your website.
180
+ #
181
+ # === Returns
182
+ # String: The HTML for embedding the video on your website.
183
+ def embed_html(width = 425, height = 350)
184
+ <<EDOC
185
+ <object width="#{width}" height="#{height}">
186
+ <param name="movie" value="#{embed_url}"></param>
187
+ <param name="wmode" value="transparent"></param>
188
+ <embed src="#{embed_url}" type="application/x-shockwave-flash"
189
+ wmode="transparent" width="#{width}" height="#{height}"></embed>
190
+ </object>
191
+ EDOC
192
+ end
193
+
194
+ # The URL needed for embedding the video in a page.
195
+ #
196
+ # === Returns
197
+ # String: Absolute URL for embedding video
198
+ def embed_url
199
+ @player_url.sub('watch?', '').sub('=', '/')
200
+ end
201
+
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,252 @@
1
+ class YouTubeG
2
+ module Parser #:nodoc:
3
+ class FeedParser #:nodoc:
4
+ def initialize(url)
5
+ @url = url
6
+ end
7
+
8
+ def parse
9
+ parse_content open(@url).read
10
+ end
11
+ end
12
+
13
+ class VideoFeedParser < FeedParser #:nodoc:
14
+
15
+ def parse_content(content)
16
+ doc = REXML::Document.new(content)
17
+ entry = doc.elements["entry"]
18
+ parse_entry(entry)
19
+ end
20
+
21
+ protected
22
+ def parse_entry(entry)
23
+ video_id = entry.elements["id"].text
24
+
25
+ if entry.elements["app:control/yt:state"]
26
+ # yt:state generally indicates a video is unavailable
27
+ # see: http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_yt:state
28
+ unless entry.elements["app:control/yt:state[@name='restricted']"]
29
+ # the exception I've found is for *restricted* videos, which generally
30
+ # means they're not available for viewing on a device other than a web browser
31
+ # or in a certain region
32
+ return nil
33
+ end
34
+ end
35
+
36
+ # if there's no content we're going to puke later
37
+ unless entry.elements['content']
38
+ return nil
39
+ end
40
+
41
+ published_at = Time.parse(entry.elements["published"].text)
42
+ updated_at = Time.parse(entry.elements["updated"].text)
43
+
44
+ # parse the category and keyword lists
45
+ categories = []
46
+ keywords = []
47
+ entry.elements.each("category") do |category|
48
+ # determine if it's really a category, or just a keyword
49
+ scheme = category.attributes["scheme"]
50
+ if (scheme =~ /\/categories\.cat$/)
51
+ # it's a category
52
+ categories << YouTubeG::Model::Category.new(
53
+ :term => category.attributes["term"],
54
+ :label => category.attributes["label"])
55
+
56
+ elsif (scheme =~ /\/keywords\.cat$/)
57
+ # it's a keyword
58
+ keywords << category.attributes["term"]
59
+ end
60
+ end
61
+
62
+ title = entry.elements["title"].text
63
+ html_content = entry.elements["content"].text
64
+
65
+ # parse the author
66
+ author_element = entry.elements["author"]
67
+ author = nil
68
+ if author_element
69
+ author = YouTubeG::Model::Author.new(
70
+ :name => author_element.elements["name"].text,
71
+ :uri => author_element.elements["uri"].text)
72
+ end
73
+
74
+ media_group = entry.elements["media:group"]
75
+ description = media_group.elements["media:description"].text
76
+ duration = media_group.elements["yt:duration"].attributes["seconds"].to_i
77
+
78
+ media_content = []
79
+ media_group.elements.each("media:content") do |mce|
80
+ media_content << parse_media_content(mce)
81
+ end
82
+
83
+ player_url = media_group.elements["media:player"].attributes["url"]
84
+
85
+ # parse thumbnails
86
+ thumbnails = []
87
+ media_group.elements.each("media:thumbnail") do |thumb_element|
88
+ # TODO: convert time HH:MM:ss string to seconds?
89
+ thumbnails << YouTubeG::Model::Thumbnail.new(
90
+ :url => thumb_element.attributes["url"],
91
+ :height => thumb_element.attributes["height"].to_i,
92
+ :width => thumb_element.attributes["width"].to_i,
93
+ :time => thumb_element.attributes["time"])
94
+ end
95
+
96
+ rating_element = entry.elements["gd:rating"]
97
+ rating = nil
98
+ if rating_element
99
+ rating = YouTubeG::Model::Rating.new(
100
+ :min => rating_element.attributes["min"].to_i,
101
+ :max => rating_element.attributes["max"].to_i,
102
+ :rater_count => rating_element.attributes["numRaters"].to_i,
103
+ :average => rating_element.attributes["average"].to_f)
104
+ end
105
+
106
+ if (el = entry.elements["yt:statistics"])
107
+ view_count, favorite_count = el.attributes["viewCount"].to_i, el.attributes["favoriteCount"].to_i
108
+ else
109
+ view_count, favorite_count = 0,0
110
+ end
111
+
112
+ noembed = entry.elements["yt:noembed"] ? true : false
113
+ racy = entry.elements["media:rating"] ? true : false
114
+
115
+ if where = entry.elements["georss:where"]
116
+ position = where.elements["gml:Point"].elements["gml:pos"].text
117
+ latitude, longitude = position.split(" ")
118
+ end
119
+
120
+ YouTubeG::Model::Video.new(
121
+ :video_id => video_id,
122
+ :published_at => published_at,
123
+ :updated_at => updated_at,
124
+ :categories => categories,
125
+ :keywords => keywords,
126
+ :title => title,
127
+ :html_content => html_content,
128
+ :author => author,
129
+ :description => description,
130
+ :duration => duration,
131
+ :media_content => media_content,
132
+ :player_url => player_url,
133
+ :thumbnails => thumbnails,
134
+ :rating => rating,
135
+ :view_count => view_count,
136
+ :favorite_count => favorite_count,
137
+ :noembed => noembed,
138
+ :racy => racy,
139
+ :where => where,
140
+ :position => position,
141
+ :latitude => latitude,
142
+ :longitude => longitude)
143
+ end
144
+
145
+ def parse_media_content (media_content_element)
146
+ content_url = media_content_element.attributes["url"]
147
+ format_code = media_content_element.attributes["yt:format"].to_i
148
+ format = YouTubeG::Model::Video::Format.by_code(format_code)
149
+ duration = media_content_element.attributes["duration"].to_i
150
+ mime_type = media_content_element.attributes["type"]
151
+ default = (media_content_element.attributes["isDefault"] == "true")
152
+
153
+ YouTubeG::Model::Content.new(
154
+ :url => content_url,
155
+ :format => format,
156
+ :duration => duration,
157
+ :mime_type => mime_type,
158
+ :default => default)
159
+ end
160
+ end
161
+
162
+ class VideosFeedParser < VideoFeedParser #:nodoc:
163
+
164
+ private
165
+ def parse_content(content)
166
+ doc = REXML::Document.new(content)
167
+ feed = doc.elements["feed"]
168
+
169
+ if (feed)
170
+ feed_id = feed.elements["id"].text
171
+ updated_at = Time.parse(feed.elements["updated"].text)
172
+ total_result_count = feed.elements["openSearch:totalResults"].text.to_i
173
+ offset = feed.elements["openSearch:startIndex"].text.to_i
174
+ max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i
175
+
176
+ videos = []
177
+ feed.elements.each("entry") do |entry|
178
+ if video = parse_entry(entry)
179
+ videos << video
180
+ end
181
+ end
182
+
183
+ YouTubeG::Response::VideoSearch.new(
184
+ :feed_id => feed_id,
185
+ :updated_at => updated_at,
186
+ :total_result_count => total_result_count,
187
+ :offset => offset,
188
+ :max_result_count => max_result_count,
189
+ :videos => videos)
190
+ else
191
+ entry = doc.elements["entry"]
192
+ video = parse_entry(entry) if (entry)
193
+ end
194
+ end
195
+ end
196
+
197
+ class CommentsFeedParser < FeedParser
198
+
199
+ def parse_content(content)
200
+ doc = REXML::Document.new(content)
201
+ feed = doc.elements['feed']
202
+
203
+ feed_id = feed.elements["id"].text
204
+ updated_at = Time.parse(feed.elements["updated"].text)
205
+ total_result_count = feed.elements["openSearch:totalResults"].text.to_i
206
+ offset = feed.elements["openSearch:startIndex"].text.to_i
207
+ max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i
208
+
209
+ comments = []
210
+ feed.elements.each('entry') do |entry|
211
+ comments << parse_entry(entry)
212
+ end
213
+ YouTubeG::Response::CommentsSearch.new(
214
+ :feed_id => feed_id,
215
+ :updated_at => updated_at,
216
+ :total_result_count => total_result_count,
217
+ :offset => offset,
218
+ :max_result_count => max_result_count,
219
+ :comments => comments)
220
+ end
221
+
222
+ private
223
+ def parse_entry(entry)
224
+ comment_id = entry.elements["id"].text
225
+
226
+ published_at = Time.parse(entry.elements["published"].text)
227
+ updated_at = Time.parse(entry.elements["updated"].text)
228
+
229
+ title = entry.elements["title"].text
230
+ content = entry.elements["content"].text
231
+
232
+ # parse the author
233
+ author_element = entry.elements["author"]
234
+ author = nil
235
+ if author_element
236
+ author = YouTubeG::Model::Author.new(
237
+ :name => author_element.elements["name"].text,
238
+ :uri => author_element.elements["uri"].text)
239
+ end
240
+
241
+
242
+ comment = YouTubeG::Model::Comment.new(
243
+ :published_at => published_at,
244
+ :updated_at => updated_at,
245
+ :title => title,
246
+ :content => content,
247
+ :author => author)
248
+ return comment
249
+ end
250
+ end
251
+ end
252
+ end