Compare commits

..

4 commits

Author SHA1 Message Date
Prunebutt
c8ce4db2a2 split up the flake better 2025-09-27 18:54:24 +02:00
Prunebutt
d6e6bd7768 making scanbuddy server more verbose 2025-09-27 18:54:09 +02:00
Prunebutt
465a5574b8 use pyproject.toml 2025-09-27 15:51:47 +02:00
Prunebutt
b421e330f4 cleanup scanbuddy script 2025-09-27 15:51:35 +02:00
6 changed files with 236 additions and 110 deletions

23
flake.lock generated
View file

@ -16,9 +16,30 @@
"type": "github"
}
},
"pyproject-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1758265079,
"narHash": "sha256-amLaLNwKSZPShQHzfgmc/9o76dU8xzN0743dWgvYlr8=",
"owner": "nix-community",
"repo": "pyproject.nix",
"rev": "02e9418fd4af638447dca4b17b1280da95527fc9",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "pyproject.nix",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs",
"pyproject-nix": "pyproject-nix"
}
}
},

View file

@ -1,59 +1,85 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
pyproject-nix = {
url = "github:nix-community/pyproject.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
...
} @ inputs: let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
in {
devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [
python313
python313Packages.flask
python313Packages.waitress
];
shellHook = ''
python --version
exec zsh
'';
python = pkgs.python3;
serverProject = inputs.pyproject-nix.lib.project.loadPyproject {
projectRoot = ./.;
};
packages.${system} = let
scanbuddy = pkgs.writeShellApplication {
name = "scanbuddy-script";
# Packages
script = pkgs.writeShellApplication {
name = "scanbuddy";
runtimeInputs = with pkgs; [sane-backends brscan5 ghostscript];
text = builtins.readFile ./scanbuddy.bash;
};
wrapper = pkgs.writeShellApplication {
name = "scanbuddy-wrapper";
text =
/*
bash
*/
''
#!/usr/bin/env bash
readarray -d '_' args < <(printf "%s" "$1")
# Returns an attribute set that can be passed to `buildPythonPackage`.
attrs = serverProject.renderers.buildPythonPackage {inherit python;};
server = python.pkgs.buildPythonPackage (attrs
// {
env.CUSTOM_ENVVAR = "foobar";
});
scanbuddy "''${args[@]}"
'';
};
package = pkgs.symlinkJoin {
scanbuddy-pkg = pkgs.symlinkJoin {
name = "scanbuddy";
paths = [scanbuddy wrapper];
paths = [script server];
};
in {
default = package;
inherit scanbuddy;
inherit wrapper;
# devShells.${system}.default = pkgs.mkShell {
# packages = with pkgs; [
# python313
# python313Packages.flask
# python313Packages.waitress
# ];
#
# shellHook = ''
# python --version
# exec zsh
# '';
# };
packages.${system} = {
default = scanbuddy-pkg;
inherit script;
inherit server;
};
nixosModules.default = {
config,
pkgs,
lib,
...
}: {
options = {
service.scanbuddy = lib.mkEnableOption "Enable the scanbuddy server";
};
config = {
systemd.services.scanbuddy = lib.mkIf config.service.scanbuddy {
description = "The scanbuddy webservice";
wantedBy = ["multi-user.target"];
serviceConfig = {
ExecStart = "${scanbuddy-pkg}/bin/scanbuddy-server";
Path = ["${scanbuddy-pkg}/bin"];
WorkingDirectory = "/var/lib/scanbuddy";
};
};
};
};
};
}

15
pyproject.toml Normal file
View file

@ -0,0 +1,15 @@
[project]
name = "scanbuddy-server"
version = "0.1.0"
description = "A REST server for the scanbuddy shell-script"
# define any Python dependencies
dependencies = [
"flask>3",
"waitress"
]
# define the CLI executable
# Here, we define the entry point to be the 'main()' function in the module 'app/main.py'
[project.scripts]
scanbuddy-server = "server.server:main"

View file

@ -13,7 +13,6 @@ sides=$simplex_source
mode='c'
resolution='300'
current_page=$(find "$PWD" -name "out*.pdf" | sort | tail -n 1);
read -r current_pageno <<<"${current_page//[^0-9]/ }"
current_pageno=${current_pageno:-0}
@ -77,19 +76,12 @@ scan() {
-x 210 -y 297 # A4
}
if [ "$#" -lt 1 ]; then
echo "You need an argument!"
exit 1
fi
clean() {
echo "Cleaning up..."
rm -rfv "$PWD/out*.pdf"
}
case "$1" in
scan)
scan "${@:2}"
;;
unscan)
rm "$last_out_file"
;;
dispatch)
dispatch() {
readarray -d '' out_files < <(find . -regextype posix-awk -regex "\./out[0-9]+\.pdf" -print0 | sort -Vz)
combined_file=$(mktemp --suff="_scanbuddy.pdf")
gs -q -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE="$combined_file" -dBATCH "${out_files[@]}"
@ -107,8 +99,29 @@ case "$1" in
else
printf "Paperless consumption job: %s\n" "$PAPERLESS_STATUS"
fi
clean
}
if [ "$#" -lt 1 ]; then
echo "You need an argument!"
exit 1
fi
case "$1" in
scan)
scan "${@:2}"
;;
unscan)
rm "$last_out_file"
;;
dispatch)
dispatch "${@:2}"
;;
clean)
clean
;;
*)
echo "Usage: scanbuddy"
echo "Usage: scanbuddy scan|unscan|dispatch|clean"
;;
esac

View file

@ -1,52 +0,0 @@
#!/usr/bin/env python3
# Run this stuff with `waitress-serve --port=5000 --call server:create_app`
import os
import subprocess
from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def hello_world():
return "Hello, World! Yoyoyo"
@app.route("/scan")
def scan():
command = ["bash", f"{os.getcwd()}/scanbuddy.bash", "scan"]
command += ["-d"] if (request.args.get("duplex", "n") == 'y') else []
result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
output = result.stdout.strip()
print("Scanbuddy output: ")
print(output)
return output
# return f"Scanning {'duplex' if duplex == 'y' else ''}..."
@app.route("/dispatch")
def dispatch():
command = ["bash", f"{os.getcwd()}/scanbuddy.bash", "dispatch"]
user = request.args.get("user", "")
result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
output = result.stdout.strip()
print("Scanbuddy output: ")
print(output)
return "Dispatching..."
@app.route("/unscan")
def unscan():
return "Unscanning..."
def create_app():
return app
if __name__=='__main__':
from waitress import serve
serve(app, host="0.0.0.0", port=8080)

103
server/server.py Executable file
View file

@ -0,0 +1,103 @@
#!/usr/bin/env python3
# Run this stuff with `waitress-serve --port=5000 --call server:create_app`
import os
import subprocess
from flask import Flask, request
app = Flask(__name__)
SCANBUDDY_COMMAND = ["bash", f"{os.getcwd()}/scanbuddy.bash"]
@app.route("/")
def hello_world():
return ("Scanbuddy server. Go to <ul>"
" <li><a href=\"scan\">scan</a></li>"
" <li><a href=\"unscan\">unscan</a></li>"
" <li><a href=\"dispatch\">dispatch</a></li>"
" <li><a href=\"clean\">clean</a></li>"
"</ul>")
@app.route("/scan")
def scan():
print("Scanning...")
command = SCANBUDDY_COMMAND + ["scan"]
duplex = request.args.get("duplex", "n") == 'y'
resolution = request.args.get("resolution", None)
if duplex:
print("Duplex: on")
command += ["-d"]
if resolution:
print(f"Res.: {int(resolution)}")
# turning the string to an int and back to a string should clean up the input
command += ["-r", f"{int(resolution)}"]
result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
output = result.stdout.strip()
print("Scanbuddy output: ")
print(output)
return output
# return f"Scanning {'duplex' if duplex == 'y' else ''}..."
@app.route("/dispatch")
def dispatch():
print("Dispatching...")
command = SCANBUDDY_COMMAND + ["dispatch"]
# TODO: implement user
user = request.args.get("user", "")
result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
output = result.stdout.strip()
print("Scanbuddy output: ")
print(output)
return output
@app.route("/unscan")
def unscan():
print("Unscanning...")
command = SCANBUDDY_COMMAND + ["unscan"]
result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
output = result.stdout.strip()
print("Scanbuddy output: ")
print(output)
return output
@app.route("/clean")
def clean():
print("Cleaning...")
command = SCANBUDDY_COMMAND + ["clean"]
result = subprocess.run(command, stdout=subprocess.PIPE, text=True)
output = result.stdout.strip()
print("Scanbuddy output: ")
print(output)
return output
def create_app():
return app
def main():
from waitress import serve
print("Serving scanbuddy on port 5000")
serve(app, host="0.0.0.0", port=5000)
if __name__=='__main__':
main()