I looked over the code that was written by GravityScore for LightShot, and it seemed really and long and fairly complex.
This script is a lot shorter and a bit less complex. It is lightweight and the saving and loading speeds are much faster than that of ScreenCapture by Ardera. However, it doesn't save nearly as fast as GravityScore's LightShot because, although I'm not entirely sure if this is true, I believe it writes the recording data to a file as its recording rather than afterwards.
I wrote this script in about half an hour, so it is likely to have a couple of bugs, but hopefully not too many.
* Fixed choppiness in playing back recordings!
* Fixed a bug where the program would crash if the recording was too long.
* Added a little prompt to tell you how far along in the saving the program is.
* Made saving and loading recordings more than ten times faster!
Code:
Spoiler
local tArgs = { ... } -- Capture arguments.
--[[
Recorder PaymentOption
22 January 2013
This is a simple implementation of
screen recording software that seems
to be so popular now-a-days.
]]--
-- Variables --
local NATIVE_TERM = {} -- A copy of the native terminal.
local recordingTerm = {} -- The terminal which will house edited functions for recording.
local recordingTable = {} -- The table that will record all calls by the terminal.
local RECORDING_PATH = nil -- The path that the user wants to save a new recording to.
local END_RECORDING = "Recording finished." -- The string that is returned by the shell thread when it has exited.
-- Below is the thread which houses the shell which will be used to record the terminal output of the computer.
-- This thread returns once the user has exited the shell by running the 'exit' program on their machine.
local shellThread = coroutine.create(function()
while true do
local eventData = { os.pullEvent() }
local hasFinished = coroutine.resume(os.run({["shell"] = shell}, "rom/programs/shell"), unpack(eventData))
-- In the case that this shell is exited because the 'exit' program was run, then we need to mimic the shell
-- and almost shutdown the computer, except we'll just return from this recording.
if hasFinished then
return END_RECORDING
end
end
end)
-- Variables --
-- Terminal Functions --
-- Creates and returns a copy of the current terminal table.
function createTerminalCopy()
local copy = {}
for itemName, item in pairs(term.native) do
copy[itemName] = item
end
return copy
end
-- Takes a terminal table and recording table and modifies the functions of
-- the terminal table to record the screen.
-- In a recording table, entries are made in the format: t[n] = {functionName = (string), parameters = (variable number of arguemts ( ... ))}
function modifyTerminalForRecording(terminalTable, recordingTable)
-- Returns a modified function that acts as an old terminal function by
-- adding the call of the function to the recording table and calling the old
-- function from the terminalTable.
local function modifyFunction(functionName, recordingTable)
return function ( ... )
recordingTable[#recordingTable + 1] = {functionName = functionName, parameters = { ... }, currentTime = os.clock()}
return NATIVE_TERM[functionName]( ... )
end
end
for itemName, item in pairs(terminalTable) do
terminalTable[itemName] = modifyFunction(itemName, recordingTable)
end
return terminalTable
end
-- Terminal Functions --
-- File Functions --
-- Writes a recording to a file. Takes a recording table.
function writeRecordingToFile(recordingTable, filePath)
local fileHandle = fs.open(filePath, 'w')
print("Saving recording... ")
fileHandle.writeLine(textutils.serialize(recordingTable))
fileHandle.close()
end
-- Reads a recording from a file. Returns the recording table.
function readRecordingFromFile(filePath)
local fileHandle = fs.open(filePath, 'r')
local recordingTable = textutils.unserialize(fileHandle.readLine())
fileHandle.close()
return recordingTable
end
-- File Functions --
-- Playback Functions --
-- Plays back a recording table.
function playRecording(recordingTable)
NATIVE_TERM.clear()
NATIVE_TERM.setCursorPos(1, 1)
print(#recordingTable)
-- Sleep the difference between each call, assuming this isn't the first call.
for itemIndex, item in pairs(recordingTable) do
-- Make sure that the difference for sleeping is greater than 0.3. If this isn't the case, then don't sleep at all.
if itemIndex > 1 and item.currentTime - recordingTable[itemIndex - 1].currentTime > 0 then
sleep(item.currentTime - recordingTable[itemIndex - 1].currentTime)
end
NATIVE_TERM[item.functionName](unpack(item.parameters))
end
NATIVE_TERM.clear()
NATIVE_TERM.setCursorPos(1, 1)
end
-- Playback Functions --
-- Initialization --
NATIVE_TERM = createTerminalCopy()
recordingTerm = createTerminalCopy()
recordingTerm = modifyTerminalForRecording(recordingTerm, recordingTable)
-- Initialization --
-- Handle arguments.
if #tArgs > 0 then
-- If the user wants to play a recording, then attempt to load it.
if fs.exists(tArgs[1]) and not fs.isDir(tArgs[1]) then
playRecording(readRecordingFromFile(tArgs[1]))
print("End of recording at " .. tArgs[1] .. '.')
return
-- If the user wants to record a new recording, then do that.
-- Don't return here.
elseif tArgs[1] == "record" and not fs.exists(tArgs[2]) then
RECORDING_PATH = tArgs[2]
term.clear()
term.setCursorPos(1, 1)
print("Run the 'exit' program in the shell to stop recording and save.")
sleep(1)
else
-- If none of the argument setups match, then print the usage.
print("Usage: " .. shell.getRunningProgram() .. " <recording path>")
print(" " .. shell.getRunningProgram() .. " record <new recording path>")
return
end
else
-- If none of the argument setups match, then print the usage.
print("Usage: " .. shell.getRunningProgram() .. " <recording path>")
print(" " .. shell.getRunningProgram() .. " record <new recording path>")
return
end
-- Handle arguments.
-- Main entry point --
-- Redirect terminal output to the modified terminal table.
term.redirect(recordingTerm)
term.clear()
term.setCursorPos(1, 1)
-- Queue a couple of events to get the shell going.
os.queueEvent("char", '')
os.queueEvent("char", '')
while coroutine.status(shellThread) ~= "dead" do
local eventData = { os.pullEvent() }
coroutine.resume(shellThread, unpack(eventData))
end
-- Once the thread has died, redirect the terminal output back to the standard terminal
-- and replay the recording.
term.redirect(NATIVE_TERM)
NATIVE_TERM.clear()
NATIVE_TERM.setCursorPos(1, 1)
writeRecordingToFile(recordingTable, RECORDING_PATH)
print("Finished recording. Saved as " .. RECORDING_PATH)
-- Main entry point --
Pastebin link: http://pastebin.com/K8aagw9K
There probably won't be any screenshots considering how easy this program is to use. The usage is displayed by the program so I won't bother explaining it here.
- Payment