From 898d1467c0de7d9430863deb40bc21d33a58a12b Mon Sep 17 00:00:00 2001 From: Gregory Ballantine Date: Sat, 30 Apr 2022 20:12:32 -0400 Subject: [PATCH] Created a Java w/ Maven project structure; added a simple directory creation and executes an FFMPEG command to transcode videos --- .gitignore | 28 +---- LICENSE | 2 +- pom.xml | 86 +++++++++++++++ src/main/java/tech/bitgoblin/App.java | 17 +++ src/main/java/tech/bitgoblin/io/IOUtils.java | 27 +++++ .../java/tech/bitgoblin/video/Transcoder.java | 102 ++++++++++++++++++ src/test/java/tech/bitgoblin/AppTest.java | 20 ++++ 7 files changed, 258 insertions(+), 24 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/tech/bitgoblin/App.java create mode 100644 src/main/java/tech/bitgoblin/io/IOUtils.java create mode 100644 src/main/java/tech/bitgoblin/video/Transcoder.java create mode 100644 src/test/java/tech/bitgoblin/AppTest.java diff --git a/.gitignore b/.gitignore index adf8f72..5f534f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,5 @@ -# ---> Go -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Go workspace file -go.work - +.classpath +.project +.settings/ +target/ +.idea/ diff --git a/LICENSE b/LICENSE index 5f662b3..e998b9b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) +Copyright (c) 2022, Bit Goblin Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..09058a6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,86 @@ + + + + 4.0.0 + + tech.bitgoblin + Dragoon + 0.1.0 + + Dragoon + https://www.bitgoblin.tech + + + UTF-8 + 11 + 11 + + + + + junit + junit + 4.11 + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + + **/log4j.properties + + + + + tech.bitgoblin.App + + + + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/src/main/java/tech/bitgoblin/App.java b/src/main/java/tech/bitgoblin/App.java new file mode 100644 index 0000000..11446f4 --- /dev/null +++ b/src/main/java/tech/bitgoblin/App.java @@ -0,0 +1,17 @@ +package tech.bitgoblin; + +import tech.bitgoblin.video.Transcoder; + +/** + * The Bit Goblin video transcoder service. + * + */ +public class App { + + public static void main(String[] args) { + // create new Transcoder object and start the service + Transcoder t = new Transcoder("~/dragoon"); + t.transcode(); + } + +} diff --git a/src/main/java/tech/bitgoblin/io/IOUtils.java b/src/main/java/tech/bitgoblin/io/IOUtils.java new file mode 100644 index 0000000..237cfe7 --- /dev/null +++ b/src/main/java/tech/bitgoblin/io/IOUtils.java @@ -0,0 +1,27 @@ +package tech.bitgoblin.io; + +import java.io.File; + +public class IOUtils { + + public static void createDirectory(String path) { + File f = new File(path); + boolean res = f.mkdir(); + + if (res) { + System.out.println(path + " was created."); + } + } + + public static String resolveTilda(String path) { + if (path.startsWith("~" + File.separator)) { + path = System.getProperty("user.home") + path.substring(1); + } else if (path.startsWith("~")) { + // here you can implement reading homedir of other users if you care + throw new UnsupportedOperationException("Home dir expansion not implemented for explicit usernames"); + } + + return path; + } + +} diff --git a/src/main/java/tech/bitgoblin/video/Transcoder.java b/src/main/java/tech/bitgoblin/video/Transcoder.java new file mode 100644 index 0000000..d48535d --- /dev/null +++ b/src/main/java/tech/bitgoblin/video/Transcoder.java @@ -0,0 +1,102 @@ +package tech.bitgoblin.video; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.lang.InterruptedException; +import java.lang.Process; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.Consumer; +import java.util.concurrent.Executors; + +import tech.bitgoblin.io.IOUtils; + +public class Transcoder { + + private String repo_dir; + private String ffmpeg_path = "/usr/bin/ffmpeg"; + + // only define the working directory; use default FFMPEG path + public Transcoder(String repo_dir) { + this.repo_dir = IOUtils.resolveTilda(repo_dir); + this.initDirectory(); + } + // define a custom FFMPEG binary path + public Transcoder(String repo_dir, String ffmpeg_path) { + this.repo_dir = IOUtils.resolveTilda(repo_dir); + this.ffmpeg_path = ffmpeg_path; + this.initDirectory(); + } + + // transcode files in the working directory + public void transcode() { + // search for files + System.out.println("Searching for files to transcode in " + this.repo_dir); + File repo = new File(Paths.get(this.repo_dir, "ingest").toString()); + File[] sourceFiles = repo.listFiles(); + + // transcode + System.out.println("Transcoding files in " + this.repo_dir + "/ingest..."); + for (File f : sourceFiles) { + String filePath = f.toString().substring(0, f.toString().lastIndexOf(".")); + String filename = Paths.get(filePath).getFileName().toString(); + String outputPath = Paths.get(this.repo_dir, "output", String.format("%s.mov", filename)).toString(); + + String cmd = String.format("%s -i %s -c:v %s -vf \"%s\" -profile:v %s -c:a %s %s", + this.ffmpeg_path, // FFMPEG binary path + f.toString(), // input file + "dnxhd", // video codec + "scale=1920x1080,fps=60,format=yuv422p", // video format + "dnxhr_hq", // video profile + "pcm_s16le", // audio codec + outputPath // output file path + ); + + ProcessBuilder pb = new ProcessBuilder(cmd.split("\\s+")); + pb.redirectErrorStream(true); + pb.redirectOutput(ProcessBuilder.Redirect.PIPE); + try { + Process process = pb.start(); + int ret = process.waitFor(); + System.out.printf("Program exited with code: %d", ret); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + // end output + System.out.println("------------ End of transcoding ------------"); + System.out.println(); + } + + // ensures the transcoder's working directory is available + private void initDirectory() { + // create transcoder directory + IOUtils.createDirectory(this.repo_dir); + // create the sub-directories + IOUtils.createDirectory(Paths.get(this.repo_dir, "ingest").toString()); + IOUtils.createDirectory(Paths.get(this.repo_dir, "archive").toString()); + IOUtils.createDirectory(Paths.get(this.repo_dir, "output").toString()); + } + + + // Stream handler class for the Transcoder + private static class StreamGobbler implements Runnable { + private InputStream inputStream; + private Consumer consumer; + + public StreamGobbler(InputStream inputStream, Consumer consumer) { + this.inputStream = inputStream; + this.consumer = consumer; + } + + @Override + public void run() { + new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer); + } + } + +} diff --git a/src/test/java/tech/bitgoblin/AppTest.java b/src/test/java/tech/bitgoblin/AppTest.java new file mode 100644 index 0000000..48e0d65 --- /dev/null +++ b/src/test/java/tech/bitgoblin/AppTest.java @@ -0,0 +1,20 @@ +package tech.bitgoblin; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +}