Skip to content

Commit 03af6ed

Browse files
committed
WIP: move automerge to loro
1 parent 59f2289 commit 03af6ed

27 files changed

Lines changed: 1034 additions & 208 deletions

backend/transcribee_backend/helpers/sync.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
from asyncio import Queue
3-
from typing import Callable
3+
from typing import Callable, Tuple
4+
import uuid
45

56
from fastapi import WebSocket, WebSocketDisconnect
67
from sqlmodel import Session, select
@@ -45,14 +46,17 @@ def __init__(
4546
self._can_write = can_write
4647
self._subscribed = set()
4748
self._msg_queue = Queue()
49+
self._id = uuid.uuid4()
4850

4951
def subscribe(self, channel: str):
5052
self._subscribed.add(channel)
5153
sync_manager.subscribe(channel, self.handle_incoming_broadcast)
5254

53-
async def handle_incoming_broadcast(self, channel: str, message: bytes):
55+
async def handle_incoming_broadcast(self, channel: str, message: Tuple[uuid.UUID, bytes]):
5456
if channel in self._subscribed:
55-
await self._msg_queue.put(message)
57+
id, msg = message
58+
if (id != self._id):
59+
await self._msg_queue.put(msg)
5660

5761
async def listener(self):
5862
while True:
@@ -75,15 +79,18 @@ async def broadcast_sender(self):
7579
# https://asgi.readthedocs.io/en/latest/specs/www.html#send-send-event
7680
message = [bytes([SyncMessageType.FULL_DOCUMENT])]
7781
for update in self._session.exec(statement):
78-
message.append(update.change_bytes)
82+
message.append(len(update.change_bytes).to_bytes(4) + update.change_bytes + bytes([SyncMessageType.CHANGE]))
83+
# message.append(update.change_bytes)
84+
print("hello", len(update.change_bytes))
85+
message[-1] = message[-1][:-1]
7986
await self._ws.send_bytes(message) # type: ignore
8087
# END
8188

8289
await self._ws.send_bytes(bytes([SyncMessageType.CHANGE_BACKLOG_COMPLETE]))
8390

8491
while True:
8592
msg = await self._msg_queue.get()
86-
await self._ws.send_bytes(bytes([SyncMessageType.CHANGE]) + msg)
93+
await self._ws.send_bytes(bytes([SyncMessageType.CHANGE]) + len(msg).to_bytes(4) + msg)
8794

8895
async def run(self):
8996
await self._ws.accept()
@@ -126,4 +133,4 @@ async def on_message(self, message: bytes):
126133
self._session.add(self._doc)
127134

128135
self._session.commit()
129-
await sync_manager.broadcast(str(self._doc.id), message)
136+
await sync_manager.broadcast(str(self._doc.id), (self._id, message))

frontend/package-lock.json

Lines changed: 18 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.1.0",
44
"scripts": {
55
"dev": "vite",
6+
"profile": "vite --profile",
67
"build": "npm-run-all --continue-on-error build:*",
78
"build:licenses": "node scripts/generate_licenses.mjs public/LICENSES.md",
89
"build:vite": "vite build",
@@ -57,13 +58,13 @@
5758
},
5859
"dependencies": {
5960
"@audapolis/webvtt-writer": "^1.0.6",
60-
"@automerge/automerge": "~2.2.0",
61-
"@automerge/automerge-wasm": "^0.15.0",
6261
"@fontsource/inter": "^4.5.15",
6362
"@podlove/html5-audio-driver": "^2.0.3",
6463
"@popperjs/core": "^2.11.8",
6564
"@zip.js/zip.js": "^2.7.31",
6665
"clsx": "^1.2.1",
66+
"fast-equals": "^5.2.2",
67+
"loro-crdt": "^1.4.4",
6768
"openapi-typescript-fetch": "github:bugbakery/openapi-typescript-fetch#4b5cc33983c5a658feedd628d417599db2bd672c",
6869
"react": "^18.2.0",
6970
"react-base16-styling": "^0.9.1",
@@ -77,7 +78,6 @@
7778
"react-popper": "^2.3.0",
7879
"reconnecting-websocket": "^4.4.0",
7980
"slate": "^0.94.1",
80-
"slate-automerge-doc": "github:bugbakery/slate-automerge-doc#98d7a78f14cb4a16484847e0090f6b5439cc16b6",
8181
"slate-history": "^0.93.0",
8282
"slate-react": "^0.94.2",
8383
"swr": "^2.2.4",

frontend/src/document.ts

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,58 @@
1-
import { Document } from './editor/types';
2-
import { next as Automerge } from '@automerge/automerge';
3-
import * as AutomergeStable from '@automerge/automerge';
1+
import { Document, EditorDocument } from './editor/types';
2+
// import { next as Automerge } from '@automerge/automerge';
3+
// import * as AutomergeStable from '@automerge/automerge';
44

5-
function convertString(s: string): string {
6-
// this typecasting is a hack to avoid having multiple types for different versions for now
7-
return new AutomergeStable.Text(s.toString()) as unknown as string;
5+
export function documentToJSON(doc: EditorDocument): Document {
6+
return doc.toJSON().root;
87
}
98

10-
export function migrateDocument(doc: Automerge.Doc<Document>): Automerge.Doc<Document> {
11-
let theDoc = doc;
12-
const v1 = theDoc.version === 1;
13-
const actorID = Automerge.getActorId(doc);
14-
if (v1) {
15-
theDoc = AutomergeStable.load(Automerge.save(doc), actorID);
16-
}
17-
if (theDoc.version === 2) {
18-
return doc;
19-
}
20-
const migratedDoc = AutomergeStable.change(theDoc, (doc: Document) => {
21-
switch (doc.version) {
22-
case 1:
23-
for (const speakerID of Object.keys(doc.speaker_names)) {
24-
doc.speaker_names[speakerID] = convertString(doc.speaker_names[speakerID]);
25-
}
9+
// function convertString(s: string): string {
10+
// // this typecasting is a hack to avoid having multiple types for different versions for now
11+
// return new AutomergeStable.Text(s.toString()) as unknown as string;
12+
// }
2613

27-
doc.children.forEach((paragraph) => {
28-
paragraph.type = convertString(paragraph.type) as 'paragraph';
29-
paragraph.speaker = paragraph.speaker ? convertString(paragraph.speaker) : null;
30-
paragraph.lang = convertString(paragraph.lang.toString());
14+
// export function migrateDocument(doc: Automerge.Doc<Document>): Automerge.Doc<Document> {
15+
// let theDoc = doc;
16+
// const v1 = theDoc.version === 1;
17+
// const actorID = Automerge.getActorId(doc);
18+
// if (v1) {
19+
// theDoc = AutomergeStable.load(Automerge.save(doc), actorID);
20+
// }
21+
// if (theDoc.version === 2) {
22+
// return doc;
23+
// }
24+
// const migratedDoc = AutomergeStable.change(theDoc, (doc: Document) => {
25+
// switch (doc.version) {
26+
// case 1:
27+
// for (const speakerID of Object.keys(doc.speaker_names)) {
28+
// doc.speaker_names[speakerID] = convertString(doc.speaker_names[speakerID]);
29+
// }
3130

32-
paragraph.children.forEach((child) => {
33-
const start = child.start;
34-
child.start = start;
35-
const end = child.end;
36-
child.end = end;
37-
const conf = child.conf;
38-
child.conf = conf;
39-
child.text = convertString(child.text.toString());
40-
});
41-
});
42-
doc.version = 2;
43-
// falls through
44-
case 2:
45-
break;
46-
}
47-
});
31+
// doc.children.forEach((paragraph) => {
32+
// paragraph.type = convertString(paragraph.type) as 'paragraph';
33+
// paragraph.speaker = paragraph.speaker ? convertString(paragraph.speaker) : null;
34+
// paragraph.lang = convertString(paragraph.lang.toString());
4835

49-
if (v1) {
50-
return Automerge.load(AutomergeStable.save(migratedDoc), actorID);
51-
} else {
52-
return migratedDoc;
53-
}
54-
}
36+
// paragraph.children.forEach((child) => {
37+
// const start = child.start;
38+
// child.start = start;
39+
// const end = child.end;
40+
// child.end = end;
41+
// const conf = child.conf;
42+
// child.conf = conf;
43+
// child.text = convertString(child.text.toString());
44+
// });
45+
// });
46+
// doc.version = 2;
47+
// // falls through
48+
// case 2:
49+
// break;
50+
// }
51+
// });
52+
53+
// if (v1) {
54+
// return Automerge.load(AutomergeStable.save(migratedDoc), actorID);
55+
// } else {
56+
// return migratedDoc;
57+
// }
58+
// }

0 commit comments

Comments
 (0)