-1

I am facing an issue when it comes to my Java application. The app downloads Music from Youtube using yt-dlp, it is basically a GUI version of yt-dlp. The issue I have is only present on MacOs, it works as intended on Windows. Here is a snippet of my download method code (This is a MacOs specific method, as I have already mentioned before I only encounter the issue on MacOs not on Windows, thus I am not providing any code specific for Windows):

The download method:

private void download() {
Process process = null;
        try {
            ProcessBuilder processBuilder;
                String command =
                       "'Project/src/Zasoby/yt-dlp'" + " -f bestaudio " +
                        "-x --audio-format " + chosenFormat
                        + " --no-playlist --output '" + outputPath + "/%(title)s.%(ext)s ''"
                        + linkYT + "'";

            processBuilder = new ProcessBuilder("bash", "-c", command);

            processBuilder.inheritIO();
            process = processBuilder.start();
            process.waitFor();

            int exitCode = process.exitValue();

            System.out.println(exitCode + " EXIT CODE"); // a log for exit codes

            if (exitCode != 0) {
                InfoFrame ef = new InfoFrame(2);
                ef.setVisible(true);
           }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
}

The problem is that whenever I try to execute a command using the ProcessBuilder when not in my IDE (in my IDE it works just fine - the path 'Project/src/Zasoby/yt-dlp' is recognized and the program stars downloading Music), instead while executing the program via a .jar File, the program cannot find the yt-dlp program via its provided path.

So far I have tried providing Paths using a getFilePath method I wrote in order to make sure I do not need to specify a "semi-full" path which obviously would not work on a different MacOs computer.

The getFilePath method:

private Path getFilePath(String resourcePath) {
        try {
            InputStream inputStream = getResourceAsStream(resourcePath);
            Path tempFilePath = Files.createTempFile("temp", null);
            Files.copy(inputStream, tempFilePath, StandardCopyOption.REPLACE_EXISTING);
            return tempFilePath.toAbsolutePath();
        } catch (IOException e) {
            throw new RuntimeException("Failed to get resource file path: " + resourcePath, e);
        }
    }

The modified command String in the download method:

Path ytDlpPathMac = getFilePath("Zasoby/yt-dlp");
String command =
             ytDlpPathMac.toString() + " -f bestaudio " +
             "-x --audio-format " + chosenFormat
             + " --no-playlist --output '" + outputPath + "/%(title)s.%(ext)s ''"
             + linkYT + "'";

Additionally the path "Zasoby/yt-dlp" is a "Path From Content Root", the full Path in the project is"Project/src/Zasoby/yt-dlp" - if that helps.I have already tried other solutions from other Questions on the forum, however none of them fixed the issue.

I have tried debugging the code using Xdebug tool on MacOs and the output I am getting is that ".. directory does not exist" - when using both of the approaches, the one with the "semi-full" path and the one with the getFilePath method. As I have previously mentioned this only happens on Mac, so maybe there is a different way of handling Paths on MacOs than on Windows when it comes to Java itself.

  • 2
    Any path that doesn't start with `/` is a *relative* path, and will be resolved starting at the process's current working directory... which could be almost anything (depending on the whims of whatever launched your program). It sounds like you need to find the location of the .jar file, and build a full path based on the directory it's in. (Unfortunately, I don't know how to do this in java.) – Gordon Davisson Jul 16 '23 at 18:53
  • Either the `Project/src/` path is not in your JAR file or it shouldn't be, but a JAR file is not the filesystem, and arbitrary processes can't find files in them. You will probably have to distribute the file(s) in question separately. – user207421 Jul 17 '23 at 00:13

2 Answers2

3

So far I have tried providing Paths using a getFilePath method I wrote in order to make sure I do not need to specify a "semi-full" path which obviously would not work on a different MacOs computer.

I don't know what you mean by the term "semi-full path", but if you want to run an external program, then that program must be

  • in the system's executable search path, OR
  • specified via an absolute path to it, OR
  • specified via a path relative to the host program's current working directory.

The problem is that whenever I try to execute a command using the ProcessBuilder when not in my IDE (in my IDE it works just fine - the path 'Project/src/Zasoby/yt-dlp' is recognized and the program stars downloading Music), instead while executing the program via a .jar File, the program cannot find the yt-dlp program via its provided path.

Project/src/Zasoby/yt-dlp is a relative path. Whether it works for launching the program depends on the working directory from which the Java program is launched (not necessarily the folder containing the JAR file or any class files), against which that relative path will be resolved. This is not particular to using a JAR file or to any operating system; it's just that Project/src/Zasoby/yt-dlp happens to be correct for the working directory used when you launch from your IDE.

Additionally, however, it looks like yt-dlp provides Windows and Unix wrapper scripts, named yt-dlp.cmd and yt-dlp.sh, respectively. You can ignore the .cmd extension on Windows, but you cannot ignore the .sh extension on MacOS or Linux.

As for your getFilePath() method, it appears to be trying to copy a single yt-dlp binary out of the JAR, onto the filesystem, so that it can be executed from there. There are numerous reasons why that probably won't work, among them:

  • It relies on yt-dlp being packaged into your JAR in the first place. It's possible to make such an arrangement, but I would not expect that to be done by default.
  • The yt-dlp application consists of multiple files and directories, but you are copying out only one.
  • Copying a file to the disk is not sufficient to make it executable on Mac and Linux.

You have a few main options:

  1. Rely on yt-dlp being installed in the system's executable search path, so that you can launch it without specifying any path.

  2. Provide a way to configure your application with the correct (full) path to yt-dlp, and execute it via its full path.

  3. Provide a way for your application to discover the correct path. Typically this would be by relying on a known relationship between the location of the JAR and the location of yt-dlp. Then you can launch it via a correct path.

Some of those options are more likely based on bundling yt-dlp with your wrapper application (but not in the JAR). Others are more oriented toward pairing your application with a separate installation of yt-dlp.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
-2

I program on MacOS and whenever I use Paths I use a base directory ie.

    private static final String BASEDIR = "/Users/<username>/devel/python_3/play/pandas/DNA/store_final";

otherwise your directories will be relative to wherever your java application is running from (ie. its working directory).

I think you can determine that with the following print statement :

System.out.println("Working Directory = " + System.getProperty("user.dir"));

Also I prefer debugging directly from my IDE.

mgillett
  • 74
  • 8
  • This is ambiguous and misleading; the "wherever your java application is running from" will be its working directory, which is not necessarily the directory where the file is located. – Gordon Davisson Jul 17 '23 at 20:37