From 437a1a22867d324ad1cb491bfa66a877af655272 Mon Sep 17 00:00:00 2001 From: Alexsuperfly Date: Tue, 26 Nov 2019 19:59:28 -0500 Subject: [PATCH] move capturing of screenshots to ImageCapture utility Extracts the logic for caturing/saving/uploading images from the screenshot plugin to a separate utility named ImageCapture. The Imgur class were made inner classes of ImageCapture since the only location they see use is there. ImageUploadStyle and TransferableBufferedImage were also moved to ulities to allow ImageCapture and other plugins to use them. --- .../runelite/client/RuneLiteProperties.java | 6 + .../plugins/screenshot/ScreenshotConfig.java | 5 +- .../plugins/screenshot/ScreenshotPlugin.java | 172 +----------- .../screenshot/imgur/ImageUploadRequest.java | 44 ---- .../screenshot/imgur/ImageUploadResponse.java | 40 --- .../runelite/client/util/ImageCapture.java | 244 ++++++++++++++++++ .../ImageUploadStyle.java} | 4 +- .../TransferableBufferedImage.java | 2 +- .../net/runelite/client/runelite.properties | 3 +- 9 files changed, 271 insertions(+), 249 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadRequest.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadResponse.java create mode 100644 runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java rename runelite-client/src/main/java/net/runelite/client/{plugins/screenshot/UploadStyle.java => util/ImageUploadStyle.java} (95%) rename runelite-client/src/main/java/net/runelite/client/{plugins/screenshot => util}/TransferableBufferedImage.java (97%) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index 6408be3522..83e30cbfc7 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -48,6 +48,7 @@ public class RuneLiteProperties private static final String JAV_CONFIG_BACKUP = "runelite.jav_config_backup"; private static final String PLUGINHUB_BASE = "runelite.pluginhub.url"; private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; + private static final String IMGUR_CLIENT_ID = "runelite.imgur.client.id"; private static final Properties properties = new Properties(); @@ -139,4 +140,9 @@ public class RuneLiteProperties String version = System.getProperty(PLUGINHUB_VERSION, properties.getProperty(PLUGINHUB_VERSION)); return HttpUrl.parse(properties.get(PLUGINHUB_BASE) + "/" + version); } + + public static String getImgurClientId() + { + return properties.getProperty(IMGUR_CLIENT_ID); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java index 2158de75e3..314af4ab07 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java @@ -28,6 +28,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.Keybind; +import net.runelite.client.util.ImageUploadStyle; @ConfigGroup("screenshot") public interface ScreenshotConfig extends Config @@ -115,9 +116,9 @@ public interface ScreenshotConfig extends Config description = "Configures whether or not screenshots are uploaded to Imgur, or placed on your clipboard", position = 7 ) - default UploadStyle uploadScreenshot() + default ImageUploadStyle uploadScreenshot() { - return UploadStyle.NEITHER; + return ImageUploadStyle.NEITHER; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index 2867e4d9f6..e5bdce4414 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -31,26 +31,14 @@ import com.google.inject.Provides; import java.awt.Desktop; import java.awt.Graphics; import java.awt.Image; -import java.awt.Toolkit; -import java.awt.TrayIcon; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; import java.awt.image.BufferedImage; -import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.time.LocalDate; -import java.util.Date; -import java.util.EnumSet; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.imageio.ImageIO; import javax.inject.Inject; import javax.swing.SwingUtilities; import lombok.AccessLevel; @@ -62,7 +50,6 @@ import net.runelite.api.GameState; import net.runelite.api.Player; import net.runelite.api.Point; import net.runelite.api.SpriteID; -import net.runelite.api.WorldType; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.GameTick; import net.runelite.api.events.PlayerDeath; @@ -86,24 +73,15 @@ import net.runelite.client.game.SpriteManager; import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest; -import net.runelite.client.plugins.screenshot.imgur.ImageUploadResponse; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.DrawManager; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.util.HotkeyListener; +import net.runelite.client.util.ImageCapture; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.Text; -import net.runelite.http.api.RuneLiteAPI; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; @PluginDescriptor( name = "Screenshot", @@ -113,12 +91,6 @@ import okhttp3.Response; @Slf4j public class ScreenshotPlugin extends Plugin { - private static final String IMGUR_CLIENT_ID = "30d71e5f6860809"; - private static final HttpUrl IMGUR_IMAGE_UPLOAD_URL = HttpUrl.parse("https://api.imgur.com/3/image"); - private static final MediaType JSON = MediaType.parse("application/json"); - - private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); - private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); private static final Pattern LEVEL_UP_PATTERN = Pattern.compile(".*Your ([a-zA-Z]+) (?:level is|are)? now (\\d+)\\."); private static final Pattern BOSSKILL_MESSAGE_PATTERN = Pattern.compile("Your (.+) kill count is: (\\d+)."); @@ -129,14 +101,6 @@ public class ScreenshotPlugin extends Plugin "You feel something weird sneaking into your backpack", "You have a funny feeling like you would have been followed"); - static String format(Date date) - { - synchronized (TIME_FORMAT) - { - return TIME_FORMAT.format(date); - } - } - private String clueType; private Integer clueNumber; @@ -183,6 +147,9 @@ public class ScreenshotPlugin extends Plugin @Inject private SpriteManager spriteManager; + @Inject + private ImageCapture imageCapture; + @Getter(AccessLevel.PACKAGE) private BufferedImage reportButton; @@ -193,7 +160,7 @@ public class ScreenshotPlugin extends Plugin @Override public void hotkeyPressed() { - takeScreenshot(format(new Date())); + takeScreenshot(""); } }; @@ -216,7 +183,7 @@ public class ScreenshotPlugin extends Plugin .tab(false) .tooltip("Take screenshot") .icon(iconImage) - .onClick(() -> takeScreenshot(format(new Date()))) + .onClick(() -> takeScreenshot("")) .popup(ImmutableMap .builder() .put("Open screenshot folder...", () -> @@ -284,11 +251,11 @@ public class ScreenshotPlugin extends Plugin Player player = playerDeath.getPlayer(); if (player == client.getLocalPlayer() && config.screenshotPlayerDeath()) { - takeScreenshot("Death " + format(new Date())); + takeScreenshot("Death"); } else if ((player.isClanMember() || player.isFriend()) && config.screenshotFriendDeath() && player.getCanvasTilePoly() != null) { - takeScreenshot("Death " + player.getName() + " " + format(new Date())); + takeScreenshot("Death " + player.getName()); } } @@ -299,7 +266,7 @@ public class ScreenshotPlugin extends Plugin { final Player player = playerLootReceived.getPlayer(); final String name = player.getName(); - String fileName = "Kill " + name + " " + format(new Date()); + String fileName = "Kill " + name; takeScreenshot(fileName); } } @@ -367,7 +334,7 @@ public class ScreenshotPlugin extends Plugin if (config.screenshotPet() && PET_MESSAGES.stream().anyMatch(chatMessage::contains)) { - String fileName = "Pet " + format(new Date()); + String fileName = "Pet"; takeScreenshot(fileName); } @@ -389,7 +356,7 @@ public class ScreenshotPlugin extends Plugin if (m.matches()) { String valuableDropName = m.group(1); - String fileName = "Valuable drop " + valuableDropName + " " + format(new Date()); + String fileName = "Valuable drop " + valuableDropName; takeScreenshot(fileName); } } @@ -400,7 +367,7 @@ public class ScreenshotPlugin extends Plugin if (m.matches()) { String untradeableDropName = m.group(1); - String fileName = "Untradeable drop " + untradeableDropName + " " + format(new Date()); + String fileName = "Untradeable drop " + untradeableDropName; takeScreenshot(fileName); } } @@ -615,120 +582,7 @@ public class ScreenshotPlugin extends Plugin // Draw the game onto the screenshot graphics.drawImage(image, gameOffsetX, gameOffsetY, null); - - File playerFolder; - if (client.getLocalPlayer() != null && client.getLocalPlayer().getName() != null) - { - final EnumSet worldTypes = client.getWorldType(); - - String playerDir = client.getLocalPlayer().getName(); - if (worldTypes.contains(WorldType.DEADMAN)) - { - playerDir += "-Deadman"; - } - else if (worldTypes.contains(WorldType.LEAGUE)) - { - playerDir += "-League"; - } - playerFolder = new File(SCREENSHOT_DIR, playerDir); - } - else - { - playerFolder = SCREENSHOT_DIR; - } - - playerFolder.mkdirs(); - - try - { - File screenshotFile = new File(playerFolder, fileName + ".png"); - - // To make sure that screenshots don't get overwritten, check if file exists, - // and if it does create file with same name and suffix. - int i = 1; - while (screenshotFile.exists()) - { - screenshotFile = new File(playerFolder, fileName + String.format("(%d)", i++) + ".png"); - } - - ImageIO.write(screenshot, "PNG", screenshotFile); - UploadStyle uploadStyle = config.uploadScreenshot(); - - if (uploadStyle == UploadStyle.IMGUR) - { - uploadScreenshot(screenshotFile); - } - else if (uploadStyle == UploadStyle.CLIPBOARD) - { - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - TransferableBufferedImage transferableBufferedImage = new TransferableBufferedImage(screenshot); - clipboard.setContents(transferableBufferedImage, null); - - if (config.notifyWhenTaken()) - { - notifier.notify("A screenshot was saved and inserted into your clipboard!", TrayIcon.MessageType.INFO); - } - } - else if (config.notifyWhenTaken()) - { - notifier.notify("A screenshot was saved to " + screenshotFile, TrayIcon.MessageType.INFO); - } - } - catch (IOException ex) - { - log.warn("error writing screenshot", ex); - } - } - - /** - * Uploads a screenshot to the Imgur image-hosting service, - * and copies the image link to the clipboard. - * - * @param screenshotFile Image file to upload. - * @throws IOException Thrown if the file cannot be read. - */ - private void uploadScreenshot(File screenshotFile) throws IOException - { - String json = RuneLiteAPI.GSON.toJson(new ImageUploadRequest(screenshotFile)); - - Request request = new Request.Builder() - .url(IMGUR_IMAGE_UPLOAD_URL) - .addHeader("Authorization", "Client-ID " + IMGUR_CLIENT_ID) - .post(RequestBody.create(JSON, json)) - .build(); - - RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback() - { - @Override - public void onFailure(Call call, IOException ex) - { - log.warn("error uploading screenshot", ex); - } - - @Override - public void onResponse(Call call, Response response) throws IOException - { - try (InputStream in = response.body().byteStream()) - { - ImageUploadResponse imageUploadResponse = RuneLiteAPI.GSON - .fromJson(new InputStreamReader(in), ImageUploadResponse.class); - - if (imageUploadResponse.isSuccess()) - { - String link = imageUploadResponse.getData().getLink(); - - StringSelection selection = new StringSelection(link); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(selection, selection); - - if (config.notifyWhenTaken()) - { - notifier.notify("A screenshot was uploaded and inserted into your clipboard!", TrayIcon.MessageType.INFO); - } - } - } - } - }); + imageCapture.takeScreenshot(screenshot, fileName, config.notifyWhenTaken(), config.uploadScreenshot()); } @VisibleForTesting diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadRequest.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadRequest.java deleted file mode 100644 index 28c8d4f2f3..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadRequest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, Lotto - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.screenshot.imgur; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.Base64; -import lombok.Data; - -@Data -public class ImageUploadRequest -{ - private final String image; - private final String type; - - public ImageUploadRequest(File imageFile) throws IOException - { - this.image = Base64.getEncoder().encodeToString(Files.readAllBytes(imageFile.toPath())); - this.type = "base64"; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadResponse.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadResponse.java deleted file mode 100644 index 820ed2aded..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/imgur/ImageUploadResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018, Lotto - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.screenshot.imgur; - -import lombok.Data; - -@Data -public class ImageUploadResponse -{ - private Data data; - private boolean success; - - @lombok.Data - public static class Data - { - private String link; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java new file mode 100644 index 0000000000..0331d6a50e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2018, Lotto + * Copyright (c) 2019, Alexsuperfly + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.util; + +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Base64; +import java.util.Date; +import java.util.EnumSet; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.WorldType; +import net.runelite.client.Notifier; +import static net.runelite.client.RuneLite.SCREENSHOT_DIR; +import net.runelite.client.RuneLiteProperties; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +@Slf4j +@Singleton +public class ImageCapture +{ + private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); + private static final HttpUrl IMGUR_IMAGE_UPLOAD_URL = HttpUrl.parse("https://api.imgur.com/3/image"); + private static final MediaType JSON = MediaType.parse("application/json"); + + @Inject + private Client client; + + @Inject + private Notifier notifier; + + /** + * Saves a screenshot of the client window to the screenshot folder as a PNG, + * and optionally uploads it to an image-hosting service. + * + * @param screenshot BufferedImage to capture. + * @param fileName Filename to use, without file extension. + * @param notify Send a notification to the system tray when the image is captured. + * @param imageUploadStyle which method to use to upload the screenshot (Imgur or directly to clipboard). + */ + public void takeScreenshot(BufferedImage screenshot, String fileName, boolean notify, ImageUploadStyle imageUploadStyle) + { + if (client.getGameState() == GameState.LOGIN_SCREEN) + { + // Prevent the screenshot from being captured + log.info("Login screenshot prevented"); + return; + } + + File playerFolder; + if (client.getLocalPlayer() != null && client.getLocalPlayer().getName() != null) + { + final EnumSet worldTypes = client.getWorldType(); + + String playerDir = client.getLocalPlayer().getName(); + if (worldTypes.contains(WorldType.DEADMAN)) + { + playerDir += "-Deadman"; + } + else if (worldTypes.contains(WorldType.LEAGUE)) + { + playerDir += "-League"; + } + playerFolder = new File(SCREENSHOT_DIR, playerDir); + } + else + { + playerFolder = SCREENSHOT_DIR; + } + + playerFolder.mkdirs(); + + fileName += " " + format(new Date()); + + try + { + File screenshotFile = new File(playerFolder, fileName + ".png"); + + // To make sure that screenshots don't get overwritten, check if file exists, + // and if it does create file with same name and suffix. + int i = 1; + while (screenshotFile.exists()) + { + screenshotFile = new File(playerFolder, fileName + String.format("(%d)", i++) + ".png"); + } + + ImageIO.write(screenshot, "PNG", screenshotFile); + + if (imageUploadStyle == ImageUploadStyle.IMGUR) + { + uploadScreenshot(screenshotFile, notify); + } + else if (imageUploadStyle == ImageUploadStyle.CLIPBOARD) + { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + TransferableBufferedImage transferableBufferedImage = new TransferableBufferedImage(screenshot); + clipboard.setContents(transferableBufferedImage, null); + + if (notify) + { + notifier.notify("A screenshot was saved and inserted into your clipboard!", TrayIcon.MessageType.INFO); + } + } + else if (notify) + { + notifier.notify("A screenshot was saved to " + screenshotFile, TrayIcon.MessageType.INFO); + } + } + catch (IOException ex) + { + log.warn("error writing screenshot", ex); + } + } + + /** + * Uploads a screenshot to the Imgur image-hosting service, + * and copies the image link to the clipboard. + * + * @param screenshotFile Image file to upload. + * @throws IOException Thrown if the file cannot be read. + */ + private void uploadScreenshot(File screenshotFile, boolean notify) throws IOException + { + String json = RuneLiteAPI.GSON.toJson(new ImageUploadRequest(screenshotFile)); + + Request request = new Request.Builder() + .url(IMGUR_IMAGE_UPLOAD_URL) + .addHeader("Authorization", "Client-ID " + RuneLiteProperties.getImgurClientId()) + .post(RequestBody.create(JSON, json)) + .build(); + + RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException ex) + { + log.warn("error uploading screenshot", ex); + } + + @Override + public void onResponse(Call call, Response response) throws IOException + { + try (InputStream in = response.body().byteStream()) + { + ImageUploadResponse imageUploadResponse = RuneLiteAPI.GSON + .fromJson(new InputStreamReader(in), ImageUploadResponse.class); + + if (imageUploadResponse.isSuccess()) + { + String link = imageUploadResponse.getData().getLink(); + + StringSelection selection = new StringSelection(link); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(selection, selection); + + if (notify) + { + notifier.notify("A screenshot was uploaded and inserted into your clipboard!", TrayIcon.MessageType.INFO); + } + } + } + } + }); + } + + private static String format(Date date) + { + synchronized (TIME_FORMAT) + { + return TIME_FORMAT.format(date); + } + } + + @Data + private static class ImageUploadResponse + { + private Data data; + private boolean success; + + @lombok.Data + private static class Data + { + private String link; + } + } + + @Data + private static class ImageUploadRequest + { + private final String image; + private final String type; + + ImageUploadRequest(File imageFile) throws IOException + { + this.image = Base64.getEncoder().encodeToString(Files.readAllBytes(imageFile.toPath())); + this.type = "base64"; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/UploadStyle.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java similarity index 95% rename from runelite-client/src/main/java/net/runelite/client/plugins/screenshot/UploadStyle.java rename to runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java index fd0594e7ba..95c75536a4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/UploadStyle.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java @@ -23,9 +23,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.plugins.screenshot; +package net.runelite.client.util; -public enum UploadStyle +public enum ImageUploadStyle { NEITHER, IMGUR, diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/TransferableBufferedImage.java b/runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java similarity index 97% rename from runelite-client/src/main/java/net/runelite/client/plugins/screenshot/TransferableBufferedImage.java rename to runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java index 27b4ddd154..f87e52564b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/TransferableBufferedImage.java +++ b/runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java @@ -24,7 +24,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.plugins.screenshot; +package net.runelite.client.util; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index e6ad242cdf..801f0cc406 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -12,4 +12,5 @@ runelite.dnschange.link=https://1.1.1.1/dns/ runelite.jav_config=http://oldschool.runescape.com/jav_config.ws runelite.jav_config_backup=http://static.runelite.net/jav_config.ws runelite.pluginhub.url=https://repo.runelite.net/plugins -runelite.pluginhub.version=${project.version} \ No newline at end of file +runelite.pluginhub.version=${project.version} +runelite.imgur.client.id=30d71e5f6860809 \ No newline at end of file