Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 243a41fa7a | |||
| 7cce11dc66 | |||
| fc808a6c28 |
@@ -10,6 +10,8 @@ bin = @["randopix", "pixctrl"]
|
||||
|
||||
# Dependencies
|
||||
requires "nim >= 1.0.0", "gintro", "argparse", "jester", "ajax"
|
||||
# Not on nimble yet
|
||||
requires "https://github.com/luxick/op.git >= 1.0.0"
|
||||
|
||||
proc genJS =
|
||||
echo "Generating JS Client"
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import os, sets, random, httpClient, json, strutils, strformat, options, deques, times
|
||||
from lenientops import `*`
|
||||
import gintro/[gdkpixbuf, gobject]
|
||||
import op, gintro/[gdkpixbuf, gobject]
|
||||
import common
|
||||
|
||||
const
|
||||
supportedExts = @[".png", ".jpg", ".jpeg"]
|
||||
placeholderImg = slurp("resources/blank.png")
|
||||
foxesUrl = "https://randomfox.ca/floof/"
|
||||
inspiroUrl = "http://inspirobot.me/api?generate=true"
|
||||
|
||||
type
|
||||
FileOpResult* = object of OpResult
|
||||
file*: string
|
||||
|
||||
ImageProvider* = ref object of RootObj
|
||||
## Manages images that should be displayed
|
||||
verbose: bool ## Additional logging for the image provider
|
||||
@@ -44,12 +42,6 @@ proc newImageProvider*(verbose: bool, mode: Mode): ImageProvider =
|
||||
proc newImageProvider*(verbose: bool, mode: Mode, path: string): ImageProvider =
|
||||
newImageProvider(verbose, mode, some(path))
|
||||
|
||||
proc newFileOpResultError(msg: string): FileOpResult =
|
||||
FileOpResult(success: false, errorMsg: msg)
|
||||
|
||||
proc newFileOpResult(file: string): FileOpResult =
|
||||
FileOpResult(success: true, file: file)
|
||||
|
||||
########################
|
||||
# Utilities
|
||||
########################
|
||||
@@ -75,7 +67,14 @@ func calcImageSize(maxWidth, maxHeight, imgWidth, imgHeight: int): tuple[width:
|
||||
# Image Provider procs
|
||||
########################
|
||||
|
||||
proc getFox(ip: ImageProvider): FileOpResult =
|
||||
proc getPlaceHolder(ip: ImageProvider): OP[string] =
|
||||
## Provide the placeholder image.
|
||||
## This is used when no mode is active
|
||||
let f = fmt"{tmpFile}.blank"
|
||||
writeFile(f, placeholderImg)
|
||||
ok f
|
||||
|
||||
proc getFox(ip: ImageProvider): OP[string] =
|
||||
## Download image from the fox API
|
||||
try:
|
||||
let urlData = client.getContent(foxesUrl)
|
||||
@@ -83,15 +82,15 @@ proc getFox(ip: ImageProvider): FileOpResult =
|
||||
let imageData = client.getContent(info["image"].getStr)
|
||||
let dlFile = fmt"{tmpFile}.download"
|
||||
writeFile(dlFile, imageData)
|
||||
return newFileOpResult(dlFile)
|
||||
ok dlFile
|
||||
except JsonParsingError:
|
||||
ip.log fmt"Error while fetching from fox API: {getCurrentExceptionMsg()}"
|
||||
return newFileOpResultError("Json parsing error")
|
||||
fail[string] "Json parsing error"
|
||||
except KeyError:
|
||||
ip.log fmt"No image in downloaded data: {getCurrentExceptionMsg()}"
|
||||
return newFileOpResultError("No image from API")
|
||||
fail[string] "No image from API"
|
||||
|
||||
proc getInspiro(ip: ImageProvider): FileOpResult =
|
||||
proc getInspiro(ip: ImageProvider): OP[string] =
|
||||
## Download and save image from the inspiro API
|
||||
try:
|
||||
let imageUrl = client.getContent(inspiroUrl)
|
||||
@@ -99,12 +98,12 @@ proc getInspiro(ip: ImageProvider): FileOpResult =
|
||||
let imageData = client.getContent(imageUrl)
|
||||
let dlFile = fmt"{tmpFile}.download"
|
||||
writeFile(dlFile,imageData)
|
||||
return newFileOpResult(dlFile)
|
||||
ok dlFile
|
||||
except:
|
||||
ip.log fmt"Unexpected error while downloading: {getCurrentExceptionMsg()}"
|
||||
return newFileOpResultError(getCurrentExceptionMsg())
|
||||
fail[string] getCurrentExceptionMsg()
|
||||
|
||||
proc getLocalFile(ip: var ImageProvider): FileOpResult =
|
||||
proc getLocalFile(ip: var ImageProvider): OP[string] =
|
||||
## Provide an image from a local folder
|
||||
|
||||
# First, check if there are still images left to be loaded.
|
||||
@@ -122,38 +121,36 @@ proc getLocalFile(ip: var ImageProvider): FileOpResult =
|
||||
for file in tmp:
|
||||
fileList.addLast(file)
|
||||
if fileList.len == 0:
|
||||
return newFileOpResultError("No files found")
|
||||
return fail[string] "No files found"
|
||||
|
||||
let next = fileList.popFirst()
|
||||
# Remove the current file after
|
||||
result = newFileOpResult(next)
|
||||
ok next
|
||||
|
||||
proc getFileName(ip: var ImageProvider): FileOpResult =
|
||||
proc getFileName(ip: var ImageProvider): OP[string] =
|
||||
## Get the temporary file name of the next file to display
|
||||
case ip.mode
|
||||
of Mode.None:
|
||||
return ip.getPlaceHolder()
|
||||
of Mode.File:
|
||||
return ip.getLocalFile()
|
||||
of Mode.Foxes:
|
||||
return ip.getFox()
|
||||
of Mode.Inspiro:
|
||||
return ip.getInspiro()
|
||||
else:
|
||||
return newFileOpResultError("Not implemented")
|
||||
|
||||
########################
|
||||
# Exported procs
|
||||
########################
|
||||
|
||||
proc next*(ip: var ImageProvider, maxWidth, maxHeight: int): FileOpResult =
|
||||
proc next*(ip: var ImageProvider, maxWidth, maxHeight: int): OP[string] =
|
||||
## Uses the image provider to get a new image ready to display.
|
||||
## `width` and `height` should be the size of the window.
|
||||
if ip.mode == Mode.None:
|
||||
return newFileOpResultError("No mode active")
|
||||
|
||||
let op = ip.getFileName()
|
||||
if not op.success: return op
|
||||
let r = ip.getFileName()
|
||||
if not r.isOk: return r
|
||||
|
||||
var rawPixbuf = newPixbufFromFile(op.file)
|
||||
var rawPixbuf = newPixbufFromFile(r.val)
|
||||
# Resize the pixbuf to best fit on screen
|
||||
let size = calcImageSize(maxWidth, maxHeight, rawPixbuf.width, rawPixbuf.height)
|
||||
ip.log "Scale image to: ", size
|
||||
@@ -165,13 +162,13 @@ proc next*(ip: var ImageProvider, maxWidth, maxHeight: int): FileOpResult =
|
||||
# directly setting the image from a pixbuf will leak memory
|
||||
let saved = pixbuf.savev(tmpFile, "png", @[])
|
||||
if not saved:
|
||||
return newFileOpResultError("Error while saving temporary image")
|
||||
return result.fail "Error while saving temporary image"
|
||||
|
||||
# GTK pixbuf leaks memory when not manually decreasing reference count
|
||||
pixbuf.unref()
|
||||
rawPixbuf.unref()
|
||||
|
||||
newFileOpResult(tmpFile)
|
||||
ok tmpFile
|
||||
|
||||
createDir(tmpDir)
|
||||
randomize()
|
||||
@@ -1,4 +1,5 @@
|
||||
import os, options, strformat
|
||||
import op
|
||||
import gintro/[glib, gobject, gtk, gio]
|
||||
import gintro/gdk except Window
|
||||
import argparse except run
|
||||
@@ -22,9 +23,9 @@ type
|
||||
port: int ## Port to host the control server
|
||||
|
||||
var
|
||||
imageProvider: ImageProvider ## Gets images from the chosen source
|
||||
args: Args ## The parsed command line args
|
||||
updateTimeout: int ## ID of the timeout that updates the images
|
||||
imageProvider: ImageProvider ## Gets images from the chosen source
|
||||
args: Args ## The parsed command line args
|
||||
updateTimeout, serverTimeout: int ## ID of the timeouts for image updating and server checking
|
||||
# Widgets
|
||||
window: ApplicationWindow
|
||||
label: Label
|
||||
@@ -98,19 +99,19 @@ proc updateImage(image: Image): bool =
|
||||
if imageProvider.mode == Mode.None:
|
||||
log "No display mode"
|
||||
label.notify "No mode selected"
|
||||
return true
|
||||
|
||||
var wWidth, wHeight: int
|
||||
window.getSize(wWidth, wHeight)
|
||||
|
||||
let op = imageProvider.next(wWidth, wHeight)
|
||||
result = op.success
|
||||
if not op.success:
|
||||
label.notify op.errorMsg
|
||||
let r = imageProvider.next(wWidth, wHeight)
|
||||
result = r.isOk
|
||||
if not r.isOk:
|
||||
label.notify r.error
|
||||
return
|
||||
|
||||
image.setFromFile(op.file)
|
||||
label.notify
|
||||
image.setFromFile(r.val)
|
||||
if imageProvider.mode != Mode.None:
|
||||
label.notify
|
||||
except:
|
||||
let
|
||||
e = getCurrentException()
|
||||
@@ -165,8 +166,6 @@ proc checkServerChannel(image: Image): bool =
|
||||
|
||||
else:
|
||||
log "Command ignored: ", msg.command
|
||||
|
||||
sleep(100)
|
||||
result = true
|
||||
|
||||
proc toggleFullscreen(action: SimpleAction; parameter: Variant; window: ApplicationWindow) =
|
||||
@@ -285,7 +284,7 @@ proc appActivate(app: Application) =
|
||||
## Start the server for handling incoming commands
|
||||
let serverArgs = ServerArgs(verbose: args.verbose, port: args.port)
|
||||
createThread(serverWorker, runServer, serverArgs)
|
||||
discard idleAdd(checkServerChannel, image)
|
||||
serverTimeout = int(timeoutAdd(100, checkServerChannel, image))
|
||||
|
||||
when isMainModule:
|
||||
let app = newApplication("org.luxick.randopix")
|
||||
|
||||
BIN
src/resources/blank.png
Normal file
BIN
src/resources/blank.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Reference in New Issue
Block a user