Thumbnail generation in Sidekiq worker
parent
da7a9532df
commit
27219f48b7
@ -0,0 +1,28 @@
|
||||
class ApplicationDecorator < Draper::Base
|
||||
# Lazy Helpers
|
||||
# PRO: Call Rails helpers without the h. proxy
|
||||
# ex: number_to_currency(model.price)
|
||||
# CON: Add a bazillion methods into your decorator's namespace
|
||||
# and probably sacrifice performance/memory
|
||||
#
|
||||
# Enable them by uncommenting this line:
|
||||
# lazy_helpers
|
||||
|
||||
# Shared Decorations
|
||||
# Consider defining shared methods common to all your models.
|
||||
#
|
||||
# Example: standardize the formatting of timestamps
|
||||
#
|
||||
# def formatted_timestamp(time)
|
||||
# h.content_tag :span, time.strftime("%a %m/%d/%y"),
|
||||
# :class => 'timestamp'
|
||||
# end
|
||||
#
|
||||
# def created_at
|
||||
# formatted_timestamp(model.created_at)
|
||||
# end
|
||||
#
|
||||
# def updated_at
|
||||
# formatted_timestamp(model.updated_at)
|
||||
# end
|
||||
end
|
@ -0,0 +1,38 @@
|
||||
class AsciicastDecorator < ApplicationDecorator
|
||||
decorates :asciicast
|
||||
|
||||
THUMBNAIL_WIDTH = 20
|
||||
THUMBNAIL_HEIGHT = 10
|
||||
|
||||
def thumbnail
|
||||
if @thumbnail.nil?
|
||||
lines = model.snapshot.split("\n")
|
||||
|
||||
top_lines = lines[0...THUMBNAIL_HEIGHT]
|
||||
top_text = prepare_lines(top_lines).join("\n")
|
||||
|
||||
bottom_lines = lines.reverse[0...THUMBNAIL_HEIGHT].reverse
|
||||
bottom_text = prepare_lines(bottom_lines).join("\n")
|
||||
|
||||
if top_text.gsub(/\s+/, '').size > bottom_text.gsub(/\s+/, '').size
|
||||
@thumbnail = top_text
|
||||
else
|
||||
@thumbnail = bottom_text
|
||||
end
|
||||
end
|
||||
|
||||
@thumbnail
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_lines(lines)
|
||||
(THUMBNAIL_HEIGHT - lines.size).times { lines << '' }
|
||||
|
||||
lines.map do |line|
|
||||
line = line[0...THUMBNAIL_WIDTH]
|
||||
line << ' ' * (THUMBNAIL_WIDTH - line.size)
|
||||
line
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,85 @@
|
||||
require 'tempfile'
|
||||
|
||||
class SnapshotWorker
|
||||
include Sidekiq::Worker
|
||||
sidekiq_options :retry => false
|
||||
|
||||
def perform(asciicast_id)
|
||||
begin
|
||||
@asciicast = Asciicast.find(asciicast_id)
|
||||
|
||||
prepare_files
|
||||
convert_to_typescript
|
||||
|
||||
delay = (@asciicast.duration / 2).to_i
|
||||
delay = 30 if delay > 30
|
||||
snapshot = capture_terminal(delay)
|
||||
|
||||
@asciicast.snapshot = snapshot
|
||||
@asciicast.save!
|
||||
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
# oh well...
|
||||
|
||||
ensure
|
||||
cleanup
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_files
|
||||
in_data_file = Tempfile.new('asciiio-data', :encoding => 'ascii-8bit')
|
||||
in_data_file.write(@asciicast.stdout.read)
|
||||
in_data_file.close
|
||||
@in_data_path = in_data_file.path
|
||||
|
||||
in_timing_file = Tempfile.new('asciiio-timing', :encoding => 'ascii-8bit')
|
||||
in_timing_file.write(@asciicast.stdout_timing.read)
|
||||
in_timing_file.close
|
||||
@in_timing_path = in_timing_file.path
|
||||
|
||||
@out_data_path = @in_data_path + '.ts'
|
||||
@out_timing_path = @in_timing_path + '.ts'
|
||||
end
|
||||
|
||||
def convert_to_typescript
|
||||
system "bash -c './script/convert-to-typescript.sh " +
|
||||
"#{@in_data_path} #{@in_timing_path} " +
|
||||
"#{@out_data_path} #{@out_timing_path}'"
|
||||
raise "Can't convert asciicast ##{@asciicast.id} to typescript" if $? != 0
|
||||
end
|
||||
|
||||
def capture_terminal(delay)
|
||||
capture_command =
|
||||
"scriptreplay #{@out_timing_path} #{@out_data_path}; sleep 10"
|
||||
|
||||
command = "bash -c 'ASCIICAST_ID=#{@asciicast.id} " +
|
||||
"COLS=#{@asciicast.terminal_columns} " +
|
||||
"LINES=#{@asciicast.terminal_lines} " +
|
||||
"COMMAND=\"#{capture_command}\" " +
|
||||
"DELAY=#{delay} ./script/capture.sh'"
|
||||
|
||||
lines = []
|
||||
status = nil
|
||||
|
||||
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
||||
while !stdout.eof?
|
||||
lines << stdout.readline
|
||||
end
|
||||
|
||||
status = wait_thr.value.exitstatus
|
||||
end
|
||||
|
||||
raise "Can't capture output of asciicast ##{@asciicast.id}" if status != 0
|
||||
|
||||
lines.join('')
|
||||
end
|
||||
|
||||
def cleanup
|
||||
FileUtils.rm_f([
|
||||
@in_data_path,
|
||||
@in_timing_path,
|
||||
@out_data_path,
|
||||
@out_timing_path
|
||||
])
|
||||
end
|
||||
end
|
@ -1,31 +0,0 @@
|
||||
require 'tempfile'
|
||||
|
||||
class ThumbnailWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(asciicast_id)
|
||||
asciicast = Asciicast.find(asciicast_id)
|
||||
|
||||
in_data_file = Tempfile.new('asciiio-data', :encoding => 'ascii-8bit')
|
||||
in_data_file.write(asciicast.stdout.read)
|
||||
in_data_file.close
|
||||
in_data_path = in_data_file.path
|
||||
|
||||
in_timing_file = Tempfile.new('asciiio-timing', :encoding => 'ascii-8bit')
|
||||
in_timing_file.write(asciicast.stdout_timing.read)
|
||||
in_timing_file.close
|
||||
in_timing_path = in_timing_file.path
|
||||
|
||||
out_data_path = in_data_path + '.ts'
|
||||
out_timing_path = in_timing_path + '.ts'
|
||||
|
||||
if system "bash -c './script/convert-to-typescript.sh #{in_data_path} #{in_timing_path} #{out_data_path} #{out_timing_path}'"
|
||||
delay = (asciicast.duration / 2).to_i
|
||||
command = "scriptreplay #{out_timing_path} #{out_data_path}; sleep 10"
|
||||
puts '-' * 80
|
||||
system "bash -c 'ASCIICAST_ID=#{asciicast_id} COLS=#{asciicast.terminal_columns} LINES=#{asciicast.terminal_lines} COMMAND=\"#{command}\" DELAY=#{delay} THUMB_LINES=10 THUMB_COLS=20 ./script/thumbnail.sh'"
|
||||
else
|
||||
puts "failed"
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
SIDEKIQ_URL = 'redis://localhost:6379'
|
||||
SIDEKIQ_NS = 'asciiio-sidekiq'
|
||||
|
||||
Sidekiq.configure_server do |config|
|
||||
config.redis = {
|
||||
:url => SIDEKIQ_URL,
|
||||
:namespace => SIDEKIQ_NS
|
||||
}
|
||||
end
|
||||
|
||||
Sidekiq.configure_client do |config|
|
||||
config.redis = {
|
||||
:url => SIDEKIQ_URL,
|
||||
:namespace => SIDEKIQ_NS,
|
||||
:size => 1
|
||||
}
|
||||
end
|
@ -0,0 +1,6 @@
|
||||
class AddSnapshotToAsciicast < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :asciicasts, :snapshot, :text
|
||||
|
||||
end
|
||||
end
|
@ -0,0 +1,4 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe ApplicationDecorator do
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe AsciicastDecorator do
|
||||
before { ApplicationController.new.set_current_view_context }
|
||||
end
|
Loading…
Reference in New Issue