aspera-cli 4.14.0 → 4.16.0
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +29 -3
- data/CHANGELOG.md +300 -185
- data/CONTRIBUTING.md +74 -23
- data/README.md +2346 -1619
- data/bin/ascli +16 -25
- data/bin/asession +15 -15
- data/examples/dascli +2 -2
- data/examples/proxy.pac +1 -1
- data/lib/aspera/aoc.rb +216 -150
- data/lib/aspera/ascmd.rb +25 -18
- data/lib/aspera/assert.rb +45 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +51 -16
- data/lib/aspera/cli/formatter.rb +276 -174
- data/lib/aspera/cli/hints.rb +81 -0
- data/lib/aspera/cli/main.rb +114 -147
- data/lib/aspera/cli/manager.rb +181 -136
- data/lib/aspera/cli/plugin.rb +82 -64
- data/lib/aspera/cli/plugins/alee.rb +0 -1
- data/lib/aspera/cli/plugins/aoc.rb +327 -331
- data/lib/aspera/cli/plugins/ats.rb +12 -8
- data/lib/aspera/cli/plugins/bss.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +575 -439
- data/lib/aspera/cli/plugins/console.rb +40 -0
- data/lib/aspera/cli/plugins/cos.rb +4 -5
- data/lib/aspera/cli/plugins/faspex.rb +111 -92
- data/lib/aspera/cli/plugins/faspex5.rb +245 -182
- data/lib/aspera/cli/plugins/node.rb +239 -160
- data/lib/aspera/cli/plugins/orchestrator.rb +56 -19
- data/lib/aspera/cli/plugins/preview.rb +54 -38
- data/lib/aspera/cli/plugins/server.rb +63 -20
- data/lib/aspera/cli/plugins/shares.rb +64 -38
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +64 -67
- data/lib/aspera/cli/transfer_progress.rb +73 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +3 -1
- data/lib/aspera/command_line_builder.rb +27 -22
- data/lib/aspera/cos_node.rb +6 -4
- data/lib/aspera/coverage.rb +22 -0
- data/lib/aspera/data_repository.rb +33 -2
- data/lib/aspera/environment.rb +21 -8
- data/lib/aspera/fasp/agent_alpha.rb +116 -0
- data/lib/aspera/fasp/agent_base.rb +40 -76
- data/lib/aspera/fasp/agent_connect.rb +21 -22
- data/lib/aspera/fasp/agent_direct.rb +169 -179
- data/lib/aspera/fasp/agent_httpgw.rb +200 -195
- data/lib/aspera/fasp/agent_node.rb +43 -35
- data/lib/aspera/fasp/agent_trsdk.rb +124 -41
- data/lib/aspera/fasp/error_info.rb +2 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +89 -191
- data/lib/aspera/fasp/management.rb +249 -0
- data/lib/aspera/fasp/parameters.rb +86 -47
- data/lib/aspera/fasp/parameters.yaml +75 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/resume_policy.rb +7 -5
- data/lib/aspera/fasp/sync.rb +273 -0
- data/lib/aspera/fasp/transfer_spec.rb +10 -8
- data/lib/aspera/fasp/uri.rb +6 -6
- data/lib/aspera/faspex_gw.rb +11 -8
- data/lib/aspera/faspex_postproc.rb +8 -7
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/id_generator.rb +3 -1
- data/lib/aspera/json_rpc.rb +51 -0
- data/lib/aspera/keychain/encrypted_hash.rb +46 -11
- data/lib/aspera/keychain/macos_security.rb +15 -13
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +61 -19
- data/lib/aspera/nagios.rb +7 -2
- data/lib/aspera/node.rb +105 -21
- data/lib/aspera/node_simulator.rb +214 -0
- data/lib/aspera/oauth.rb +57 -36
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +13 -14
- data/lib/aspera/persistency_folder.rb +5 -4
- data/lib/aspera/preview/file_types.rb +56 -268
- data/lib/aspera/preview/generator.rb +28 -39
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +36 -16
- data/lib/aspera/preview/utils.rb +23 -29
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +127 -80
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +16 -14
- data/lib/aspera/rest_errors_aspera.rb +39 -34
- data/lib/aspera/secret_hider.rb +18 -17
- data/lib/aspera/ssh.rb +10 -5
- data/lib/aspera/temp_file_manager.rb +11 -4
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +11 -5
- data.tar.gz.sig +0 -0
- metadata +108 -39
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/fasp/listener.rb +0 -13
- data/lib/aspera/sync.rb +0 -213
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'aspera/log'
|
|
4
4
|
require 'singleton'
|
|
5
|
+
require 'mime/types'
|
|
5
6
|
|
|
6
7
|
module Aspera
|
|
7
8
|
module Preview
|
|
@@ -11,266 +12,41 @@ module Aspera
|
|
|
11
12
|
# values for conversion_type : input format
|
|
12
13
|
CONVERSION_TYPES = %i[image office pdf plaintext video].freeze
|
|
13
14
|
|
|
14
|
-
#
|
|
15
|
+
# special cases for mime types
|
|
15
16
|
# spellchecker:disable
|
|
16
17
|
SUPPORTED_MIME_TYPES = {
|
|
17
|
-
'application/json'
|
|
18
|
-
'
|
|
19
|
-
'application/
|
|
20
|
-
'
|
|
21
|
-
'application/
|
|
22
|
-
'application/
|
|
23
|
-
'application/
|
|
24
|
-
'application/
|
|
25
|
-
'application/
|
|
26
|
-
'application/
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'application/
|
|
34
|
-
'application/
|
|
35
|
-
'application/vnd.
|
|
36
|
-
'application/vnd.
|
|
37
|
-
'application/vnd.
|
|
38
|
-
'application/vnd.
|
|
39
|
-
'application/vnd.
|
|
40
|
-
'application/
|
|
41
|
-
'application/
|
|
42
|
-
'application/
|
|
43
|
-
'application/
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'application/vnd.oasis.opendocument.text' => :office,
|
|
48
|
-
'application/vnd.oasis.opendocument.text-template' => :office,
|
|
49
|
-
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => :office,
|
|
50
|
-
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => :office,
|
|
51
|
-
'application/vnd.openxmlformats-officedocument.presentationml.template' => :office,
|
|
52
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => :office,
|
|
53
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => :office,
|
|
54
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => :office,
|
|
55
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => :office,
|
|
56
|
-
'application/vnd.palm' => :office,
|
|
57
|
-
'application/vnd.sun.xml.calc' => :office,
|
|
58
|
-
'application/vnd.sun.xml.calc.template' => :office,
|
|
59
|
-
'application/vnd.sun.xml.draw' => :office,
|
|
60
|
-
'application/vnd.sun.xml.draw.template' => :office,
|
|
61
|
-
'application/vnd.sun.xml.impress' => :office,
|
|
62
|
-
'application/vnd.sun.xml.impress.template' => :office,
|
|
63
|
-
'application/vnd.sun.xml.math' => :office,
|
|
64
|
-
'application/vnd.sun.xml.writer' => :office,
|
|
65
|
-
'application/vnd.sun.xml.writer.template' => :office,
|
|
66
|
-
'application/vnd.wordperfect' => :office,
|
|
67
|
-
'application/x-abiword' => :office,
|
|
68
|
-
'application/x-director' => :image,
|
|
69
|
-
'application/x-font-type1' => :image,
|
|
70
|
-
'application/x-msmetafile' => :image,
|
|
71
|
-
'application/x-mspublisher' => :office,
|
|
72
|
-
'application/x-xfig' => :image,
|
|
73
|
-
'audio/ogg' => :video,
|
|
74
|
-
'font/ttf' => :image,
|
|
75
|
-
'image/bmp' => :image,
|
|
76
|
-
'image/cgm' => :image,
|
|
77
|
-
'image/gif' => :image,
|
|
78
|
-
'image/jpeg' => :image,
|
|
79
|
-
'image/png' => :image,
|
|
80
|
-
'image/sgi' => :image,
|
|
81
|
-
'image/svg+xml' => :image,
|
|
82
|
-
'image/tiff' => :image,
|
|
83
|
-
'image/vnd.adobe.photoshop' => :image,
|
|
84
|
-
'image/vnd.djvu' => :image,
|
|
85
|
-
'image/vnd.dxf' => :office,
|
|
86
|
-
'image/vnd.fpx' => :image,
|
|
87
|
-
'image/vnd.ms-photo' => :image,
|
|
88
|
-
'image/vnd.wap.wbmp' => :image,
|
|
89
|
-
'image/webp' => :image,
|
|
90
|
-
'image/x-cmx' => :office,
|
|
91
|
-
'image/x-freehand' => :office,
|
|
92
|
-
'image/x-icon' => :image,
|
|
93
|
-
'image/x-mrsid-image' => :image,
|
|
94
|
-
'image/x-pcx' => :image,
|
|
95
|
-
'image/x-pict' => :office,
|
|
96
|
-
'image/x-portable-anymap' => :image,
|
|
97
|
-
'image/x-portable-bitmap' => :image,
|
|
98
|
-
'image/x-portable-graymap' => :image,
|
|
99
|
-
'image/x-portable-pixmap' => :image,
|
|
100
|
-
'image/x-rgb' => :image,
|
|
101
|
-
'image/x-tga' => :image,
|
|
102
|
-
'image/x-xbitmap' => :image,
|
|
103
|
-
'image/x-xpixmap' => :image,
|
|
104
|
-
'image/x-xwindowdump' => :image,
|
|
105
|
-
'text/csv' => :office,
|
|
106
|
-
'text/html' => :office,
|
|
107
|
-
'text/plain' => :plaintext,
|
|
108
|
-
'text/troff' => :image,
|
|
109
|
-
'video/h261' => :video,
|
|
110
|
-
'video/h263' => :video,
|
|
111
|
-
'video/h264' => :video,
|
|
112
|
-
'video/mp4' => :video,
|
|
113
|
-
'video/mpeg' => :video,
|
|
114
|
-
'video/quicktime' => :video,
|
|
115
|
-
'video/x-flv' => :video,
|
|
116
|
-
'video/x-m4v' => :video,
|
|
117
|
-
'video/x-matroska' => :video,
|
|
118
|
-
'video/x-mng' => :image,
|
|
119
|
-
'video/x-ms-wmv' => :video,
|
|
120
|
-
'video/x-msvideo' => :video}.freeze
|
|
18
|
+
'application/json' => :plaintext,
|
|
19
|
+
'text/plain' => :plaintext,
|
|
20
|
+
'application/pdf' => :pdf,
|
|
21
|
+
'audio/ogg' => :video,
|
|
22
|
+
'application/mxf' => :video,
|
|
23
|
+
'application/mac-binhex40' => :office,
|
|
24
|
+
'application/msword' => :office,
|
|
25
|
+
'application/rtf' => :office,
|
|
26
|
+
'application/x-abiword' => :office,
|
|
27
|
+
'application/x-mspublisher' => :office,
|
|
28
|
+
'image/vnd.dxf' => :office,
|
|
29
|
+
'image/x-cmx' => :office,
|
|
30
|
+
'image/x-freehand' => :office,
|
|
31
|
+
'image/x-pict' => :office,
|
|
32
|
+
'text/csv' => :office,
|
|
33
|
+
'text/html' => :office,
|
|
34
|
+
'application/dicom' => :image,
|
|
35
|
+
'application/postscript' => :image,
|
|
36
|
+
'application/vnd.3gpp.pic-bw-small' => :image,
|
|
37
|
+
'application/vnd.hp-hpgl' => :image,
|
|
38
|
+
'application/vnd.hp-pcl' => :image,
|
|
39
|
+
'application/vnd.mobius.msl' => :image,
|
|
40
|
+
'application/vnd.mophun.certificate' => :image,
|
|
41
|
+
'application/x-director' => :image,
|
|
42
|
+
'application/x-font-type1' => :image,
|
|
43
|
+
'application/x-msmetafile' => :image,
|
|
44
|
+
'application/x-xfig' => :image,
|
|
45
|
+
'font/ttf' => :image,
|
|
46
|
+
'text/troff' => :image,
|
|
47
|
+
'video/x-mng' => :image}.freeze
|
|
121
48
|
|
|
122
|
-
|
|
123
|
-
SUPPORTED_EXTENSIONS = {
|
|
124
|
-
'aai' => :image,
|
|
125
|
-
'art' => :image,
|
|
126
|
-
'arw' => :image,
|
|
127
|
-
'avs' => :image,
|
|
128
|
-
'bmp2' => :image,
|
|
129
|
-
'bmp3' => :image,
|
|
130
|
-
'bpg' => :image,
|
|
131
|
-
'cals' => :image,
|
|
132
|
-
'cdr' => :office,
|
|
133
|
-
'cin' => :image,
|
|
134
|
-
'clipboard' => :image,
|
|
135
|
-
'cmyk' => :image,
|
|
136
|
-
'cmyka' => :image,
|
|
137
|
-
'cr2' => :image,
|
|
138
|
-
'crw' => :image,
|
|
139
|
-
'cur' => :image,
|
|
140
|
-
'cut' => :image,
|
|
141
|
-
'cwk' => :office,
|
|
142
|
-
'dbf' => :office,
|
|
143
|
-
'dcm' => :image,
|
|
144
|
-
'dcx' => :image,
|
|
145
|
-
'dds' => :image,
|
|
146
|
-
'dib' => :image,
|
|
147
|
-
'dif' => :office,
|
|
148
|
-
'divx' => :video,
|
|
149
|
-
'dng' => :image,
|
|
150
|
-
'docx' => :office,
|
|
151
|
-
'dpx' => :image,
|
|
152
|
-
'epdf' => :image,
|
|
153
|
-
'epi' => :image,
|
|
154
|
-
'eps2' => :image,
|
|
155
|
-
'eps3' => :image,
|
|
156
|
-
'epsf' => :image,
|
|
157
|
-
'epsi' => :image,
|
|
158
|
-
'ept' => :image,
|
|
159
|
-
'exr' => :image,
|
|
160
|
-
'fax' => :image,
|
|
161
|
-
'fb2' => :office,
|
|
162
|
-
'fits' => :image,
|
|
163
|
-
'fodg' => :office,
|
|
164
|
-
'fodp' => :office,
|
|
165
|
-
'fods' => :office,
|
|
166
|
-
'fodt' => :office,
|
|
167
|
-
'gplt' => :image,
|
|
168
|
-
'gray' => :image,
|
|
169
|
-
'hdr' => :image,
|
|
170
|
-
'hpw' => :office,
|
|
171
|
-
'hrz' => :image,
|
|
172
|
-
'info' => :image,
|
|
173
|
-
'inline' => :image,
|
|
174
|
-
'j2c' => :image,
|
|
175
|
-
'j2k' => :image,
|
|
176
|
-
'jbig' => :image,
|
|
177
|
-
'jng' => :image,
|
|
178
|
-
'jp2' => :image,
|
|
179
|
-
'jpt' => :image,
|
|
180
|
-
'jxr' => :image,
|
|
181
|
-
'key' => :office,
|
|
182
|
-
'log' => :plaintext,
|
|
183
|
-
'mat' => :image,
|
|
184
|
-
'mcw' => :office,
|
|
185
|
-
'met' => :office,
|
|
186
|
-
'miff' => :image,
|
|
187
|
-
'mml' => :office,
|
|
188
|
-
'mono' => :image,
|
|
189
|
-
'mpr' => :image,
|
|
190
|
-
'mrsid' => :image,
|
|
191
|
-
'mrw' => :image,
|
|
192
|
-
'mtv' => :image,
|
|
193
|
-
'mvg' => :image,
|
|
194
|
-
'mw' => :office,
|
|
195
|
-
'mwd' => :office,
|
|
196
|
-
'mxf' => :video,
|
|
197
|
-
'nef' => :image,
|
|
198
|
-
'numbers' => :office,
|
|
199
|
-
'orf' => :image,
|
|
200
|
-
'otb' => :image,
|
|
201
|
-
'p7' => :image,
|
|
202
|
-
'pages' => :office,
|
|
203
|
-
'palm' => :image,
|
|
204
|
-
'pam' => :image,
|
|
205
|
-
'pcd' => :image,
|
|
206
|
-
'pcds' => :image,
|
|
207
|
-
'pdf' => :pdf,
|
|
208
|
-
'pef' => :image,
|
|
209
|
-
'picon' => :image,
|
|
210
|
-
'pict' => :image,
|
|
211
|
-
'pix' => :image,
|
|
212
|
-
'pm' => :office,
|
|
213
|
-
'pm6' => :office,
|
|
214
|
-
'pmd' => :office,
|
|
215
|
-
'png00' => :image,
|
|
216
|
-
'png24' => :image,
|
|
217
|
-
'png32' => :image,
|
|
218
|
-
'png48' => :image,
|
|
219
|
-
'png64' => :image,
|
|
220
|
-
'png8' => :image,
|
|
221
|
-
'ps2' => :image,
|
|
222
|
-
'ps3' => :image,
|
|
223
|
-
'ptif' => :image,
|
|
224
|
-
'pwp' => :image,
|
|
225
|
-
'rad' => :image,
|
|
226
|
-
'raf' => :image,
|
|
227
|
-
'rfg' => :image,
|
|
228
|
-
'rgba' => :image,
|
|
229
|
-
'rla' => :image,
|
|
230
|
-
'rle' => :image,
|
|
231
|
-
'sct' => :image,
|
|
232
|
-
'sfw' => :image,
|
|
233
|
-
'sgf' => :office,
|
|
234
|
-
'sgv' => :office,
|
|
235
|
-
'slk' => :office,
|
|
236
|
-
'sparse-color' => :image,
|
|
237
|
-
'sun' => :image,
|
|
238
|
-
'svm' => :office,
|
|
239
|
-
'sylk' => :office,
|
|
240
|
-
'tim' => :image,
|
|
241
|
-
'txt' => :plaintext,
|
|
242
|
-
'uil' => :image,
|
|
243
|
-
'uof' => :office,
|
|
244
|
-
'uop' => :office,
|
|
245
|
-
'uos' => :office,
|
|
246
|
-
'uot' => :office,
|
|
247
|
-
'uyvy' => :image,
|
|
248
|
-
'vds' => :office,
|
|
249
|
-
'vdx' => :office,
|
|
250
|
-
'vicar' => :image,
|
|
251
|
-
'viff' => :image,
|
|
252
|
-
'vsdx' => :office,
|
|
253
|
-
'webm' => :video,
|
|
254
|
-
'wb2' => :office,
|
|
255
|
-
'wk1' => :office,
|
|
256
|
-
'wk3' => :office,
|
|
257
|
-
'wn' => :office,
|
|
258
|
-
'wpg' => :image,
|
|
259
|
-
'wq1' => :office,
|
|
260
|
-
'wq2' => :office,
|
|
261
|
-
'x' => :image,
|
|
262
|
-
'x3f' => :image,
|
|
263
|
-
'xcf' => :image,
|
|
264
|
-
'xlk' => :office,
|
|
265
|
-
'xlsx' => :office,
|
|
266
|
-
'xls' => :office,
|
|
267
|
-
'ycbcr' => :image,
|
|
268
|
-
'ycbcra' => :image,
|
|
269
|
-
'yuv' => :image,
|
|
270
|
-
'zabw' => :office}.freeze
|
|
271
|
-
# spellchecker:enable
|
|
272
|
-
|
|
273
|
-
private_constant :SUPPORTED_MIME_TYPES, :SUPPORTED_EXTENSIONS
|
|
49
|
+
private_constant :SUPPORTED_MIME_TYPES
|
|
274
50
|
|
|
275
51
|
# @attr use_mimemagic [bool] true to use mimemagic to determine real mime type based on file content
|
|
276
52
|
attr_accessor :use_mimemagic
|
|
@@ -279,16 +55,26 @@ module Aspera
|
|
|
279
55
|
@use_mimemagic = false
|
|
280
56
|
end
|
|
281
57
|
|
|
58
|
+
# @param mimetype [String] mime type
|
|
59
|
+
# @return file type, one of enum CONVERSION_TYPES
|
|
60
|
+
def mime_to_type(mimetype)
|
|
61
|
+
return SUPPORTED_MIME_TYPES[mimetype] if SUPPORTED_MIME_TYPES.key?(mimetype)
|
|
62
|
+
return :office if mimetype.start_with?('application/vnd.')
|
|
63
|
+
return :video if mimetype.start_with?('video/')
|
|
64
|
+
return :image if mimetype.start_with?('image/')
|
|
65
|
+
return nil
|
|
66
|
+
end
|
|
67
|
+
|
|
282
68
|
# use mime magic to find mime type based on file content (magic numbers)
|
|
283
|
-
def
|
|
284
|
-
# moved here, as mimemagic can cause installation issues
|
|
69
|
+
def file_to_mime(filepath)
|
|
70
|
+
# moved here, as `mimemagic` can cause installation issues
|
|
285
71
|
require 'mimemagic'
|
|
286
72
|
require 'mimemagic/version'
|
|
287
73
|
require 'mimemagic/overlay' if MimeMagic::VERSION.start_with?('0.3.')
|
|
288
74
|
# check magic number inside file (empty string if not found)
|
|
289
75
|
detected_mime = MimeMagic.by_magic(File.open(filepath)).to_s
|
|
290
76
|
# check extension only
|
|
291
|
-
if
|
|
77
|
+
if mime_to_type(detected_mime).nil?
|
|
292
78
|
Log.log.debug{"no conversion for #{detected_mime}, trying extension"}
|
|
293
79
|
detected_mime = MimeMagic.by_extension(File.extname(filepath)).to_s
|
|
294
80
|
end
|
|
@@ -297,18 +83,19 @@ module Aspera
|
|
|
297
83
|
return detected_mime
|
|
298
84
|
end
|
|
299
85
|
|
|
300
|
-
# return file type, one of enum CONVERSION_TYPES
|
|
301
86
|
# @param filepath [String] full path to file
|
|
302
|
-
# @param mimetype [String] provided by node
|
|
87
|
+
# @param mimetype [String] provided by node API
|
|
88
|
+
# @return file type, one of enum CONVERSION_TYPES
|
|
89
|
+
# @raise [RuntimeError] if no conversion type found
|
|
303
90
|
def conversion_type(filepath, mimetype)
|
|
304
91
|
Log.log.debug{"conversion_type(#{filepath},m=#{mimetype},t=#{@use_mimemagic})"}
|
|
305
92
|
# 1- get type from provided mime type, using local mapping
|
|
306
|
-
conversion_type =
|
|
93
|
+
conversion_type = mime_to_type(mimetype) if !mimetype.nil?
|
|
307
94
|
# 2- else, from computed mime type (if available)
|
|
308
95
|
if conversion_type.nil? && @use_mimemagic
|
|
309
|
-
detected_mime =
|
|
96
|
+
detected_mime = file_to_mime(filepath)
|
|
310
97
|
if !detected_mime.nil?
|
|
311
|
-
conversion_type =
|
|
98
|
+
conversion_type = mime_to_type(detected_mime)
|
|
312
99
|
if !mimetype.nil?
|
|
313
100
|
if mimetype.eql?(detected_mime)
|
|
314
101
|
Log.log.debug('matching mime type per magic number')
|
|
@@ -320,9 +107,10 @@ module Aspera
|
|
|
320
107
|
end
|
|
321
108
|
end
|
|
322
109
|
# 3- else, from extensions, using local mapping
|
|
323
|
-
|
|
324
|
-
conversion_type =
|
|
325
|
-
|
|
110
|
+
mime_by_ext = MIME::Types.of(File.basename(filepath)).first
|
|
111
|
+
conversion_type = mime_to_type(mime_by_ext.to_s) if conversion_type.nil? && !mime_by_ext.nil?
|
|
112
|
+
raise "no conversion type found for #{File.basename(filepath)}" if conversion_type.nil?
|
|
113
|
+
Log.log.trace1{"conversion_type(#{File.basename(filepath)}): #{conversion_type.class.name} [#{conversion_type}]"}
|
|
326
114
|
return conversion_type
|
|
327
115
|
end
|
|
328
116
|
end
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# ffmpeg options:
|
|
4
4
|
# spellchecker:ignore pauseframes libx264 trunc bufsize muxer apng libmp3lame maxrate posterize movflags faststart
|
|
5
|
-
# spellchecker:ignore palettegen paletteuse pointsize bordercolor repage lanczos
|
|
5
|
+
# spellchecker:ignore palettegen paletteuse pointsize bordercolor repage lanczos unoconv optipng reencode conv transframes
|
|
6
6
|
|
|
7
7
|
require 'open3'
|
|
8
8
|
require 'aspera/preview/options'
|
|
9
9
|
require 'aspera/preview/utils'
|
|
10
10
|
require 'aspera/preview/file_types'
|
|
11
|
+
require 'aspera/log'
|
|
12
|
+
require 'aspera/assert'
|
|
11
13
|
|
|
12
14
|
module Aspera
|
|
13
15
|
module Preview
|
|
@@ -19,6 +21,7 @@ module Aspera
|
|
|
19
21
|
FFMPEG_OPTIONS_LIST = %w[in out].freeze
|
|
20
22
|
|
|
21
23
|
# CLI needs to know conversion type to know if need skip it
|
|
24
|
+
# one of CONVERSION_TYPES
|
|
22
25
|
attr_reader :conversion_type
|
|
23
26
|
|
|
24
27
|
# @param src source file path
|
|
@@ -32,55 +35,40 @@ module Aspera
|
|
|
32
35
|
# -> preview_format is one of Generator::PREVIEW_FORMATS
|
|
33
36
|
# the conversion video->mp4 is implemented in methods: convert_video_to_mp4_using_<video_conversion>
|
|
34
37
|
# -> conversion method is one of Generator::VIDEO_CONVERSION_METHODS
|
|
35
|
-
def initialize(
|
|
36
|
-
@options = options
|
|
38
|
+
def initialize(src, dst, options, main_temp_dir, api_mime_type)
|
|
37
39
|
@source_file_path = src
|
|
38
40
|
@destination_file_path = dst
|
|
41
|
+
@options = options
|
|
39
42
|
@temp_folder = File.join(main_temp_dir, @source_file_path.split('/').last.gsub(/\s/, '_').gsub(/\W/, ''))
|
|
40
43
|
# extract preview format from extension of target file
|
|
41
|
-
@
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# combination of: conversion type and output format (and video_conversion for video)
|
|
47
|
-
def processing_method_symb
|
|
48
|
-
name = "convert_#{@conversion_type}_to_#{@preview_format_symb}"
|
|
49
|
-
if @conversion_type.eql?(:video)
|
|
50
|
-
case @preview_format_symb
|
|
44
|
+
@preview_format_sym = File.extname(@destination_file_path).gsub(/^\./, '').to_sym
|
|
45
|
+
conversion_type = FileTypes.instance.conversion_type(@source_file_path, api_mime_type)
|
|
46
|
+
@processing_method = "convert_#{conversion_type}_to_#{@preview_format_sym}"
|
|
47
|
+
if conversion_type.eql?(:video)
|
|
48
|
+
case @preview_format_sym
|
|
51
49
|
when :mp4
|
|
52
|
-
|
|
50
|
+
@processing_method = "#{@processing_method}_using_#{@options.video_conversion}"
|
|
53
51
|
when :png
|
|
54
|
-
|
|
52
|
+
@processing_method = "#{@processing_method}_using_#{@options.video_png_conv}"
|
|
55
53
|
end
|
|
56
54
|
end
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# @return true if conversion is supported.
|
|
62
|
-
# for instance, plaintext to mp4 is not supported
|
|
63
|
-
def supported?
|
|
64
|
-
return false if @conversion_type.nil?
|
|
65
|
-
return respond_to?(processing_method_symb, true)
|
|
55
|
+
@processing_method = @processing_method.to_sym
|
|
56
|
+
Log.log.debug{"method: #{@processing_method}"}
|
|
57
|
+
assert(respond_to?(@processing_method, true)){"no processing know for #{conversion_type} -> #{@preview_format_sym}"}
|
|
66
58
|
end
|
|
67
59
|
|
|
68
60
|
# create preview as specified in constructor
|
|
69
61
|
def generate
|
|
70
|
-
|
|
71
|
-
method_symb = processing_method_symb
|
|
72
|
-
Log.log.info{"#{@source_file_path}->#{@destination_file_path} (#{method_symb})"}
|
|
62
|
+
Log.log.info{"#{@source_file_path}->#{@destination_file_path} (#{@processing_method})"}
|
|
73
63
|
begin
|
|
74
|
-
send(
|
|
64
|
+
send(@processing_method)
|
|
75
65
|
# check that generated size does not exceed maximum
|
|
76
66
|
result_size = File.size(@destination_file_path)
|
|
77
|
-
if result_size > @options.max_size
|
|
78
|
-
Log.log.warn{"preview size exceeds maximum allowed #{result_size} > #{@options.max_size}"}
|
|
79
|
-
end
|
|
67
|
+
Log.log.warn{"preview size exceeds maximum allowed #{result_size} > #{@options.max_size}"} if result_size > @options.max_size
|
|
80
68
|
rescue StandardError => e
|
|
81
69
|
Log.log.error{"Ignoring: #{e.message}"}
|
|
82
70
|
Log.log.debug(e.backtrace.join("\n").red)
|
|
83
|
-
FileUtils.cp(File.expand_path(@
|
|
71
|
+
FileUtils.cp(File.expand_path(@preview_format_sym.eql?(:mp4) ? 'video_error.png' : 'image_error.png', File.dirname(__FILE__)), @destination_file_path)
|
|
84
72
|
ensure
|
|
85
73
|
FileUtils.rm_rf(@temp_folder)
|
|
86
74
|
end
|
|
@@ -100,7 +88,7 @@ module Aspera
|
|
|
100
88
|
# @param total_count of parts
|
|
101
89
|
# @param index of part (start at 1)
|
|
102
90
|
def get_offset(duration, start_offset, total_count, index)
|
|
103
|
-
|
|
91
|
+
assert_type(duration, Float){'duration'}
|
|
104
92
|
return start_offset + ((index - 1) * (duration - start_offset) / total_count)
|
|
105
93
|
end
|
|
106
94
|
|
|
@@ -134,8 +122,8 @@ module Aspera
|
|
|
134
122
|
# generate n clips starting at offset
|
|
135
123
|
def convert_video_to_mp4_using_clips
|
|
136
124
|
p_duration = Utils.video_get_duration(@source_file_path)
|
|
137
|
-
|
|
138
|
-
File.open(
|
|
125
|
+
file_list_file = File.join(this_tmpdir, 'clip_files.txt')
|
|
126
|
+
File.open(file_list_file, 'w+') do |f|
|
|
139
127
|
1.upto(@options.clips_count.to_i) do |i|
|
|
140
128
|
offset_seconds = get_offset(p_duration, @options.video_start_sec.to_i, @options.clips_count.to_i, i)
|
|
141
129
|
tmp_file_name = format('clip%04d.mp4', i)
|
|
@@ -153,19 +141,20 @@ module Aspera
|
|
|
153
141
|
end
|
|
154
142
|
# concat clips
|
|
155
143
|
Utils.ffmpeg(
|
|
156
|
-
in_f:
|
|
144
|
+
in_f: file_list_file,
|
|
157
145
|
in_p: ['-f', 'concat'],
|
|
158
146
|
out_f: @destination_file_path,
|
|
159
147
|
out_p: ['-codec', 'copy'])
|
|
148
|
+
File.delete(file_list_file)
|
|
160
149
|
end
|
|
161
150
|
|
|
162
151
|
# do a simple re-encoding
|
|
163
152
|
def convert_video_to_mp4_using_reencode
|
|
164
153
|
options = @options.reencode_ffmpeg
|
|
165
|
-
|
|
154
|
+
assert_type(options, Hash){'reencode_ffmpeg'}
|
|
166
155
|
options.each do |k, v|
|
|
167
|
-
|
|
168
|
-
|
|
156
|
+
assert_values(k, FFMPEG_OPTIONS_LIST){'key'}
|
|
157
|
+
assert_type(v, Array){k}
|
|
169
158
|
end
|
|
170
159
|
Utils.ffmpeg(
|
|
171
160
|
in_f: @source_file_path,
|
|
@@ -2,36 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
# cspell:words Magick MAGICKCORE ITERM mintty winsize termcap
|
|
4
4
|
|
|
5
|
-
require 'rmagick' # https://rmagick.github.io/index.html
|
|
6
5
|
require 'rainbow'
|
|
7
6
|
require 'io/console'
|
|
8
7
|
module Aspera
|
|
9
8
|
module Preview
|
|
10
|
-
#
|
|
9
|
+
# Display a picture in the terminal, either using coloured characters or iTerm2
|
|
11
10
|
class Terminal
|
|
12
|
-
# quantum depth is 8 or 16: convert xc: -format "%q" info:
|
|
13
11
|
# Rainbow only supports 8-bit colors
|
|
14
|
-
|
|
15
|
-
ITERM_NAMES = %w[iTerm WezTerm mintty].freeze
|
|
12
|
+
# env vars to detect terminal type
|
|
16
13
|
TERM_ENV_VARS = %w[TERM_PROGRAM LC_TERMINAL].freeze
|
|
17
|
-
|
|
14
|
+
# terminal names that support iTerm2 image display
|
|
15
|
+
ITERM_NAMES = %w[iTerm WezTerm mintty].freeze
|
|
16
|
+
# TODO: retrieve terminal font ratio using some termcap ?
|
|
17
|
+
# ratio = font height / font width
|
|
18
|
+
DEFAULT_FONT_RATIO = 32.0 / 14.0
|
|
19
|
+
private_constant :TERM_ENV_VARS, :ITERM_NAMES, :DEFAULT_FONT_RATIO
|
|
18
20
|
class << self
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
# @return [String] the image as text, or the iTerm2 escape sequence
|
|
22
|
+
# @param blob [String] the image as a binary string
|
|
23
|
+
# @param reserve [Integer] number of lines to reserve for other text than the image
|
|
24
|
+
# @param text [Boolean] true to display the image as text, false to use iTerm2
|
|
25
|
+
# @param double [Boolean] true to use colors on half lines, false to use colors on full lines
|
|
26
|
+
# @param font_ratio [Float] ratio = font height / font width
|
|
27
|
+
def build(blob, reserve: 3, text: false, double: true, font_ratio: DEFAULT_FONT_RATIO)
|
|
28
|
+
return iterm_display_image(blob) if iterm_supported? && !text
|
|
29
|
+
begin
|
|
30
|
+
# do not require statically, as the package is optional
|
|
31
|
+
require 'rmagick' # https://rmagick.github.io/index.html
|
|
32
|
+
rescue LoadError => e
|
|
33
|
+
Log.log.error('Install missing gem: gem install rmagick')
|
|
34
|
+
# fallback to iterm, if supported
|
|
35
|
+
return iterm_display_image(blob) if iterm_supported?
|
|
36
|
+
Log.log.error('Cant display picture.')
|
|
37
|
+
raise e
|
|
38
|
+
end
|
|
21
39
|
image = Magick::ImageList.new.from_blob(blob)
|
|
22
40
|
(term_rows, term_columns) = IO.console.winsize
|
|
23
|
-
term_rows -=
|
|
41
|
+
term_rows -= reserve
|
|
24
42
|
# compute scaling to fit terminal
|
|
25
43
|
fit_term_ratio = [term_rows / image.rows.to_f, term_columns / image.columns.to_f].min
|
|
26
|
-
|
|
27
|
-
font_ratio = 1.7
|
|
28
|
-
height_ratio = double_precision ? 2.0 : 1.0
|
|
44
|
+
height_ratio = double ? 2.0 : 1.0
|
|
29
45
|
image = image.scale((image.columns * fit_term_ratio * font_ratio).to_i, (image.rows * fit_term_ratio * height_ratio).to_i)
|
|
46
|
+
# quantum depth is 8 or 16, see: `convert xc: -format "%q" info:`
|
|
47
|
+
shift_for_8_bit = Magick::MAGICKCORE_QUANTUM_DEPTH - 8
|
|
30
48
|
# get all pixel colors, adjusted for Rainbow
|
|
31
49
|
pixel_colors = []
|
|
32
50
|
image.each_pixel do |pixel, col, row|
|
|
33
51
|
pixel_rgb = [pixel.red, pixel.green, pixel.blue]
|
|
34
|
-
pixel_rgb = pixel_rgb.map { |color| color >>
|
|
52
|
+
pixel_rgb = pixel_rgb.map { |color| color >> shift_for_8_bit } unless shift_for_8_bit.eql?(0)
|
|
35
53
|
# init 2-dim array
|
|
36
54
|
pixel_colors[row] ||= []
|
|
37
55
|
pixel_colors[row][col] = pixel_rgb
|
|
@@ -39,10 +57,10 @@ module Aspera
|
|
|
39
57
|
# now generate text
|
|
40
58
|
text_pixels = []
|
|
41
59
|
pixel_colors.each_with_index do |row_data, row|
|
|
42
|
-
next if
|
|
60
|
+
next if double && (row.odd? || row.eql?(pixel_colors.length - 1))
|
|
43
61
|
row_data.each_with_index do |pixel_rgb, col|
|
|
44
62
|
text_pixels.push("\n") if col.eql?(0) && !row.eql?(0)
|
|
45
|
-
if
|
|
63
|
+
if double
|
|
46
64
|
text_pixels.push(Rainbow('▄').background(pixel_rgb).foreground(pixel_colors[row + 1][col]))
|
|
47
65
|
else
|
|
48
66
|
text_pixels.push(Rainbow(' ').background(pixel_rgb))
|
|
@@ -53,8 +71,10 @@ module Aspera
|
|
|
53
71
|
end
|
|
54
72
|
|
|
55
73
|
# display image in iTerm2
|
|
74
|
+
# https://iterm2.com/documentation-images.html
|
|
56
75
|
def iterm_display_image(blob)
|
|
57
76
|
# image = Magick::ImageList.new.from_blob(blob)
|
|
77
|
+
# parameters for iTerm2 image display
|
|
58
78
|
arguments = {
|
|
59
79
|
inline: 1,
|
|
60
80
|
preserveAspectRatio: 1,
|
|
@@ -63,7 +83,7 @@ module Aspera
|
|
|
63
83
|
# height: image.rows
|
|
64
84
|
}.map { |k, v| "#{k}=#{v}" }.join(';')
|
|
65
85
|
# \a is BEL, \e is ESC : https://github.com/ruby/ruby/blob/master/doc/syntax/literals.rdoc#label-Strings
|
|
66
|
-
#
|
|
86
|
+
# escape sequence for iTerm2 image display
|
|
67
87
|
return "\e]1337;File=#{arguments}:#{Base64.encode64(blob)}\a"
|
|
68
88
|
end
|
|
69
89
|
|