~kris/dots

srice

ref: ff7367cb2ef828502c3f7fde003f45d5daa33206 srice/.local/bin/dread -rwxr-xr-x 5.2 KiB
ff7367cb — Kris Yotam add ram memory visualization script 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env bash
###############################################################################
# dread — Browse research library via dmenu, open in Zathura
#
# Maintainer:   Kris Yotam <krisyotam@pm.me>
# License:      MIT
# Created:      2026-02-15
#
# Top-level categories:
#   Papers        → ~/research/library/papers/     (field → subfield → PDF)
#   Texts         → ~/research/library/texts/       (textbooks by field)
#   Primary Texts → ~/research/library/primary-texts/ (drama, essays, fiction, poetry)
#   Books         → ~/Calibre Library/               (personal library via Calibre)
#
# All files opened in Zathura.
###############################################################################

LIBRARY="$HOME/research/library"
CALIBRE="$HOME/Calibre Library"

###############################################################################
# helpers
###############################################################################

# Drill into a directory: show subdirs, then files. Recurse on subdir selection.
browse_dir() {
    local dir="$1"
    local prompt="$2"

    while true; do
        # Build list: subdirectories first, then readable files
        local items=""

        # Subdirectories (with trailing /, skip hidden)
        while IFS= read -r d; do
            [ -z "$d" ] && continue
            local dname
            dname=$(basename "$d")
            [[ "$dname" == .* ]] && continue
            items+="󰉋  ${dname}/"$'\n'
        done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d | sort)

        # Files (PDF, EPUB, DJVU, etc.)
        while IFS= read -r f; do
            [ -z "$f" ] && continue
            local name
            name=$(basename "$f")
            case "$name" in
                *.pdf)  items+="  $name"$'\n' ;;
                *.epub) items+="  $name"$'\n' ;;
                *.djvu) items+="  $name"$'\n' ;;
                *.mobi) items+="  $name"$'\n' ;;
                *.cbz)  items+="󰋩  $name"$'\n' ;;
                *)      items+="  $name"$'\n' ;;
            esac
        done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f \( -name "*.pdf" -o -name "*.epub" -o -name "*.djvu" -o -name "*.mobi" -o -name "*.cbz" \) | sort)

        [ -z "$items" ] && { notify-send "dread" "Empty: $(basename "$dir")"; return; }

        local choice
        choice=$(printf '%s' "$items" | sed '/^$/d' | dmenu -i -l 20 -p "$prompt")
        [ -z "$choice" ] && return

        # Strip icon prefix
        local name
        name=$(echo "$choice" | sed 's/^[^ ]* *//')

        if [[ "$name" == */ ]]; then
            # Directory selected — recurse
            name="${name%/}"
            browse_dir "$dir/$name" "  $name"
        else
            # File selected — open in zathura
            zathura "$dir/$name" &>/dev/null & disown
            return
        fi
    done
}

# Browse Calibre library — flat list of all books via metadata.db
browse_calibre() {
    local db="$CALIBRE/metadata.db"
    if [ ! -f "$db" ]; then
        notify-send "dread" "Calibre Library not found"
        return
    fi

    # Query all books: "Title — Author" with path
    local entries
    entries=$(sqlite3 "$db" "
        SELECT b.title, COALESCE(a.name, 'Unknown'), b.path,
               COALESCE(d.format, 'pdf'), COALESCE(d.name, b.title)
        FROM books b
        LEFT JOIN books_authors_link bal ON bal.book = b.id
        LEFT JOIN authors a ON a.id = bal.author
        LEFT JOIN data d ON d.book = b.id
        WHERE d.format IS NOT NULL
        ORDER BY b.title
    " 2>/dev/null)

    [ -z "$entries" ] && { notify-send "dread" "No books in Calibre"; return; }

    # Build dmenu list and parallel path array
    local menu=""
    local -a paths=()

    while IFS='|' read -r title author relpath fmt fname; do
        [ -z "$title" ] && continue
        local ext
        ext=$(echo "$fmt" | tr '[:upper:]' '[:lower:]')
        menu+="  ${title}${author}"$'\n'
        paths+=("$CALIBRE/$relpath/$fname.$ext")
    done <<< "$entries"

    local choice
    choice=$(printf '%s' "$menu" | sed '/^$/d' | dmenu -i -l 20 -p "  Books")
    [ -z "$choice" ] && return

    # Find matching index (avoid Plan 9 grep which lacks -F)
    local idx=-1
    local i=0
    while IFS= read -r line; do
        [ -z "$line" ] && continue
        if [[ "$line" == "$choice" ]]; then
            idx=$i
            break
        fi
        ((i++))
    done < <(printf '%s' "$menu" | sed '/^$/d')
    [[ $idx -lt 0 ]] && return

    local file="${paths[$idx]}"

    if [ -f "$file" ]; then
        zathura "$file" &>/dev/null & disown
    else
        notify-send "dread" "File not found: $(basename "$file")"
    fi
}

###############################################################################
# main
###############################################################################

CHOICE=$(printf '%s\n' \
    "  Papers" \
    "  Texts" \
    "  Primary Texts" \
    "  Books" \
| dmenu -i -l 4 -p "󰂺  Library")

[ -z "$CHOICE" ] && exit 0

case "$CHOICE" in
    *Papers*)        browse_dir "$LIBRARY/papers"        "  Papers" ;;
    *Texts*)         browse_dir "$LIBRARY/texts"         "  Texts" ;;
    *Primary\ Texts*) browse_dir "$LIBRARY/primary-texts"  "  Primary" ;;
    *Books*)         browse_calibre ;;
esac