| … | … | @@ -90,6 +90,15 @@ export async function notebookApply (batch, view, base) { |
| 90 | 90 | author: op.author, |
| 91 | 91 | addedAt: op.timestamp |
| 92 | 92 | }) |
| 93 | // Also index by filename for blob lookup | |
| 94 | if (op.filename) { | |
| 95 | await b.put(`media-file:${op.filename}`, { | |
| 96 | hash: op.hash, | |
| 97 | noteId: op.noteId || null, | |
| 98 | blobId: op.blobId || null, | |
| 99 | mimeType: op.mimeType | |
| 100 | }) | |
| 101 | } | |
| 93 | 102 | break |
| 94 | 103 | } |
| 95 | 104 |
| … | … | @@ -244,17 +244,26 @@ export class SharedNotebook { |
| 244 | 244 | } |
| 245 | 245 | |
| 246 | 246 | async storeBlob (buffer) { |
| 247 | return this._blobs.put(buffer) | |
| 247 | const id = await this._blobs.put(buffer) | |
| 248 | // Include the blob core key so other instances can find this data | |
| 249 | id.coreKey = this._blobCore.key.toString('hex') | |
| 250 | return id | |
| 248 | 251 | } |
| 249 | 252 | |
| 250 | 253 | async getBlob (id) { |
| 254 | // If blob is on a remote core, open a Hyperblobs session for it | |
| 255 | if (id.coreKey) { | |
| 256 | const keyBuf = typeof id.coreKey === 'string' ? Buffer.from(id.coreKey, 'hex') : id.coreKey | |
| 257 | if (!this._blobCore.key.equals(keyBuf)) { | |
| 258 | const remoteCore = this._store.get(keyBuf) | |
| 259 | await remoteCore.ready() | |
| 260 | const remoteBlobs = new Hyperblobs(remoteCore) | |
| 261 | return remoteBlobs.get(id) | |
| 262 | } | |
| 263 | } | |
| 251 | 264 | return this._blobs.get(id) |
| 252 | 265 | } |
| 253 | 266 | |
| 254 | get blobCoreKey () { | |
| 255 | return this._blobCore.key | |
| 256 | } | |
| 257 | ||
| 258 | 267 | async removeMedia (noteId, hash, author) { |
| 259 | 268 | await this._base.append(encodeOp({ |
| 260 | 269 | type: 'remove-media', |
| … | … | @@ -329,11 +329,14 @@ export function registerNotebookRoutes (router, server) { |
| 329 | 329 | if (!buf) { |
| 330 | 330 | const snb = server.noteManager._getSharedNotebook(req.params.notebook) |
| 331 | 331 | if (snb) { |
| 332 | const media = await snb.getMedia(null, req.params.filename) | |
| 333 | if (media && media.blobId) { | |
| 334 | try { | |
| 335 | buf = await snb.getBlob(media.blobId) | |
| 336 | } catch {} | |
| 332 | try { | |
| 333 | await snb.update() | |
| 334 | const entry = await snb.view.get('media-file:' + req.params.filename) | |
| 335 | if (entry && entry.value && entry.value.blobId) { | |
| 336 | buf = await snb.getBlob(entry.value.blobId) | |
| 337 | } | |
| 338 | } catch (err) { | |
| 339 | console.error('[Media Serve] Blob fallback error:', err.message) | |
| 337 | 340 | } |
| 338 | 341 | } |
| 339 | 342 | } |