aspera-cli 4.14.0 → 4.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +29 -3
  4. data/CHANGELOG.md +300 -185
  5. data/CONTRIBUTING.md +74 -23
  6. data/README.md +2346 -1619
  7. data/bin/ascli +16 -25
  8. data/bin/asession +15 -15
  9. data/examples/dascli +2 -2
  10. data/examples/proxy.pac +1 -1
  11. data/lib/aspera/aoc.rb +216 -150
  12. data/lib/aspera/ascmd.rb +25 -18
  13. data/lib/aspera/assert.rb +45 -0
  14. data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
  15. data/lib/aspera/cli/error.rb +17 -0
  16. data/lib/aspera/cli/extended_value.rb +51 -16
  17. data/lib/aspera/cli/formatter.rb +276 -174
  18. data/lib/aspera/cli/hints.rb +81 -0
  19. data/lib/aspera/cli/main.rb +114 -147
  20. data/lib/aspera/cli/manager.rb +181 -136
  21. data/lib/aspera/cli/plugin.rb +82 -64
  22. data/lib/aspera/cli/plugins/alee.rb +0 -1
  23. data/lib/aspera/cli/plugins/aoc.rb +327 -331
  24. data/lib/aspera/cli/plugins/ats.rb +12 -8
  25. data/lib/aspera/cli/plugins/bss.rb +2 -2
  26. data/lib/aspera/cli/plugins/config.rb +575 -439
  27. data/lib/aspera/cli/plugins/console.rb +40 -0
  28. data/lib/aspera/cli/plugins/cos.rb +4 -5
  29. data/lib/aspera/cli/plugins/faspex.rb +111 -92
  30. data/lib/aspera/cli/plugins/faspex5.rb +245 -182
  31. data/lib/aspera/cli/plugins/node.rb +239 -160
  32. data/lib/aspera/cli/plugins/orchestrator.rb +56 -19
  33. data/lib/aspera/cli/plugins/preview.rb +54 -38
  34. data/lib/aspera/cli/plugins/server.rb +63 -20
  35. data/lib/aspera/cli/plugins/shares.rb +64 -38
  36. data/lib/aspera/cli/sync_actions.rb +68 -0
  37. data/lib/aspera/cli/transfer_agent.rb +64 -67
  38. data/lib/aspera/cli/transfer_progress.rb +73 -0
  39. data/lib/aspera/cli/version.rb +1 -1
  40. data/lib/aspera/colors.rb +3 -1
  41. data/lib/aspera/command_line_builder.rb +27 -22
  42. data/lib/aspera/cos_node.rb +6 -4
  43. data/lib/aspera/coverage.rb +22 -0
  44. data/lib/aspera/data_repository.rb +33 -2
  45. data/lib/aspera/environment.rb +21 -8
  46. data/lib/aspera/fasp/agent_alpha.rb +116 -0
  47. data/lib/aspera/fasp/agent_base.rb +40 -76
  48. data/lib/aspera/fasp/agent_connect.rb +21 -22
  49. data/lib/aspera/fasp/agent_direct.rb +169 -179
  50. data/lib/aspera/fasp/agent_httpgw.rb +200 -195
  51. data/lib/aspera/fasp/agent_node.rb +43 -35
  52. data/lib/aspera/fasp/agent_trsdk.rb +124 -41
  53. data/lib/aspera/fasp/error_info.rb +2 -2
  54. data/lib/aspera/fasp/faux_file.rb +52 -0
  55. data/lib/aspera/fasp/installation.rb +89 -191
  56. data/lib/aspera/fasp/management.rb +249 -0
  57. data/lib/aspera/fasp/parameters.rb +86 -47
  58. data/lib/aspera/fasp/parameters.yaml +75 -8
  59. data/lib/aspera/fasp/products.rb +162 -0
  60. data/lib/aspera/fasp/resume_policy.rb +7 -5
  61. data/lib/aspera/fasp/sync.rb +273 -0
  62. data/lib/aspera/fasp/transfer_spec.rb +10 -8
  63. data/lib/aspera/fasp/uri.rb +6 -6
  64. data/lib/aspera/faspex_gw.rb +11 -8
  65. data/lib/aspera/faspex_postproc.rb +8 -7
  66. data/lib/aspera/hash_ext.rb +2 -2
  67. data/lib/aspera/id_generator.rb +3 -1
  68. data/lib/aspera/json_rpc.rb +51 -0
  69. data/lib/aspera/keychain/encrypted_hash.rb +46 -11
  70. data/lib/aspera/keychain/macos_security.rb +15 -13
  71. data/lib/aspera/line_logger.rb +23 -0
  72. data/lib/aspera/log.rb +61 -19
  73. data/lib/aspera/nagios.rb +7 -2
  74. data/lib/aspera/node.rb +105 -21
  75. data/lib/aspera/node_simulator.rb +214 -0
  76. data/lib/aspera/oauth.rb +57 -36
  77. data/lib/aspera/open_application.rb +4 -4
  78. data/lib/aspera/persistency_action_once.rb +13 -14
  79. data/lib/aspera/persistency_folder.rb +5 -4
  80. data/lib/aspera/preview/file_types.rb +56 -268
  81. data/lib/aspera/preview/generator.rb +28 -39
  82. data/lib/aspera/preview/options.rb +2 -0
  83. data/lib/aspera/preview/terminal.rb +36 -16
  84. data/lib/aspera/preview/utils.rb +23 -29
  85. data/lib/aspera/proxy_auto_config.rb +6 -3
  86. data/lib/aspera/rest.rb +127 -80
  87. data/lib/aspera/rest_call_error.rb +1 -1
  88. data/lib/aspera/rest_error_analyzer.rb +16 -14
  89. data/lib/aspera/rest_errors_aspera.rb +39 -34
  90. data/lib/aspera/secret_hider.rb +18 -17
  91. data/lib/aspera/ssh.rb +10 -5
  92. data/lib/aspera/temp_file_manager.rb +11 -4
  93. data/lib/aspera/web_auth.rb +10 -7
  94. data/lib/aspera/web_server_simple.rb +11 -5
  95. data.tar.gz.sig +0 -0
  96. metadata +108 -39
  97. metadata.gz.sig +0 -0
  98. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  99. data/lib/aspera/cli/listener/logger.rb +0 -22
  100. data/lib/aspera/cli/listener/progress.rb +0 -50
  101. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  102. data/lib/aspera/cli/plugins/sync.rb +0 -44
  103. data/lib/aspera/fasp/listener.rb +0 -13
  104. 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
- # define how files are processed based on mime type
15
+ # special cases for mime types
15
16
  # spellchecker:disable
16
17
  SUPPORTED_MIME_TYPES = {
17
- 'application/json' => :plaintext,
18
- 'application/mac-binhex40' => :office,
19
- 'application/msword' => :office,
20
- 'application/pdf' => :pdf,
21
- 'application/postscript' => :image,
22
- 'application/rtf' => :office,
23
- 'application/vnd.3gpp.pic-bw-small' => :image,
24
- 'application/vnd.hp-hpgl' => :image,
25
- 'application/vnd.hp-pcl' => :image,
26
- 'application/vnd.lotus-wordpro' => :office,
27
- 'application/vnd.mobius.msl' => :image,
28
- 'application/vnd.mophun.certificate' => :image,
29
- 'application/vnd.ms-excel' => :office,
30
- 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => :office,
31
- 'application/vnd.ms-excel.sheet.macroenabled.12' => :office,
32
- 'application/vnd.ms-excel.template.macroenabled.12' => :office,
33
- 'application/vnd.ms-powerpoint' => :office,
34
- 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => :office,
35
- 'application/vnd.ms-powerpoint.template.macroenabled.12' => :office,
36
- 'application/vnd.ms-word.document.macroenabled.12' => :office,
37
- 'application/vnd.ms-word.template.macroenabled.12' => :office,
38
- 'application/vnd.ms-works' => :office,
39
- 'application/vnd.oasis.opendocument.chart' => :office,
40
- 'application/vnd.oasis.opendocument.formula' => :office,
41
- 'application/vnd.oasis.opendocument.graphics' => :office,
42
- 'application/vnd.oasis.opendocument.graphics-template' => :office,
43
- 'application/vnd.oasis.opendocument.presentation' => :office,
44
- 'application/vnd.oasis.opendocument.presentation-template' => :office,
45
- 'application/vnd.oasis.opendocument.spreadsheet' => :office,
46
- 'application/vnd.oasis.opendocument.spreadsheet-template' => :office,
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
- # this is a way to add support for extensions that are otherwise not known by node api (mime type)
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 mime_from_file(filepath)
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 !SUPPORTED_MIME_TYPES.key?(detected_mime)
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 api
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 = SUPPORTED_MIME_TYPES[mimetype] if !mimetype.nil?
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 = mime_from_file(filepath)
96
+ detected_mime = file_to_mime(filepath)
310
97
  if !detected_mime.nil?
311
- conversion_type = SUPPORTED_MIME_TYPES[detected_mime]
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
- extension = File.extname(filepath.downcase)[1..-1]
324
- conversion_type = SUPPORTED_EXTENSIONS[extension] if conversion_type.nil?
325
- Log.log.debug{"conversion_type(#{extension}): #{conversion_type.class.name} [#{conversion_type}]"}
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(options, src, dst, main_temp_dir, api_mime_type)
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
- @preview_format_symb = File.extname(@destination_file_path).gsub(/^\./, '').to_sym
42
- @conversion_type = FileTypes.instance.conversion_type(@source_file_path, api_mime_type)
43
- end
44
-
45
- # name of processing method in this object
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
- name = "#{name}_using_#{@options.video_conversion}"
50
+ @processing_method = "#{@processing_method}_using_#{@options.video_conversion}"
53
51
  when :png
54
- name = "#{name}_using_#{@options.video_png_conv}"
52
+ @processing_method = "#{@processing_method}_using_#{@options.video_png_conv}"
55
53
  end
56
54
  end
57
- Log.log.debug{"method: #{name}"}
58
- return name.to_sym
59
- end
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
- raise 'could not detect type of file' if @conversion_type.nil?
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(method_symb)
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(@preview_format_symb.eql?(:mp4) ? 'video_error.png' : 'image_error.png', File.dirname(__FILE__)), @destination_file_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
- raise 'duration must be Float' unless duration.is_a?(Float)
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
- filelist = File.join(this_tmpdir, 'clip_files.txt')
138
- File.open(filelist, 'w+') do |f|
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: filelist,
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
- raise 'reencode_ffmpeg must be a Hash' unless options.is_a?(Hash)
154
+ assert_type(options, Hash){'reencode_ffmpeg'}
166
155
  options.each do |k, v|
167
- raise "Key not supported: #{k}. Use keys: #{FFMPEG_OPTIONS_LIST.join(',')}" unless FFMPEG_OPTIONS_LIST.include?(k)
168
- raise "Value for #{k} must be Array" unless v.is_a?(Array)
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,
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:ignore reencode conv transframes pauseframes
4
+
3
5
  module Aspera
4
6
  module Preview
5
7
  # generator options. Used as parameter to preview generator object.
@@ -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
- # Generates a string that can display an image in a terminal
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
- SHIFT_FOR_8_BIT = Magick::MAGICKCORE_QUANTUM_DEPTH - 8
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
- private_constant :SHIFT_FOR_8_BIT, :ITERM_NAMES, :TERM_ENV_VARS
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
- def build(blob, reserved_lines: 0, double_precision: true)
20
- return iterm_display_image(blob) if iterm_supported?
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 -= reserved_lines
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
- # TODO: retrieve terminal font ratio using some termcap ?
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 >> SHIFT_FOR_8_BIT } unless SHIFT_FOR_8_BIT.eql?(0)
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 double_precision && row.odd?
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 double_precision
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
- # https://iterm2.com/documentation-images.html
86
+ # escape sequence for iTerm2 image display
67
87
  return "\e]1337;File=#{arguments}:#{Base64.encode64(blob)}\a"
68
88
  end
69
89