refactor: flatten project structure, remove ti2026h-web subdirectory
This commit is contained in:
@@ -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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user