class EventHandler { constructor(worker) { this.events = {}; this.worker = worker; this.worker.addEventListener('message', this.handleEvent.bind(this)); } addEvent(name, cb) { this.events[name] = cb; } sendData(name, status, data) { this.worker.postMessage({ cmd: name, status: status, data: data }); } handleEvent(event) { let data = event.data; if (!data.cmd) { return; } if (this.events[data.cmd]) { this.events[data.cmd](data.data); } } } class Database { constructor(name, version) { this.isInit = false; this.name = name; this.version = version; this.errored = false; this.db; this.prepareDB(); } async prepareDB() { if (this.isInit || this.errored) { return; } let req = this.db = indexedDB.open(this.name, this.version); req.onerror = DatabaseHandler.onError.bind(this); req.onsuccess = DatabaseHandler.onSuccess.bind(this); req.onupgradeneeded = DatabaseHandler.onUpgrade.bind(this); req.onblocked = DatabaseHandler.onBlocked.bind(this); } async set(key, data, store) { data['key'] = key; return await this.run('put', data, store); } async get(key, store) { return await this.run('get', key, store); } async remove(key, store) { return await this.run('delete', key, store); } check() { return !(!this.isInit || this.errored); } async getTX(store) { return await this.db.transaction([store], "readwrite") } async getObjectStore(tx, store) { return await tx.objectStore(store) } async run(action, key, store) { if (this.check()) { let tx = await this.getTX(store); let obj = await this.getObjectStore(tx, store); let data = await this.request(obj[action](key)); await tx.complete return await data; } return null; } request(req) { return new Promise((resolve, reject) => { req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } } class DatabaseHandler { static onError(e) { this.errored = true; eventHandler.sendData("databaseError", "error", e.message); } static onSuccess(e) { this.db = this.db.result; this.isInit = true; eventHandler.sendData("databaseCreated", "success", ""); eventHandler.handleEvent({ data: { cmd: 'dbReady-' + this.name, data: this.db } }); } static onUpgrade(e) { eventHandler.sendData("databaseUpgradeNeeded", "info", e.message); eventHandler.handleEvent({ data: { cmd: 'dbUpgrade-' + this.name, data: this.db } }); } static onBlocked(e) { eventHandler.sendData("databaseBlocked", "error", e.message); } } self.importScripts('jsmediatags.min.js'); class Tagger { constructor(worker) { this.db = new Database("SongLib", 1); } static prepareName(data) { let name = data.name || ''; return name.replace(/[^\w\s]/gi, '').split(" ").join("") } init() { eventHandler.addEvent('getData', this.getData.bind(this)); eventHandler.addEvent('removeData', this.getData.bind(this)); eventHandler.addEvent('setData', this.getData.bind(this)); eventHandler.addEvent('dbReady-SongLib', this.ready.bind(this)); eventHandler.addEvent('dbUpgrade-SongLib', this.upgrade.bind(this)); } async getData(data) { let key = Tagger.prepareName(data), newData = await this.db.get(key, 'songs'), handlerName = data.force ? 'id3-request-force' : 'id3-request'; if (newData) { newData['index'] = data['index']; eventHandler.sendData(handlerName, 'success', newData); } else { this.parseData(data, key).then(r => { r['index'] = data['index']; eventHandler.sendData(handlerName, 'success', r); }); eventHandler.sendData(handlerName, 'waiting', data); } } async removeData(data) { let key = Tagger.prepareName(data), newData = await this.db.remove(key, 'songs'); eventHandler.sendData('id3-remove', 'success', newData); } async setData(data, key) { let newData = await this.db.set(key, data, 'songs'); eventHandler.sendData('id3-set', 'success', newData); } ready(data) { console.log("[ID3] > Song Database Ready"); eventHandler.sendData('id3-ready', "startup", ""); } upgrade(data) { let db = data.result, songs = db.createObjectStore("songs", {keyPath: 'key'}); songs.createIndex("name", "name", {unique: false}); } //if not found in key-value storage read it! this take some time so this is async! async parseData(data, key) { let tag = await new Promise((resolve, reject) => { new jsmediatags.Reader(data.file) .read({ onSuccess: (tag) => { resolve(tag); }, onError: (error) => { console.log(`[ID3] > Error Parsing Data!`); resolve({ tags: {} }); } }); }) let tags = tag.tags, values = { title: tags.title || data.name, artist: tags.artist || 'VA', genre: tags.genre || 'Unknown', year: tags.year || 1970, key: key }; await this.setData(values, key); return values; } } const tagger = new Tagger(self), eventHandler = new EventHandler(self); tagger.init();