Preview GIF created with ScreenScrot:
Take a look about Moriarty on GitHub:
decentralizuj / moriarty
Moriarty - Tool to check social networks for available username
Open-Source Development need donations, in my case that's link/repo sharing, instead of Bitcoin. Comments, discussions and opinions are highly appreciated (good or bad).
Introduction
GIF recording is great to show your friends how the script work. I was looking for an easy way to capture my script output into GIF, but somehow I couldn't find anything that suit my needs. I decided to write few lines of code, and to make it work just like I want.
First, I want to capture screenshots from my terminal, and to have it saved in PNG, so I can use those photos as background or something. I want to start it from terminal, and to record only active window, but with ability to record everything sometimes (or to select with mouse). At the end, I want to enter for how long to capture screenshots, before gif creation. Let's start!
Install SCROT and ImageMagick
On ubuntu-like linux:
sudo apt update -qq
sudo apt install scrot imagemagick -y
Now it's time to write some ruby code to do what we want. Create file called 'screenscrot.rb'.
Create class ScreenScrot
Create class, define constants and make initializer accept args or use those constants.
# capture screenshots on linux with ruby and scrot
# save photos as .png
# create gif with imagemagick
class ScreenScrot
TITLE = 'screenscrot' # name of file and folder to create
PATH = Dir.pwd # define path to root directory
EXT = :png # screenshots extension
attr_reader :title, :ext, :path, :directory, :c
def initialize( title = TITLE, path = PATH, ext = EXT )
@c = 0
@path, @ext, @title = path.to_s, ext.to_s, title.to_s
@path += '/' unless @path.end_with?('/')
@filename = @title + '_' + rand(999).to_s
@directory = @path + @filename + '/'
system "mkdir -p #{@directory}" unless Dir.exist?(@directory)
Dir.chdir(@directory)
end
end
Here, TITLE is constant to use if parameter title is not defined. Path is path to the working directory, and extension is for captured screenshots. Our new object accept all those parameters, or will use this ones if not defined. After title I added random number, so I do not overwrite past screenshots if I want to record it again (so if I use 'recorder', it will become 'recorder_456'. That is saved in variable @filename. We add this filename to path, together with '/' at the end, and we get @directory variable, which is path where we will save our screenshots and gif at the end). If directory not exist, it will be created and entered.
Capture screenshots
This method is here to save .png screenshot that we will use later with imagemagick.
# record active window
# use :all to capture everything
# use :select to capture select rec window
# >> @screen.capture(:all)
def capture( win = :active )
case win.to_sym
when :all then scr = '' # rec everything
when :select then scr = '-s' # select with mouse
else scr = '-u' # active window (default)
end
# without name scrot add timestamp, but that is too slow
# if use .to_i we get 1 screenshot per second only
# so we use .to_f and then remove '.' from name
screenshot = @directory + Time.now.to_f.to_s.gsub!('.', '')
# prepend command for scrot, add counter to the name
# so we can save all frames without problems
exec = "scrot #{scr} -q 10 #{screenshot + @c.to_s}.#{@ext}"
`exec`
@c += 1 # captured frames counter
end
With this we have option to save screenshots into folder, with chosen quality, extension and part of screen to save. Default is active window, so part of screen that is active when recording start. If you change terminal, do it on same part of screen. Otherwise use :select, so you click with mouse on window you want to record. Last option is :all, that will record entire screen.
Convert into GIF
After getting screenshots, we will use ImageMagick's convert
to create animated GIF.
def to_gif( name = nil, opts = {} )
# if name param is nil or '' use default title
name = @filename if name.empty?
@gif = name.to_s
# accept options hash or use default values
opts[:dir] ||= @directory
opts[:ext] ||= @ext
opts[:delay] ||= 20
opts[:loop] ||= 2
Dir.chdir(opts[:dir])
# add .gif to the filename, if do not contain
@gif += '.gif' unless @gif.end_with? '.gif'
# construct ImageMagick's convert command from parameters
exec = "convert -delay #{opts[:delay]} -loop #{opts[:loop]} *.#{opts[:ext]} #{@gif}"
# create GIF
`exec`
end
That's it! Method #to_gif
accept filename and options hash as parameters, or will use default values. Default filename is @title, and delay is pause in microseconds between two frames. I will add some sleep between capturing screenshots because I do not need all those frames for terminal recording. Loop param define how many times to repeat animation. Zero is infinite loop, but all other values will be increased by one (if loop is 1, it's played once. If loop is 2, animation is played and repeated two more times, which is 3 times at all).
GIF created this way is high quality, even when png quality in scrot command is set to 10 (100 is max). This lead to pretty big file, but still it's ok for short videos. This can be fixed with more arguments in convert command.
Run script
To run everything, I just made it simple. Accept time to record, gif name and part of screen to record as arguments. First argument is time to record, default is 60. Second is name, default is screenscrot. Third arg is part of screen, default is active (that's why script count 5 seconds, so you can activate window to rec).
# enter name as second argument or use hardcoded TITLE
puts "Start Recording Screenshots..."
name = ARGV[1] or ScreenScrot::TITLE
# initialize new object with name
@screen = ScreenScrot.new name
# clean terminal and count 5 seconds before start
system 'clear'
cnt = 5
cnt.times do
puts "Starting in #{cnt -= 1}"
sleep 1
system 'clear'
end
puts "[Screenshot Recording Started]"
str_time = Time.now
# add recording time as first argument, in seconds
# default is 60
max_time = ARGV.empty? ? 60 : ARGV[0].to_i
# check args for window recording type
# if contain --all or --select, use it, else use :active
display = :active
display = :select if desplay.include?('--select')
display = :all if display.include?('--all')
# save screenshots untill time elapsed
# add small sleep because we record terminal
until (Time.now - str_time).to_i >= max_time do
@screen.capture(display)
# edit or remove sleep, per use-case
sleep 0.2
end
# notify about end, print time in minutes and number of frames
print "[Screenshot Recording Stopped] after "
puts "#{(Time.now - str_time).to_f.round(2)} seconds"
# at the end, create GIF animation
puts "\n [GIF!] - Start creating gif animation:"
puts " Name: #{@screen.gif}"
puts " Path: #{@screen.directory}\n"
@screen.to_gif
# notify user about end, print file name and directory
print "[CREATED!] - #{@screen.c.to_s} frame"
print "s" unless @screen.c.to_s.end_with?('1')
puts "collected into GIF animation."
puts
With this you can make nice gif animation of terminal, for showcase or to record tests...
ruby screenscrot.rb 60 myfilename --all
Top comments (0)