vibedeck-youtube_it 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/README.rdoc +234 -0
  2. data/Rakefile +35 -0
  3. data/lib/youtube_it/chain_io.rb +76 -0
  4. data/lib/youtube_it/client.rb +367 -0
  5. data/lib/youtube_it/middleware/faraday_authheader.rb +24 -0
  6. data/lib/youtube_it/middleware/faraday_oauth.rb +21 -0
  7. data/lib/youtube_it/middleware/faraday_youtubeit.rb +30 -0
  8. data/lib/youtube_it/model/author.rb +13 -0
  9. data/lib/youtube_it/model/category.rb +11 -0
  10. data/lib/youtube_it/model/comment.rb +16 -0
  11. data/lib/youtube_it/model/contact.rb +16 -0
  12. data/lib/youtube_it/model/content.rb +18 -0
  13. data/lib/youtube_it/model/playlist.rb +11 -0
  14. data/lib/youtube_it/model/rating.rb +23 -0
  15. data/lib/youtube_it/model/subscription.rb +7 -0
  16. data/lib/youtube_it/model/thumbnail.rb +17 -0
  17. data/lib/youtube_it/model/user.rb +26 -0
  18. data/lib/youtube_it/model/video.rb +225 -0
  19. data/lib/youtube_it/parser.rb +357 -0
  20. data/lib/youtube_it/record.rb +12 -0
  21. data/lib/youtube_it/request/base_search.rb +72 -0
  22. data/lib/youtube_it/request/error.rb +15 -0
  23. data/lib/youtube_it/request/standard_search.rb +43 -0
  24. data/lib/youtube_it/request/user_search.rb +47 -0
  25. data/lib/youtube_it/request/video_search.rb +102 -0
  26. data/lib/youtube_it/request/video_upload.rb +415 -0
  27. data/lib/youtube_it/response/video_search.rb +41 -0
  28. data/lib/youtube_it/version.rb +4 -0
  29. data/lib/youtube_it.rb +75 -0
  30. data/test/helper.rb +10 -0
  31. data/test/test_chain_io.rb +63 -0
  32. data/test/test_client.rb +418 -0
  33. data/test/test_field_search.rb +48 -0
  34. data/test/test_video.rb +43 -0
  35. data/test/test_video_feed_parser.rb +271 -0
  36. data/test/test_video_search.rb +141 -0
  37. metadata +150 -0
@@ -0,0 +1,225 @@
1
+ # TODO
2
+ # * self atom feed
3
+ # * alternate youtube watch url
4
+ # * comments feedLink
5
+
6
+ class YouTubeIt
7
+ module Model
8
+ class Video < YouTubeIt::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
+ # YouTubeIt::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
+ # YouTubeIt::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 = YouTubeIt::Model::Video::Format.new(0, :flash)
46
+
47
+ # RTSP streaming URL for mobile video playback. H.263 video (176x144) and AMR audio.
48
+ RTSP = YouTubeIt::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 = YouTubeIt::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 = YouTubeIt::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 16:9 ratio.
62
+ attr_reader :widescreen
63
+
64
+ # *Boolean*:: Specifies that a video may or may not be embedded on other websites.
65
+ attr_reader :noembed
66
+
67
+ # *Fixnum*:: Specifies the order in which the video appears in a playlist.
68
+ attr_reader :position
69
+
70
+ # *Boolean*:: Specifies that a video is flagged as adult or not.
71
+ attr_reader :racy
72
+
73
+ # *String*: Specifies a URI that uniquely and permanently identifies the video.
74
+ attr_reader :video_id
75
+
76
+ # *Time*:: When the video was published on Youtube.
77
+ attr_reader :published_at
78
+
79
+ # *Time*:: When the video's data was last updated.
80
+ attr_reader :updated_at
81
+
82
+ # *Array*:: A array of YouTubeIt::Model::Category objects that describe the videos categories.
83
+ attr_reader :categories
84
+
85
+ # *Array*:: An array of words associated with the video.
86
+ attr_reader :keywords
87
+
88
+ # *String*:: Description of the video.
89
+ attr_reader :description
90
+
91
+ # *String*:: Title for the video.
92
+ attr_reader :title
93
+
94
+ # *String*:: Description of the video.
95
+ attr_reader :html_content
96
+
97
+ # YouTubeIt::Model::Author:: Information about the YouTube user who owns a piece of video content.
98
+ attr_reader :author
99
+
100
+ # *Array*:: An array of YouTubeIt::Model::Content objects describing the individual media content data available for this video. Most, but not all, videos offer this.
101
+ attr_reader :media_content
102
+
103
+ # *Array*:: An array of YouTubeIt::Model::Thumbnail objects that contain information regarding the videos thumbnail images.
104
+ attr_reader :thumbnails
105
+
106
+ # *String*:: The link to watch the URL on YouTubes website.
107
+ attr_reader :player_url
108
+
109
+ # YouTubeIt::Model::Rating:: Information about the videos rating.
110
+ attr_reader :rating
111
+
112
+ # *Fixnum*:: Number of times that the video has been viewed
113
+ attr_reader :view_count
114
+
115
+ # *Fixnum*:: Number of times that the video has been favorited
116
+ attr_reader :favorite_count
117
+
118
+ # *String*:: State of the video (processing, restricted, deleted, rejected and failed)
119
+ attr_reader :state
120
+
121
+
122
+ # Geodata
123
+ attr_reader :where
124
+ attr_reader :position
125
+ attr_reader :latitude
126
+ attr_reader :longitude
127
+
128
+ # Videos related to the current video.
129
+ #
130
+ # === Returns
131
+ # YouTubeIt::Response::VideoSearch
132
+ def related
133
+ YouTubeIt::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/related").parse
134
+ end
135
+
136
+ # Video responses to the current video.
137
+ #
138
+ # === Returns
139
+ # YouTubeIt::Response::VideoSearch
140
+ def responses
141
+ YouTubeIt::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/responses").parse
142
+ end
143
+
144
+ # The ID of the video, useful for searching for the video again without having to store it anywhere.
145
+ # A regular query search, with this id will return the same video.
146
+ #
147
+ # === Example
148
+ # >> video.unique_id
149
+ # => "ZTUVgYoeN_o"
150
+ #
151
+ # === Returns
152
+ # String: The Youtube video id.
153
+ def unique_id
154
+ video_id[/videos\/([^<]+)/, 1] || video_id[/video\:([^<]+)/, 1]
155
+ end
156
+
157
+ # Allows you to check whether the video can be embedded on a webpage.
158
+ #
159
+ # === Returns
160
+ # Boolean: True if the video can be embedded, false if not.
161
+ def embeddable?
162
+ not @noembed
163
+ end
164
+
165
+ # Allows you to check whether the video is widescreen (16:9) or not.
166
+ #
167
+ # === Returns
168
+ # Boolean: True if the video is (approximately) 16:9, false if not.
169
+ def widescreen?
170
+ @widescreen
171
+ end
172
+
173
+ # Provides a URL and various other types of information about a video.
174
+ #
175
+ # === Returns
176
+ # YouTubeIt::Model::Content: Data about the embeddable video.
177
+ def default_media_content
178
+ @media_content.find { |c| c.is_default? }
179
+ end
180
+
181
+ # Gives you the HTML to embed the video on your website.
182
+ #
183
+ # === Returns
184
+ # String: The HTML for embedding the video on your website.
185
+ def embed_html(width = 425, height = 350)
186
+ <<EDOC
187
+ <object width="#{width}" height="#{height}">
188
+ <param name="movie" value="#{embed_url}"></param>
189
+ <param name="wmode" value="transparent"></param>
190
+ <embed src="#{embed_url}" type="application/x-shockwave-flash"
191
+ wmode="transparent" width="#{width}" height="#{height}"></embed>
192
+ </object>
193
+ EDOC
194
+ end
195
+
196
+ # Gives you the HTML to embed the video on your website.
197
+ #
198
+ # === Returns
199
+ # String: The HTML for embedding the video on your website.
200
+ def embed_html_with_width(width = 1280)
201
+ height = (widescreen? ? width * 9/16 : width * 3/4) + 25
202
+
203
+ <<EDOC
204
+ <object width="#{width}" height="#{height}">
205
+ <param name="movie" value="#{embed_url}"></param>
206
+ <param name="wmode" value="transparent"></param>
207
+ <embed src="#{embed_url}" type="application/x-shockwave-flash"
208
+ wmode="transparent" width="#{width}" height="#{height}"></embed>
209
+ </object>
210
+ EDOC
211
+ end
212
+
213
+ # The URL needed for embedding the video in a page.
214
+ #
215
+ # === Returns
216
+ # String: Absolute URL for embedding video
217
+ def embed_url
218
+ @player_url.sub('watch?', '').sub('=', '/').sub('feature/', 'feature=')
219
+ end
220
+
221
+
222
+ end
223
+ end
224
+ end
225
+
@@ -0,0 +1,357 @@
1
+ class YouTubeIt
2
+ module Parser #:nodoc:
3
+ class FeedParser #:nodoc:
4
+ def initialize(content)
5
+ @content = open(content).read
6
+
7
+ rescue OpenURI::HTTPError => e
8
+ raise OpenURI::HTTPError.new(e.io.status[0],e)
9
+ rescue
10
+ @content = content
11
+
12
+ end
13
+
14
+ def parse
15
+ parse_content @content
16
+ end
17
+
18
+ def parse_videos
19
+ doc = REXML::Document.new(@content)
20
+ videos = []
21
+ doc.elements.each("*/entry") do |video|
22
+ videos << parse_entry(video)
23
+ end
24
+ videos
25
+ end
26
+ end
27
+
28
+ class CommentsFeedParser < FeedParser #:nodoc:
29
+ # return array of comments
30
+ def parse_content(content)
31
+ doc = REXML::Document.new(content.body)
32
+ feed = doc.elements["feed"]
33
+
34
+ comments = []
35
+ feed.elements.each("entry") do |entry|
36
+ comments << parse_entry(entry)
37
+ end
38
+ return comments
39
+ end
40
+
41
+ protected
42
+ def parse_entry(entry)
43
+ author = YouTubeIt::Model::Author.new(
44
+ :name => entry.elements["author"].elements["name"].text,
45
+ :uri => entry.elements["author"].elements["uri"].text,
46
+ :thumbnail_url => entry.elements["media:thumbnail"].attributes["url"]
47
+ )
48
+ YouTubeIt::Model::Comment.new(
49
+ :author => author,
50
+ :content => entry.elements["content"].text,
51
+ :published => entry.elements["published"].text,
52
+ :title => entry.elements["title"].text,
53
+ :updated => entry.elements["updated "].text,
54
+ :url => entry.elements["id"].text
55
+ )
56
+ end
57
+ end
58
+
59
+ class PlaylistFeedParser < FeedParser #:nodoc:
60
+
61
+ def parse_content(content)
62
+ xml = REXML::Document.new(content.body)
63
+ entry = xml.elements["entry"] || xml.elements["feed"]
64
+ YouTubeIt::Model::Playlist.new(
65
+ :title => entry.elements["title"].text,
66
+ :summary => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
67
+ :description => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
68
+ :playlist_id => entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':',''),
69
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil,
70
+ :response_code => content.status,
71
+ :xml => content.body)
72
+ end
73
+ end
74
+
75
+ class PlaylistsFeedParser < FeedParser #:nodoc:
76
+
77
+ # return array of playlist objects
78
+ def parse_content(content)
79
+ doc = REXML::Document.new(content.body)
80
+ feed = doc.elements["feed"]
81
+
82
+ playlists = []
83
+ feed.elements.each("entry") do |entry|
84
+ playlists << parse_entry(entry)
85
+ end
86
+ return playlists
87
+ end
88
+
89
+ protected
90
+
91
+ def parse_entry(entry)
92
+ YouTubeIt::Model::Playlist.new(
93
+ :title => entry.elements["title"].text,
94
+ :summary => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
95
+ :description => (entry.elements["summary"] || entry.elements["media:group"].elements["media:description"]).text,
96
+ :playlist_id => entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':',''),
97
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil,
98
+ :response_code => nil,
99
+ :xml => nil)
100
+ end
101
+ end
102
+
103
+ class ProfileFeedParser < FeedParser #:nodoc:
104
+ def parse_content(content)
105
+ xml = REXML::Document.new(content.body)
106
+ entry = xml.elements["entry"] || xml.elements["feed"]
107
+ YouTubeIt::Model::User.new(
108
+ :age => entry.elements["yt:age"] ? entry.elements["yt:age"].text : nil,
109
+ :username => entry.elements["yt:username"] ? entry.elements["yt:username"].text : nil,
110
+ :company => entry.elements["yt:company"] ? entry.elements["yt:company"].text : nil,
111
+ :gender => entry.elements["yt:gender"] ? entry.elements["yt:gender"].text : nil,
112
+ :hobbies => entry.elements["yt:hobbies"] ? entry.elements["yt:hobbies"].text : nil,
113
+ :hometown => entry.elements["yt:hometown"] ? entry.elements["yt:hometown"].text : nil,
114
+ :location => entry.elements["yt:location"] ? entry.elements["yt:location"].text : nil,
115
+ :last_login => entry.elements["yt:statistics"].attributes["lastWebAccess"],
116
+ :join_date => entry.elements["published"] ? entry.elements["published"].text : nil,
117
+ :movies => entry.elements["yt:movies"] ? entry.elements["yt:movies"].text : nil,
118
+ :music => entry.elements["yt:music"] ? entry.elements["yt:music"].text : nil,
119
+ :occupation => entry.elements["yt:occupation"] ? entry.elements["yt:occupation"].text : nil,
120
+ :relationship => entry.elements["yt:relationship"] ? entry.elements["yt:relationship"].text : nil,
121
+ :school => entry.elements["yt:school"] ? entry.elements["yt:school"].text : nil,
122
+ :subscribers => entry.elements["yt:statistics"].attributes["subscriberCount"],
123
+ :videos_watched => entry.elements["yt:statistics"].attributes["videoWatchCount"],
124
+ :view_count => entry.elements["yt:statistics"].attributes["viewCount"],
125
+ :upload_views => entry.elements["yt:statistics"].attributes["totalUploadViews"]
126
+ )
127
+ end
128
+ end
129
+
130
+ class SubscriptionFeedParser < FeedParser #:nodoc:
131
+
132
+ def parse_content(content)
133
+ doc = REXML::Document.new(content.body)
134
+ feed = doc.elements["feed"]
135
+
136
+ subscriptions = []
137
+ feed.elements.each("entry") do |entry|
138
+ subscriptions << parse_entry(entry)
139
+ end
140
+ return subscriptions
141
+ end
142
+
143
+ protected
144
+
145
+ def parse_entry(entry)
146
+ YouTubeIt::Model::Subscription.new(
147
+ :title => entry.elements["title"].text,
148
+ :id => entry.elements["id"].text[/subscription([^<]+)/, 1].sub(':',''),
149
+ :published => entry.elements["published"] ? entry.elements["published"].text : nil
150
+ )
151
+ end
152
+ end
153
+
154
+
155
+ class VideoFeedParser < FeedParser #:nodoc:
156
+
157
+ def parse_content(content)
158
+ doc = REXML::Document.new(content)
159
+ entry = doc.elements["entry"]
160
+ parse_entry(entry)
161
+ end
162
+
163
+ protected
164
+ def parse_entry(entry)
165
+ video_id = entry.elements["id"].text
166
+ published_at = entry.elements["published"] ? Time.parse(entry.elements["published"].text) : nil
167
+ updated_at = entry.elements["updated"] ? Time.parse(entry.elements["updated"].text) : nil
168
+
169
+ # parse the category and keyword lists
170
+ categories = []
171
+ keywords = []
172
+ entry.elements.each("category") do |category|
173
+ # determine if it's really a category, or just a keyword
174
+ scheme = category.attributes["scheme"]
175
+ if (scheme =~ /\/categories\.cat$/)
176
+ # it's a category
177
+ categories << YouTubeIt::Model::Category.new(
178
+ :term => category.attributes["term"],
179
+ :label => category.attributes["label"])
180
+
181
+ elsif (scheme =~ /\/keywords\.cat$/)
182
+ # it's a keyword
183
+ keywords << category.attributes["term"]
184
+ end
185
+ end
186
+
187
+ title = entry.elements["title"].text
188
+ html_content = entry.elements["content"] ? entry.elements["content"].text : nil
189
+
190
+ # parse the author
191
+ author_element = entry.elements["author"]
192
+ author = nil
193
+ if author_element
194
+ author = YouTubeIt::Model::Author.new(
195
+ :name => author_element.elements["name"].text,
196
+ :uri => author_element.elements["uri"].text)
197
+ end
198
+ media_group = entry.elements["media:group"]
199
+
200
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
201
+ description = ""
202
+ unless media_group.elements["media:description"].nil?
203
+ description = media_group.elements["media:description"].text
204
+ end
205
+
206
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
207
+ duration = 0
208
+ unless media_group.elements["yt:duration"].nil?
209
+ duration = media_group.elements["yt:duration"].attributes["seconds"].to_i
210
+ end
211
+
212
+ # if content is not available on certain region, there is no media:description, media:player or yt:duration
213
+ player_url = ""
214
+ unless media_group.elements["media:player"].nil?
215
+ player_url = media_group.elements["media:player"].attributes["url"]
216
+ end
217
+
218
+ unless media_group.elements["yt:aspectRatio"].nil?
219
+ widescreen = media_group.elements["yt:aspectRatio"].text == 'widescreen' ? true : false
220
+ end
221
+
222
+ media_content = []
223
+ media_group.elements.each("media:content") do |mce|
224
+ media_content << parse_media_content(mce)
225
+ end
226
+
227
+ # parse thumbnails
228
+ thumbnails = []
229
+ media_group.elements.each("media:thumbnail") do |thumb_element|
230
+ # TODO: convert time HH:MM:ss string to seconds?
231
+ thumbnails << YouTubeIt::Model::Thumbnail.new(
232
+ :url => thumb_element.attributes["url"],
233
+ :height => thumb_element.attributes["height"].to_i,
234
+ :width => thumb_element.attributes["width"].to_i,
235
+ :time => thumb_element.attributes["time"])
236
+ end
237
+
238
+ rating_element = entry.elements["gd:rating"]
239
+ extended_rating_element = entry.elements["yt:rating"]
240
+
241
+ rating = nil
242
+ if rating_element
243
+ rating_values = {
244
+ :min => rating_element.attributes["min"].to_i,
245
+ :max => rating_element.attributes["max"].to_i,
246
+ :rater_count => rating_element.attributes["numRaters"].to_i,
247
+ :average => rating_element.attributes["average"].to_f
248
+ }
249
+
250
+ if extended_rating_element
251
+ rating_values[:likes] = extended_rating_element.attributes["numLikes"].to_i
252
+ rating_values[:dislikes] = extended_rating_element.attributes["numDislikes"].to_i
253
+ end
254
+
255
+ rating = YouTubeIt::Model::Rating.new(rating_values)
256
+ end
257
+
258
+ if (el = entry.elements["yt:statistics"])
259
+ view_count, favorite_count = el.attributes["viewCount"].to_i, el.attributes["favoriteCount"].to_i
260
+ else
261
+ view_count, favorite_count = 0,0
262
+ end
263
+
264
+ noembed = entry.elements["yt:noembed"] ? true : false
265
+ racy = entry.elements["media:rating"] ? true : false
266
+
267
+ if where = entry.elements["georss:where"]
268
+ position = where.elements["gml:Point"].elements["gml:pos"].text
269
+ latitude, longitude = position.split(" ")
270
+ end
271
+
272
+ control = entry.elements["app:control"]
273
+ state = { :name => "published" }
274
+ if control && control.elements["yt:state"]
275
+ state = {
276
+ :name => control.elements["yt:state"].attributes["name"],
277
+ :reason_code => control.elements["yt:state"].attributes["reasonCode"],
278
+ :help_url => control.elements["yt:state"].attributes["helpUrl"],
279
+ :copy => control.elements["yt:state"].text
280
+ }
281
+
282
+ end
283
+
284
+ YouTubeIt::Model::Video.new(
285
+ :video_id => video_id,
286
+ :published_at => published_at,
287
+ :updated_at => updated_at,
288
+ :categories => categories,
289
+ :keywords => keywords,
290
+ :title => title,
291
+ :html_content => html_content,
292
+ :author => author,
293
+ :description => description,
294
+ :duration => duration,
295
+ :media_content => media_content,
296
+ :player_url => player_url,
297
+ :thumbnails => thumbnails,
298
+ :rating => rating,
299
+ :view_count => view_count,
300
+ :favorite_count => favorite_count,
301
+ :widescreen => widescreen,
302
+ :noembed => noembed,
303
+ :racy => racy,
304
+ :where => where,
305
+ :position => position,
306
+ :latitude => latitude,
307
+ :longitude => longitude,
308
+ :state => state)
309
+ end
310
+
311
+ def parse_media_content (media_content_element)
312
+ content_url = media_content_element.attributes["url"]
313
+ format_code = media_content_element.attributes["yt:format"].to_i
314
+ format = YouTubeIt::Model::Video::Format.by_code(format_code)
315
+ duration = media_content_element.attributes["duration"].to_i
316
+ mime_type = media_content_element.attributes["type"]
317
+ default = (media_content_element.attributes["isDefault"] == "true")
318
+
319
+ YouTubeIt::Model::Content.new(
320
+ :url => content_url,
321
+ :format => format,
322
+ :duration => duration,
323
+ :mime_type => mime_type,
324
+ :default => default)
325
+ end
326
+ end
327
+
328
+ class VideosFeedParser < VideoFeedParser #:nodoc:
329
+
330
+ private
331
+ def parse_content(content)
332
+ videos = []
333
+ doc = REXML::Document.new(content)
334
+ feed = doc.elements["feed"]
335
+ if feed
336
+ feed_id = feed.elements["id"].text
337
+ updated_at = Time.parse(feed.elements["updated"].text)
338
+ total_result_count = feed.elements["openSearch:totalResults"].text.to_i
339
+ offset = feed.elements["openSearch:startIndex"].text.to_i
340
+ max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i
341
+
342
+ feed.elements.each("entry") do |entry|
343
+ videos << parse_entry(entry)
344
+ end
345
+ end
346
+ YouTubeIt::Response::VideoSearch.new(
347
+ :feed_id => feed_id || nil,
348
+ :updated_at => updated_at || nil,
349
+ :total_result_count => total_result_count || nil,
350
+ :offset => offset || nil,
351
+ :max_result_count => max_result_count || nil,
352
+ :videos => videos)
353
+ end
354
+ end
355
+ end
356
+ end
357
+
@@ -0,0 +1,12 @@
1
+ class YouTubeIt
2
+ class Record #:nodoc:
3
+ def initialize (params)
4
+ return if params.nil?
5
+
6
+ params.each do |key, value|
7
+ name = key.to_s
8
+ instance_variable_set("@#{name}", value) if respond_to?(name)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,72 @@
1
+ class YouTubeIt
2
+ module Request #:nodoc:
3
+ class BaseSearch #:nodoc:
4
+ attr_reader :url
5
+
6
+ private
7
+
8
+ def base_url
9
+ "http://gdata.youtube.com/feeds/api/"
10
+ end
11
+
12
+ def set_instance_variables( variables )
13
+ variables.each do |key, value|
14
+ name = key.to_s
15
+ instance_variable_set("@#{name}", value) if respond_to?(name)
16
+ end
17
+ end
18
+
19
+ def build_query_params(params)
20
+ qs = params.to_a.map { | k, v | v.nil? ? nil : "#{YouTubeIt.esc(k)}=#{YouTubeIt.esc(v)}" }.compact.sort.join('&')
21
+ qs.empty? ? '' : (@dev_key ? "?#{qs}&key=#{@dev_key}" : "?#{qs}")
22
+ end
23
+ end
24
+
25
+
26
+ module FieldSearch
27
+ def default_fields
28
+ "id,updated,openSearch:totalResults,openSearch:startIndex,openSearch:itemsPerPage"
29
+ end
30
+
31
+ def fields_to_params(fields)
32
+ return "" unless fields
33
+
34
+ fields_param = [default_fields]
35
+
36
+ if fields[:recorded]
37
+ if fields[:recorded].is_a? Range
38
+ fields_param << "entry[xs:date(yt:recorded) > xs:date('#{formatted_date(fields[:recorded].first)}') and xs:date(yt:recorded) < xs:date('#{formatted_date(fields[:recorded].last)}')]"
39
+ else
40
+ fields_param << "entry[xs:date(yt:recorded) = xs:date('#{formatted_date(fields[:recorded])}')]"
41
+ end
42
+ end
43
+
44
+ if fields[:published]
45
+ if fields[:published].is_a? Range
46
+ fields_param << "entry[xs:dateTime(published) > xs:dateTime('#{formatted_date(fields[:published].first)}T00:00:00') and xs:dateTime(published) < xs:dateTime('#{formatted_date(fields[:published].last)}T00:00:00')]"
47
+ else
48
+ fields_param << "entry[xs:date(published) = xs:date('#{formatted_date(fields[:published])}')]"
49
+ end
50
+ end
51
+
52
+ if fields[:view_count]
53
+ fields_param << "entry[yt:statistics/@viewCount > #{fields[:view_count]}]"
54
+ end
55
+
56
+
57
+ return "&fields=#{URI.escape(fields_param.join(","))}"
58
+ end
59
+
60
+ #youtube taked dates that look like 'YYYY-MM-DD'
61
+ def formatted_date(date)
62
+ return date if date.is_a? String
63
+ if date.respond_to? :strftime
64
+ date.strftime("%Y-%m-%d")
65
+ else
66
+ ""
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,15 @@
1
+ class UploadError < YouTubeIt::Error
2
+ attr_reader :code
3
+ def initialize(msg, code = 0)
4
+ super(msg)
5
+ @code = code
6
+ end
7
+ end
8
+
9
+ class AuthenticationError < YouTubeIt::Error
10
+ attr_reader :code
11
+ def initialize(msg, code = 0)
12
+ super(msg)
13
+ @code = code
14
+ end
15
+ end