-- bimg_player.lua -- BIMG Player (Shell Version) -- Usage: bimg_player [options] -- Initialize settings local running = false local currentAnimation = nil local httpEnabled = http and http.get -- Show help message local function showHelp() print("BIMG Player - Usage:") print("bimg_player [options]") print() print("Options:") print(" --loop Loop animation") print(" --url Load from URL") print(" --monitor Play on specific monitor (top/bottom/left/right/front/back)") print(" --scale Set display scale (0.5-3)") print(" --fps Set frame rate (1-60)") print(" --help Show this help") print() print("Examples:") print("bimg_player animation.bimg --loop") print("bimg_player http://example.com/image.bimg --url --monitor top") end -- Draw single frame local function drawFrame(frame, termObj) for y, row in ipairs(frame) do termObj.setCursorPos(1, y) termObj.blit(table.unpack(row)) end if frame.palette then for i = 0, #frame.palette do local c = frame.palette[i] if type(c) == "table" then termObj.setPaletteColor(2^i, table.unpack(c)) else termObj.setPaletteColor(2^i, c) end end end end -- Load image file local function loadImageFile(path) local file, err = fs.open(shell.resolve(path), "rb") if not file then error("Cannot open file: " .. err) end local data = file.readAll() file.close() local success, img = pcall(textutils.unserialize, data) if not success then error("Invalid BIMG file: " .. img) end return img end -- Load image from URL local function loadImageURL(url) if not httpEnabled then error("HTTP API not available, cannot load from URL") end print("Downloading from URL: "..url) local response = http.get(url) if not response then error("Download failed: " .. url) end local data = response.readAll() response.close() local success, img = pcall(textutils.unserialize, data) if not success then error("Invalid BIMG data: " .. img) end return img end -- Monitor calibration local function calibrateMonitors(width, height) term.clear() term.setCursorPos(1, 1) print("Multi-Monitor Calibration Mode") print("Please right-click each monitor in order") print("From top-left to bottom-right, left to right first") local monitors = {} local names = {} for y = 1, height do monitors[y] = {} for x = 1, width do local _, oy = term.getCursorPos() -- Draw calibration UI for ly = 1, height do term.setCursorPos(3, oy + ly - 1) term.clearLine() for lx = 1, width do term.blit('\x8F ', (lx == x and ly == y) and '00' or '77', 'ff') end end term.setCursorPos(3, oy + height) term.write(string.format("Position (%d, %d)", x, y)) term.setCursorPos(1, oy) -- Wait for monitor click repeat local _, name = os.pullEvent('monitor_touch') monitors[y][x] = name until not names[name] names[monitors[y][x]] = true sleep(0.25) end end settings.set('bimg_player.multimonitor', monitors) settings.save() print("Calibration complete. Settings saved.") return monitors end -- Stop playback local function stopPlayback() if currentAnimation then currentAnimation.running = false os.queueEvent("bimg_stop") end running = false end -- Clean up terminal local function cleanupTerminal(termObj) termObj.setBackgroundColor(colors.black) termObj.setTextColor(colors.white) termObj.clear() termObj.setCursorPos(1, 1) for i = 0, 15 do termObj.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end end -- Main playback function local function playAnimation(img, options) options = options or {} local termObj = options.terminal or term local loop = options.loop or false local fps = options.fps or (img.secondsPerFrame and (1/img.secondsPerFrame)) or 20 local frameDelay = 1/fps -- Set running state running = true currentAnimation = { running = true, loop = loop } -- Playback function local function playFrames() repeat if img.multiMonitor then local width, height = img.multiMonitor.width, img.multiMonitor.height local monitors = settings.get('bimg_player.multimonitor') -- Calibrate if needed if not monitors or #monitors < height or #monitors[1] < width then monitors = calibrateMonitors(width, height) end -- Multi-monitor playback for i = 1, #img, width * height do if not currentAnimation.running then break end for y = 1, height do for x = 1, width do local frameIndex = i + (y-1) * width + x-1 if frameIndex <= #img then local monitor = peripheral.wrap(monitors[y][x]) if monitor then drawFrame(img[frameIndex], monitor) end end end end sleep(frameDelay) end else -- Single monitor playback termObj.clear() for _, frame in ipairs(img) do if not currentAnimation.running then break end drawFrame(frame, termObj) sleep(frameDelay) end end until not loop or not currentAnimation.running end -- Start playback parallel.waitForAny( function() local ok, err = pcall(playFrames) if not ok then printError("Playback error: "..err) end cleanupTerminal(termObj) running = false end, function() while currentAnimation.running do os.pullEvent("bimg_stop") currentAnimation.running = false end end ) end -- Parse command line arguments local function parseArguments(args) local options = {} local path = nil local i = 1 while i <= #args do local arg = args[i] if arg == "--loop" then options.loop = true elseif arg == "--url" then options.isURL = true elseif arg == "--monitor" then i = i + 1 options.terminal = peripheral.wrap(args[i]) if not options.terminal then error("Invalid monitor side: " .. (args[i] or "nil")) end elseif arg == "--scale" then i = i + 1 local scale = tonumber(args[i]) if scale and scale >= 0.5 and scale <= 3 then options.scale = scale else error("Invalid scale value (should be 0.5-3)") end elseif arg == "--fps" then i = i + 1 local fps = tonumber(args[i]) if fps and fps >= 1 and fps <= 60 then options.fps = fps else error("Invalid FPS value (should be 1-60)") end elseif arg == "--help" then showHelp() return nil elseif not path and not arg:find("^-") then path = arg else error("Unknown option: " .. arg) end i = i + 1 end if not path then showHelp() return nil end return path, options end -- Main program entry local function main(...) local args = {...} -- Show help if no arguments if #args == 0 then showHelp() return end -- Parse arguments local ok, path, options = pcall(parseArguments, args) if not ok then printError(path) -- Here path contains the error message showHelp() return end if not path then return end -- User requested help -- Load image local img if options.isURL then img = loadImageURL(path) else img = loadImageFile(path) end -- Apply display scale if options.scale and img.multiMonitor then img.multiMonitor.scale = options.scale end -- Start playback playAnimation(img, options) -- Wait for keypress if not looping if not options.loop then stopPlayback() end end -- Start program main(...)