refactor: flatten project structure, remove ti2026h-web subdirectory

This commit is contained in:
2026-08-01 16:08:44 +08:00
parent 6bbe83e1f1
commit 386d8d5852
26 changed files with 18 additions and 1 deletions
+111
View File
@@ -0,0 +1,111 @@
/**
* IndexedDB wrapper for storing MJPEG recordings.
* Each recording holds an array of frames (JPEG blobs) with timestamps.
*/
export interface Frame {
/** Milliseconds since recording start */
timestamp: number
blob: Blob
}
export interface Recording {
id: string
name: string
createdAt: Date
duration: number // ms
frameCount: number
frames: Frame[]
}
const DB_NAME = 'mjpeg-viewer'
const DB_VERSION = 1
const STORE_NAME = 'recordings'
function openDB(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION)
request.onupgradeneeded = () => {
const db = request.result
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' })
}
}
request.onsuccess = () => resolve(request.result)
request.onerror = () => reject(request.error)
})
}
export async function saveRecording(recording: Recording): Promise<void> {
const db = await openDB()
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite')
const store = tx.objectStore(STORE_NAME)
// Serialize: convert Date to ISO string and store frames
const record = {
...recording,
createdAt: recording.createdAt.toISOString(),
}
store.put(record)
tx.oncomplete = () => {
db.close()
resolve()
}
tx.onerror = () => reject(tx.error)
})
}
export async function getAllRecordings(): Promise<Recording[]> {
const db = await openDB()
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly')
const store = tx.objectStore(STORE_NAME)
const request = store.getAll()
request.onsuccess = () => {
db.close()
const results = (request.result || []).map((r: any) => ({
...r,
createdAt: new Date(r.createdAt),
}))
// Sort newest first
results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
resolve(results)
}
request.onerror = () => reject(request.error)
})
}
export async function deleteRecording(id: string): Promise<void> {
const db = await openDB()
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite')
const store = tx.objectStore(STORE_NAME)
store.delete(id)
tx.oncomplete = () => {
db.close()
resolve()
}
tx.onerror = () => reject(tx.error)
})
}
export async function getRecording(id: string): Promise<Recording | undefined> {
const db = await openDB()
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly')
const store = tx.objectStore(STORE_NAME)
const request = store.get(id)
request.onsuccess = () => {
db.close()
if (!request.result) {
resolve(undefined)
return
}
resolve({
...request.result,
createdAt: new Date(request.result.createdAt),
})
}
request.onerror = () => reject(request.error)
})
}