bliptv 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Michael Kelly Sutton
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.
@@ -0,0 +1,16 @@
1
+ lib/bliptv/api_spec.rb
2
+ lib/bliptv/base.rb
3
+ lib/bliptv/multipart_params.rb
4
+ lib/bliptv/request.rb
5
+ lib/bliptv/video.rb
6
+ lib/bliptv.rb
7
+ lib/ext/array.rb
8
+ lib/ext/hash.rb
9
+ lib/ext/open_struct.rb
10
+ License.txt
11
+ Manifest
12
+ Rakefile
13
+ README.rdoc
14
+ test/test_base.rb
15
+ test/test_suite.rb
16
+ test/test_video.rb
@@ -0,0 +1,86 @@
1
+ = Blip.tv Ruby Library
2
+
3
+ A Ruby gem for accessing the blip.tv API (http://wiki.blip.tv/index.php/REST_Upload_API)
4
+
5
+ == Install
6
+
7
+ gem sources -a http://gems.github.com
8
+ sudo gem install kellysutton-bliptv
9
+ sudo gem install rest-client
10
+ sudo gem install mime-types
11
+
12
+ == Usage
13
+
14
+ While the blip.tv API is extremely simple to use, sometimes it's nice to have a wrapper
15
+ library to make life even easier. The BlipTV Ruby gem is just that.
16
+
17
+ Let's say you want to upload a video:
18
+
19
+ require 'rubygems'
20
+ require 'bliptv'
21
+
22
+ client = BlipTV::Base.new
23
+
24
+ options = {
25
+ :username => "barack_obama",
26
+ :password => "michellexoxo",
27
+ :file => File.open('movie.mov'),
28
+ :title => "Ordering Hamburgers in DC",
29
+ :description => "I love this one burger joint, but the press keeps following me."
30
+ }
31
+
32
+ video = client.upload_video(options) #=> BlipTV::Video
33
+
34
+ The upload_video method call returns a BlipTV::Video object
35
+ that you can play around with.
36
+
37
+ Or what if you wanted a list of all videos by a user? Also easy:
38
+
39
+ require 'rubygems'
40
+ require 'bliptv'
41
+
42
+ client = BlipTV::Base.new
43
+
44
+ video_list = client.find_all_videos_by_user("barack_obama")
45
+
46
+ find_all_videos_by_user will return a list of BlipTV::Video objects.
47
+
48
+ More usage coming soon.
49
+
50
+ == Authors
51
+
52
+ Kelly Sutton (http://michaelkellysutton.com)
53
+
54
+ == Features
55
+
56
+ Provides an easy way to interact with the blip.tv API in Ruby.
57
+
58
+ == Special Thanks
59
+
60
+ This gem is extensively based on Shane Vitrana's Viddler
61
+ gem as well as the YouTubeG gem. Much of the code was simply
62
+ copied and then tweaked to fit the blip.tv nomenclature
63
+ of certain calls.
64
+
65
+ == License
66
+
67
+ Copyright (c) 2009 Michael Kelly Sutton
68
+
69
+ Permission is hereby granted, free of charge, to any person obtaining
70
+ a copy of this software and associated documentation files (the
71
+ "Software"), to deal in the Software without restriction, including
72
+ without limitation the rights to use, copy, modify, merge, publish,
73
+ distribute, sublicense, and/or sell copies of the Software, and to
74
+ permit persons to whom the Software is furnished to do so, subject to
75
+ the following conditions:
76
+
77
+ The above copyright notice and this permission notice shall be
78
+ included in all copies or substantial portions of the Software.
79
+
80
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
81
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
82
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
83
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
84
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
85
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
86
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('bliptv', '0.1.1') do |p|
6
+ p.description = "A Ruby library from Blip.tv"
7
+ p.url = "http://github.com/kellysutton/bliptv"
8
+ p.author = "Michael Kelly Sutton"
9
+ p.email = "michael.k.sutton@gmail.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.development_dependencies = []
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{bliptv}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Michael Kelly Sutton"]
9
+ s.date = %q{2009-06-05}
10
+ s.description = %q{A Ruby library from Blip.tv}
11
+ s.email = %q{michael.k.sutton@gmail.com}
12
+ s.extra_rdoc_files = ["lib/bliptv/api_spec.rb", "lib/bliptv/base.rb", "lib/bliptv/multipart_params.rb", "lib/bliptv/request.rb", "lib/bliptv/video.rb", "lib/bliptv.rb", "lib/ext/array.rb", "lib/ext/hash.rb", "lib/ext/open_struct.rb", "README.rdoc"]
13
+ s.files = ["lib/bliptv/api_spec.rb", "lib/bliptv/base.rb", "lib/bliptv/multipart_params.rb", "lib/bliptv/request.rb", "lib/bliptv/video.rb", "lib/bliptv.rb", "lib/ext/array.rb", "lib/ext/hash.rb", "lib/ext/open_struct.rb", "License.txt", "Manifest", "Rakefile", "README.rdoc", "test/test_base.rb", "test/test_suite.rb", "test/test_video.rb", "bliptv.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/kellysutton/bliptv}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Bliptv", "--main", "README.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{bliptv}
19
+ s.rubygems_version = %q{1.3.2}
20
+ s.summary = %q{A Ruby library from Blip.tv}
21
+ s.test_files = ["test/test_base.rb", "test/test_suite.rb", "test/test_video.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ else
29
+ end
30
+ else
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ #
2
+ # This is the main entry point for the library. All files in the project are
3
+ # included here, as well as anything required across the project.
4
+ #
5
+
6
+ $:.unshift(File.dirname(__FILE__)) unless
7
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
8
+
9
+ require 'rubygems'
10
+ require 'active_support'
11
+ require 'ostruct'
12
+ require 'net/http'
13
+ require 'uri'
14
+
15
+ require 'ext/array'
16
+ require 'ext/hash'
17
+ require 'ext/open_struct'
18
+
19
+ require 'bliptv/base'
20
+ require 'bliptv/video'
21
+ require 'bliptv/api_spec'
22
+ require 'bliptv/multipart_params'
23
+ require 'bliptv/request'
24
+
25
+
26
+ module BlipTV
27
+ end
@@ -0,0 +1,53 @@
1
+ module BlipTV
2
+ class ApiSpec
3
+ VIDEOS_UPLOAD_ATTRS = {
4
+ :required => [
5
+ :title,
6
+ :file,
7
+ :userlogin,
8
+ :password
9
+ ],
10
+ :optional => [
11
+ :thumbnail,
12
+ :nsfw,
13
+ :description,
14
+ :keywords,
15
+ :categories,
16
+ :license,
17
+ :interactive_post
18
+ ]
19
+ }
20
+
21
+ VIDEOS_DELETE_ATTRS = {
22
+ :required => [
23
+ :userlogin,
24
+ :password
25
+ ],
26
+ :optional => [
27
+ :username # kind of sloppy, because a user is unlikely to specify both a userlogin AND a username
28
+ ]
29
+ }
30
+
31
+ def self.check_attributes(bliptv_method, attributes)
32
+ valid_attributes = bliptv_method_to_const(bliptv_method)
33
+ required = valid_attributes[:required] || Array.new
34
+ optional = valid_attributes[:optional] || Array.new
35
+
36
+ # blip calls it a "userlogin" instead of a "username"
37
+ if attributes[:username] != nil
38
+ attributes[:userlogin] = attributes[:username]
39
+ attributes.delete(:username)
40
+ end
41
+
42
+ attributes.assert_valid_keys(required + optional)
43
+ attributes.assert_required_keys(required)
44
+ end
45
+
46
+ protected
47
+
48
+ def self.bliptv_method_to_const(method)
49
+ const_name = method.gsub('.', '_').upcase
50
+ const_get("#{const_name}_ATTRS")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,127 @@
1
+ module BlipTV
2
+ # Generic BlipTV exception class.
3
+ class BlipTVError < StandardError #:nodoc:
4
+ end
5
+
6
+ # Raised when username and password has not been set.
7
+ class AuthenticationRequiredError < BlipTVError #:nodoc:
8
+ def message
9
+ "Method that you're trying to execute requires username and password."
10
+ end
11
+ end
12
+
13
+ # Raised when calling not yet implemented API methods.
14
+ class NotImplementedError < BlipTVError #:nodoc:
15
+ def message
16
+ 'This method is not yet implemented.'
17
+ end
18
+ end
19
+
20
+ #
21
+ # This is the class that should be instantiated for basic
22
+ # communication with the Blip.tv API
23
+ #
24
+ class Base
25
+
26
+ # TODO allow user to specify userlogin and password on intialize
27
+ def initialize
28
+ end
29
+
30
+ # Implements the Blip.tv REST Upload API
31
+ #
32
+ # <tt>new_attributes</tt> hash should contain next required keys:
33
+ # * <tt>title:</tt> The video title;
34
+ # * <tt>file:</tt> The video file;
35
+ #
36
+ # and optionally:
37
+ # * <tt>thumbnail:</tt> A thumbnail file;
38
+ # * <tt>nsfw:</tt> true if explicit, false otherwise. Defaults to false;
39
+ # * <tt>description:</tt> A description of the video
40
+ # * <tt>username:</tt> Username
41
+ # * <tt>password:</tt> Password
42
+ # * <tt>keywords:</tt> A comma-separated string of keywords # TODO this should be nice and also accept Arrays
43
+ # * <tt>categories:</tt> A Hash of categories
44
+ # * <tt>license:</tt> A license for the video
45
+ # * <tt>interactive_post:</tt> Specify whether or not a post is interactive. More here[http://wiki.blip.tv/index.php/API_2.0:_Post_Interactivity]
46
+ #
47
+ # Example:
48
+ #
49
+ # bliptv.upload_video(:title => 'Check out this guy getting kicked in the nuts!', :file => File.open('/movies/nuts.mov'))
50
+ #
51
+ # Returns BlipTV::Video instance.
52
+ #
53
+ def upload_video(new_attributes={})
54
+ BlipTV::ApiSpec.check_attributes('videos.upload', new_attributes)
55
+
56
+ new_attributes = {
57
+ :post => "1",
58
+ :item_type => "file",
59
+ :skin => "xmlhttprequest",
60
+ :file_role => "Web"
61
+ }.merge(new_attributes) # blip.tv requires the "post" param to be set to 1
62
+
63
+ request = BlipTV::Request.new(:post, 'videos.upload')
64
+ request.run do |p|
65
+ for param, value in new_attributes
66
+ p.send("#{param}=", value)
67
+ end
68
+ end
69
+
70
+ BlipTV::Video.new(request.response['post_url'].to_s)
71
+ end
72
+
73
+
74
+ # Looks up all videos on Blip.tv with a given <tt>username</tt>
75
+ #
76
+ # Options hash could contain next values:
77
+ # * <tt>page</tt>: The "page number" of results to retrieve (e.g. 1, 2, 3); if not specified, the default value equals 1.
78
+ # * <tt>pagelen</tt>: The number of results to retrieve per page (maximum 100). If not specified, the default value equals 20.
79
+ #
80
+ # Example:
81
+ #
82
+ # bliptv.find_all_videos_by_user("username")
83
+ # or
84
+ # bliptv.find_all_videos_by_user("username", {:page => 1, :pagelen => 20})
85
+ #
86
+ # Returns array of BlipTV::Video objects.
87
+ #
88
+ def find_all_videos_by_user(username, options={})
89
+ options[:page] ||= 1; options[:pagelen] ||= 20
90
+ url, path = "#{username}.blip.tv", "/posts/?skin=api&page=#{options[:page]}&pagelen=#{options[:pagelen]}"
91
+ request = Net::HTTP.get(url, path)
92
+ hash = Hash.from_xml(request)
93
+ hash == nil ? [] : parse_videos_list(hash)
94
+ end
95
+
96
+
97
+ # Searches through and returns videos based on the <tt>search_string</tt>.
98
+ #
99
+ # This method is a direct call of Blip.tv's search method. You get what you get. No guarantees are made.
100
+ #
101
+ # Example:
102
+ #
103
+ # bliptv.search_videos("cool stuff")
104
+ #
105
+ # Returns an array of BlipTV::Video objects
106
+ #
107
+ def search_videos(search_string)
108
+ request = Net::HTTP.get(URI.parse("http://www.blip.tv/search/?search=#{search_string}&skin=api"))
109
+ hash = Hash.from_xml(request)
110
+ parse_videos_list(hash)
111
+ end
112
+
113
+ private
114
+
115
+ def parse_videos_list(hash)
116
+ list = []
117
+ begin
118
+ hash["response"]["payload"]["asset"].each do |entry|
119
+ list << Video.new(entry)
120
+ end
121
+ rescue NoMethodError
122
+ list = []
123
+ end
124
+ list
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,49 @@
1
+ require 'mime/types'
2
+
3
+ class MultipartParams #:nodoc:
4
+ attr_accessor :content_type, :body
5
+
6
+ def initialize(param_hash={})
7
+ @boundary_token = generate_boundary_token
8
+ self.content_type = "multipart/form-data; boundary=#{@boundary_token}"
9
+ self.body = pack_params(param_hash)
10
+ end
11
+
12
+ protected
13
+
14
+ def generate_boundary_token
15
+ [Array.new(8) {rand(256)}].join
16
+ end
17
+
18
+ def pack_params(hash)
19
+ marker = "--#{@boundary_token}\r\n"
20
+ files_params = hash.find_all{|k,v| v.is_a?(File)}.to_h
21
+ text_params = hash - files_params
22
+
23
+ pack_hash(text_params, marker) + marker + pack_hash(files_params, marker) + "--#{@boundary_token}--\r\n"
24
+ end
25
+
26
+ def pack_hash(hash, marker)
27
+ hash.map do |name, value|
28
+ marker + case value
29
+ when String
30
+ text_to_multipart(name, value)
31
+ when File
32
+ file_to_multipart(name, value)
33
+ end
34
+ end.join('')
35
+ end
36
+
37
+ def file_to_multipart(key,file)
38
+ filename = File.basename(file.path)
39
+ mime_types = MIME::Types.of(filename)
40
+ mime_type = mime_types.empty? ? "application/octet-stream" : mime_types.first.content_type
41
+ part = %Q[Content-Disposition: form-data; name="#{key}"; filename="#{filename}"\r\n]
42
+ part += "Content-Transfer-Encoding: binary\r\n"
43
+ part += "Content-Type: video/mp4\r\n\r\n#{file.read}\r\n"
44
+ end
45
+
46
+ def text_to_multipart(key,value)
47
+ "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n#{value.to_s}\r\n"
48
+ end
49
+ end
@@ -0,0 +1,135 @@
1
+ require 'rest_client'
2
+
3
+ module BlipTV
4
+
5
+ # Raised when response from Blip.tv contains absolutely no data
6
+ class EmptyResponseError < BlipTVError #:nodoc:
7
+ end
8
+
9
+ # Raised when response from Blip.tv contains an error
10
+ class ResponseError < BlipTVError #:nodoc:
11
+ def initialize(message)
12
+ super message
13
+ end
14
+ end
15
+
16
+ # Class used to send requests over http to Viddler API.
17
+ class Request #:nodoc:
18
+
19
+ API_URL = 'http://uploads.blip.tv/'
20
+ DEFAULT_HEADERS = {:accept => 'application/xml', :content_type => 'multi-part/form-data'}
21
+
22
+ attr_accessor :url, :http_method, :response, :body
23
+ attr_reader :headers, :params
24
+
25
+ def initialize(http_method, method) #:nodoc:
26
+ @http_method = http_method.to_s
27
+ @url = API_URL
28
+ self.params = {} #{:method => viddlerize(method)}
29
+ self.headers = DEFAULT_HEADERS
30
+ end
31
+
32
+ # Use this method to setup your request's payload and headers.
33
+ #
34
+ # Example:
35
+ #
36
+ # request.set :headers do |h|
37
+ # h.content_type = 'application/ufo'
38
+ # end
39
+ #
40
+ # request.set :params do |p|
41
+ # p.sessionid = '12323'
42
+ # p.api_key = '13123
43
+ # end
44
+ #
45
+ def set(container, &declarations)
46
+ struct = OpenStruct.new
47
+ declarations.call(struct)
48
+ send("#{container}=", struct.table)
49
+ end
50
+
51
+ # Send http request to Viddler API.
52
+ def run(&block)
53
+ if block_given?
54
+ set(:params, &block)
55
+ end
56
+
57
+ if post? and multipart?
58
+ put_multipart_params_into_body
59
+ else
60
+ put_params_into_url
61
+ end
62
+
63
+ request = RestClient::Request.execute(
64
+ :method => http_method,
65
+ :url => url,
66
+ :headers => headers,
67
+ :payload => body
68
+ )
69
+ self.response = parse_response(request)
70
+ end
71
+
72
+ private
73
+
74
+ def parse_response(raw_response)
75
+ raise EmptyResponseError if raw_response.blank?
76
+ response_hash = Hash.from_xml(raw_response)
77
+ begin
78
+ if response_error = response_hash["otter_responses"]["response"]["error"]
79
+ raise ResponseError.new(bliptv_error_message(response_error))
80
+ end
81
+ rescue # we don't care if it fails, that means it works
82
+ end
83
+ response_hash["post_url"] = raw_response.match(/\d{3,12}/)[0] # extracts the post_url, since from_xml isn't grabbing it
84
+ response_hash
85
+ end
86
+
87
+ def put_multipart_params_into_body
88
+ multiparams = MultipartParams.new(params)
89
+ self.body = multiparams.body
90
+ self.headers = {:content_type => multiparams.content_type}
91
+ end
92
+
93
+ def put_params_into_url
94
+ self.url = API_URL + '?' + params.to_query
95
+ end
96
+
97
+ def viddlerize(name)
98
+ if name.include?('viddler.')
99
+ name
100
+ else
101
+ 'viddler.' + name
102
+ end
103
+ end
104
+
105
+ def params=(hash) #:nodoc:
106
+ @params ||= Hash.new
107
+ @params.update(hash)
108
+ end
109
+
110
+ def headers=(hash) #:nodoc:
111
+ @headers ||= Hash.new
112
+ @headers.update(hash)
113
+ end
114
+
115
+ def multipart? #:nodoc:
116
+ # TOOD let's be nice and do a File.exists?(v)
117
+ if params.find{|k,v| v.is_a?(File)} then true else false end
118
+ end
119
+
120
+ def post? #:nodoc:
121
+ http_method == 'post'
122
+ end
123
+
124
+ def bliptv_error_message(response_error)
125
+ description = response_error['description'] || ''
126
+ details = response_error['details'] || ''
127
+ code = response_error['code'] || ''
128
+
129
+ details = ": #{details};" unless details.empty?
130
+ code = " [code: #{code}]" unless code.empty?
131
+ %Q[#{description}#{details}#{code}]
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,165 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module BlipTV
5
+
6
+ BLIP_TV_ID_EXPR = /\d{3,12}/
7
+
8
+ # Raised when pinging Blip.tv for video information results in an error
9
+ class VideoResponseError < BlipTVError #:nodoc:
10
+ def initialize(message)
11
+ super message
12
+ end
13
+ end
14
+
15
+ class VideoDeleteError < BlipTVError
16
+ def intialize(message)
17
+ super message
18
+ end
19
+ end
20
+
21
+ # This class wraps Blip.tv's video's information.
22
+ class Video
23
+
24
+ attr_accessor :id,
25
+ :title,
26
+ :description,
27
+ :guid,
28
+ :deleted,
29
+ :view_count,
30
+ :tags,
31
+ :links,
32
+ :author,
33
+ :update_time,
34
+ :explicit,
35
+ :notes,
36
+ :license,
37
+ :embed_url,
38
+ :embed_code
39
+
40
+ def initialize(blip_id) #:nodoc:
41
+ blip_id = blip_id.to_s if blip_id.class == Fixnum
42
+
43
+ if blip_id.class == String && blip_id.match(BLIP_TV_ID_EXPR)
44
+ update_attributes_from_id(blip_id)
45
+ elsif blip_id.class == Hash
46
+ update_attributes_from_hash(blip_id)
47
+ end
48
+ end
49
+
50
+ def update_attributes_from_id(blip_id)
51
+ @id = blip_id
52
+
53
+ a = get_attributes
54
+ update_attributes_from_hash(a)
55
+ end
56
+
57
+ def update_attributes_from_hash(a)
58
+
59
+ @id = a['item_id'] if @id == nil
60
+ @title = a['title']
61
+ @description = a['description']
62
+ @guid = a['guid']
63
+ @deleted = a['deleted']
64
+ @view_count = a['views']
65
+ @tags = parse_tags a['tags']
66
+ @links = a['links']['link']
67
+ @thumbnail_url = a['thumbnail_url']
68
+ @author = a['created_by']['login'] if a['created_by']
69
+ @update_time = a['timestamp'] ? Time.at(a['update_time'].to_i) : nil
70
+ @explicit = a['explicit']
71
+ @license = a['license']
72
+ @notes = a['notes']
73
+ @embed_url = a['embed_url']
74
+ @embed_code = a['embed_code']
75
+ end
76
+
77
+ #
78
+ # fire off a HTTP GET response to Blip.tv
79
+ #
80
+ # In the future, this should probably be rolled into the
81
+ # BlipTV::Request class, so that all exception raising and
82
+ # network communication exists in instances of that class.
83
+ #
84
+ def get_attributes
85
+ url = URI.parse('http://www.blip.tv/')
86
+ res = Net::HTTP.start(url.host, url.port) {|http|
87
+ http.get("http://www.blip.tv/file/#{@id.to_s}?skin=api")
88
+ }
89
+
90
+ hash = Hash.from_xml(res.body)
91
+
92
+ if hash["response"]["status"] != "OK"
93
+ raise VideoResponseError.new(hash["response"]["notice"])
94
+ end
95
+
96
+ if hash["response"]["payload"]["asset"].is_a?(Array)
97
+ return hash["response"]["payload"]["asset"][0] # there may be several assets. In that case, read the first one
98
+ else
99
+ return hash["response"]["payload"]["asset"]
100
+ end
101
+ end
102
+
103
+ #
104
+ # Refresh the current video object. Useful to check up on encoding progress,
105
+ # etc.
106
+ #
107
+ def refresh
108
+ update_attributes_from_id(@id)
109
+ end
110
+
111
+ #
112
+ # delete! will delete the file from Blip.tv
113
+ #
114
+ def delete!(creds = {}, section = "file", reason = "because")
115
+ BlipTV::ApiSpec.check_attributes('videos.delete', creds)
116
+
117
+ reason = reason.gsub(" ", "%20") # TODO write a method to handle this and other illegalities of URL
118
+
119
+ if creds[:username] && !creds[:userlogin]
120
+ creds[:userlogin] = creds[:username]
121
+ end
122
+
123
+ url, path = "www.blip.tv", "/?userlogin=#{creds[:userlogin]}&password=#{creds[:password]}&cmd=delete&s=file&id=#{@id}&reason=#{reason}&skin=api"
124
+ request = Net::HTTP.get(url, path)
125
+ hash = Hash.from_xml(request)
126
+ make_sure_video_was_deleted(hash)
127
+ end
128
+
129
+ private
130
+
131
+ #
132
+ # Makes sense out of Blip.tv's strucutre of the <tt>tag</tt> element
133
+ #
134
+ # returns a String
135
+ #
136
+ def parse_tags(element)
137
+ if element.class == Hash && element['string']
138
+ if element['string'].class == Array
139
+ return element['string'].join(", ")
140
+ elsif element['string'].class == String
141
+ return element['string']
142
+ end
143
+ else
144
+ return ""
145
+ end
146
+ end
147
+
148
+ #
149
+ # make_sure_video_was_deleted analyzes the response <tt>hash</tt>
150
+ # to make sure it was a success
151
+ #
152
+ # raises a descriptive BlipTV::VideoDeleteError
153
+ #
154
+ def make_sure_video_was_deleted(hash)
155
+ # TODO have a special case for authentication required?
156
+ if hash["response"]["status"] != "OK"
157
+ begin
158
+ raise VideoDeleteError.new("#{hash['response']['error']['code']}: #{hash['response']['error']['message']} ")
159
+ rescue NoMethodError # TODO irony!
160
+ raise VideoDeleteError.new(hash.to_yaml)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,18 @@
1
+ class Array #:nodoc:
2
+
3
+ # Extraceted from Ruby Facets:
4
+ # Converts a two-element associative array into a hash.
5
+ def to_h(arrayed=nil)
6
+ h = {}
7
+ if arrayed
8
+ each{ |k,*v| h[k] = v }
9
+ else
10
+ ary = []
11
+ each do |a|
12
+ Array===a ? ary.concat(a) : ary << a
13
+ end
14
+ h = Hash[*ary]
15
+ end
16
+ h
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ class Hash #:nodoc:
2
+
3
+ def assert_required_keys(keys)
4
+ for key in keys
5
+ raise(ArgumentError, "Missing required key(s): #{key}") unless self.keys.include?(key)
6
+ end
7
+ true
8
+ end
9
+
10
+ # Extracted from Ruby Facets:
11
+ # Operator for remove hash paris. If another hash is given the pairs are only removed if both key and value are equal. If an array is given then matching keys are removed.
12
+ def -(other)
13
+ h = self.dup
14
+ if other.respond_to?(:to_ary)
15
+ other.to_ary.each do |k|
16
+ h.delete(k)
17
+ end
18
+ else
19
+ other.each do |k,v|
20
+ if h.key?(k)
21
+ h.delete(k) if v == h[k]
22
+ end
23
+ end
24
+ end
25
+ h
26
+ end
27
+
28
+ end
@@ -0,0 +1,7 @@
1
+ class OpenStruct #:nodoc:
2
+
3
+ def table
4
+ @table
5
+ end
6
+
7
+ end
@@ -0,0 +1,58 @@
1
+ require 'lib/bliptv'
2
+
3
+ class TC_BaseTest < Test::Unit::TestCase
4
+ def setup
5
+ @base = BlipTV::Base.new
6
+ end
7
+
8
+ def teardown
9
+ end
10
+
11
+ def test_find_all_videos_by_user
12
+
13
+ videos = @base.find_all_videos_by_user("onemonthhere")
14
+
15
+ assert_not_equal nil, videos
16
+ assert_instance_of Array, videos
17
+ assert videos.size >= 12 # at the time of the writing of the test
18
+
19
+ videos.each do |video|
20
+ assert_not_equal "", video.title # all videos must have a title
21
+ assert_equal "onemonthhere", video.author
22
+ assert_equal "false", video.explicit # I know for a fact that all videos are not explicit
23
+ end
24
+ end
25
+
26
+ def test_find_all_videos_by_user_retrieve_100
27
+
28
+ videos = @base.find_all_videos_by_user("verycocinar", {:page => 1, :pagelen => 100})
29
+
30
+ assert_not_equal nil, videos
31
+ assert_instance_of Array, videos
32
+ assert videos.size == 100
33
+ end
34
+
35
+ def test_find_all_videos_by_user_more_than_one_page
36
+
37
+ videos_1 = @base.find_all_videos_by_user("verycocinar", {:page => 2, :pagelen => 30})
38
+ videos_2 = @base.find_all_videos_by_user("verycocinar", {:page => 3, :pagelen => 30})
39
+
40
+ assert_instance_of Array, videos_1
41
+ assert_instance_of Array, videos_2
42
+ assert videos_1.size == 30
43
+ assert videos_2.size == 30
44
+ assert_not_equal videos_1, videos_2
45
+ end
46
+
47
+ def test_search_videos
48
+ videos = @base.search_videos("cool")
49
+
50
+ assert_not_equal nil, videos # unless Blip.tv is having a bad day :(
51
+ assert_instance_of Array, videos
52
+ assert videos.size > 10 # assumption
53
+
54
+ videos.each do |video|
55
+ assert_not_equal "", video.title # all videos must have a title
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'test/unit/testsuite'
3
+ require 'test/unit/ui/console/testrunner'
4
+
5
+ require 'test/test_video'
6
+ require 'test/test_base'
7
+
8
+ class TS_BlipTVTests
9
+ def self.suite
10
+ suite = Test::Unit::TestSuite.new "Blip.tv gem tests"
11
+ suite << TC_VideoTest.suite
12
+ suite << TC_BaseTest.suite
13
+ return suite
14
+ end
15
+ end
16
+ Test::Unit::UI::Console::TestRunner.run(TS_BlipTVTests)
@@ -0,0 +1,133 @@
1
+ require 'lib/bliptv'
2
+
3
+ NO_TAGS = "\n\t\t\n\t"
4
+
5
+ class TC_VideoTest < Test::Unit::TestCase
6
+ def setup
7
+ end
8
+
9
+ def teardown
10
+ end
11
+
12
+ def test_video_initialize_with_int_and_string
13
+ video1 = BlipTV::Video.new(2193230)
14
+ video2 = BlipTV::Video.new("2193230")
15
+
16
+ a = [video1, video2]
17
+
18
+ a.each do |video|
19
+ assert_not_equal nil, video
20
+ assert_instance_of BlipTV::Video, video
21
+
22
+
23
+ assert_equal "Super Mario Galaxy 2", video.title
24
+
25
+ h = { "mode" => "escaped", "type" => "text/html" }
26
+ assert_equal h, video.description
27
+
28
+ assert_equal "D2215402-5017-11DE-9B2F-C1BCBB520399", video.guid
29
+ assert_equal "false", video.deleted
30
+ assert_equal nil, video.view_count # because we don't have the user login info
31
+ assert_equal "", video.tags
32
+
33
+ links = [{"href"=>"http://blip.tv/file/2193230",
34
+ "rel"=>"alternate",
35
+ "type"=>"text/html"},
36
+ {"href"=>"http://blip.tv/file/2193230/?skin=api",
37
+ "rel"=>"alternate",
38
+ "type"=>"text/xml"},
39
+ {"href"=>"http://blip.tv/file/post/2193230/",
40
+ "rel"=>"service.edit",
41
+ "type"=>"text/html"},
42
+ {"href"=>"http://blip.tv/rss/2204077",
43
+ "rel"=>"alternate",
44
+ "type"=>"application/rss+xml"},
45
+ {"href"=>"http://blip.tv/file/2193230/?skin=atom",
46
+ "rel"=>"alternate",
47
+ "type"=>"application/atom+xml"},
48
+ {"href"=>"http://blip.tv/file/post/2193230/?skin=api",
49
+ "rel"=>"service.edit",
50
+ "type"=>"text/xml"}]
51
+
52
+ assert_equal links, video.links
53
+ assert_equal "kotaku", video.author
54
+ assert_equal "Thu Jan 01 01:00:00 +0100 1970", video.update_time.to_s # not sure why this is the epoch
55
+ assert_equal "false", video.explicit
56
+
57
+ license = {"name"=>"No license (All rights reserved)"}
58
+ assert_equal license, video.license
59
+
60
+ notes = {"mode"=>"escaped", "type"=>"text/html"}
61
+ assert_equal notes, video.notes
62
+
63
+ assert_equal "http://blip.tv/play/g4Q9gYbEEY35ZA", video.embed_url
64
+ assert_equal "<embed src=\"http://blip.tv/play/g4Q9gYbEEY35ZA\" type=\"application/x-shockwave-flash\" width=\"854\" height=\"510\" allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>", video.embed_code
65
+ end
66
+ end
67
+
68
+ def test_video_with_more_data
69
+ video = BlipTV::Video.new(2141730)
70
+
71
+ assert_match /\d{3,12}/, video.id
72
+ assert_equal "Field Recon - One Month Here - Episode 9", video.title
73
+ assert_equal "I spend most of today out in the field, scoping out challenge locations. Berghain looks eerie and deserted. It's likely the exact opposite at night. I need some swimming trunks...<br /><br /> Tweets read today from @<a href=\"http://twitter.com/iMuesli\">iMuesli</a> , @<a href=\"http://twitter.com/andrewseely\">andrewseely</a> , @<a href=\"http://twitter.com/JDFirst\">JDFirst</a> , @<a href=\"http://twitter.com/grasp183\">grasp183</a> and @<a href=\"http://twitter.com/mxchickmagnet86\">mxchickmagnet86</a><br /><br /> Thanks for watching!", video.description
74
+ assert_equal "DB14423E-460F-11DE-B85A-FF0EAE6B9387", video.guid
75
+ assert_equal "false", video.deleted
76
+ assert_equal nil, video.view_count
77
+ assert_equal "bergain, badeschiff, kelly sutton, berlin, germany, kreuzberg", video.tags
78
+
79
+ links = [{"href"=>"http://blip.tv/file/2141730",
80
+ "rel"=>"alternate",
81
+ "type"=>"text/html"},
82
+ {"href"=>"http://blip.tv/file/2141730/?skin=api",
83
+ "rel"=>"alternate",
84
+ "type"=>"text/xml"},
85
+ {"href"=>"http://blip.tv/file/post/2141730/",
86
+ "rel"=>"service.edit",
87
+ "type"=>"text/html"},
88
+ {"href"=>"http://blip.tv/rss/2152384",
89
+ "rel"=>"alternate",
90
+ "type"=>"application/rss+xml"},
91
+ {"href"=>"http://blip.tv/file/2141730/?skin=atom",
92
+ "rel"=>"alternate",
93
+ "type"=>"application/atom+xml"},
94
+ {"href"=>"http://blip.tv/file/post/2141730/?skin=api",
95
+ "rel"=>"service.edit",
96
+ "type"=>"text/xml"}]
97
+ assert_equal links, video.links
98
+ assert_equal "onemonthhere", video.author
99
+ assert_equal 0, video.update_time.to_i
100
+ assert_equal "false", video.explicit
101
+
102
+ licensce = {"name"=>"Creative Commons Attribution 3.0",
103
+ "link"=>
104
+ {"href"=>"http://creativecommons.org/licenses/by/3.0/", "type"=>"text/html"}}
105
+ assert_equal licensce, video.license
106
+
107
+ notes = {"mode"=>"escaped", "type"=>"text/html"}
108
+ assert_equal notes, video.notes
109
+ assert_equal "http://blip.tv/play/AYGDsCSV5jE", video.embed_url
110
+ assert_equal "<embed src=\"http://blip.tv/play/AYGDsCSV5jE\" type=\"application/x-shockwave-flash\" width=\"640\" height=\"510\" allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>", video.embed_code
111
+ end
112
+
113
+ def test_video_delete
114
+ base = BlipTV::Base.new
115
+
116
+ options = {
117
+ :userlogin => "bliptv_ruby_gem",
118
+ :password => "thisissosecret",
119
+ :file => File.open('/Users/msutton/Desktop/output.mp4'), # change this to a file of your own
120
+ :title => "test title",
121
+ :description => "test description"
122
+ }
123
+
124
+ video = base.upload_video(options) #=> BlipTV::Video
125
+
126
+ options = {
127
+ :userlogin => "bliptv_ruby_gem",
128
+ :password => "thisissosecret"
129
+ }
130
+
131
+ video.delete!(options)
132
+ end
133
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bliptv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Kelly Sutton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-05 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A Ruby library from Blip.tv
17
+ email: michael.k.sutton@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - lib/bliptv/api_spec.rb
24
+ - lib/bliptv/base.rb
25
+ - lib/bliptv/multipart_params.rb
26
+ - lib/bliptv/request.rb
27
+ - lib/bliptv/video.rb
28
+ - lib/bliptv.rb
29
+ - lib/ext/array.rb
30
+ - lib/ext/hash.rb
31
+ - lib/ext/open_struct.rb
32
+ - README.rdoc
33
+ files:
34
+ - lib/bliptv/api_spec.rb
35
+ - lib/bliptv/base.rb
36
+ - lib/bliptv/multipart_params.rb
37
+ - lib/bliptv/request.rb
38
+ - lib/bliptv/video.rb
39
+ - lib/bliptv.rb
40
+ - lib/ext/array.rb
41
+ - lib/ext/hash.rb
42
+ - lib/ext/open_struct.rb
43
+ - License.txt
44
+ - Manifest
45
+ - Rakefile
46
+ - README.rdoc
47
+ - test/test_base.rb
48
+ - test/test_suite.rb
49
+ - test/test_video.rb
50
+ - bliptv.gemspec
51
+ has_rdoc: true
52
+ homepage: http://github.com/kellysutton/bliptv
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --line-numbers
58
+ - --inline-source
59
+ - --title
60
+ - Bliptv
61
+ - --main
62
+ - README.rdoc
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "1.2"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project: bliptv
80
+ rubygems_version: 1.3.5
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: A Ruby library from Blip.tv
84
+ test_files:
85
+ - test/test_base.rb
86
+ - test/test_suite.rb
87
+ - test/test_video.rb