From 4fc29ff0fe01d528f3e14995db6f671bea3ec858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kier=C3=A1n=20Meinhardt?= Date: Tue, 17 Feb 2026 21:32:10 +0100 Subject: [PATCH] package .bin/ scripts as proper nix packages, delete .bin/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Packaged 14 scripts from .bin/ into packages/ with proper dependency declarations (writers.writeDashBin/writeBashBin/writePython3Bin): - 256color → two56color (terminal color chart) - avesta.sed → avesta (Avestan transliteration) - bvg.sh → bvg (Berlin transit disruptions) - unicode → charinfo (Unicode character info) - chunk-pdf → chunk-pdf (split PDFs by page count) - csv2json → csv2json (CSV to JSON converter) - fix-sd.sh → fix-sd (exFAT SD card recovery, improved output handling) - json2csv → json2csv (JSON to CSV converter) - mp3player-write → mp3player-write (audio conversion for MP3 players) - mushakkil.sh → mushakkil (Arabic diacritization) - nix-haddock-index → nix-haddock-index (GHC Haddock index generator) - pdf-ocr.sh → pdf-ocr (OCR PDFs via tesseract) - prospekte.sh → prospekte (German supermarket flyer browser) - readme → readme (GitHub README as man page) All added to overlay and packages output. .bin/ directory removed. --- .bin/256color | 38 ----------- .bin/avesta.sed | 54 --------------- .bin/bvg.sh | 46 ------------- .bin/chunk-pdf | 23 ------- .bin/csv2json | 14 ---- .bin/fix-sd.sh | 21 ------ .bin/json2csv | 24 ------- .bin/mp3player-write | 117 --------------------------------- .bin/mushakkil.sh | 6 -- .bin/nix-haddock-index | 93 -------------------------- .bin/pdf-ocr.sh | 23 ------- .bin/prospekte.sh | 65 ------------------ .bin/readme | 4 -- .bin/unicode | 8 --- flake.nix | 30 +++++++++ packages/256color.nix | 37 +++++++++++ packages/avesta.nix | 65 ++++++++++++++++++ packages/bvg.nix | 53 +++++++++++++++ packages/charinfo.nix | 17 +++++ packages/chunk-pdf.nix | 30 +++++++++ packages/csv2json.nix | 21 ++++++ packages/fix-sd.nix | 34 ++++++++++ packages/json2csv.nix | 32 +++++++++ packages/mp3player-write.nix | 89 +++++++++++++++++++++++++ packages/mushakkil.nix | 13 ++++ packages/nix-haddock-index.nix | 84 +++++++++++++++++++++++ packages/pdf-ocr.nix | 29 ++++++++ packages/prospekte.nix | 77 ++++++++++++++++++++++ packages/readme.nix | 12 ++++ 29 files changed, 623 insertions(+), 536 deletions(-) delete mode 100755 .bin/256color delete mode 100755 .bin/avesta.sed delete mode 100755 .bin/bvg.sh delete mode 100755 .bin/chunk-pdf delete mode 100755 .bin/csv2json delete mode 100755 .bin/fix-sd.sh delete mode 100755 .bin/json2csv delete mode 100755 .bin/mp3player-write delete mode 100755 .bin/mushakkil.sh delete mode 100755 .bin/nix-haddock-index delete mode 100755 .bin/pdf-ocr.sh delete mode 100755 .bin/prospekte.sh delete mode 100755 .bin/readme delete mode 100644 .bin/unicode create mode 100644 packages/256color.nix create mode 100644 packages/avesta.nix create mode 100644 packages/bvg.nix create mode 100644 packages/charinfo.nix create mode 100644 packages/chunk-pdf.nix create mode 100644 packages/csv2json.nix create mode 100644 packages/fix-sd.nix create mode 100644 packages/json2csv.nix create mode 100644 packages/mp3player-write.nix create mode 100644 packages/mushakkil.nix create mode 100644 packages/nix-haddock-index.nix create mode 100644 packages/pdf-ocr.nix create mode 100644 packages/prospekte.nix create mode 100644 packages/readme.nix diff --git a/.bin/256color b/.bin/256color deleted file mode 100755 index c33cd1f..0000000 --- a/.bin/256color +++ /dev/null @@ -1,38 +0,0 @@ -#! /bin/sh -set -euf - -pl() { - for i in $(seq $1 $(expr $2 - 1)); do - printf '\e[38;5;%sm%03i\e[m ' $i $i - done - printf '\e[38;5;%sm%03i\e[m\n' $2 $2 -} - -p() { - printf '\e[38;5;%sm%03i\e[m ' $1 $1 -} -pn() { - printf '\e[38;5;%sm%03i\e[m\n' $1 $1 -} - -p6x6() { - for i in $(seq 0 5); do - for j in $(seq 0 5); do - p $(expr $1 + $i + $j \* 6) - done - echo - done -} - -pl 0 7 -pl 8 15 - -p6x6 16 -p6x6 52 -p6x6 88 -p6x6 124 -p6x6 160 -p6x6 196 - -pl 232 243 -pl 244 255 diff --git a/.bin/avesta.sed b/.bin/avesta.sed deleted file mode 100755 index 7cedb1e..0000000 --- a/.bin/avesta.sed +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env -S sed -f -s/ā̊/𐬃/g -s/t̰/𐬝/g -s/ṣ̌/𐬴/g -s/š́/𐬳/g -s/ą̄/𐬅/g -s/ŋᵛ/𐬤/g -s/ə̄/𐬇/g -s/ŋ́/𐬣/g -s/x́/𐬒/g -s/xᵛ/𐬓/g -s/a/𐬀/g -s/ā/𐬁/g -s/å/𐬂/g -s/ą/𐬄/g -s/ə/𐬆/g -s/e/𐬈/g -s/ē/𐬉/g -s/o/𐬊/g -s/ō/𐬋/g -s/i/𐬌/g -s/ī/𐬍/g -s/u/𐬎/g -s/ū/𐬏/g -s/k/𐬐/g -s/x/𐬑/g -s/g/𐬔/g -s/ġ/𐬕/g -s/γ/𐬖/g -s/c/𐬗/g -s/j/𐬘/g -s/t/𐬙/g -s/θ/𐬚/g -s/d/𐬛/g -s/δ/𐬜/g -s/p/𐬞/g -s/f/𐬟/g -s/b/𐬠/g -s/β/𐬡/g -s/ŋ/𐬢/g -s/n/𐬥/g -s/ń/𐬦/g -s/ṇ/𐬧/g -s/m/𐬨/g -s/m̨/𐬩/g -s/ẏ/𐬫/g -s/y/𐬪/g -s/v/𐬬/g -s/r/𐬭/g -s/s/𐬯/g -s/z/𐬰/g -s/š/𐬱/g -s/ž/𐬲/g -s/h/𐬵/g diff --git a/.bin/bvg.sh b/.bin/bvg.sh deleted file mode 100755 index 690e021..0000000 --- a/.bin/bvg.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -interesting="U6 N6 140 M46 184 N84" - -curl -sSL 'https://www.bvg.de/disruption-reports/q' \ - --data-raw '{"variables":{},"query":"{ - allDisruptions { - disruptions { - meldungsId - linie - verkehrsmittel - __typename - ... on Traffic { - datum - gueltigVonDatum - gueltigVonZeit - gueltigBisDatum - gueltigBisZeit - richtungName - richtungHafasId - beginnAbschnittName - beginnAbschnittHafasId - endeAbschnittName - endeAbschnittHafasId - textIntUrsache - sev - textIntAuswirkung - umfahrung - textWAPSMSUrsache - textWAPSMSAuswirkung - prioritaet - __typename - } - } - __typename - } - }"}' \ - | jq --arg interesting "$interesting" ' - .data.allDisruptions.disruptions - | map(select( - (.linie as $linie - | $interesting - | split(" ") - | index($linie)) - and (.["__typename"] == "Traffic") - )) - ' diff --git a/.bin/chunk-pdf b/.bin/chunk-pdf deleted file mode 100755 index 86b2b69..0000000 --- a/.bin/chunk-pdf +++ /dev/null @@ -1,23 +0,0 @@ -#! /usr/bin/env nix-shell -#! nix-shell -i bash -p pdftk gnugrep -set -efu - -INPUT_FILE="${2:?Pass the PDF path as second argument.}" -PAGES_PER_REPORT="${1:?Pass the chunk size as first argument.}" - -if [ ! -f "$INPUT_FILE" ]; then - echo >&2 "File $INPUT_FILE does not exist." - exit 1 -fi - -TOTAL_PAGES="$(pdftk "$INPUT_FILE" dump_data | grep NumberOfPages | cut -f2 -d' ')" - -RUNS=$((TOTAL_PAGES/PAGES_PER_REPORT)) - -for run in $(seq 0 "$((RUNS-1))"); do - start_page=$((run*PAGES_PER_REPORT+1)) - end_page=$(((run+1)*PAGES_PER_REPORT)) - output_file="chunk_$((run+1)).pdf" - echo "splitting $INPUT_FILE from $start_page to $end_page into $output_file" - pdftk "$INPUT_FILE" cat "$start_page-$end_page" output "$output_file" -done diff --git a/.bin/csv2json b/.bin/csv2json deleted file mode 100755 index 064e4ef..0000000 --- a/.bin/csv2json +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import csv -import json -import sys -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument("--delimiter", "-d", default=",", help="CSV field separator") - -args = parser.parse_args() - -if __name__ == "__main__": - json.dump(list(csv.DictReader(sys.stdin, delimiter=args.delimiter)), sys.stdout) diff --git a/.bin/fix-sd.sh b/.bin/fix-sd.sh deleted file mode 100755 index 456a8ff..0000000 --- a/.bin/fix-sd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -xfu - -drive="$1" -mountpoint="/media/sd-card-$(date +%s)" -backup_directory="$(pwd)" - -trap clean EXIT -clean() { - umount "$mountpoint" - rmdir "$mountpoint" - fsck.exfat "$drive" -} - -filenames="$(fsck.exfat "$drive" 2>&1 | sed -nE "s/.* file '(.*?)' is not allocated.*/\1/p")" -mkdir "$mountpoint" -mount "$drive" "$mountpoint" - -echo "$filenames" | while read -r filename; do - find "$mountpoint" -type f -name "$filename" -exec mv {} "$backup_directory" \; -done diff --git a/.bin/json2csv b/.bin/json2csv deleted file mode 100755 index 0f1f7a1..0000000 --- a/.bin/json2csv +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 -import csv -import json -import sys - -if __name__ == "__main__": - json_list = json.load(sys.stdin) - if not isinstance(json_list, list): - print("JSON object is not a list.", file=sys.stderr) - sys.exit(1) - if len(json_list) == 0: - print("JSON list is empty.", file=sys.stderr) - sys.exit(1) - keys = set() - for element in json_list: - if isinstance(element, dict): - keys |= element.keys() - else: - print("Non-dict element:", element, file=sys.stderr) - sys.exit(1) - writer = csv.DictWriter(sys.stdout, fieldnames=list(keys)) - writer.writeheader() - for element in json_list: - writer.writerow(element) diff --git a/.bin/mp3player-write b/.bin/mp3player-write deleted file mode 100755 index 7072631..0000000 --- a/.bin/mp3player-write +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env bash - -# Usage: -# ./mp3_transfer.sh -s 1.3 /mnt/mp3player file1.m4a file2.m4a ... - -set -e - -# Default speed -SPEED=1.0 - -# Parse options -while getopts ":s:" opt; do - case $opt in - s) - SPEED=$OPTARG - ;; - \?) - echo "Invalid option: -$OPTARG" >&2 - exit 1 - ;; - :) - echo "Option -$OPTARG requires a value." >&2 - exit 1 - ;; - esac -done - -# Shift past the options -shift $((OPTIND -1)) - -# Check arguments -if [ "$#" -lt 2 ]; then - echo "Usage: $0 [-s speed] MOUNT_POINT FILE1 [FILE2 ...]" - exit 1 -fi - -MOUNT_POINT=$1 -shift -FILES=("$@") - -# Check mount point exists -if [ ! -d "$MOUNT_POINT" ]; then - echo "Error: Mount point '$MOUNT_POINT' does not exist." - exit 1 -fi - -# Estimate required space -TOTAL_SIZE=0 -for f in "${FILES[@]}"; do - if [ ! -f "$f" ]; then - echo "Warning: File '$f' does not exist, skipping." - continue - fi - # Get file size in bytes - FILE_SIZE=$(stat --printf="%s" "$f") - # Estimate mp3 output size: roughly 1/2 of original m4a (adjust if needed) - TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE / 2)) -done - -# Get available space in bytes -AVAILABLE=$(df --output=avail "$MOUNT_POINT" | tail -n 1) -AVAILABLE=$((AVAILABLE * 1024)) # df reports in KB - -if [ "$TOTAL_SIZE" -gt "$AVAILABLE" ]; then - echo "Error: Not enough space on device. Required: $TOTAL_SIZE bytes, Available: $AVAILABLE bytes" - exit 1 -fi - -echo "Enough space available. Starting conversion..." - -sanitize_filename() { - local name="$1" - # Remove path, keep only base name - name=$(basename "$name") - # Remove any extension - name=${name%.*} - # Replace spaces and special chars with underscore - name=$(echo "$name" | tr ' ' '_' | tr -cd '[:alnum:]_-') - # Truncate to max 50 chars - echo "${name:0:50}" -} - -# Convert and copy files -for f in "${FILES[@]}"; do - if [ ! -f "$f" ]; then - continue - fi - - # Determine the next prefix - existing_prefixes=$(ls "$MOUNT_POINT" | grep -E '^[0-9].*\.mp3$' | sed -E 's/^([0-9]).*/\1/' | sort -n | uniq) - for i in {0..9}; do - if ! echo "$existing_prefixes" | grep -q "^$i$"; then - PREFIX=$i - break - fi - done - - echo "Using prefix: $PREFIX" - - BASENAME=$(sanitize_filename "$f") - OUT_PATTERN="$MOUNT_POINT/${PREFIX}_%03d_${BASENAME}.mp3" - - echo "Converting '$f' to '$OUT_PATTERN' at speed $SPEED..." - - ffmpeg -nostdin -i "$f" \ - -filter:a "atempo=$SPEED" \ - -ar 22050 -ac 1 -c:a libmp3lame -b:a 32k \ - -f segment -segment_time 300 \ - "$OUT_PATTERN" - - # Update prefix for next file - # Count how many segments were created - SEG_COUNT=$(ls "$MOUNT_POINT" | grep -E "^${PREFIX}[0-9]{2}_" | wc -l) - PREFIX=$((PREFIX + SEG_COUNT)) -done - -echo "All files processed successfully." diff --git a/.bin/mushakkil.sh b/.bin/mushakkil.sh deleted file mode 100755 index 08716d4..0000000 --- a/.bin/mushakkil.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -curl -sSL 'https://diac.alsharekh.org/Diac/DiacText' \ - -H "Content-Type: application/json" \ - --data-raw "$(jq --raw-input '{word: ., type: 1}')" \ - --compressed \ - | jq -r .diacWord diff --git a/.bin/nix-haddock-index b/.bin/nix-haddock-index deleted file mode 100755 index 294e3df..0000000 --- a/.bin/nix-haddock-index +++ /dev/null @@ -1,93 +0,0 @@ -#! /usr/bin/env nix-shell -#! nix-shell -i bash -p coreutils gnugrep gnused graphviz -# -# usage: nix-haddock-index -# -# Run this script in an environment where either NIX_GHC is set, or the ghc -# executable exists, to generate an HTML index file pointing to all Haddock -# files accessible to the respective ghc version. -# -# Additionally, an SVG dependency graph of all packages is linked at the -# bottom of the index file. -# -# Note: all files will be generated in /tmp, and won't be deleted automatically -# - -set -efux - -if test -z "${NIX_GHC-}"; then - NIX_GHC=$(readlink -f "$(type -P ghc)") -fi - -if ! echo $NIX_GHC | grep -q '^/nix/store/'; then - printf '%s: error: unsupported GHC executable path (not in Nix store): %q\n' \ - "$0" \ - "$NIX_GHC" \ - >&2 - exit -1 -fi - -NIX_GHC_PREFIX=$(dirname "$(dirname "$NIX_GHC")") -NIX_GHC_DOCDIR=$NIX_GHC_PREFIX/share/doc/ghc/html - -main() { - - hash=$(echo $NIX_GHC_PREFIX | sed -n 's|^/nix/store/\([a-z0-9]\+\).*|\1|p') - title="Haddock index for $NIX_GHC_PREFIX" - - header=$( - printf 'Haddock index for %s\n' \ - $NIX_GHC_PREFIX \ - $NIX_GHC_PREFIX \ - ) - - suffix=${hash:+-$hash} - index_file=/tmp/haddock$suffix-index.html - svg_file=/tmp/haddock$suffix.svg - - #if ! test -e $index_file; then - eval "$( - echo 'gen_index() {' - echo ' html_head' - "$NIX_GHC_PREFIX"/bin/ghc-pkg dump | sed -n ' - s/^---$/ reset/p - s/^\(name\|version\):\s*\([-A-Za-z0-9_.]\+\)$/ \1=\2/p - s/^haddock-html:\s*\([-A-Za-z0-9_./]\+\)$/ haddock_html \1/p - ' - echo ' html_foot' - echo '}' - )" - - gen_index > $index_file - #fi - - #if ! test -e $svg_file; then - "$NIX_GHC_PREFIX"/bin/ghc-pkg dot | tred | dot -Tsvg | sed ' - s/ $svg_file - #fi - - echo $index_file -} -reset() { - unset name version -} -haddock_html() { - printf '
  • ' - printf '%s' "$1" "$name-$version" - printf '
  • \n' -} -html_head() { - printf '\n' - printf '%s\n' "$title" - printf '\n' \ - "$NIX_GHC_DOCDIR/libraries/ocean.css" - printf '

    %s

    \n' "$header" - printf '\n' - printf 'graph\n' "$svg_file" -} - -main "$@" diff --git a/.bin/pdf-ocr.sh b/.bin/pdf-ocr.sh deleted file mode 100755 index 66f56c3..0000000 --- a/.bin/pdf-ocr.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env nix-shell -#! nix-shell -i bash -p poppler_utils tesseract4 -set -eu - -pdf_path="$(realpath "$1")" - -[ -f "$pdf_path" ] || { - echo "Usage: $0 FILE.pdf" >&2 - exit 1 -} - - -tmpdir="$(mktemp -d)" -trap 'rm -rf $tmpdir' EXIT - -cd "$tmpdir" - -pdftoppm -png "$pdf_path" pdf-ocr -for png in pdf-ocr*.png; do - tesseract "$png" "$png.txt" 2>/dev/null -done - -cat pdf-ocr-*.txt diff --git a/.bin/prospekte.sh b/.bin/prospekte.sh deleted file mode 100755 index 383d54b..0000000 --- a/.bin/prospekte.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -lidl() { - echo LIDL - curl -sSL 'https://endpoints.lidl-flyer.com/v3/region-overview/lidl/de-DE/0.json' \ - | jq -r ' - .categories - | map(select(.name == "Filial-Angebote") | .subcategories | map(.flyers)) - | flatten - | flatten - | .[] - | .pdfUrl - ' -} - -aldi_nord() { - echo ALDI nord - echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-aktuell/GetPDF.ashx' - echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-vorschau/GetPDF.ashx' -} - -rewe_berlin() {( - store_id=662366923 - publisher_id=1062 - - echo REWE - curl -sSL 'https://www.bonialserviceswidget.de/de/stores/'$store_id'/brochures?storeId='$store_id'&publisherId='$publisher_id | while read -r brochure_id; do - curl -sSL 'https://www.bonialserviceswidget.de/de/v5/brochureDetails/'"$brochure_id"'?publisherId='$publisher_id | jq -r .pdfUrl - done -)} - -kaufland() {( - region_code=8920 - echo KAUFLAND - curl -sSL https://filiale.kaufland.de/prospekte.html | htmlq --attribute href '.flyer a' | grep -Eo 'DE_de_KDZ[^/]*' | sed "s/_3000_/_${region_code}_/" | while read -r flyer_id; do - curl -sSL "https://endpoints.leaflets.kaufland.com/v3/$flyer_id/flyer.json?regionCode=$region_code" | jq -r .flyer.pdfUrl - done -)} - -netto_schwarz() { - echo 'NETTO (schwarz)' - curl -sSL 'https://squid-api.tjek.com/v2/catalogs?dealer_ids=90f2VL&order_by=created' \ - | jq -r '.[] | .id' \ - | while read -r flyer_id; do - curl -sSL "https://squid-api.tjek.com/v2/catalogs/$flyer_id/download" \ - | jq -r .pdf_url - done -} - -dir="$(mktemp -d)" -trap clean EXIT - -clean() { - rm -rf "$dir" -} - -prospekt_url="$( ( - lidl - aldi_nord - rewe_berlin - kaufland - netto_schwarz -) | fzf)" - -curl -sSL "$prospekt_url" -o "$dir/prospekt.pdf" -zathura "$dir/prospekt.pdf" diff --git a/.bin/readme b/.bin/readme deleted file mode 100755 index 6698bf0..0000000 --- a/.bin/readme +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -curl -sSL "https://raw.githubusercontent.com/$*/master/README.md" \ - | pandoc -f gfm -t man -s \ - | man -l - diff --git a/.bin/unicode b/.bin/unicode deleted file mode 100644 index 15fd968..0000000 --- a/.bin/unicode +++ /dev/null @@ -1,8 +0,0 @@ -import sys -import unicodedata - -for index, character in enumerate(sys.stdin.read().strip()): - try: - print(index, character, hex(ord(character)), unicodedata.category(character), unicodedata.name(character)) - except: - print(index, character, hex(ord(character))) diff --git a/flake.nix b/flake.nix index 969a14b..fbd9f3b 100644 --- a/flake.nix +++ b/flake.nix @@ -200,6 +200,22 @@ niveum-browser = final.niphas-web-browser; niveum-filemanager = final.niphas-file-browser; + # packaged from .bin/ + two56color = prev.callPackage packages/256color.nix { }; + avesta = prev.callPackage packages/avesta.nix { }; + bvg = prev.callPackage packages/bvg.nix { }; + charinfo = prev.callPackage packages/charinfo.nix { }; + chunk-pdf = prev.callPackage packages/chunk-pdf.nix { }; + csv2json = prev.callPackage packages/csv2json.nix { }; + fix-sd = prev.callPackage packages/fix-sd.nix { }; + json2csv = prev.callPackage packages/json2csv.nix { }; + mp3player-write = prev.callPackage packages/mp3player-write.nix { }; + mushakkil = prev.callPackage packages/mushakkil.nix { }; + nix-haddock-index = prev.callPackage packages/nix-haddock-index.nix { }; + pdf-ocr = prev.callPackage packages/pdf-ocr.nix { }; + prospekte = prev.callPackage packages/prospekte.nix { }; + readme = prev.callPackage packages/readme.nix { }; + ashell = nixpkgs-unstable.legacyPackages.${prev.system}.ashell; # wrapped from upstream @@ -546,14 +562,20 @@ ]; }; inherit (pkgs) + two56color + avesta auc betacode booksplit brainmelter brassica + bvg + charinfo cheat-sh + chunk-pdf closest cro + csv2json cyberlocker-tools dawn-editor default-gateway @@ -564,6 +586,7 @@ emailmenu exodus fkill + fix-sd fzfmenu gfs-fonts bring-out-the-gimp @@ -574,6 +597,7 @@ image-convert-tolino ipa jsesh + json2csv kirciuoklis klem kpaste @@ -582,24 +606,30 @@ mansplain manual-sort morris + mp3player-write mpv-iptv mpv-radio mpv-tuner mpv-tv + mushakkil new-mac niveum-ssh nix-git + nix-haddock-index noise-waves notemenu obsidian-vim opustags + pdf-ocr picoclaw pls polyglot + prospekte q qrpaste radio-news random-zeno + readme rfc scanned stag diff --git a/packages/256color.nix b/packages/256color.nix new file mode 100644 index 0000000..b14a71a --- /dev/null +++ b/packages/256color.nix @@ -0,0 +1,37 @@ +{ + writers, +}: +writers.writeDashBin "256color" '' + pl() { + for i in $(seq $1 $(expr $2 - 1)); do + printf '\e[38;5;%sm%03i\e[m ' $i $i + done + printf '\e[38;5;%sm%03i\e[m\n' $2 $2 + } + + p() { + printf '\e[38;5;%sm%03i\e[m ' $1 $1 + } + + p6x6() { + for i in $(seq 0 5); do + for j in $(seq 0 5); do + p $(expr $1 + $i + $j \* 6) + done + echo + done + } + + pl 0 7 + pl 8 15 + + p6x6 16 + p6x6 52 + p6x6 88 + p6x6 124 + p6x6 160 + p6x6 196 + + pl 232 243 + pl 244 255 +'' diff --git a/packages/avesta.nix b/packages/avesta.nix new file mode 100644 index 0000000..85ac887 --- /dev/null +++ b/packages/avesta.nix @@ -0,0 +1,65 @@ +# Transliterate Latin-script Avestan to Avestan Unicode script +{ + writers, + gnused, +}: +let + sedScript = builtins.toFile "avesta.sed" '' + s/ā̊/𐬃/g + s/t̰/𐬝/g + s/ṣ̌/𐬴/g + s/š́/𐬳/g + s/ą̄/𐬅/g + s/ŋᵛ/𐬤/g + s/ə̄/𐬇/g + s/ŋ́/𐬣/g + s/x́/𐬒/g + s/xᵛ/𐬓/g + s/a/𐬀/g + s/ā/𐬁/g + s/å/𐬂/g + s/ą/𐬄/g + s/ə/𐬆/g + s/e/𐬈/g + s/ē/𐬉/g + s/o/𐬊/g + s/ō/𐬋/g + s/i/𐬌/g + s/ī/𐬍/g + s/u/𐬎/g + s/ū/𐬏/g + s/k/𐬐/g + s/x/𐬑/g + s/g/𐬔/g + s/ġ/𐬕/g + s/γ/𐬖/g + s/c/𐬗/g + s/j/𐬘/g + s/t/𐬙/g + s/θ/𐬚/g + s/d/𐬛/g + s/δ/𐬜/g + s/p/𐬞/g + s/f/𐬟/g + s/b/𐬠/g + s/β/𐬡/g + s/ŋ/𐬢/g + s/n/𐬥/g + s/ń/𐬦/g + s/ṇ/𐬧/g + s/m/𐬨/g + s/m̨/𐬩/g + s/ẏ/𐬫/g + s/y/𐬪/g + s/v/𐬬/g + s/r/𐬭/g + s/s/𐬯/g + s/z/𐬰/g + s/š/𐬱/g + s/ž/𐬲/g + s/h/𐬵/g + ''; +in +writers.writeDashBin "avesta" '' + exec ${gnused}/bin/sed -f ${sedScript} "$@" +'' diff --git a/packages/bvg.nix b/packages/bvg.nix new file mode 100644 index 0000000..2a73ccf --- /dev/null +++ b/packages/bvg.nix @@ -0,0 +1,53 @@ +# Berlin BVG transit disruption checker +{ + writers, + curl, + jq, +}: +writers.writeDashBin "bvg" '' + interesting="U6 N6 140 M46 184 N84" + + ${curl}/bin/curl -sSL 'https://www.bvg.de/disruption-reports/q' \ + --data-raw '{"variables":{},"query":"{ + allDisruptions { + disruptions { + meldungsId + linie + verkehrsmittel + __typename + ... on Traffic { + datum + gueltigVonDatum + gueltigVonZeit + gueltigBisDatum + gueltigBisZeit + richtungName + richtungHafasId + beginnAbschnittName + beginnAbschnittHafasId + endeAbschnittName + endeAbschnittHafasId + textIntUrsache + sev + textIntAuswirkung + umfahrung + textWAPSMSUrsache + textWAPSMSAuswirkung + prioritaet + __typename + } + } + __typename + } + }"}' \ + | ${jq}/bin/jq --arg interesting "$interesting" ' + .data.allDisruptions.disruptions + | map(select( + (.linie as $linie + | $interesting + | split(" ") + | index($linie)) + and (.["__typename"] == "Traffic") + )) + ' +'' diff --git a/packages/charinfo.nix b/packages/charinfo.nix new file mode 100644 index 0000000..fe17f83 --- /dev/null +++ b/packages/charinfo.nix @@ -0,0 +1,17 @@ +# Print Unicode character info for each character on stdin +{ + writers, + python3, +}: +writers.writePython3Bin "charinfo" { + flakeIgnore = [ "E501" "E722" ]; +} '' + import sys + import unicodedata + + for index, character in enumerate(sys.stdin.read().strip()): + try: + print(index, character, hex(ord(character)), unicodedata.category(character), unicodedata.name(character)) + except Exception: + print(index, character, hex(ord(character))) +'' diff --git a/packages/chunk-pdf.nix b/packages/chunk-pdf.nix new file mode 100644 index 0000000..3c951ce --- /dev/null +++ b/packages/chunk-pdf.nix @@ -0,0 +1,30 @@ +# Split a PDF into chunks of N pages +{ + writers, + pdftk, + gnugrep, + coreutils, +}: +writers.writeDashBin "chunk-pdf" '' + set -efu + + INPUT_FILE="''${2:?Pass the PDF path as second argument.}" + PAGES_PER_REPORT="''${1:?Pass the chunk size as first argument.}" + + if [ ! -f "$INPUT_FILE" ]; then + echo >&2 "File $INPUT_FILE does not exist." + exit 1 + fi + + TOTAL_PAGES="$(${pdftk}/bin/pdftk "$INPUT_FILE" dump_data | ${gnugrep}/bin/grep NumberOfPages | ${coreutils}/bin/cut -f2 -d' ')" + + RUNS=$((TOTAL_PAGES/PAGES_PER_REPORT)) + + for run in $(${coreutils}/bin/seq 0 "$((RUNS-1))"); do + start_page=$((run*PAGES_PER_REPORT+1)) + end_page=$(((run+1)*PAGES_PER_REPORT)) + output_file="chunk_$((run+1)).pdf" + echo "splitting $INPUT_FILE from $start_page to $end_page into $output_file" + ${pdftk}/bin/pdftk "$INPUT_FILE" cat "$start_page-$end_page" output "$output_file" + done +'' diff --git a/packages/csv2json.nix b/packages/csv2json.nix new file mode 100644 index 0000000..c5afbc9 --- /dev/null +++ b/packages/csv2json.nix @@ -0,0 +1,21 @@ +# Convert CSV to JSON +{ + writers, + python3, +}: +writers.writePython3Bin "csv2json" { + flakeIgnore = [ "E501" ]; +} '' + import csv + import json + import sys + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--delimiter", "-d", default=",", help="CSV field separator") + + args = parser.parse_args() + + if __name__ == "__main__": + json.dump(list(csv.DictReader(sys.stdin, delimiter=args.delimiter)), sys.stdout) +'' diff --git a/packages/fix-sd.nix b/packages/fix-sd.nix new file mode 100644 index 0000000..eaf6b98 --- /dev/null +++ b/packages/fix-sd.nix @@ -0,0 +1,34 @@ +# Recover files from a corrupted exFAT SD card +{ + writers, + exfatprogs, + util-linux, + coreutils, + findutils, + gnused, +}: +writers.writeDashBin "fix-sd" '' + set -efu + + drive="''${1:?Usage: fix-sd /dev/sdX [output-dir]}" + output_dir="''${2:-$(${coreutils}/bin/mktemp -d "''${TMPDIR:-/tmp}/fix-sd-XXXXXX")}" + mountpoint="$(${coreutils}/bin/mktemp -d "''${TMPDIR:-/tmp}/fix-sd-mount-XXXXXX")" + + trap clean EXIT + clean() { + ${util-linux}/bin/umount "$mountpoint" 2>/dev/null || true + ${coreutils}/bin/rmdir "$mountpoint" 2>/dev/null || true + } + + filenames="$(${exfatprogs}/bin/fsck.exfat "$drive" 2>&1 | ${gnused}/bin/sed -nE "s/.* file '(.*?)' is not allocated.*/\1/p")" + ${coreutils}/bin/mkdir -p "$mountpoint" "$output_dir" + ${util-linux}/bin/mount "$drive" "$mountpoint" + + echo "$filenames" | while read -r filename; do + [ -n "$filename" ] || continue + ${findutils}/bin/find "$mountpoint" -type f -name "$filename" -exec ${coreutils}/bin/cp {} "$output_dir" \; + done + + echo "Recovered files saved to $output_dir" + ${exfatprogs}/bin/fsck.exfat "$drive" +'' diff --git a/packages/json2csv.nix b/packages/json2csv.nix new file mode 100644 index 0000000..873fa6c --- /dev/null +++ b/packages/json2csv.nix @@ -0,0 +1,32 @@ +# Convert JSON array of objects to CSV +{ + writers, + python3, +}: +writers.writePython3Bin "json2csv" { + flakeIgnore = [ "E501" ]; +} '' + import csv + import json + import sys + + if __name__ == "__main__": + json_list = json.load(sys.stdin) + if not isinstance(json_list, list): + print("JSON object is not a list.", file=sys.stderr) + sys.exit(1) + if len(json_list) == 0: + print("JSON list is empty.", file=sys.stderr) + sys.exit(1) + keys = set() + for element in json_list: + if isinstance(element, dict): + keys |= element.keys() + else: + print("Non-dict element:", element, file=sys.stderr) + sys.exit(1) + writer = csv.DictWriter(sys.stdout, fieldnames=list(keys)) + writer.writeheader() + for element in json_list: + writer.writerow(element) +'' diff --git a/packages/mp3player-write.nix b/packages/mp3player-write.nix new file mode 100644 index 0000000..97108d2 --- /dev/null +++ b/packages/mp3player-write.nix @@ -0,0 +1,89 @@ +# Convert and transfer audio files to an MP3 player +{ + writers, + ffmpeg, + coreutils, + gnugrep, + bash, +}: +writers.writeBashBin "mp3player-write" '' + set -e + + SPEED=1.0 + + while getopts ":s:" opt; do + case $opt in + s) SPEED=$OPTARG ;; + \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;; + :) echo "Option -$OPTARG requires a value." >&2; exit 1 ;; + esac + done + shift $((OPTIND -1)) + + if [ "$#" -lt 2 ]; then + echo "Usage: mp3player-write [-s speed] MOUNT_POINT FILE1 [FILE2 ...]" + exit 1 + fi + + MOUNT_POINT=$1 + shift + FILES=("$@") + + if [ ! -d "$MOUNT_POINT" ]; then + echo "Error: Mount point '$MOUNT_POINT' does not exist." + exit 1 + fi + + TOTAL_SIZE=0 + for f in "''${FILES[@]}"; do + if [ ! -f "$f" ]; then + echo "Warning: File '$f' does not exist, skipping." + continue + fi + FILE_SIZE=$(${coreutils}/bin/stat --printf="%s" "$f") + TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE / 2)) + done + + AVAILABLE=$(${coreutils}/bin/df --output=avail "$MOUNT_POINT" | ${coreutils}/bin/tail -n 1) + AVAILABLE=$((AVAILABLE * 1024)) + + if [ "$TOTAL_SIZE" -gt "$AVAILABLE" ]; then + echo "Error: Not enough space. Required: $TOTAL_SIZE bytes, Available: $AVAILABLE bytes" + exit 1 + fi + + echo "Enough space available. Starting conversion..." + + sanitize_filename() { + local name + name=$(${coreutils}/bin/basename "$1") + name=''${name%.*} + name=$(echo "$name" | ${coreutils}/bin/tr ' ' '_' | ${coreutils}/bin/tr -cd '[:alnum:]_-') + echo "''${name:0:50}" + } + + for f in "''${FILES[@]}"; do + [ -f "$f" ] || continue + + existing_prefixes=$(${coreutils}/bin/ls "$MOUNT_POINT" | ${gnugrep}/bin/grep -E '^[0-9].*\.mp3$' | ${coreutils}/bin/sed -E 's/^([0-9]).*/\1/' | ${coreutils}/bin/sort -n | ${coreutils}/bin/uniq) + for i in {0..9}; do + if ! echo "$existing_prefixes" | ${gnugrep}/bin/grep -q "^$i$"; then + PREFIX=$i + break + fi + done + + BASENAME=$(sanitize_filename "$f") + OUT_PATTERN="$MOUNT_POINT/''${PREFIX}_%03d_''${BASENAME}.mp3" + + echo "Converting '$f' to '$OUT_PATTERN' at speed $SPEED..." + + ${ffmpeg}/bin/ffmpeg -nostdin -i "$f" \ + -filter:a "atempo=$SPEED" \ + -ar 22050 -ac 1 -c:a libmp3lame -b:a 32k \ + -f segment -segment_time 300 \ + "$OUT_PATTERN" + done + + echo "All files processed successfully." +'' diff --git a/packages/mushakkil.nix b/packages/mushakkil.nix new file mode 100644 index 0000000..0c53d61 --- /dev/null +++ b/packages/mushakkil.nix @@ -0,0 +1,13 @@ +# Add Arabic diacritics (tashkeel) to text via alsharekh.org +{ + writers, + curl, + jq, +}: +writers.writeDashBin "mushakkil" '' + ${curl}/bin/curl -sSL 'https://diac.alsharekh.org/Diac/DiacText' \ + -H "Content-Type: application/json" \ + --data-raw "$(${jq}/bin/jq --raw-input '{word: ., type: 1}')" \ + --compressed \ + | ${jq}/bin/jq -r .diacWord +'' diff --git a/packages/nix-haddock-index.nix b/packages/nix-haddock-index.nix new file mode 100644 index 0000000..98c562e --- /dev/null +++ b/packages/nix-haddock-index.nix @@ -0,0 +1,84 @@ +# Generate a Haddock index page for all packages visible to the current GHC +{ + writers, + coreutils, + gnugrep, + gnused, + graphviz, + bash, +}: +writers.writeBashBin "nix-haddock-index" '' + set -efux + + if test -z "''${NIX_GHC-}"; then + NIX_GHC=$(${coreutils}/bin/readlink -f "$(type -P ghc)") + fi + + if ! echo $NIX_GHC | ${gnugrep}/bin/grep -q '^/nix/store/'; then + printf '%s: error: unsupported GHC executable path (not in Nix store): %q\n' \ + "$0" \ + "$NIX_GHC" \ + >&2 + exit 1 + fi + + NIX_GHC_PREFIX=$(${coreutils}/bin/dirname "$(${coreutils}/bin/dirname "$NIX_GHC")") + NIX_GHC_DOCDIR=$NIX_GHC_PREFIX/share/doc/ghc/html + + main() { + hash=$(echo $NIX_GHC_PREFIX | ${gnused}/bin/sed -n 's|^/nix/store/\([a-z0-9]\+\).*|\1|p') + title="Haddock index for $NIX_GHC_PREFIX" + + header=$( + printf 'Haddock index for %s\n' \ + $NIX_GHC_PREFIX \ + $NIX_GHC_PREFIX \ + ) + + suffix=''${hash:+-$hash} + index_file=/tmp/haddock$suffix-index.html + svg_file=/tmp/haddock$suffix.svg + + eval "$( + echo 'gen_index() {' + echo ' html_head' + "$NIX_GHC_PREFIX"/bin/ghc-pkg dump | ${gnused}/bin/sed -n ' + s/^---$/ reset/p + s/^\(name\|version\):\s*\([-A-Za-z0-9_.]\+\)$/ \1=\2/p + s/^haddock-html:\s*\([-A-Za-z0-9_./]\+\)$/ haddock_html \1/p + ' + echo ' html_foot' + echo '}' + )" + + gen_index > $index_file + + "$NIX_GHC_PREFIX"/bin/ghc-pkg dot | ${graphviz}/bin/tred | ${graphviz}/bin/dot -Tsvg | ${gnused}/bin/sed ' + s/ $svg_file + + echo $index_file + } + reset() { + unset name version + } + haddock_html() { + printf '
  • ' + printf '%s' "$1" "$name-$version" + printf '
  • \n' + } + html_head() { + printf '\n' + printf '%s\n' "$title" + printf '\n' \ + "$NIX_GHC_DOCDIR/libraries/ocean.css" + printf '

    %s

    \n' "$header" + printf '\n' + printf 'graph\n' "$svg_file" + } + + main "$@" +'' diff --git a/packages/pdf-ocr.nix b/packages/pdf-ocr.nix new file mode 100644 index 0000000..d6448f8 --- /dev/null +++ b/packages/pdf-ocr.nix @@ -0,0 +1,29 @@ +# OCR a PDF file to text using tesseract +{ + writers, + poppler_utils, + tesseract, + coreutils, +}: +writers.writeDashBin "pdf-ocr" '' + set -efu + + pdf_path="$(${coreutils}/bin/realpath "$1")" + + [ -f "$pdf_path" ] || { + echo "Usage: pdf-ocr FILE.pdf" >&2 + exit 1 + } + + tmpdir="$(${coreutils}/bin/mktemp -d)" + trap 'rm -rf $tmpdir' EXIT + + cd "$tmpdir" + + ${poppler_utils}/bin/pdftoppm -png "$pdf_path" pdf-ocr + for png in pdf-ocr*.png; do + ${tesseract}/bin/tesseract "$png" "$png.txt" 2>/dev/null + done + + cat pdf-ocr-*.txt +'' diff --git a/packages/prospekte.nix b/packages/prospekte.nix new file mode 100644 index 0000000..e45019f --- /dev/null +++ b/packages/prospekte.nix @@ -0,0 +1,77 @@ +# Browse and view German supermarket flyers (Lidl, Aldi, REWE, Kaufland, Netto) +{ + writers, + curl, + jq, + fzf, + zathura, + coreutils, + htmlq, + gnugrep, + gnused, + lib, +}: +writers.writeDashBin "prospekte" '' + export PATH=${lib.makeBinPath [ curl jq fzf zathura coreutils htmlq gnugrep gnused ]}:$PATH + + lidl() { + echo LIDL + curl -sSL 'https://endpoints.lidl-flyer.com/v3/region-overview/lidl/de-DE/0.json' \ + | jq -r ' + .categories + | map(select(.name == "Filial-Angebote") | .subcategories | map(.flyers)) + | flatten + | flatten + | .[] + | .pdfUrl + ' + } + + aldi_nord() { + echo ALDI nord + echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-aktuell/GetPDF.ashx' + echo 'https://magazine.aldi-nord.de/aldi-nord/aldi-vorschau/GetPDF.ashx' + } + + rewe_berlin() { + store_id=662366923 + publisher_id=1062 + + echo REWE + curl -sSL "https://www.bonialserviceswidget.de/de/stores/$store_id/brochures?storeId=$store_id&publisherId=$publisher_id" | while read -r brochure_id; do + curl -sSL "https://www.bonialserviceswidget.de/de/v5/brochureDetails/$brochure_id?publisherId=$publisher_id" | jq -r .pdfUrl + done + } + + kaufland() { + region_code=8920 + echo KAUFLAND + curl -sSL https://filiale.kaufland.de/prospekte.html | htmlq --attribute href '.flyer a' | grep -Eo 'DE_de_KDZ[^/]*' | sed "s/_3000_/_''${region_code}_/" | while read -r flyer_id; do + curl -sSL "https://endpoints.leaflets.kaufland.com/v3/$flyer_id/flyer.json?regionCode=$region_code" | jq -r .flyer.pdfUrl + done + } + + netto_schwarz() { + echo 'NETTO (schwarz)' + curl -sSL 'https://squid-api.tjek.com/v2/catalogs?dealer_ids=90f2VL&order_by=created' \ + | jq -r '.[] | .id' \ + | while read -r flyer_id; do + curl -sSL "https://squid-api.tjek.com/v2/catalogs/$flyer_id/download" \ + | jq -r .pdf_url + done + } + + dir="$(mktemp -d)" + trap 'rm -rf "$dir"' EXIT + + prospekt_url="$( ( + lidl + aldi_nord + rewe_berlin + kaufland + netto_schwarz + ) | fzf)" + + curl -sSL "$prospekt_url" -o "$dir/prospekt.pdf" + zathura "$dir/prospekt.pdf" +'' diff --git a/packages/readme.nix b/packages/readme.nix new file mode 100644 index 0000000..b69ac46 --- /dev/null +++ b/packages/readme.nix @@ -0,0 +1,12 @@ +# Render a GitHub repo's README.md as a man page +{ + writers, + curl, + pandoc, + man, +}: +writers.writeDashBin "readme" '' + ${curl}/bin/curl -sSL "https://raw.githubusercontent.com/$*/master/README.md" \ + | ${pandoc}/bin/pandoc -f gfm -t man -s \ + | ${man}/bin/man -l - +''