No description
  • Go 79.3%
  • HTML 12.9%
  • Python 5.8%
  • Shell 1.6%
  • Dockerfile 0.4%
Find a file
2026-05-26 17:06:10 +02:00
.claude new track detection 2026-04-26 17:42:26 +02:00
docs aggiunto monitoraggio lumiere client 2026-05-14 21:48:47 +02:00
internal nuovi indici 2026-05-26 16:53:07 +02:00
scripts nuovi indici 2026-05-26 16:53:07 +02:00
state reindex in remoto 2026-05-26 17:06:10 +02:00
static aggiunto monitoraggio lumiere client 2026-05-14 21:48:47 +02:00
.gitignore aggiunto ffprobe 2026-04-26 11:33:53 +02:00
blog.md feat(enrichment): add progress tracking and revert logger 2026-04-21 15:25:57 +02:00
byschbuster migliorata gestione della connessione locale 2026-03-24 17:28:53 +01:00
deploy_raspberry.sh reso un po piu white label 2026-03-25 10:38:41 +01:00
docker-compose.yml fix permessi 2026-04-25 19:19:42 +02:00
Dockerfile fix sqlite on docker 2026-05-12 16:54:05 +02:00
go.mod rimosso blece 2026-05-14 14:38:33 +02:00
go.sum rimosso blece 2026-05-14 14:38:33 +02:00
localbuster sono un coglione andiamo a pagare sundar piciai 2026-04-24 20:17:24 +02:00
main.go chiariti i nomi delle var dei check 2026-05-15 09:40:49 +02:00
README.md aggiunto trigger manuale per liveness-check lumiere 2026-05-26 09:18:23 +02:00

byschbuster

Server locale che gestisce VLC e mantiene un catalogo dei file video hostati su domini controllati. Espone un'interfaccia web per cercare contenuti e avviare la riproduzione; supporta connessioni sia dalla rete locale che da internet (VLC gira sempre in locale, il client può connettersi da qualunque posto).

Funzionalità principali

  • Catalogo — visita ricorsivamente le cartelle radice sul dominio raggiungibile e costruisce una lista piatta di tutti i file presenti.
  • Arricchimento AI — interroga Gemini con i metadati del catalogo per produrre titolo, serie, stagione, episodio, anno e trame.
  • Scan traccia italiana — usa ffprobe sulla direct_url per rilevare l'indice della traccia audio italiana.
  • Indicizzazione e ricerca — costruisce un indice in memoria da SQLite + catalogo; ricerca fuzzy con closestmatch.

La rete viene tentata prima sul dominio locale (granarolo.local:3923), con fallback su internet (dropbox.byschii.com).

Per i dettagli operativi vedi docs/background_ops.md e docs/user_interaction.md.

Prerequisiti

  • Go 1.23+
  • VLC installato e raggiungibile nel PATH
  • Una chiave API Gemini (per l'arricchimento AI)

Installazione

go mod tidy
go build -o byschbuster .

File .env

Le variabili possono essere messe in un file .env nella root del progetto. Le variabili già presenti nell'ambiente hanno sempre precedenza.

GEMINI_API_KEY=la-tua-chiave
GEMINI_MODEL=gemini-3.1-flash-lite-preview
PORT=8080
STATE_DIR=./state
CONNECTION_CHECK_INTERVAL_SECS=60
LUMIERE_CHECK_INTERVAL_SECS=60
CATALOG_INTERVAL_SECS=900
ENRICHMENT_INTERVAL_SECS=1200
ITA_TRACK_SCAN_INTERVAL_SECS=60
INDEX_INTERVAL_SECS=1500
LOCAL_DOMAIN=granarolo.local:3923
INTERNET_DOMAIN=dropbox.byschii.com
LUMIERE_URL=http://lumiere.local:8888
ROOT_FOLDERS=4. cartoni, 6. film, 7. serie, 8. tvshow

Variabili d'ambiente

Variabile Obbligatoria Default Descrizione
GEMINI_API_KEY no* Chiave API Google Gemini. Senza di essa l'arricchimento AI è disabilitato
GEMINI_MODEL no gemini-3.1-flash-lite-preview Modello Gemini usato per l'arricchimento
PORT no 8080 Porta su cui il server HTTP ascolta
STATE_DIR no ./state Cartella dove salvare i file di stato (catalog.json, enriched.json)
CONNECTION_CHECK_INTERVAL_SECS no 0 (manual) Intervallo in secondi tra un probe dei domini e il successivo. 0 = modalità manuale
CATALOG_INTERVAL_SECS no 0 (manual) Intervallo in secondi tra un auto-refresh del catalogo e il successivo. 0 = modalità manuale
ENRICHMENT_INTERVAL_SECS no 0 (manual) Intervallo in secondi tra un ciclo di auto-arricchimento e il successivo. 0 = modalità manuale
ITA_TRACK_SCAN_INTERVAL_SECS no 0 (manual) Intervallo in secondi tra uno scan ffprobe e il successivo per aggiornare ita_lang_audio_track_index da direct_url. 0 = modalità manuale
INDEX_INTERVAL_SECS no 0 (manual) Intervallo in secondi tra una ricostruzione dell'indice e la successiva. 0 = modalità manuale
LOCAL_DOMAIN no granarolo.local:3923 Dominio locale provato per primo durante la sync del catalogo
INTERNET_DOMAIN no dropbox.byschii.com Dominio fallback se il locale non risponde
LUMIERE_URL no URL base del server Lumiere da monitorare
LUMIERE_CHECK_INTERVAL_SECS no 0 (manual) Intervallo in secondi tra un check di raggiungibilità Lumiere e il successivo. 0 = modalità manuale
ROOT_FOLDERS no 4. cartoni, 6. film, 7. serie, 8. tvshow Cartelle root del catalogo separate da virgola; gli spazi sono accettati

I tre check di stato

GET /api/status riporta tre campi che provengono da meccanismi distinti.

connection — probe della connessione di rete

Gestito da un goroutine in background (internal/domainmonitor/monitor.go). Ad ogni ciclo esegue probe HTTP nell'ordine:

  1. GET http://<LOCAL_DOMAIN>/ — se raggiungibile, connection = "local"
  2. GET https://<INTERNET_DOMAIN>/ — se raggiungibile, connection = "internet"
  3. Nessuno dei due risponde → connection = "none"

Il primo probe parte subito all'avvio; i successivi seguono l'intervallo CONNECTION_CHECK_INTERVAL_SECS. Con 0 (modalità manuale) il probe non si ripete automaticamente ma può essere scatenato via POST /api/manual/monitor.

lumiere — raggiungibilità del server Lumiere

Gestito da un goroutine in background (internal/lumiere/lumiere.go). Ad ogni ciclo invia una richiesta HEAD a <LUMIERE_URL>/ e salva il risultato booleano in memoria (protetto da RWMutex). Il valore è esposto tramite IsReachable().

L'intervallo è controllato da LUMIERE_CHECK_INTERVAL_SECS. Con 0 (modalità manuale) il check non si ripete automaticamente ma può essere scatenato via POST /api/manual/lumiere.

clientOrigin — origine del client

Non è un check in background: viene calcolato in tempo reale ad ogni richiesta da isLocalRequest() in internal/server/server.go. Controlla semplicemente se l'header HTTP Host contiene .local o localhost:

  • "local" — richiesta proveniente dalla rete locale
  • "remote" — richiesta proveniente da internet

Questo valore è usato per bloccare le azioni di scrittura (/api/play, /api/stop, cambio velocità) quando il client non è in rete locale.

Avvio

Modalità manuale (default)

Di default tutti i job sono in modalità manuale (*_INTERVAL_SECS=0). I job vanno attivati via API.

GEMINI_API_KEY=la-tua-chiave ./byschbuster

Modalità automatica

Imposta un intervallo positivo (in secondi) per ogni job che vuoi far girare in autonomia:

GEMINI_API_KEY=la-tua-chiave \
  CONNECTION_CHECK_INTERVAL_SECS=60 \
  LUMIERE_CHECK_INTERVAL_SECS=60 \
  CATALOG_INTERVAL_SECS=900 \
  ENRICHMENT_INTERVAL_SECS=1200 \
  ITA_TRACK_SCAN_INTERVAL_SECS=60 \
  INDEX_INTERVAL_SECS=1500 \
  ./byschbuster

API HTTP

GET /

Serve l'interfaccia web.


GET /api/status

Stato del server: dominio raggiunto e origine del client.

Risposta:

{
  "connection": "local",
  "clientOrigin": "local"
}
  • connection: "local" | "internet" | "none" — quale dominio ha risposto all'ultima sync
  • clientOrigin: "local" | "remote" — determinato dall'header Host della richiesta (.local → locale)

GET /api/search?q=<query>

Cerca nel catalogo per nome file, percorso logico o nome episodio (case-insensitive, substring). Restituisce max 50 risultati, arricchiti con i dati AI se disponibili.

Un file può contenere più episodi (es. S01E01-E03). In quel caso il campo episodi conterrà un elemento per ciascun episodio, multi_episodio sarà true e verrà aggiunto un warning.

curl "http://localhost:8080/api/search?q=breaking+bad"

Risposta (singolo episodio):

[
  {
    "name": "Breaking.Bad.S01E01.mkv",
    "logical_path": "7. serie/Breaking Bad/Breaking.Bad.S01E01.mkv",
    "direct_url": "https://dropbox.byschii.com/7. serie/...",
    "root_folder": "7. serie",
    "size": 1234567890,
    "ts": 1715885468,
    "enriched": true,
    "serie": "Breaking Bad",
    "anno": 2008,
    "episodi": [
      {
        "stagione": 1,
        "episodio": 1,
        "nome": "Pilot",
        "trama_breve": "...",
        "trama_classica": "...",
        "trama_estesa": "..."
      }
    ]
  }
]

Risposta (multi-episodio):

[
  {
    "name": "Breaking.Bad.S01E01-E03.mkv",
    "logical_path": "7. serie/Breaking Bad/Breaking.Bad.S01E01-E03.mkv",
    "direct_url": "https://dropbox.byschii.com/7. serie/...",
    "root_folder": "7. serie",
    "size": 3456789012,
    "ts": 1715885468,
    "enriched": true,
    "serie": "Breaking Bad",
    "anno": 2008,
    "multi_episodio": true,
    "warning": "Questo file contiene più episodi",
    "episodi": [
      { "stagione": 1, "episodio": 1, "nome": "Pilot", "trama_breve": "...", "trama_classica": "...", "trama_estesa": "..." },
      { "stagione": 1, "episodio": 2, "nome": "Cat's in the Bag...", "trama_breve": "...", "trama_classica": "...", "trama_estesa": "..." },
      { "stagione": 1, "episodio": 3, "nome": "...And the Bag's in the River", "trama_breve": "...", "trama_classica": "...", "trama_estesa": "..." }
    ]
  }
]

Se q è omesso, restituisce tutti i file (limitati a 50).


POST /api/play

Avvia la riproduzione in VLC. Solo da rete locale (header Host deve contenere .local o localhost).

curl -X POST http://byschbuster.local:8080/api/play \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://dropbox.byschii.com/7. serie/.../file.mkv", "rate": false}'

Body:

{
  "url": "https://dropbox.byschii.com/...",
  "rate": true
}
  • url: deve avere host granarolo.local:3923 o dropbox.byschii.com
  • rate: true aggiunge --rate=1.1 al comando VLC

Risposte:

  • 200 — riproduzione avviata
  • 400 — URL non in whitelist o body malformato
  • 403 — client non locale
  • 409 — VLC è già in riproduzione

POST /api/stop

Termina VLC. Solo da rete locale.

curl -X POST http://byschbuster.local:8080/api/stop

Risposte:

  • 200 — VLC terminato
  • 403 — client non locale
  • 500 — VLC non in esecuzione o errore di segnale

API modalità manuale

Attivi sempre, ma utili principalmente con MANUAL_CATALOG=true e/o MANUAL_ENRICHMENT=true.

In modalità manuale, i rispettivi job non partono automaticamente — vanno scatenati a mano via API. Per sapere quali file mancano di arricchimento, usare /api/manual/unenriched.

POST /api/manual/monitor

Esegue un probe dei domini (locale e internet) e restituisce lo stato aggiornato.

curl -X POST http://localhost:8080/api/manual/monitor

Risposta:

{
  "status": "probe completato",
  "connection": "local",
  "activeDomain": "granarolo.local:3923"
}

POST /api/manual/lumiere

Esegue un probe di raggiungibilità del server Lumiere e restituisce lo stato aggiornato.

curl -X POST http://localhost:8080/api/manual/lumiere

Risposta:

{
  "status": "probe completato",
  "lumiere": "reachable"
}
  • lumiere: "reachable" | "unreachable" — risultato del probe HEAD su LUMIERE_URL

POST /api/manual/catalog

Avvia una sync del catalogo in background.

curl -X POST http://localhost:8080/api/manual/catalog

Risposta:

{ "status": "catalog refresh avviato" }

POST /api/manual/unenriched

Restituisce le key dei file presenti nel catalogo ma non ancora arricchiti via Gemini.

curl -X POST http://localhost:8080/api/manual/unenriched

Risposta:

{
  "status": "ok",
  "unenriched": 3,
  "total": 4820,
  "keys": [
    "a3f1c2d4...",
    "b8e9f012...",
    "cc34ab56..."
  ]
}

POST /api/manual/enrich

Arricchisce un singolo file via Gemini AI. Usare una key restituita da /api/manual/unenriched.

curl -X POST http://localhost:8080/api/manual/enrich \
  -H 'Content-Type: application/json' \
  -d '{"key": "a3f1c2d4..."}'

Body:

{ "key": "a3f1c2d4..." }

Risposta:

{
  "status": "arricchimento completato",
  "enriched": {
    "key": "a3f1c2d4...",
    "name": "Inception.2010.mkv",
    "logical_path": "6. film/Inception.2010.mkv",
    "direct_url": "https://dropbox.byschii.com/6. film/Inception.2010.mkv",
    "root_folder": "6. film",
    "size": 1234567890,
    "ts": 1715885468,
    "serie": "",
    "anno": 2010,
    "episodi": [
      {
        "nome": "Inception",
        "trama_breve": "...",
        "trama_classica": "...",
        "trama_estesa": "..."
      }
    ]
  }
}

Errori:

  • 400 — key mancante nel body
  • 404 — nessun file in catalogo con quella key
  • 500 — errore Gemini o API key non impostata

Flusso tipico in modalità manuale

# 1. Sincronizza il catalogo (aspetta qualche secondo)
curl -X POST http://localhost:8080/api/manual/catalog
sleep 30

# 2. Vedi quali file non sono ancora arricchiti
curl -X POST http://localhost:8080/api/manual/unenriched

# 3. Arricchisci un file specifico per testare
curl -X POST http://localhost:8080/api/manual/enrich \
  -H 'Content-Type: application/json' \
  -d '{"key": "<key-dal-passo-2>"}'

# 4. Verifica il risultato
curl "http://localhost:8080/api/search?q=nome+del+file"

File di stato

Nella cartella STATE_DIR (default ./state):

File Contenuto
catalog.json Lista piatta di tutti i file trovati sul dominio
enriched.json Mappa key → EnrichedFile con i dati AI