Architektura
Eutepio stojí na třech rozhodnutích: lokální databáze jako jediný zdroj pravdy, synchronizace pouze v lokální síti a striktní oddělení sdílených a osobních dat.
Offline-first
Každé zařízení má vlastní úplnou kopii knihovny. Neexistuje server, který by data vlastnil, ani účet, pod kterým by byla vedená. Synchronizace je způsob, jak během zkoušky nebo koncertu držet kapelu na stejné stránce — ne záloha a ne cloudové úložiště.
Důsledky:
- Aplikace funguje bez internetu
- Kvalita připojení nemá vliv na chování na pódiu
- Zálohu si musíš udělat sám — slouží k tomu formát
.eutbackup - Když přijdeš o telefon bez zálohy, přijdeš o knihovnu
Úložiště
Databází je Isar (komunitní fork verze 3), vestavěná databáze běžící přímo v procesu aplikace.
Od verze 0.2.0 nejde o jednu databázi, ale o jednu databázi na knihovnu plus samostatný registr:
| Kde | Co v tom je |
|---|---|
| Registr | Seznam knihoven a tvůj uživatelský profil — jméno, nástroj, vzhled pódia, mapování pedálu a MIDI |
| Knihovna | Písně, setlisty, anotace a stav té konkrétní knihovny |
Profil je proto globální: když přepneš knihovnu, pódium ani ovládání se ti nepřenastaví. Podrobnosti v Víc knihoven.
| Platforma | Umístění databáze |
|---|---|
| Android, iOS | adresář dokumentů aplikace |
| Windows, Linux, macOS | adresář podpory aplikace |
Binární přílohy v databázi neleží. PDF, MusicXML a obrázky se ukládají jako soubory do podsložek
pdfs/, musicxml/ a images/; v databázi je uložená jen relativní cesta. Každá knihovna má vlastní kořen pro přílohy.Odolnost
- Pokud se databázi nepodaří otevřít, poškozený soubor se odloží stranou pod jménem s příponou
.corrupt-<časové razítko>a aplikace nastartuje s čerstvou databází místo toho, aby spadla. - Databáze si pamatuje verzi schématu, se kterou do ní bylo naposledy zapsáno. Když otevřeš starší build aplikace nad novější databází, aplikace se zamkne na varovné obrazovce místo toho, aby data poškodila.
- Aplikace rozlišuje zamčenou databázi (jiný proces ji drží — zkusí to znovu) od poškozené (odloží ji stranou). Zamčený soubor se tedy neodkládá zbytečně.
Datový model
Píseň
Sdílená část, kterou má smysl znát celá kapela:
| Pole | Typ | Poznámka |
|---|---|---|
title, artist | text | povinné |
variantName | text | volitelná varianta, součást identity písně |
key | text | tónina |
bpm | číslo | tempo |
timeSignature | text | taktové označení |
capo | číslo | výchozí 0 |
tags | seznam textů | indexované pro vyhledávání |
chordProContent | text | vlastní obsah písně v ChordPro |
durationSeconds | číslo | délka |
version | číslo | zvyšuje se při úpravě sdílených polí |
createdAt, updatedAt, deletedAt | čas | mazání je měkké — záznam zůstane a jen se označí |
K tomu tři identifikátory používané při synchronizaci a zálohách:
syncId (unikátní), matchKey (odvozený z názvu, interpreta a varianty) a contentHash (SHA-256 sdílených polí).Setlist
name, eventDate, venue, startTime, estimatedDurationMin, songIds (seřazený seznam), breaks, songNotes, customFieldsJson, version, createdAt, deletedAt a lokální sortOrder.⚠
Setlist nemá
syncId — mezi zařízeními se páruje jen podle názvu a data akce. Když stejný setlist na jednom zařízení přejmenuješ, na druhém z něj po synchronizaci vznikne duplicita.Sdílená a lokální pole
Píseň má vedle sdílených polí i řadu per-device hodnot, které se nikdy nesynchronizují a nikdy je nepřepíše nikdo jiný:
privateNotes— tvoje osobní poznámky k písnilocalTranspose,localTransposeXml— transpozice akordů a MusicXMLfontSizeScale— velikost textu- rychlosti autoscrollu zvlášť pro každý z pěti režimů zobrazení
defaultViewMode,pdfStartPage,pdfFitWidth,musicXmlZoom- cesty k přílohám (
pdfPath,musicXmlPath,imagePaths) a odložené alternativní verze midiJson— MIDI příkaz odeslaný při otevření písně
Anotace jsou lokální úplně — do synchronizace nevstupují vůbec, byť je najdeš v záloze.
ℹ
Tohle rozdělení je důvod, proč ti kytarista nemůže přepsat transpozici ani poznámky. Zpěvák může mít stejnou píseň o tercii výš a nikoho to neruší.
Vrstvy synchronizace
1. Objevování
Leader se ohlásí v lokální síti přes mDNS/Bonjour pod službou
_eutepio._tcp. Souběžně běží záložní sken lokálního rozsahu adres, aby relace šla najít i v sítích, kde multicast nefunguje. Připojit se dá i naskenováním QR kódu nebo ručním zadáním IP adresy.2. Přenos
Leader spustí HTTP server na portu 8765. Pokud je port obsazený, zkusí postupně 8766 až 8769. Server nabízí čtyři endpointy — ohlášení relace, připojení, WebSocket upgrade a health check. Vlastní komunikace pak jede po WebSocketu.
3. Aplikační protokol
Zprávy jsou JSON objekty s klíčem
command. Leader je vysílá, členové je vykonávají; člen, který chce něco poslat ostatním, pošle zprávu leaderovi a ten ji přerazítkuje a rozešle dál. Každá zpráva nese razítko leaderových hodin a čas executeAt (standardně o 300 ms v budoucnosti), aby všechna zařízení akci provedla ve stejný okamžik.Kompletní seznam příkazů je v Leader/member modelu.
Co opouští zařízení
Aby bylo jasno, protože „data nikdy neopustí zařízení" by nebyla pravda:
| Situace | Co se posílá | Kam |
|---|---|---|
| Synchronizace | Písně, setlisty a přílohy, které si členové vyžádají | Ostatním zařízením ve stejné lokální síti |
| Automatická záloha | Celá záloha knihovny | Do složky, kterou sám vybereš — může být i složka synchronizovaná do cloudu |
| Import z URL | Jen HTTP GET požadavek na adresu, kterou sám zadáš | Na server, který jsi zadal |
| První export setlistu do PDF | Stažení písma Noto Sans | Google Fonts |
Co se neposílá nikdy a nikam: žádná analytika, žádné crash reporty, žádná telemetrie, žádný účet. V aplikaci není Firebase, Supabase, Sentry ani jiná podobná služba.