clip-tagger / userFeedbackStore.js
sohei1l's picture
Initial model release
361ee5e
class UserFeedbackStore {
constructor() {
this.dbName = 'ClipTaggerDB';
this.version = 1;
this.db = null;
}
async initialize() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Create object stores
if (!db.objectStoreNames.contains('audioFeedback')) {
const audioStore = db.createObjectStore('audioFeedback', {
keyPath: 'id',
autoIncrement: true
});
audioStore.createIndex('timestamp', 'timestamp', { unique: false });
}
if (!db.objectStoreNames.contains('tagFeedback')) {
const tagStore = db.createObjectStore('tagFeedback', {
keyPath: 'id',
autoIncrement: true
});
tagStore.createIndex('tag', 'tag', { unique: false });
tagStore.createIndex('timestamp', 'timestamp', { unique: false });
}
if (!db.objectStoreNames.contains('customTags')) {
const customTagStore = db.createObjectStore('customTags', {
keyPath: 'tag'
});
customTagStore.createIndex('usage', 'usage', { unique: false });
}
};
});
}
async saveAudioFeedback(audioHash, originalTags, correctedTags, audioFeatures) {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['audioFeedback'], 'readwrite');
const store = transaction.objectStore('audioFeedback');
const feedback = {
audioHash,
originalTags,
correctedTags,
audioFeatures,
timestamp: Date.now()
};
return new Promise((resolve, reject) => {
const request = store.add(feedback);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async saveTagFeedback(tag, feedback, audioHash) {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['tagFeedback'], 'readwrite');
const store = transaction.objectStore('tagFeedback');
const tagFeedback = {
tag,
feedback, // 'positive', 'negative', or 'custom'
audioHash,
timestamp: Date.now()
};
return new Promise((resolve, reject) => {
const request = store.add(tagFeedback);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async saveCustomTag(tag) {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['customTags'], 'readwrite');
const store = transaction.objectStore('customTags');
return new Promise((resolve, reject) => {
const getRequest = store.get(tag);
getRequest.onsuccess = () => {
const existing = getRequest.result;
const tagData = existing ?
{ ...existing, usage: existing.usage + 1 } :
{ tag, usage: 1, timestamp: Date.now() };
const putRequest = store.put(tagData);
putRequest.onsuccess = () => resolve(putRequest.result);
putRequest.onerror = () => reject(putRequest.error);
};
getRequest.onerror = () => reject(getRequest.error);
});
}
async getCustomTags(limit = 20) {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['customTags'], 'readonly');
const store = transaction.objectStore('customTags');
const index = store.index('usage');
return new Promise((resolve, reject) => {
const request = index.openCursor(null, 'prev'); // Descending order
const results = [];
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor && results.length < limit) {
results.push(cursor.value);
cursor.continue();
} else {
resolve(results);
}
};
request.onerror = () => reject(request.error);
});
}
async getTagFeedback(tag = null) {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['tagFeedback'], 'readonly');
const store = transaction.objectStore('tagFeedback');
return new Promise((resolve, reject) => {
let request;
if (tag) {
const index = store.index('tag');
request = index.getAll(tag);
} else {
request = store.getAll();
}
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getAudioFeedback(limit = 100) {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['audioFeedback'], 'readonly');
const store = transaction.objectStore('audioFeedback');
const index = store.index('timestamp');
return new Promise((resolve, reject) => {
const request = index.openCursor(null, 'prev'); // Most recent first
const results = [];
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor && results.length < limit) {
results.push(cursor.value);
cursor.continue();
} else {
resolve(results);
}
};
request.onerror = () => reject(request.error);
});
}
// Generate a simple hash for audio content
async hashAudioFile(file) {
const arrayBuffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').substring(0, 16);
}
async clearAllData() {
if (!this.db) await this.initialize();
const transaction = this.db.transaction(['audioFeedback', 'tagFeedback', 'customTags'], 'readwrite');
await Promise.all([
new Promise((resolve, reject) => {
const request = transaction.objectStore('audioFeedback').clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
}),
new Promise((resolve, reject) => {
const request = transaction.objectStore('tagFeedback').clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
}),
new Promise((resolve, reject) => {
const request = transaction.objectStore('customTags').clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
})
]);
}
}
export default UserFeedbackStore;