dm_cloud 0.0.1 → 0.0.3
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/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
|