diff --git a/src/chatty/SettingsManager.java b/src/chatty/SettingsManager.java index fd39a10bc..186b46de9 100644 --- a/src/chatty/SettingsManager.java +++ b/src/chatty/SettingsManager.java @@ -256,6 +256,7 @@ public void defineSettings() { settings.addLong("emoteMaxHeight", 0); settings.addLong("emoteScale", 100); settings.addLong("emoteScaleDialog", 100); + settings.addLong("emoteScaleGigantified", 200); settings.addList("emoteHiddenSets", new ArrayList<>(), Setting.STRING); settings.addBoolean("closeEmoteDialogOnDoubleClick", false); settings.addBoolean("ffz", true); diff --git a/src/chatty/TwitchClient.java b/src/chatty/TwitchClient.java index d2d8436fd..ef97cee34 100644 --- a/src/chatty/TwitchClient.java +++ b/src/chatty/TwitchClient.java @@ -2757,7 +2757,7 @@ private void handleModAction(ModeratorActionData data) { String warnedUsername = ModLogInfo.getTargetUsername(data); if (warnedUsername != null) { User warnedUser = c.getUser(channel, warnedUsername); - String reason = data.args.get(1); + String reason = ModLogInfo.getWarnReason(data); warnedUser.addWarning(reason, data.created_by); g.updateUserinfo(warnedUser); } diff --git a/src/chatty/gui/GuiUtil.java b/src/chatty/gui/GuiUtil.java index 86345e4e4..b38c01394 100644 --- a/src/chatty/gui/GuiUtil.java +++ b/src/chatty/gui/GuiUtil.java @@ -715,7 +715,11 @@ public void replace(DocumentFilter.FilterBypass fb, int offset, int limit = defaultLimit; - if (!inputLimitsEnabled) { + /** + * If component isn't showing or is not focus owner it's likely + * that the text was set automatically, not entered by the user. + */ + if (!inputLimitsEnabled || !comp.isShowing() || !comp.isFocusOwner()) { limit = 0; } else { @@ -747,7 +751,7 @@ public void replace(DocumentFilter.FilterBypass fb, int offset, popup.showPopup("Length limit reached"); } else { - popup.showPopup("Length limit reached ("+overLimit+" characters not added)"); + popup.showPopup("Length limit reached ("+overLimit+" over limit)"); } } if (!allowNewlines) { diff --git a/src/chatty/gui/StyleManager.java b/src/chatty/gui/StyleManager.java index d35f94c15..58b71dc31 100644 --- a/src/chatty/gui/StyleManager.java +++ b/src/chatty/gui/StyleManager.java @@ -42,6 +42,7 @@ public class StyleManager implements StyleServer { "lineSpacing", "bufferSize", "actionColored","combineBanMessages", "timestampTimezone", "autoScrollTimeout", "searchResultColor2", "inputFont","emoteScale", "emoteMaxHeight", "usericonScale", + "emoteScaleGigantified", "customUsericonScaleMode", "botBadgeEnabled", "filterCombiningCharacters", "pauseChatOnMouseMove", "pauseChatOnMouseMoveCtrlRequired", @@ -200,6 +201,7 @@ private void makeStyles() { addBooleanSetting(Setting.EMOTICONS_ENABLED, "emoticonsEnabled"); addLongSetting(Setting.EMOTICON_SCALE_FACTOR, "emoteScale"); addLongSetting(Setting.EMOTICON_MAX_HEIGHT, "emoteMaxHeight"); + addLongSetting(Setting.EMOTICON_SCALE_FACTOR_GIGANTIFIED, "emoteScaleGigantified"); addLongSetting(Setting.USERICON_SCALE_FACTOR, "usericonScale"); addLongSetting(Setting.CUSTOM_USERICON_SCALE_MODE, "customUsericonScaleMode"); addBooleanSetting(Setting.USERICONS_ENABLED, "usericonsEnabled"); diff --git a/src/chatty/gui/components/FavoritesDialog.java b/src/chatty/gui/components/FavoritesDialog.java index 28e5f2da5..9b4e2c2f0 100644 --- a/src/chatty/gui/components/FavoritesDialog.java +++ b/src/chatty/gui/components/FavoritesDialog.java @@ -103,7 +103,7 @@ public void changedUpdate(DocumentEvent e) { channelsChanged(); } }); - GuiUtil.installLengthLimitDocumentFilter(input, 8000, false); + GuiUtil.installLengthLimitDocumentFilter(input, 80000, false); TextSelectionMenu.install(input); // Table diff --git a/src/chatty/gui/components/JoinDialog.java b/src/chatty/gui/components/JoinDialog.java index 0cb9b0a46..cde02c3a0 100644 --- a/src/chatty/gui/components/JoinDialog.java +++ b/src/chatty/gui/components/JoinDialog.java @@ -62,7 +62,7 @@ public void changedUpdate(DocumentEvent e) { changed(); } }); - GuiUtil.installLengthLimitDocumentFilter(channels, 8000, false); + GuiUtil.installLengthLimitDocumentFilter(channels, 80000, false); TextSelectionMenu.install(channels); GridBagConstraints gbc; diff --git a/src/chatty/gui/components/SimplePopup.java b/src/chatty/gui/components/SimplePopup.java index f44eb64eb..3eeb72327 100644 --- a/src/chatty/gui/components/SimplePopup.java +++ b/src/chatty/gui/components/SimplePopup.java @@ -41,7 +41,7 @@ public SimplePopup(Component owner, SimplePopupListener listener) { } public void showPopup(String text) { - if (owner == null) { + if (owner == null || !owner.isShowing()) { return; } hidePopup(); diff --git a/src/chatty/gui/components/admin/SelectTagsDialog.java b/src/chatty/gui/components/admin/SelectTagsDialog.java index 916473d56..b36c177b2 100644 --- a/src/chatty/gui/components/admin/SelectTagsDialog.java +++ b/src/chatty/gui/components/admin/SelectTagsDialog.java @@ -430,7 +430,7 @@ private static String showTagEditDialog(Window parent, StreamTag tag) { // Input JTextField input = new JTextField(25); input.setText(tag.getName()); - GuiUtil.installLengthLimitDocumentFilter(input, 25, false); + GuiUtil.installLengthLimitDocumentFilter(input, MAX_TAG_LENGTH, false); dialog.add(input, GuiUtil.makeGbc(0, 0, 2, 1)); dialog.add(GuiUtil.createInputLenghtLabel(input, 25), GuiUtil.makeGbc(2, 0, 1, 1)); // Buttons diff --git a/src/chatty/gui/components/help/help-releases.html b/src/chatty/gui/components/help/help-releases.html index 289653a84..ba82898a1 100644 --- a/src/chatty/gui/components/help/help-releases.html +++ b/src/chatty/gui/components/help/help-releases.html @@ -80,7 +80,7 @@

Version 0.27 (This one!) (2024-??-??) [back to top]

-

TBD

+

Beta, see list of changes on GitHub

Version 0.26 (2024-03-21) diff --git a/src/chatty/gui/components/help/help.html b/src/chatty/gui/components/help/help.html index ee93d0c00..c23e1a3aa 100644 --- a/src/chatty/gui/components/help/help.html +++ b/src/chatty/gui/components/help/help.html @@ -5,7 +5,7 @@ -

Chatty (Version: 0.27-b1)

+

Chatty (Version: 0.27-b2)

diff --git a/src/chatty/gui/components/settings/Editor.java b/src/chatty/gui/components/settings/Editor.java index 5e37df33e..51d454bb3 100644 --- a/src/chatty/gui/components/settings/Editor.java +++ b/src/chatty/gui/components/settings/Editor.java @@ -44,7 +44,7 @@ */ public class Editor implements StringEditor { - private static final int INPUT_LENGTH_LIMIT = 100*1000; + private static final int INPUT_LENGTH_LIMIT = 200*1000; private final JDialog dialog; private final JLabel label; diff --git a/src/chatty/gui/components/settings/EmoteSettings.java b/src/chatty/gui/components/settings/EmoteSettings.java index 206f2f0e1..19d7a43f2 100644 --- a/src/chatty/gui/components/settings/EmoteSettings.java +++ b/src/chatty/gui/components/settings/EmoteSettings.java @@ -26,6 +26,7 @@ import chatty.util.api.CachedImage.CachedImageUser; import chatty.util.api.IgnoredEmotes; import chatty.util.seventv.WebPUtil; +import java.awt.Dimension; import java.awt.Font; import java.awt.Insets; import java.awt.Window; @@ -71,7 +72,7 @@ protected EmoteSettings(SettingsDialog d) { d.makeGbc(0, 3, 1, 1, GridBagConstraints.WEST)); ComboLongSetting emoteScale = new ComboLongSetting(makeScaleValues()); d.addLongSetting("emoteScale", emoteScale); - main.add(emoteScale, d.makeGbc(1, 3, 1, 1, GridBagConstraints.CENTER)); + main.add(emoteScale, d.makeGbc(1, 3, 1, 1, GridBagConstraints.WEST)); // Maximum Emote Height (Chat) main.add(new JLabel(Language.getString("settings.emoticons.maxHeight")), @@ -81,18 +82,24 @@ protected EmoteSettings(SettingsDialog d) { main.add(new JLabel(Language.getString("settings.emoticons.maxHeightPixels")), d.makeGbc(4, 3, 1, 1, GridBagConstraints.WEST)); + main.add(new JLabel(Language.getString("settings.emoticons.gigantifiedScale")), + d.makeGbc(0, 4, 1, 1, GridBagConstraints.WEST)); + ComboLongSetting emoteScaleGigantified = new ComboLongSetting(makeScaleValuesGigantified()); + d.addLongSetting("emoteScaleGigantified", emoteScaleGigantified); + main.add(emoteScaleGigantified, d.makeGbc(1, 4, 1, 1, GridBagConstraints.WEST)); + // Emotes Dialog Emote Scale main.add(new JLabel(Language.getString("settings.emoticons.dialogScale")), - d.makeGbc(0, 4, 1, 1, GridBagConstraints.WEST)); + d.makeGbc(0, 5, 1, 1, GridBagConstraints.WEST)); ComboLongSetting emoteScaleDialog = new ComboLongSetting(makeScaleValues()); d.addLongSetting("emoteScaleDialog", emoteScaleDialog); - main.add(emoteScaleDialog, d.makeGbc(1, 4, 1, 1, GridBagConstraints.CENTER)); + main.add(emoteScaleDialog, d.makeGbc(1, 5, 1, 1, GridBagConstraints.WEST)); //------- // Other //------- main.add(d.addSimpleBooleanSetting("closeEmoteDialogOnDoubleClick"), - d.makeGbc(0, 5, 3, 1)); + d.makeGbc(0, 6, 3, 1)); //-------------------------- // Animations @@ -103,11 +110,11 @@ protected EmoteSettings(SettingsDialog d) { SettingsUtil.addSubsettings(animatePauseSetting, s -> s != 2, animatePauseFrameSetting); SettingsUtil.addLabeledComponent(main, "animationPause", - 0, 6, 4, GridBagConstraints.WEST, + 0, 7, 4, GridBagConstraints.WEST, animatePauseSetting); SettingsUtil.addLabeledComponent(main, "animationPauseFrame", - 0, 7, 4, GridBagConstraints.WEST, + 0, 8, 4, GridBagConstraints.WEST, animatePauseFrameSetting); //-------------------------- @@ -115,14 +122,14 @@ protected EmoteSettings(SettingsDialog d) { //-------------------------- JLabel webpTest = new JLabel("WebP not supported."); main.add(webpTest, - SettingsDialog.makeGbc(2, 8, 3, 1, GridBagConstraints.WEST)); + SettingsDialog.makeGbc(2, 9, 3, 1, GridBagConstraints.WEST)); WebPUtil.runIfWebPAvailable(() -> { webpTest.setText("WebP is supported."); }); main.add( d.addSimpleBooleanSetting("webp"), - d.makeGbc(0, 8, 2, 1, GridBagConstraints.WEST)); + d.makeGbc(0, 9, 2, 1, GridBagConstraints.WEST)); //========================== // Provider specific @@ -263,12 +270,16 @@ protected EmoteSettings(SettingsDialog d) { public static Map makeScaleValues() { final Map scaleDef = new LinkedHashMap<>(); - for (int i=50;i<=200;i += 10) { - if (i == 10) { - scaleDef.put((long)i, "Normal"); - } else { - scaleDef.put((long)i, (i)+"%"); - } + for (int i = 50; i <= 200; i += 10) { + scaleDef.put((long) i, (i) + "%"); + } + return scaleDef; + } + + public static Map makeScaleValuesGigantified() { + final Map scaleDef = new LinkedHashMap<>(); + for (int i = 100; i <= 200; i += 10) { + scaleDef.put((long) i, "+" + (i - 100) + "%"); } return scaleDef; } @@ -545,7 +556,7 @@ public static void main(String[] args) { List matches = new ArrayList<>(); matches.add(IgnoredEmotes.Item.parse("Kappa for:c")); matches.add(IgnoredEmotes.Item.parse("Kappa for:t")); - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, "Kappa", null); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, "Kappa"); // b.setStringId("123abc"); System.out.println(new EditIgnoredEmote(null).showDialog(null, b.build(), matches)); System.exit(0); diff --git a/src/chatty/gui/components/settings/HighlightSettings.java b/src/chatty/gui/components/settings/HighlightSettings.java index 1822dfacc..c51d00058 100644 --- a/src/chatty/gui/components/settings/HighlightSettings.java +++ b/src/chatty/gui/components/settings/HighlightSettings.java @@ -350,7 +350,7 @@ private void updateTestSubstitutes() { private static class SubstitutesEditor extends JDialog implements StringEditor { - private static final int INPUT_LENGTH_LIMIT = 100*1000; + private static final int INPUT_LENGTH_LIMIT = 200*1000; private final JTextArea itemValue = new JTextArea(4, 20); private final JTextArea info = new JTextArea(20, 68); diff --git a/src/chatty/gui/components/settings/MainSettings.java b/src/chatty/gui/components/settings/MainSettings.java index c879ca8fb..e2d33b5a3 100644 --- a/src/chatty/gui/components/settings/MainSettings.java +++ b/src/chatty/gui/components/settings/MainSettings.java @@ -58,7 +58,7 @@ public MainSettings(SettingsDialog d) { SettingsUtil.addLabeledComponent(startSettingsPanel, "settings.startup.onStart", 0, 1, 1, EAST, onStart); JTextField channels = d.addSimpleStringSetting("autojoinChannel", 25, true); - GuiUtil.installLengthLimitDocumentFilter(channels, 8000, false); + GuiUtil.installLengthLimitDocumentFilter(channels, 80000, false); SettingsUtil.addLabeledComponent(startSettingsPanel, "settings.startup.channels", 0, 2, 1, EAST, channels); JCheckBox connectDialogIfMissing = d.addSimpleBooleanSetting("connectDialogIfMissing"); diff --git a/src/chatty/gui/components/textpane/ChannelTextPane.java b/src/chatty/gui/components/textpane/ChannelTextPane.java index c811fc4e7..54dbddd0e 100644 --- a/src/chatty/gui/components/textpane/ChannelTextPane.java +++ b/src/chatty/gui/components/textpane/ChannelTextPane.java @@ -151,7 +151,7 @@ public enum Attribute { URL_DELETED, DELETED_LINE, EMOTICON, IS_APPENDED_INFO, INFO_TEXT, BANS, BAN_MESSAGE, ID, ID_AUTOMOD, AUTOMOD_ACTION, USERICON, IMAGE_ID, ANIMATED, APPENDED_INFO_UPDATED, MENTION, USERICON_INFO, GENERAL_LINK, - REPEAT_MESSAGE_COUNT, LOW_TRUST_INFO, IS_RESTRICTED, + REPEAT_MESSAGE_COUNT, LOW_TRUST_INFO, IS_RESTRICTED, POWER_UP_INFO, HIGHLIGHT_WORD, HIGHLIGHT_LINE, HIGHLIGHT_SOURCE, EVEN, PARAGRAPH_SPACING, CUSTOM_BACKGROUND, CUSTOM_BACKGROUND_ORIG, CUSTOM_FOREGROUND, @@ -180,6 +180,7 @@ public enum Setting { ACTION_COLORED, LINKS_CUSTOM_COLOR, BUFFER_SIZE, AUTO_SCROLL_TIME, EMOTICON_MAX_HEIGHT, EMOTICON_SCALE_FACTOR, USERICON_SCALE_FACTOR, + EMOTICON_SCALE_FACTOR_GIGANTIFIED, CUSTOM_USERICON_SCALE_MODE, BOT_BADGE_ENABLED, CHANNEL_LOGO_SIZE, FILTER_COMBINING_CHARACTERS, PAUSE_ON_MOUSEMOVE, PAUSE_ON_MOUSEMOVE_CTRL_REQUIRED, @@ -692,6 +693,13 @@ private void printUserMessage(UserMessage message, String timestamp) { } finishLine(); + String powerUpInfo = message.tags.getPowerUpInfo(); + if (powerUpInfo != null) { + changeInfo(getLastLine(doc), (attributes) -> { + attributes.addAttribute(Attribute.POWER_UP_INFO, powerUpInfo); + }); + } + int repeatMsg = RepeatMsgHelper.getRepeatMsg(message.tags); if (repeatMsg > 1) { changeInfo(getLastLine(doc), (attributes) -> { @@ -1053,6 +1061,12 @@ private void changeInfo(Element line, InfoChanger changer) { // Make text based on current attributes //--------------------------------------- String text = ""; + + String powerUpInfo = (String)attributes.getAttribute(Attribute.POWER_UP_INFO); + if (!StringUtil.isNullOrEmpty(powerUpInfo)) { + text = StringUtil.append(text, " ", "["+powerUpInfo+"]"); + } + Integer repeatMsgCount = (Integer)attributes.getAttribute(Attribute.REPEAT_MESSAGE_COUNT); if (repeatMsgCount != null && repeatMsgCount > 1) { text += String.format("(x%d)", repeatMsgCount); @@ -2501,7 +2515,7 @@ protected void printSpecialsNormal(String text, User user, MutableAttributeSet s } if (styles.isEnabled(Setting.EMOTICONS_ENABLED)) { - findEmoticons(text, user, ranges, rangesStyle, emotes); + findEmoticons(text, user, ranges, rangesStyle, emotes, tags != null && tags.hasGigantifiedEmote()); if (containsBits) { findBits(main.emoticons.getCheerEmotes(), text, ranges, rangesStyle, user); } @@ -2821,7 +2835,7 @@ public int getRand(int bound) { } private void findEmoticons(String text, User user, Map ranges, - Map rangesStyle, TagEmotes tagEmotes) { + Map rangesStyle, TagEmotes tagEmotes, boolean gigantified) { Set accessToSets = user.isLocalUser() ? main.emoticons.getLocalEmotesets() : null; findEmoticons(user, main.emoticons.getCustomEmotes(), text, ranges, rangesStyle, accessToSets); @@ -2840,7 +2854,7 @@ private void findEmoticons(String text, User user, Map ranges, if (tagEmotes != null) { // Add emotes from tags Map emoticonsById = main.emoticons.getEmoticonsById(); - addTwitchTagsEmoticons(user, emoticonsById, text, ranges, rangesStyle, tagEmotes); + addTwitchTagsEmoticons(user, emoticonsById, text, ranges, rangesStyle, tagEmotes, gigantified); } // Sent messages @@ -2907,7 +2921,7 @@ private void findEmoticons(String text, User user, Map ranges, // combined emote still needs to be created if (emotes.size() > 1) { Emoticon emote = main.emoticons.getCombinedEmote(emotes, styles.emoticonImageType()); - styleChanges.put(baseStart, styles.emoticon(emote)); + styleChanges.put(baseStart, styles.emoticon(emote, false)); } // Always reset when it's not an overlay emote emotes.clear(); @@ -2924,7 +2938,7 @@ private void findEmoticons(String text, User user, Map ranges, // Finish any remaining changes if (emotes.size() > 1) { Emoticon emote = main.emoticons.getCombinedEmote(emotes, styles.emoticonImageType()); - styleChanges.put(baseStart, styles.emoticon(emote)); + styleChanges.put(baseStart, styles.emoticon(emote, false)); } // Apply changes (except removing entries, which is already done) for (Entry entry : changes.entrySet()) { @@ -2947,11 +2961,12 @@ private void findEmoticons(String text, User user, Map ranges, */ private void addTwitchTagsEmoticons(User user, Map emoticons, String text, Map ranges, Map rangesStyle, - TagEmotes emotesDef) { + TagEmotes emotesDef, boolean gigantified) { if (emotesDef == null) { return; } Map def = emotesDef.emotes; + int lastIndex = emotesDef.getLargestIndex(); /** * Iterate over each character of the message and check if an emote starts @@ -2968,9 +2983,10 @@ private void addTwitchTagsEmoticons(User user, Map emoticons, int offset = 0; for (int i=0;i emoticons, */ String code = text.substring(start, end+1); Emoticon.Builder b = new Emoticon.Builder( - Emoticon.Type.TWITCH, code, null); + Emoticon.Type.TWITCH, code); b.setStringId(id); b.setEmoteset(Emoticon.SET_UNKNOWN); emoticon = b.build(); main.emoticons.addTempEmoticon(emoticon); } if (!main.emoticons.isEmoteIgnored(emoticon, IgnoredEmotes.CHAT)) { - addEmoticon(emoticon, start, end, ranges, rangesStyle); + addEmoticon(emoticon, start, end, ranges, rangesStyle, gigantified && currentIndex == lastIndex); } } } @@ -3043,7 +3059,7 @@ private void findEmoticons(User user, Set emoticons, String text, // For Emoji, check for text style variation selector boolean textEmoji = emoticon.type == Emoticon.Type.EMOJI && m.group().endsWith("\uFE0E"); if (!textEmoji) { - addEmoticon(emoticon, start, end, ranges, rangesStyle); + addEmoticon(emoticon, start, end, ranges, rangesStyle, false); } } } @@ -3070,7 +3086,7 @@ private void findBits(Set emotes, String text, continue; } boolean ignored = main.emoticons.isEmoteIgnored(emote, IgnoredEmotes.CHAT); - if (!ignored && addEmoticon(emote, start, end - bitsLength, ranges, rangesStyle)) { + if (!ignored && addEmoticon(emote, start, end - bitsLength, ranges, rangesStyle, false)) { // Add emote addFormattedText(emote.color, end - bitsLength + 1, end, ranges, rangesStyle); } else { @@ -3086,10 +3102,10 @@ private void findBits(Set emotes, String text, private boolean addEmoticon(Emoticon emoticon, int start, int end, Map ranges, - Map rangesStyle) { + Map rangesStyle, boolean gigantified) { if (!inRanges(start, ranges) && !inRanges(end, ranges)) { ranges.put(start, end); - MutableAttributeSet attr = styles.emoticon(emoticon); + MutableAttributeSet attr = styles.emoticon(emoticon, gigantified); // Add an extra attribute, making this Style unique // (else only one icon will be output if two of the same // follow in a row) @@ -3929,6 +3945,7 @@ private void setSettings() { addNumericSetting(Setting.AUTO_SCROLL_TIME, 30, 5, 1234); addNumericSetting(Setting.EMOTICON_MAX_HEIGHT, 200, 0, 300); addNumericSetting(Setting.EMOTICON_SCALE_FACTOR, 100, 1, 200); + addNumericSetting(Setting.EMOTICON_SCALE_FACTOR_GIGANTIFIED, 150, 1, 200); addNumericSetting(Setting.USERICON_SCALE_FACTOR, 100, 1, 200); addNumericSetting(Setting.CUSTOM_USERICON_SCALE_MODE, 0, 0, 10); addNumericSetting(Setting.DISPLAY_NAMES_MODE, 0, 0, 10); @@ -4310,12 +4327,19 @@ public MutableAttributeSet makeIconStyle(Usericon icon, User user) { return style; } - public int emoticonMaxHeight() { - return numericSettings.get(Setting.EMOTICON_MAX_HEIGHT); + public int emoticonMaxHeight(boolean gigantified) { + return (int)(numericSettings.get(Setting.EMOTICON_MAX_HEIGHT) * gigantifiedFactor(gigantified)); } - public float emoticonScaleFactor() { - return (float)(numericSettings.get(Setting.EMOTICON_SCALE_FACTOR) / 100.0); + public float emoticonScaleFactor(boolean gigantified) { + return (float)(numericSettings.get(Setting.EMOTICON_SCALE_FACTOR) / 100.0) * gigantifiedFactor(gigantified); + } + + private float gigantifiedFactor(boolean gigantified) { + if (gigantified) { + return (float)(numericSettings.get(Setting.EMOTICON_SCALE_FACTOR_GIGANTIFIED) / 100.0); + } + return 1; } public float usericonScaleFactor() { @@ -4360,13 +4384,14 @@ public MutableAttributeSet replacement(String text, String replacement) { * Make a style with the given icon. * * @param emoticon + * @param gigantified * @return */ - public MutableAttributeSet emoticon(Emoticon emoticon) { + public MutableAttributeSet emoticon(Emoticon emoticon, boolean gigantified) { // Does this need any other attributes e.g. standard? SimpleAttributeSet emoteStyle = new SimpleAttributeSet(); CachedImage emoteImage = emoticon.getIcon( - emoticonScaleFactor(), emoticonMaxHeight(), emoticonImageType(), ChannelTextPane.this); + emoticonScaleFactor(gigantified), emoticonMaxHeight(gigantified), emoticonImageType(), ChannelTextPane.this); StyleConstants.setIcon(emoteStyle, emoteImage.getImageIcon()); emoteStyle.addAttribute(Attribute.EMOTICON, emoteImage); diff --git a/src/chatty/gui/components/textpane/ModLogInfo.java b/src/chatty/gui/components/textpane/ModLogInfo.java index d797997b9..ac9e14492 100644 --- a/src/chatty/gui/components/textpane/ModLogInfo.java +++ b/src/chatty/gui/components/textpane/ModLogInfo.java @@ -67,6 +67,9 @@ public static String makeArgsText(ModeratorActionData data) { if (data.moderation_action.equals("delete") && data.args.size() > 1) { return String.format("%s (%s)", data.args.get(0), data.args.get(1)); } + if (data.moderation_action.equals("warn") && data.args.size() > 1) { + return String.format("%s %s", data.args.get(0), getWarnReason(data)); + } return StringUtil.join(data.args, " "); } @@ -193,6 +196,24 @@ public static String getTargetUsername(ModeratorActionData data) { return null; } + public static String getWarnReason(ModeratorActionData data) { + StringBuilder result = new StringBuilder(); + // First arg is target username + for (int i = 1; i < data.args.size(); i++) { + String item = data.args.get(i); + if (item.isEmpty() && i == 1) { + result.append("Selected rules: "); + } + if (!item.isEmpty()) { + if (result.length() > 0) { + result.append(", "); + } + result.append(item); + } + } + return result.toString(); + } + @Override public String toString() { return text; diff --git a/src/chatty/gui/components/userinfo/PastMessages.java b/src/chatty/gui/components/userinfo/PastMessages.java index 90907c12d..cfa94485c 100644 --- a/src/chatty/gui/components/userinfo/PastMessages.java +++ b/src/chatty/gui/components/userinfo/PastMessages.java @@ -258,7 +258,7 @@ else if (m instanceof User.WarnMessage) { User.WarnMessage wm = (User.WarnMessage)m; b.append(timestampFormat.make(m.getTime(), user.getRoom())).append("! "); if (wm.by == null && wm.reason == null) { - b.append("Warnung acknowledged"); + b.append("Warning acknowledged"); } else { b.append("Warned by ").append(wm.by).append(" (").append(wm.reason).append(")"); diff --git a/src/chatty/gui/emoji/EmojiUtil.java b/src/chatty/gui/emoji/EmojiUtil.java index 1e8e53b3c..3e32fea59 100644 --- a/src/chatty/gui/emoji/EmojiUtil.java +++ b/src/chatty/gui/emoji/EmojiUtil.java @@ -95,7 +95,8 @@ public static void add(EmojiSet set, Collection emotes, String code, String filename, String name, String alias, String category, String unicodeVersion) { String url = EmojiUtil.class.getResource(set.internalPath+filename).toString(); //System.out.println(url); - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.EMOJI, code, url); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.EMOJI, code); + b.addUrl(1, url); b.setCreator(set.name); b.setSize(24, 24); if (name != null) { diff --git a/src/chatty/lang/Strings.properties b/src/chatty/lang/Strings.properties index ed0f98b51..99bc29b1d 100644 --- a/src/chatty/lang/Strings.properties +++ b/src/chatty/lang/Strings.properties @@ -910,6 +910,7 @@ settings.section.emoticons = General Emoticon Settings settings.boolean.emoticonsEnabled = Show emoticons settings.emoticons.button.editIgnored = Edit Ignored Emotes settings.emoticons.chatScale = Scale (Chat): +settings.emoticons.gigantifiedScale = Gigantified: settings.emoticons.dialogScale = Emotes Dialog: settings.emoticons.maxHeight = Maximum Height: settings.emoticons.maxHeightPixels = pixels diff --git a/src/chatty/util/BTTVEmotes.java b/src/chatty/util/BTTVEmotes.java index f28e53fdb..33739257a 100644 --- a/src/chatty/util/BTTVEmotes.java +++ b/src/chatty/util/BTTVEmotes.java @@ -24,7 +24,7 @@ public class BTTVEmotes { private static final String URL_GLOBAL = "https://api.betterttv.net/3/cached/emotes/global"; private static final String URL_CHANNEL = "https://api.betterttv.net/3/cached/users/twitch/"; - private static final String TEMPLATE = "https://cdn.betterttv.net/emote/{{id}}/{{image}}"; + public static final String TEMPLATE = "https://cdn.betterttv.net/emote/{{id}}/{{image}}"; public static final String GLOBAL = "$global$"; @@ -236,7 +236,7 @@ private static Emoticon parseEmote(JSONObject o, String urlTemplate, String chan } Emoticon.Builder builder = new Emoticon.Builder(Emoticon.Type.BTTV, - code, urlTemplate); + code); builder.setCreator(userName); builder.setLiteral(true); builder.setStringId(id); diff --git a/src/chatty/util/ChattyMisc.java b/src/chatty/util/ChattyMisc.java index 227201509..74061262c 100644 --- a/src/chatty/util/ChattyMisc.java +++ b/src/chatty/util/ChattyMisc.java @@ -123,7 +123,7 @@ private static void parseSmilies(JSONObject root) { String id = JSONUtil.getString(emoteData, "id"); String code = JSONUtil.getString(emoteData, "code"); String regex = JSONUtil.getString(emoteData, "regex"); - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, code, null); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, code); b.setStringId(id); b.setRegex(regex); result.get(type).add(b.build()); diff --git a/src/chatty/util/CombinedEmoticon.java b/src/chatty/util/CombinedEmoticon.java index 575c8acb8..67a9ed44e 100644 --- a/src/chatty/util/CombinedEmoticon.java +++ b/src/chatty/util/CombinedEmoticon.java @@ -59,7 +59,8 @@ public static String getCode(List emotes) { public static CombinedEmoticon create(List emotes, String code, ImageType imageType) { Debugging.println("combinedemotes", "Create: %s", emotes); Emoticon base = emotes.get(0); - Emoticon.Builder b = new Emoticon.Builder(base.type, code, base.url); + Emoticon.Builder b = new Emoticon.Builder(base.type, code); + b.setImageUrl(base.url); b.setStringId(base.stringId); b.setStringIdAlias(base.stringIdAlias); b.setLiteral(base.literal); @@ -75,7 +76,6 @@ public static CombinedEmoticon create(List emotes, String code, ImageT } } b.setSize(maxWidth, maxHeight); - b.setX2Url(base.urlX2); b.setSubType(base.subType); b.addInfo("Special Combined Emote"); return new CombinedEmoticon(b, emotes, imageType); diff --git a/src/chatty/util/ImageCache.java b/src/chatty/util/ImageCache.java index 1cecd1033..520e70050 100644 --- a/src/chatty/util/ImageCache.java +++ b/src/chatty/util/ImageCache.java @@ -557,14 +557,22 @@ public ImageRequest(Function urlRequester, float scaleFactor, i Dimension expectedSize = getScaledSize(defaultSize, scaleFactor, maxHeight); int actualUrlFactor = 1; String preferredUrl = null; - if (expectedSize.width > defaultSize.width) { - preferredUrl = urlRequester.apply(2); + int preferredFactor = getPreferredScale(expectedSize, defaultSize); + for (int f = preferredFactor; f <= 4; f++) { + preferredUrl = urlRequester.apply(f); if (preferredUrl != null) { - actualUrlFactor = 2; + actualUrlFactor = f; + break; } } if (preferredUrl == null) { - preferredUrl = urlRequester.apply(1); + for (int f = preferredFactor - 1; f > 0; f--) { + preferredUrl = urlRequester.apply(f); + if (preferredUrl != null) { + actualUrlFactor = f; + break; + } + } } URL actualUrl = null; try { @@ -689,6 +697,13 @@ public static Dimension getScaledSize(Dimension d, float scaleFactor, int maxHei } return new Dimension(resultWidth, resultHeight); } + + private static int getPreferredScale(Dimension expectedSize, Dimension defaultSize) { + if (expectedSize.width > 0 && defaultSize.width > 0) { + return MathUtil.divRoundUp(expectedSize.width, defaultSize.width); + } + return 1; + } /** * Gets the URL factor corrected size from the image. diff --git a/src/chatty/util/ImageUrl.java b/src/chatty/util/ImageUrl.java new file mode 100644 index 000000000..d021509b5 --- /dev/null +++ b/src/chatty/util/ImageUrl.java @@ -0,0 +1,182 @@ + +package chatty.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * + * @author tduva + */ +public abstract class ImageUrl { + + private static final Map cache = new WeakHashMap<>(); + + private static ImageUrl cache(TemplateImageUrl object) { + synchronized (cache) { + ImageUrl cached = cache.get(object); + if (cached != null) { + return cached; + } + cache.put(object, object); + return object; + } + } + + public static int cacheSize() { + return cache.size(); + } + + public static class Builder { + + private final Map urls; + private final String id; + + public Builder(String id) { + this.id = id; + this.urls = new HashMap<>(); + } + + public Builder(String id, Map urls) { + this.id = id; + this.urls = urls; + } + + public ImageUrl.Builder addUrl(int size, String url) { + urls.put(size, url); + return this; + } + + public ImageUrl build() { + if (urls.size() == 1 && urls.containsKey(1)) { + return new SingleImageUrl(urls.get(1)); + } + if (id == null) { + return new MapImageUrl(urls); + } + String template = null; + for (Map.Entry entry : urls.entrySet()) { + int size = entry.getKey(); + String url = entry.getValue(); + String temp = StringUtil.replaceLast(url, id, "{id}"); + temp = StringUtil.replaceLast(temp, String.valueOf(entry.getKey()), "{x}"); + if (!buildUrl(temp, id, size).equals(url) + || (template != null && !template.equals(temp))) { + return new MapImageUrl(urls); + } + template = temp; + } + ImageUrl result = cache(new TemplateImageUrl(template, urls.keySet())); +// System.out.println(result+" "+System.identityHashCode(result)+" "+urls); +// System.out.println(cacheSize()); + return result; + } + + } + + public static final void main(String[] args) { + ImageUrl url = ImageUrl.builder("382581").addUrl(1, "https://cdn.frankerfacez.com/emote/382581/1").addUrl(2, "https://cdn.frankerfacez.com/emote/382581/2").build(); + ImageUrl url2 = ImageUrl.builder("382582").addUrl(1, "https://cdn.frankerfacez.com/emote/382582/1").addUrl(2, "https://cdn.frankerfacez.com/emote/382582/2").build(); + System.out.println(url == url2); + + System.out.println(ImageUrl.builder("382581").addUrl(1, "https://cdn.frankerfacez.com/emote/382581/2").addUrl(2, "https://cdn.frankerfacez.com/emote/382581/2").build().getClass()); + } + + public static ImageUrl.Builder builder(String id) { + return new ImageUrl.Builder(id); + } + + private static String buildUrl(String template, String id, int size) { + return template.replace("{id}", id).replace("{x}", String.valueOf(size)); + } + + public abstract String getUrl(String id, int size); + + public static class TemplateImageUrl extends ImageUrl { + + private final String template; + private final Set sizes; + + private TemplateImageUrl(String template, Set sizes) { + this.template = template; + this.sizes = sizes; + } + + @Override + public String getUrl(String id, int size) { + if (sizes.contains(size)) { + return buildUrl(template, id, size); + } + return null; + } + + @Override + public String toString() { + return String.format("%s [%s]", template, sizes); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TemplateImageUrl other = (TemplateImageUrl) obj; + if (!Objects.equals(this.template, other.template)) { + return false; + } + return Objects.equals(this.sizes, other.sizes); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + Objects.hashCode(this.template); + hash = 53 * hash + Objects.hashCode(this.sizes); + return hash; + } + + } + + public static class MapImageUrl extends ImageUrl { + + private final Map urls; + + private MapImageUrl(Map urls) { + this.urls = urls; + } + + @Override + public String getUrl(String id, int size) { + return urls.get(size); + } + + } + + public static class SingleImageUrl extends ImageUrl { + + private final String url; + + private SingleImageUrl(String url) { + this.url = url; + } + + @Override + public String getUrl(String id, int size) { + if (size == 1) { + return url; + } + return null; + } + + } + +} diff --git a/src/chatty/util/MathUtil.java b/src/chatty/util/MathUtil.java new file mode 100644 index 000000000..44bb546c5 --- /dev/null +++ b/src/chatty/util/MathUtil.java @@ -0,0 +1,22 @@ + +package chatty.util; + +/** + * + * @author tduva + */ +public class MathUtil { + + /** + * Division with integers with rounding up. Check if this has the desired + * behaviour for negative values if needed. + * + * @param a + * @param b + * @return + */ + public static int divRoundUp(int a, int b) { + return a / b + (a % b == 0 ? 0 : 1); + } + +} diff --git a/src/chatty/util/RawMessageTest.java b/src/chatty/util/RawMessageTest.java index ca80b4533..f7c5257c8 100644 --- a/src/chatty/util/RawMessageTest.java +++ b/src/chatty/util/RawMessageTest.java @@ -196,6 +196,12 @@ public static String simulateIRC(String channel, String parameters, String local if (type.equals("hypechat2")) { return "@badge-info=;badges=;color=#00FF7F;display-name=USERNAME;emotes=;first-msg=0;flags=;id=1234;mod=0;pinned-chat-paid-amount=7500;pinned-chat-paid-canonical-amount=7500;pinned-chat-paid-currency=KRW;pinned-chat-paid-exponent=0;pinned-chat-paid-is-system-message=1;pinned-chat-paid-level=TWO;returning-chatter=0;room-id=1234;subscriber=0;tmi-sent-ts=1687458800209;turbo=0;user-id=1234;user-type= :username!username@username.tmi.twitch.tv PRIVMSG "+channel+" :User sent Hype Chat"; } + if (type.equals("gigantified")) { + return "@badge-info=;badges=;color=#0000FF;display-name=Test;emotes=emotesv2_37fd23c055024b1e8dc15bd584dad6b2:3-10/emotesv2_37fd23c055024b1e8dc15bd584dad6b2:12-19;first-msg=0;flags=;id=123;mod=0;msg-id=gigantified-emote-message;returning-chatter=0;room-id=123;subscriber=0;tmi-sent-ts=123;turbo=0;user-id=123;user-type= :test!test@test.tmi.twitch.tv PRIVMSG "+channel+" :hi joshWave joshWave"; + } + if (type.equals("gigantified2")) { + return "@badge-info=;badges=vip/1;color=#0000FF;display-name=Test;emotes=65:0-7/30259:18-24;first-msg=0;flags=;id=1234;mod=0;msg-id=gigantified-emote-message;returning-chatter=0;room-id=1234;subscriber=0;tmi-sent-ts=1234;turbo=0;user-id=1234;user-type=;vip=1 :test!test@test.tmi.twitch.tv PRIVMSG "+channel+" :FrankerZ ZreknarF HeyGuys"; + } if (type.equals("custom")) { String[] parts = options.split("&"); String badges = parts[0]; diff --git a/src/chatty/util/StringUtil.java b/src/chatty/util/StringUtil.java index bd3863e22..2056ede15 100644 --- a/src/chatty/util/StringUtil.java +++ b/src/chatty/util/StringUtil.java @@ -821,6 +821,16 @@ public static String plural(String input, int num) { return input; } + public static String replaceLast(String string, String toReplace, String replacement) { + int pos = string.lastIndexOf(toReplace); + if (pos > -1) { + return string.substring(0, pos) + + replacement + + string.substring(pos + toReplace.length()); + } + return string; + } + public static final void main(String[] args) { System.out.println(shortenTo("abcdefghi", 8, 5)); System.out.println(concats("a", null, "b", null)); diff --git a/src/chatty/util/api/CheerEmoticon.java b/src/chatty/util/api/CheerEmoticon.java index 9d94420a4..ac31a1b63 100644 --- a/src/chatty/util/api/CheerEmoticon.java +++ b/src/chatty/util/api/CheerEmoticon.java @@ -39,7 +39,7 @@ public CheerEmoticonUrl(String url, String background, String type, String scale public static CheerEmoticon create(String prefix, int min_bits, Color color, Set urls, String stream) { - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, "(?i)"+prefix+"([0-9]+)", ""); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, "(?i)"+prefix+"([0-9]+)"); b.setSubType(Emoticon.SubType.CHEER); b.addInfo(Helper.formatViewerCount(min_bits)+" bits"); b.addStreamRestriction(stream); diff --git a/src/chatty/util/api/Emoticon.java b/src/chatty/util/api/Emoticon.java index 9dbae3849..7f3a7be0c 100644 --- a/src/chatty/util/api/Emoticon.java +++ b/src/chatty/util/api/Emoticon.java @@ -3,7 +3,9 @@ import chatty.Helper; import chatty.User; +import chatty.util.BTTVEmotes; import chatty.util.ImageCache.ImageResult; +import chatty.util.ImageUrl; import chatty.util.StringUtil; import chatty.util.api.CachedImage.ImageType; import java.awt.Dimension; @@ -19,8 +21,10 @@ import chatty.util.api.CachedImage.CachedImageUser; import chatty.util.seventv.WebPUtil; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; /** * A single emoticon, that contains a pattern, an URL to the image and @@ -106,11 +110,10 @@ public static enum SubType { public final String regex; public final String emoteset; private final Set streamRestrictions; - public final String url; + public final ImageUrl url; public final boolean literal; public final String stringId; public final String stringIdAlias; - public final String urlX2; public final String creator; private String stream; @@ -136,11 +139,11 @@ public static class Builder { private final Type type; private final String search; - private final String url; + private final Map urls = new HashMap<>(); + private ImageUrl imageUrl; private String regex; private SubType subtype; - private String urlX2; private int width = -1; private int height = -1; private boolean literal = false; @@ -155,10 +158,9 @@ public static class Builder { private boolean isAnimated = false; private boolean isZeroWidth = false; - public Builder(Type type, String search, String url) { + public Builder(Type type, String search) { this.type = type; this.search = search; - this.url = url; } public Builder setRegex(String regex) { @@ -212,8 +214,13 @@ public Builder setStringIdAlias(String id) { return this; } - public Builder setX2Url(String url) { - this.urlX2 = url; + public Builder addUrl(int scale, String url) { + urls.put(scale, Helper.checkHttpUrl(url)); + return this; + } + + public Builder setImageUrl(ImageUrl url) { + this.imageUrl = url; return this; } @@ -265,7 +272,7 @@ public Emoticon build() { */ public String getEmoteUrl(int factor, ImageType imageType) { if (type == Type.TWITCH || type == Type.CUSTOM2) { - if (stringId != null) { + if (stringId != null && (factor == 1 || factor == 2 || factor == 4)) { return getTwitchEmoteUrlById(stringId, factor, imageType); } } else if (type == Type.BTTV && stringId != null) { @@ -274,8 +281,8 @@ public String getEmoteUrl(int factor, ImageType imageType) { return getSevenTVEmoteUrl(stringId, factor); } else if (type == Type.FFZ) { return getFFZUrl(factor); - } else if (factor == 1) { - return url; + } else if (url != null) { + return url.getUrl(stringId, factor); } return null; } @@ -297,31 +304,41 @@ public static String getTwitchEmoteUrlById(String id, int factor, ImageType imag } public String getBttvEmoteUrl(String id, int factor) { - if (url == null || url.isEmpty()) { - return null; - } - String result = url; - result = result.replace("{{id}}", id); - result = result.replace("{{image}}", factor + "x"); - if (WebPUtil.shouldUseWebP()) { - result += ".webp"; + if (factor == 1 || factor == 2 || factor == 4) { + if (factor == 4) { + factor = 3; + } + String result = BTTVEmotes.TEMPLATE; + result = result.replace("{{id}}", id); + result = result.replace("{{image}}", factor + "x"); + if (WebPUtil.shouldUseWebP()) { + result += ".webp"; + } + return result; } - return result; + return null; } public String getFFZUrl(int factor) { - String gif = isAnimated() && !WebPUtil.shouldUseWebP() ? ".gif" : ""; - if (factor == 2 && urlX2 != null) { - return urlX2+gif; + if (url == null) { + return null; + } + String result = url.getUrl(stringId, factor); + if (result == null) { + return null; } - return url+gif; + String gif = isAnimated() && !WebPUtil.shouldUseWebP() ? ".gif" : ""; + return result+gif; } public String getSevenTVEmoteUrl(String id, int factor) { - if (StringUtil.isNullOrEmpty(id) || StringUtil.isNullOrEmpty(url) || factor > 4) { + if (StringUtil.isNullOrEmpty(id) || url == null || factor > 4) { + return null; + } + String result = url.getUrl(id, factor); + if (result == null) { return null; } - String result = url.replace("{size}", factor+"x"); if (!WebPUtil.shouldUseWebP()) { if (isAnimated()) { result = result.replace(".webp", ".gif"); @@ -354,8 +371,15 @@ protected Emoticon(Builder builder) { this.type = builder.type; this.emoteset = builder.emoteset; - this.url = Helper.checkHttpUrl(builder.url); - this.urlX2 = Helper.checkHttpUrl(builder.urlX2); + if (builder.imageUrl != null) { + this.url = builder.imageUrl; + } + else if (!builder.urls.isEmpty()) { + this.url = new ImageUrl.Builder(builder.stringId, builder.urls).build(); + } + else { + this.url = null; + } int width = builder.width; int height = builder.height; diff --git a/src/chatty/util/api/EmoticonParsing.java b/src/chatty/util/api/EmoticonParsing.java index a841ce8f5..722f12c61 100644 --- a/src/chatty/util/api/EmoticonParsing.java +++ b/src/chatty/util/api/EmoticonParsing.java @@ -48,7 +48,7 @@ protected static EmoticonUpdate parseEmoteList(String json, EmoticonUpdate.Sourc // Channel emotes API doesn't include owner_id owner_id = streamId; } - Emoticon.Builder builder = new Emoticon.Builder(Emoticon.Type.TWITCH, code, null); + Emoticon.Builder builder = new Emoticon.Builder(Emoticon.Type.TWITCH, code); builder.setStringId(id); builder.setEmoteset(set); builder.setStream(streamName); diff --git a/src/chatty/util/api/Emoticons.java b/src/chatty/util/api/Emoticons.java index 20668dad4..57d075ec3 100644 --- a/src/chatty/util/api/Emoticons.java +++ b/src/chatty/util/api/Emoticons.java @@ -1109,7 +1109,8 @@ private boolean loadCustomEmote(String line) { // With code and URL found we can add the emote, other stuff is optional if (code != null && url != null) { - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.CUSTOM, code, url); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.CUSTOM, code); + b.addUrl(1, url); b.setLiteral(literal).setEmoteset(emoteset); b.setStringId(id); if (size != null) { @@ -1218,7 +1219,7 @@ public static void main(String[] args) { } private static Emoticon testBuild(String code) { - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, code, ""); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.TWITCH, code); return b.build(); } @@ -1258,6 +1259,14 @@ public int hashCode() { hash = 37 * hash + Objects.hashCode(this.emotes); return hash; } + + public int getLargestIndex() { + if (emotes == null || emotes.isEmpty()) { + return -1; + } + return Collections.max(emotes.keySet()); + } + } /** diff --git a/src/chatty/util/api/IgnoredEmotes.java b/src/chatty/util/api/IgnoredEmotes.java index f9658ab41..606ec3e81 100644 --- a/src/chatty/util/api/IgnoredEmotes.java +++ b/src/chatty/util/api/IgnoredEmotes.java @@ -240,7 +240,7 @@ public int hashCode() { public static void main(String[] args) { Item item = Item.parse("abc type:ffz in:c"); System.out.println(item); - Emoticon emote = (new Emoticon.Builder(Emoticon.Type.FFZ, "abc", null)).build(); + Emoticon emote = (new Emoticon.Builder(Emoticon.Type.FFZ, "abc")).build(); System.out.println(item.matches(emote, CHAT)); System.out.println(Item.create(emote, 0)); } diff --git a/src/chatty/util/api/LocalEmotesSetting.java b/src/chatty/util/api/LocalEmotesSetting.java index 6a120cd18..36551492f 100644 --- a/src/chatty/util/api/LocalEmotesSetting.java +++ b/src/chatty/util/api/LocalEmotesSetting.java @@ -43,7 +43,7 @@ public Object fromObject(Emoticon emote) { } private Emoticon createEmote(String id, String code) { - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.CUSTOM2, code, null); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.CUSTOM2, code); b.setStringId(id); return b.build(); } diff --git a/src/chatty/util/api/usericons/Usericon.java b/src/chatty/util/api/usericons/Usericon.java index 8503aef55..902260d0d 100644 --- a/src/chatty/util/api/usericons/Usericon.java +++ b/src/chatty/util/api/usericons/Usericon.java @@ -422,7 +422,10 @@ public String getImageUrl(int scale, CachedImage.ImageType type) { if (scale == 2) { return url2 != null ? url2.toString() : null; } - return url.toString(); + else if (scale == 1) { + return url.toString(); + } + return null; } @Override diff --git a/src/chatty/util/ffz/FrankerFaceZParsing.java b/src/chatty/util/ffz/FrankerFaceZParsing.java index 8fa76dd19..488b33be9 100644 --- a/src/chatty/util/ffz/FrankerFaceZParsing.java +++ b/src/chatty/util/ffz/FrankerFaceZParsing.java @@ -198,8 +198,6 @@ public static Emoticon parseEmote(JSONObject emote, String streamRestriction, urls = (JSONObject)emote.get("animated"); isAnimated = true; } - String url1 = (String)urls.get("1"); - String url2 = (String)urls.get("2"); int id = ((Number)emote.get("id")).intValue(); @@ -214,12 +212,13 @@ public static Emoticon parseEmote(JSONObject emote, String streamRestriction, if (code == null || code.isEmpty()) { return null; } - if (url1 == null || url1.isEmpty()) { - return null; - } - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.FFZ, code, url1); - b.setX2Url(url2); + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.FFZ, code); + for (Object key : urls.keySet()) { + int scale = Integer.parseInt((String) key); + String url = (String) urls.get(key); + b.addUrl(scale, url); + } b.setSize(width, height); b.setCreator(creator); b.setStringId(String.valueOf(id)); diff --git a/src/chatty/util/irc/MsgTags.java b/src/chatty/util/irc/MsgTags.java index e1aafff8a..6f8cf022d 100644 --- a/src/chatty/util/irc/MsgTags.java +++ b/src/chatty/util/irc/MsgTags.java @@ -112,6 +112,23 @@ public String getHypeChatInfo() { getHypeChatAmountText()); } + public String getPowerUpInfo() { + if (hasValue("msg-id")) { + switch (get("msg-id")) { + case "gigantified-emote-message": + return "Gigantified Emote"; + case "animated-message": + return String.format("Styled Message (%s)", + StringUtil.shortenTo(get("animation-id"), 20)); + } + } + return null; + } + + public boolean hasGigantifiedEmote() { + return isValue("msg-id", "gigantified-emote-message"); + } + //================ // Factory Methods //================ diff --git a/src/chatty/util/seventv/SevenTV.java b/src/chatty/util/seventv/SevenTV.java index 854a6b538..f6589920d 100644 --- a/src/chatty/util/seventv/SevenTV.java +++ b/src/chatty/util/seventv/SevenTV.java @@ -12,7 +12,9 @@ import chatty.util.api.Emoticon; import chatty.util.api.EmoticonUpdate; import chatty.util.api.TwitchApi; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.logging.Logger; import org.json.simple.JSONArray; @@ -157,15 +159,24 @@ private Emoticon parseEmote(String stream, JSONObject emoteObject) { boolean animated = JSONUtil.getBoolean(data, "animated", false); JSONObject host = (JSONObject) data.get("host"); - File file = getFile(host); - if (file == null) { + List files = getFiles(host); + if (files == null) { LOGGER.warning("SevenTV emote: No file found"); return null; } - int width = file.width; - int height = file.height; - Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.SEVENTV, code, file.url); + int width = Integer.MAX_VALUE; + int height = Integer.MAX_VALUE; + + Emoticon.Builder b = new Emoticon.Builder(Emoticon.Type.SEVENTV, code); + for (File file : files) { + width = Integer.min(width, file.width); + height = Integer.min(height, file.height); + } + for (File file : files) { + int scale = file.width / width; + b.addUrl(scale, file.url); + } b.setSize(width, height); b.setStringId(id); b.setAnimated(animated); @@ -183,11 +194,12 @@ private Emoticon parseEmote(String stream, JSONObject emoteObject) { } } - private File getFile(JSONObject host) { + private List getFiles(JSONObject host) { String baseUrl = JSONUtil.getString(host, "url"); if (baseUrl == null) { return null; } + List result = new ArrayList<>(); JSONArray files = (JSONArray) host.get("files"); for (Object o : files) { JSONObject file = (JSONObject) o; @@ -195,12 +207,10 @@ private File getFile(JSONObject host) { String name = (String) file.get("name"); int width = JSONUtil.getInteger(file, "width", -1); int height = JSONUtil.getInteger(file, "height", -1); - if (name.contains("1x") && width != -1 && height != -1) { - return new File(baseUrl+"/"+name.replace("1x", "{size}"), width, height); - } + result.add(new File(baseUrl+"/"+name, width, height)); } } - return null; + return result; } private static class File { diff --git a/test/chatty/util/ImageCacheTest.java b/test/chatty/util/ImageCacheTest.java new file mode 100644 index 000000000..c5d387c06 --- /dev/null +++ b/test/chatty/util/ImageCacheTest.java @@ -0,0 +1,86 @@ + +package chatty.util; + +import chatty.util.ImageCache.ImageRequest; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author tduva + */ +public class ImageCacheTest { + + @Test + public void testImageRequest() { + testUrlFactor(1, 0, 28, 28, + new int[]{1, 2, 4}, + 1, + new int[]{1}); + + testUrlFactor(1.1f, 0, 28, 28, + new int[]{1, 2, 4}, + 2, + new int[]{2}); + + testUrlFactor(1.1f, 0, 28, 28, + new int[]{1, 4}, + 4, + new int[]{2, 3, 4}); + + testUrlFactor(1.1f, 0, 28, 28, + new int[]{1, 2}, + 2, + new int[]{2}); + + testUrlFactor(1.1f, 20, 28, 28, + new int[]{1, 2, 4}, + 1, + new int[]{1}); + + testUrlFactor(2.1f, 0, 28, 28, + new int[]{1, 2, 3, 4}, + 3, + new int[]{3}); + + testUrlFactor(2.1f, 0, 28, 28, + new int[]{1, 2, 4}, + 4, + new int[]{3, 4}); + + testUrlFactor(0.9f, 0, 28, 28, + new int[]{1, 2, 4}, + 1, + new int[]{1}); + + testUrlFactor(0.8f, 0, 28, 28, + new int[]{2, 4}, + 2, + new int[]{1, 2}); + } + + private static void testUrlFactor(float scaleFactor, int maxHeight, int defaultWidth, int defaultHeight, int[] availableFactors, int expectedUrlFactor, int[] expectedTries) { + List tries = new ArrayList<>(); + ImageRequest r = new ImageCache.ImageRequest(factor -> { + tries.add(factor); + for (int i = 0; i < availableFactors.length; i++) { + if (availableFactors[i] == factor) { + return String.valueOf(factor); + } + } + return null; + }, scaleFactor, maxHeight, new Dimension(defaultWidth, defaultHeight), false); + Assert.assertEquals(expectedUrlFactor, r.urlFactor); + int[] tries2 = new int[tries.size()]; + for (int i=0;i urls = new HashMap<>(); + for (int i = 0; i < params.length; i += 2) { + urls.put(Integer.valueOf(params[i]), params[i+1]); + } + ImageUrl.Builder b = ImageUrl.builder(id); + urls.forEach((size, url) -> b.addUrl(size, url)); + ImageUrl url = b.build(); + for (Map.Entry entry : urls.entrySet()) { + assertEquals(entry.getValue(), url.getUrl(id, entry.getKey())); + } + for (int i = 0; i < 10; i++) { + assertEquals(urls.get(i), url.getUrl(id, i)); + } + return url; + } + +}