diff --git a/package.json b/package.json index 2fea4816..9a5103b5 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.62.2", + "version": "4.63.0", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", diff --git a/src/experiments.json b/src/experiments.json index 5ceea43e..836acc1d 100644 --- a/src/experiments.json +++ b/src/experiments.json @@ -20,8 +20,8 @@ "name": "MQTT-Based PubSub", "description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.", "groups": [ - {"value": true, "weight": 5}, - {"value": false, "weight": 95} + {"value": true, "weight": 0}, + {"value": false, "weight": 100} ] } -} \ No newline at end of file +} diff --git a/src/experiments.ts b/src/experiments.ts index e1f36069..98587996 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -4,7 +4,7 @@ // Experiments // ============================================================================ -import {DEBUG, SERVER} from 'utilities/constants'; +import {DEBUG, SERVER, SERVER_OR_EXT} from 'utilities/constants'; import Module, { GenericModule } from 'utilities/module'; import {has, deep_copy, fetchJSON} from 'utilities/object'; import { getBuster } from 'utilities/time'; @@ -465,11 +465,43 @@ export default class ExperimentManager extends Module<'experiments', ExperimentE } + private _checkExternalAccess() { + let stack; + try { + stack = new Error().stack; + } catch(err) { + /* :thinking: */ + try { + stack = err.stack; + } catch(err_again) { /* aww */ } + } + + if ( ! stack ) + return; + + stack = stack.split(/\s*\n+\s*/g).filter(x => x.startsWith('at ')); + + let external = false; + + for(const line of stack) { + if ( ! line.includes(SERVER_OR_EXT) ) { + external = true; + break; + } + } + + if ( external ) + this.log.warn('Detected access by external script.'); + } + + setTwitchOverride(key: string, value: string) { const overrides = this._getOverrideCookie(), experiments = overrides.experiments, disabled = overrides.disabled; + this._checkExternalAccess(); + experiments[key] = value; const idx = disabled.indexOf(key); @@ -489,6 +521,8 @@ export default class ExperimentManager extends Module<'experiments', ExperimentE const overrides = this._getOverrideCookie(), experiments = overrides.experiments; + this._checkExternalAccess(); + if ( ! has(experiments, key) ) return; diff --git a/src/sites/clips/index.jsx b/src/sites/clips/index.jsx index 4f9eb2d7..4c949906 100644 --- a/src/sites/clips/index.jsx +++ b/src/sites/clips/index.jsx @@ -125,6 +125,11 @@ export default class ClipsSite extends BaseSite { route_data: this.router.match }); + // We need the default to be defined to get the correct value. + this.settings.add('clips.layout.big', { + default: false, + }); + this.settings.getChanges('channel.hide-unfollow', val => this.css_tweaks.toggleHide('unfollow-button', val)); diff --git a/src/sites/shared/player.jsx b/src/sites/shared/player.jsx index 15dce848..1e6da86e 100644 --- a/src/sites/shared/player.jsx +++ b/src/sites/shared/player.jsx @@ -526,7 +526,7 @@ export default class PlayerBase extends Module { ui: { path: 'Player > General >> Extensions', title: 'Show Overlay Extensions', - description: 'Note: This feature does not prevent extensions from loading. Hidden extensions are merely invisible. Hiding extensions with this feature will not improve your security.', + description: '**Note**: This feature does not prevent extensions from loading. Hidden extensions are merely invisible. Hiding extensions with this feature will not improve your security. To prevent extensions from loading entirely, we recommend using the [Disable Twitch Extensions browser extension](https://twitch-tools.rootonline.de/disable_twitch_extensions.php) by CommanderRoot.', component: 'setting-select-box', data: [ {value: 2, title: 'Never'}, @@ -2327,4 +2327,4 @@ export default class PlayerBase extends Module { return null; } -} \ No newline at end of file +} diff --git a/src/sites/twitch-twilight/modules/chat/input.jsx b/src/sites/twitch-twilight/modules/chat/input.jsx index 48643dfb..5bf9a93c 100644 --- a/src/sites/twitch-twilight/modules/chat/input.jsx +++ b/src/sites/twitch-twilight/modules/chat/input.jsx @@ -725,7 +725,7 @@ export default class Input extends Module { if ( inst.props.isShowingEmotePicker ) inst.props.closeEmotePicker(); else if ( inst.props.tray && (! inst.state.value || ! inst.state.value.length) ) - inst.closeTray(); + inst.props.clearTray(); } } catch(err) { diff --git a/src/sites/twitch-twilight/modules/chat/scroller.js b/src/sites/twitch-twilight/modules/chat/scroller.js index c9dbfaa7..5b842fe2 100644 --- a/src/sites/twitch-twilight/modules/chat/scroller.js +++ b/src/sites/twitch-twilight/modules/chat/scroller.js @@ -582,8 +582,10 @@ export default class Scroller extends Module { // Pause Stuff cls.prototype.ffzShouldBePaused = function(since) { + // We may not have moved the mouse. If that's the case, + // since should be zero. if ( since == null ) - since = Date.now() - this.ffz_last_move; + since = Date.now() - (this.ffz_last_move ?? Date.now()); if ( this.state.ffz_scrolled_up ) return true; diff --git a/src/sites/twitch-twilight/modules/directory/index.jsx b/src/sites/twitch-twilight/modules/directory/index.jsx index ec44bb07..10858863 100644 --- a/src/sites/twitch-twilight/modules/directory/index.jsx +++ b/src/sites/twitch-twilight/modules/directory/index.jsx @@ -24,7 +24,7 @@ export const CONTENT_FLAGS = [ 'MatureGame', 'ProfanityVulgarity', 'SexualThemes', - 'ViolentGrpahic' + 'ViolentGraphic' ]; function formatTerms(data, flags) { @@ -61,7 +61,8 @@ export default class Directory extends Module { this.inject(Game); this.DirectoryCard = this.elemental.define( - 'directory-card', 'article[data-a-target^="followed-vod-"],article[data-a-target^="card-"],div[data-a-target^="video-tower-card-"] article,div[data-a-target^="clips-card-"] article,.shelf-card__impression-wrapper article,.tw-tower div article', + 'directory-card', + 'article[data-a-target^="video-carousel-card-"],article[data-a-target^="followed-vod-"],article[data-a-target^="card-"],div[data-a-target^="video-tower-card-"] article,div[data-a-target^="clips-card-"] article,.shelf-card__impression-wrapper article,.tw-tower div article', DIR_ROUTES, null, 0, 0 ); @@ -138,6 +139,17 @@ export default class Directory extends Module { changed: () => this.updateCards() }); + this.settings.add('directory.show-flags', { + default: false, + + ui: { + path: 'Directory > Channels >> Appearance', + title: 'Display Content Flags on channel cards.', + component: 'setting-check-box' + }, + + changed: () => this.updateCards() + }); /*this.settings.add('directory.show-channel-avatars', { default: true, @@ -343,9 +355,13 @@ export default class Directory extends Module { always_inherit: true, process(ctx, val) { const out = new Set; - for(const v of val) - if ( v?.v ) - out.add(v.v); + for(const v of val) { + let item = v?.v; + if ( item === 'ViolentGrpahic') + item = 'ViolentGraphic'; + if ( item ) + out.add(item); + } return out; }, @@ -588,16 +604,34 @@ export default class Directory extends Module { tags = props.tagListProps?.freeformTags; const need_flags = this.settings.get('directory.wait-flags'), + show_flags = this.settings.get('directory.show-flags'), blur_flags = this.settings.get('directory.blur-flags', []), block_flags = this.settings.get('directory.block-flags', []), - has_flags = blur_flags.size > 0 || block_flags.size > 0; + filter_flags = blur_flags.size > 0 || block_flags.size > 0, + has_flags = show_flags || filter_flags; if ( el._ffz_flags === undefined && has_flags ) { el._ffz_flags = null; - this.twitch_data.getStreamFlags(null, props.channelLogin).then(data => { - el._ffz_flags = data ?? []; - this.updateCard(el); - }); + + // Are we getting a clip, a video, or a stream? + if ( props.slug ) { + // Clip + console.log('need flags for clip', props.slug); + el._ffz_flags = []; + + } else if ( props.vodID ) { + // Video + console.log('need flags for vod', props.vodID); + el._ffz_flags = []; + + } else { + // Stream? + console.log('need flags for stream', props.channelLogin); + this.twitch_data.getStreamFlags(null, props.channelLogin).then(data => { + el._ffz_flags = data ?? []; + this.updateCard(el); + }); + } } let bad_tag = false, @@ -623,7 +657,7 @@ export default class Directory extends Module { } let should_blur = blur_tag; - if ( need_flags && has_flags && el._ffz_flags == null ) + if ( need_flags && filter_flags && el._ffz_flags == null ) should_blur = true; if ( ! should_blur ) should_blur = this.settings.provider.get('directory.game.hidden-thumbnails', []).includes(game); @@ -682,8 +716,41 @@ export default class Directory extends Module { hide_container.classList.toggle('tw-hide', should_hide); this.updateUptime(el, props); + this.updateFlags(el); } + updateFlags(el) { + if ( ! document.contains(el) ) + return this.clearFlags(el); + + const setting = this.settings.get('directory.show-flags'); + + if ( ! setting || ! el._ffz_flags?.length ) + return this.clearFlags(el); + + const container = this._getTopRightContainer(el); + if ( ! container ) + return this.clearFlags(el); + + if ( ! el.ffz_flags_el ) + container.appendChild(el.ffz_flags_el = (