
Mengeluarkan Backend daripada Laluan Muat Naik
GeekBye merakam skrin anda dan menyimpan videonya ke Google Drive anda. Versi pertama menghantar setiap rakaman melalui pelayan milik GeekBye sendiri dalam perjalanannya; satu keluaran kemudian, fail itu pergi terus daripada mesin anda ke Drive, dan backend diturunkan taraf kepada hanya memegang satu penunjuk. Bahagian yang menarik ialah betapa sedikit kod yang sebenarnya dikandung versi 'terus, boleh sambung semula' itu β kerana keupayaan-menyambung datang daripada memadam satu proxy, bukan daripada menulis satu.
GeekBye boleh merakam skrin anda β video, audio sistem, dan mik β dan menjatuhkan rakaman yang siap ke Google Drive anda secara automatik. Antara v1.8.11 dan v1.8.13, penerangan ciri yang menghadap-pengguna hampir tidak berubah, tetapi laluan yang diambil video untuk sampai ke Drive dialihkan sepenuhnya. Dalam versi pertama, rakaman anda mengembara melalui pelayan GeekBye. Dalam yang kedua, ia pergi terus daripada mesin anda ke Drive anda, dan backend GeekBye tidak pernah melihat satu bait pun daripadanya. Pengalihan itu ialah keseluruhan cerita, dan bahagian yang memuaskan ialah bahawa versi "lebih baik" mengandungi kod yang ketara lebih sedikit daripada yang ia gantikan.
Talian paip itu, hujung ke hujung
Rakaman berlaku di renderer. ScreenRecordingService.ts mencipta satu MediaRecorder atas strim tangkapan dengan { mimeType: 'video/webm;codecs=vp9,opus', videoBitsPerSecond: 1_000_000 } dan memulakannya dengan satu hirisan masa satu saat (CHUNK_INTERVAL_MS = 1000). Setiap blob ondataavailable dihantar melalui IPC ke proses utama, di mana handler recording:save-chunk menambahnya pada satu fail di cakera dengan fs.promises.appendFile. Berbaloi ditanda awal, kerana ia sesuatu yang menggoda untuk disalah baca: kadensi satu-saat itu ialah irama tulis-ke-cakera, bukan satu saiz cebis muat naik. Menjelang muat naik bermula, ada satu fail WebM siap yang duduk di cakera.
Yang berbeza antara kedua-dua keluaran hanyalah apa yang berlaku pada fail yang siap itu.
Sebelumnya: semua melalui backend (v1.8.11)
Laluan muat naik v1.8.11, dalam CloudUploader.processRecording(), ialah reka bentuk yang jelas dan satu versi pertama yang sepenuhnya munasabah:
POST /api/geekbye/recordingsuntuk mencipta satu rekod backend dengan metadata rakaman.- Baca seluruh video ke memori dan POST ke backend sebagai multipart β
const fileBuffer = fs.readFileSync(filePath)β mengenaPOST /api/geekbye/recordings/${id}/upload. Backend kemudian memuat naik fail itu ke Drive dan memulangkan satudriveUrl. POST β¦/processuntuk menghantar transkrip bagi analisis AI.
Butiran yang menanggung beban ialah langkah 2: klien memuatkan seluruh rakaman ke memori dan setiap baitnya melintasi pelayan milik GeekBye sendiri dalam perjalanan ke Drive. Untuk klip pendek itu tidak mengapa. Untuk rakaman skrin panjang ia tiga masalah sekali gus β tekanan memori pada klien daripada readFileSync, kos lebar jalur dan geganti pada backend untuk satu fail yang ia tidak pun simpan, dan satu POST monolitik yang terpaksa bermula semula dari sifar jika rangkaian tersekat separuh jalan.
Berbaloi juga berlaku jujur bahawa "sebelumnya" ini bukanlah suatu reka bentuk asal yang murni. Ciri v1.8.11 memampatkan bersama satu pusingan dalam-seminggu: satu keratan awal memuat naik ke Cloudflare R2 melalui URL pra-tandatangan dan menukar WebM ke MP4 dengan satu ffmpeg-static yang dibundel; itu digantikan oleh aliran Drive yang diperantara backend; dan kemudian penukaran ffmpeg dicabut sepenuhnya demi memuat naik WebM sebagaimana adanya (commit itu mencatat bahawa ia menurunkan bitrate dan menghasilkan fail lebih kurang empat kali lebih kecil β satu anggaran sebaris, bukan satu benchmark). Jadi walaupun "sebelumnya" sudah pun belajar memadam satu kebergantungan natif sekali. Keluaran seterusnya akan memadam proxy juga.
Sesudahnya: terus ke Drive (v1.8.13)
v1.8.13 menulis semula CloudUploader.processRecording() sekeliling satu ayat daripada commit yang menghantarnya: muat naik terus ke Drive, "keeping backend for metadata record + AI transcript analysis only." Laluan baharu:
POST /api/geekbye/recordingsβ metadata sahaja (tajuk, tempoh, saiz fail,format: 'webm'). Komen dalam kod itu berterus terang:// Create backend record (metadata only β no file upload).- Muat naik video terus ke Google Drive, melangkau orang tengah backend sepenuhnya.
PATCH /api/geekbye/recordings/${backendRecordingId}dengan{ driveUrl, driveFolderId }yang terhasil β rekod backend diberitahu di mana fail berakhir.- Transkrip masih pergi ke
β¦/processuntuk analisis.
Backend berubah daripada berada dalam laluan fail kepada diberitahu tentang fail selepas kejadian. Ia memegang satu baris dan satu penunjuk; bait hidup hanya di cakera pengguna dan dalam Drive pengguna.
Menjadi klien Drive sebenar
Supaya klien bercakap dengan Drive secara terus, ia mesti menjadi satu klien Google Drive API yang sebenar, dan v1.8.13 menambah jentera untuk itu: satu DriveService baharu (klien Drive), satu DriveAuthRepository baharu yang disokong oleh satu jadual SQLite drive_auth baharu, dan SDK googleapis sebagai satu kebergantungan.
Kebenaran itu ialah satu penyerahan keupayaan yang bersih. Endpoint app-config milik backend menyampaikan kelayakan klien Google OAuth β satu clientId dan clientSecret β turun ke aplikasi, yang CloudUploader baca dan hantar ke driveService.initialize(...). Dari situ aplikasi desktop menjalankan seluruh aliran OAuth sendiri: ia mendirikan satu pelayan http sementara pada satu port 127.0.0.1 rawak, membuka skrin persetujuan dalam pelayar sistem dengan shell.openExternal, menangkap ubah hala pada satu /callback, dan menukar kod itu untuk token dengan google-auth-library. Token-token itu disimpan secara setempat β saveDriveAuth(access_token, refresh_token, expiry_date) ke drive_auth β dengan skop drive.file, yang membenarkan aplikasi menguruskan hanya fail yang ia cipta. Penyegaran juga setempat: satu pendengar oauth2Client.on('tokens', β¦) menulis token yang disegarkan terus kembali ke jadual. Backend menyerahkan keupayaan sekali dan kemudian kekal di luar gelung.
Bahagian "boleh sambung semula", secara jujur
Inilah butiran yang paling saya mahu berterus terang tentangnya, kerana ia betul-betul jenis perkara yang dibulatkan oleh satu changelog. Nota keluaran berkata "resumable uploads," dan itu benar dari tempat pengguna berdiri. Tetapi GeekBye tidak melaksanakan protokol boleh sambung semula. Tiada pemalar saiz-cebis, tiada penjejak bait-offset, dan tiada pengendalian Content-Range / 308 Resume Incomplete di mana-mana dalam sumber GeekBye. Grep untuknya dan anda tidak dapat apa-apa β ia hidup di dalam SDK googleapis.
Apa yang kod GeekBye sendiri lakukan ialah ini:
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)
},
},
)
Dua pilihan melakukan semua kerja. Pertama, media.body ialah satu strim (createReadStream), bukan satu penimbal β dan menghantar satu strim betul-betul apa yang membuatkan googleapis merundingkan satu sesi boleh sambung semula bagi pihak anda dan bukan melakukannya sekali tembak. Kedua, kemajuan dibaca daripada callback onUploadProgress milik SDK terhadap fileStats.size. Itulah keseluruhan pelaksanaan "resumable upload" di peringkat aplikasi: strim masuk, kemajuan keluar. Protokol yang sukar β membuka satu sesi, memuat naik dalam cebisan, menyambung semula daripada satu offset selepas satu putus β ialah kerja SDK, dan keputusan kejuruteraan yang betul ialah membiarkannya melakukan kerja itu dan bukan mencipta semulanya.
Yang membingkai semula keseluruhan keluaran. Penambahbaikan kebolehpercayaan tidak datang daripada menulis satu enjin muat naik. Ia datang daripada memadam proxy yang duduk di hadapannya β dan, lebih awal pada minggu yang sama, memadam langkah ffmpeg di hadapan itu. Kurang kod, lebih daya tahan, kerana benda yang tahan itu ialah satu pustaka yang sudah lasak pada saat anda berhenti menyuapnya satu penimbal dengan tangan.
Bahagian yang benar-benar milik GeekBye
Menyerahkan pemindahan kepada SDK tidak bermakna tiada apa yang tinggal untuk dibina. Dua kepingan logik aplikasi sebenar mengelilinginya.
Satu rollback folder-yatim. Setiap rakaman mendapat subfolder Drive-nya sendiri (dicipta dengan mimeType: 'application/vnd.google-apps.folder'), dan transkrip mendarat di sana sebagai fail kedua di sebelah video. Jika muat naik melontar, catch memadam folder itu β this.drive.files.delete({ fileId: folderId }), dilog sebagai Rolled back orphaned Drive folder β supaya satu muat naik yang gagal tidak meninggalkan jejak folder kosong dalam Drive anda. Mencipta satu bekas sebelum anda tahu operasi itu akan berjaya ialah satu liabiliti kecil; membuatkan laluan kegagalan membersihkannya ialah penyelesaiannya.
Kitaran hayat sekeliling satu singleton. DriveService ialah satu singleton yang memegang satu klien OAuth dalam-memori, dan klien dalam-memori itu tidak terus hidup melepasi satu permulaan semula aplikasi walaupun token begitu (ia dalam pangkalan data). Jadi CloudUploader memulakan semula DriveService daripada token yang disimpan sebelum setiap muat naik, yang bermakna satu rakaman yang dibuat sebaik selepas pelancaran semula masih dimuat naik tanpa meminta anda menyambung semula. Dan pusingan semakan menukar handler drive-connect menjadi tembak-dan-lupa, kerana satu panggilan connect yang menyekat sedang menghambat pengundian status renderer β UI tidak dapat menunjukkan "connectingβ¦" jika panggilan connect memegang benang. Dua itu β mula-semula-daripada-storan dan connect tak-menyekat β ialah kes tepi sebenar bagi laluan terus, dan ketara kedua-duanya bukan tentang pencebisan muat naik. Apabila anda mewakilkan bahagian yang sukar, pepijat yang tinggal pada anda ialah pepijat kitaran hayat.
Tiga perkara yang keluaran ini ajarkan kepada kami
- Tanya ke mana bait mengalir, bukan sekadar sama ada ia berfungsi. Proxy v1.8.11 berfungsi. Tetapi menghala setiap rakaman melalui pelayan anda sendiri membebankan anda lebar jalur, memori, dan keupayaan-mula-semula untuk satu fail yang anda tidak simpan. Melukis semula laluan data supaya klien bercakap dengan destinasi secara terus selalunya ialah keseluruhan pengoptimuman.
- Muat naik boleh sambung semula terbaik ialah yang anda tidak tulis. Menghantar satu strim ke satu SDK matang membeli satu sesi boleh sambung semula penuh dengan harga dua baris kod. Naluri untuk membuat sendiri pencebisan dan matematik offset akan menghasilkan lebih banyak kod dan kurang kebolehpercayaan.
- Apabila anda mewakilkan teras, anda mewarisi kitaran hayat, bukan algoritma. Pepijat sebenar klien terus ialah "mula semula singleton selepas permulaan semula" dan "jangan sekat panggilan connect," bukan offset bait. Itu satu pertukaran yang baik β pepijat kitaran hayat kelihatan dan setempat; pepijat protokol bukan kedua-duanya.
Untuk bab sebelumnya dalam kisah v1, lahir dan dibetulkan dalam dua belas minit (v1.8.10); dan untuk keseluruhan lengkungnya, anatomi mengeluarkan perisian hingga ke kesempurnaan.