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;
+ }
+
+}
|