dmcloud 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +22 -0
- data/README.md +157 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/dmcloud.gemspec +78 -0
- data/lib/dm_cloud/builder/media.rb +117 -0
- data/lib/dm_cloud/media.rb +85 -0
- data/lib/dm_cloud/request.rb +43 -0
- data/lib/dm_cloud/signing.rb +189 -0
- data/lib/dm_cloud/streaming.rb +80 -0
- data/lib/dm_cloud/version.rb +3 -0
- data/lib/dm_cloud.rb +58 -0
- data/spec/dm_cloud/media_spec.rb +48 -0
- data/spec/dm_cloud/signing_spec.rb +35 -0
- data/spec/dm_cloud/streaming_spec.rb +23 -0
- data/spec/dm_cloud_spec.rb +28 -0
- data/spec/spec_helper.rb +57 -0
- data/spec/vcr_cassettes/dm_cloud/dm_cloud_streaming.yml +0 -0
- data/spec/vcr_cassettes/dm_cloud_media.yml +163 -0
- metadata +173 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
module DmCloud
|
4
|
+
class Signing
|
5
|
+
# Generate auth token for request from Media
|
6
|
+
# Params:
|
7
|
+
# request: A hash of params generated from Media methods and Media::MetaData
|
8
|
+
# Result :
|
9
|
+
# return a string which contain the auth token for the request
|
10
|
+
# <url>?auth=<expires>-<sec>-<nonce>-<md5sum>[-<pub-sec-data>]
|
11
|
+
def self.identify(request)
|
12
|
+
user_id = DmCloud.config[:user_key]
|
13
|
+
api_key = DmCloud.config[:secret_key]
|
14
|
+
|
15
|
+
normalized_request = normalize(request).to_s
|
16
|
+
# puts 'identify:: normalized_values : ' + normalized_request + "\n" + '-' * 80
|
17
|
+
|
18
|
+
params = user_id + normalized_request + api_key
|
19
|
+
|
20
|
+
# puts 'identify:: Values before MD5 encrypt : ' + params + "\n" + '-' * 80
|
21
|
+
|
22
|
+
checksum = Digest::MD5.hexdigest(params)
|
23
|
+
auth_token = user_id + ':' + checksum
|
24
|
+
|
25
|
+
auth_token
|
26
|
+
end
|
27
|
+
|
28
|
+
# To sign a URL, the client needs a secret shared with Dailymotion Cloud.
|
29
|
+
# This secret is call client secret and is available in the back-office interface.
|
30
|
+
# Params:
|
31
|
+
# expires: An expiration timestamp.
|
32
|
+
# sec-level: A security level mask.
|
33
|
+
# url-no-query: The URL without the query-string.
|
34
|
+
# nonce: A 8 characters-long random alphanumeric lowercase string to make the signature unique.
|
35
|
+
# secret: The client secret.
|
36
|
+
# sec-data: If sec-level doesn’t have the DELEGATED bit activated,
|
37
|
+
# this component contains concatenated informations
|
38
|
+
# for all activated sec levels.
|
39
|
+
# pub-sec-data: Some sec level data have to be passed in clear in the signature.
|
40
|
+
# To generate this component the parameters are serialized using x-www-form-urlencoded, compressed with gzip and encoded in base64.
|
41
|
+
# Result :
|
42
|
+
# return a string which contain the signed url like
|
43
|
+
# <expires>-<sec>-<nonce>-<md5sum>[-<pub-sec-data>]
|
44
|
+
def self.sign(stream, security_datas = nil)
|
45
|
+
raise StandardError, "missing :stream in params" unless stream
|
46
|
+
sec_level = security(DmCloud.config[:security_level])
|
47
|
+
sec_data = security_data(DmCloud.config[:security_level], security_datas) unless security_datas.nil?
|
48
|
+
|
49
|
+
base = {
|
50
|
+
:sec_level => sec_level,
|
51
|
+
:url_no_query => stream,
|
52
|
+
:expires => 1.hours.from_now.to_i,
|
53
|
+
:nonce => SecureRandom.hex(16)[0,16],
|
54
|
+
:secret => DmCloud.config[:secret_key]
|
55
|
+
}
|
56
|
+
base.merge!(:sec_data => sec_data, :pub_sec_data => sec_data) unless sec_data.nil?
|
57
|
+
|
58
|
+
digest_struct = build_digest_struct(base)
|
59
|
+
check_sum = Digest::MD5.hexdigest(digest_struct)
|
60
|
+
|
61
|
+
signed_url = [base[:expires], base[:sec_level], base[:nonce], check_sum].compact
|
62
|
+
signed_url.merge!(:pub_sec_data => sec_data) unless sec_data.nil?
|
63
|
+
|
64
|
+
# puts signed_url
|
65
|
+
|
66
|
+
signed_url = signed_url.join('-')
|
67
|
+
signed_url
|
68
|
+
end
|
69
|
+
|
70
|
+
# Prepare datas for signing
|
71
|
+
# Params :
|
72
|
+
# base : contains media id and others for url signing
|
73
|
+
def self.build_digest_struct(base)
|
74
|
+
result = []
|
75
|
+
base.each_pair { |key, value| result << value }
|
76
|
+
result.join('')
|
77
|
+
end
|
78
|
+
|
79
|
+
# The client must choose a security level for the signature.
|
80
|
+
# Security level defines the mechanism used by Dailymotion Cloud architecture
|
81
|
+
# to ensure the signed URL will be used by a single end-user.
|
82
|
+
# Params :
|
83
|
+
# type :
|
84
|
+
# None: The signed URL will be valid for everyone
|
85
|
+
# ASNUM: The signed URL will only be valid for the AS of the end-user.
|
86
|
+
# The ASNUM (for Autonomous System Number) stands for the network identification,
|
87
|
+
# each ISP have a different ASNUM for instance.
|
88
|
+
# IP: The signed URL will only be valid for the IP of the end-user.
|
89
|
+
# This security level may wrongly block some users
|
90
|
+
# which have their internet access load-balanced between several proxies.
|
91
|
+
# This is the case in some office network or some ISPs.
|
92
|
+
# User-Agent: Used in addition to one of the two former levels,
|
93
|
+
# this level a limit on the exact user-agent of the end-user.
|
94
|
+
# This is more secure but in some specific condition may lead to wrongly blocked users.
|
95
|
+
# Use Once: The signed URL will only be usable once.
|
96
|
+
# Note: should not be used with stream URLs.
|
97
|
+
# Country: The URL can only be queried from specified countrie(s).
|
98
|
+
# The rule can be reversed to allow all countries except some.
|
99
|
+
# Referer: The URL can only be queried
|
100
|
+
# if the Referer HTTP header contains a specified value.
|
101
|
+
# If the URL contains a Referer header with a different value,
|
102
|
+
# the request is refused. If the Referer header is missing,
|
103
|
+
# the request is accepted in order to prevent from false positives as some browsers,
|
104
|
+
# anti-virus or enterprise proxies may remove this header.
|
105
|
+
# Delegate: This option instructs the signing algorithm
|
106
|
+
# that security level information won’t be embeded into the signature
|
107
|
+
# but gathered and lock at the first use.
|
108
|
+
# Result :
|
109
|
+
# Return a string which contain the signed url like
|
110
|
+
# http://cdn.DmCloud.net/route/<user_id>/<media_id>/<asset_name>.<asset_extension>?auth=<auth_token>
|
111
|
+
def self.security(type = nil)
|
112
|
+
type = :none unless type
|
113
|
+
type = type.to_sym if type.class == String
|
114
|
+
|
115
|
+
case type
|
116
|
+
when :none
|
117
|
+
0 # None
|
118
|
+
when :delegate
|
119
|
+
1 << 0 # None
|
120
|
+
when :asnum
|
121
|
+
1 << 1 # The number part of the end-user AS prefixed by the ‘AS’ string (ie: as=AS41690)
|
122
|
+
when :ip
|
123
|
+
1 << 2 # The end-user quad dotted IP address (ie: ip=195.8.215.138)
|
124
|
+
when :user_agent
|
125
|
+
1 << 3 # The end-user browser user-agent (parameter name is ua)
|
126
|
+
when :use_once
|
127
|
+
1 << 4 # None
|
128
|
+
when :country
|
129
|
+
1 << 5 # A list of 2 characters long country codes in lowercase by comas. If the list starts with a dash, the rule is inverted (ie: cc=fr,gb,de or cc=-fr,it). This data have to be stored in pub-sec-data component
|
130
|
+
when :referer
|
131
|
+
1 << 6 # A list of URL prefixes separated by spaces stored in the pub-sec-data component (ex: rf=http;//domain.com/a/+http:/domain.com/b/).
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.security_data(type, value = nil)
|
136
|
+
type = type.to_sym if type.class == String
|
137
|
+
|
138
|
+
case type
|
139
|
+
when :asnum
|
140
|
+
"as=#{value}" # The number part of the end-user AS prefixed by the ‘AS’ string (ie: as=AS41690)
|
141
|
+
when :ip
|
142
|
+
"ip=#{value}" # The end-user quad dotted IP address (ie: ip=195.8.215.138)
|
143
|
+
when :user_agent
|
144
|
+
"ua=#{value}" # The end-user browser user-agent (parameter name is ua)
|
145
|
+
when :country
|
146
|
+
"cc=#{value}" # A list of 2 characters long country codes in lowercase by comas. If the list starts with a dash, the rule is inverted (ie: cc=fr,gb,de or cc=-fr,it). This data have to be stored in pub-sec-data component
|
147
|
+
when :referer
|
148
|
+
"rf=#{value}" # A list of URL prefixes separated by spaces stored in the pub-sec-data component (ex: rf=http;//domain.com/a/+http:/domain.com/b/).
|
149
|
+
else
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.security_pub_sec_data(type, value)
|
155
|
+
type = type.to_sym if type.class == String
|
156
|
+
|
157
|
+
case type
|
158
|
+
when :country
|
159
|
+
"cc=#{value}" # A list of 2 characters long country codes in lowercase by comas. If the list starts with a dash, the rule is inverted (ie: cc=fr,gb,de or cc=-fr,it). This data have to be stored in pub-sec-data component
|
160
|
+
when :referer
|
161
|
+
"rf=#{value}" # A list of URL prefixes separated by spaces stored in the pub-sec-data component (ex: rf=http;//domain.com/a/+http:/domain.com/b/).
|
162
|
+
else
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
# This block comes from Cloudkey gem.
|
169
|
+
# I discovered this gem far after I start this one
|
170
|
+
# and I will try to add file upload from http or ftp.
|
171
|
+
# (Missing in their gem)
|
172
|
+
def self.normalize params
|
173
|
+
case params
|
174
|
+
when Array
|
175
|
+
params.collect { |element| normalize(element) }.join('')
|
176
|
+
when Hash
|
177
|
+
params.to_a.sort_by {|a,b| a.to_s }.collect {|array| array.first.to_s + normalize(array.last)}.join('')
|
178
|
+
else
|
179
|
+
params.to_s
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# def self.normalize(params)
|
184
|
+
# str = params.to_json.to_s
|
185
|
+
# str.gsub!(/[^A-Za-z0-9]/, '')
|
186
|
+
# str
|
187
|
+
# end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "time"
|
2
|
+
require "openssl"
|
3
|
+
require "base64"
|
4
|
+
require 'digest/md5'
|
5
|
+
|
6
|
+
# This module generate methods to generate video's fluxes
|
7
|
+
# before signing it and request it.
|
8
|
+
module DmCloud
|
9
|
+
class Streaming
|
10
|
+
# Default URL to get embed content ou direct url
|
11
|
+
DIRECT_STREAM = "[PROTOCOL]://cdn.dmcloud.net/route/[USER_ID]/[MEDIA_ID]/[ASSET_NAME].[ASSET_EXTENSION]".freeze
|
12
|
+
EMBED_STREAM = "[PROTOCOL]://api.dmcloud.net/embed/[USER_ID]/[MEDIA_ID]".freeze
|
13
|
+
EMBED_IFRAME = '<iframe width="[WIDTH]" height="[HEIGHT]" frameborder="0" scrolling="no" src="[EMBED_URL]"></iframe>'.freeze
|
14
|
+
|
15
|
+
# Get embeded player
|
16
|
+
# Params :
|
17
|
+
# media_id: this is the id of the media (eg: 4c922386dede830447000009)
|
18
|
+
# options:
|
19
|
+
# skin_id: (optional) the id of the custom skin for the video player
|
20
|
+
# width: (optional) the width for the video player frame
|
21
|
+
# height: (optional) the height for the video player frame
|
22
|
+
# Result :
|
23
|
+
# return a string which contain the signed url like
|
24
|
+
# <iframe width="848" height="480" frameborder="0" scrolling="no" src="http://api.DmCloud.net/embed/<user_id>/<media_id>?auth=<auth_token>&skin=<skin_id>"></iframe>
|
25
|
+
def self.embed(media_id, options = {}, security = {})
|
26
|
+
raise StandardError, "missing :media_id in params" unless media_id
|
27
|
+
|
28
|
+
skin_id = options[:skin_id] ? options[:skin_id] : 'modern1'
|
29
|
+
width = options[:width] ? options[:width].to_s : '848'
|
30
|
+
height = options[:height] ? options[:height].to_s : '480'
|
31
|
+
|
32
|
+
stream = EMBED_STREAM.dup
|
33
|
+
stream.gsub!('[PROTOCOL]', DmCloud.config[:protocol])
|
34
|
+
stream.gsub!('[USER_ID]', DmCloud.config[:user_key])
|
35
|
+
stream.gsub!('[MEDIA_ID]', media_id)
|
36
|
+
signed_url = DmCloud::Signing.sign(stream, security)
|
37
|
+
signed_url = stream + "?auth=#{signed_url}"
|
38
|
+
|
39
|
+
frame = EMBED_IFRAME.dup
|
40
|
+
frame.gsub!('[WIDTH]', width)
|
41
|
+
frame.gsub!('[HEIGHT]', height)
|
42
|
+
frame.gsub!('[EMBED_URL]', signed_url)
|
43
|
+
|
44
|
+
frame.html_safe
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get media url for direct link to the file on DailyMotion Cloud
|
48
|
+
# Params :
|
49
|
+
# media_id: this is the id of the media (eg: 4c922386dede830447000009)
|
50
|
+
# asset_name: the name of the asset you want to stream (eg: mp4_h264_aac)
|
51
|
+
# asset_extension: the extension of the asset, most of the time it is the first part of the asset name (eg: mp4)
|
52
|
+
# Result :
|
53
|
+
# return a string which contain the signed url like
|
54
|
+
# http://cdn.DmCloud.net/route/<user_id>/<media_id>/<asset_name>.<asset_extension>?auth=<auth_token>
|
55
|
+
def self.url(media_id, asset_name, asset_extension = nil, security = {})
|
56
|
+
raise StandardError, "missing :media_id in params" unless media_id
|
57
|
+
raise StandardError, "missing :asset_name in params" unless asset_name
|
58
|
+
asset_extension = asset_name.split('_').first unless asset_extension
|
59
|
+
|
60
|
+
stream = DIRECT_STREAM.dup
|
61
|
+
stream.gsub!('[PROTOCOL]', DmCloud.config[:protocol])
|
62
|
+
stream.gsub!('[USER_ID]', DmCloud.config[:user_key])
|
63
|
+
stream.gsub!('[MEDIA_ID]', media_id)
|
64
|
+
stream.gsub!('[ASSET_NAME]', asset_name)
|
65
|
+
stream.gsub!('[ASSET_EXTENSION]', asset_extension)
|
66
|
+
|
67
|
+
stream += '?auth=[AUTH_TOKEN]'.gsub!('[AUTH_TOKEN]', DmCloud::Signing.sign(stream, security))
|
68
|
+
stream
|
69
|
+
end
|
70
|
+
|
71
|
+
# Gets the real URL that points to the download link on DMCloud's specific server
|
72
|
+
def self.download_url(media_id, asset_name, asset_extension = nil, security = {})
|
73
|
+
download_url = self.url(media_id, asset_name, asset_extension, security)
|
74
|
+
response = Net::HTTP.get_response(URI.parse(download_url))
|
75
|
+
download_url = response.header["location"]
|
76
|
+
|
77
|
+
download_url
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/dm_cloud.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# This gem's comments come from DailyMotion Cloud API,
|
4
|
+
# that's the better way to see changes on new version and logic.
|
5
|
+
# For parts more generals and not representating DailyMotion Cloud API,
|
6
|
+
# I add some about my own opinion.
|
7
|
+
module DmCloud
|
8
|
+
|
9
|
+
# Configuration defaults
|
10
|
+
# I used this parts from Slainer68 paybox_system gem.
|
11
|
+
# I liked the concept and how he handle this part.
|
12
|
+
# Thx Slainer68, I created my first gem,
|
13
|
+
# and next one will be an update to your paybox_system gem.
|
14
|
+
@@config = {
|
15
|
+
security_level: 'none',
|
16
|
+
protocol: 'http',
|
17
|
+
auto_call: true,
|
18
|
+
user_key: nil,
|
19
|
+
secret_key: nil
|
20
|
+
}
|
21
|
+
|
22
|
+
YAML_INITIALIZER_PATH = File.dirname(__FILE__)
|
23
|
+
@valid_config_keys = @@config.keys
|
24
|
+
|
25
|
+
# Configure through hash
|
26
|
+
def self.configure(opts = {})
|
27
|
+
opts.each {|k,v| @@config[k.to_sym] = v } # if @valid_config_keys.include? k.to_sym}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Configure through yaml file
|
31
|
+
# for ruby scripting usage
|
32
|
+
def self.configure_with(yaml_file_path = nil)
|
33
|
+
yaml_file_path = YAML_INITIALIZER_PATH unless yaml_file_path
|
34
|
+
begin
|
35
|
+
config = YAML::load(IO.read(path_to_yaml_file))
|
36
|
+
rescue Errno::ENOENT
|
37
|
+
log(:warning, "YAML configuration file couldn't be found. Using defaults."); return
|
38
|
+
rescue Psych::SyntaxError
|
39
|
+
log(:warning, "YAML configuration file contains invalid syntax. Using defaults."); return
|
40
|
+
end
|
41
|
+
|
42
|
+
configure(config)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Access to config variables (security level, user_id and api_key)
|
46
|
+
def self.config
|
47
|
+
@@config = configure unless @@config
|
48
|
+
@@config
|
49
|
+
end
|
50
|
+
|
51
|
+
# Loading classes to easier access
|
52
|
+
# NOTE: I like this way to handle my classes,
|
53
|
+
# sexiest than using require 'my_class_file' everywhere
|
54
|
+
autoload(:Streaming, 'dm_cloud/streaming')
|
55
|
+
autoload(:Media, 'dm_cloud/media')
|
56
|
+
autoload(:Request, 'dm_cloud/request')
|
57
|
+
autoload(:Signing, 'dm_cloud/signing')
|
58
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'dm_cloud/media'
|
4
|
+
|
5
|
+
describe DmCloud::Media do
|
6
|
+
use_vcr_cassette
|
7
|
+
|
8
|
+
context "Using test account" do
|
9
|
+
before :each do
|
10
|
+
DmCloud.configure({:user_key => TEST_USER_KEY, :secret_key => TEST_SECRET_KEY, auto_call: false})
|
11
|
+
end
|
12
|
+
|
13
|
+
context "Having a collection" do
|
14
|
+
it "should list four medias" do
|
15
|
+
subject { list(:per_page => 20)['result']['total'].should == (4) }
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should get all the titles" do
|
19
|
+
result = DmCloud::Media.list(:fields => {:meta => [:title]}, :per_page => 20) #['result']['list']
|
20
|
+
result[:call].should == "media.list"
|
21
|
+
result[:params][:args][:fields].should include("meta.title")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should get page 2 with 2 records per page" do
|
25
|
+
result = DmCloud::Media.list(:per_page => 2, :page => 2)
|
26
|
+
result[:params][:page].should == 2
|
27
|
+
result[:params][:per_page].should == 2
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Querying a single media" do
|
32
|
+
it "should have an default fields" do
|
33
|
+
result = DmCloud::Media.info('4f33ddbc94a6f6517c001577')
|
34
|
+
fields = result[:params][:args][:fields]
|
35
|
+
puts fields.to_yaml
|
36
|
+
fields.should include('embed_url')
|
37
|
+
end
|
38
|
+
|
39
|
+
# it "should have a stream url" do
|
40
|
+
# subject { stream_url('4f33ddbc94a6f6517c001577').should include("http://cdn.DmCloud.net/route/4f33d9c8f325e11c830016af/4f33ddbc94a6f6517c001577/mp4_h264_aac.mp4")
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# it "should have http detected as protocol" do
|
44
|
+
# @cloudkey.media.stream_url('4f33ddbc94a6f6517c001577', 'mp4_h264_aac',Cloudkey::SecurityPolicy.new, :download => true).should include("/http/")
|
45
|
+
# end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'dm_cloud/signing'
|
3
|
+
|
4
|
+
describe DmCloud::Signing do
|
5
|
+
|
6
|
+
before do
|
7
|
+
DmCloud.configure({ :user_key => "hello world", :secret_key => "sEcReT_KeY" })
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should sign 'hello world' with sEcReT_KeY and returns 'b5d93121a6dc87562b46beb8ba809ace'" do
|
11
|
+
auth_token = subject { identify() }
|
12
|
+
auth_token.should == 'b5d93121a6dc87562b46beb8ba809ace'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "it should sign an url" do
|
16
|
+
let(:signed_url) { stub(:sign).with("http://google.fr","olol") }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "Normalizing" do
|
20
|
+
{
|
21
|
+
'foo42bar' => ['foo', 42, 'bar'],
|
22
|
+
'pink3red2yellow1' => {'yellow' => 1, 'red' => 2, 'pink' => 3},
|
23
|
+
'foo42pink3red2yellow1bar' => ['foo', 42, {'yellow' => 1, 'red' => 2, 'pink' => 3}, 'bar'],
|
24
|
+
'foo42pink3red2yellow1bar' => [:foo, 42, {:yellow => 1, :red => 2, :pink => 3}, :bar],
|
25
|
+
'12' => [nil, 1,2],
|
26
|
+
'' => nil,
|
27
|
+
'212345' => {2 => [nil, 1,2], 3 => nil, 4 => 5}
|
28
|
+
}.each do |normalized, original|
|
29
|
+
it "should normalize #{original.inspect} into #{normalized}" do
|
30
|
+
subject { normalize(original).should == normalized }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'dm_cloud/streaming'
|
3
|
+
|
4
|
+
|
5
|
+
describe DmCloud::Streaming do
|
6
|
+
use_vcr_cassette
|
7
|
+
|
8
|
+
context "check " do
|
9
|
+
before :each do
|
10
|
+
DmCloud.configure({user_key: TEST_USER_KEY, secret_key: TEST_SECRET_KEY, auto_execute: false})
|
11
|
+
end
|
12
|
+
|
13
|
+
context "Querying a single media" do
|
14
|
+
it "should have an embedded url" do
|
15
|
+
DmCloud::Streaming.embed('4f33ddbc94a6f6517c001577').should include("http://api.DmCloud.net/embed/4f33d9c8f325e11c830016af/4f33ddbc94a6f6517c001577")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have a stream url" do
|
19
|
+
DmCloud::Streaming.url('4f33ddbc94a6f6517c001577', 'source').should include("http://cdn.DmCloud.net/route/4f33d9c8f325e11c830016af/4f33ddbc94a6f6517c001577/mp4_h264_aac.mp4")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dm_cloud'
|
3
|
+
|
4
|
+
describe DmCloud do
|
5
|
+
context "configuration" do
|
6
|
+
use_vcr_cassette
|
7
|
+
|
8
|
+
it "should provide a config on DmCloud" do
|
9
|
+
DmCloud.should respond_to :config
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be initialized with default values" do
|
13
|
+
DmCloud.configure
|
14
|
+
DmCloud.config[:security_level].should == 'none'
|
15
|
+
DmCloud.config[:protocol].should == 'http'
|
16
|
+
DmCloud.config[:auto_call].should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
context "after configuration setted" do
|
20
|
+
it "should be properly set" do
|
21
|
+
DmCloud.configure({user_key: TEST_USER_KEY, secret_key: TEST_SECRET_KEY, auto_call: false })
|
22
|
+
DmCloud.config[:user_key].should == TEST_USER_KEY
|
23
|
+
DmCloud.config[:secret_key].should == TEST_SECRET_KEY
|
24
|
+
DmCloud.config[:auto_call].should == false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '/../', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rspec'
|
5
|
+
require 'rspec/autorun'
|
6
|
+
|
7
|
+
# Requires supporting files with custom matchers and macros, etc,
|
8
|
+
# in ./support/ and its subdirectories.
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
10
|
+
|
11
|
+
# def self.behavior(obj)
|
12
|
+
# if @methods
|
13
|
+
# @methods = @methods.select{|met| @methods.member? met }
|
14
|
+
# else
|
15
|
+
# @methods = obj.public_methods
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# puts "Common Methods: #{@methods.sort.join(', ')}" if @methods
|
19
|
+
|
20
|
+
module Compare
|
21
|
+
def self.type(obj)
|
22
|
+
@objects ||= []
|
23
|
+
@objects << obj
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.report
|
27
|
+
puts "Object Types: #{@objects.collect{|o| o.class}.join(', ')}" if @objects
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Object
|
32
|
+
def put_methods(regex=/.*/)
|
33
|
+
puts self.methods.grep(regex)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec.configure do |config|
|
38
|
+
config.after(:suite) do
|
39
|
+
Compare.report
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Hook on http requests
|
44
|
+
require 'vcr'
|
45
|
+
|
46
|
+
TEST_USER_KEY = "my_user_key"
|
47
|
+
TEST_SECRET_KEY = "my_secret_key"
|
48
|
+
|
49
|
+
VCR.configure do |c|
|
50
|
+
c.cassette_library_dir = 'spec/cassettes'
|
51
|
+
# c.stub_with :fakeweb
|
52
|
+
c.default_cassette_options = { :record => :new_episodes }
|
53
|
+
end
|
54
|
+
|
55
|
+
RSpec.configure do |c|
|
56
|
+
c.extend VCR::RSpec::Macros
|
57
|
+
end
|
File without changes
|