mikedamage-ruby_tube 0.2.1

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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ ._*
7
+ *~
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mike Green
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,11 @@
1
+ = ruby_tube
2
+
3
+ RubyTube is a simple library built to interact with YouTube through ActiveRecord-like methods (like find, count, all, etc).
4
+
5
+ It's far from polished, and not all its methods are AR-like. It does provide a relatively simple interface for uploading videos to YouTube and retrieving info on the videos you've uploaded.
6
+
7
+ Yet another gem I don't expect anyone to actually use - I just built it because I needed basic YouTube functionality for a Rails app I'm working on.
8
+
9
+ == Copyright
10
+
11
+ Copyright (c) 2009 Mike Green. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ruby_tube"
8
+ gem.summary = %Q{TODO}
9
+ gem.email = "mike.is.green@gmail.com"
10
+ gem.homepage = "http://github.com/mikedamage/ruby_tube"
11
+ gem.authors = ["Mike Green"]
12
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
13
+ end
14
+
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ Rake::TestTask.new(:test) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.pattern = 'test/**/*_test.rb'
23
+ test.verbose = true
24
+ end
25
+
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |test|
29
+ test.libs << 'test'
30
+ test.pattern = 'test/**/*_test.rb'
31
+ test.verbose = true
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+
40
+ task :default => :test
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ if File.exist?('VERSION.yml')
45
+ config = YAML.load(File.read('VERSION.yml'))
46
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
47
+ else
48
+ version = ""
49
+ end
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "ruby_tube #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
56
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.1
data/lib/ruby_tube.rb ADDED
@@ -0,0 +1,167 @@
1
+ require "ostruct"
2
+ require File.join(File.dirname(__FILE__), "yt_client.rb")
3
+ require File.join(File.dirname(__FILE__), "yt_video.rb")
4
+ require File.join(File.dirname(__FILE__), "yt_comment.rb")
5
+ require File.join(File.dirname(__FILE__), "yt_rating.rb")
6
+
7
+ class RubyTube < YTClient
8
+
9
+ def initialize(username, password, key, options={:refresh=>300})
10
+ super(username, password, key, options)
11
+ end
12
+
13
+ def find(id)
14
+ xml = check_video(id)
15
+ entry = (xml/"entry")
16
+ status = (xml/"yt:state").empty? ? "ok" : (xml/"yt:state").attr("name")
17
+ video = YTVideo.new({
18
+ :id => (entry/"yt:videoid").text,
19
+ :title => (entry/"title").text,
20
+ :description => (entry/"media:description").text,
21
+ :keywords => (entry/"media:keywords").text,
22
+ :duration => (entry/"yt:duration").attr("seconds").to_i,
23
+ :player_uri => (entry/"link[@rel='alternate']").attr("href"),
24
+ :ratings_uri => (entry/"link[@rel$='ratings']").attr("href"),
25
+ :comments_uri => (entry/"gd:comments").search("gd:feedlink").attr("href"),
26
+ :comment_count => (entry/"gd:comments").search("gd:feedlink").attr("countHint").to_i,
27
+ :published_at => Time.parse((entry/"published").text),
28
+ :updated_at => Time.parse((entry/"updated").text),
29
+ :thumbnails => {
30
+ :small => {
31
+ :url => (entry/"media:thumbnail[@url$='default.jpg']").attr("url"),
32
+ :width => 120,
33
+ :height => 90
34
+ },
35
+ :large => {
36
+ :url => (entry/"media:thumbnail[@url$='hqdefault.jpg]").attr("url"),
37
+ :width => 480,
38
+ :height => 360
39
+ }
40
+ },
41
+ :view_count => (entry/"yt:statistics").empty? ? 0 : (entry/"yt:statistics").attr("viewCount"),
42
+ :favorite_count => (entry/"yt:statistics").empty? ? 0 : (entry/"yt:statistics").attr("favoriteCount"),
43
+ :comments => comments((entry/"yt:videoid").text),
44
+ :ratings => ratings((entry/"yt:videoid").text),
45
+ :status => status,
46
+ :thumbnails => process_thumbnail_urls(entry)
47
+ })
48
+ return video
49
+ end
50
+
51
+ def find_all
52
+ @all = all()
53
+ videos = Array.new
54
+ (all/"entry").each do |entry|
55
+ status = (entry/"yt:state").empty? ? "ok" : (entry/"yt:state").attr("name")
56
+ video = YTVideo.new({
57
+ :id => (entry/"yt:videoid").text,
58
+ :title => (entry/"title").text,
59
+ :description => (entry/"media:description").text,
60
+ :keywords => (entry/"media:keywords").text,
61
+ :duration => (entry/"yt:duration").attr("seconds").to_i,
62
+ :player_uri => (entry/"link[@rel='alternate']").attr("href"),
63
+ :ratings_uri => (entry/"link[@rel$='ratings']").attr("href"),
64
+ :comments_uri => (entry/"gd:comments").search("gd:feedlink").attr("href"),
65
+ :comment_count => (entry/"gd:comments").search("gd:feedlink").attr("countHint").to_i,
66
+ :published_at => Time.parse((entry/"published").text),
67
+ :updated_at => Time.parse((entry/"updated").text),
68
+ :thumbnails => {
69
+ :small => {
70
+ :url => (entry/"media:thumbnail[@url$='default.jpg']").attr("url"),
71
+ :width => 120,
72
+ :height => 90
73
+ },
74
+ :large => {
75
+ :url => (entry/"media:thumbnail[@url$='hqdefault.jpg]").attr("url"),
76
+ :width => 480,
77
+ :height => 360
78
+ }
79
+ },
80
+ :view_count => (entry/"yt:statistics").empty? ? 0 : (entry/"yt:statistics").attr("viewCount"),
81
+ :favorite_count => (entry/"yt:statistics").empty? ? 0 : (entry/"yt:statistics").attr("favoriteCount"),
82
+ :comments => comments((entry/"yt:videoid").text),
83
+ :ratings => ratings((entry/"yt:videoid").text),
84
+ :status => status,
85
+ :thumbnails => process_thumbnail_urls(entry)
86
+ })
87
+ videos << video
88
+ end
89
+ return videos
90
+ end
91
+
92
+ def count
93
+ super
94
+ end
95
+
96
+ def comments(id)
97
+ xml = super
98
+ comments = Array.new
99
+ if (xml/"entry").nitems > 0
100
+ (xml/"entry").each do |entry|
101
+ comment = YTComment.new({
102
+ :title => (entry/"title").text,
103
+ :content => (entry/"content").text,
104
+ :author => (entry/"author").search("name").text,
105
+ :author_uri => (entry/"author").search("uri").text,
106
+ :video_uri => (entry/"link[@rel='related']").attr("href")
107
+ })
108
+ comments << comment
109
+ end
110
+ end
111
+ return comments
112
+ end
113
+
114
+ def ratings(id)
115
+ xml = super
116
+ rating = nil
117
+ if xml
118
+ rating = YTRating.new({
119
+ :num_raters => xml.attr("numRaters").to_i,
120
+ :max => xml.attr("max").to_i,
121
+ :min => xml.attr("min").to_i,
122
+ :average => xml.attr("average").to_f
123
+ })
124
+ end
125
+ return rating
126
+ end
127
+
128
+ def update_video(id, options)
129
+ video = find(id)
130
+ if options[:title]
131
+ video.title = options[:title]
132
+ end
133
+ if options[:description]
134
+ video.description = options[:description]
135
+ end
136
+ if options[:keywords]
137
+ video.keywords = options[:keywords]
138
+ end
139
+ entry = video.to_xml
140
+ response = update(video.id, entry)
141
+ if response.status_code == 200
142
+ return video
143
+ else
144
+ return false
145
+ end
146
+ end
147
+
148
+ def upload_video(filename, options={})
149
+ response = upload(filename, options)
150
+ end
151
+
152
+ def delete_video(id)
153
+ response = delete(id)
154
+ if response.status_code == 200
155
+ return true
156
+ else
157
+ return false
158
+ end
159
+ end
160
+
161
+ private
162
+ def process_thumbnail_urls(hpricot)
163
+ thumbs = (hpricot/"media:thumbnail")
164
+ OpenStruct.new({:big => thumbs.last.attr("url"), :small => thumbs.first.attr("url")})
165
+ end
166
+
167
+ end
data/lib/yt_client.rb ADDED
@@ -0,0 +1,119 @@
1
+ require "uri"
2
+ require "net/http"
3
+ require "gdata"
4
+ require "hpricot"
5
+ require "httparty"
6
+ require "time"
7
+
8
+ class YTClient
9
+ attr_accessor :username, :password, :developer_key, :token, :client
10
+ include HTTParty
11
+
12
+ base_uri "http://gdata.youtube.com/feeds/api"
13
+ format :plain
14
+
15
+ UPLOAD_URI = "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads"
16
+
17
+ def initialize(username, password, key, options={:refresh=>300})
18
+ @username = username
19
+ @password = password
20
+ @developer_key = key
21
+ @client = GData::Client::YouTube.new
22
+ @client.source = "acer_timeline_contest"
23
+ @client.developer_key = @developer_key
24
+ @token = @client.clientlogin(@username, @password)
25
+ @options = options
26
+ end
27
+
28
+ def all
29
+ if @all_vids
30
+ if Time.parse((@all_vids/"updated").text) < (Time.now - @options[:refresh])
31
+ @all_vids = Hpricot(@client.get(self.class.base_uri + "/users/default/uploads").body)
32
+ else
33
+ return @all_vids
34
+ end
35
+ else
36
+ @all_vids = Hpricot(@client.get(self.class.base_uri + "/users/default/uploads").body)
37
+ end
38
+ end
39
+
40
+ def count
41
+ (@all_vids/"entry").nitems
42
+ end
43
+
44
+ def check_video(id)
45
+ Hpricot(@client.get(self.class.base_uri + "/videos/#{id}").body)
46
+ end
47
+
48
+ def ratings(id)
49
+ response = Hpricot(@client.get(self.class.base_uri + "/videos/#{id}").body)
50
+ ratings = (response/"gd:rating")
51
+ if ratings.nitems > 0
52
+ return ratings
53
+ else
54
+ return nil
55
+ end
56
+ end
57
+
58
+ def comments(id)
59
+ Hpricot(@client.get(self.class.base_uri + "/videos/#{id}/comments").body)
60
+ end
61
+
62
+ def upload(file, options={})
63
+ upload_uri = URI.parse(UPLOAD_URI)
64
+ binary_data = read_file(file)
65
+ request_data = <<-REQDATA
66
+ --bbe873dc
67
+ Content-Type: application/atom+xml; charset=utf-8
68
+
69
+ <?xml version="1.0"?>
70
+ <entry xmlns="http://www.w3.org/2005/Atom"
71
+ xmlns:media="http://search.yahoo.com/mrss/"
72
+ xmlns:yt="http://gdata.youtube.com/schemas/2007">
73
+ <media:group>
74
+ <media:title type="plain">#{options[:title]}</media:title>
75
+ <media:description type="plain">
76
+ #{options[:description]}
77
+ </media:description>
78
+ <media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">
79
+ People
80
+ </media:category>
81
+ <media:keywords>#{options[:keywords]}</media:keywords>
82
+ </media:group>
83
+ </entry>
84
+ --bbe873dc
85
+ Content-Type: #{options[:content_type]}
86
+ Content-Transfer-Encoding: binary
87
+
88
+ #{binary_data}
89
+ --bbe873dc
90
+ REQDATA
91
+ http = Net::HTTP.new(upload_uri.host)
92
+ headers = {
93
+ 'GData-Version' => "2",
94
+ 'X-GData-Key' => "key=#{@developer_key}",
95
+ 'Slug' => File.basename(file),
96
+ 'Authorization' => "GoogleLogin auth=#{@token}",
97
+ 'Content-Type' => 'multipart/related; boundary="bbe873dc"',
98
+ 'Content-Length' => binary_data.length.to_s,
99
+ 'Connection' => 'close'
100
+ }
101
+ res = http.post(upload_uri.path, request_data, headers)
102
+ response = {:code => res.code, :body => Hpricot(res.body)}
103
+ return response
104
+ end
105
+
106
+ def update(id, xml)
107
+ response = @client.put(self.class.base_uri + "/users/default/uploads/#{id}", xml)
108
+ end
109
+
110
+ def delete(id)
111
+ response = @client.delete(self.class.base_uri + "/users/default/uploads/#{id}")
112
+ end
113
+
114
+ private
115
+ def read_file(file)
116
+ contents = File.open(file, "r") {|io| io.read }
117
+ return contents
118
+ end
119
+ end
data/lib/yt_comment.rb ADDED
@@ -0,0 +1,13 @@
1
+ class YTComment
2
+
3
+ attr_accessor :title, :content, :author, :author_uri, :video_uri
4
+
5
+ def initialize(data)
6
+ @title = data[:title]
7
+ @content = data[:content]
8
+ @author = data[:author]
9
+ @author_uri = data[:author_uri]
10
+ @video_uri = data[:video_uri]
11
+ end
12
+
13
+ end
data/lib/yt_rating.rb ADDED
@@ -0,0 +1,10 @@
1
+ class YTRating
2
+ attr_accessor :num_raters, :min, :max, :average
3
+
4
+ def initialize(data)
5
+ @num_raters = data[:num_raters].to_i
6
+ @min = data[:min].to_i
7
+ @max = data[:max].to_i
8
+ @average = data[:average].to_f
9
+ end
10
+ end
data/lib/yt_video.rb ADDED
@@ -0,0 +1,44 @@
1
+ class YTVideo
2
+
3
+ attr_reader :id, :duration, :player_uri, :thumbnails, :published_at, :updated_at, :ratings_uri, :comments_uri, :view_count, :favorite_count, :comment_count, :ratings, :comments, :status
4
+ attr_accessor :title, :description, :keywords
5
+
6
+ def initialize(data)
7
+ @id = data[:id]
8
+ @title = data[:title]
9
+ @description = data[:description]
10
+ @keywords = data[:keywords]
11
+ @duration = data[:duration]
12
+ @player_uri = data[:player_uri]
13
+ @ratings_uri = data[:ratings_uri]
14
+ @comments_uri = data[:comments_uri]
15
+ @published_at = data[:published_at]
16
+ @updated_at = data[:updated_at]
17
+ @thumbnails = data[:thumbnails]
18
+ @view_count = data[:view_count]
19
+ @favorite_count = data[:favorite_count]
20
+ @comment_count = data[:comment_count]
21
+ @ratings = data[:ratings]
22
+ @comments = data[:comments]
23
+ @status = data[:status]
24
+ @thumbnails = data[:thumbnails]
25
+ end
26
+
27
+ def to_xml
28
+ data = <<-XMLSTOP
29
+ <?xml version="1.0"?>
30
+ <entry xmlns="http://www.w3.org/2005/Atom"
31
+ xmlns:media="http://search.yahoo.com/mrss/"
32
+ xmlns:yt="http://gdata.youtube.com/schemas/2007">
33
+ <media:group>
34
+ <media:title type="plain">#{@title}</media:title>
35
+ <media:description type="plain">#{@description}</media:description>
36
+ <media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Tech</media:category>
37
+ <media:keywords>#{@keywords}</media:keywords>
38
+ </media:group>
39
+ </entry>
40
+ XMLSTOP
41
+ return data
42
+ end
43
+
44
+ end
Binary file
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class RubyTubeTest < Test::Unit::TestCase
4
+ context "a RubyTube instance" do
5
+ setup do
6
+ @yt = RubyTube.new
7
+ end
8
+
9
+ should "contain an instance of GData::Client::YouTube" do
10
+ assert @yt.client.is_a?(GData::Client::YouTube)
11
+ end
12
+
13
+ should "get a ClientLogin token from YouTube" do
14
+ assert @yt.token
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'ruby_tube'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,33 @@
1
+ require "test_helper"
2
+
3
+
4
+ class YTClientTest < Test::Unit::TestCase
5
+ context "A YTClient instance" do
6
+ setup do
7
+ @yt = YTClient.new("frcmike", "k2130k", RubyTube::DEV_KEY)
8
+ end
9
+
10
+ should "retrieve a ClientLogin token on instantiation" do
11
+ assert not_nil(@yt.token)
12
+ end
13
+
14
+ should "return a list of uploaded files" do
15
+ assert not_nil(@yt.all)
16
+ end
17
+
18
+ should "return Fixnum when #count is called" do
19
+ assert @yt.count.is_a?(Fixnum)
20
+ end
21
+
22
+ # TODO: write upload tests w/o having to repeat the upload process for each test
23
+ end
24
+
25
+ private
26
+ def not_nil(item)
27
+ !item.nil?
28
+ end
29
+
30
+ def not_empty(item)
31
+ !item.empty?
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mikedamage-ruby_tube
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Green
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: gdata
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: httparty
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.3
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: hpricot
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.1
44
+ version:
45
+ description:
46
+ email: mike.is.green@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.rdoc
59
+ - Rakefile
60
+ - VERSION
61
+ - lib/ruby_tube.rb
62
+ - lib/yt_client.rb
63
+ - lib/yt_comment.rb
64
+ - lib/yt_rating.rb
65
+ - lib/yt_video.rb
66
+ - test/josh_walking.mp4
67
+ - test/ruby_tube_test.rb
68
+ - test/test_helper.rb
69
+ - test/yt_client_test.rb
70
+ has_rdoc: false
71
+ homepage: http://github.com/mikedamage/ruby_tube
72
+ post_install_message:
73
+ rdoc_options:
74
+ - --charset=UTF-8
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.2.0
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Custom Ruby library for working with YouTube videos, for the Acer Timeline contest project
96
+ test_files:
97
+ - test/ruby_tube_test.rb
98
+ - test/test_helper.rb
99
+ - test/yt_client_test.rb