class YouTubeG module Parser #:nodoc: class FeedParserError < Exception; end #:nodoc: class FeedParser #:nodoc: attr_reader :url_based alias :url_based? :url_based # Accept a URL or a string assumed to be XML. def initialize(arg) @url_based = assert_valid_url(arg) @content = arg end def parse if @url_based parse_content open(@content).read else parse_content @content end end private def assert_valid_url (url) URI::parse(url) return true rescue return false end end class UploadErrorParser #:nodoc: def initialize(xml) raise YouTubeG::Parser::FeedParserError.new("You must pass some xml") if xml == '' @doc = REXML::Document.new(xml) end def parse upload_errors = [] @doc.elements.each("//error") do |error| location = error.elements["location"].text #[/media:group\/media:(.*)\/text\(\)/,1] code = error.elements["code"].text domain = error.elements["domain"].text upload_errors << YouTubeG::Model::UploadError.new(:location => location, :code => code, :domain => domain) end return upload_errors end end class VideoFeedParser < FeedParser #:nodoc: def parse_content(content) doc = REXML::Document.new(content) entry = doc.elements["entry"] parse_entry(entry) end protected def parse_entry(entry) video_id = entry.elements["id"].text published_at = Time.parse(entry.elements["published"].text) updated_at = Time.parse(entry.elements["updated"].text) app_control_element = entry.elements["app:control"] app_control = nil if app_control_element app_control = YouTubeG::Model::Video::AppControl.new( :draft => app_control_element.elements["app:draft"].text, :state => app_control_element.elements["yt:state"].attributes["name"]) end # parse the category and keyword lists categories = [] keywords = [] entry.elements.each("category") do |category| # determine if it's really a category, or just a keyword scheme = category.attributes["scheme"] if (scheme =~ /\/categories\.cat$/) # it's a category categories << YouTubeG::Model::Category.new( :term => category.attributes["term"], :label => category.attributes["label"]) elsif (scheme =~ /\/keywords\.cat$/) # it's a keyword keywords << category.attributes["term"] end end title = entry.elements["title"].text html_content = entry.elements["content"].text # parse the author author_element = entry.elements["author"] author = nil if author_element author = YouTubeG::Model::Author.new( :name => author_element.elements["name"].text, :uri => author_element.elements["uri"].text) end media_group = entry.elements["media:group"] description = media_group.elements["media:description"].text duration = media_group.elements["yt:duration"].attributes["seconds"].to_i if media_group.elements["yt:duration"] media_content = [] media_group.elements.each("media:content") do |mce| media_content << parse_media_content(mce) end player_url = media_group.elements["media:player"].attributes["url"] if media_group.elements["media:player"] # parse thumbnails thumbnails = [] media_group.elements.each("media:thumbnail") do |thumb_element| # TODO: convert time HH:MM:ss string to seconds? thumbnails << YouTubeG::Model::Thumbnail.new( :url => thumb_element.attributes["url"], :height => thumb_element.attributes["height"].to_i, :width => thumb_element.attributes["width"].to_i, :time => thumb_element.attributes["time"]) end rating_element = entry.elements["gd:rating"] rating = nil if rating_element rating = YouTubeG::Model::Rating.new( :min => rating_element.attributes["min"].to_i, :max => rating_element.attributes["max"].to_i, :rater_count => rating_element.attributes["numRaters"].to_i, :average => rating_element.attributes["average"].to_f) end view_count = (el = entry.elements["yt:statistics"]) ? el.attributes["viewCount"].to_i : 0 noembed = entry.elements["yt:noembed"] ? true : false racy = entry.elements["media:rating"] ? true : false YouTubeG::Model::Video.new( :video_id => video_id, :published_at => published_at, :updated_at => updated_at, :app_control => app_control, :categories => categories, :keywords => keywords, :title => title, :html_content => html_content, :author => author, :description => description, :duration => duration, :media_content => media_content, :player_url => player_url, :thumbnails => thumbnails, :rating => rating, :view_count => view_count, :noembed => noembed, :racy => racy) end def parse_media_content (media_content_element) content_url = media_content_element.attributes["url"] format_code = media_content_element.attributes["yt:format"].to_i format = YouTubeG::Model::Video::Format.by_code(format_code) duration = media_content_element.attributes["duration"].to_i mime_type = media_content_element.attributes["type"] default = (media_content_element.attributes["isDefault"] == "true") YouTubeG::Model::Content.new( :url => content_url, :format => format, :duration => duration, :mime_type => mime_type, :default => default) end end class VideosFeedParser < VideoFeedParser #:nodoc: private def parse_content(content) #:nodoc: doc = REXML::Document.new(content) feed = doc.elements["feed"] feed_id = feed.elements["id"].text updated_at = Time.parse(feed.elements["updated"].text) total_result_count = feed.elements["openSearch:totalResults"].text.to_i offset = feed.elements["openSearch:startIndex"].text.to_i max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i videos = [] feed.elements.each("entry") do |entry| videos << parse_entry(entry) end YouTubeG::Response::VideoSearch.new( :feed_id => feed_id, :updated_at => updated_at, :total_result_count => total_result_count, :offset => offset, :max_result_count => max_result_count, :videos => videos) end end end end