diff --git a/package-lock.json b/package-lock.json index 064dbb0e..b1ffc510 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "frankerfacez", - "version": "4.22.9", + "version": "4.23.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -580,9 +580,9 @@ } }, "@ffz/icu-msgparser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@ffz/icu-msgparser/-/icu-msgparser-1.0.2.tgz", - "integrity": "sha512-LTtWOQ4fftxcC7Z86u9LTi1eZ4X07hQH5yvcURlYSpVTaY9dXXE/h5BdtJB0TIk7FkSiAuN/3sRkfHD0u3+S7Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ffz/icu-msgparser/-/icu-msgparser-2.0.0.tgz", + "integrity": "sha512-VYohS74qsdjZl9KMMlpHIKVGDVFy6kktz/rtvZgfWyngKiqJalHqfdhnQ9meu1hCFQZnTUNi+PUe5xoVAZgYGg==" }, "@npmcli/move-file": { "version": "1.0.1", diff --git a/package.json b/package.json index 4df08c37..a17d4c76 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.23.1", + "version": "4.23.2", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", @@ -67,7 +67,7 @@ "url": "https://github.com/FrankerFaceZ/FrankerFaceZ.git" }, "dependencies": { - "@ffz/icu-msgparser": "^1.0.2", + "@ffz/icu-msgparser": "^2.0.0", "chartjs": "^0.3.24", "chartjs-plugin-waterfall": "^1.0.3", "chartjs-plugin-zoom": "^0.7.7", diff --git a/src/modules/chat/index.js b/src/modules/chat/index.js index b07ce3b3..58dae08f 100644 --- a/src/modules/chat/index.js +++ b/src/modules/chat/index.js @@ -1079,30 +1079,12 @@ export default class Chat extends Module { }); this.settings.add('chat.emotes.animated', { - requires: ['context.bttv.gifs'], - default: null, - process(ctx, val) { - if ( val == null ) { - const temp = ctx.get('ffzap.betterttv.gif_emoticons_mode'); - if ( temp == null ) - val = ctx.get('context.bttv.gifs') ? 1 : 0; - else - val = temp === 2 ? 1 : 0; - } - return val; - }, + default: 1, ui: { path: 'Chat > Appearance >> Emotes', sort: -50, title: 'Animated Emotes', - default(ctx) { - const temp = ctx.get('ffzap.betterttv.gif_emoticons_mode'); - if ( temp == null ) - return ctx.get('context.bttv.gifs') ? 1 : 0; - return temp === 2 ? 1 : 0; - }, - getExtraTerms: () => GIF_TERMS, description: 'This controls whether or not animated emotes are allowed to play in chat. When this is `Disabled`, emotes will appear as static images. Setting this to `Enable on Hover` may cause performance issues.', diff --git a/src/modules/chat/tokenizers.jsx b/src/modules/chat/tokenizers.jsx index b338b8e7..4a492d64 100644 --- a/src/modules/chat/tokenizers.jsx +++ b/src/modules/chat/tokenizers.jsx @@ -5,9 +5,9 @@ // ============================================================================ import {sanitize, createElement} from 'utilities/dom'; -import {has, split_chars} from 'utilities/object'; +import {has, getTwitchEmoteURL, split_chars, getTwitchEmoteSrcSet} from 'utilities/object'; -import {TWITCH_EMOTE_BASE, EmoteTypes, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants'; +import {EmoteTypes, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants'; import {CATEGORIES} from './emoji'; @@ -1328,7 +1328,7 @@ export const AddonEmotes = { const set_id = hide_source ? null : await this.emotes.getTwitchEmoteSet(emote_id), emote_set = set_id != null && await this.emotes.getTwitchSetChannel(set_id); - preview = `${TWITCH_EMOTE_BASE}${ds.id}/3.0?_=preview`; + preview = `${getTwitchEmoteURL(ds.id, 4, true, true)}?_=preview`; fav_source = 'twitch'; if ( emote_set ) { @@ -1641,6 +1641,7 @@ export const TwitchEmotes = { return tokens; const data = msg.ffz_emotes, + anim = this.context.get('chat.emotes.animated'), big = this.context.get('chat.emotes.2x'), use_replacements = this.context.get('chat.fix-bad-emotes'), emotes = []; @@ -1713,8 +1714,8 @@ export const TwitchEmotes = { text: text.slice(idx - t_start, e_start - t_start).join('') }); - let src, srcSet; - let src2, srcSet2; + let src, srcSet, animSrc, animSrcSet; + let src2, srcSet2, animSrc2, animSrcSet2; let can_big = true; const replacement = REPLACEMENTS[e_id]; @@ -1724,12 +1725,22 @@ export const TwitchEmotes = { can_big = false; } else { - src = `${TWITCH_EMOTE_BASE}${e_id}/1.0`; - srcSet = `${TWITCH_EMOTE_BASE}${e_id}/1.0 1x, ${TWITCH_EMOTE_BASE}${e_id}/2.0 2x`; + src = getTwitchEmoteURL(e_id, 1, false); + srcSet = getTwitchEmoteSrcSet(e_id, false); + + if ( anim > 0 ) { + animSrc = getTwitchEmoteURL(e_id, 1, true); + animSrcSet = getTwitchEmoteSrcSet(e_id, true); + } if ( big ) { - src2 = `${TWITCH_EMOTE_BASE}${e_id}/2.0`; - srcSet2 = `${TWITCH_EMOTE_BASE}${e_id}/2.0 1x, ${TWITCH_EMOTE_BASE}${e_id}/3.0 2x`; + src2 = getTwitchEmoteURL(e_id, 2, false); + srcSet2 = getTwitchEmoteSrcSet(e_id, false, true, true); + + if ( anim > 0 ) { + animSrc2 = getTwitchEmoteURL(e_id, 2, true); + animSrcSet2 = getTwitchEmoteSrcSet(e_id, true, true, true); + } } } @@ -1741,6 +1752,11 @@ export const TwitchEmotes = { srcSet, src2, srcSet2, + animSrc, + animSrc2, + animSrcSet, + animSrcSet2, + anim, big, can_big, height: 28, // Not always accurate but close enough. diff --git a/src/modules/metadata.jsx b/src/modules/metadata.jsx index ab1eaa17..ae092ba7 100644 --- a/src/modules/metadata.jsx +++ b/src/modules/metadata.jsx @@ -461,7 +461,7 @@ export default class Metadata extends Module { const stats = data.stats, video_info = this.i18n.t( 'metadata.player-stats.video-info', - 'Video: {videoResolution}p{fps}\nPlayback Rate: {playbackRate,number} Kbps\nDropped Frames:{skippedFrames,number}', + 'Video: {videoResolution}p{fps}\nPlayback Rate: {playbackRate, number} Kbps\nDropped Frames: {skippedFrames, number}', stats ); diff --git a/src/sites/twitch-twilight/modules/chat/emote_menu.jsx b/src/sites/twitch-twilight/modules/chat/emote_menu.jsx index 4db09441..8cafb5c4 100644 --- a/src/sites/twitch-twilight/modules/chat/emote_menu.jsx +++ b/src/sites/twitch-twilight/modules/chat/emote_menu.jsx @@ -4,8 +4,8 @@ // Chat Emote Menu // ============================================================================ -import {has, get, once, maybe_call, set_equals} from 'utilities/object'; -import {TWITCH_GLOBAL_SETS, EmoteTypes, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS, WEBKIT_CSS as WEBKIT, IS_OSX, KNOWN_CODES, TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS, KEYS} from 'utilities/constants'; +import {has, get, once, maybe_call, set_equals, getTwitchEmoteURL, getTwitchEmoteSrcSet} from 'utilities/object'; +import {TWITCH_GLOBAL_SETS, EmoteTypes, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS, WEBKIT_CSS as WEBKIT, IS_OSX, KNOWN_CODES, REPLACEMENT_BASE, REPLACEMENTS, KEYS} from 'utilities/constants'; import {HIDDEN_CATEGORIES, CATEGORIES, CATEGORY_SORT, IMAGE_PATHS} from 'src/modules/chat/emoji'; import {ClickOutside} from 'utilities/dom'; @@ -639,8 +639,8 @@ export default class EmoteMenu extends Module { this.props.stopObserving(this.ref); } - keyInteract(code) { - + keyInteract(code) { // eslint-disable-line + /* no-op */ } clickEmote(event) { @@ -1855,14 +1855,16 @@ export default class EmoteMenu extends Module { overridden = mapped && mapped.id != new_id, replacement = REPLACEMENTS[new_id]; - let src, srcSet; + let src, srcSet, animSrc, animSrcSet; if ( replacement && t.chat.context.get('chat.fix-bad-emotes') ) src = `${REPLACEMENT_BASE}${replacement}`; else { - const base = `${TWITCH_EMOTE_BASE}${new_id}`; - src = `${base}/1.0`; - srcSet = `${src} 1x, ${base}/2.0 2x` + src = getTwitchEmoteURL(new_id, 1, false); + srcSet = getTwitchEmoteSrcSet(new_id, false); + + animSrc = getTwitchEmoteURL(new_id, 1, true); + animSrcSet = getTwitchEmoteSrcSet(new_id, true); } const em = { @@ -1872,6 +1874,8 @@ export default class EmoteMenu extends Module { name: new_name, src, srcSet, + animSrc, + animSrcSet, order: order++, overridden: overridden ? mapped.id : null, misc: ! chan, @@ -1967,7 +1971,6 @@ export default class EmoteMenu extends Module { continue; const id = emote.id, - base = `${TWITCH_EMOTE_BASE}${id}`, name = KNOWN_CODES[emote.token] || emote.token, seen = twitch_seen.has(id), is_fav = twitch_favorites.includes(id); @@ -1979,8 +1982,10 @@ export default class EmoteMenu extends Module { name, order: order++, locked: locked && ! seen, - src: `${base}/1.0`, - srcSet: `${base}/1.0 1x, ${base}/2.0 2x`, + src: getTwitchEmoteURL(id, 1, false), + srcSet: getTwitchEmoteSrcSet(id, false), + animSrc: getTwitchEmoteURL(id, 1, true), + animSrcSet: getTwitchEmoteSrcSet(id, true), favorite: is_fav, hidden: twitch_hidden.includes(id) }; @@ -2024,8 +2029,7 @@ export default class EmoteMenu extends Module { emotes: new Set([emote.id]) } - const base = `${TWITCH_EMOTE_BASE}${id}`, - is_fav = twitch_favorites.includes(id); + const is_fav = twitch_favorites.includes(id); /*if ( Array.isArray(emote.modifiers) && emote.modifiers.length ) modifiers[id] = emote.modifiers;*/ @@ -2037,8 +2041,10 @@ export default class EmoteMenu extends Module { name: emote.token, locked, order: order++, - src: `${base}/1.0`, - srcSet: `${base}/1.0 1x, ${base}/2.0 2x`, + src: getTwitchEmoteURL(id, 1, false), + srcSet: getTwitchEmoteSrcSet(id, false), + animSrc: getTwitchEmoteURL(id, 1, true), + animSrcSet: getTwitchEmoteSrcSet(id, true), bits: true, bit_value: summary.threshold, favorite: is_fav, diff --git a/src/sites/twitch-twilight/modules/chat/input.jsx b/src/sites/twitch-twilight/modules/chat/input.jsx index a14cb7d9..5d0fc5e7 100644 --- a/src/sites/twitch-twilight/modules/chat/input.jsx +++ b/src/sites/twitch-twilight/modules/chat/input.jsx @@ -6,10 +6,11 @@ import Module from 'utilities/module'; import { findReactFragment } from 'utilities/dom'; -import { TWITCH_POINTS_SETS, TWITCH_GLOBAL_SETS, TWITCH_PRIME_SETS, KNOWN_CODES, REPLACEMENTS, REPLACEMENT_BASE, TWITCH_EMOTE_BASE, KEYS } from 'utilities/constants'; +import { TWITCH_POINTS_SETS, TWITCH_GLOBAL_SETS, TWITCH_PRIME_SETS, KNOWN_CODES, REPLACEMENTS, REPLACEMENT_BASE, KEYS } from 'utilities/constants'; import Twilight from 'site'; import { FFZEvent } from 'src/utilities/events'; +import { getTwitchEmoteSrcSet, getTwitchEmoteURL } from 'src/utilities/object'; export default class Input extends Module { constructor(...args) { @@ -623,15 +624,13 @@ export default class Input extends Module { continue; const replacement = REPLACEMENTS[id]; - let src, srcSet; + let srcSet, animSrcSet; if ( replacement && this.chat.context.get('chat.fix-bad-emotes') ) { - src = `${REPLACEMENT_BASE}${replacement}`; - srcSet = `${src} 1x`; + srcSet = `${REPLACEMENT_BASE}${replacement} 1x`; } else { - const base = `${TWITCH_EMOTE_BASE}${id}`; - src = `${base}/1.0`; - srcSet = `${src} 1x, ${base}/2.0 2x` + srcSet = getTwitchEmoteSrcSet(id, false); + animSrcSet = getTwitchEmoteSrcSet(id, true); } out.push({ @@ -642,6 +641,7 @@ export default class Input extends Module { token, tokenLower: token.toLowerCase(), srcSet, + animSrcSet, favorite: favorites.includes(id) }); } diff --git a/src/utilities/constants.js b/src/utilities/constants.js index 5a8e6782..866c8888 100644 --- a/src/utilities/constants.js +++ b/src/utilities/constants.js @@ -55,6 +55,7 @@ export const KEYS = { export const TWITCH_EMOTE_BASE = '//static-cdn.jtvnw.net/emoticons/v1/'; +export const TWITCH_EMOTE_V2 = '//static-cdn.jtvnw.net/emoticons/v2'; export const KNOWN_CODES = { '#-?[\\\\/]': '#-/', diff --git a/src/utilities/object.js b/src/utilities/object.js index abba1fff..7f8a8387 100644 --- a/src/utilities/object.js +++ b/src/utilities/object.js @@ -1,9 +1,20 @@ 'use strict'; -import {BAD_HOTKEYS} from 'utilities/constants'; +import {BAD_HOTKEYS, TWITCH_EMOTE_V2} from 'utilities/constants'; const HOP = Object.prototype.hasOwnProperty; +export function getTwitchEmoteURL(id, scale, animated = false, dark = true) { + return `${TWITCH_EMOTE_V2}/${id}/${animated ? 'default' : 'static'}/${dark ? 'dark' : 'light'}/${scale}.0` +} + +export function getTwitchEmoteSrcSet(id, animated = false, dark = true, big = false) { + if ( big ) + return `${getTwitchEmoteURL(id, 2, animated, dark)} 1x, ${getTwitchEmoteURL(id, 4, animated, dark)} 2x`; + + return `${getTwitchEmoteURL(id, 1, animated, dark)} 1x, ${getTwitchEmoteURL(id, 2, animated, dark)} 2x, ${getTwitchEmoteURL(id, 4, animated, dark)} 4x`; +} + export function isValidShortcut(key) { if ( ! key ) return false; diff --git a/src/utilities/translation-core.js b/src/utilities/translation-core.js index 5aa92b20..ece59830 100644 --- a/src/utilities/translation-core.js +++ b/src/utilities/translation-core.js @@ -12,6 +12,11 @@ dayjs.extend(RelativeTime); import Parser from '@ffz/icu-msgparser'; +const DEFAULT_PARSER_OPTIONS = { + allowTags: false, + requireOther: false +}; + import {get} from 'utilities/object'; import {duration_to_string} from 'utilities/time'; @@ -217,7 +222,7 @@ export default class TranslationCore { this.formats[key] = Object.assign({}, this.formats[key], options.formats[key]); this.types = Object.assign({}, DEFAULT_TYPES, options.types || {}); - this.parser = new Parser(options.parserOptions); + this.parser = new Parser(Object.assign({}, DEFAULT_PARSER_OPTIONS, options.parserOptions)); if ( options.phrases ) this.extend(options.phrases);