commit d0b08e8adc353e0ac07cf2b20bdb36bb37d32713
parent 02361b2dbaa755c0da360f3788c35a38e5a908ad
Author: Sivaldo <gxixtx@xsxixvxaxlxdxoxdxaxvxix.xcxoxm>
Date: Sun, 9 Aug 2026 15:26:28 -0300
refactor(culto): usa hinário local em JSON em vez de baixar do Codeberg
Diffstat:
| M | .local/bin/culto | | | 256 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
1 file changed, 137 insertions(+), 119 deletions(-)
diff --git a/.local/bin/culto b/.local/bin/culto
@@ -4,17 +4,14 @@
# Configuração
# ==============================================================================
-MUSICAS_URL="https://codeberg.org/sivaldodavi/ipp/archive/main.tar.gz"
-PIX_URL="https://codeberg.org/sivaldodavi/ipp/raw/branch/main/pix-igreja.png"
-
-CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/culto"
+CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}"
BIBLE_FILE="$CACHE_DIR/biblia"
-BIBLE_URL="https://codeberg.org/sivaldodavi/ipp/raw/branch/main/biblia"
-MUSICAS_DIR="$CACHE_DIR/musicas"
-PIX_FILE="$CACHE_DIR/pix.png"
+MUSICAS_FILE="$CACHE_DIR/musicas.json"
+PIX_FILE="$CACHE_DIR/pix-igreja.png"
+AGENDA_FILE="$CACHE_DIR/agenda.jpeg"
SENT_FILE="/tmp/sent_presentation"
-: "${EDITOR:=vi}"
+: "${EDITOR:=vim}"
LITURGIA_FIXA=(
"T: oração inicial"
@@ -38,38 +35,28 @@ Domingo (18:00) - Culto solene"
# Utilitários
# ==============================================================================
-# Verifica/Instala o xrandr
if ! command -v xrandr >/dev/null 2>&1; then
echo "'xrandr' não encontrado. Instalando..." >&2
doas apk add xrandr || { echo "ERRO: Falha ao instalar xrandr." >&2; exit 1; }
fi
-_download() {
- local url="$1" dest="$2"
- if command -v curl >/dev/null 2>&1; then
- curl -fsSL "$url" -o "$dest"
- elif command -v wget >/dev/null 2>&1; then
- wget -q "$url" -O "$dest"
- else
- echo "Erro: curl ou wget necessário." >&2
- return 1
- fi
-}
-
# ==============================================================================
-# Bíblia
+# Bíblia Local
# ==============================================================================
_ensure_bible() {
- [ -f "$BIBLE_FILE" ] && return 0
- mkdir -p "$(dirname "$BIBLE_FILE")"
- echo "Baixando bíblia..." >&2
- _download "$BIBLE_URL" "$BIBLE_FILE"
+ if [ ! -f "$BIBLE_FILE" ]; then
+ echo "ERRO: Arquivo de bíblia não encontrado em: $BIBLE_FILE" >&2
+ return 1
+ fi
}
_bible_python() {
BIBLE_FILE="$BIBLE_FILE" python3 - "$@" <<'PYEOF'
-import os, re, sys
+import os, re, sys, signal
+
+# Ignora erro de Broken Pipe quando pipeamos pro fzf ou less
+signal.signal(signal.SIGPIPE, signal.SIG_DFL)
BIBLE_FILE = os.environ["BIBLE_FILE"]
SLIDE_WIDTH = 180
@@ -151,7 +138,6 @@ def slides_read(rows, ref_label, no_cover, resp):
return "\n".join(out)
def terminal_read(rows, ref_label):
- USE_COLOR = sys.stdout.isatty()
print(azul(ref_label))
print()
for r in rows:
@@ -202,7 +188,7 @@ PYEOF
}
# ==============================================================================
-# Integração com o 'sent'
+# Integração ao 'sent'
# ==============================================================================
format_sent() {
@@ -227,92 +213,138 @@ clear_sent() {
# Músicas
# ==============================================================================
-ensure_musicas() {
- if [ ! -d "$MUSICAS_DIR" ] || [ -z "$(ls -A "$MUSICAS_DIR"/*.md 2>/dev/null)" ]; then
- echo "Baixando hinário..." >&2
- local tmp_tar="$CACHE_DIR/musicas.tar.gz"
- mkdir -p "$CACHE_DIR"
- _download "$MUSICAS_URL" "$tmp_tar" || return 1
- rm -rf "$MUSICAS_DIR"
- mkdir -p "$MUSICAS_DIR"
- tar -xzf "$tmp_tar" -C "$MUSICAS_DIR" --strip-components=1 2>/dev/null \
- || tar -xzf "$tmp_tar" -C "$MUSICAS_DIR"
- find "$MUSICAS_DIR" -mindepth 2 -name "*.md" -exec mv {} "$MUSICAS_DIR/" \; 2>/dev/null
- rm -f "$tmp_tar"
+_ensure_musicas() {
+ if [ ! -f "$MUSICAS_FILE" ]; then
+ echo "{}" > "$MUSICAS_FILE" # Cria vazio se não existir
fi
}
-_song_title() {
- basename "$1" .md | sed -E 's/^[0-9]+[a-z]?\s*-\s*//; s/\s*\(.*\)\s*$//'
-}
-
-_show_md_song() {
- local filepath="$1"
- local title
- title=$(_song_title "$filepath")
-
- {
- printf '%s\n\n\n' "$title"
+_render_json_song() {
+ local title="$1"
+ MUSICAS_FILE="$MUSICAS_FILE" python3 - "$title" <<'PYEOF'
+import sys, json, os, signal
- python3 - "$filepath" <<'PYEOF'
-import sys, re
+signal.signal(signal.SIGPIPE, signal.SIG_DFL)
-with open(sys.argv[1], encoding='utf-8') as f:
- content = f.read()
+musicas_file = os.environ["MUSICAS_FILE"]
+target_title = sys.argv[1]
-content = re.sub(r'^#.*\n?', '', content, flags=re.MULTILINE)
+with open(musicas_file, encoding='utf-8') as f:
+ songs = json.load(f)
-for block in re.split(r'\n{2,}', content.strip()):
- block = block.strip()
- if block:
- print(block)
+if target_title in songs:
+ print(target_title)
+ print("\n\n")
+ for stanza in songs[target_title]:
+ print(stanza)
print("\n")
+else:
+ print(f"Erro: Música '{target_title}' não encontrada.", file=sys.stderr)
+ sys.exit(1)
PYEOF
- } | format_sent
}
pick_song() {
- ensure_musicas || return 1
+ _ensure_musicas || return 1
local chosen
- chosen=$(ls "$MUSICAS_DIR"/*.md 2>/dev/null \
- | xargs -I{} basename {} .md \
- | sed -E 's/^[0-9]+[a-z]?\s*-\s*//; s/\s*\(.*\)\s*$//' \
- | sort \
+ # Adicionado o signal import e handler aqui também no one-liner
+ chosen=$(python3 -c "import json, signal; signal.signal(signal.SIGPIPE, signal.SIG_DFL); data=json.load(open('$MUSICAS_FILE')); print('\n'.join(sorted(data.keys())))" \
| fzf --reverse --prompt="Música: ")
[ -z "$chosen" ] && return 0
- local match
- match=$(ls "$MUSICAS_DIR"/*.md 2>/dev/null \
- | while IFS= read -r f; do
- t=$(_song_title "$f")
- [ "$t" = "$chosen" ] && printf '%s\n' "$f" && break
- done)
+ _render_json_song "$chosen" | format_sent
+}
- if [ -n "$match" ]; then
- _show_md_song "$match"
- else
- echo "Música não encontrada: $chosen" >&2
- fi
+# ==============================================================================
+# Adicionar / Editar Músicas (JSON <-> MD temporário)
+# ==============================================================================
+
+_update_json_from_md() {
+ local action="$1"
+ local title="$2"
+ local tmp_md="$3"
+
+ MUSICAS_FILE="$MUSICAS_FILE" python3 - "$action" "$title" "$tmp_md" <<'PYEOF'
+import json, sys, os, re
+
+musicas_file = os.environ["MUSICAS_FILE"]
+action = sys.argv[1]
+title = sys.argv[2]
+tmp_md = sys.argv[3]
+
+if os.path.exists(musicas_file):
+ with open(musicas_file, 'r', encoding='utf-8') as f:
+ try:
+ songs = json.load(f)
+ except json.JSONDecodeError:
+ songs = {}
+else:
+ songs = {}
+
+if action == "extract":
+ text = "\n\n".join(songs.get(title, []))
+ with open(tmp_md, 'w', encoding='utf-8') as f:
+ f.write(f"# {title}\n\n{text}\n")
+
+elif action == "save":
+ with open(tmp_md, 'r', encoding='utf-8') as f:
+ content = f.read()
+
+ # Remove o título em markdown (ex: # Nome da música)
+ content = re.sub(r'^#.*\n?', '', content, flags=re.MULTILINE)
+
+ # Separa os blocos (estrofes) por linhas em branco
+ stanzas = [b.strip() for b in re.split(r'\n{2,}', content) if b.strip()]
+
+ if stanzas:
+ songs[title] = stanzas
+ with open(musicas_file, 'w', encoding='utf-8') as f:
+ json.dump(songs, f, ensure_ascii=False, indent=2)
+ print(f"✅ Música salva com sucesso no JSON!")
+ else:
+ print("⚠️ Nenhuma estrofe encontrada. Alteração descartada.")
+PYEOF
}
-song_by_name() {
- ensure_musicas || return 1
- local query="${1,,}"
+add_song() {
+ _ensure_musicas
+ echo -n "Digite o título da nova música: "
+ read -r title
+ [ -z "$title" ] && return 1
+
+ local tmp_md="/tmp/culto_musica_$$.md"
+ echo -e "# $title\n\nEstrofe 1...\n\nEstrofe 2..." > "$tmp_md"
+
+ # Abre no Vim forçando o modo markdown
+ ${EDITOR} -c "set filetype=markdown" "$tmp_md"
+
+ # Salva o resultado no json
+ _update_json_from_md "save" "$title" "$tmp_md"
+ rm -f "$tmp_md"
+}
- local match
- match=$(ls "$MUSICAS_DIR"/*.md 2>/dev/null \
- | while IFS= read -r f; do
- t=$(_song_title "$f" | tr '[:upper:]' '[:lower:]')
- [[ "$t" == *"$query"* ]] && printf '%s\n' "$f" && break
- done)
+edit_song() {
+ _ensure_musicas || return 1
- if [ -n "$match" ]; then
- _show_md_song "$match"
- else
- echo "Música não encontrada: $1" >&2
- fi
+ local chosen
+ chosen=$(python3 -c "import json, signal; signal.signal(signal.SIGPIPE, signal.SIG_DFL); data=json.load(open('$MUSICAS_FILE')); print('\n'.join(sorted(data.keys())))" \
+ | fzf --reverse --prompt="Editar Música: ")
+
+ [ -z "$chosen" ] && return 0
+
+ local tmp_md="/tmp/culto_musica_$$.md"
+
+ # Extrai o array do JSON e converte em texto pra editar
+ _update_json_from_md "extract" "$chosen" "$tmp_md"
+
+ # Abre no Vim forçando o modo markdown
+ ${EDITOR} -c "set filetype=markdown" "$tmp_md"
+
+ # Pega o arquivo editado e devolve pro JSON
+ _update_json_from_md "save" "$chosen" "$tmp_md"
+ rm -f "$tmp_md"
}
# ==============================================================================
@@ -328,13 +360,12 @@ get_bible() {
[ "$no_cover" = "true" ] && flags="$flags --no-cover"
[ "$resp" = "true" ] && flags="$flags --resp"
- # shellcheck disable=SC2086
_ensure_bible || exit 1
_bible_python read $flags "$query" | format_sent
}
# ==============================================================================
-# Liturgia fixa (menu fzf)
+# Liturgia
# ==============================================================================
show_menu() {
@@ -358,13 +389,15 @@ show_menu() {
}
# ==============================================================================
-# Lógica principal
+# Lógica Principal
# ==============================================================================
case "${1:-}" in
m|menu) show_menu ;;
+
cm) pick_song ;;
- song) song_by_name "$2" ;;
+ as|add-song) add_song ;;
+ es|edit-song) edit_song ;;
b|bible) get_bible "$2" "false" "false" ;;
bd|bible-clean) get_bible "$2" "true" "false" ;;
@@ -392,48 +425,33 @@ case "${1:-}" in
p|pix)
if [ ! -f "$PIX_FILE" ]; then
- echo "Baixando imagem PIX..." >&2
- mkdir -p "$CACHE_DIR"
- _download "$PIX_URL" "$PIX_FILE" || exit 1
+ echo "Erro: Arquivo PIX não encontrado em $PIX_FILE" >&2
+ exit 1
fi
nohup nsxiv "$PIX_FILE" >/dev/null 2>&1 &
;;
- bible-reset)
- rm -f "$BIBLE_FILE"
- _ensure_bible && echo "Bíblia atualizada."
- ;;
- musicas-reset)
- rm -rf "$MUSICAS_DIR"
- ensure_musicas && echo "Hinário atualizado."
- ;;
- pix-reset)
- rm -f "$PIX_FILE"
- mkdir -p "$CACHE_DIR"
- _download "$PIX_URL" "$PIX_FILE" && echo "Imagem PIX atualizada."
- ;;
-
*)
cat <<'USAGE'
culto <comando> [arg]
m / menu Liturgia (fzf)
- cm Músicas (fzf)
- song <nome> Música por nome parcial
+
+ cm Selecionar música (fzf)
+ as / add-song Adicionar nova música (via Vim)
+ es / edit-song Editar música existente (via Vim)
+
b / bible <ref> Bíblia (com capa)
bd <ref> Bíblia (sem capa)
r / resp <ref> Responsivo (DIRIGENTE / IGREJA)
v / ver <ref> Bíblia no terminal
s / search <pal> Buscar na bíblia
+
t / text <texto> Texto livre
ag / agenda Agenda da semana
i / index Índice dos slides atuais
x / clear Limpar tela do sent
p / pix Abrir QR code PIX
-
- bible-reset Rebaixar bíblia
- musicas-reset Rebaixar hinário
- pix-reset Rebaixar imagem PIX
USAGE
;;
esac