mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.34.0
* Added: Option to hide channels from the directory based on their title. * Fixed: Issue with FrankerFaceZ failing to initialize correctly due to a change in Twitch's JS layout.
This commit is contained in:
parent
213c2195cc
commit
bcee12a6b3
8 changed files with 158 additions and 25 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.33.6",
|
||||
"version": "4.34.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -8,7 +8,7 @@ import dayjs from 'dayjs';
|
|||
|
||||
import Module from 'utilities/module';
|
||||
import {createElement, ManagedStyle} from 'utilities/dom';
|
||||
import {timeout, has, glob_to_regex, escape_regex, split_chars} from 'utilities/object';
|
||||
import {timeout, has, addWordSeparators, glob_to_regex, escape_regex, split_chars} from 'utilities/object';
|
||||
import {Color} from 'utilities/color';
|
||||
|
||||
import Badges from './badges';
|
||||
|
@ -24,8 +24,6 @@ import * as RICH_PROVIDERS from './rich_providers';
|
|||
import Actions from './actions';
|
||||
import { getFontsList } from 'src/utilities/fonts';
|
||||
|
||||
export const SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]';
|
||||
|
||||
function sortPriorityColorTerms(list) {
|
||||
list.sort((a,b) => {
|
||||
if ( a[0] < b[0] ) return 1;
|
||||
|
@ -37,10 +35,6 @@ function sortPriorityColorTerms(list) {
|
|||
return list;
|
||||
}
|
||||
|
||||
function addSeparators(str) {
|
||||
return `(^|.*?${SEPARATORS})(?:${str})(?=$|${SEPARATORS})`
|
||||
}
|
||||
|
||||
const TERM_FLAGS = ['g', 'gi'];
|
||||
|
||||
function formatTerms(data) {
|
||||
|
@ -49,7 +43,7 @@ function formatTerms(data) {
|
|||
for(let i=0; i < data.length; i++) {
|
||||
const list = data[i];
|
||||
if ( list[0].length )
|
||||
list[1].push(addSeparators(list[0].join('|')));
|
||||
list[1].push(addWordSeparators(list[0].join('|')));
|
||||
|
||||
out.push(list[1].length ? new RegExp(list[1].join('|'), TERM_FLAGS[i] || 'gi') : null);
|
||||
}
|
||||
|
|
|
@ -249,7 +249,7 @@ Twilight.KNOWN_MODULES = {
|
|||
}
|
||||
}
|
||||
|
||||
const VEND_CHUNK = n => n && n.includes('vendor');
|
||||
const VEND_CHUNK = n => ! n || n.includes('vendor');
|
||||
|
||||
Twilight.KNOWN_MODULES.core.use_result = true;
|
||||
//Twilight.KNOWN_MODULES.core.chunks = 'core';
|
||||
|
@ -263,7 +263,7 @@ Twilight.KNOWN_MODULES['gql-printer'].chunks = VEND_CHUNK;
|
|||
|
||||
Twilight.KNOWN_MODULES.mousetrap.chunks = VEND_CHUNK;
|
||||
|
||||
const CHAT_CHUNK = n => n && n.includes('chat');
|
||||
const CHAT_CHUNK = n => ! n || n.includes('chat');
|
||||
|
||||
Twilight.KNOWN_MODULES['chat-types'].use_result = true;
|
||||
Twilight.KNOWN_MODULES['chat-types'].chunks = CHAT_CHUNK;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import {SiteModule} from 'utilities/module';
|
||||
import {duration_to_string} from 'utilities/time';
|
||||
import {createElement} from 'utilities/dom';
|
||||
import {get} from 'utilities/object';
|
||||
import {get, glob_to_regex, escape_regex, addWordSeparators} from 'utilities/object';
|
||||
|
||||
import Game from './game';
|
||||
|
||||
|
@ -18,6 +18,15 @@ export const CARD_CONTEXTS = ((e ={}) => {
|
|||
return e;
|
||||
})();
|
||||
|
||||
function formatTerms(data, flags) {
|
||||
if ( data[0].length )
|
||||
data[1].push(addWordSeparators(data[0].join('|')));
|
||||
|
||||
if ( ! data[1].length )
|
||||
return null;
|
||||
|
||||
return new RegExp(data[1].join('|'), flags);
|
||||
}
|
||||
|
||||
//const CREATIVE_ID = 488191;
|
||||
|
||||
|
@ -175,6 +184,58 @@ export default class Directory extends SiteModule {
|
|||
changed: () => this.DirectoryShelf.forceUpdate()
|
||||
});
|
||||
|
||||
this.settings.add('directory.block-titles', {
|
||||
default: [],
|
||||
type: 'array_merge',
|
||||
always_inherit: true,
|
||||
ui: {
|
||||
path: 'Directory > Channels >> Block by Title',
|
||||
component: 'basic-terms'
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('__filter:directory.block-titles', {
|
||||
requires: ['directory.block-titles'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
const val = ctx.get('directory.block-titles');
|
||||
if ( ! val || ! val.length )
|
||||
return null;
|
||||
|
||||
const out = [
|
||||
[ // sensitive
|
||||
[], [] // word
|
||||
],
|
||||
[
|
||||
[], []
|
||||
]
|
||||
];
|
||||
|
||||
for(const item of val) {
|
||||
const t = item.t;
|
||||
let v = item.v;
|
||||
|
||||
if ( t === 'glob' )
|
||||
v = glob_to_regex(v);
|
||||
|
||||
else if ( t !== 'raw' )
|
||||
v = escape_regex(v);
|
||||
|
||||
if ( ! v || ! v.length )
|
||||
continue;
|
||||
|
||||
out[item.s ? 0 : 1][item.w ? 0 : 1].push(v);
|
||||
}
|
||||
|
||||
return [
|
||||
formatTerms(out[0], 'g'),
|
||||
formatTerms(out[1], 'gi')
|
||||
];
|
||||
},
|
||||
|
||||
changed: () => this.updateCards()
|
||||
});
|
||||
|
||||
/*this.settings.add('directory.hide-viewing-history', {
|
||||
default: false,
|
||||
ui: {
|
||||
|
@ -339,9 +400,23 @@ export default class Directory extends SiteModule {
|
|||
}
|
||||
}
|
||||
|
||||
const should_hide = bad_tag || (props.streamType === 'rerun' && this.settings.get('directory.hide-vodcasts')) ||
|
||||
(props.context != null && props.context !== CARD_CONTEXTS.SingleGameList && this.settings.provider.get('directory.game.blocked-games', []).includes(game)) ||
|
||||
((props.isPromotion || props.sourceType === 'COMMUNITY_BOOST' || props.sourceType === 'PROMOTION' || props.sourceType === 'SPONSORED') && this.settings.get('directory.hide-promoted'));
|
||||
let should_hide = false;
|
||||
if ( bad_tag )
|
||||
should_hide = true;
|
||||
else if ( props.streamType === 'rerun' && this.settings.get('directory.hide-vodcasts') )
|
||||
should_hide = true;
|
||||
else if ( props.context != null && props.context !== CARD_CONTEXTS.SingleGameList && this.settings.provider.get('directory.game.blocked-games', []).includes(game) )
|
||||
should_hide = true;
|
||||
else if ( (props.isPromotion || props.sourceType === 'COMMUNITY_BOOST' || props.sourceType === 'PROMOTION' || props.sourceType === 'SPONSORED') && this.settings.get('directory.hide-promoted') )
|
||||
should_hide = true;
|
||||
else {
|
||||
const regexes = this.settings.get('__filter:directory.block-titles');
|
||||
if ( regexes &&
|
||||
(( regexes[0] && regexes[0].test(props.title) ) ||
|
||||
( regexes[1] && regexes[1].test(props.title) ))
|
||||
)
|
||||
should_hide = true;
|
||||
}
|
||||
|
||||
let hide_container = el.closest('.tw-tower > div');
|
||||
if ( ! hide_container )
|
||||
|
|
|
@ -359,9 +359,24 @@ export default class Layout extends Module {
|
|||
game = stream?.game?.displayName,
|
||||
offline = props?.offline ?? false;
|
||||
|
||||
let should_hide = false;
|
||||
if ( game && blocked_games.includes(game) )
|
||||
should_hide = true;
|
||||
if ( props.isPromoted && this.settings.get('directory.hide-promoted') )
|
||||
should_hide = true;
|
||||
else {
|
||||
const regexes = this.settings.get('__filter:directory.block-titles');
|
||||
const title = stream?.broadcaster?.broadcastSettings?.title;
|
||||
if ( regexes && title &&
|
||||
(( regexes[0] && regexes[0].test(title) ) ||
|
||||
( regexes[1] && regexes[1].test(title) ))
|
||||
)
|
||||
should_hide = true;
|
||||
}
|
||||
|
||||
card.classList.toggle('ffz--side-nav-card-rerun', rerun);
|
||||
card.classList.toggle('ffz--side-nav-card-offline', offline);
|
||||
card.classList.toggle('tw-hide', game ? blocked_games.includes(game) : false);
|
||||
card.classList.toggle('tw-hide', should_hide);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -222,7 +222,12 @@ export default class WebMunch extends Module {
|
|||
if ( modules )
|
||||
this.processModulesV4(modules, false);
|
||||
|
||||
this._checked_module = {};
|
||||
for(const [key,val] of Object.entries(this._checked_module)) {
|
||||
if (val == true)
|
||||
this._checked_module[key] = null;
|
||||
}
|
||||
|
||||
//this._checked_module = {};
|
||||
const res = this._original_loader.apply(this._original_store, arguments); // eslint-disable-line prefer-rest-params
|
||||
this.emit(':loaded', chunk_ids, names, modules);
|
||||
return res;
|
||||
|
@ -375,7 +380,7 @@ export default class WebMunch extends Module {
|
|||
return null;
|
||||
|
||||
let ids;
|
||||
if ( this._original_store && predicate.chunks ) {
|
||||
if ( this._original_store && predicate.chunks && this._chunk_names && Object.keys(this._chunk_names).length ) {
|
||||
const chunk_pred = typeof predicate.chunks === 'function';
|
||||
if ( ! chunk_pred && ! Array.isArray(predicate.chunks) )
|
||||
predicate.chunks = [predicate.chunks];
|
||||
|
@ -429,7 +434,7 @@ export default class WebMunch extends Module {
|
|||
return null;
|
||||
|
||||
let ids = this._known_ids;
|
||||
if ( this._original_store && predicate.chunks ) {
|
||||
if ( this._original_store && predicate.chunks && this._chunk_names && Object.keys(this._chunk_names).length ) {
|
||||
const chunk_pred = typeof predicate.chunks === 'function';
|
||||
if ( ! chunk_pred && ! Array.isArray(predicate.chunks) )
|
||||
predicate.chunks = [predicate.chunks];
|
||||
|
@ -498,9 +503,19 @@ export default class WebMunch extends Module {
|
|||
}
|
||||
|
||||
|
||||
_checkModule(id) {
|
||||
const fn = this._require?.m?.[id];
|
||||
_checkModule(id, checked = null) {
|
||||
if (checked) {
|
||||
if (checked.has(id))
|
||||
return (this._checked_module[id] ?? false);
|
||||
|
||||
checked.add(id);
|
||||
}
|
||||
|
||||
let fn = this._require?.m?.[id];
|
||||
if ( fn ) {
|
||||
if ( fn.original )
|
||||
fn = fn.original;
|
||||
|
||||
let reqs = fn[Requires],
|
||||
banned = false;
|
||||
|
||||
|
@ -543,12 +558,40 @@ export default class WebMunch extends Module {
|
|||
|
||||
} else if ( reqs ) {
|
||||
for(const mod_id of reqs)
|
||||
if ( ! this._require.m[mod_id] )
|
||||
if ( ! this._require.m[mod_id] ) {
|
||||
banned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! banned && reqs ) {
|
||||
if ( ! checked ) {
|
||||
checked = new Set();
|
||||
checked.add(id);
|
||||
}
|
||||
|
||||
for(const mod_id of reqs) {
|
||||
let val = this._checked_module[mod_id];
|
||||
if (val == null && ! checked.has(mod_id))
|
||||
try {
|
||||
val = this._checkModule(mod_id, checked);
|
||||
} catch (err) {
|
||||
this.log.verbose(`Recursion error checking module ${id} (${mod_id})`);
|
||||
val = true;
|
||||
}
|
||||
|
||||
if ( val ) {
|
||||
this.log.verbose(`Unable to load module ${id} due to unable to load dependency ${mod_id}`);
|
||||
banned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._checked_module[id] = banned;
|
||||
}
|
||||
|
||||
return this._checked_module[id] = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -604,7 +647,7 @@ export default class WebMunch extends Module {
|
|||
const loader = require.e && require.e.toString();
|
||||
let modules;
|
||||
if ( loader && loader.indexOf('Loading chunk') !== -1 ) {
|
||||
const data = this.v4 ? /assets\/"\+\(({1:.*?})/.exec(loader) : /({0:.*?})/.exec(loader);
|
||||
const data = this.v4 ? /assets\/"\+\(?({1:.*?})/.exec(loader) : /({0:.*?})/.exec(loader);
|
||||
if ( data )
|
||||
try {
|
||||
modules = JSON.parse(data[1].replace(/(\d+):/g, '"$1":'))
|
||||
|
@ -612,7 +655,7 @@ export default class WebMunch extends Module {
|
|||
|
||||
} else if ( require.u ) {
|
||||
const builder = require.u.toString(),
|
||||
match = /assets\/"\+({\d+:.*?})/.exec(builder),
|
||||
match = /assets\/"\+\(?({\d+:.*?})/.exec(builder),
|
||||
data = match ? match[1].replace(/([\de]+):/g, (_, m) => {
|
||||
if ( /^\d+e\d+$/.test(m) ) {
|
||||
const bits = m.split('e');
|
||||
|
|
|
@ -17,6 +17,7 @@ export const SENTRY_ID = 'https://74b46b3894114f399d51949c6d237489@sentry.franke
|
|||
export const LV_SERVER = 'https://cbenni.com/api';
|
||||
export const LV_SOCKET_SERVER = 'wss://cbenni.com/socket.io/';
|
||||
|
||||
export const WORD_SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]';
|
||||
|
||||
export const BAD_HOTKEYS = [
|
||||
'f',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
import {BAD_HOTKEYS, TWITCH_EMOTE_V2} from 'utilities/constants';
|
||||
import {BAD_HOTKEYS, TWITCH_EMOTE_V2, WORD_SEPARATORS} from 'utilities/constants';
|
||||
|
||||
const HOP = Object.prototype.hasOwnProperty;
|
||||
|
||||
|
@ -539,6 +539,11 @@ export const escape_regex = RegExp.escape || function escape_regex(str) {
|
|||
}
|
||||
|
||||
|
||||
export function addWordSeparators(str) {
|
||||
return `(^|.*?${WORD_SEPARATORS})(?:${str})(?=$|${WORD_SEPARATORS})`
|
||||
}
|
||||
|
||||
|
||||
const CONTROL_CHARS = '/$^+.()=!|';
|
||||
|
||||
export function glob_to_regex(input) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue