mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.65.1
* Fixed: Issue with the IndexedDB storage provider sometimes failing to save values for Chromium users, notably when restoring a backup. This is due to a browser bug, where Chromium would fail to properly handle multiple simultaneous read/write transactions to an IndexedDB database. To prevent issues going forward, we now use a ticket lock to prevent more than one concurrent write transaction. TL;DR: You can restore your backups properly again. * Changed: Add Bluesky's new brand icon for use displaying embedded Bluesky content.
This commit is contained in:
parent
5bafc824b1
commit
786275e7d7
15 changed files with 167 additions and 53 deletions
|
@ -909,6 +909,20 @@
|
|||
"search": [
|
||||
"tiktok"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "d051eb0cf088c2759ee7ed7be581938d",
|
||||
"css": "bluesky",
|
||||
"code": 59472,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M562.5 443.6C511.6 344.6 372.8 159.9 243.9 68.9 120.3-18.3 73.2-3.2 42.3 10.7 6.5 26.9 0 81.8 0 114.1 0 146.4 17.7 379 29.3 417.8 67.4 546.1 203.3 589.4 328.4 575.5 334.8 574.5 341.3 573.7 347.9 572.9 341.5 573.9 335 574.8 328.4 575.5 145.1 602.6-17.8 669.5 195.8 907.3 430.8 1150.6 517.8 855.1 562.5 705.3 607.2 855.1 658.6 1139.9 925 907.3 1125 705.3 979.9 602.7 796.5 575.5 790 574.8 783.5 573.9 777.1 572.9 783.7 573.7 790.2 574.5 796.5 575.5 921.7 589.4 1057.6 546.1 1095.7 417.8 1107.3 379 1125 146.4 1125 114.1 1125 81.8 1118.5 26.9 1082.7 10.7 1051.8-3.2 1004.7-18.3 881.1 68.9 752.2 160 613.4 344.6 562.5 443.6Z",
|
||||
"width": 1125
|
||||
},
|
||||
"search": [
|
||||
"bluesky"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.65.0",
|
||||
"version": "4.65.1",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
Binary file not shown.
|
@ -166,6 +166,8 @@
|
|||
|
||||
<glyph glyph-name="discord" unicode="" d="M1118 766a1091 1091 0 0 1-272 84 748 748 0 0 1-35-71 1014 1014 0 0 1-302 0 751 751 0 0 1-35 71 1099 1099 0 0 1-273-84c-172-255-219-503-195-748h0a1097 1097 0 0 1 334-168 806 806 0 0 1 71 115 710 710 0 0 0-113 54c10 7 19 14 28 21a784 784 0 0 1 668 0c9-8 18-15 27-21a713 713 0 0 0-113-54 799 799 0 0 1 72-115 1092 1092 0 0 1 334 168h0c27 284-47 530-196 748z m-677-598c-65 0-119 59-119 132s52 132 118 132 120-59 119-132-52-132-118-132z m438 0c-65 0-119 59-119 132s52 132 119 132 120-59 119-132-53-132-119-132z" horiz-adv-x="1319" />
|
||||
|
||||
<glyph glyph-name="bluesky" unicode="" d="M563 406c-51 99-190 284-319 375-124 87-171 72-202 58-35-16-42-71-42-103 0-32 18-265 29-304 38-128 174-171 299-157 7 1 13 1 20 2-6-1-13-2-20-2-183-28-346-94-132-332 235-244 322 52 367 202 44-150 96-435 362-202 200 202 55 304-128 332-7 0-13 1-20 2 7-1 13-1 20-2 125-14 261 29 299 157 11 39 29 272 29 304 0 32-6 87-42 103-31 14-78 29-202-58-129-91-268-276-318-375z" horiz-adv-x="1125" />
|
||||
|
||||
<glyph glyph-name="move" unicode="" d="M1000 350q0-14-11-25l-142-143q-11-11-26-11t-25 11-10 25v72h-215v-215h72q14 0 25-10t11-25-11-25l-143-143q-10-11-25-11t-25 11l-143 143q-11 10-11 25t11 25 25 10h72v215h-215v-72q0-14-10-25t-25-11-25 11l-143 143q-11 11-11 25t11 25l143 143q10 11 25 11t25-11 10-25v-72h215v215h-72q-14 0-25 10t-11 25 11 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26t-11-25-25-10h-72v-215h215v72q0 14 10 25t25 11 26-11l142-143q11-10 11-25z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="link-ext" unicode="" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
||||
|
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -234,6 +234,7 @@ export default {
|
|||
|
||||
// Settings second.
|
||||
provider.clear();
|
||||
await provider.flush();
|
||||
let i = 0;
|
||||
for(const key of Object.keys(data.values)) {
|
||||
const val = data.values[key];
|
||||
|
@ -242,6 +243,8 @@ export default {
|
|||
i++;
|
||||
}
|
||||
|
||||
await provider.flush();
|
||||
|
||||
this.message = this.t('setting.backup-restore.zip-restored', '{count,number} items and {blobs,number} binary blobs have been restored. Please refresh this page.', {
|
||||
count: i,
|
||||
blobs: b
|
||||
|
@ -250,4 +253,4 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
@ -8,7 +8,7 @@ import { isValidBlob, deserializeBlob, serializeBlob, BlobLike, SerializedBlobLi
|
|||
// ============================================================================
|
||||
|
||||
import {EventEmitter} from 'utilities/events';
|
||||
import {has, once} from 'utilities/object';
|
||||
import {TicketLock, has, once} from 'utilities/object';
|
||||
import type SettingsManager from '.';
|
||||
import type { OptionalArray, OptionalPromise, ProviderTypeMap } from '../utilities/types';
|
||||
|
||||
|
@ -460,6 +460,9 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
|
||||
private db?: IDBDatabase | null;
|
||||
|
||||
private _last_tx: number = 0;
|
||||
private _lock: TicketLock;
|
||||
|
||||
// Event Handling
|
||||
private _broadcaster?: BroadcastChannel | null;
|
||||
private _boundHandleMessage?: ((event: MessageEvent) => void) | null;
|
||||
|
@ -474,6 +477,8 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
this._pending = new Set<unknown>;
|
||||
this._flush_wait = null;
|
||||
|
||||
this._lock = new TicketLock();
|
||||
|
||||
this._cached = new Map;
|
||||
this.ready = false;
|
||||
this._ready_wait = null;
|
||||
|
@ -798,25 +803,36 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
async loadSettings() {
|
||||
const db = await this.getDB(),
|
||||
trx = db.transaction(['settings'], 'readonly'),
|
||||
store = trx.objectStore('settings');
|
||||
store = trx.objectStore('settings'),
|
||||
id = this._last_tx++;
|
||||
|
||||
this._onStart(id);
|
||||
|
||||
return new Promise<void>((resolve, fail) => {
|
||||
|
||||
trx.onabort = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Transaction aborted reading settings from database.', err);
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
};
|
||||
|
||||
const request = store.getAll();
|
||||
this._onStart(request);
|
||||
|
||||
request.onsuccess = () => {
|
||||
for(const entry of request.result)
|
||||
this._cached.set(entry.k, entry.v);
|
||||
|
||||
this._onFinish(id);
|
||||
resolve();
|
||||
this._onFinish(request);
|
||||
}
|
||||
|
||||
request.onerror = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Error reading settings from database.', err);
|
||||
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
this._onFinish(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -825,23 +841,33 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
async _getKeys() {
|
||||
const db = await this.getDB(),
|
||||
trx = db.transaction(['settings'], 'readonly'),
|
||||
store = trx.objectStore('settings');
|
||||
store = trx.objectStore('settings'),
|
||||
id = this._last_tx++;
|
||||
|
||||
this._onStart(id);
|
||||
|
||||
return new Promise<IDBValidKey[]>((resolve,fail) => {
|
||||
|
||||
trx.onabort = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Transaction aborted reading keys from database.', err);
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
};
|
||||
|
||||
const request = store.getAllKeys();
|
||||
this._onStart(request);
|
||||
|
||||
request.onsuccess = () => {
|
||||
this._onFinish(id);
|
||||
resolve(request.result);
|
||||
this._onFinish(request);
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Error reading keys from database.', err);
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
this._onFinish(request);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -849,21 +875,32 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
async _get(key: string) {
|
||||
const db = await this.getDB(),
|
||||
trx = db.transaction(['settings'], 'readonly'),
|
||||
store = trx.objectStore('settings');
|
||||
store = trx.objectStore('settings'),
|
||||
id = this._last_tx++;
|
||||
|
||||
this._onStart(id);
|
||||
|
||||
return new Promise<any>((resolve, fail) => {
|
||||
//store.onerror = fail;
|
||||
|
||||
trx.onabort = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Transaction aborted reading value from database.', err);
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
};
|
||||
|
||||
const req = store.get(key);
|
||||
this._onStart(req);
|
||||
|
||||
req.onerror = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Error reading value from database.', err);
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
this._onFinish(req);
|
||||
}
|
||||
|
||||
req.onsuccess = () => {
|
||||
this._onFinish(id);
|
||||
resolve(req.result.v);
|
||||
this._onFinish(req);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -873,22 +910,40 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
if ( this.disabled )
|
||||
return;
|
||||
|
||||
// Limit concurrent access to this table.
|
||||
const id = this._last_tx++;
|
||||
this._onStart(id);
|
||||
const release = await this._lock.wait();
|
||||
|
||||
const db = await this.getDB(),
|
||||
trx = db.transaction(['settings'], 'readwrite'),
|
||||
store = trx.objectStore('settings');
|
||||
|
||||
return new Promise<void>((resolve, fail) => {
|
||||
//store.onerror = f;
|
||||
const req = store.put({k: key, v: value});
|
||||
this._onStart(req);
|
||||
|
||||
req.onerror = () => {
|
||||
trx.onabort = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Transaction aborted setting value to database.', err);
|
||||
release();
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
};
|
||||
|
||||
const req = store.put({k: key, v: value});
|
||||
|
||||
req.onerror = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Error setting value to database.', err);
|
||||
release();
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
this._onFinish(req);
|
||||
}
|
||||
|
||||
req.onsuccess = () => {
|
||||
release();
|
||||
this._onFinish(id);
|
||||
resolve();
|
||||
this._onFinish(req);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -898,23 +953,40 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
if ( this.disabled )
|
||||
return;
|
||||
|
||||
// Limit concurrent access to this table.
|
||||
const id = this._last_tx++;
|
||||
this._onStart(id);
|
||||
const release = await this._lock.wait();
|
||||
|
||||
const db = await this.getDB(),
|
||||
trx = db.transaction(['settings'], 'readwrite'),
|
||||
store = trx.objectStore('settings');
|
||||
|
||||
return new Promise<void>((resolve, fail) => {
|
||||
//store.onerror = f;
|
||||
const req = store.delete(key);
|
||||
this._onStart(req);
|
||||
|
||||
req.onerror = () => {
|
||||
trx.onabort = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Transaction aborted deleting value from database.', err);
|
||||
release();
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
this._onFinish(req);
|
||||
}
|
||||
};
|
||||
|
||||
const req = store.delete(key);
|
||||
|
||||
req.onerror = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Error deleting value from database.', err);
|
||||
release();
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
};
|
||||
|
||||
req.onsuccess = () => {
|
||||
release();
|
||||
this._onFinish(id);
|
||||
resolve();
|
||||
this._onFinish(req);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -923,23 +995,40 @@ export class IndexedDBProvider extends AdvancedSettingsProvider {
|
|||
if ( this.disabled )
|
||||
return;
|
||||
|
||||
// Limit concurrent access to this table.
|
||||
const id = this._last_tx++;
|
||||
this._onStart(id);
|
||||
const release = await this._lock.wait();
|
||||
|
||||
const db = await this.getDB(),
|
||||
trx = db.transaction(['settings'], 'readwrite'),
|
||||
store = trx.objectStore('settings');
|
||||
|
||||
return new Promise<void>((resolve, fail) => {
|
||||
//store.onerror = f;
|
||||
const req = store.clear();
|
||||
this._onStart(req);
|
||||
|
||||
req.onerror = () => {
|
||||
trx.onabort = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Transaction aborted clearing database.', err);
|
||||
release();
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
this._onFinish(req);
|
||||
}
|
||||
};
|
||||
|
||||
const req = store.clear();
|
||||
|
||||
req.onerror = err => {
|
||||
if ( this.manager )
|
||||
this.manager.log.error('Error clearing database.', err);
|
||||
release();
|
||||
this._onFinish(id);
|
||||
fail();
|
||||
};
|
||||
|
||||
req.onsuccess = () => {
|
||||
release();
|
||||
this._onFinish(id);
|
||||
resolve();
|
||||
this._onFinish(req);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -122,5 +122,6 @@ export default [
|
|||
"fx",
|
||||
"artist",
|
||||
"discord",
|
||||
"tiktok"
|
||||
"tiktok",
|
||||
"bluesky"
|
||||
] as const;
|
|
@ -79,6 +79,7 @@
|
|||
.ffz-i-fx:before { content: '\e84d'; } /* '' */
|
||||
.ffz-i-artist:before { content: '\e84e'; } /* '' */
|
||||
.ffz-i-discord:before { content: '\e84f'; } /* '' */
|
||||
.ffz-i-bluesky:before { content: '\e850'; } /* '' */
|
||||
.ffz-i-move:before { content: '\f047'; } /* '' */
|
||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -79,6 +79,7 @@
|
|||
.ffz-i-fx { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-artist { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-discord { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-bluesky { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
.ffz-i-fx { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-artist { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-discord { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-bluesky { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@font-face {
|
||||
font-family: 'ffz-fontello';
|
||||
src: url('../font/ffz-fontello.eot?21539525');
|
||||
src: url('../font/ffz-fontello.eot?21539525#iefix') format('embedded-opentype'),
|
||||
url('../font/ffz-fontello.woff2?21539525') format('woff2'),
|
||||
url('../font/ffz-fontello.woff?21539525') format('woff'),
|
||||
url('../font/ffz-fontello.ttf?21539525') format('truetype'),
|
||||
url('../font/ffz-fontello.svg?21539525#ffz-fontello') format('svg');
|
||||
src: url('../font/ffz-fontello.eot?3490719');
|
||||
src: url('../font/ffz-fontello.eot?3490719#iefix') format('embedded-opentype'),
|
||||
url('../font/ffz-fontello.woff2?3490719') format('woff2'),
|
||||
url('../font/ffz-fontello.woff?3490719') format('woff'),
|
||||
url('../font/ffz-fontello.ttf?3490719') format('truetype'),
|
||||
url('../font/ffz-fontello.svg?3490719#ffz-fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
|||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'ffz-fontello';
|
||||
src: url('../font/ffz-fontello.svg?21539525#ffz-fontello') format('svg');
|
||||
src: url('../font/ffz-fontello.svg?3490719#ffz-fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -134,6 +134,7 @@
|
|||
.ffz-i-fx:before { content: '\e84d'; } /* '' */
|
||||
.ffz-i-artist:before { content: '\e84e'; } /* '' */
|
||||
.ffz-i-discord:before { content: '\e84f'; } /* '' */
|
||||
.ffz-i-bluesky:before { content: '\e850'; } /* '' */
|
||||
.ffz-i-move:before { content: '\f047'; } /* '' */
|
||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue