s3_media_server_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 49178c41050a932cd11ba20ab661e0e3353e64d4
4
+ data.tar.gz: bf5572843932b79e5424b649b1fa3db92432be50
5
+ SHA512:
6
+ metadata.gz: 415b6b8a82653c15cd62d9bbca6b0f4cabd12cd44951d9c5bc954fa55ab29dd302bf1fe7845bdb084d6ce2b3b4987d7f402ba9d1782f657307436cd26603d50e
7
+ data.tar.gz: 0e2df36585d3c516f74a322de409580c2c07da68a2ea1386b8b02760b4543786504fee4ffc2e524806f00e8ca70ec03ad47ef50596f224383bc0fa3f111ff7d6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in s3_media_server_api.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Ayrat Badykov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # S3MediaServerApi - - a Ruby client for the S3 Media Server
2
+
3
+ S3MediaServerApi helps you write apps that need to interact with S3 Media Server.
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 's3_media_server_api'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install s3_media_server_api
21
+
22
+ ## Usage
23
+ ### Available resources
24
+
25
+ #### S3MediaServerApi::Media::Document
26
+ Use S3MediaServerApi::Media::Document to interact Document resource
27
+ ```ruby
28
+ # if you want create document from file path, use create method
29
+ created_document = S3MediaServerApi::Media::Document.create('/Users/ayrat/Development/s3_media_server_api/tmp/sample_mpeg4.mp4')
30
+
31
+
32
+ # to resolve document, use resolve method
33
+ resolved_document = S3MediaServerApi::Media::Document.resolve(created_document.uuid)
34
+
35
+ # both methods create and resolve return document object
36
+
37
+ resolved_document.uuid # uuid of document
38
+ resolved_document.url # url of document
39
+ resolved_document.size # size of document
40
+ resolved_document.name # url of document
41
+
42
+ # use destroy method, to destroy document
43
+ # this method is asynchronous, so it doesn't return anything
44
+ S3MediaServerApi::Media::Document.destroy(created_document.uuid)
45
+ ```
46
+ #### S3MediaServerApi::Media::Image
47
+ Use S3MediaServerApi::Media::Image to interact Image resource
48
+ ```ruby
49
+ # if you want create image from file path, use create method
50
+ created_image = S3MediaServerApi::Media::Image.create('/Users/ayrat/Development/s3_media_server_api/tmp/test_image.jpg')
51
+
52
+ # to resolve image, use resolve method
53
+ resolved_image = S3MediaServerApi::Media::Image.resolve(created_image.uuid)
54
+
55
+ # both methods create and resolve return document object
56
+ resolved_image.size
57
+ resolved_image.name
58
+ resolved_image.uuid
59
+ resolved_image.source.url
60
+ resolved_image.source.width
61
+ resolved_image.source.height
62
+
63
+ # to create thumb of image, use resize method.
64
+ # this method is asynchronous
65
+ S3MediaServerApi::Media::Image.resize(created_image.uuid)
66
+
67
+ # after thumb is created, new attributes will be available
68
+ thumbed_image = S3MediaServerApi::Media::Image.resolve(created_image.uuid)
69
+ thumbed_image.thumb.url
70
+ thumbed_image.thumb.width
71
+ thumbed_image.thumb.height
72
+
73
+ # to copy image, use copy method
74
+ copied_image = S3MediaServerApi::Media::Image.copy(created_image.uuid)
75
+
76
+ # to destroy image, use destroy method
77
+ # this method is asynchronous
78
+ S3MediaServerApi::Media::Image.destroy(created_image.uuid)
79
+ ```
80
+ #### S3MediaServerApi::Media::Audio
81
+ Use S3MediaServerApi::Media::Audio to interact Audio resource
82
+ ```ruby
83
+ # to create audio file from its path, use create method
84
+ created_audio = S3MediaServerApi::Media::Audio.create('/Users/ayrat/Development/s3_media_server_api/tmp/music_test.mp3')
85
+ # to resolve image, use resolve method
86
+ resolved_audio = S3MediaServerApi::Media::Audio.resolve(created_audio.uuid)
87
+
88
+ # both methods create and resolve return audio object
89
+ resolved_audio.url
90
+ resolved_audio.uuid
91
+ resolved_audio.name
92
+ resolved_audio.size
93
+
94
+ # cut method sends request to create audio sampel
95
+ # parameters: uuid - uuid of file
96
+ # audio_url - url of audio file
97
+ # duration - duration of cutted file
98
+ # start_position - position where cut wil be made
99
+ # this method is asynchronous
100
+ cut_params = { audio_url: created_audio.url,
101
+ duration: 20,
102
+ start_position: 40}
103
+ S3MediaServerApi::Media::Audio.cut(created_audio.uuid, cut_params)
104
+
105
+ # after audio is cutted, new attributes will be available
106
+ cutted_audio = S3MediaServerApi::Media::Audio.resolve(created_audio.uuid)
107
+
108
+ cutted_audio.sample_url
109
+ cutted_audio.duration
110
+ cutted_audio.sample_duration
111
+
112
+ # destroys audio file
113
+ # this method is asynchronous
114
+ S3MediaServerApi::Media::Audio.destroy(created_audio.uuid)
115
+ ```
116
+ #### S3MediaServerApi::Media::Video
117
+ Use S3MediaServerApi::Media::Video to interact Video resource
118
+ ```ruby
119
+ # to create video from its path, use create method
120
+ video = S3MediaServerApi::Media::Video.create('/Users/ayrat/Development/s3_media_server_api/tmp/sample_mpeg4.mp4')
121
+
122
+ # to resolve video, use resolve method
123
+ video = S3MediaServerApi::Media::Video.resolve(video.uuid)
124
+
125
+ # both methods create and resolve return video object
126
+ video.
127
+
128
+ video.name
129
+ video.embed_url
130
+ video.transcoded # true if video has all versions
131
+ # false otherwise
132
+
133
+ # after screenshots are made, screenshots attributes will be available with 3 screenshots
134
+ video = S3MediaServerApi::Media::Video.resolve(video.uuid)
135
+ video.screenshots[0].source.url
136
+ video.screenshots[0].source.width
137
+ video.screenshots[0].source.height
138
+ video.screenshots[0].thumb.url
139
+ video.screenshots[0].thumb.width
140
+ video.screenshots[0].thumb.height
141
+
142
+ # you can get available video versions in version attribute
143
+
144
+ video.versions[0].format
145
+ video.versions[0].resolution
146
+ video.versions[0].url
147
+ video.versions[0].size
148
+
149
+ # to destroy video, use destroy method
150
+ # this method is asynchronous
151
+ S3MediaServerApi::Media::Video.destroy(video.uuid)
152
+ ```
153
+
154
+ #### AwsFile
155
+ ```ruby
156
+ # to create aws file from its path, use upload method from S3MediaServerApi::Uploader module
157
+ aws_file = S3MediaServerApi::Uploader.upload('/Users/ayrat/Development/s3_media_server_api/tmp/test_image.jpg')
158
+
159
+ # to resolve aws file, use resolve method
160
+ resolved_aws_file = S3MediaServerApi::AwsFile.resolve(aws_file.uuid)
161
+
162
+ # both S3MediaServerApi::Uploader.upload and S3MediaServerApi::AwsFile.resolve return aws file object
163
+ aws_file.uuid
164
+ aws_file.size
165
+ aws_file.mime_type
166
+ aws_file.uploads_count
167
+ aws_file.default_part_size
168
+ aws_file.state
169
+ aws_file.public_url
170
+ aws_file.name
171
+
172
+ ```
173
+ NOTE: you can't remove aws file without creating media resource
174
+
175
+ ## Development
176
+
177
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
178
+
179
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
180
+
181
+ ## Contributing
182
+
183
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/s3_media_server_api.
184
+
185
+
186
+ ## License
187
+
188
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ require "s3_media_server_api/version"
2
+ require "s3_media_server_api/error"
3
+ require "s3_media_server_api/config"
4
+ require "s3_media_server_api/uploader"
5
+ require "s3_media_server_api/file_part"
6
+ require "s3_media_server_api/aws_file"
7
+ require "s3_media_server_api/media"
8
+ require "s3_media_server_api/asynk_request"
9
+
10
+ module S3MediaServerApi
11
+
12
+ class << self
13
+
14
+ def upload_thread_count
15
+ config.upload_thread_count
16
+ end
17
+
18
+ private
19
+
20
+ def config
21
+ @@config ||= Config.new
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ module S3MediaServerApi
2
+ module AsynkRequest
3
+ class << self
4
+ #
5
+ # sends asynchronous request using Asynk gem - https://github.com/konalegi/asynk
6
+ # parameters: path - base path of consumer
7
+ # action - consumer action
8
+ # params - parametes that will be passed to consumer
9
+ #
10
+ def async_request(path, action, params)
11
+ Asynk::Publisher.publish("#{server}.#{path}.#{action}", params)
12
+ end
13
+ #
14
+ # sends synchronous request using Asynk gem - https://github.com/konalegi/asynk
15
+ # parameters: path - base path of consumer
16
+ # action - consumer action
17
+ # params - parametes that will be passed to consumer
18
+ #
19
+ def sync_request(path, action, params)
20
+ Asynk::Publisher.sync_publish("#{server}.#{path}.#{action}", params)
21
+ end
22
+
23
+ private
24
+
25
+ def server
26
+ 's3_media_server'
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,106 @@
1
+ module S3MediaServerApi
2
+ class AwsFile
3
+
4
+ def initialize(response)
5
+ @params = response[:data]
6
+ end
7
+
8
+ def uuid
9
+ @params[:uuid]
10
+ end
11
+
12
+ def size
13
+ @params[:size]
14
+ end
15
+
16
+ def mime_type
17
+ @params[:mime_type]
18
+ end
19
+
20
+ def uploads_count
21
+ @params[:uploads_count]
22
+ end
23
+
24
+ def default_part_size
25
+ @params[:default_part_size]
26
+ end
27
+
28
+ def state
29
+ @params[:state]
30
+ end
31
+
32
+ def public_url
33
+ @params[:public_url]
34
+ end
35
+
36
+ def name
37
+ @params[:name]
38
+ end
39
+
40
+ def as_hash
41
+ @params
42
+ end
43
+
44
+ class << self
45
+ class AwsFileError < S3MediaServerApiError; end
46
+ class FileCreationError < AwsFileError; end
47
+ class CompleteUploadError < AwsFileError; end
48
+ #
49
+ # creates file on s3_media_server
50
+ # parameters: file_path - path on file system to file
51
+ #
52
+ # returns: response with created file
53
+ #
54
+ def create_from_path(file_path)
55
+ params = {
56
+ size: File.size(file_path),
57
+ mime_type: file_mime_type(file_path),
58
+ name: File.basename(file_path)
59
+ }
60
+ response = AsynkRequest.sync_request(base_path, :create, params)
61
+ raise FileCreationError.message_from_asynk_response(response) unless response.success?
62
+ AwsFile.new(response)
63
+ end
64
+ #
65
+ # fetches media file
66
+ # parameters: uuid - uuid of file
67
+ #
68
+ # returns: response with file information
69
+ #
70
+ def resolve(uuid)
71
+ AsynkRequest.sync_request(base_path, :resolve, uuid: uuid)
72
+ end
73
+ #
74
+ # fetches signed upload url to upload specified part number
75
+ # parameters: uuid - file uuid
76
+ # part_numer - part number that will be uploaded
77
+ #
78
+ # returns: signed upload url
79
+ #
80
+ def get_signed_upload_url(uuid, part_number)
81
+ response = AsynkRequest.sync_request(:uploads, :show, aws_file_uuid: uuid, uuid: part_number)
82
+ response[:data][:upload_url]
83
+ end
84
+ #
85
+ # completes multipart upload
86
+ # parameters: uuid - file uuid
87
+ #
88
+ def complete_upload(uuid)
89
+ response = AsynkRequest.sync_request(base_path, :complete_upload, uuid: uuid)
90
+ raise CompleteUploadError.message_from_asynk_response(response) unless response.success?
91
+ AwsFile.new(response)
92
+ end
93
+
94
+ private
95
+
96
+ def file_mime_type(file_source_path)
97
+ mime_magic = MimeMagic.by_magic(File.open(file_source_path))
98
+ mime_magic ? mime_magic.type : 'application/octet-stream'
99
+ end
100
+
101
+ def base_path
102
+ 'aws_file'
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,26 @@
1
+ require 'yaml'
2
+
3
+ module S3MediaServerApi
4
+ class ConfigError < S3MediaServerApiError; end
5
+ #
6
+ # By default 4 threads is used for file uploading. To change
7
+ # this value you can create config/s3_media_server_api.yml and specify
8
+ # upload_thread_count
9
+ #
10
+ # Example
11
+ # prosuction:
12
+ # upload_thread_count: 10
13
+ #
14
+ class Config
15
+ attr_reader :upload_thread_count
16
+
17
+ def initialize
18
+ @upload_thread_count = nil
19
+ environment = ENV['RUBY_ENV'] || ENV['RAILS_ENV']
20
+ if File.exists?('config/s3_media_server_api.yml')
21
+ @attributes = YAML.load_file('config/s3_media_server_api.yml')[environment][:upload_thread_count]
22
+ end
23
+ @upload_thread_count ||= 4
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module S3MediaServerApi
2
+ class S3MediaServerApiError < RuntimeError
3
+ class<<self
4
+ def message_from_asynk_response(asynk_response)
5
+ self.new("body: #{asynk_response.body}, message: #{asynk_response.error_message}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,69 @@
1
+ module S3MediaServerApi
2
+ module Uploader
3
+
4
+ class FilePart
5
+ # @option options [required,String,Pathname,File,Tempfile] :source
6
+ # @option options [required,Integer] :offset The file part will read
7
+ # starting at this byte offset.
8
+ # @option options [required,Integer] :size The maximum number of bytes to
9
+ # read from the `:offset`.
10
+ def initialize(options = {})
11
+ @source = options[:source]
12
+ @first_byte = options[:offset]
13
+ @last_byte = @first_byte + options[:size]
14
+ @size = options[:size]
15
+ end
16
+
17
+ # @return [String,Pathname,File,Tempfile]
18
+ attr_reader :source
19
+
20
+ # @return [Integer]
21
+ attr_reader :first_byte
22
+
23
+ # @return [Integer]
24
+ attr_reader :last_byte
25
+
26
+ # @return [Integer]
27
+ attr_reader :size
28
+
29
+ def read(bytes = nil, output_buffer = nil)
30
+ open_file unless @file
31
+ read_from_file(bytes, output_buffer)
32
+ end
33
+
34
+ def rewind
35
+ if @file
36
+ @file.seek(@first_byte)
37
+ @position = @first_byte
38
+ end
39
+ 0
40
+ end
41
+
42
+ def close
43
+ @file.close if @file
44
+ end
45
+
46
+ private
47
+
48
+ def open_file
49
+ @file = File.open(@source, 'rb')
50
+ rewind
51
+ end
52
+
53
+ def read_from_file(bytes, output_buffer)
54
+ if bytes
55
+ data = @file.read([remaining_bytes, bytes].min)
56
+ data = nil if data == ''
57
+ else
58
+ data = @file.read(remaining_bytes)
59
+ end
60
+ @position += data ? data.bytesize : 0
61
+ output_buffer ? output_buffer.replace(data || '') : data
62
+ end
63
+
64
+ def remaining_bytes
65
+ @last_byte - @position
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,11 @@
1
+ require 's3_media_server_api/media/common_media_api'
2
+ require 's3_media_server_api/media/image'
3
+ require 's3_media_server_api/media/video'
4
+ require 's3_media_server_api/media/audio'
5
+ require 's3_media_server_api/media/document'
6
+
7
+
8
+ module S3MediaServerApi
9
+ module Media
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ module S3MediaServerApi
2
+ module Media
3
+ class Audio < CommonMediaApi
4
+ AUDIO = 'audio'
5
+
6
+ def sample_url
7
+ @params[:sample_url]
8
+ end
9
+
10
+ def url
11
+ @params[:url]
12
+ end
13
+
14
+ def sample_url
15
+ @params[:sample_url]
16
+ end
17
+
18
+ def duration
19
+ @params[:duration]
20
+ end
21
+
22
+ def sample_duration
23
+ @params[:sample_duration]
24
+ end
25
+
26
+ class << self
27
+ def create(path)
28
+ Audio.new(super(path))
29
+ end
30
+
31
+ def resolve(uuid)
32
+ Audio.new(super(uuid))
33
+ end
34
+ #
35
+ # sends request to cut audio file
36
+ # parameters: uuid - uuid of file
37
+ # audio_url - url of audio file
38
+ # duration - duration of audio file
39
+ # start_position - position where cut wil be made
40
+ #
41
+ def cut(uuid, audio_url: audio_url, duration: duration, start_position: start_position)
42
+ params = {
43
+ uuid: uuid,
44
+ audio_url: audio_url,
45
+ duration: duration,
46
+ start_position: start_position
47
+ }
48
+ custom_async_request(:cut, params)
49
+ end
50
+
51
+ private
52
+ def media_type; AUDIO; end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,98 @@
1
+ module S3MediaServerApi
2
+ module Media
3
+ class CommonMediaApiError < S3MediaServerApiError; end
4
+ class CreationError < CommonMediaApiError ; end
5
+ #
6
+ # Parent module for all media api, implements common methods for all media files
7
+ # To use methods from this module new module must be inherited from this module and
8
+ # media_type method with existing media type must be overwritten
9
+ #
10
+ class CommonMediaApi
11
+
12
+ def initialize(response)
13
+ @params = response[:data]
14
+ end
15
+
16
+ def uuid
17
+ @params[:uuid]
18
+ end
19
+
20
+ def name
21
+ @params[:name]
22
+ end
23
+
24
+ def size
25
+ @params[:size]
26
+ end
27
+
28
+ def as_hash
29
+ @params
30
+ end
31
+
32
+ class << self
33
+ #
34
+ # creates media file
35
+ # parameters: path - file path in file system
36
+ #
37
+ # returns: response with created AwsFile information
38
+ #
39
+ def create(path)
40
+ aws_file = Uploader.upload(path)
41
+ uuid = aws_file.uuid
42
+ params = (media_type == 'video') ? { uuid: uuid } : { aws_file_uuid: uuid }
43
+ response = AsynkRequest.sync_request(base_path, :create, params)
44
+ raise CreationError.message_from_asynk_response(response) unless response.success?
45
+ response
46
+ end
47
+ #
48
+ # destroys media file
49
+ # parameters: uuid - uuid of file
50
+ #
51
+ def destroy(uuid)
52
+ AsynkRequest.async_request(base_path, :destroy, uuid: uuid)
53
+ end
54
+ #
55
+ # fetches media file
56
+ # parameters: uuid - uuid of file
57
+ #
58
+ # returns: response with file information
59
+ #
60
+ def resolve(uuid)
61
+ AsynkRequest.sync_request(base_path, :resolve, uuid: uuid)
62
+ end
63
+ #
64
+ # this method should be used to send custom synchronous request to
65
+ # s3_media_server. For example, to copy image file
66
+ # parameters: action - method that should be called
67
+ # params - parameters for specified method
68
+ # Example:
69
+ # custom_sync_request (:copy, 'image')
70
+ #
71
+ def custom_sync_request(action, params)
72
+ AsynkRequest.sync_request(base_path, action, params)
73
+ end
74
+ #
75
+ # this method should be used to send custom asynchronous request to
76
+ # s3_media_server. For example, to cut audio file
77
+ # parameters: action - method that should be called
78
+ # params - parameters for specified method
79
+ # Example:
80
+ # custom_async_request (:cut, 'image')
81
+ #
82
+ def custom_async_request(action, params)
83
+ AsynkRequest.async_request(base_path, action, params)
84
+ end
85
+
86
+ private
87
+ #
88
+ # specifies media type which methods will be called
89
+ #
90
+ def media_type; end
91
+ #
92
+ # base path of media consumers on s3_media_server
93
+ #
94
+ def base_path; "media.#{media_type}"; end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,26 @@
1
+ module S3MediaServerApi
2
+ module Media
3
+ class Document< CommonMediaApi
4
+ DOCUMENT = 'document'
5
+
6
+ def url
7
+ @params[:url]
8
+ end
9
+
10
+ class << self
11
+
12
+ def create(path)
13
+ Document.new(super(path))
14
+ end
15
+
16
+ def resolve(uuid)
17
+ Document.new(super(uuid))
18
+ end
19
+
20
+ private
21
+
22
+ def media_type; DOCUMENT; end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,69 @@
1
+ module S3MediaServerApi
2
+ module Media
3
+ class Image< CommonMediaApi
4
+ IMAGE = 'image'
5
+
6
+ class ImageObject
7
+
8
+ def initialize(params)
9
+ @params = params
10
+ end
11
+
12
+ def url
13
+ @params[:url]
14
+ end
15
+
16
+ def width
17
+ @params[:width]
18
+ end
19
+
20
+ def height
21
+ @params[:height]
22
+ end
23
+
24
+ def as_hash
25
+ @params
26
+ end
27
+ end
28
+
29
+ def source
30
+ ImageObject.new(@params[:source])
31
+ end
32
+
33
+ def thumb
34
+ ImageObject.new(@params[:thumb])
35
+ end
36
+
37
+ class << self
38
+
39
+ def create(path)
40
+ Image.new(super(path))
41
+ end
42
+
43
+ def resolve(uuid)
44
+ Image.new(super(uuid))
45
+ end
46
+ #
47
+ # copies image file
48
+ # parameters: uuid - uuid of file
49
+ # aduio - url of image file
50
+ # returns: response with copied image
51
+ #
52
+ def copy(uuid)
53
+ Image.new(custom_sync_request(:copy, uuid: uuid))
54
+ end
55
+ #
56
+ # sends request to make thumb of image
57
+ # parameters: uuid - uuid of file
58
+ #
59
+ def resize(uuid)
60
+ custom_async_request(:resize, uuid: uuid)
61
+ end
62
+
63
+ private
64
+
65
+ def media_type; IMAGE; end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,72 @@
1
+ module S3MediaServerApi
2
+ module Media
3
+ class Video< CommonMediaApi
4
+ VIDEO = 'video'
5
+
6
+ class Version
7
+ def initialize(params)
8
+ @params = params
9
+ end
10
+
11
+ def url
12
+ @params[:url]
13
+ end
14
+
15
+ def format
16
+ @params[:format]
17
+ end
18
+
19
+ def resolution
20
+ @params[:resolution]
21
+ end
22
+
23
+ def size
24
+ @params[:size]
25
+ end
26
+ end
27
+
28
+ def preview
29
+ @params[:preview]
30
+ end
31
+
32
+ def duration
33
+ @params[:duration]
34
+ end
35
+
36
+ def transcoded
37
+ @params[:transcoded]
38
+ end
39
+
40
+ def embed_url
41
+ @params[:embed_url]
42
+ end
43
+
44
+ def provider
45
+ @params[:provider]
46
+ end
47
+
48
+ def screenshots
49
+ @params[:screenshots].map { |screenshot| Image::ImageObject.new(screenshot) }
50
+ end
51
+
52
+ def versions
53
+ @params[:versions].map { |version| Version.new(version) }
54
+ end
55
+
56
+ class << self
57
+
58
+ def create(path)
59
+ Video.new(super(path))
60
+ end
61
+
62
+ def resolve(uuid)
63
+ Video.new(super(uuid))
64
+ end
65
+
66
+ private
67
+
68
+ def media_type; VIDEO; end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,107 @@
1
+ require 'mimemagic'
2
+ require 'asynk'
3
+ require 'parallel'
4
+ require 'faraday'
5
+
6
+ module S3MediaServerApi
7
+
8
+ # Module with file uplod finctionality implementation
9
+
10
+ module Uploader
11
+
12
+ class << self
13
+
14
+ class UploaderError < S3MediaServerApiError; end
15
+ class PartUploadError < UploaderError; end
16
+ #
17
+ # uploads file to amazon s3 and create AwsFile object
18
+ # parameter : filepath - file path in file system
19
+ # returns: [AwsFile object]
20
+ #
21
+ # Example
22
+ #
23
+ # file = S3MediaServerApi::Uploader.upload(/home/vasya/my_awesome_file.awesome)
24
+ #
25
+ def upload(file_path)
26
+ parts = []
27
+ file = AwsFile.create_from_path(file_path)
28
+ default_part_size = file.default_part_size
29
+ aws_file_uuid = file.uuid
30
+ uploads_count = file.uploads_count
31
+ parts = compute_parts(file_path, default_part_size)
32
+ Parallel.each(parts, in_threads: S3MediaServerApi.upload_thread_count) do |part|
33
+ signed_upload_url = AwsFile.get_signed_upload_url(aws_file_uuid, part[:part_number])
34
+
35
+ raise PartUploadError.new("Part #{part[:part_number]} wasn't uploaded") unless upload_part(signed_upload_url, part[:body].read)
36
+ end
37
+
38
+ AwsFile.complete_upload(aws_file_uuid)
39
+ ensure
40
+ close_all_parts(parts)
41
+ end
42
+
43
+ private
44
+ #
45
+ # closes all parts of file that were used in multipart upload to prevent memory leak
46
+ #
47
+ def close_all_parts(parts)
48
+ parts.each do |part|
49
+ part[:body].close
50
+ end
51
+ end
52
+ #
53
+ # divides file into parts for multipart upload
54
+ # parameter:
55
+ # source - path of source file
56
+ # default_part_size - wanted part size (in bytes)
57
+ # returns: [ array of parts ]
58
+ #
59
+ def compute_parts(source, default_part_size)
60
+ size = File.size(source)
61
+ offset, part_number, parts = 0, 1, []
62
+ while offset < size
63
+ parts << {
64
+ part_number: part_number,
65
+ body: FilePart.new(source: source, offset: offset, size: part_size(size, default_part_size, offset))
66
+ }
67
+
68
+ part_number += 1
69
+ offset += default_part_size
70
+ end
71
+ parts
72
+ end
73
+ #
74
+ # calculates size of one part - last part often has smaller size than other parts
75
+ # parameters: total_size - totol size of file (in bytes)
76
+ # part_size - default size of one part (in bytes)
77
+ # offset - offset from beginning of file (in bytes)
78
+ # returns: part size
79
+ #
80
+ def part_size(total_size, part_size, offset)
81
+ if offset + part_size > total_size
82
+ total_size - offset
83
+ else
84
+ part_size
85
+ end
86
+ end
87
+ #
88
+ # uploads data to specified url
89
+ # parameters: url - upload url
90
+ # data - data to upload
91
+ # returns: true if upload was successful
92
+ # false otherwise
93
+ #
94
+ def upload_part(url, data)
95
+ conn = Faraday.new(url: url) do |faraday|
96
+ faraday.adapter :net_http
97
+ end
98
+ resp = conn.put do |req|
99
+ req.body = data
100
+ # to prevent Faraday from adding garbage header
101
+ req.headers['Content-Type'] = ''
102
+ end
103
+ resp.success?
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module S3MediaServerApi
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 's3_media_server_api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "s3_media_server_api"
8
+ spec.version = S3MediaServerApi::VERSION
9
+ spec.authors = ["Ayrat Badykov"]
10
+ spec.email = ["ayratin555@gmail.com"]
11
+
12
+ spec.summary = 'S3MediaServerApi helps you write apps that need to interact with S3 Media Server.'
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.11"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "minitest", "~> 5.0"
23
+ spec.add_dependency 'asynk', '>= 0.0.1'
24
+ spec.add_dependency 'parallel'
25
+ spec.add_dependency 'mimemagic'
26
+ spec.add_dependency 'faraday'
27
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: s3_media_server_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ayrat Badykov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-07-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: asynk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: parallel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: mimemagic
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description:
112
+ email:
113
+ - ayratin555@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - Gemfile
119
+ - LICENSE.txt
120
+ - README.md
121
+ - Rakefile
122
+ - lib/s3_media_server_api.rb
123
+ - lib/s3_media_server_api/asynk_request.rb
124
+ - lib/s3_media_server_api/aws_file.rb
125
+ - lib/s3_media_server_api/config.rb
126
+ - lib/s3_media_server_api/error.rb
127
+ - lib/s3_media_server_api/file_part.rb
128
+ - lib/s3_media_server_api/media.rb
129
+ - lib/s3_media_server_api/media/audio.rb
130
+ - lib/s3_media_server_api/media/common_media_api.rb
131
+ - lib/s3_media_server_api/media/document.rb
132
+ - lib/s3_media_server_api/media/image.rb
133
+ - lib/s3_media_server_api/media/video.rb
134
+ - lib/s3_media_server_api/uploader.rb
135
+ - lib/s3_media_server_api/version.rb
136
+ - s3_media_server_api.gemspec
137
+ homepage:
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.4.5.1
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: S3MediaServerApi helps you write apps that need to interact with S3 Media
161
+ Server.
162
+ test_files: []