distorted 0.5.3 → 0.6.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +661 -0
  3. data/README.md +4 -139
  4. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Less_Perfect_DOS_VGA.png +0 -0
  5. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/More_Perfect_DOS_VGA.png +0 -0
  6. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Perfect_DOS_VGA.png +0 -0
  7. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/less_more_perfect_dos_vga_437.html +52 -0
  8. data/font/1252/LICENSE/PerfectDOSVGA437/font-comment.php@file=perfect_dos_vga_437.html +5 -0
  9. data/font/1252/LessPerfectDOSVGA.ttf +0 -0
  10. data/font/1252/MorePerfectDOSVGA.ttf +0 -0
  11. data/font/1252/Perfect DOS VGA 437 Win.ttf +0 -0
  12. data/font/437/Perfect DOS VGA 437.ttf +0 -0
  13. data/font/437/dos437.txt +72 -0
  14. data/font/65001/Anonymous Pro B.ttf +0 -0
  15. data/font/65001/Anonymous Pro BI.ttf +0 -0
  16. data/font/65001/Anonymous Pro I.ttf +0 -0
  17. data/font/65001/Anonymous Pro.ttf +0 -0
  18. data/font/65001/LICENSE/AnonymousPro/FONTLOG.txt +45 -0
  19. data/font/65001/LICENSE/AnonymousPro/OFL-FAQ.txt +235 -0
  20. data/font/65001/LICENSE/AnonymousPro/OFL.txt +94 -0
  21. data/font/65001/LICENSE/AnonymousPro/README.txt +55 -0
  22. data/font/850/ProFont-Bold-01/LICENSE +22 -0
  23. data/font/850/ProFont-Bold-01/readme.txt +28 -0
  24. data/font/850/ProFontWindows-Bold.ttf +0 -0
  25. data/font/850/ProFontWindows.ttf +0 -0
  26. data/font/850/Profont/LICENSE +22 -0
  27. data/font/850/Profont/readme.txt +31 -0
  28. data/font/932/LICENSE/README-ttf.txt +213 -0
  29. data/font/932/mona.ttf +0 -0
  30. data/lib/distorted/checking_you_out.rb +116 -0
  31. data/lib/distorted/error_code.rb +51 -0
  32. data/lib/distorted/injection_of_love.rb +247 -0
  33. data/lib/distorted/modular_technology/pango.rb +90 -0
  34. data/lib/distorted/modular_technology/triple_counter.rb +45 -0
  35. data/lib/distorted/modular_technology/ttfunk.rb +48 -0
  36. data/lib/distorted/modular_technology/vips.rb +17 -0
  37. data/lib/distorted/modular_technology/vips_load.rb +77 -0
  38. data/lib/distorted/modular_technology/vips_save.rb +172 -0
  39. data/lib/distorted/molecule/C18H27NO3.rb +10 -0
  40. data/lib/distorted/molecule/font.rb +198 -0
  41. data/lib/distorted/molecule/image.rb +36 -0
  42. data/lib/distorted/molecule/pdf.rb +119 -0
  43. data/lib/distorted/molecule/svg.rb +60 -0
  44. data/lib/distorted/molecule/text.rb +225 -0
  45. data/lib/distorted/molecule/video.rb +195 -0
  46. data/lib/distorted/monkey_business/hash.rb +33 -0
  47. data/lib/distorted/monkey_business/mnemoniq.rb +8 -0
  48. data/lib/distorted/monkey_business/set.rb +15 -0
  49. data/lib/distorted/monkey_business/string.rb +6 -0
  50. data/lib/distorted/types/README +4 -0
  51. data/lib/distorted/types/application.yaml +8 -0
  52. data/lib/distorted/types/font.yaml +29 -0
  53. data/lib/distorted/version.rb +22 -0
  54. data/test/distorted_test.rb +11 -0
  55. data/test/test_helper.rb +4 -0
  56. metadata +102 -5
@@ -0,0 +1,36 @@
1
+
2
+ require 'set'
3
+
4
+ require 'distorted/checking_you_out'
5
+ require 'distorted/modular_technology/vips'
6
+ require 'distorted/injection_of_love'
7
+
8
+
9
+ module Cooltrainer
10
+ module DistorteD
11
+ module Image
12
+
13
+
14
+ # Attributes for our <picture>/<img>.
15
+ # Automatically enabled as attrs for DD Liquid Tag.
16
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture#Attributes
17
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes
18
+ # https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading
19
+ ATTRIBUTES = Set[:alt, :caption, :href, :loading]
20
+
21
+ # Defaults for HTML Element attributes.
22
+ # Not every attr has to be listed here.
23
+ # Many need no default and just won't render.
24
+ ATTRIBUTES_DEFAULT = {
25
+ :loading => :eager,
26
+ }
27
+ ATTRIBUTES_VALUES = {
28
+ :loading => Set[:eager, :lazy],
29
+ }
30
+
31
+ include Cooltrainer::DistorteD::Technology::Vips
32
+ include Cooltrainer::DistorteD::InjectionOfLove
33
+
34
+ end # Image
35
+ end # DistorteD
36
+ end # Cooltrainer
@@ -0,0 +1,119 @@
1
+ require 'set'
2
+
3
+ require 'hexapdf'
4
+
5
+ require 'distorted/checking_you_out'
6
+ require 'distorted/injection_of_love'
7
+ require 'distorted/molecule/C18H27NO3'
8
+
9
+
10
+ module Cooltrainer
11
+ module DistorteD
12
+ module PDF
13
+
14
+
15
+ include Cooltrainer::DistorteD::Molecule::C18H27NO3
16
+
17
+ LOWER_WORLD = CHECKING::YOU::IN("application/pdf")
18
+
19
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#Attributes
20
+ # https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_open_parameters.pdf
21
+ PDF_OPEN_PARAMS = Array[
22
+ # Keep the PDF Open Params in the order they are defined
23
+ # in the Adobe documentation, since it says they should
24
+ # be specified in the URL in that same order.
25
+ # Ruby's Set doesn't guarantee order, so use a plain Array here.
26
+ :nameddest,
27
+ :page,
28
+ :comment,
29
+ :collab,
30
+ :zoom,
31
+ :view,
32
+ :viewrect,
33
+ :pagemode,
34
+ :scrollbar,
35
+ :search,
36
+ :toolbar,
37
+ :statusbar,
38
+ :messages,
39
+ :navpanes,
40
+ :highlight,
41
+ :fdf,
42
+ ]
43
+ ATTRIBUTES = Set[
44
+ :alt,
45
+ :caption,
46
+ :height, #<object> viewer container height.
47
+ :width, # <object> viewer container width.
48
+ ].merge(PDF_OPEN_PARAMS)
49
+
50
+ # "You cannot use the reserved characters =, #, and &.
51
+ # There is no way to escape these special characters."
52
+ RESERVED_CHARACTERS_FRAGMENT = '[^=#&]+'.freeze
53
+
54
+ FLOAT_INT_FRAGMENT = '[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)'.freeze
55
+ ZERO_TO_ONE_HUNDRED = /^(([1-9]\d?|1\d{1})([.,]\d{0,1})?|100([.,]0{1})?)$/
56
+
57
+ ATTRIBUTES_DEFAULT = {
58
+ :height => '100%'.freeze,
59
+ :width => '100%'.freeze,
60
+ # BEGIN PDF Open Parameters
61
+ :page => 1,
62
+ :view => :Fit,
63
+ :pagemode => :none,
64
+ :scrollbar => 1,
65
+ :toolbar => 1,
66
+ :statusbar => 1,
67
+ :messages => 0,
68
+ :navpanes => 1,
69
+ # END PDF Open Parameters
70
+ }
71
+
72
+ # Adobe's PDF Open Parameters documentation sez:
73
+ # "Individual parameters, together with their values (separated by & or #),
74
+ # can be no greater then 32 characters in length."
75
+ # …but then goes on to show some examples (like `comment`)
76
+ # that are clearly longer than 32 characters.
77
+ # Dunno. I'll err on the side of giving you a footgun.
78
+ ATTRIBUTES_VALUES = {
79
+ :nameddest => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
80
+ :page => /\d/,
81
+ :comment => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
82
+ :collab => /^(DAVFDF|FSFDF|DB)@#{RESERVED_CHARACTERS_FRAGMENT}$/,
83
+ :zoom => /^#{FLOAT_INT_FRAGMENT}(,#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT})?$/,
84
+ :view => /^Fit(H|V|B|BH|BV(,#{FLOAT_INT_FRAGMENT})?)?$/,
85
+ :viewrect => /^#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT}$/,
86
+ :pagemode => Set[:none, :thumbs, :bookmarks],
87
+ :scrollbar => BOOLEAN_ATTR_VALUES,
88
+ :search => /^#{RESERVED_CHARACTERS_FRAGMENT}(,\s#{RESERVED_CHARACTERS_FRAGMENT})*$/,
89
+ :toolbar => BOOLEAN_ATTR_VALUES,
90
+ :statusbar => BOOLEAN_ATTR_VALUES,
91
+ :messages => BOOLEAN_ATTR_VALUES,
92
+ :navpanes => BOOLEAN_ATTR_VALUES,
93
+ :fdf => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
94
+ }
95
+
96
+ include Cooltrainer::DistorteD::InjectionOfLove
97
+
98
+ # TODO: Use MuPDF instead of libvips magick-based PDF loader.
99
+
100
+ def self.optimize(src, dest)
101
+ HexaPDF::Document.open(src) do |doc|
102
+ doc.task(
103
+ :optimize,
104
+ compact: true,
105
+ object_streams: :generate,
106
+ xref_streams: :generate,
107
+ compress_pages: false,
108
+ )
109
+ doc.write(dest)
110
+ end
111
+ end
112
+
113
+ def to_application_pdf(*a, **k, &b)
114
+ copy_file(*a, **k, &b)
115
+ end
116
+
117
+ end # PDF
118
+ end # DistorteD
119
+ end # Cooltrainer
@@ -0,0 +1,60 @@
1
+ require 'set'
2
+
3
+ require 'svg_optimizer'
4
+
5
+ require 'distorted/checking_you_out'
6
+ require 'distorted/injection_of_love'
7
+ require 'distorted/molecule/C18H27NO3'
8
+
9
+
10
+ module Cooltrainer
11
+ module DistorteD
12
+ module SVG
13
+
14
+ SUB_TYPE = 'svg'.freeze
15
+ include Cooltrainer::DistorteD::Molecule::C18H27NO3
16
+
17
+ #WISHLIST: Support VML for old IE compatibility.
18
+ # Example: RaphaëlJS — https://en.wikipedia.org/wiki/Rapha%C3%ABl_(JavaScript_library)
19
+ LOWER_WORLD = CHECKING::YOU::IN(/^image\/svg/)
20
+
21
+ ATTRIBUTES = Set[
22
+ :alt,
23
+ :caption,
24
+ :href,
25
+ :loading,
26
+ :optimize,
27
+ ]
28
+ ATTRIBUTES_VALUES = {
29
+ :optimize => BOOLEAN_ATTR_VALUES,
30
+ }
31
+ ATTRIBUTES_DEFAULT = {
32
+ :optimize => false,
33
+ }
34
+
35
+ include Cooltrainer::DistorteD::Technology::VipsSave
36
+ include Cooltrainer::DistorteD::InjectionOfLove
37
+
38
+ def to_vips_image
39
+ # TODO: Load-time options for various formats, like SVG's `unlimited`:
40
+ # "SVGs larger than 10MB are normally blocked for security. Set unlimited to allow SVGs of any size."
41
+ # https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-svgload
42
+ @vips_image ||= Vips::Image.new_from_file(path)
43
+ end
44
+
45
+ def to_image_svg_xml(dest, *a, **k, &b)
46
+ if abstract(:optimize)
47
+ SvgOptimizer.optimize_file(path, dest, SvgOptimizer::DEFAULT_PLUGINS)
48
+ else
49
+ copy_file(dest, *a, **k, &b)
50
+ end
51
+ end
52
+
53
+ def self.optimize(src, dest)
54
+ # TODO: Make optimizations/plugins configurable
55
+ SvgOptimizer.optimize_file(src, dest, SvgOptimizer::DEFAULT_PLUGINS)
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,225 @@
1
+ require 'set'
2
+
3
+ require 'charlock_holmes' # Text file charset detection
4
+
5
+ require 'distorted/monkey_business/string' # String#map
6
+ require 'distorted/modular_technology/pango'
7
+ require 'distorted/modular_technology/ttfunk'
8
+ require 'distorted/modular_technology/vips_save'
9
+
10
+ require 'distorted/checking_you_out'
11
+ require 'distorted/injection_of_love'
12
+ require 'distorted/molecule/image'
13
+
14
+
15
+ module Cooltrainer
16
+ module DistorteD
17
+ module Text
18
+
19
+
20
+ LOWER_WORLD = CHECKING::YOU::IN(/^text\/(plain|x-nfo)/)
21
+ OUTER_LIMITS = CHECKING::YOU::IN(/^text\/(plain|x-nfo)/)
22
+
23
+ # Track supported fonts by codepage.
24
+ # Avoid renaming these from the original archives / websites.
25
+ # Try not to go nuts here bloating the size of our Gem for a
26
+ # very niche feature, but I want to ensure good coverage too.
27
+ #
28
+ # Treat codepage 8859 documents as codepage 1252 to avoid breaking smart-
29
+ # quotes and other printable chars in 1252 that are control chars in 8859.
30
+ # https://encoding.spec.whatwg.org/#names-and-labels
31
+ #
32
+ # Numeric key for UTF-8 is codepage 65001 like Win32:
33
+ # https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
34
+ FONT_FILENAME = {
35
+ :anonpro => 'Anonymous Pro.ttf'.freeze,
36
+ :anonpro_b => 'Anonymous Pro B.ttf'.freeze,
37
+ :anonpro_bi => 'Anonymous Pro BI.ttf'.freeze,
38
+ :anonpro_i => 'Anonymous Pro I.ttf'.freeze,
39
+ :lessperfectdosvga => 'LessPerfectDOSVGA.ttf'.freeze,
40
+ :moreperfectdisvga => 'MorePerfectDOSVGA.ttf'.freeze,
41
+ :perfectdosvgawin => 'Perfect DOS VGA 437 Win.ttf'.freeze,
42
+ :mona => 'mona.ttf'.freeze,
43
+ :perfectdosvga => 'Perfect DOS VGA 437.ttf'.freeze,
44
+ :profont => 'ProFontWindows.ttf'.freeze,
45
+ :profont_b => 'ProFontWindows-Bold.ttf'.freeze,
46
+ }
47
+ # Certain fonts are more suitable for certain codepages,
48
+ # so track each codepage's available fonts…
49
+ CODEPAGE_FONT = {
50
+ 65001 => [
51
+ :anonpro,
52
+ :anonpro_b,
53
+ :anonpro_bi,
54
+ :anonpro_i,
55
+ ],
56
+ 1252 => [
57
+ :lessperfectdosvga,
58
+ :moreperfectdosvga,
59
+ :perfectdosvgawin,
60
+ ],
61
+ 932 => [
62
+ :mona,
63
+ ],
64
+ 850 => [
65
+ :profont,
66
+ :profont_b,
67
+ ],
68
+ 437 => [
69
+ :perfectdosvga,
70
+ ],
71
+ }
72
+ # …as well as the inverse, the numeric codepage for each font:
73
+ FONT_CODEPAGE = self::CODEPAGE_FONT.reduce(Hash.new([])) { |memo, (key, values)|
74
+ values.each { |value| memo[value] = key }
75
+ memo
76
+ }
77
+
78
+ self::OUTER_LIMITS.each { |t|
79
+ define_method(t.distorted_method) { |*a, **k, &b|
80
+ copy_file(*a, **k, &b)
81
+ }
82
+ }
83
+
84
+ ATTRIBUTES = Set[
85
+ :alt,
86
+ :crop,
87
+ :font,
88
+ :encoding,
89
+ :spacing,
90
+ :dpi,
91
+ ]
92
+ ATTRIBUTES_VALUES = {
93
+ :spacing => Set[:monospace, :proportional],
94
+ :font => self::FONT_FILENAME.keys.to_set,
95
+ }
96
+ ATTRIBUTES_DEFAULT = {
97
+ :crop => :none,
98
+ :dpi => 144,
99
+ :encoding => 'UTF-8'.freeze
100
+ }
101
+
102
+ include Cooltrainer::DistorteD::Technology::TTFunk
103
+ include Cooltrainer::DistorteD::Technology::Pango
104
+ include Cooltrainer::DistorteD::Technology::VipsSave
105
+ include Cooltrainer::DistorteD::InjectionOfLove
106
+
107
+ # Using a numeric key for things for simplicity.
108
+ # TODO: Replace this with Ruby's built-in Encoding class after I have
109
+ # a better idea what I want to do.
110
+ def codepage
111
+ case text_file_encoding
112
+ when 'UTF-8'.freeze then 65001
113
+ when 'Shift_JIS'.freeze then 932
114
+ when 'IBM437'.freeze then 437
115
+ else 1252
116
+ end
117
+ end
118
+
119
+ # Return a Pango Markup escaped version of the document.
120
+ def to_pango
121
+ # https://developer.gnome.org/glib/stable/glib-Simple-XML-Subset-Parser.html#g-markup-escape-text
122
+ escaped = text_file_utf8_content.map{ |c|
123
+ g_markup_escape_char(c)
124
+ }
125
+ if font_spacing == :monospace
126
+ "<tt>" << escaped << "</tt>"
127
+ else
128
+ escaped
129
+ end
130
+ end
131
+
132
+ protected
133
+
134
+ def text_file_content
135
+ # VIPS makes us provide the text content as a single variable,
136
+ # so we may as well just one-shot File.read() it into memory.
137
+ # https://kunststube.net/encoding/
138
+ @text_file_content ||= File.read(path)
139
+ end
140
+
141
+ def text_file_utf8_content
142
+ CharlockHolmes::Converter.convert(text_file_content, text_file_encoding, 'UTF-8'.freeze)
143
+ end
144
+
145
+ def text_file_encoding
146
+ # It's not easy or even possible in some cases to tell the "true" codepage
147
+ # we should use for any given text document, but using character detection
148
+ # is worth a shot if the user gave us nothing.
149
+ #
150
+ # TODO: Figure out if/how we can get IBM437 files to not be detected as ISO-8859-1
151
+ @text_file_encoding ||= (
152
+ abstract(:encoding).to_s ||
153
+ CharlockHolmes::EncodingDetector.detect(text_file_content)[:encoding] ||
154
+ 'UTF-8'.freeze
155
+ ).to_s
156
+ end
157
+
158
+ def vips_font
159
+ # Set the shorthand Symbol key for our chosen font.
160
+ return abstract(:font)&.to_sym || CODEPAGE_FONT[codepage].first
161
+ end
162
+
163
+ def to_vips_image
164
+ # Load font metadata directly from the file so we don't have to
165
+ # duplicate it here to feed to Vips/Pango.
166
+ #
167
+ # irb(main)> font_meta.name.font_name
168
+ # => ["Perfect DOS VGA 437", "\x00P\x00e\x00r\x00f\x00e\x00c\x00t\x00 \x00D\x00O\x00S\x00 \x00V\x00G\x00A\x00 \x004\x003\x007"]
169
+ # irb(main)> font_meta.name.font_family
170
+ # => ["Perfect DOS VGA 437", "\x00P\x00e\x00r\x00f\x00e\x00c\x00t\x00 \x00D\x00O\x00S\x00 \x00V\x00G\x00A\x00 \x004\x003\x007"]
171
+ # irb(main)> font_meta.name.font_subfamily
172
+ # => ["Regular", "\x00R\x00e\x00g\x00u\x00l\x00a\x00r"]
173
+ # irb(main)> font_meta.name.postscript_name
174
+ # => "PerfectDOSVGA437"
175
+ # irb(main)> font_meta.line_gap
176
+ # => 0
177
+
178
+ # https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
179
+ Vips::Image.text(
180
+ # This string must be well-escaped Pango Markup:
181
+ # https://developer.gnome.org/pango/stable/pango-Markup.html
182
+ # However the official function for escaping text is
183
+ # not implemented in Ruby GLib, so we have to do it ourselves.
184
+ to_pango,
185
+ **{
186
+ # String absolute path to TTF
187
+ :fontfile => font_path,
188
+ # It's not enough to just specify the TTF path;
189
+ # we must also specify a font family, subfamily, and size.
190
+ :font => "#{font_name} 16",
191
+ # Space between lines (in Points).
192
+ :spacing => to_ttfunk.line_gap,
193
+ :justify => true, # Requires libvips 8.8
194
+ :dpi => abstract(:dpi)&.to_i,
195
+ },
196
+ )
197
+ end
198
+
199
+ # Return the String absolute path to the TTF file
200
+ def font_path
201
+ File.join(
202
+ File.dirname(__FILE__), # molecule
203
+ '..'.freeze, # distorted
204
+ '..'.freeze, # lib
205
+ '..'.freeze, # DistorteD-Ruby
206
+ 'font'.freeze,
207
+ font_codepage.to_s,
208
+ font_filename,
209
+ )
210
+ end
211
+
212
+ # Returns the numeric representation of the codepage
213
+ # covered by our font.
214
+ def font_codepage
215
+ FONT_CODEPAGE.dig(vips_font).to_s
216
+ end
217
+
218
+ # Returns the basename (with file extension) of our font.
219
+ def font_filename
220
+ FONT_FILENAME.dig(vips_font)
221
+ end
222
+
223
+ end # Text
224
+ end # DistorteD
225
+ end # Cooltrainer
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env ruby
2
+ # Tell the user to install the shared library if it's missing.
3
+ begin
4
+ require 'gst'
5
+ rescue LoadError => le
6
+ raise unless le.message =~ /libgst/
7
+
8
+ # Multiple OS help
9
+ help = <<~INSTALL
10
+
11
+ Please install the GStreamer library for your system.
12
+ INSTALL
13
+
14
+ # Re-raise with install message
15
+ raise $!, "#{help}\n#{$!}", $!.backtrace
16
+ end
17
+
18
+ require 'set'
19
+
20
+ require 'distorted/checking_you_out'
21
+ require 'distorted/injection_of_love'
22
+
23
+
24
+ module Cooltrainer
25
+ module DistorteD
26
+ module Video
27
+
28
+ LOWER_WORLD = CHECKING::YOU::IN(/^video\/mp4/)
29
+
30
+ # Attributes for our <video>.
31
+ # Automatically enabled as attrs for DD Liquid Tag.
32
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#Attributes
33
+ ATTRIBUTES = Set[:caption]
34
+
35
+ # Defaults for HTML Element attributes.
36
+ # Not every attr has to be listed here.
37
+ # Many need no default and just won't render.
38
+ ATTRIBUTES_DEFAULT = {}
39
+ ATTRIBUTES_VALUES = {}
40
+ include Cooltrainer::DistorteD::InjectionOfLove
41
+
42
+ attr_accessor :dest
43
+
44
+
45
+ def initialize(src, dest, basename)
46
+ @src = src
47
+ @dest = dest
48
+ @basename = basename
49
+ end
50
+
51
+ def rotate(angle=nil)
52
+ false
53
+ end
54
+
55
+ def clean
56
+ false
57
+ end
58
+
59
+ def generate
60
+ self.generate_hls
61
+ begin
62
+ self.generate_dash
63
+ rescue Gst::ParseError::NoSuchElement
64
+ # This is going away once the new dashsink2 lands in Gst so :effort:
65
+ end
66
+ end
67
+
68
+ def generate_dash
69
+ orig_dest = @dest
70
+ orig_path = @src
71
+
72
+ FileUtils.mkdir_p(File.dirname(orig_dest))
73
+
74
+ hls_dest = File.join(File.dirname(orig_dest), @basename + '.dash')
75
+ FileUtils.mkdir_p(hls_dest)
76
+ Jekyll.logger.debug(@tag_name, "Re-muxing #{orig_path} to #{hls_dest}.")
77
+
78
+ #FileUtils.rm(orig_dest) if File.exist?(orig_dest)
79
+ if not File.file?(orig_dest)
80
+ FileUtils.cp(orig_path, orig_dest)
81
+ end
82
+
83
+ # https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description
84
+ # TODO: Convert this from parse_launch() pipeline notation to Element objects
85
+ # TODO: Get source video duration/resolution/etc and use it to compute a
86
+ # value for `target-duration`.
87
+ # TODO: Also support urldecodebin for remote media.
88
+ pipeline, error = Gst.parse_launch("filesrc name=src ! decodebin name=demux ! videoconvert ! vaapih264enc ! queue2 ! h264parse ! queue2 ! mux.video dashsink name=mux max-files=0 playlist-length=0 target-duration=2 demux. ! audioconvert ! voaacenc ! queue2 ! mux.audio")
89
+
90
+ if pipeline.nil?
91
+ Jekyll.logger.error(@tag_name, "Parse error: #{error.message}")
92
+ return false
93
+ end
94
+
95
+ filesrc = pipeline.get_by_name('src')
96
+ filesrc.location = orig_path
97
+
98
+ hls_playlist = "#{hls_dest}/#{@basename}.m3u8"
99
+ hls = pipeline.get_by_name('mux')
100
+ hls.location = "#{hls_dest}/#{@basename}%05d.ts"
101
+ hls.playlist_location = hls_playlist
102
+
103
+ # TODO: config option for absolute vs relative segment URIs in the playlist.
104
+ #hls.playlist_root = @url
105
+
106
+ # TODO: dashsink support once there is a stable GStreamer release including it:
107
+ # https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/merge_requests/704
108
+
109
+ pipeline.play
110
+
111
+ # Play until End Of Stream
112
+ event_loop(pipeline)
113
+
114
+ pipeline.stop
115
+
116
+ end
117
+
118
+ def generate_hls
119
+ orig_dest = @dest
120
+ orig_path = @src
121
+
122
+ FileUtils.mkdir_p(File.dirname(orig_dest))
123
+
124
+ hls_dest = File.join(File.dirname(orig_dest), @basename + '.hls')
125
+ FileUtils.mkdir_p(hls_dest)
126
+ Jekyll.logger.debug(@tag_name, "Re-muxing #{orig_path} to #{hls_dest}.")
127
+
128
+ #FileUtils.rm(orig_dest) if File.exist?(orig_dest)
129
+ if not File.file?(orig_dest)
130
+ FileUtils.cp(orig_path, orig_dest)
131
+ end
132
+
133
+ # https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description
134
+ # TODO: Convert this from parse_launch() pipeline notation to Element objects
135
+ # TODO: Get source video duration/resolution/etc and use it to compute a
136
+ # value for `target-duration`.
137
+ # TODO: Also support urldecodebin for remote media.
138
+ pipeline, error = Gst.parse_launch("filesrc name=src ! decodebin name=demux ! videoconvert ! vaapih264enc ! queue2 ! h264parse ! queue2 ! mux.video hlssink2 name=mux max-files=0 playlist-length=0 target-duration=2 demux. ! audioconvert ! voaacenc ! queue2 ! mux.audio")
139
+
140
+ if pipeline.nil?
141
+ Jekyll.logger.error(@tag_name, "Parse error: #{error.message}")
142
+ return false
143
+ end
144
+
145
+ filesrc = pipeline.get_by_name('src')
146
+ filesrc.location = orig_path
147
+
148
+ hls_playlist = "#{hls_dest}/#{@basename}.m3u8"
149
+ hls = pipeline.get_by_name('mux')
150
+ hls.location = "#{hls_dest}/#{@basename}%05d.ts"
151
+ hls.playlist_location = hls_playlist
152
+
153
+ # TODO: config option for absolute vs relative segment URIs in the playlist.
154
+ #hls.playlist_root = @url
155
+
156
+ # TODO: dashsink support once there is a stable GStreamer release including it:
157
+ # https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/merge_requests/704
158
+
159
+ pipeline.play
160
+
161
+ # Play until End Of Stream
162
+ event_loop(pipeline)
163
+
164
+ pipeline.stop
165
+
166
+ # HACK HACK HACK: Replace X-ALLOW-CACHE line in playlist with YES.
167
+ # This property does not seem to be exposed to the outside of hlssink:
168
+ # https://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/ext/hls/gsthlssink.c
169
+ text = File.read(hls_playlist)
170
+ File.write(hls_playlist, text.gsub(/^#EXT-X-ALLOW-CACHE:NO$/, '#EXT-X-ALLOW-CACHE:YES'))
171
+ end
172
+
173
+ def event_loop(pipeline)
174
+ running = true
175
+ bus = pipeline.bus
176
+
177
+ while running
178
+ message = bus.poll(Gst::MessageType::ANY, -1)
179
+
180
+ case message.type
181
+ when Gst::MessageType::EOS
182
+ running = false
183
+ when Gst::MessageType::WARNING
184
+ warning, _debug = message.parse_warning
185
+ Jekyll.logger.warning(@tag_name, warning)
186
+ when Gst::MessageType::ERROR
187
+ error, _debug = message.parse_error
188
+ Jekyll.logger.error(@tag_name, error)
189
+ running = false
190
+ end
191
+ end
192
+ end
193
+ end # Image
194
+ end # DistorteD
195
+ end # Cooltrainer