dm_cloud 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +49 -12
- data/lib/dm_cloud/builder/media.rb +132 -0
- data/lib/dm_cloud/media.rb +37 -23
- data/lib/dm_cloud/request.rb +28 -3
- data/lib/dm_cloud/signing.rb +25 -0
- data/lib/dm_cloud/version.rb +1 -1
- data/lib/dm_cloud.rb +6 -13
- metadata +2 -1
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# DMCloud
|
2
2
|
|
3
3
|
I created this gem to simplify request and responses from DailyMotion Cloud API.
|
4
4
|
With this gem, you can :
|
@@ -23,9 +23,9 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
## Usage
|
25
25
|
|
26
|
-
First, your will need to specify your :user_id, :api_key and your security level.
|
27
|
-
I used a file in `APP_ROOT/config/initializers/conf.rb`.
|
28
|
-
You can note the securitylevel, for more information about it, take a look at
|
26
|
+
First, your will need to specify your :user_id, :api_key and your security level.
|
27
|
+
I used a file in `APP_ROOT/config/initializers/conf.rb`.
|
28
|
+
You can note the securitylevel, for more information about it, take a look at `lib/dm_cloud/signing.rb`.
|
29
29
|
|
30
30
|
# DAILYMOTION CLOUD SETTINGS
|
31
31
|
require 'dm_cloud'
|
@@ -39,23 +39,60 @@ You can note the securitylevel, for more information about it, take a look at ``
|
|
39
39
|
:security_level => DMC_SECURITY_LEVEL
|
40
40
|
})
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
Description of security levels :
|
43
|
+
|
44
|
+
* **None:**
|
45
|
+
The signed URL will be valid for everyone
|
46
|
+
* **ASNUM:**
|
47
|
+
The signed URL will only be valid for the AS of the end-user.
|
48
|
+
The ASNUM (for Autonomous System Number) stands for the network identification,
|
49
|
+
each ISP have a different ASNUM for instance.
|
50
|
+
* **IP:**
|
51
|
+
The signed URL will only be valid for the IP of the end-user.
|
52
|
+
This security level may wrongly block some users
|
53
|
+
which have their internet access load-balanced between several proxies.
|
54
|
+
This is the case in some office network or some ISPs.
|
55
|
+
* **User-Agent:**
|
56
|
+
Used in addition to one of the two former levels,
|
57
|
+
this level a limit on the exact user-agent of the end-user.
|
58
|
+
This is more secure but in some specific condition may lead to wrongly blocked users.
|
59
|
+
* **Use Once:**
|
60
|
+
The signed URL will only be usable once.
|
61
|
+
Note: should not be used with stream URLs.
|
62
|
+
* **Country:**
|
63
|
+
The URL can only be queried from specified countrie(s).
|
64
|
+
The rule can be reversed to allow all countries except some.
|
65
|
+
* **Referer:**
|
66
|
+
The URL can only be queried
|
67
|
+
if the Referer HTTP header contains a specified value.
|
68
|
+
If the URL contains a Referer header with a different value,
|
69
|
+
the request is refused. If the Referer header is missing,
|
70
|
+
the request is accepted in order to prevent from false positives as some browsers,
|
71
|
+
anti-virus or enterprise proxies may remove this header.
|
72
|
+
* **Delegate:**
|
73
|
+
This option instructs the signing algorithm
|
74
|
+
that security level information won’t be embeded into the signature
|
75
|
+
but gathered and lock at the first use.
|
76
|
+
|
77
|
+
|
78
|
+
Second part, get you embed url :
|
79
|
+
It will return a string containing the iframe with the DailyMotion Cloud player.
|
45
80
|
|
46
81
|
DMCloud::Streaming.embed('your video id looks like a secret key')
|
47
82
|
|
48
|
-
Or how to get your direct url :
|
83
|
+
Or how to get your direct url :
|
84
|
+
It will return a string containing the direct link to your file.
|
49
85
|
|
50
86
|
DMCloud::Streaming.url('your video id', ['asset_name'], {options})
|
51
87
|
|
52
|
-
The next parts will come soon, just need some time to finish
|
88
|
+
The next parts will come soon, just need some time to finish it
|
89
|
+
and create corresponding tests.
|
53
90
|
|
54
91
|
## Contributing
|
55
92
|
|
56
|
-
Your welcome to share and enhance this gem.
|
57
|
-
This is my first one (and not the last one) but I know some mistakes might be done by myself.
|
58
|
-
I do my best and I'm open to all ideas or comments about my work.
|
93
|
+
Your welcome to share and enhance this gem.
|
94
|
+
This is my first one (and not the last one) but I know some mistakes might be done by myself.
|
95
|
+
I do my best and I'm open to all ideas or comments about my work.
|
59
96
|
|
60
97
|
1. Fork it
|
61
98
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module DMCloud
|
2
|
+
module Builder
|
3
|
+
module Media
|
4
|
+
def self.create(url = '', assets_names = [], meta = {})
|
5
|
+
request = Hash.new
|
6
|
+
|
7
|
+
request['url'] = url
|
8
|
+
|
9
|
+
if not meta.empty?
|
10
|
+
request['meta'] = {}
|
11
|
+
request['meta']['author'] = meta[:author] if meta[:author]
|
12
|
+
request['meta']['author'] = meta[:title] if meta[:title]
|
13
|
+
end
|
14
|
+
|
15
|
+
request['assets_names'] = assets_names if not assets_names.empty?
|
16
|
+
|
17
|
+
request.rehash
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.info(media_id, assets_names = ['source'], fields = {})
|
21
|
+
raise StandardError, "missing :media_id in params" unless media_id
|
22
|
+
request = Hash.new
|
23
|
+
|
24
|
+
puts 'media_id :' + media_id
|
25
|
+
|
26
|
+
# the media id
|
27
|
+
request['id'] = media_id
|
28
|
+
request['fields'] = []
|
29
|
+
|
30
|
+
|
31
|
+
# requested media meta datas
|
32
|
+
fields[:meta].each { |value| request['fields'] << "meta.#{value}" } if fields[:meta].present?
|
33
|
+
|
34
|
+
#the creation date as a unix timestamp
|
35
|
+
request['fields'] << 'created' if fields[:created]
|
36
|
+
|
37
|
+
# the url of the embed player
|
38
|
+
request['fields'] << 'embed_url' if fields[:embed_url]
|
39
|
+
|
40
|
+
# the ratio of the video frame as a float (eg: 16/9, 4/3, etc).
|
41
|
+
request['fields'] << 'frame_ratio' if fields[:frame_ratio]
|
42
|
+
# the worldwide statistics on the number of views
|
43
|
+
# request['fields'] << 'stats.global.last_week' if fields[:stats][:global]
|
44
|
+
|
45
|
+
# TODO: handle statistics request per country
|
46
|
+
# fields[:stats].each { |key| request << "meta.#{key.to_s}" } if fields[:meta].present?
|
47
|
+
# request['stats'][COUNTRY_CODE][TIME_INTERVAL] : the statistics on the number of views in a specific country (eg: stats.fr.total, stats.us.last_week, etc...)
|
48
|
+
# request['extended_stats'][COUNTRY_CODE][TIME_INTERVAL]
|
49
|
+
|
50
|
+
assets_names = ['source'] if assets_names.nil?
|
51
|
+
if not fields[:assets]
|
52
|
+
request = all_assets_fields(request, assets_names)
|
53
|
+
else
|
54
|
+
assets_names.each do |name|
|
55
|
+
fields[:assets].each { |value| request << "assets.#{name}.#{value.to_s}" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
request
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.list(fields = {})
|
63
|
+
# raise StandardError, "missing :media_id in params" unless media_id
|
64
|
+
request = Hash.new
|
65
|
+
|
66
|
+
request['fields'] = []
|
67
|
+
# requested media meta datas
|
68
|
+
fields[:meta].each { |key| request << "meta.#{key.to_s}" } if fields[:meta].present?
|
69
|
+
|
70
|
+
#the creation date as a unix timestamp
|
71
|
+
request['fields'] << 'created' if fields[:created]
|
72
|
+
|
73
|
+
# the url of the embed player
|
74
|
+
request['fields'] << 'embed_url' if fields[:embed_url]
|
75
|
+
|
76
|
+
# the ratio of the video frame as a float (eg: 16/9, 4/3, etc).
|
77
|
+
request['fields'] << 'frame_ratio' if fields[:frame_ratio]
|
78
|
+
|
79
|
+
# TODO: handle global statistics request in another module
|
80
|
+
# the worldwide statistics on the number of views
|
81
|
+
# request << 'stats.global.last_week' if fields[:stats][:global]
|
82
|
+
|
83
|
+
# TODO: handle statistics request per country
|
84
|
+
# fields[:stats].each { |key| request << "meta.#{key.to_s}" } if fields[:meta].present?
|
85
|
+
# request['stats'][COUNTRY_CODE][TIME_INTERVAL] : the statistics on the number of views in a specific country (eg: stats.fr.total, stats.us.last_week, etc...)
|
86
|
+
# request['extended_stats'][COUNTRY_CODE][TIME_INTERVAL]
|
87
|
+
|
88
|
+
assets_names = ['source'] if assets_names.nil?
|
89
|
+
if not fields[:assets]
|
90
|
+
request = all_assets_fields(request, assets_names)
|
91
|
+
else
|
92
|
+
assets_names.each do |name|
|
93
|
+
fields[:assets].each { |value| request << "assets.#{name}.#{value.to_s}" }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
request
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
protected
|
102
|
+
# This method exclude stats, but return all information for a media (video or images)
|
103
|
+
def self.all_assets_fields(request, assets_names)
|
104
|
+
assets_names.each do |name|
|
105
|
+
request['fields'] << "assets.#{name}.download_url"
|
106
|
+
request['fields'] << "assets.#{name}.status"
|
107
|
+
request['fields'] << "assets.#{name}.container"
|
108
|
+
request['fields'] << "assets.#{name}.duration"
|
109
|
+
request['fields'] << "assets.#{name}.global_bitrate"
|
110
|
+
request['fields'] << "assets.#{name}.video_codec"
|
111
|
+
request['fields'] << "assets.#{name}.video_width"
|
112
|
+
request['fields'] << "assets.#{name}.video_height"
|
113
|
+
request['fields'] << "assets.#{name}.video_bitrate"
|
114
|
+
request['fields'] << "assets.#{name}.video_rotation"
|
115
|
+
request['fields'] << "assets.#{name}.video_fps"
|
116
|
+
request['fields'] << "assets.#{name}.video_fps_mode"
|
117
|
+
request['fields'] << "assets.#{name}.video_aspect"
|
118
|
+
request['fields'] << "assets.#{name}.video_interlaced"
|
119
|
+
request['fields'] << "assets.#{name}.audio_codec"
|
120
|
+
request['fields'] << "assets.#{name}.audio_bitrate"
|
121
|
+
request['fields'] << "assets.#{name}.audio_nbr_channel"
|
122
|
+
request['fields'] << "assets.#{name}.audio_samplerate"
|
123
|
+
request['fields'] << "assets.#{name}.created"
|
124
|
+
request['fields'] << "assets.#{name}.file_extension"
|
125
|
+
request['fields'] << "assets.#{name}.file_size"
|
126
|
+
end
|
127
|
+
request
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/dm_cloud/media.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'dm_cloud/builder/media'
|
2
|
+
|
1
3
|
module DMCloud
|
2
4
|
class Media
|
3
5
|
# Creates a new media object.
|
@@ -13,14 +15,14 @@ module DMCloud
|
|
13
15
|
# when you set this parameter you must also set the url parameter
|
14
16
|
# Return :
|
15
17
|
# media_id: return the media id of the object
|
16
|
-
def self.create(
|
17
|
-
|
18
|
+
def self.create(options)
|
19
|
+
call_type = "media.create"
|
18
20
|
|
19
21
|
params = {
|
20
|
-
call
|
21
|
-
args:
|
22
|
+
:call => call_type,
|
23
|
+
args: Builder::Media.create(options)
|
22
24
|
}
|
23
|
-
DMCloud::Request.execute(
|
25
|
+
DMCloud::Request.execute(call_type, params)
|
24
26
|
end
|
25
27
|
|
26
28
|
# Delete a media object with all its associated assets.
|
@@ -29,43 +31,55 @@ module DMCloud
|
|
29
31
|
# id (media ID) – (required) the id of the media object you want to delete.
|
30
32
|
# Return :
|
31
33
|
# Nothing
|
32
|
-
def self.delete
|
33
|
-
|
34
|
+
def self.delete(media_id)
|
35
|
+
call_type = "media.delete"
|
34
36
|
|
35
37
|
params = {
|
36
|
-
call
|
38
|
+
:call => call_type,
|
37
39
|
args: { id: media_id}
|
38
40
|
}
|
39
|
-
DMCloud::Request.execute(
|
41
|
+
DMCloud::Request.execute(call_type, params)
|
40
42
|
end
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
+
# Gives information about a given media object.
|
45
|
+
#
|
46
|
+
# Params :
|
47
|
+
# media_id: (media ID) – (required) the id of the new media object.
|
48
|
+
# fields (Array) – (required) the list of fields to retrieve.
|
49
|
+
# Returns:
|
50
|
+
# a multi-level structure containing about the media related to the requested fields.
|
51
|
+
def self.info(media_id, assets_names = ['source'], fields = [])
|
52
|
+
call_type = "media.info"
|
44
53
|
|
45
54
|
params = {
|
46
|
-
call
|
47
|
-
args: DMCloud::Builder::Media.info(fields)
|
55
|
+
:call => call_type,
|
56
|
+
args: DMCloud::Builder::Media.info(media_id, assets_names, fields)
|
48
57
|
}
|
49
|
-
DMCloud::Request.execute(
|
58
|
+
DMCloud::Request.execute(call_type, params)
|
50
59
|
end
|
51
60
|
|
52
|
-
#
|
61
|
+
# Returns a paginated list of media info structures.
|
62
|
+
# You must specify the fields you want to retrieve.
|
63
|
+
# The fields are described in the documentation of the method info.
|
53
64
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
|
65
|
+
# Parameters:
|
66
|
+
# options:
|
67
|
+
# fields (Array) – (optional default return all informations) the fields to retrieve
|
68
|
+
# page (Integer) – (optional) the page number, default: 1
|
69
|
+
# per_page (Integer) – (optional) the number of objet per page, default: 10
|
70
|
+
# Returns:
|
71
|
+
# an object with information for the pagination and the result of the query.
|
59
72
|
def self.list(options = {})
|
60
|
-
|
73
|
+
call_type = "media.list"
|
74
|
+
|
61
75
|
page = options[:page].present? ? options[:page] : 1
|
62
76
|
per_page = options[:per_page].present? ? options[:per_page] : 10
|
63
77
|
|
64
78
|
params = {
|
65
|
-
call
|
79
|
+
:call => call_type,
|
66
80
|
args: DMCloud::Builder::Media.list(options)
|
67
81
|
}
|
68
|
-
DMCloud::Request.execute(
|
82
|
+
DMCloud::Request.execute(call_type, params)
|
69
83
|
end
|
70
84
|
|
71
85
|
end
|
data/lib/dm_cloud/request.rb
CHANGED
@@ -1,13 +1,38 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
1
3
|
module DMCloud
|
2
4
|
class Request
|
3
5
|
|
6
|
+
DAILYMOTION_API = 'http://api.dmcloud.net/api'
|
7
|
+
|
8
|
+
def self.send_request(params)
|
9
|
+
puts 'params : ' + params.to_yaml
|
10
|
+
@uri = URI.parse(DAILYMOTION_API)
|
11
|
+
|
12
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
13
|
+
request = Net::HTTP::Post.new(@uri.request_uri)
|
14
|
+
# request.basic_auth @uri.user, @uri.password
|
15
|
+
request.content_type = 'application/json'
|
16
|
+
request.body = params.to_json
|
17
|
+
|
18
|
+
puts 'request : ' + request.to_yaml
|
19
|
+
|
20
|
+
http.request(request).body
|
21
|
+
|
22
|
+
end
|
23
|
+
|
4
24
|
|
5
25
|
def self.execute(call, params = {})
|
6
|
-
request = DMCloud.identify(params)
|
26
|
+
request = DMCloud::Signing.identify(params)
|
7
27
|
params.merge!({'auth' => request})
|
8
|
-
result =
|
9
|
-
|
28
|
+
result = send_request(params)
|
29
|
+
parse_response(result)
|
10
30
|
end
|
11
31
|
|
32
|
+
def self.parse_response(result)
|
33
|
+
puts 'result : ' + result.to_yaml
|
34
|
+
end
|
35
|
+
|
36
|
+
|
12
37
|
end
|
13
38
|
end
|
data/lib/dm_cloud/signing.rb
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
module DMCloud
|
2
2
|
class Signing
|
3
|
+
# Generate auth token for request from Media
|
4
|
+
# Params:
|
5
|
+
# request: A hash of params generated from Media methods and Media::MetaData
|
6
|
+
# Result :
|
7
|
+
# return a string which contain the auth token for the request
|
8
|
+
# <url>?auth=<expires>-<sec>-<nonce>-<md5sum>[-<pub-sec-data>]
|
9
|
+
def self.identify(request)
|
10
|
+
user_id = DMCloud.config[:user_key]
|
11
|
+
api_key = DMCloud.config[:secret_key]
|
12
|
+
|
13
|
+
normalized_request = normalize(request).to_s
|
14
|
+
params = user_id + normalized_request + api_key
|
15
|
+
|
16
|
+
checksum = Digest::MD5.hexdigest(params)
|
17
|
+
auth_token = user_id + ':' + checksum
|
18
|
+
|
19
|
+
auth_token
|
20
|
+
end
|
21
|
+
|
3
22
|
# To sign a URL, the client needs a secret shared with Dailymotion Cloud.
|
4
23
|
# This secret is call client secret and is available in the back-office interface.
|
5
24
|
# Params:
|
@@ -142,5 +161,11 @@ module DMCloud
|
|
142
161
|
end
|
143
162
|
result
|
144
163
|
end
|
164
|
+
|
165
|
+
def self.normalize(params)
|
166
|
+
str = params.to_json.to_s
|
167
|
+
str.gsub!(/[^A-Za-z0-9]/, '')
|
168
|
+
str
|
169
|
+
end
|
145
170
|
end
|
146
171
|
end
|
data/lib/dm_cloud/version.rb
CHANGED
data/lib/dm_cloud.rb
CHANGED
@@ -36,14 +36,6 @@ module DMCloud
|
|
36
36
|
@@config
|
37
37
|
end
|
38
38
|
|
39
|
-
def self.identify(request)
|
40
|
-
user_id = @@config[:user_id]
|
41
|
-
api_key = @@config[:api_key]
|
42
|
-
checksum = md5(user_id + normalize(request) + api_key)
|
43
|
-
|
44
|
-
auth_token = user_id + ':' + checksum
|
45
|
-
end
|
46
|
-
|
47
39
|
def self.create_has_library(library)
|
48
40
|
define_singleton_method("has_#{library}?") do
|
49
41
|
cv="@@#{library}"
|
@@ -58,9 +50,9 @@ module DMCloud
|
|
58
50
|
class_variable_get(cv)
|
59
51
|
end
|
60
52
|
end
|
61
|
-
|
62
|
-
create_has_library :treaming
|
63
53
|
|
54
|
+
create_has_library :streaming
|
55
|
+
create_has_library :media
|
64
56
|
|
65
57
|
class << self
|
66
58
|
# Load a object saved on a file.
|
@@ -76,8 +68,9 @@ module DMCloud
|
|
76
68
|
end
|
77
69
|
|
78
70
|
autoload(:Streaming, 'dm_cloud/streaming')
|
79
|
-
|
80
|
-
|
71
|
+
autoload(:Media, 'dm_cloud/media')
|
72
|
+
autoload(:Request, 'dm_cloud/request')
|
73
|
+
autoload(:Signing, 'dm_cloud/signing')
|
81
74
|
end
|
82
75
|
|
83
|
-
Dir.glob('dm_cloud/**/*.rb').each{ |m| require File.dirname(__FILE__) + '/dm_cloud/' + m }
|
76
|
+
# Dir.glob('dm_cloud/**/*.rb').each{ |m| require File.dirname(__FILE__) + '/dm_cloud/' + m }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm_cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -26,6 +26,7 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- dm_cloud.gemspec
|
28
28
|
- lib/dm_cloud.rb
|
29
|
+
- lib/dm_cloud/builder/media.rb
|
29
30
|
- lib/dm_cloud/media.rb
|
30
31
|
- lib/dm_cloud/request.rb
|
31
32
|
- lib/dm_cloud/signing.rb
|