
Vyjmutí backendu z cesty nahrávání
GeekBye nahrává tvou obrazovku a ukládá video na tvůj Google Drive. První verze posílala každou nahrávku cestou tam přes vlastní servery GeekBye; o vydání později šel soubor přímo z tvého stroje na Drive a backend byl degradován na držení jediného ukazatele. Zajímavé je, jak málo kódu vlastně obsahuje „přímá, resumable" verze — protože resumabilita přišla ze smazání proxy, ne z jeho napsání.
GeekBye umí nahrát tvou obrazovku — video, systémový zvuk a mikrofon — a hotovou nahrávku automaticky pustit na tvůj Google Drive. Mezi v1.8.11 a v1.8.13 se popis funkce viditelný pro uživatele sotva změnil, ale cesta, kterou video urazí, aby se dostalo na Drive, byla přesměrována úplně. V první verzi tvoje nahrávka cestovala přes servery GeekBye. Ve druhé šla přímo z tvého stroje na tvůj Drive a backend GeekBye z ní nikdy neviděl ani byte. To přesměrování je celý příběh a uspokojivá část je, že „lepší" verze obsahuje nápadně méně kódu než ta, kterou nahradila.
Pipeline, od začátku do konce
Nahrávání se děje v rendereru. ScreenRecordingService.ts vytvoří MediaRecorder nad capture streamem s { mimeType: 'video/webm;codecs=vp9,opus', videoBitsPerSecond: 1_000_000 } a spustí ho s jednosekundovým timeslice (CHUNK_INTERVAL_MS = 1000). Každý ondataavailable blob je poslán přes IPC do hlavního procesu, kde ho handler recording:save-chunk připojí k souboru na disku pomocí fs.promises.appendFile. Stojí za to to zdůraznit brzy, protože je to lákavá věc k mylnému výkladu: ta jednosekundová kadence je rytmus zápisu na disk, ne velikost chunku nahrávání. Než nahrávání začne, na disku sedí jediný hotový WebM soubor.
To, co se mezi dvěma vydáními liší, je jen to, co se stane s tím hotovým souborem.
Předtím: všechno přes backend (v1.8.11)
Cesta nahrávání z v1.8.11, v CloudUploader.processRecording(), je zřejmý návrh a naprosto rozumná první verze:
POST /api/geekbye/recordingspro vytvoření backend záznamu s metadaty nahrávky.- Přečti celé video do paměti a POST ho na backend jako multipart —
const fileBuffer = fs.readFileSync(filePath)— udeřením naPOST /api/geekbye/recordings/${id}/upload. Backend pak nahraje ten soubor na Drive a vrátídriveUrl. POST …/processpro odeslání transkriptu k AI analýze.
Nosný detail je krok 2: klient načte celou nahrávku do paměti a každý její byte prochází vlastními servery GeekBye na cestě na Drive. U krátkého klipu je to v pořádku. U dlouhé nahrávky obrazovky jsou to tři problémy najednou — tlak na paměť klienta z readFileSync, náklady na šířku pásma a přeposílání na backendu za soubor, který si ani nenechává, a jeden monolitický POST, který musí restartovat od nuly, pokud síť v půlce škytne.
Stojí za to být také upřímný, že tohle „předtím" nebyl nějaký bezvadný původní návrh. Funkce v1.8.11 zmáčkla dohromady pivot v rámci týdne: raná verze nahrávala na Cloudflare R2 přes pre-signed URL a konvertovala WebM na MP4 se zabaleným ffmpeg-static; to bylo nahrazeno Drive flow zprostředkovaným backendem; a pak byla ffmpeg konverze zcela vytržena ve prospěch nahrávání WebM tak, jak je (commit poznamenává, že snížil bitrate a produkoval zhruba čtyřikrát menší soubory — inline odhad, ne benchmark). Takže i „předtím" se už jednou naučilo smazat nativní závislost. Následující vydání mělo smazat i proxy.
Potom: přímo na Drive (v1.8.13)
v1.8.13 přepisuje CloudUploader.processRecording() kolem jedné věty z commitu, který ho vydal: nahrávej přímo na Drive, „keeping backend for metadata record + AI transcript analysis only." Nová cesta:
POST /api/geekbye/recordings— jen metadata (titul, délka, velikost souboru,format: 'webm'). Komentář v kódu je bez obalu:// Create backend record (metadata only — no file upload).- Nahraj video přímo na Google Drive, zcela přeskočíc backend prostředníka.
PATCH /api/geekbye/recordings/${backendRecordingId}s výsledným{ driveUrl, driveFolderId }— backend záznamu se řekne, kde soubor skončil.- Transkript stále jde na
…/processk analýze.
Backend přešel z toho, že byl v cestě souboru, k tomu, že se mu o souboru řekne dodatečně. Drží řádek a ukazatel; byty žijí jen na disku uživatele a v Drive uživatele.
Stát se skutečným Drive klientem
Aby klient mluvil s Drive přímo, musí být skutečný Google Drive API klient, a v1.8.13 přidává na to mašinerii: nový DriveService (Drive klient), nový DriveAuthRepository podepřený novou SQLite tabulkou drive_auth, a SDK googleapis jako závislost.
Autorizace je čisté předání schopnosti. app-config endpoint backendu předává Google OAuth klientské přihlašovací údaje — clientId a clientSecret — dolů do aplikace, které CloudUploader přečte a předá do driveService.initialize(...). Odtud desktopová aplikace spouští celý OAuth flow sama: postaví dočasný http server na náhodném 127.0.0.1 portu, otevře obrazovku souhlasu v systémovém prohlížeči pomocí shell.openExternal, zachytí redirect na /callback a vymění kód za tokeny pomocí google-auth-library. Ty tokeny se ukládají lokálně — saveDriveAuth(access_token, refresh_token, expiry_date) do drive_auth — se scopem drive.file, který nechá aplikaci spravovat jen soubory, které vytvoří. Obnova je také lokální: listener oauth2Client.on('tokens', …) zapisuje obnovené tokeny rovnou zpátky do tabulky. Backend předá schopnost jednou a pak zůstane mimo smyčku.
Ta „resumable" část, upřímně
Zde je detail, ohledně kterého chci být nejvíc přímý, protože je to přesně ten druh věci, který changelog zaokrouhluje. Poznámka k vydání říká „resumable uploads" a to je pravda z místa, kde stojí uživatel. Ale GeekBye neimplementoval resumable protokol. Není tu žádná konstanta velikosti chunku, žádný tracker byte-offsetu a žádná obsluha Content-Range / 308 Resume Incomplete nikde ve zdrojovém kódu GeekBye. Udělej na ně grep a nedostaneš nic — žijí uvnitř SDK googleapis.
Co dělá vlastní kód GeekBye, je tohle:
const fileStats = statSync(videoFilePath)
const videoStream = createReadStream(videoFilePath)
const videoRes = await this.drive.files.create(
{
requestBody: { name: `${title}.webm`, parents: [folderId] },
media: { mimeType: 'video/webm', body: videoStream },
fields: 'id',
},
{
onUploadProgress: (evt) => {
const percent = Math.round((evt.bytesRead / fileStats.size) * 100)
onProgress(percent)
},
},
)
Dvě volby dělají všechnu práci. Za prvé, media.body je stream (createReadStream), ne buffer — a předání streamu je přesně to, co přiměje googleapis, aby tvým jménem vyjednal resumable session místo toho, aby to udělal na jeden zátah. Za druhé, průběh se čte z callbacku onUploadProgress SDK proti fileStats.size. To je celá implementace „resumable upload" na úrovni aplikace: stream dovnitř, průběh ven. Těžký protokol — otevři session, nahraj v chuncích, pokračuj od offsetu po výpadku — je práce SDK, a správné inženýrské rozhodnutí bylo nechat ho tu práci dělat, místo abychom ji znovu vynalézali.
Což přerámuje celé vydání. Zlepšení spolehlivosti nepřišlo z napsání nahrávacího enginu. Přišlo ze smazání proxy, která seděla před jedním — a, dříve ve stejném týdnu, ze smazání ffmpeg kroku před tím. Méně kódu, více odolnosti, protože ta odolná věc byla dobře prošlapaná knihovna ve chvíli, kdy jsi ji přestal ručně krmit bufferem.
Kousky, které jsou opravdu GeekBye
Předání přenosu SDK neznamená, že nezbylo nic k postavení. Obklopují ho dva kusy skutečné aplikační logiky.
Rollback osiřelé složky. Každá nahrávka dostane svou vlastní Drive podsložku (vytvořenou s mimeType: 'application/vnd.google-apps.folder'), a transkript tam přistane jako druhý soubor vedle videa. Pokud nahrávání vyhodí výjimku, catch smaže tu složku — this.drive.files.delete({ fileId: folderId }), logováno jako Rolled back orphaned Drive folder — takže neúspěšné nahrání nenechá stopu prázdných složek ve tvém Drive. Vytvoření kontejneru předtím, než víš, že operace uspěje, je malý závazek; udělat cestu selhání tak, aby ho uklidila, je oprava.
Životní cyklus kolem singletonu. DriveService je singleton držící OAuth klienta v paměti, a ten klient v paměti nepřežije restart aplikace, i když tokeny přežijí (jsou v databázi). Takže CloudUploader reinicializuje DriveService z uložených tokenů před každým nahráním, což znamená, že nahrávka udělaná hned po opětovném spuštění se stále nahraje, aniž by tě žádala o opětovné připojení. A review průchod proměnil handler drive-connect na fire-and-forget, protože blokující connect volání zdržovalo status polling rendereru — UI nemohlo ukázat „connecting…", pokud connect volání drželo vlákno. Tyto dvě — reinicializace-z-úložiště a neblokující connect — jsou skutečné hraniční případy přímé cesty a, pozoruhodně, ani jeden z nich není o chunkingu nahrávání. Když deleguješ tu těžkou část, bugy, se kterými zůstaneš, jsou bugy životního cyklu.
Tři věci, které nás toto vydání naučilo
- Ptej se, kudy tečou byty, ne jen jestli to funguje. Proxy z v1.8.11 fungovala. Ale rutování každé nahrávky přes vlastní servery tě stojí šířku pásma, paměť a schopnost restartu za soubor, který si nenecháváš. Překreslení datové cesty tak, aby klient mluvil přímo s cílem, je často celá optimalizace.
- Nejlepší resumable nahrání je to, které jsi nenapsal. Předání streamu zralému SDK koupilo plnou resumable session za dva řádky kódu. Instinkt ručně napsat chunking a matematiku offsetu by produkoval více kódu a méně spolehlivosti.
- Když deleguješ jádro, dědíš životní cyklus, ne algoritmy. Skutečné bugy přímého klienta byly „reinicializuj singleton po restartu" a „neblokuj connect volání", ne byte offsety. To je dobrý obchod — bugy životního cyklu jsou viditelné a lokální; bugy protokolu nejsou ani jedno.
Předchozí kapitolu příběhu v1 najdete v zrozen a opraven za dvanáct minut (v1.8.10); a celý oblouk v anatomii dodávání softwaru k dokonalosti.