diff --git a/src/main/java/Backend/Drifty.java b/src/main/java/Backend/Drifty.java index 9528c6050..4b0d98fad 100644 --- a/src/main/java/Backend/Drifty.java +++ b/src/main/java/Backend/Drifty.java @@ -94,7 +94,7 @@ public void start() { } } - if (((fileName == null) || (fileName.length() == 0)) && (!Utility.isYoutubeLink(url))) { + if (((fileName == null) || (fileName.length() == 0)) && (!Utility.isYoutubeLink(url) && !Utility.isInstagramLink(url))) { fileName = utility.findFilenameInLink(url); if (fileName == null || fileName.length() == 0) { messageBroker.sendMessage("Filename cannot be empty!", DriftyConstants.LOGGER_ERROR, "Filename"); diff --git a/src/main/java/Backend/FileDownloader.java b/src/main/java/Backend/FileDownloader.java index c1f3db0e7..fb33a49e0 100644 --- a/src/main/java/Backend/FileDownloader.java +++ b/src/main/java/Backend/FileDownloader.java @@ -1,5 +1,6 @@ package Backend; +import CLI.Drifty_CLI; import Utils.MessageBroker; import java.awt.*; @@ -12,6 +13,7 @@ import java.util.Objects; import static Utils.DriftyConstants.*; +import static Utils.Utility.isInstagramLink; import static Utils.Utility.isYoutubeLink; /** @@ -77,7 +79,9 @@ public FileDownloader(String link, String fileName, String dir) { FileDownloader.fileName = fileName; FileDownloader.dir = dir; FileDownloader.supportsMultithreading = false; - setYt_dlpProgramName(getYt_dlpProgramName()); + if (isYoutubeLink(link) || !Drifty_CLI.getIsInstagramImage()) { + setYt_dlpProgramName(getYt_dlpProgramName()); + } } /** @@ -301,8 +305,12 @@ public void run() { dir = dir + System.getProperty("file.separator"); } try { - // If link is of an YouTube video, then the following block of code will execute. - if (isYoutubeLink(link)) { + boolean isInstagramImage = false; + if (isInstagramLink(link)) { + isInstagramImage = Drifty_CLI.getIsInstagramImage(); + } + // If link is of an YouTube or Instagram video, then the following block of code will execute. + if (isYoutubeLink(link) || !isInstagramImage) { try { String directoryOfYt_dlp = "./src/main/resources/"; messageBroker.sendMessage("Checking for component (yt-dlp) update ...", LOGGER_INFO, "download"); @@ -310,7 +318,11 @@ public void run() { yt_dlp_update.inheritIO(); Process yt_dlp = yt_dlp_update.start(); yt_dlp.waitFor(); - downloadFromYouTube(directoryOfYt_dlp); + if (isYoutubeLink(link)) { + downloadFromYouTube(directoryOfYt_dlp); + } else if (!isInstagramImage) { + downloadFromInstagram(directoryOfYt_dlp); + } } catch (IOException e) { try { messageBroker.sendMessage(GETTING_READY_TO_DOWNLOAD_FILE, LOGGER_INFO, "download"); @@ -323,7 +335,11 @@ public void run() { yt_dlp_update.inheritIO(); Process yt_dlp = yt_dlp_update.start(); yt_dlp.waitFor(); - downloadFromYouTube(tempDir); + if (isYoutubeLink(link)) { + downloadFromYouTube(tempDir); + } else if (!isInstagramImage) { + downloadFromInstagram(tempDir); + } } catch (InterruptedException ie) { messageBroker.sendMessage(USER_INTERRUPTION, LOGGER_ERROR, "download"); } catch (Exception e1) { @@ -347,6 +363,13 @@ public void run() { messageBroker.sendMessage(USER_INTERRUPTION, LOGGER_ERROR, "download"); } } else { + if (isInstagramImage){ + if (link.endsWith("/") || link.endsWith("\\")){ + link += "media"; + } else { + link += "/media"; + } + } url = new URI(link).toURL(); URLConnection openConnection = url.openConnection(); openConnection.connect(); @@ -368,4 +391,44 @@ public void run() { messageBroker.sendMessage(FAILED_TO_CONNECT_TO_URL + url + " !", LOGGER_ERROR, "download"); } } + + /** + * This method downloads a video from Instagram. + * + * @param dirOfYt_dlp The directory to save the file to. + * @throws InterruptedException If the download is interrupted. + * @throws IOException If there is an error reading or writing the file. + */ + public static void downloadFromInstagram(String dirOfYt_dlp) throws InterruptedException, IOException { + String outputFileName; + outputFileName = Objects.requireNonNullElse(fileName, "%(title)s.%(ext)s"); + String fileDownloadMessagePart; + if (outputFileName.equals("%(title)s.%(ext)s")) { + fileDownloadMessagePart = "the Instagram Video"; + } else { + fileDownloadMessagePart = outputFileName; + } + ProcessBuilder processBuilder; // Creates a new ProcessBuilder object + messageBroker.sendMessage("Trying to download " + fileDownloadMessagePart + " ...", LOGGER_INFO, "download"); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // E.g. java.awt.Dimension[width=1366,height=768] + int height = (int) screenSize.getHeight(); // E.g.: 768 + int width = (int) screenSize.getWidth(); // E.g.: 1366 + if (dir.length() == 0 || (dir.equalsIgnoreCase("."))) { + processBuilder = new ProcessBuilder(dirOfYt_dlp + yt_dlpProgramName, "--quiet", "--progress", link, "-o", + outputFileName, "-f", "[height<=" + height + "][width<=" + width + "]"); // The command line arguments tell `yt-dlp` to download the video and to save it to the specified directory + } else { + processBuilder = new ProcessBuilder(dirOfYt_dlp + yt_dlpProgramName, "--quiet", "--progress", "-P", dir, link, + "-o", outputFileName, "-f", "[height<=" + height + "][width<=" + width + "]"); // The command line arguments tell `yt-dlp` to download the video and to save it to the specified directory. + } + processBuilder.inheritIO(); + messageBroker.sendMessage(DOWNLOADING + fileDownloadMessagePart + " ...", LOGGER_INFO, "download"); + Process yt_dlp = processBuilder.start(); // Starts the download process + yt_dlp.waitFor(); + int exitValueOfYt_Dlp = yt_dlp.exitValue(); + if (exitValueOfYt_Dlp == 0) { + messageBroker.sendMessage(SUCCESSFULLY_DOWNLOADED + fileDownloadMessagePart + " !", LOGGER_INFO, "download"); + } else if (exitValueOfYt_Dlp == 1) { + messageBroker.sendMessage(FAILED_TO_DOWNLOAD + fileDownloadMessagePart + " !", LOGGER_ERROR, "download"); + } + } } diff --git a/src/main/java/Backend/ProgressBarThread.java b/src/main/java/Backend/ProgressBarThread.java index b4355efa9..b3e0f4771 100644 --- a/src/main/java/Backend/ProgressBarThread.java +++ b/src/main/java/Backend/ProgressBarThread.java @@ -255,12 +255,12 @@ private String convertBytes(long bytes) { private void cleanup() { if (isMultiThreadedDownloading) { String sizeWithUnit = convertBytes(totalDownloadedBytes); - messageBroker.sendMessage(DOWNLOADED + fileName + OF_SIZE + sizeWithUnit + " at " + FileDownloader.getDir() + fileName + SUCCESSFULLY, LOGGER_INFO, "download"); + messageBroker.sendMessage("\n" + DOWNLOADED + fileName + OF_SIZE + sizeWithUnit + " at " + FileDownloader.getDir() + fileName + SUCCESSFULLY, LOGGER_INFO, "download"); } else if (downloadedBytes == totalDownloadedBytes) { String sizeWithUnit = convertBytes(downloadedBytes); - messageBroker.sendMessage(DOWNLOADED + fileName + OF_SIZE + sizeWithUnit + " at " + FileDownloader.getDir() + fileName + SUCCESSFULLY, LOGGER_INFO, "download"); + messageBroker.sendMessage("\n" + DOWNLOADED + fileName + OF_SIZE + sizeWithUnit + " at " + FileDownloader.getDir() + fileName + SUCCESSFULLY, LOGGER_INFO, "download"); } else { - messageBroker.sendMessage(DOWNLOAD_FAILED, LOGGER_ERROR, "download"); + messageBroker.sendMessage("\n" + DOWNLOAD_FAILED, LOGGER_ERROR, "download"); } Drifty_GUI.setIsFileBeingDownloaded(false); } diff --git a/src/main/java/CLI/Drifty_CLI.java b/src/main/java/CLI/Drifty_CLI.java index 2f1d1798e..0e7dd477c 100644 --- a/src/main/java/CLI/Drifty_CLI.java +++ b/src/main/java/CLI/Drifty_CLI.java @@ -35,6 +35,14 @@ public class Drifty_CLI { * Boolean value which determines if the given link is an YouTube video URL or not */ protected static boolean isYoutubeURL; + /** + * Boolean value which determines if the given link is an Instagram media URL or not + */ + protected static boolean isInstagramLink; + /** + * Boolean value which determines if the given link is an Instagram image URL or not + */ + protected static boolean isInstagramImage; /** * Message broker instance which helps to send messages to the output stream */ @@ -85,8 +93,9 @@ public static void main(String[] args) { } if (!batchDownloading) { isYoutubeURL = isYoutubeLink(link); + isInstagramLink = isInstagramLink(link); fileName = (name == null) ? fileName : name; - if (!isYoutubeURL) { + if (!isYoutubeURL && !isInstagramLink) { fileName = utility.findFilenameInLink(link); } takeFileNameInputIfNull(); @@ -138,31 +147,16 @@ public static void main(String[] args) { System.out.print("Enter the download directory (Enter \".\" for default downloads folder) : "); downloadsFolder = SC.next(); isYoutubeURL = isYoutubeLink(link); - if (!isYoutubeURL) { + isInstagramLink = isInstagramLink(link); + if (!isYoutubeURL && !isInstagramLink) { fileName = utility.findFilenameInLink(link); } - if ((fileName == null || (fileName.length() == 0)) && (!isYoutubeURL)) { - System.out.print(ENTER_FILE_NAME_WITH_EXTENSION); - fileName = SC.next(); - } else { - if (isYoutubeURL) { - System.out.print(RENAME_VIDEO_TITLE); - } else { - System.out.print(RENAME_FILE); - } - SC.nextLine(); // To remove 'whitespace' from input buffer. - String choiceString = SC.nextLine().toLowerCase(); - boolean choice = utility.yesNoValidation(choiceString, RENAME_FILE); - if (choice) { - System.out.print(ENTER_FILE_NAME_WITH_EXTENSION); - fileName = SC.nextLine(); - } - } + takeFileNameInputIfNull(); Drifty backend = new Drifty(link, downloadsFolder, fileName, System.out); backend.start(); } System.out.println(QUIT_OR_CONTINUE); - String choice = SC.nextLine().trim().toLowerCase(); + String choice = SC.next().toLowerCase(); if (choice.equals("q")) { logger.log(LOGGER_INFO, CLI_APPLICATION_TERMINATED); break; @@ -242,22 +236,43 @@ private static void batchDownloader() { * Takes the filename as input from the user interactively if the system cannot find the filename i.e. if it is null */ private static void takeFileNameInputIfNull() { - if ((fileName == null || (fileName.length() == 0)) && (!isYoutubeURL)) { + if ((fileName == null || (fileName.length() == 0)) && (!isYoutubeURL && !isInstagramLink)) { System.out.print(ENTER_FILE_NAME_WITH_EXTENSION); fileName = SC.nextLine(); } else { if (isYoutubeURL) { System.out.print("Do you like to use the video title as the filename? (Enter Y for yes and N for no) : "); + SC.nextLine(); // To remove 'whitespace' from input buffer. + String choiceString = SC.nextLine().toLowerCase(); + boolean choice = utility.yesNoValidation(choiceString, "Do you like to use the video title as the filename? (Enter Y for yes and N for no) : "); + if (!choice) { + System.out.print(ENTER_FILE_NAME_WITH_EXTENSION); + fileName = SC.nextLine(); + } + } else if (isInstagramLink) { + System.out.print("Is the instagram link of a video? (Enter Y for video and N for image) : "); + SC.nextLine(); // To remove 'whitespace' from input buffer. + String choiceString = SC.nextLine().toLowerCase(); + boolean choice = utility.yesNoValidation(choiceString, "Is the instagram link of a video? (Enter Y for video and N for image) : "); + if (!choice) { + System.out.print("Please enter the filename for the Instagram image with the file extension (filename.extension [usually png]) : "); + fileName = SC.nextLine(); + isInstagramImage = true; + } else { + isInstagramImage = false; + } } else { System.out.print(RENAME_FILE); } - SC.nextLine(); // To remove 'whitespace' from input buffer. - String choiceString = SC.nextLine(); - boolean choice = utility.yesNoValidation(choiceString, RENAME_FILE); - if (!choice) { - System.out.print(ENTER_FILE_NAME_WITH_EXTENSION); - fileName = SC.nextLine(); - } } } + + /** + * This method returns true if the given Instagram link is of an image + * @return True if the Instagram link is of an image else false (for video) [Image/Video is decided by user] + * @since v2.0.0 + */ + public static boolean getIsInstagramImage() { + return isInstagramImage; + } } diff --git a/src/main/java/Utils/DriftyConstants.java b/src/main/java/Utils/DriftyConstants.java index 6137c4255..cb2e2decb 100644 --- a/src/main/java/Utils/DriftyConstants.java +++ b/src/main/java/Utils/DriftyConstants.java @@ -19,7 +19,7 @@ public final class DriftyConstants { public static final String CLI_APPLICATION_TERMINATED = "Drifty CLI (Command Line Interface) Application Terminated!"; public static final String GUI_APPLICATION_TERMINATED = "Drifty GUI (Graphical User Interface) Application Terminated!"; public static final String INVALID_LINK = "Invalid Link!"; - public static final String AUTO_FILE_NAME_DETECTION_FAILED = "An error occurred! Either the file name or the extension was missing in the url. The url must be of the form of https://www.example.com/fileName.extension"; + public static final String AUTO_FILE_NAME_DETECTION_FAILED = "An error occurred! Either the file name or the extension was missing in the url.\nThe url must be of the form of https://www.example.com/fileName.extension or an YouTube/Instagram link"; public static final String TRYING_TO_AUTO_DETECT_DOWNLOADS_FOLDER = "Trying to automatically detect default Downloads folder..."; public static final String TRYING_TO_DOWNLOAD_FILE = "Trying to download the file ..."; public static final String HELP_FLAG = "-help"; @@ -33,7 +33,7 @@ public final class DriftyConstants { public static final String LOCATION_FLAG_SHORT = "-l"; public static final String BATCH_FLAG_SHORT = "-b"; public static final String ENTER_FILE_NAME_WITH_EXTENSION = "Please enter the filename with file extension (fileName.extension) : "; - public static final String ENTER_FILE_LINK = "Enter the link to the file (in the form of https://www.example.com/fileName.extension) or a YouTube Video link : "; + public static final String ENTER_FILE_LINK = "Enter the link to the file (in the form of https://www.example.com/fileName.extension) or a YouTube/Instagram Video/Image link : "; public static final String OS_NAME = "os.name"; public static final String WINDOWS_OS_NAME = "Windows"; public static final String USER_HOME_PROPERTY = "user.home"; diff --git a/src/main/java/Utils/MessageBroker.java b/src/main/java/Utils/MessageBroker.java index 5b74d6900..d727f924c 100644 --- a/src/main/java/Utils/MessageBroker.java +++ b/src/main/java/Utils/MessageBroker.java @@ -62,7 +62,9 @@ public MessageBroker(String applicationType, JLabel link, JLabel dir, JLabel dow */ public void sendMessage(String message, String messageType, String messageCategory){ if (appType.equals("CLI")){ - output.println(message); + if (!messageCategory.equalsIgnoreCase("only log")) { + output.println(message); + } logger.log(messageType, message); } else if (appType.equals("GUI")){ Color color = Color.BLACK; diff --git a/src/main/java/Utils/Utility.java b/src/main/java/Utils/Utility.java index cf7e9d029..f5fcb39e3 100644 --- a/src/main/java/Utils/Utility.java +++ b/src/main/java/Utils/Utility.java @@ -2,6 +2,7 @@ import Backend.DefaultDownloadFolderLocationFinder; import Backend.Drifty; +import CLI.Drifty_CLI; import java.net.*; import java.util.Scanner; @@ -17,9 +18,10 @@ public Utility() {} public Utility(MessageBroker messageBroker) { this.messageBroker = messageBroker; } + /** * This method checks whether the link provided is of YouTube or not and returns the resultant boolean value accordingly. - * @param url link to the file to be downloaded. + * @param url link to the file to be downloaded * @return true if the url is of YouTube and false if it is not. */ public static boolean isYoutubeLink(String url) { @@ -27,6 +29,16 @@ public static boolean isYoutubeLink(String url) { return url.matches(pattern); } + /** + * This method checks whether the link provided is of Instagram or not and returns the resultant boolean value accordingly. + * @param url link to the file to be downloaded + * @return true if the url is of Instagram and false if it is not. + */ + public static boolean isInstagramLink(String url) { + String pattern = "(https?:\\/\\/(?:www\\.)?instagr(am|.am)?(\\.com)?\\/p\\/([^/?#&]+)).*"; + return url.matches(pattern); + } + /** * @param link Link to the file that the user wants to download * @throws Exception if URL is not valid or cannot be connected to, then this Exception is thrown with proper message