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.
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