distorted 0.6.0 → 0.7.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/bin/console +14 -0
  4. data/bin/distorted +6 -0
  5. data/bin/setup +8 -0
  6. data/lib/distorted.rb +2 -0
  7. data/lib/distorted/checking_you_out.rb +116 -13
  8. data/lib/distorted/{types → checking_you_out}/README +0 -0
  9. data/lib/distorted/checking_you_out/application.yaml +33 -0
  10. data/lib/distorted/{types → checking_you_out}/font.yaml +0 -0
  11. data/lib/distorted/checking_you_out/image.yaml +108 -0
  12. data/lib/distorted/click_again.rb +333 -0
  13. data/lib/distorted/element_of_media.rb +2 -0
  14. data/lib/distorted/element_of_media/change.rb +119 -0
  15. data/lib/distorted/element_of_media/compound.rb +120 -0
  16. data/lib/distorted/floor.rb +17 -0
  17. data/lib/distorted/invoker.rb +97 -0
  18. data/lib/distorted/media_molecule.rb +58 -0
  19. data/lib/distorted/media_molecule/font.rb +195 -0
  20. data/lib/distorted/media_molecule/image.rb +33 -0
  21. data/lib/distorted/media_molecule/pdf.rb +44 -0
  22. data/lib/distorted/media_molecule/svg.rb +45 -0
  23. data/lib/distorted/media_molecule/text.rb +203 -0
  24. data/lib/distorted/media_molecule/video.rb +18 -0
  25. data/lib/distorted/modular_technology/gstreamer.rb +174 -0
  26. data/lib/distorted/modular_technology/vips.rb +4 -4
  27. data/lib/distorted/modular_technology/vips/foreign.rb +489 -0
  28. data/lib/distorted/modular_technology/vips/load.rb +133 -0
  29. data/lib/distorted/modular_technology/{vips_save.rb → vips/save.rb} +23 -34
  30. data/lib/distorted/monkey_business/encoding.rb +317 -0
  31. data/lib/distorted/monkey_business/hash.rb +0 -15
  32. data/lib/distorted/{modular_technology/triple_counter.rb → triple_counter.rb} +8 -1
  33. data/lib/distorted/version.rb +16 -16
  34. metadata +59 -46
  35. data/lib/distorted/injection_of_love.rb +0 -247
  36. data/lib/distorted/modular_technology/vips_load.rb +0 -77
  37. data/lib/distorted/molecule/C18H27NO3.rb +0 -10
  38. data/lib/distorted/molecule/font.rb +0 -198
  39. data/lib/distorted/molecule/image.rb +0 -36
  40. data/lib/distorted/molecule/pdf.rb +0 -119
  41. data/lib/distorted/molecule/svg.rb +0 -60
  42. data/lib/distorted/molecule/text.rb +0 -225
  43. data/lib/distorted/molecule/video.rb +0 -195
  44. data/lib/distorted/monkey_business/mnemoniq.rb +0 -8
  45. data/lib/distorted/types/application.yaml +0 -8
@@ -1,225 +0,0 @@
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
@@ -1,195 +0,0 @@
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
@@ -1,8 +0,0 @@
1
- require 'mime/types'
2
-
3
- # MIME::Types#preferred_extension returns @extensions.first unless
4
- # otherwise set. I don't like some of the defaults, so this file
5
- # changes them.
6
- # Normally I don't like to monkey patch just on import without calling
7
- # some method, but this is one time I explicitly want to do that.
8
- MIME::Types['image/jpeg'].last.preferred_extension = 'jpg'
@@ -1,8 +0,0 @@
1
- # Define our own type to trigger Last Resort instead of abusing application/x-imagemap :)
2
- - !ruby/object:MIME::Type
3
- content-type: application/x.distorted.last-resort
4
- encoding: 8bit
5
- xrefs:
6
- person:
7
- - okeeblow
8
- registered: false