crabgrass_media 0.0.1
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.
- checksums.yaml +7 -0
- data/README.md +29 -0
- data/Rakefile +34 -0
- data/config/initializers/paths.rb +9 -0
- data/lib/media.rb +64 -0
- data/lib/media/mime_type.rb +272 -0
- data/lib/media/temp_file.rb +152 -0
- data/lib/media/transmogrifier.rb +331 -0
- data/lib/media/transmogrifiers/graphicsmagick.rb +154 -0
- data/lib/media/transmogrifiers/inkscape.rb +58 -0
- data/lib/media/transmogrifiers/libremagick.rb +53 -0
- data/lib/media/transmogrifiers/libreoffice.rb +155 -0
- data/lib/media/version.rb +3 -0
- data/lib/tasks/media_tasks.rake +4 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +1046 -0
- data/test/files/anarchism.svg +361 -0
- data/test/files/corrupt.jpg +0 -0
- data/test/files/lyra.png +0 -0
- data/test/files/msword.doc +0 -0
- data/test/sleepy_transmogrifier.rb +39 -0
- data/test/support/file.rb +21 -0
- data/test/tempfile_test.rb +8 -0
- data/test/test_helper.rb +13 -0
- data/test/transmogrifier_test.rb +106 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d09be8b214809ae6616a5126df305f329876c7ad
|
4
|
+
data.tar.gz: ce7bc5c445c29e015eccde25093b94d5d5c3a419
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d87316cc0f0719e2f2f7ab0cd222b13e2dad38c38962aa104bf433c957e5b9f1102011a6e2569000cb170f3199c16dc3a67b998a1e521b3f07f1868a8ade3b28
|
7
|
+
data.tar.gz: 5aa9cfe9a207f706eb78fcf88963c8340e30f8693acb4f99317286eeb0e8f6d1c81749e6d785f0af1de6380d1436a6b9cbf937d428cd5f1c82335361c809534c
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Crabgrass::Media is the Media engine of Crabgrass.
|
2
|
+
|
3
|
+
Crabgrass is a web application designed for activist groups to be better able to collaborate online. Mostly, it is a glorified wiki with fine-grain control over access rights.
|
4
|
+
|
5
|
+
Crabgrass::Media is a rails engine to do media transformations.
|
6
|
+
|
7
|
+
You can add new media transformations by subclassing Transmogrifier.
|
8
|
+
|
9
|
+
Example usage:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
transmog = Media.transmogrifier(:input_file => 'myfile.odt', :output_file => 'myfile.jpg')
|
13
|
+
status = transmog.run do |progress|
|
14
|
+
puts progress
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
Requirements:
|
19
|
+
|
20
|
+
|
21
|
+
Strongly suggested:
|
22
|
+
|
23
|
+
```sh
|
24
|
+
gem install mime-types
|
25
|
+
```
|
26
|
+
|
27
|
+
Crabgrass and Crabgrass::Media are based on Ruby on Rails and MySQL.
|
28
|
+
They are released under the AGPL license, version 3.
|
29
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Media'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,9 @@
|
|
1
|
+
unless defined?(Media::TMP_PATH)
|
2
|
+
if defined?(Rails)
|
3
|
+
Media::TMP_PATH = File.join(Rails.root, 'tmp', 'media')
|
4
|
+
else
|
5
|
+
Media::TMP_PATH = File.join('', 'tmp', 'media')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
FileUtils.mkdir_p(Media::TempFile.tempfile_path) unless File.exist?(Media::TempFile.tempfile_path)
|
data/lib/media.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'media/mime_type'
|
2
|
+
require 'media/temp_file'
|
3
|
+
require 'media/transmogrifier'
|
4
|
+
require 'media/transmogrifiers/graphicsmagick'
|
5
|
+
require 'media/transmogrifiers/inkscape'
|
6
|
+
require 'media/transmogrifiers/libremagick'
|
7
|
+
require 'media/transmogrifiers/libreoffice'
|
8
|
+
|
9
|
+
module Media
|
10
|
+
|
11
|
+
#
|
12
|
+
# creates a new instance of transmogrifier suitable for turning
|
13
|
+
# input into output.
|
14
|
+
#
|
15
|
+
def self.transmogrifier(options)
|
16
|
+
if options[:input_type]
|
17
|
+
input_type = Media::MimeType.simple options[:input_type]
|
18
|
+
elsif options[:input_file]
|
19
|
+
input_type = Media::MimeType.mime_type_from_extension options[:input_file]
|
20
|
+
else
|
21
|
+
raise ArgumentError.new
|
22
|
+
end
|
23
|
+
|
24
|
+
if options[:output_type]
|
25
|
+
output_type = Media::MimeType.simple options[:output_type]
|
26
|
+
elsif options[:output_file]
|
27
|
+
output_type = Media::MimeType.mime_type_from_extension options[:output_file]
|
28
|
+
else
|
29
|
+
raise ArgumentError.new
|
30
|
+
end
|
31
|
+
|
32
|
+
unless input_type and output_type
|
33
|
+
raise ArgumentError.new("Both input and output types are required (given %s -> %s)." % [input_type||'nil', output_type||'nil'])
|
34
|
+
end
|
35
|
+
|
36
|
+
transmog_class = Transmogrifier.find_class(input_type, output_type)
|
37
|
+
if transmog_class
|
38
|
+
transmog_class.new(options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.may_produce?(src_type, dst_type)
|
43
|
+
Transmogrifier.find_class(src_type, dst_type)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# special graphicsmagick hooks.
|
48
|
+
# we use graphicsmagick in order to parse the dimensions of image files.
|
49
|
+
#
|
50
|
+
|
51
|
+
def self.has_dimensions?(mime_type)
|
52
|
+
if Transmogrifier.list["Media::GraphicsMagickTransmogrifier"]
|
53
|
+
Transmogrifier.list["Media::GraphicsMagickTransmogrifier"].input_types.include? mime_type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.dimensions(filepath)
|
58
|
+
if Transmogrifier.list["Media::GraphicsMagickTransmogrifier"]
|
59
|
+
Transmogrifier.list["Media::GraphicsMagickTransmogrifier"].dimensions filepath
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,272 @@
|
|
1
|
+
begin
|
2
|
+
require 'mime/types'
|
3
|
+
rescue LoadError => exc
|
4
|
+
puts "WARNING! no mime-types gem installed"
|
5
|
+
puts "you should really do 'gem install mime-types'"
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
module Media
|
10
|
+
module MimeType
|
11
|
+
|
12
|
+
def self.mime_group(mime_type)
|
13
|
+
mime_type.sub(/\/.*$/,'/') if mime_type # remove everything after /
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.simple(mime_type)
|
17
|
+
mime_type.to_s.sub(/\/x\-/,'/') if mime_type # remove x-
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.lookup(mime_type,field)
|
21
|
+
(MIME_TYPES[simple(mime_type)]||[])[field]
|
22
|
+
end
|
23
|
+
|
24
|
+
# def self.group_from_mime_type(mime_type)
|
25
|
+
# lookup(mime_type,GROUP) || lookup(mime_group(mime_type),GROUP)
|
26
|
+
# end
|
27
|
+
|
28
|
+
def self.icon_for(mtype)
|
29
|
+
lookup(mtype,ICON) || lookup(mime_group(mtype),ICON) || lookup('default',ICON)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.asset_class_from_mime_type(mime_type)
|
33
|
+
asset_symbol_from_mime_type(mime_type).to_s.classify
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.asset_symbol_from_mime_type(mime_type)
|
37
|
+
lookup(mime_type,ASSET_CLASS) || lookup(mime_group(mime_type),ASSET_CLASS) || lookup('default',ASSET_CLASS)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.extension_from_mime_type(mime_type)
|
41
|
+
lookup(mime_type,EXT)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.mime_type_from_extension(ext)
|
45
|
+
ext = ext.to_s
|
46
|
+
ext = File.extname(ext).gsub('.','') if ext =~ /\./
|
47
|
+
mimetype = EXTENSIONS[ext]
|
48
|
+
if defined?(MIME::Types)
|
49
|
+
unless MIME::Types.type_for('.'+ext).empty?
|
50
|
+
mimetype ||= MIME::Types.type_for('.'+ext).first.content_type
|
51
|
+
end
|
52
|
+
end
|
53
|
+
mimetype ||= 'application/octet-stream'
|
54
|
+
return mimetype
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# perhaps use http://code.google.com/p/mimetype-fu/
|
59
|
+
# for all this?
|
60
|
+
def self.type_for(filename)
|
61
|
+
self.mime_type_from_extension(filename)
|
62
|
+
# todo: add type_from_file_command if ext doesn't pan out.
|
63
|
+
end
|
64
|
+
|
65
|
+
#def type_from_file_command(filename)
|
66
|
+
# # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
|
67
|
+
# type = (filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
|
68
|
+
# mime_type = (Paperclip.run("file", "-b --mime-type :file", :file => filename).split(':').last.strip rescue "application/x-#{type}")
|
69
|
+
# mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
|
70
|
+
# mime_type
|
71
|
+
#end
|
72
|
+
|
73
|
+
def self.description_from_mime_type(mime_type)
|
74
|
+
lookup(mime_type,DESCRIPTION) || lookup(mime_group(mime_type),DESCRIPTION) || lookup('default',DESCRIPTION)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.compatible_types?(type1, type2)
|
78
|
+
(TYPE_ALIASES[type1] || []).include?(type2)
|
79
|
+
end
|
80
|
+
|
81
|
+
EXT = 0; ICON = 1; ASSET_CLASS = 2; DESCRIPTION = 3;
|
82
|
+
MIME_TYPES = {
|
83
|
+
# mime_type => [file_extension, icon, asset_class, description]
|
84
|
+
'default' => [nil,'default',:asset,'Unknown'],
|
85
|
+
|
86
|
+
'text/' => [:txt,:html,:text_asset, 'Text'],
|
87
|
+
'text/plain' => [:txt,:html,:text_asset, 'Text'],
|
88
|
+
'text/html' => [:html,:html,:text_asset, 'Webpage'],
|
89
|
+
'application/rtf' => [:rtf,:rtf,:text_asset, 'Rich Text'],
|
90
|
+
'text/rtf' => [:rtf,:rtf,:text_asset, 'Rich Text'],
|
91
|
+
'text/sgml' => [:sgml,:xml,nil,'XML'],
|
92
|
+
'text/xml' => [:xml,:xml,nil,'XML'],
|
93
|
+
'text/csv' => [:csv,:spreadsheet,:doc_asset, 'Comma Separated Values'],
|
94
|
+
'text/comma-separated-values' => [:csv,:spreadsheet,:doc_asset, 'Comma Separated Values'],
|
95
|
+
|
96
|
+
'application/pdf' => [:pdf,:pdf,:image_asset, 'Portable Document Format'],
|
97
|
+
'application/bzpdf' => [:pdf,:pdf,:image_asset, 'Portable Document Format'],
|
98
|
+
'application/gzpdf' => [:pdf,:pdf,:image_asset, 'Portable Document Format'],
|
99
|
+
'application/postscript' => [:ps,:pdf,:image_asset,'Postscript'],
|
100
|
+
|
101
|
+
'text/spreadsheet' => [:txt,:spreadsheet,:doc_asset,'Spreadsheet'],
|
102
|
+
'application/gnumeric' => [:gnumeric,:spreadsheet,:doc_asset,'Gnumeric'],
|
103
|
+
'application/kspread' => [:kspread,:spreadsheet,:doc_asset,'KSpread'],
|
104
|
+
|
105
|
+
'application/scribus' => [:scribus,:doc,nil,'Scribus'],
|
106
|
+
'application/abiword' => [:abw,:doc,:doc_asset,'Abiword'],
|
107
|
+
'application/kword' => [:kwd,:doc,:doc_asset,'KWord'],
|
108
|
+
|
109
|
+
|
110
|
+
'application/msword' => [:doc,:msword,:text_asset,'MS Word'],
|
111
|
+
'application/mswrite' => [:doc,:msword,:text_asset,'MS Write'],
|
112
|
+
'application/powerpoint' => [:ppt,:mspowerpoint,:doc_asset,'MS Powerpoint'],
|
113
|
+
'application/excel' => [:xls,:msexcel,:spreadsheet_asset,'MS Excel'],
|
114
|
+
'application/access' => [nil, :msaccess, :doc_asset,'MS Access'],
|
115
|
+
'application/vnd.ms-msword' => [:doc,:msword,:text_asset,'MS Word'],
|
116
|
+
'application/vnd.ms-mswrite' => [:doc,:msword,:text_asset,'MS Write'],
|
117
|
+
'application/vnd.ms-powerpoint' => [:ppt,:mspowerpoint,:doc_asset,'MS Powerpoint'],
|
118
|
+
'application/vnd.ms-excel' => [:xls,:msexcel,:spreadsheet_asset,'MS Excel'],
|
119
|
+
'application/vnd.ms-access' => [nil, :msaccess, :doc_asset,'MS Access'],
|
120
|
+
'application/msword-template' => [:dot,:msword,:text_asset,'MS Word Template'],
|
121
|
+
'application/excel-template' => [:xlt,:msexcel,:spreadsheet_asset,'MS Excel Template'],
|
122
|
+
'application/powerpoint-template' => [:pot,:mspowerpoint,:doc_asset,'MS Powerpoint Template'],
|
123
|
+
|
124
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation' =>
|
125
|
+
[:pptx, :mspowerpoint,:doc_asset,'MS Powerpoint'],
|
126
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation' =>
|
127
|
+
[:pptm, :mspowerpoint,:doc_asset,'MS Powerpoint'],
|
128
|
+
'application/vnd.openxmlformats-officedocument.presentationml.template' =>
|
129
|
+
[:potx,:mspowerpoint,:doc_asset,'MS Powerpoint Template'],
|
130
|
+
|
131
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
|
132
|
+
[:docm,:msword,:text_asset,'MS Word'],
|
133
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
|
134
|
+
[:docx,:msword,:text_asset,'MS Word'],
|
135
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' =>
|
136
|
+
[:dotx,:msword,:text_asset,'MS Word Template'],
|
137
|
+
|
138
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' =>
|
139
|
+
[:xlsm,:msexcel,:spreadsheet_asset,'MS Excel'],
|
140
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' =>
|
141
|
+
[:xlsx,:msexcel,:spreadsheet_asset,'MS Excel'],
|
142
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' =>
|
143
|
+
[:xltx,:msexcel,:spreadsheet_asset,'MS Excel Template'],
|
144
|
+
|
145
|
+
'application/executable' => [nil,:binary,nil,'Program'],
|
146
|
+
'application/ms-dos-executable' => [nil,:binary,nil,'Program'],
|
147
|
+
'application/octet-stream' => [nil,:binary,nil],
|
148
|
+
|
149
|
+
'application/shellscript' => [:sh,:shell,nil,'Script'],
|
150
|
+
'application/ruby' => [:rb,:ruby,nil,'Script'],
|
151
|
+
|
152
|
+
'application/vnd.oasis.opendocument.spreadsheet' =>
|
153
|
+
[:ods,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Spreadsheet'],
|
154
|
+
'application/vnd.oasis.opendocument.formula' =>
|
155
|
+
[nil,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Formula'],
|
156
|
+
'application/vnd.oasis.opendocument.chart' =>
|
157
|
+
[nil,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Chart'],
|
158
|
+
'application/vnd.oasis.opendocument.image' =>
|
159
|
+
[nil,:oo_graphics, :doc_asset, 'OpenDocument Image'],
|
160
|
+
'application/vnd.oasis.opendocument.graphics' =>
|
161
|
+
[:odg,:oo_graphics, :doc_asset, 'OpenDocument Graphics'],
|
162
|
+
'application/vnd.oasis.opendocument.presentation' =>
|
163
|
+
[:odp,:oo_presentation,:doc_asset, 'OpenDocument Presentation'],
|
164
|
+
'application/vnd.oasis.opendocument.database' =>
|
165
|
+
[:odf,:oo_database,:doc_asset, 'OpenDocument Database'],
|
166
|
+
'application/vnd.oasis.opendocument.text-web' =>
|
167
|
+
[:html,:oo_html,:doc_asset, 'OpenDocument Webpage'],
|
168
|
+
'application/vnd.oasis.opendocument.text' =>
|
169
|
+
[:odt,:oo_document,:doc_asset, 'OpenDocument Text'],
|
170
|
+
'application/vnd.oasis.opendocument.text-master' =>
|
171
|
+
[:odm,:oo_document,:doc_asset, 'OpenDocument Master'],
|
172
|
+
|
173
|
+
'application/vnd.oasis.opendocument.presentation-template' =>
|
174
|
+
[:otp,:oo_presentation,:doc_asset, 'OpenDocument Presentation'],
|
175
|
+
'application/vnd.oasis.opendocument.graphics-template' =>
|
176
|
+
[:otg,:oo_graphics,:doc_asset, 'OpenDocument Graphics'],
|
177
|
+
'application/vnd.oasis.opendocument.spreadsheet-template' =>
|
178
|
+
[:ots,:oo_spreadsheet,:spreadsheet_asset, 'OpenDocument Spreadsheet'],
|
179
|
+
'application/vnd.oasis.opendocument.text-template' =>
|
180
|
+
[:ott,:oo_document,:doc_asset, 'OpenDocument Text'],
|
181
|
+
|
182
|
+
'packages/' => [nil,:archive,nil,'Archive'],
|
183
|
+
'multipart/zip' => [:zip,:archive,nil,'Archive'],
|
184
|
+
'multipart/gzip' => [:gzip,:archive,nil,'Archive'],
|
185
|
+
'multipart/tar' => [:tar,:archive,nil,'Archive'],
|
186
|
+
'application/zip' => [:gzip,:archive,nil,'Archive'],
|
187
|
+
'application/gzip' => [:gzip,:archive,nil,'Archive'],
|
188
|
+
'application/rar' => [:rar,:archive,nil,'Archive'],
|
189
|
+
'application/deb' => [:deb,:archive,nil,'Archive'],
|
190
|
+
'application/tar' => [:tar,:archive,nil,'Archive'],
|
191
|
+
'application/stuffit' => [:sit,:archive,nil,'Archive'],
|
192
|
+
'application/compress' => [nil,:archive,nil,'Archive'],
|
193
|
+
'application/zip-compressed' => [:zip,:archive,nil,'Archive'],
|
194
|
+
|
195
|
+
'video/' => [nil,:video,nil,'Video'],
|
196
|
+
|
197
|
+
'audio/' => [nil,:audio,:audio_asset,'Audio'],
|
198
|
+
|
199
|
+
'image/' => [nil,:image,:image_asset,'Image'],
|
200
|
+
'image/jpeg' => [:jpg,:image,:image_asset, 'JPEG Image'],
|
201
|
+
'image/jpg' => [:jpg,:image,:image_asset, 'JPEG Image'],
|
202
|
+
'image/png' => [:png,:image,:png_asset, 'PNG Image'],
|
203
|
+
'image/gif' => [:png,:image,:gif_asset, 'GIF Image'],
|
204
|
+
|
205
|
+
'image/svg+xml' => [:svg,:vector,:svg_asset,'Vector Graphic'],
|
206
|
+
'image/svg+xml-compressed' => [:svg,:vector,:svg_asset,'Vector Graphic'],
|
207
|
+
'application/illustrator' => [:ai,:vector,:image_asset,'Vector Graphic'],
|
208
|
+
'image/bzeps' => [:bzeps,:vector,:image_asset,'Vector Graphic'],
|
209
|
+
'image/eps' => [:eps,:vector,:image_asset,'Vector Graphic'],
|
210
|
+
'image/gzeps' => [:gzeps,:vector,:image_asset,'Vector Graphic'],
|
211
|
+
|
212
|
+
'application/pgp-encrypted' => [nil,:lock,nil,'Crypt'],
|
213
|
+
'application/pgp-signature' => [nil,:lock,nil,'Crypt'],
|
214
|
+
'application/pgp-keys' => [nil,:lock,nil,'Crypt']
|
215
|
+
}.freeze
|
216
|
+
|
217
|
+
#
|
218
|
+
# This extension mapping is used to force certain mime types.
|
219
|
+
# Usually, firefox does pretty good at reporting the correct mime-type,
|
220
|
+
# but IE always fails (firefox fails on ogg). So, we use the MIME::Types
|
221
|
+
# gem to try to get the correct mime from the extension. Sometimes, however,
|
222
|
+
# even this doesn't work. This able will force certain types when
|
223
|
+
# MIME::Types fails or is ambiguous
|
224
|
+
#
|
225
|
+
EXTENSIONS = {
|
226
|
+
'jpg' => 'image/jpeg',
|
227
|
+
'png' => 'image/png',
|
228
|
+
'txt' => 'text/plain',
|
229
|
+
'flv' => 'video/flv',
|
230
|
+
'ogg' => 'audio/ogg',
|
231
|
+
'oga' => 'audio/ogg',
|
232
|
+
'ogv' => 'video/ogg',
|
233
|
+
'pdf' => 'application/pdf',
|
234
|
+
|
235
|
+
'doc' => 'application/msword',
|
236
|
+
'xsl' => 'application/excel',
|
237
|
+
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
238
|
+
'pptm' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
239
|
+
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
240
|
+
'docm' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
241
|
+
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
242
|
+
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
243
|
+
'xlsm' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
244
|
+
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
245
|
+
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
246
|
+
|
247
|
+
'odt' => 'application/vnd.oasis.opendocument.text',
|
248
|
+
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
249
|
+
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
250
|
+
'svg' => 'image/svg+xml',
|
251
|
+
'mod' => 'video/mpeg',
|
252
|
+
|
253
|
+
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
254
|
+
'pptm' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
255
|
+
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
256
|
+
'docm' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
257
|
+
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
258
|
+
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
259
|
+
'xlsm' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
260
|
+
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
261
|
+
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'
|
262
|
+
}.freeze
|
263
|
+
|
264
|
+
#
|
265
|
+
# some types can have multiple names
|
266
|
+
#
|
267
|
+
TYPE_ALIASES = {
|
268
|
+
'image/jpg' => ['image/jpeg'],
|
269
|
+
'image/jpeg' => ['image/jpg']
|
270
|
+
}
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
#
|
6
|
+
# media processing requires a different type of tempfile... because we use command
|
7
|
+
# line tools to process our temp files, these files can't be open and closed by ruby.
|
8
|
+
#
|
9
|
+
# instead, Media::TempFile is used to generate closed files from binary data (for files
|
10
|
+
# to be fed to command line tools), or to generate empty tmp files (for output filenames
|
11
|
+
# to be fed to command line tools).
|
12
|
+
#
|
13
|
+
# We use the Tempfile class for generating these files, but then we always close them
|
14
|
+
# right away. By doing this, we ensure that the temp file will eventually get removed
|
15
|
+
# when the Tempfile gets garbage collected.
|
16
|
+
#
|
17
|
+
|
18
|
+
module Media
|
19
|
+
class TempFile
|
20
|
+
|
21
|
+
def self.tempfile_path
|
22
|
+
::Media::TMP_PATH
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
## INSTANCE METHODS
|
27
|
+
##
|
28
|
+
|
29
|
+
public
|
30
|
+
|
31
|
+
#
|
32
|
+
# data may be one of:
|
33
|
+
#
|
34
|
+
# - FileUpload object: like the kind returned in multibyte encoded file upload forms.
|
35
|
+
# - Pathname object: then load data from the file pointed to by the pathname.
|
36
|
+
# - IO object: read the contents of the io object, copy to tmp file.
|
37
|
+
# - otherwise, dump the contents of the data to the tmp file.
|
38
|
+
#
|
39
|
+
# if data is empty, we generate an empty one.
|
40
|
+
#
|
41
|
+
def initialize(data, content_type=nil)
|
42
|
+
if data.nil?
|
43
|
+
@tmpfile = TempFile.create_from_content_type(content_type)
|
44
|
+
elsif data.respond_to?(:path)
|
45
|
+
# we are dealing with an uploaded file object
|
46
|
+
@tmpfile = TempFile.create_from_file(data.path, content_type, {mode: :move})
|
47
|
+
elsif data.is_a?(StringIO)
|
48
|
+
data.rewind
|
49
|
+
@tmpfile = TempFile.create_from_data(data.read, content_type)
|
50
|
+
elsif data.instance_of?(Pathname)
|
51
|
+
@tmpfile = TempFile.create_from_file(data.to_s, content_type)
|
52
|
+
else
|
53
|
+
@tmpfile = TempFile.create_from_data(data, content_type)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# like initialize, but if given a block, then it yields the TempFile
|
59
|
+
# and also unlinks the file at the end of the block.
|
60
|
+
#
|
61
|
+
def self.open(data, content_type=nil)
|
62
|
+
tmp = TempFile.new(data, content_type)
|
63
|
+
if block_given?
|
64
|
+
begin
|
65
|
+
yield tmp
|
66
|
+
ensure
|
67
|
+
tmp.clear
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
else
|
71
|
+
tmp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def clear
|
76
|
+
# this is not really needed, because the tmp files are deleted as soon as
|
77
|
+
# @tmpfile gets garbage collected.
|
78
|
+
# @tmpfile.unlink
|
79
|
+
end
|
80
|
+
|
81
|
+
def any?
|
82
|
+
@tmpfile.any?
|
83
|
+
end
|
84
|
+
|
85
|
+
def path
|
86
|
+
@tmpfile.path
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s
|
90
|
+
@tmpfile.path
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
## CLASS METHODS
|
95
|
+
##
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
#
|
100
|
+
# creates a tempfile filled with the given binary data
|
101
|
+
#
|
102
|
+
def self.create_from_data(data, content_type=nil)
|
103
|
+
tf = new_for_content_type(content_type)
|
104
|
+
tf.binmode
|
105
|
+
tf.write(data)
|
106
|
+
tf.close
|
107
|
+
tf
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# create an empty temp file with an extension to match the content_type
|
112
|
+
#
|
113
|
+
def self.create_from_content_type(content_type)
|
114
|
+
tf = new_for_content_type(content_type)
|
115
|
+
tf.close
|
116
|
+
tf
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# create a tmp file that is a copy of another file.
|
121
|
+
#
|
122
|
+
def self.create_from_file(filepath, content_type, options = {})
|
123
|
+
tf = new_for_content_type(content_type)
|
124
|
+
tf.close
|
125
|
+
if options[:mode] == :move
|
126
|
+
FileUtils.mv filepath, tf.path
|
127
|
+
else
|
128
|
+
FileUtils.cp filepath, tf.path
|
129
|
+
end
|
130
|
+
tf
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.new_for_content_type(content_type)
|
134
|
+
Tempfile.new content_type_basename(content_type),
|
135
|
+
tempfile_path,
|
136
|
+
mode: 022
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# create a filename with a file extension from the content_type
|
141
|
+
#
|
142
|
+
def self.content_type_basename(content_type)
|
143
|
+
if content_type
|
144
|
+
extension = Media::MimeType.extension_from_mime_type(content_type) || :bin
|
145
|
+
['media_temp_file', ".#{extension}"]
|
146
|
+
else
|
147
|
+
'media_temp_file'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|