From bcc8a14555479f8980ad7876031511dd58181e1c Mon Sep 17 00:00:00 2001 From: vlOd Date: Sat, 25 Feb 2023 16:44:44 +0200 Subject: [PATCH] Release v2.0 code --- .../me/vlod/lightshotscraper/Delegate.java | 11 + .../lightshotscraper/LightshotScraper.java | 527 +++++++++++------- src/src/me/vlod/lightshotscraper/Utils.java | 219 ++++++++ .../lightshotscraper/console/Console.java | 161 ++++++ .../console/ConsoleContextMenu.java | 38 ++ .../console/ConsoleInputContextMenu.java | 68 +++ .../lightshotscraper/logger/LogLevel.java | 10 + .../vlod/lightshotscraper/logger/Logger.java | 71 +++ 8 files changed, 903 insertions(+), 202 deletions(-) create mode 100644 src/src/me/vlod/lightshotscraper/Delegate.java create mode 100644 src/src/me/vlod/lightshotscraper/Utils.java create mode 100644 src/src/me/vlod/lightshotscraper/console/Console.java create mode 100644 src/src/me/vlod/lightshotscraper/console/ConsoleContextMenu.java create mode 100644 src/src/me/vlod/lightshotscraper/console/ConsoleInputContextMenu.java create mode 100644 src/src/me/vlod/lightshotscraper/logger/LogLevel.java create mode 100644 src/src/me/vlod/lightshotscraper/logger/Logger.java diff --git a/src/src/me/vlod/lightshotscraper/Delegate.java b/src/src/me/vlod/lightshotscraper/Delegate.java new file mode 100644 index 0000000..e909e67 --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/Delegate.java @@ -0,0 +1,11 @@ +package me.vlod.lightshotscraper; + + +public interface Delegate { + public static final Delegate empty = new Delegate() { + @Override + public void call(Object... args) { + } + }; + public void call(Object... args); +} diff --git a/src/src/me/vlod/lightshotscraper/LightshotScraper.java b/src/src/me/vlod/lightshotscraper/LightshotScraper.java index e345c44..5ac2dc3 100644 --- a/src/src/me/vlod/lightshotscraper/LightshotScraper.java +++ b/src/src/me/vlod/lightshotscraper/LightshotScraper.java @@ -1,14 +1,15 @@ package me.vlod.lightshotscraper; +import java.awt.Color; +import java.awt.GraphicsEnvironment; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; import java.util.Scanner; import java.util.concurrent.ThreadLocalRandom; -import java.util.regex.Matcher; -import java.util.regex.Pattern; + +import javax.swing.JOptionPane; import org.jsoup.Connection.Response; import org.jsoup.HttpStatusException; @@ -16,63 +17,81 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import me.vlod.lightshotscraper.console.Console; +import me.vlod.lightshotscraper.logger.Logger; + public class LightshotScraper implements Runnable { public static LightshotScraper instance; + public static Logger logger; public boolean running; + public Console console; public String outputFolderBase; public boolean noSaving; public boolean noDownload; - - /** - * Splits a string by spaces, ignoring quotes
- * From https://stackoverflow.com/a/366532 - * - * @param str the string to split - * @return the result or empty array - */ - public static String[] splitBySpace(String str) { - try { - List matchList = new ArrayList(); - Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); - Matcher regexMatcher = regex.matcher(str); - - while (regexMatcher.find()) { - if (regexMatcher.group(1) != null) { - // Add double-quoted string without the quotes - matchList.add(regexMatcher.group(1)); - } else if (regexMatcher.group(2) != null) { - // Add single-quoted string without the quotes - matchList.add(regexMatcher.group(2)); - } else { - // Add unquoted word - matchList.add(regexMatcher.group()); - } - } - - return matchList.toArray(new String[0]); - } catch (Exception ex) { - ex.printStackTrace(); - return new String[0]; - } + public String linkIDFormat = "cciiii"; + + static { + // Logger setup + logger = new Logger(); + + // Console target + logger.targets.add(new Delegate() { + @Override + public void call(Object... args) { + String str = (String) args[0]; + Color color = (Color) args[1]; + + if (instance != null && instance.console != null) { + instance.console.write(str, color); + } + } + }); + + // Stdout and Stderr target + logger.targets.add(new Delegate() { + @Override + public void call(Object... args) { + String str = (String) args[0]; + Color color = (Color) args[1]; + + if (color != Color.red) { + System.out.println(str); + } else { + System.err.println(str); + } + } + }); } public String[] generateLinks(int amount) { String chars = "abcdefghijklmnoprqstuwvxyz"; + String bigChars = chars.toUpperCase(); ArrayList links = new ArrayList(); ThreadLocalRandom random = ThreadLocalRandom.current(); for (int i = 0; i < amount; i++) { String link = "https://prnt.sc/"; - for (int j = 0; j < 2; j++) { - link += chars.charAt(random.nextInt(0, chars.length() - 1)); + for (char chr : this.linkIDFormat.toCharArray()) { + if (chr == 'c') { + link += chars.charAt(random.nextInt(0, chars.length())); + } else if (chr == 'C') { + link += bigChars.charAt(random.nextInt(0, bigChars.length())); + } else if (chr == 'i') { + link += "" + random.nextInt(0, 10); + } else if (chr == 'r') { + int chance = random.nextInt(0, 3); + + if (chance == 0) { + link += bigChars.charAt(random.nextInt(0, bigChars.length())); + } else if (chance == 1) { + link += chars.charAt(random.nextInt(0, chars.length())); + } else { + link += "" + random.nextInt(0, 10); + } + } } - link += "" + random.nextInt(0, 9); - link += "" + random.nextInt(0, 9); - link += "" + random.nextInt(0, 9); - link += "" + random.nextInt(0, 9); - links.add(link); } @@ -88,185 +107,289 @@ public void downloadImage(String url, String outputFilePath) { } catch (Exception ex) { if (ex instanceof HttpStatusException && ((HttpStatusException)ex).getStatusCode() == 404) { - System.err.println(String.format("Link \"%s\" doesn't exist!", url)); + logger.error(String.format("Link \"%s\" doesn't exist!", url)); } else { - System.err.println(String.format("Unable to download the image from the link \"%s\"!", url)); + logger.error(String.format("Unable to download the image from the link \"%s\"!", url)); ex.printStackTrace(); } } } - @Override - public void run() { - this.running = true; - Scanner inputScanner = new Scanner(System.in); + public void handleInput(String input) { + input = input.trim(); + if (input.length() < 1) { + if (this.console != null) { + JOptionPane.showMessageDialog(null, "Invalid input specified!", "LightshotScraper - Error", + JOptionPane.ERROR_MESSAGE | JOptionPane.OK_OPTION); + } else { + logger.error("Invalid input specified!"); + } + + return; + } - System.out.println("Welcome to LightshotScraper!"); - System.out.println("Type \"help\" for more information"); - System.out.println(""); + String[] inputSplitted = Utils.splitBySpace(input); + String cmd = inputSplitted[0]; + String[] arguments = new String[inputSplitted.length - 1]; + System.arraycopy(inputSplitted, 1, arguments, 0, arguments.length); - while (this.running) { - System.out.print("> "); - - String input = inputScanner.nextLine(); - String[] inputSplitted = LightshotScraper.splitBySpace(input); - - String cmd = inputSplitted[0]; - String[] arguments = new String[inputSplitted.length - 1]; - System.arraycopy(inputSplitted, 1, arguments, 0, arguments.length); - - for (int argIndex = 0; argIndex < arguments.length; argIndex++) { - String arg = arguments[argIndex]; + for (int argIndex = 0; argIndex < arguments.length; argIndex++) { + String arg = arguments[argIndex]; - if (arg.startsWith("\"")) { - arg = arg.substring(1, arg.length() - 1); - } - - if (arg.endsWith("\"")) { - arg = arg.substring(0, arg.length() - 1); - } + if (arg.startsWith("\"")) { + arg = arg.substring(1, arg.length() - 1); + } - arguments[argIndex] = arg; + if (arg.endsWith("\"")) { + arg = arg.substring(0, arg.length() - 1); } + + arguments[argIndex] = arg; + } - switch (cmd) { - case "generate": - if (arguments.length < 1) { - System.out.println("Not enough arguments!"); - break; - } - - String numberRaw = arguments[0]; - int number = 0; - - try { - number = Integer.valueOf(numberRaw); - } catch (Exception ex) { - System.out.println("Invalid arguments provided!"); - break; - } - - long timeBeforeGen = System.currentTimeMillis(); - String outputFolderPath = ""; - - if (!this.noSaving) { - outputFolderPath = (this.outputFolderBase == null ? - "." : this.outputFolderBase.trim()) + "/" + timeBeforeGen; - System.out.println(String.format("Output folder: %s", outputFolderPath)); - - // Create the output folder if it doesn't exist - try { - File outputFolder = new File(outputFolderPath); - if (!outputFolder.exists()) { - outputFolder.mkdir(); - } - } catch (Exception ex) { - System.err.println("Unable to create the output folder! Do we have not write permissions?"); - System.err.println("Disabled file saving"); - this.noSaving = true; - ex.printStackTrace(); - } - } + Delegate handleInputDelegate = new Delegate() { + @Override + public void call(Object... args) { + switch (cmd) { + case "generate": + if (arguments.length < 1) { + logger.error("Not enough arguments!"); + break; + } + + String numberRaw = arguments[0]; + int number = 0; + + try { + number = Integer.valueOf(numberRaw); + } catch (Exception ex) { + logger.error("Invalid arguments provided!"); + break; + } + + long timeBeforeGen = System.currentTimeMillis(); + String outputFolderPath = ""; + + if (!noSaving) { + outputFolderPath = (outputFolderBase == null ? + "." : outputFolderBase.trim()) + "/" + timeBeforeGen; + logger.info("Output folder: %s", outputFolderPath); + + // Create the output folder if it doesn't exist + try { + File outputFolder = new File(outputFolderPath); + if (!outputFolder.exists()) { + outputFolder.mkdir(); + } + } catch (Exception ex) { + logger.error("Unable to create the output folder! Do we have not write permissions?"); + logger.error("Disabled file saving"); + noSaving = true; + ex.printStackTrace(); + } + } - String[] links = this.generateLinks(number); - PrintWriter fileDump = null; - if (!this.noSaving) { - try { - fileDump = new PrintWriter(new FileOutputStream( - new File(String.format("%s/links.html", outputFolderPath)))); - fileDump.println(""); - fileDump.println(""); - fileDump.println("\t"); - fileDump.println("\t\t"); - } catch (Exception ex) { - System.err.println("Unable to create the HTML file! Do we have not write permissions?"); - ex.printStackTrace(); - } - } + String[] links = generateLinks(number); + PrintWriter fileDump = null; + if (!noSaving) { + try { + fileDump = new PrintWriter(new FileOutputStream( + new File(String.format("%s/links.html", outputFolderPath)))); + fileDump.println(""); + fileDump.println(""); + fileDump.println("\t"); + fileDump.println("\t\t"); + } catch (Exception ex) { + logger.error("Unable to create the HTML file! Do we have not write permissions?"); + ex.printStackTrace(); + } + } - for (int i = 0; i < links.length; i++) { - String link = links[i]; - String linkImageURL = ""; - - // Print to console - System.out.println(String.format("(%d) %s", i, link)); - - if (!this.noSaving) { - // Get image URL - try { - Document document = Jsoup.connect(link).get(); - Element element = document.getElementById("screenshot-image"); - linkImageURL = element.attr("src"); - } catch (Exception ex) { - System.err.println( - String.format("Unable to get the image URL for the link %s!", linkImageURL)); - ex.printStackTrace(); - } - - // Write to file - if (fileDump != null) { - fileDump.println(String.format( - "\t\t\t(%d) %s:
", i, link, link)); - fileDump.println(String.format( - "\t\t\t\"Link
", linkImageURL, i)); - } - - // Download image URL - if (linkImageURL != "" && !this.noDownload) { - this.downloadImage(linkImageURL, String.format("%s/%d.png", - outputFolderPath, i)); - } - } - } - - if (fileDump != null) { - fileDump.println("\t\t
"); - fileDump.println("\t"); - fileDump.println(""); - fileDump.flush(); - fileDump.close(); + for (int i = 0; i < links.length; i++) { + String link = links[i]; + String linkImageURL = ""; + + // Print to console + logger.info("(%d) %s", i, link); + + if (!noSaving) { + // Get image URL + try { + Document document = Jsoup.connect(link).get(); + Element element = document.getElementById("screenshot-image"); + if (element == null) { + logger.error("Unable to get the image URL for the link %s!", link); + } else { + linkImageURL = element.attr("src"); + // FIX: The schema is not present for some images + if (!linkImageURL.startsWith("http") && + !linkImageURL.startsWith("https")) { + linkImageURL = String.format("https:%s", linkImageURL); + } + } + } catch (Exception ex) { + logger.error("Something went wrong whilst getting the" + + " image URL for the link %s!", link); + ex.printStackTrace(); + } + + // Write to file + if (fileDump != null) { + fileDump.println(String.format( + "\t\t\t(%d) %s:
", i, link, link)); + fileDump.println(String.format( + "\t\t\t\"Link
", + linkImageURL, i)); + } + + // Download image URL + if (linkImageURL != "" && !noDownload) { + downloadImage(linkImageURL, String.format("%s/%d.png", + outputFolderPath, i)); + } + } + } + + if (fileDump != null) { + fileDump.println("\t\t
"); + fileDump.println("\t"); + fileDump.println(""); + fileDump.flush(); + fileDump.close(); + } + + long timeAfterGen = System.currentTimeMillis(); + logger.info("Generated" + (noSaving || noDownload ? " " : " & downloaded ") + "%d links in %d seconds", + links.length, (timeAfterGen - timeBeforeGen) / 1000); + + break; + case "togglenosaving": + noSaving = !noSaving; + logger.info("No saving is now %s", noSaving); + break; + case "togglenodownload": + noDownload = !noDownload; + logger.info("No download is now %s", noDownload); + break; + case "setoutputfolderbase": + if (arguments.length < 1) { + logger.error("Not enough arguments!"); + break; + } + + outputFolderBase = arguments[0]; + logger.info("Set output folder base to %s", outputFolderBase); + + break; + case "getlinkidformat": + logger.info(linkIDFormat); + break; + case "setlinkidformat": + if (arguments.length < 1) { + logger.error("Not enough arguments!"); + break; + } + + linkIDFormat = arguments[0]; + logger.info("Set link ID format to %s", linkIDFormat); + + break; + case "exit": + running = false; + if (console != null) { + System.exit(0); + } + break; + case "help": + logger.info("generate - Generates x amount of links"); + logger.info("setoutputfolderbase - Sets the output folder base"); + logger.info("togglenosaving - Toggles no saving"); + logger.info("togglenodownload - Toggles no downloading"); + logger.info("getlinkidformat - Prints the link ID format used for generation"); + logger.info("setlinkidformat - Sets the link ID format used for generation"); + logger.info("clear - Clears the screen"); + logger.info("exit - Exits LightshotScraper"); + logger.info(""); + logger.info("Link ID format information:"); + logger.info("c - random a-z small character"); + logger.info("C - random a-z big character"); + logger.info("i - random 0-9 number"); + logger.info("r - picks randomly between c, C and i"); + break; + case "clear": + for (int i = 0; i < 1000; i++) { + System.out.println(""); + } + if (console != null) { + console.clear(); + } + printStartupMessage(); + break; + default: + logger.error("Invalid command! Type \"help\" for more information"); + break; + } + } + }; + + if (this.console != null) { + new Thread() { + @Override + public void run() { + handleInputDelegate.call(); + } + }.start(); + } else { + handleInputDelegate.call(); + } + } + + public void printStartupMessage() { + logger.info("Welcome to LightshotScraper!"); + logger.info("Type \"help\" for more information"); + logger.warn("WARNING: The photos you generate may contain pornography" + + " or other inappropriate imagery. Use with caution"); + } + + @Override + public void run() { + this.running = true; + + Scanner inputScanner = null; + if (System.console() != null) { + inputScanner = new Scanner(System.in); + } + + if (System.console() == null && !GraphicsEnvironment.isHeadless()) { + this.console = new Console(); + this.console.onSubmit = new Delegate() { + @Override + public void call(Object... args) { + logger.info("> %s", args[0]); + handleInput((String)args[0]); } - - long timeAfterGen = System.currentTimeMillis(); - System.out.println(String.format("Generated" + (this.noSaving || this.noDownload ? " " : " & downloaded ") + "%d links in %d seconds", - links.length, (timeAfterGen - timeBeforeGen) / 1000)); - - break; - case "togglenosaving": - this.noSaving = !this.noSaving; - System.out.println(String.format("No saving is now %s", this.noSaving)); - break; - case "togglenodownload": - this.noDownload = !this.noDownload; - System.out.println(String.format("No download is now %s", this.noDownload)); - break; - case "setoutputfolderbase": - if (arguments.length < 1) { - System.out.println("Not enough arguments!"); - break; + }; + this.console.onClose = new Delegate() { + @Override + public void call(Object... args) { + System.exit(0); } - - this.outputFolderBase = arguments[0]; - System.out.println(String.format("Set output folder base to %s", this.outputFolderBase)); - - break; - case "exit": - this.running = false; - break; - case "help": - System.out.println("generate - Generates x amount of links"); - System.out.println("setoutputfolderbase - Sets the output folder base"); - System.out.println("togglenosaving - Toggles no saving"); - System.out.println("togglenodownload - Toggles no downloading"); - System.out.println("exit"); - break; - default: - System.out.println("Invalid command!"); - break; + }; + this.console.show(); + } + + this.printStartupMessage(); + while (this.running) { + if (inputScanner != null) { + System.out.print("> "); + String input = inputScanner.nextLine(); + this.handleInput(input); } } - inputScanner.close(); + if (inputScanner != null) { + inputScanner.close(); + } } public static void main(String[] args) { diff --git a/src/src/me/vlod/lightshotscraper/Utils.java b/src/src/me/vlod/lightshotscraper/Utils.java new file mode 100644 index 0000000..de75cfa --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/Utils.java @@ -0,0 +1,219 @@ +package me.vlod.lightshotscraper; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * General purpose utilities + */ +public class Utils { + /** + * Converts a byte array to a hex string
+ * From https://stackoverflow.com/a/9855338 + * + * @param bytes the byte array + * @return the hex string or "DEADBEEF" on invalid input + */ + public static String bytesToHex(byte[] bytes) { + if (bytes == null || bytes.length < 1) return "DEADBEEF"; + + char[] hexArray = "0123456789ABCDEF".toCharArray(); + char[] hexChars = new char[bytes.length * 2]; + + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + + return new String(hexChars); + } + + /** + * Gets an throwable's stacktrace as a string + * + * @param throwable the exception + * @return the throwable's stacktrace or null + */ + public static String getThrowableStackTraceAsStr(Throwable throwable) { + if (throwable == null) return null; + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + throwable.printStackTrace(printWriter); + return stringWriter.toString(); + } + + /** + * Checks if the specified string is a numeric value + * + * @param str the string + * @param checkForDouble check if the string is a valid double value + * @return the check status + */ + public static boolean isNumeric(String str, boolean checkForDouble) { + if (str == null || + (!checkForDouble && str.contains(".")) || + (checkForDouble && !str.contains("."))) + return false; + + try { + if (!checkForDouble) + Integer.valueOf(str); + else + Double.valueOf(str); + } catch (NumberFormatException ex) { + return false; + } + + return true; + } + + /** + * Checks if the specified string is a boolean value + * + * @param str the string + * @return the check status + */ + public static boolean isBoolean(String str) { + if (str == null) + return false; + else if (str.equalsIgnoreCase("true") || + str.equalsIgnoreCase("false")) + return true; + else + return false; + } + + /** + * Replaces the last match in the specified string + * + * @param str the string + * @param match the match + * @param replaceWith what to the replace the match with + * @return the replaced string or the string if no match + */ + public static String replaceLast(String str, String match, String replaceWith) { + return new StringBuilder( + new StringBuilder(str) + .reverse() + .toString() + .replaceFirst(match, replaceWith)) + .reverse() + .toString(); + } + + /** + * Splits a string by spaces, ignoring quotes
+ * From https://stackoverflow.com/a/366532 + * + * @param str the string to split + * @return the result or empty array + */ + public static String[] splitBySpace(String str) { + try { + List matchList = new ArrayList(); + Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); + Matcher regexMatcher = regex.matcher(str); + + while (regexMatcher.find()) { + if (regexMatcher.group(1) != null) { + // Add double-quoted string without the quotes + matchList.add(regexMatcher.group(1)); + } else if (regexMatcher.group(2) != null) { + // Add single-quoted string without the quotes + matchList.add(regexMatcher.group(2)); + } else { + // Add unquoted word + matchList.add(regexMatcher.group()); + } + } + + return matchList.toArray(new String[0]); + } catch (Exception ex) { + ex.printStackTrace(); + return new String[0]; + } + } + + /** + * Gets a salted MD5 hash from the specified string + * + * @param salt the salt to use + * @param content the string to hash + * @return the MD5 hash or empty on error + */ + public static String getMD5HashFromStr(String salt, String content) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + messageDigest.update((salt + content).getBytes()); + return (new BigInteger(1, messageDigest.digest())).toString(16); + } catch (Exception ex) { + return ""; + } + } + + /** + * Splits the specified string into chunks
+ * From https://stackoverflow.com/a/3760193 + * + * @param str the string to split + * @param chunkSize the chunk size + * @return the chunks + */ + public static String[] splitStringIntoChunks(String str, int chunkSize) { + List chunks = new ArrayList((str.length() + chunkSize - 1) / chunkSize); + + for (int chunkIndex = 0; chunkIndex < str.length(); chunkIndex += chunkSize) { + chunks.add(str.substring(chunkIndex, Math.min(str.length(), chunkIndex + chunkSize))); + } + + return chunks.toArray(new String[0]); + } + + /** + * Checks if the specified string is a valid IP address + * + * @param str the string + * @return true if it is a valid address, false if otherwise + */ + public static boolean isIPv4Address(String str) { + if (str.isEmpty()) { + return false; + } + + try { + InetAddress inetAddress = InetAddress.getByName(str); + return inetAddress instanceof Inet4Address; + } catch (UnknownHostException ex) { + return false; + } + } + + /** + * Re-maps the specified value in a range to a new range
+ * From https://stackoverflow.com/a/929107 + * + * @param oldValue the value + * @param oldMin the old minimum + * @param oldMax the old maximum + * @param min the new minimum + * @param max the new maximum + * @return the re-mapped value + */ + public static int remapToRange(int oldValue, int oldMin, int oldMax, int min, int max) { + int oldRange = (oldMax - oldMin); + int range = (max - min); + int value = (((oldValue - oldMin) * range) / oldRange) + min; + return value; + } +} + diff --git a/src/src/me/vlod/lightshotscraper/console/Console.java b/src/src/me/vlod/lightshotscraper/console/Console.java new file mode 100644 index 0000000..bd98808 --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/console/Console.java @@ -0,0 +1,161 @@ +package me.vlod.lightshotscraper.console; + +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.text.AttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; + +import me.vlod.lightshotscraper.Delegate; + +public class Console { + private JFrame frame; + private JTextPane txtContents; + private JScrollPane spContents; + private JPanel pInput; + private JTextField txtInput; + private JButton btnSubmit; + public Delegate onClose; + public Delegate onSubmit; + + private void init() { + GridBagConstraints gridBagContraints = new GridBagConstraints(); + + this.frame = new JFrame("LightshotScraper - Console"); + this.frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + this.frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + if (onClose != null) { + onClose.call(); + } + } + }); + this.frame.setLayout(new GridBagLayout()); + this.frame.setSize(800, 480); + this.frame.setLocationRelativeTo(null); + + this.txtContents = new JTextPane(); + this.spContents = new JScrollPane(this.txtContents, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + this.pInput = new JPanel(new GridBagLayout()); + this.txtInput = new JTextField(); + this.btnSubmit = new JButton("Submit"); + + this.txtContents.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + e.consume(); + } + + @Override + public void keyPressed(KeyEvent e) { + e.consume(); + } + }); + + this.txtInput.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + btnSubmit.doClick(); + } + } + }); + + this.txtContents.setComponentPopupMenu(new ConsoleContextMenu(this.txtContents)); + this.txtInput.setComponentPopupMenu(new ConsoleInputContextMenu(this.txtInput)); + + this.btnSubmit.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (onSubmit != null) { + onSubmit.call(txtInput.getText()); + } + txtInput.setText(null); + } + }); + + gridBagContraints.fill = GridBagConstraints.BOTH; + gridBagContraints.gridx = 0; + gridBagContraints.gridy = 0; + gridBagContraints.weightx = 1; + gridBagContraints.weighty = 1; + this.frame.add(this.spContents, gridBagContraints); + + gridBagContraints.fill = GridBagConstraints.BOTH; + gridBagContraints.gridx = 0; + gridBagContraints.gridy = 1; + gridBagContraints.weightx = 1; + gridBagContraints.weighty = 0; + this.frame.add(this.pInput, gridBagContraints); + + gridBagContraints.fill = GridBagConstraints.BOTH; + gridBagContraints.gridx = 0; + gridBagContraints.gridy = 0; + gridBagContraints.weightx = 1; + gridBagContraints.weighty = 0; + this.pInput.add(this.txtInput, gridBagContraints); + + gridBagContraints.fill = GridBagConstraints.NONE; + gridBagContraints.gridx = 1; + gridBagContraints.gridy = 0; + gridBagContraints.weightx = 0; + gridBagContraints.weighty = 0; + this.pInput.add(this.btnSubmit, gridBagContraints); + } + + public void show() { + this.init(); + this.frame.setVisible(true); + } + + public void hide() { + this.frame.setVisible(false); + this.frame.dispose(); + } + + public void write(String str, Color color, boolean newLine) { + StyleContext styleContext = StyleContext.getDefaultStyleContext(); + AttributeSet attributeSet = styleContext.addAttribute( + SimpleAttributeSet.EMPTY, StyleConstants.Foreground, color); + + attributeSet = styleContext.addAttribute(attributeSet, + StyleConstants.FontFamily, "Lucida Console"); + attributeSet = styleContext.addAttribute(attributeSet, + StyleConstants.Alignment, StyleConstants.ALIGN_JUSTIFIED); + + this.txtContents.setCaretPosition(txtContents.getDocument().getLength()); + this.txtContents.setCharacterAttributes(attributeSet, false); + this.txtContents.replaceSelection(str + (newLine ? "\n" : "")); + this.txtContents.setCharacterAttributes(styleContext.addAttribute( + SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.black), false); + } + + public void write(String str, Color color) { + this.write(str, color, true); + } + + public void write(String str) { + this.write(str, Color.black, true); + } + + public void clear() { + this.txtContents.setText(null); + } +} diff --git a/src/src/me/vlod/lightshotscraper/console/ConsoleContextMenu.java b/src/src/me/vlod/lightshotscraper/console/ConsoleContextMenu.java new file mode 100644 index 0000000..490bcc7 --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/console/ConsoleContextMenu.java @@ -0,0 +1,38 @@ +package me.vlod.lightshotscraper.console; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JTextPane; + +public class ConsoleContextMenu extends JPopupMenu { + private static final long serialVersionUID = 4149806726964272221L; + + public ConsoleContextMenu(JTextPane textPane) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + + JMenuItem copyMenu = new JMenuItem("Copy"); + copyMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clipboard.setContents(new StringSelection(textPane.getSelectedText()), null); + } + }); + + JMenuItem selectAllMenu = new JMenuItem("Select All"); + selectAllMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + textPane.selectAll(); + } + }); + + this.add(copyMenu); + this.add(selectAllMenu); + } +} diff --git a/src/src/me/vlod/lightshotscraper/console/ConsoleInputContextMenu.java b/src/src/me/vlod/lightshotscraper/console/ConsoleInputContextMenu.java new file mode 100644 index 0000000..17b54e6 --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/console/ConsoleInputContextMenu.java @@ -0,0 +1,68 @@ +package me.vlod.lightshotscraper.console; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JTextField; + +public class ConsoleInputContextMenu extends JPopupMenu { + private static final long serialVersionUID = 8686746525908854488L; + + public ConsoleInputContextMenu(JTextField textField) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + + JMenuItem copyMenu = new JMenuItem("Copy"); + copyMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clipboard.setContents(new StringSelection(textField.getSelectedText()), null); + } + }); + + JMenuItem selectAllMenu = new JMenuItem("Select All"); + selectAllMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + textField.selectAll(); + } + }); + + JMenuItem pasteMenu = new JMenuItem("Paste"); + pasteMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Transferable content = clipboard.getContents(null); + + if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) { + try { + String contentStr = (String) content.getTransferData(DataFlavor.stringFlavor); + textField.getDocument().insertString(textField.getCaretPosition(), contentStr, null); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + }); + + JMenuItem cutMenu = new JMenuItem("Cut"); + cutMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clipboard.setContents(new StringSelection(textField.getSelectedText()), null); + textField.replaceSelection(null); + } + }); + + this.add(copyMenu); + this.add(selectAllMenu); + this.add(pasteMenu); + this.add(cutMenu); + } +} diff --git a/src/src/me/vlod/lightshotscraper/logger/LogLevel.java b/src/src/me/vlod/lightshotscraper/logger/LogLevel.java new file mode 100644 index 0000000..e193486 --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/logger/LogLevel.java @@ -0,0 +1,10 @@ +package me.vlod.lightshotscraper.logger; + +public enum LogLevel { + VERBOSE, + INFO, + WARN, + ERROR, + SEVERE, + FATAL +} diff --git a/src/src/me/vlod/lightshotscraper/logger/Logger.java b/src/src/me/vlod/lightshotscraper/logger/Logger.java new file mode 100644 index 0000000..1aaffe5 --- /dev/null +++ b/src/src/me/vlod/lightshotscraper/logger/Logger.java @@ -0,0 +1,71 @@ +package me.vlod.lightshotscraper.logger; + +import java.awt.Color; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; + +import me.vlod.lightshotscraper.Delegate; +import me.vlod.lightshotscraper.Utils; + +public class Logger { + public final ArrayList targets = new ArrayList(); + + public void log(String header, String message, Color color, Object... format) { + message = String.format(message, (Object[])format); + + LocalDateTime localDateTime = LocalDateTime.now(); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); + String text = dateTimeFormatter.format(localDateTime) + " [" + header + "] " + message; + + for (Delegate target : this.targets) { + target.call(text, color); + } + } + + public void log(String header, String message, Object... format) { + this.log(header, message, Color.black, format); + } + + public void level(LogLevel level, String message, Object... format) { + if (level == LogLevel.ERROR || + level == LogLevel.SEVERE || + level == LogLevel.FATAL) { + this.log(level.toString(), message, Color.red, format); + } else if (level == LogLevel.WARN) { + this.log(level.toString(), message, new Color(246, 190, 0), format); + } else { + this.log(level.toString(), message, Color.black, format); + } + } + + public void verbose(String message, Object... format) { + this.level(LogLevel.VERBOSE, message, format); + } + + public void info(String message, Object... format) { + this.level(LogLevel.INFO, message, format); + } + + public void warn(String message, Object... format) { + this.level(LogLevel.WARN, message, format); + } + + public void error(String message, Object... format) { + this.level(LogLevel.ERROR, message, format); + } + + public void severe(String message, Object... format) { + this.level(LogLevel.SEVERE, message, format); + } + + public void fatal(String message, Object... format) { + this.level(LogLevel.FATAL, message, format); + } + + public void throwable(Throwable throwable) { + this.level(LogLevel.ERROR, "An throwable of type " + throwable.getClass().getName() + + " has occured: " + throwable.getMessage()); + this.level(LogLevel.ERROR, "Throwable stacktrace: " + Utils.getThrowableStackTraceAsStr(throwable)); + } +}