#!/usr/bin/env bash
###############################################################################
# dlog — Reading log & completed content manager via dmenu
#
# Writes to krisyotam.com media.db (SQLite)
# Tables: reading_log, reading_books, reading_audiobooks, reading_blogs,
# reading_essays, reading_papers, reading_verse, short_stories
#
# Maintainer: Kris Yotam <krisyotam@pm.me>
# License: MIT
# Created: 2026-02-18
###############################################################################
DB="$HOME/dev/krisyotam.com/public/data/media.db"
# ── Helpers ────────────────────────────────────────────────────
ask() { printf '' | dmenu -p "$1"; }
pick() { dmenu -i -l "${2:-10}" -p "$1"; }
esc() { printf '%s' "$1" | sed "s/'/''/g"; }
today() { date +%Y-%m-%d; }
# Duration: >99m displays as Xh Ym
fmt_dur() {
local m="$1"
if [ "$m" -gt 99 ] 2>/dev/null; then
printf '%dh %dm' "$((m / 60))" "$((m % 60))"
else
printf '%dm' "$m"
fi
}
sql() { sqlite3 "$DB" "$1"; }
# ── Log Entry (reading_log) ───────────────────────────────────
log_entry() {
local dt title author type minutes
dt=$(echo "$(today)" | pick " Date:")
[ -z "$dt" ] && return
title=$(ask " Title:")
[ -z "$title" ] && return
author=$(ask " Author:")
[ -z "$author" ] && return
type=$(printf '%s\n' \
" Book" \
" Audiobook" \
" Blog Post" \
" Short Story" \
" Verse" \
" Essay" \
" Paper" \
| pick " Type:" 7)
[ -z "$type" ] && return
type=$(echo "$type" | sed 's/^[^ ]* *//')
# Offer "+ Audiobook" if primary type isn't already Audiobook
if [ "$type" != "Audiobook" ]; then
local also
also=$(printf '%s\n' " No" " + Audiobook" \
| pick " Also listening?" 2)
[[ -n "$also" && "$also" == *Audiobook* ]] && type="$type + Audiobook"
fi
minutes=$(printf '%s\n' 5 10 15 20 30 45 60 90 120 180 \
| pick " Minutes:" 10)
[ -z "$minutes" ] && return
local dur
dur=$(fmt_dur "$minutes")
local ok
ok=$(printf '%s\n' " Confirm" " Cancel" \
| pick "$title · $author · $type · $dur" 2)
[[ -z "$ok" || "$ok" == *Cancel* ]] && return
sql "INSERT INTO reading_log (date, title, author, type, minutes)
VALUES ('$(esc "$dt")', '$(esc "$title")', '$(esc "$author")',
'$(esc "$type")', $minutes);"
notify-send "dlog" " Logged: $title ($dur)"
}
# ── Completed: Books ──────────────────────────────────────────
add_book() {
local title subtitle author cover link
title=$(ask " Title:")
[ -z "$title" ] && return
subtitle=$(ask " Subtitle:")
author=$(ask " Author:")
[ -z "$author" ] && return
cover=$(ask " Cover URL:")
link=$(ask " Link:")
sql "INSERT INTO reading_books (title, subtitle, author, cover, link)
VALUES ('$(esc "$title")', '$(esc "$subtitle")', '$(esc "$author")',
'$(esc "$cover")', '$(esc "$link")');"
notify-send "dlog" " Added: $title"
}
# ── Completed: Audiobooks ─────────────────────────────────────
add_audiobook() {
local title subtitle author cover link
title=$(ask " Title:")
[ -z "$title" ] && return
subtitle=$(ask " Subtitle:")
author=$(ask " Author:")
[ -z "$author" ] && return
cover=$(ask " Cover URL:")
link=$(ask " Link:")
sql "INSERT INTO reading_audiobooks (title, subtitle, author, cover, link)
VALUES ('$(esc "$title")', '$(esc "$subtitle")', '$(esc "$author")',
'$(esc "$cover")', '$(esc "$link")');"
notify-send "dlog" " Added: $title"
}
# ── Completed: Blog Posts ─────────────────────────────────────
add_blog() {
local title author src arc year
title=$(ask " Title:")
[ -z "$title" ] && return
author=$(ask " Author:")
src=$(ask " Source URL:")
arc=$(ask " Archive URL:")
year=$(ask " Year:")
sql "INSERT INTO reading_blogs (title, author, source_link, archive_link, publication_year)
VALUES ('$(esc "$title")', '$(esc "$author")', '$(esc "$src")',
'$(esc "$arc")', ${year:-NULL});"
notify-send "dlog" " Added: $title"
}
# ── Completed: Short Stories ──────────────────────────────────
add_short_story() {
local title author year
title=$(ask " Title:")
[ -z "$title" ] && return
author=$(ask " Author:")
[ -z "$author" ] && return
year=$(ask " Year:")
sql "INSERT INTO short_stories (title, author, publication_year)
VALUES ('$(esc "$title")', '$(esc "$author")', ${year:-NULL});"
notify-send "dlog" " Added: $title"
}
# ── Completed: Verse ──────────────────────────────────────────
add_verse() {
local title author vtype src year
title=$(ask " Title:")
[ -z "$title" ] && return
author=$(ask " Author:")
[ -z "$author" ] && return
vtype=$(printf '%s\n' \
"lyric poem" "narrative poem" "ballad" "sonnet" "epigram" \
"haiku" "ode" "elegy" "free verse" "epic" "limerick" "villanelle" \
| pick " Form:" 12)
src=$(ask " Source URL:")
year=$(ask " Year:")
sql "INSERT INTO reading_verse (title, author, verse_type, source_link, publication_year)
VALUES ('$(esc "$title")', '$(esc "$author")', '$(esc "$vtype")',
'$(esc "$src")', ${year:-NULL});"
notify-send "dlog" " Added: $title"
}
# ── Completed: Essays ─────────────────────────────────────────
add_essay() {
local title author src arc year
title=$(ask " Title:")
[ -z "$title" ] && return
author=$(ask " Author:")
[ -z "$author" ] && return
src=$(ask " Source URL:")
arc=$(ask " Archive URL:")
year=$(ask " Year:")
sql "INSERT INTO reading_essays (title, author, source_link, archive_link, publication_year)
VALUES ('$(esc "$title")', '$(esc "$author")', '$(esc "$src")',
'$(esc "$arc")', ${year:-NULL});"
notify-send "dlog" " Added: $title"
}
# ── Completed: Papers ─────────────────────────────────────────
add_paper() {
local title src arc year
local authors=()
title=$(ask " Title:")
[ -z "$title" ] && return
# Multi-author loop
while true; do
local a
a=$(printf '%s\n' " Done adding authors" \
| pick " Author $((${#authors[@]} + 1)):" 1)
[[ -z "$a" || "$a" == *Done* ]] && break
authors+=("$a")
done
[ ${#authors[@]} -eq 0 ] && return
# Build JSON array for author field
local json='['
for i in "${!authors[@]}"; do
[ "$i" -gt 0 ] && json+=','
json+="\"$(esc "${authors[$i]}")\""
done
json+=']'
src=$(ask " Source URL:")
arc=$(ask " Archive URL:")
year=$(ask " Year:")
sql "INSERT INTO reading_papers (title, author, source_link, archive_link, publication_year)
VALUES ('$(esc "$title")', '$(esc "$json")', '$(esc "$src")',
'$(esc "$arc")', ${year:-NULL});"
notify-send "dlog" " Added: $title (${#authors[@]} authors)"
}
# ── Completed Submenu ─────────────────────────────────────────
completed_menu() {
local choice
choice=$(printf '%s\n' \
" Books" \
" Audiobooks" \
" Blog Posts" \
" Short Stories" \
" Verse" \
" Essays" \
" Papers" \
| pick " Completed" 7)
[ -z "$choice" ] && return
case "$choice" in
*Books*) add_book ;;
*Audiobooks*) add_audiobook ;;
*Blog*) add_blog ;;
*Short*) add_short_story ;;
*Verse*) add_verse ;;
*Essays*) add_essay ;;
*Papers*) add_paper ;;
esac
}
# ── Main ──────────────────────────────────────────────────────
main=$(printf '%s\n' \
" Log" \
" Completed" \
| pick " dlog" 2)
[ -z "$main" ] && exit 0
case "$main" in
*Log*) log_entry ;;
*Completed*) completed_menu ;;
esac