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.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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: <col=ff0000>(\\d+)</col>.");
|
||||
@@ -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
|
||||
.<String, Runnable>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<WorldType> 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
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* Copyright (c) 2019, Alexsuperfly <https://github.com/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<WorldType> 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
@@ -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}
|
||||
runelite.pluginhub.version=${project.version}
|
||||
runelite.imgur.client.id=30d71e5f6860809
|
||||
Reference in New Issue
Block a user