beef-youtube-g 0.4.9.9

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,190 @@
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
+ # Geodata
113
+ attr_reader :where
114
+ attr_reader :position
115
+ attr_reader :latitude
116
+ attr_reader :longitude
117
+
118
+ attr_reader :statistics
119
+
120
+ # Videos related to the current video.
121
+ #
122
+ # === Returns
123
+ # YouTubeG::Response::VideoSearch
124
+ def related
125
+ YouTubeG::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/related").parse
126
+ end
127
+
128
+ # Video responses to the current video.
129
+ #
130
+ # === Returns
131
+ # YouTubeG::Response::VideoSearch
132
+ def responses
133
+ YouTubeG::Parser::VideosFeedParser.new("http://gdata.youtube.com/feeds/api/videos/#{unique_id}/responses").parse
134
+ end
135
+
136
+ # The ID of the video, useful for searching for the video again without having to store it anywhere.
137
+ # A regular query search, with this id will return the same video.
138
+ #
139
+ # === Example
140
+ # >> video.unique_id
141
+ # => "ZTUVgYoeN_o"
142
+ #
143
+ # === Returns
144
+ # String: The Youtube video id.
145
+ def unique_id
146
+ video_id[/videos\/([^<]+)/, 1]
147
+ end
148
+
149
+ # Allows you to check whether the video can be embedded on a webpage.
150
+ #
151
+ # === Returns
152
+ # Boolean: True if the video can be embedded, false if not.
153
+ def embeddable?
154
+ not @noembed
155
+ end
156
+
157
+ # Provides a URL and various other types of information about a video.
158
+ #
159
+ # === Returns
160
+ # YouTubeG::Model::Content: Data about the embeddable video.
161
+ def default_media_content
162
+ @media_content.find { |c| c.is_default? }
163
+ end
164
+
165
+ # Gives you the HTML to embed the video on your website.
166
+ #
167
+ # === Returns
168
+ # String: The HTML for embedding the video on your website.
169
+ def embed_html(width = 425, height = 350)
170
+ <<EDOC
171
+ <object width="#{width}" height="#{height}">
172
+ <param name="movie" value="#{embed_url}"></param>
173
+ <param name="wmode" value="transparent"></param>
174
+ <embed src="#{embed_url}" type="application/x-shockwave-flash"
175
+ wmode="transparent" width="#{width}" height="#{height}"></embed>
176
+ </object>
177
+ EDOC
178
+ end
179
+
180
+ # The URL needed for embedding the video in a page.
181
+ #
182
+ # === Returns
183
+ # String: Absolute URL for embedding video
184
+ def embed_url
185
+ @player_url.sub('watch?', '').sub('=', '/')
186
+ end
187
+
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,169 @@
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
+ published_at = Time.parse(entry.elements["published"].text)
25
+ updated_at = Time.parse(entry.elements["updated"].text)
26
+
27
+ # parse the category and keyword lists
28
+ categories = []
29
+ keywords = []
30
+ entry.elements.each("category") do |category|
31
+ # determine if it's really a category, or just a keyword
32
+ scheme = category.attributes["scheme"]
33
+ if (scheme =~ /\/categories\.cat$/)
34
+ # it's a category
35
+ categories << YouTubeG::Model::Category.new(
36
+ :term => category.attributes["term"],
37
+ :label => category.attributes["label"])
38
+
39
+ elsif (scheme =~ /\/keywords\.cat$/)
40
+ # it's a keyword
41
+ keywords << category.attributes["term"]
42
+ end
43
+ end
44
+
45
+ title = entry.elements["title"].text
46
+ html_content = entry.elements["content"].text
47
+
48
+ # parse the author
49
+ author_element = entry.elements["author"]
50
+ author = nil
51
+ if author_element
52
+ author = YouTubeG::Model::Author.new(
53
+ :name => author_element.elements["name"].text,
54
+ :uri => author_element.elements["uri"].text)
55
+ end
56
+
57
+ media_group = entry.elements["media:group"]
58
+ description = media_group.elements["media:description"].text
59
+ duration = media_group.elements["yt:duration"].attributes["seconds"].to_i
60
+
61
+ media_content = []
62
+ media_group.elements.each("media:content") do |mce|
63
+ media_content << parse_media_content(mce)
64
+ end
65
+
66
+ player_url = media_group.elements["media:player"].attributes["url"]
67
+
68
+ # parse thumbnails
69
+ thumbnails = []
70
+ media_group.elements.each("media:thumbnail") do |thumb_element|
71
+ # TODO: convert time HH:MM:ss string to seconds?
72
+ thumbnails << YouTubeG::Model::Thumbnail.new(
73
+ :url => thumb_element.attributes["url"],
74
+ :height => thumb_element.attributes["height"].to_i,
75
+ :width => thumb_element.attributes["width"].to_i,
76
+ :time => thumb_element.attributes["time"])
77
+ end
78
+
79
+ rating_element = entry.elements["gd:rating"]
80
+ rating = nil
81
+ if rating_element
82
+ rating = YouTubeG::Model::Rating.new(
83
+ :min => rating_element.attributes["min"].to_i,
84
+ :max => rating_element.attributes["max"].to_i,
85
+ :rater_count => rating_element.attributes["numRaters"].to_i,
86
+ :average => rating_element.attributes["average"].to_f)
87
+ end
88
+
89
+ view_count = (el = entry.elements["yt:statistics"]) ? el.attributes["viewCount"].to_i : 0
90
+
91
+ noembed = entry.elements["yt:noembed"] ? true : false
92
+ racy = entry.elements["media:rating"] ? true : false
93
+
94
+ if where = entry.elements["georss:where"]
95
+ position = where.elements["gml:Point"].elements["gml:pos"].text
96
+ latitude, longitude = position.split(" ")
97
+ end
98
+
99
+ YouTubeG::Model::Video.new(
100
+ :video_id => video_id,
101
+ :published_at => published_at,
102
+ :updated_at => updated_at,
103
+ :categories => categories,
104
+ :keywords => keywords,
105
+ :title => title,
106
+ :html_content => html_content,
107
+ :author => author,
108
+ :description => description,
109
+ :duration => duration,
110
+ :media_content => media_content,
111
+ :player_url => player_url,
112
+ :thumbnails => thumbnails,
113
+ :rating => rating,
114
+ :view_count => view_count,
115
+ :noembed => noembed,
116
+ :racy => racy,
117
+ :where => where,
118
+ :position => position,
119
+ :latitude => latitude,
120
+ :longitude => longitude)
121
+ end
122
+
123
+ def parse_media_content (media_content_element)
124
+ content_url = media_content_element.attributes["url"]
125
+ format_code = media_content_element.attributes["yt:format"].to_i
126
+ format = YouTubeG::Model::Video::Format.by_code(format_code)
127
+ duration = media_content_element.attributes["duration"].to_i
128
+ mime_type = media_content_element.attributes["type"]
129
+ default = (media_content_element.attributes["isDefault"] == "true")
130
+
131
+ YouTubeG::Model::Content.new(
132
+ :url => content_url,
133
+ :format => format,
134
+ :duration => duration,
135
+ :mime_type => mime_type,
136
+ :default => default)
137
+ end
138
+ end
139
+
140
+ class VideosFeedParser < VideoFeedParser #:nodoc:
141
+
142
+ private
143
+ def parse_content(content) #:nodoc:
144
+ doc = REXML::Document.new(content)
145
+ feed = doc.elements["feed"]
146
+
147
+ feed_id = feed.elements["id"].text
148
+ updated_at = Time.parse(feed.elements["updated"].text)
149
+ total_result_count = feed.elements["openSearch:totalResults"].text.to_i
150
+ offset = feed.elements["openSearch:startIndex"].text.to_i
151
+ max_result_count = feed.elements["openSearch:itemsPerPage"].text.to_i
152
+
153
+ videos = []
154
+ feed.elements.each("entry") do |entry|
155
+ videos << parse_entry(entry)
156
+ end
157
+
158
+ YouTubeG::Response::VideoSearch.new(
159
+ :feed_id => feed_id,
160
+ :updated_at => updated_at,
161
+ :total_result_count => total_result_count,
162
+ :offset => offset,
163
+ :max_result_count => max_result_count,
164
+ :videos => videos)
165
+ end
166
+ end
167
+
168
+ end
169
+ end
@@ -0,0 +1,12 @@
1
+ class YouTubeG
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,43 @@
1
+ class YouTubeG
2
+ module Request #:nodoc:
3
+ class BaseSearch #:nodoc:
4
+ attr_reader :url
5
+
6
+ private
7
+
8
+ def base_url #:nodoc:
9
+ "http://gdata.youtube.com/feeds/api/"
10
+ end
11
+
12
+ def set_instance_variables( variables ) #:nodoc:
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) #:nodoc:
20
+ # nothing to do if there are no params
21
+ return '' if (!params || params.empty?)
22
+
23
+ # build up the query param string, tacking on every key/value
24
+ # pair for which the value is non-nil
25
+ u = '?'
26
+ item_count = 0
27
+ params.keys.sort.each do |key|
28
+ value = params[key]
29
+ next if value.nil?
30
+
31
+ u << '&' if (item_count > 0)
32
+ u << "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
33
+ item_count += 1
34
+ end
35
+
36
+ # if we found no non-nil values, we've got no params so just
37
+ # return an empty string
38
+ (item_count == 0) ? '' : u
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ class YouTubeG
2
+ module Request #:nodoc:
3
+ class StandardSearch < BaseSearch #:nodoc:
4
+ attr_reader :max_results # max_results
5
+ attr_reader :order_by # orderby, ([relevance], viewCount, published, rating)
6
+ attr_reader :offset # start-index
7
+ attr_reader :time # time
8
+
9
+ TYPES = [ :top_rated, :top_favorites, :most_viewed, :most_popular,
10
+ :most_recent, :most_discussed, :most_linked, :most_responded,
11
+ :recently_featured, :watch_on_mobile ]
12
+
13
+ def initialize(type, options={})
14
+ if TYPES.include?(type)
15
+ @max_results, @order_by, @offset, @time = nil
16
+ set_instance_variables(options)
17
+ @url = base_url + type.to_s << build_query_params(to_youtube_params)
18
+ else
19
+ raise "Invalid type, must be one of: #{ TYPES.map { |t| t.to_s }.join(", ") }"
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def base_url #:nodoc:
26
+ super << "standardfeeds/"
27
+ end
28
+
29
+ def to_youtube_params #:nodoc:
30
+ {
31
+ 'max-results' => @max_results,
32
+ 'orderby' => @order_by,
33
+ 'start-index' => @offset,
34
+ 'time' => @time
35
+ }
36
+ end
37
+ end
38
+
39
+ end
40
+ end