Skip to content

Commit

Permalink
Feat(App): Added support for downloading Instagram videos and photos
Browse files Browse the repository at this point in the history
Added support for downloading Instagram photos and videos by @abhishekkumarXD via #223 closing #216.
---------
* Add support for downloading Instagram videos and photos (Issue #216)

This commit adds support for downloading videos and photos from Instagram. The new feature uses the yt-dlp program to download the videos and photos in MP4 format. The videos and photos are saved to the current directory or to the directory specified by the dir variable.

The commit also adds a new method called downloadFromInstagram() to the FileDownloader class. This method takes a link to an Instagram video or photo and a directory as arguments and downloads the video or photo to the specified directory.

* feat: Incorporated the downloadFromInstagram() function into CLI only and added support for downloading Instagram Images besides videos

Co-Authored-By: Saptarshi Sarkar <saptarshi.programmer@gmail.com>

---------

Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com>
  • Loading branch information
abhisheks-gh and SaptarshiSarkar12 committed Jul 13, 2023
1 parent 091f6d2 commit 37a9f15
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 41 deletions.
2 changes: 1 addition & 1 deletion src/main/java/Backend/Drifty.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
73 changes: 68 additions & 5 deletions src/main/java/Backend/FileDownloader.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package Backend;

import CLI.Drifty_CLI;
import Utils.MessageBroker;

import java.awt.*;
Expand All @@ -12,6 +13,7 @@
import java.util.Objects;

import static Utils.DriftyConstants.*;
import static Utils.Utility.isInstagramLink;
import static Utils.Utility.isYoutubeLink;

/**
Expand Down Expand Up @@ -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());
}
}

/**
Expand Down Expand Up @@ -301,16 +305,24 @@ 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");
ProcessBuilder yt_dlp_update = new ProcessBuilder(directoryOfYt_dlp + yt_dlpProgramName, "-U");
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");
Expand All @@ -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) {
Expand All @@ -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();
Expand All @@ -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");
}
}
}
6 changes: 3 additions & 3 deletions src/main/java/Backend/ProgressBarThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
71 changes: 43 additions & 28 deletions src/main/java/CLI/Drifty_CLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
4 changes: 2 additions & 2 deletions src/main/java/Utils/DriftyConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/Utils/MessageBroker.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/Utils/Utility.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import Backend.DefaultDownloadFolderLocationFinder;
import Backend.Drifty;
import CLI.Drifty_CLI;

import java.net.*;
import java.util.Scanner;
Expand All @@ -17,16 +18,27 @@ 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) {
String pattern = "^(http(s)?:\\/\\/)?((w){3}.)?youtu(be|.be)?(\\.com)?\\/.+";
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
Expand Down

1 comment on commit 37a9f15

@vercel
Copy link

@vercel vercel bot commented on 37a9f15 Jul 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.