
Å ta backend ut av opplastingsstien
GeekBye tar opp skjermen din og lagrer videoen til din Google Drive. Den første versjonen sendte hvert opptak gjennom GeekByes egne servere på veien dit; én release senere gikk filen rett fra maskinen din til Drive, og backend ble degradert til å holde én enkelt peker. Den interessante delen er hvor lite kode den 'direkte, gjenopptakbare' versjonen faktisk inneholder — fordi gjenopptakbarheten kom fra å slette en proxy, ikke fra å skrive en.
GeekBye kan ta opp skjermen din — video, systemlyd og mikrofon — og slippe det ferdige opptaket i din Google Drive automatisk. Mellom v1.8.11 og v1.8.13 endret funksjonens brukervendte beskrivelse seg knapt, men veien videoen tar for å nå Drive ble rutet fullstendig om. I den første versjonen reiste opptaket ditt gjennom GeekByes servere. I den andre gikk det rett fra maskinen din til din Drive, og GeekByes backend så aldri en byte av det. Den omrutingen er hele historien, og det tilfredsstillende er at den "bedre" versjonen inneholder påfallende mindre kode enn den den erstattet.
Pipelinen, fra ende til ende
Opptak skjer i rendereren. ScreenRecordingService.ts oppretter en MediaRecorder over opptaksstrømmen med { mimeType: 'video/webm;codecs=vp9,opus', videoBitsPerSecond: 1_000_000 } og starter den med en ett-sekunds tidsskive (CHUNK_INTERVAL_MS = 1000). Hver ondataavailable-blob sendes over IPC til hovedprosessen, der recording:save-chunk-handleren føyer den til en fil på disk med fs.promises.appendFile. Verdt å flagge tidlig, fordi det er fristende å felllese: den ett-sekunds kadensen er disk-skrivings-rytmen, ikke en opplastings-stumpstørrelse. Når opplastingen begynner, ligger det én enkelt ferdig WebM-fil på disken.
Det som skiller de to releasene er bare hva som skjer med den ferdige filen.
Før: alt gjennom backend (v1.8.11)
v1.8.11-opplastingsveien, i CloudUploader.processRecording(), er den åpenbare designen og en fullstendig fornuftig første versjon:
POST /api/geekbye/recordingsfor å opprette en backend-post med opptakets metadata.- Les hele videoen inn i minnet og POST den til backend som multipart —
const fileBuffer = fs.readFileSync(filePath)— og treffPOST /api/geekbye/recordings/${id}/upload. Backend laster så opp den filen til Drive og returnerer endriveUrl. POST …/processfor å sende transkriptet til AI-analyse.
Den bærende detaljen er steg 2: klienten laster hele opptaket inn i minnet, og hver byte av det transiterer GeekByes egne servere på veien til Drive. For et kort klipp er det greit. For et langt skjermopptak er det tre problemer på én gang — minnetrykk på klienten fra readFileSync, båndbredde- og relékostnad på backend for en fil den ikke engang beholder, og én monolittisk POST som må starte på nytt fra null hvis nettverket hakker halvveis.
Det er også verdt å være ærlig om at dette "før" ikke var noe uberørt opprinnelig design. v1.8.11-funksjonen klemte sammen et pivot innen samme uke: en tidlig utgave lastet opp til Cloudflare R2 via forhåndssignerte URL-er og konverterte WebM til MP4 med en medfølgende ffmpeg-static; det ble erstattet av den backend-medierte Drive-flyten; og så ble ffmpeg-konverteringen revet helt ut til fordel for å laste opp WebM som den er (commiten bemerker at den senket bitraten og produserte omtrent fire ganger mindre filer — et innebygd anslag, ikke en benchmark). Så selv "før" hadde allerede lært å slette en nativ avhengighet én gang. Den neste releasen ville slette proxyen også.
Etter: rett til Drive (v1.8.13)
v1.8.13 skriver om CloudUploader.processRecording() rundt én setning fra commiten som shippet det: last opp direkte til Drive, "keeping backend for metadata record + AI transcript analysis only." Den nye veien:
POST /api/geekbye/recordings— bare metadata (tittel, varighet, filstørrelse,format: 'webm'). Kommentaren i koden er kontant:// Create backend record (metadata only — no file upload).- Last opp videoen direkte til Google Drive, og hopp over backend-mellommannen helt.
PATCH /api/geekbye/recordings/${backendRecordingId}med den resulterende{ driveUrl, driveFolderId }— backend-posten får vite hvor filen endte.- Transkriptet går fortsatt til
…/processfor analyse.
Backend gikk fra å være i filens vei til å bli fortalt om filen i etterkant. Den holder en rad og en peker; bytene bor bare på brukerens disk og i brukerens Drive.
Å bli en ekte Drive-klient
For at klienten skal snakke direkte med Drive, må den være en faktisk Google Drive API-klient, og v1.8.13 legger til maskineriet for det: en ny DriveService (Drive-klienten), en ny DriveAuthRepository støttet av en ny drive_auth SQLite-tabell, og googleapis-SDK-en som en avhengighet.
Autorisasjonen er en ren overlevering av kapabilitet. Backendens app-config-endepunkt reléer Google OAuth klientlegitimasjoner — en clientId og en clientSecret — ned til appen, som CloudUploader leser og sender til driveService.initialize(...). Derfra kjører skrivebordsappen hele OAuth-flyten selv: den reiser en midlertidig http-server på en tilfeldig 127.0.0.1-port, åpner samtykkeskjermen i systemnettleseren med shell.openExternal, fanger omdirigeringen på et /callback, og bytter koden mot tokens med google-auth-library. De tokenene lagres lokalt — saveDriveAuth(access_token, refresh_token, expiry_date) inn i drive_auth — med drive.file-scopet, som lar appen håndtere bare de filene den oppretter. Fornyelse er også lokal: en oauth2Client.on('tokens', …)-lytter skriver fornyede tokens rett tilbake til tabellen. Backend overleverer kapabiliteten én gang og holder seg så utenfor loopen.
Den "gjenopptakbare" delen, ærlig
Her er detaljen jeg mest vil være rett fram om, fordi det er nettopp den slags ting en endringslogg runder av. Release-notatet sier "resumable uploads", og det er sant der brukeren står. Men GeekBye implementerte ikke den gjenopptakbare protokollen. Det finnes ingen stumpstørrelse-konstant, ingen byte-offset-sporer og ingen Content-Range / 308 Resume Incomplete-håndtering noe sted i GeekByes kildekode. Grep etter dem, og du får ingenting — de bor inne i googleapis-SDK-en.
Det GeekByes egen kode gjør er dette:
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)
},
},
)
To valg gjør hele jobben. Først er media.body en strøm (createReadStream), ikke en buffer — og å sende en strøm er nettopp det som får googleapis til å forhandle en gjenopptakbar økt på dine vegne i stedet for å gjøre det i ett skudd. For det andre leses fremdriften av fra SDK-ens onUploadProgress-callback mot fileStats.size. Det er hele "gjenopptakbar opplasting"-implementeringen på applikasjonsnivå: strøm inn, fremdrift ut. Den vanskelige protokollen — åpne en økt, last opp i stumper, gjenoppta fra en offset etter et frafall — er SDK-ens jobb, og den riktige ingeniørbeslutningen var å la den gjøre den jobben i stedet for å finne den opp på nytt.
Noe som rammer inn hele releasen på nytt. Pålitelighetsforbedringen kom ikke fra å skrive en opplastingsmotor. Den kom fra å slette proxyen som satt foran en — og, tidligere samme uke, å slette ffmpeg-steget foran den. Mindre kode, mer robusthet, fordi den robuste tingen var et velbrukt bibliotek i det øyeblikket du sluttet å håndmate det en buffer.
Stumpene som ekte er GeekByes
Å overlevere overføringen til SDK-en betyr ikke at det ikke var noe igjen å bygge. To stykker ekte applikasjonslogikk omgir den.
En foreldreløs-mappe-tilbakerulling. Hvert opptak får sin egen Drive-undermappe (opprettet med mimeType: 'application/vnd.google-apps.folder'), og transkriptet lander der som en andre fil ved siden av videoen. Hvis opplastingen kaster, sletter catch den mappen — this.drive.files.delete({ fileId: folderId }), logget som Rolled back orphaned Drive folder — slik at en mislykket opplasting ikke etterlater et spor av tomme mapper i din Drive. Å opprette en beholder før du vet at operasjonen vil lykkes, er et lite ansvar; å få feilveien til å rydde opp etter den er fiksen.
Livssyklus rundt en singleton. DriveService er en singleton som holder en in-memory OAuth-klient, og den in-memory klienten overlever ikke en app-omstart selv om tokenene gjør det (de er i databasen). Så CloudUploader reinitialiserer DriveService fra de lagrede tokenene før hver opplasting, som betyr at et opptak gjort rett etter omstart fortsatt lastes opp uten å be deg om å koble til på nytt. Og gjennomgangsrunden gjorde drive-connect-handleren til fire-and-forget, fordi et blokkerende connect-kall stanset rendererens statuspolling — brukergrensesnittet kunne ikke vise "kobler til…" hvis connect-kallet holdt tråden. De to — reinitialisér-fra-lager og ikke-blokkerende connect — er den direkte veiens virkelige kanttilfeller, og bemerkelsesverdig handler ingen av dem om opplastings-stumping. Når du delegerer den vanskelige delen, er buggene du sitter igjen med livssyklusbugger.
Tre ting denne releasen lærte oss
- Spør hvor bytene flyter, ikke bare om det virker. v1.8.11-proxyen virket. Men å rute hvert opptak gjennom dine egne servere koster deg båndbredde, minne og gjenopptakbarhet for en fil du ikke beholder. Å tegne dataveien på nytt slik at klienten snakker direkte med destinasjonen er ofte hele optimaliseringen.
- Den beste gjenopptakbare opplastingen er den du ikke skrev. Å sende en strøm til et modent SDK kjøpte en full gjenopptakbar økt for to linjer kode. Instinktet til å håndrulle stumping og offset-matte ville ha produsert mer kode og mindre pålitelighet.
- Når du delegerer kjernen, arver du livssyklus, ikke algoritmer. Den direkte klientens virkelige bugger var "reinitialisér singletonen etter omstart" og "ikke blokkér connect-kallet", ikke byte-offsets. Det er en god handel — livssyklusbugger er synlige og lokale; protokollbugger er ingen av delene.
For det forrige kapittelet i v1-historien, født og fikset på tolv minutter (v1.8.10); og for hele buen, anatomien i å shippe programvare til perfeksjon.