Separate providers module.
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,2 +1,12 @@
|
|||||||
|
# Ignore all
|
||||||
|
*
|
||||||
|
|
||||||
|
# Unignore all with extensions
|
||||||
|
!*.*
|
||||||
|
|
||||||
|
# Unignore all dirs
|
||||||
|
!*/
|
||||||
|
|
||||||
|
### Above combination will ignore all files without extension ###
|
||||||
bin/
|
bin/
|
||||||
.vscode/
|
.vscode/
|
||||||
@@ -9,7 +9,7 @@ srcDir = "src"
|
|||||||
bin = @["randopix"]
|
bin = @["randopix"]
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
requires "nim >= 1.0.0", "gintro <= 0.5.5", "argparse >=0.10.1"
|
requires "nim >= 1.0.0", "gintro >= 0.5.5", "argparse >=0.10.1"
|
||||||
|
|
||||||
task debug, "Compile debug version":
|
task debug, "Compile debug version":
|
||||||
exec "nim c -d:debug --debugger:native --out:bin/randopix src/randopix.nim"
|
exec "nim c -d:debug --debugger:native --out:bin/randopix src/randopix.nim"
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import os, sets, random
|
|
||||||
|
|
||||||
const
|
|
||||||
supportedExts = @[".png", ".jpg", ".jpeg"]
|
|
||||||
|
|
||||||
type
|
|
||||||
FileProvider* = ref object
|
|
||||||
exts: HashSet[string]
|
|
||||||
path*: string
|
|
||||||
files*: seq[string]
|
|
||||||
|
|
||||||
proc load*(fp: FileProvider) =
|
|
||||||
## Reload the file list
|
|
||||||
if fp.path == "":
|
|
||||||
return
|
|
||||||
|
|
||||||
for file in walkDirRec(fp.path):
|
|
||||||
let split = splitFile(file)
|
|
||||||
if fp.exts.contains(split.ext):
|
|
||||||
fp.files.add(file)
|
|
||||||
|
|
||||||
randomize()
|
|
||||||
shuffle(fp.files)
|
|
||||||
|
|
||||||
proc next*(fp: FileProvider): string =
|
|
||||||
if fp.files.len < 1:
|
|
||||||
fp.load
|
|
||||||
result = fp.files[0]
|
|
||||||
fp.files.delete(0)
|
|
||||||
|
|
||||||
proc newFileProvider*(path: string): FileProvider =
|
|
||||||
result = FileProvider(path: path, exts: supportedExts.toHashSet)
|
|
||||||
result.load
|
|
||||||
73
src/providers.nim
Normal file
73
src/providers.nim
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import os, sets, random, httpClient, json
|
||||||
|
import gintro/[gdkpixbuf]
|
||||||
|
|
||||||
|
const
|
||||||
|
supportedExts = @[".png", ".jpg", ".jpeg"]
|
||||||
|
foxesUrl = "https://randomfox.ca/floof/"
|
||||||
|
|
||||||
|
type
|
||||||
|
ProviderKind* {.pure.} = enum
|
||||||
|
Foxes = "foxes" ## Some nice foxes
|
||||||
|
Inspiro = "inspiro" ## Inspiring nonsense
|
||||||
|
File = "file" ## Images from a local path
|
||||||
|
|
||||||
|
ImageProvider* = ref object
|
||||||
|
case kind: ProviderKind
|
||||||
|
of ProviderKind.Foxes, ProviderKind.Inspiro:
|
||||||
|
url: string
|
||||||
|
of ProviderKind.File:
|
||||||
|
exts: HashSet[string]
|
||||||
|
path*: string
|
||||||
|
files*: seq[string]
|
||||||
|
|
||||||
|
var client = newHttpClient() ## For loading images from the web
|
||||||
|
|
||||||
|
proc downloadFox(ip: ImageProvider): Pixbuf =
|
||||||
|
## Download image from the fox API
|
||||||
|
let urlData = client.getContent(ip.url)
|
||||||
|
let info = parseJson(urlData)
|
||||||
|
let imageData = client.getContent(info["image"].getStr)
|
||||||
|
let loader = newPixbufLoader()
|
||||||
|
discard loader.write(imageData)
|
||||||
|
loader.getPixbuf()
|
||||||
|
|
||||||
|
proc reloadFileList(ip: ImageProvider) =
|
||||||
|
## Reload the file list
|
||||||
|
if ip.path == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
for file in walkDirRec(ip.path):
|
||||||
|
let split = splitFile(file)
|
||||||
|
if ip.exts.contains(split.ext):
|
||||||
|
ip.files.add(file)
|
||||||
|
|
||||||
|
randomize()
|
||||||
|
shuffle(ip.files)
|
||||||
|
|
||||||
|
proc next*(ip: ImageProvider): Pixbuf =
|
||||||
|
## Return a new image from the chosen image source
|
||||||
|
case ip.kind
|
||||||
|
of ProviderKind.Foxes, ProviderKind.Inspiro:
|
||||||
|
return ip.downloadFox
|
||||||
|
of ProviderKind.File:
|
||||||
|
if ip.files.len < 1:
|
||||||
|
ip.reloadFileList
|
||||||
|
result = ip.files[0].newPixbufFromFile
|
||||||
|
ip.files.delete(0)
|
||||||
|
|
||||||
|
proc newFileProvider(path: string): ImageProvider =
|
||||||
|
result = ImageProvider(kind: ProviderKind.File, path: path, exts: supportedExts.toHashSet)
|
||||||
|
result.reloadFileList
|
||||||
|
|
||||||
|
proc newFoxProvider(): ImageProvider = ImageProvider(kind: ProviderKind.Foxes, url: foxesUrl)
|
||||||
|
|
||||||
|
proc newImageProvider*(kind: ProviderKind, filePath: string = ""): ImageProvider =
|
||||||
|
## Create a new `ImageProvider` for the API chosen with thge `kind` parameter
|
||||||
|
case kind
|
||||||
|
of ProviderKind.Foxes:
|
||||||
|
newFoxProvider()
|
||||||
|
of ProviderKind.Inspiro:
|
||||||
|
# TODO
|
||||||
|
newFoxProvider()
|
||||||
|
of ProviderKind.File:
|
||||||
|
newFileProvider(filePath)
|
||||||
@@ -1,31 +1,22 @@
|
|||||||
import httpClient, json, os, options, strformat
|
import os, options, strformat
|
||||||
import gintro/[gtk, glib, gobject, gio, gdkpixbuf]
|
import gintro/[gtk, glib, gobject, gio, gdkpixbuf]
|
||||||
import gintro/gdk except Window
|
import gintro/gdk except Window
|
||||||
import argparse except run
|
import argparse except run
|
||||||
import fileAccess
|
import providers
|
||||||
|
|
||||||
const
|
const
|
||||||
css = slurp("app.css")
|
css = slurp("app.css")
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
floofUrl = "https://randomfox.ca/floof/"
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Mode {.pure.} = enum
|
|
||||||
Foxes = "foxes" ## Some nice foxes
|
|
||||||
Inspiro = "inspiro" ## Inspiring nonsense
|
|
||||||
File = "file" ## Images from a local path
|
|
||||||
|
|
||||||
Args = object
|
Args = object
|
||||||
fullscreen: bool ## Applicaion is show in fullscreen mode
|
fullscreen: bool ## Applicaion is show in fullscreen mode
|
||||||
verbose: bool ## More debug information in notification label
|
verbose: bool ## More debug information in notification label
|
||||||
mode: Option[Mode] ## The chosen image source
|
|
||||||
path: string ## File mode only: the path to the images
|
|
||||||
timeout: int ## Milliseconds between image refreshes
|
timeout: int ## Milliseconds between image refreshes
|
||||||
|
|
||||||
var
|
var
|
||||||
client = newHttpClient() ## For loading images from the web
|
imageProvider: ImageProvider ## Gets images from the chosen source
|
||||||
fileProvider: FileProvider ## Gets images from the chosen source
|
args: Args ## The parsed command line args
|
||||||
args: Args ## The parsed command line args
|
|
||||||
# Widgets
|
# Widgets
|
||||||
window: ApplicationWindow
|
window: ApplicationWindow
|
||||||
imageWidget: Image
|
imageWidget: Image
|
||||||
@@ -44,74 +35,41 @@ proc notify(label: Label, message: string = "") =
|
|||||||
else:
|
else:
|
||||||
label.show
|
label.show
|
||||||
|
|
||||||
proc newArgs(): Args =
|
proc newArgs(): Option[Args] =
|
||||||
let p = newParser("randopix"):
|
let p = newParser("randopix"):
|
||||||
help(fmt"Version {version} - Display random images from different sources")
|
help(fmt"Version {version} - Display random images from different sources")
|
||||||
option("-m", "--mode", help="The image source mode.", choices=enumToStrings(Mode))
|
option("-m", "--mode", help="The image source mode.", choices=enumToStrings(ProviderKind))
|
||||||
option("-p", "--path", help="Path to a directory with images ('file' mode only)")
|
option("-p", "--path", help="Path to a directory with images ('file' mode only)")
|
||||||
option("-t", "--timeout", help="Seconds before the image is refreshed", default="300")
|
option("-t", "--timeout", help="Seconds before the image is refreshed", default="300")
|
||||||
flag("-w", "--windowed", help="Do not start in fullscreen mode")
|
flag("-w", "--windowed", help="Do not start in fullscreen mode")
|
||||||
flag("-v", "--verbose", help="Show more information")
|
flag("-v", "--verbose", help="Show more information")
|
||||||
let opts = p.parse(commandLineParams())
|
|
||||||
var mode: Option[Mode]
|
|
||||||
if (opts.mode == ""):
|
|
||||||
echo p.help
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mode = some(parseEnum[Mode](opts.mode))
|
let opts = p.parse(commandLineParams())
|
||||||
except ValueError:
|
let mode = some(parseEnum[ProviderKind](opts.mode))
|
||||||
echo fmt"Invaild mode: {opts.mode}"
|
imageProvider = newImageProvider(mode.get, opts.path)
|
||||||
|
## Timeout is given in seconds as an argument
|
||||||
|
var timeout = 3000
|
||||||
|
try:
|
||||||
|
timeout = opts.timeout.parseInt * 1000
|
||||||
|
except ValueError:
|
||||||
|
raise newException(UsageError, fmt"Invalid timeout value: {opts.timeout}")
|
||||||
|
|
||||||
|
return some(Args(
|
||||||
|
fullscreen: not opts.windowed,
|
||||||
|
verbose: opts.verbose,
|
||||||
|
timeout: timeout))
|
||||||
|
except UsageError:
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
echo p.help
|
echo p.help
|
||||||
return
|
|
||||||
|
|
||||||
if (mode.get == Mode.File):
|
|
||||||
fileProvider = newFileProvider(opts.path)
|
|
||||||
|
|
||||||
## Timeout is given in seconds as an argument
|
|
||||||
var timeout = 3000
|
|
||||||
try:
|
|
||||||
timeout = opts.timeout.parseInt * 1000
|
|
||||||
except ValueError:
|
|
||||||
echo "Invalid timeout: ", opts.timeout
|
|
||||||
|
|
||||||
Args(
|
|
||||||
fullscreen: not opts.windowed,
|
|
||||||
verbose: opts.verbose,
|
|
||||||
mode: mode,
|
|
||||||
path: opts.path,
|
|
||||||
timeout: timeout)
|
|
||||||
|
|
||||||
proc downloadFox(): Pixbuf =
|
|
||||||
let urlData = client.getContent(floofUrl)
|
|
||||||
let info = parseJson(urlData)
|
|
||||||
let imageData = client.getContent(info["image"].getStr)
|
|
||||||
let loader = newPixbufLoader()
|
|
||||||
discard loader.write(imageData)
|
|
||||||
loader.getPixbuf()
|
|
||||||
|
|
||||||
proc getLocalImage(): Pixbuf =
|
|
||||||
## let the file provider serve another image
|
|
||||||
fileProvider.next.newPixbufFromFile
|
|
||||||
|
|
||||||
proc tryGetImage(): Option[Pixbuf] =
|
|
||||||
## Get the raw image from an image provider
|
|
||||||
## The kind of image is based on the command line args
|
|
||||||
if args.mode.isSome:
|
|
||||||
case args.mode.get
|
|
||||||
of Mode.Foxes:
|
|
||||||
result = some(downloadFox())
|
|
||||||
of Mode.Inspiro:
|
|
||||||
echo "Not Implemented"
|
|
||||||
of Mode.File:
|
|
||||||
result = some(getLocalImage())
|
|
||||||
|
|
||||||
proc updateImage(): bool =
|
proc updateImage(): bool =
|
||||||
## Updates the UI with a new image
|
## Updates the UI with a new image
|
||||||
# Loading new image
|
# Loading new image
|
||||||
try:
|
try:
|
||||||
if (args.verbose): echo "Refreshing..."
|
if (args.verbose): echo "Refreshing..."
|
||||||
let data = tryGetImage();
|
# TODO better error signalling from providers.nim
|
||||||
|
let data = some(imageProvider.next)
|
||||||
if data.isNone:
|
if data.isNone:
|
||||||
label.notify "No image to display..."
|
label.notify "No image to display..."
|
||||||
return false;
|
return false;
|
||||||
@@ -186,9 +144,11 @@ proc connectSignals(app: Application) =
|
|||||||
|
|
||||||
proc appActivate(app: Application) =
|
proc appActivate(app: Application) =
|
||||||
# Parse arguments from the command line
|
# Parse arguments from the command line
|
||||||
args = newArgs()
|
let parsed = newArgs()
|
||||||
# No mode was given, exit and display the help text
|
if parsed.isNone:
|
||||||
if (args.mode.isNone): return
|
return
|
||||||
|
else:
|
||||||
|
args = parsed.get
|
||||||
|
|
||||||
window = newApplicationWindow(app)
|
window = newApplicationWindow(app)
|
||||||
window.title = "randopix"
|
window.title = "randopix"
|
||||||
|
|||||||
Reference in New Issue
Block a user