diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 83e44400..84e8dd1f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,54 +1,87 @@
-# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
-
name: Java CI with Maven
on:
push:
branches: [ "master" ]
+ tags: [ "ffmpeg-*" ]
pull_request:
branches: [ "master" ]
+ workflow_dispatch:
+ inputs:
+ skipTests:
+ description: 'Skip tests during publish'
+ required: false
+ default: 'false'
jobs:
- build:
+ test:
runs-on: ubuntu-latest
- # Enable debugging to help resolve:
- # https://github.com/federicocarboni/setup-ffmpeg/issues/19
- environment: debug
strategy:
fail-fast: false
matrix:
- # Long term supported versions
java-version: [11, 17, 21]
- # TODO Should we test locales? The old travis setup did, see:
- # https://github.com/bramp/ffmpeg-cli-wrapper/pull/55
-
name: JDK ${{ matrix.java-version }}
steps:
- - uses: actions/checkout@v6
-
- - name: Set up FFmpeg
- uses: FedericoCarboni/setup-ffmpeg@v3
- id: setup-ffmpeg
- with:
- ffmpeg-version: release
-
- - name: Set up JDK ${{ matrix.java-version }}
- uses: actions/setup-java@v5
- with:
- java-version: ${{ matrix.java-version }}
- distribution: 'temurin'
- cache: maven
-
- - name: Compile with Maven
- run: mvn --batch-mode --update-snapshots compile
-
- - name: Test with Maven, Package and Verify with Maven
- run: mvn --batch-mode --update-snapshots verify -Dgpg.skip
-
- # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- - name: Update dependency graph
- uses: advanced-security/maven-dependency-submission-action@v5
- continue-on-error: true
+ - uses: actions/checkout@v4
+
+ - name: Set up FFmpeg
+ uses: FedericoCarboni/setup-ffmpeg@v3
+ with:
+ ffmpeg-version: release
+
+ - name: Set up JDK ${{ matrix.java-version }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ matrix.java-version }}
+ distribution: 'temurin'
+ cache: maven
+
+ - name: Test with Maven
+ run: mvn --batch-mode verify -Dgpg.skip -DskipTests=${{ github.event.inputs.skipTests || 'false' }}
+
+ - name: Update dependency graph
+ if: matrix.java-version == '21' && github.event_name == 'push' && github.ref == 'refs/heads/master'
+ uses: advanced-security/maven-dependency-submission-action@v4
+ continue-on-error: true
+
+ publish:
+ needs: test
+ # Only publish on tags (ffmpeg-*) or manual dispatch
+ # and only if it's not a pull request
+ if: |
+ (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/ffmpeg-')) ||
+ (github.event_name == 'workflow_dispatch')
+ runs-on: ubuntu-latest
+ environment: publish
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up FFmpeg
+ uses: FedericoCarboni/setup-ffmpeg@v3
+ with:
+ ffmpeg-version: release
+
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ server-id: central
+ server-username: CENTRAL_USERNAME
+ server-password: CENTRAL_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: GPG_PASSPHRASE
+ cache: 'maven'
+
+ - name: Publish to Sonatype Central Portal
+ run: |
+ mvn clean deploy \
+ -DskipTests=true \
+ --no-transfer-progress \
+ -P java11plus
+ env:
+ CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
+ CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
diff --git a/README.md b/README.md
index d9386ad1..ebb6b250 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ We currently support Java 11 and above. Use Maven to install the dependency.
net.bramp.ffmpeg
ffmpeg
- 0.8.1-SNAPSHOT
+ 0.9.0-SNAPSHOT
```
diff --git a/pom.xml b/pom.xml
index 4d6f410f..2ae50880 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
net.bramp.ffmpeg
ffmpeg
- 0.8.1-SNAPSHOT
+ 0.9.0-SNAPSHOT
FFmpeg Wrapper
Simple Java wrapper around FFmpeg command-line interface
@@ -11,7 +11,6 @@
https://github.com/bramp/ffmpeg-cli-wrapper
scm:git:git@github.com:bramp/ffmpeg-cli-wrapper.git
- ffmpeg-0.7.0
@@ -208,13 +207,9 @@
- ossrh
+ central
https://oss.sonatype.org/content/repositories/snapshots
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
@@ -314,9 +309,9 @@
3.0.1
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.13
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.7.0
com.spotify.fmt
@@ -431,6 +426,12 @@
sign
+
+
+ --pinentry-mode
+ loopback
+
+
@@ -447,13 +448,14 @@
- org.sonatype.plugins
- nexus-staging-maven-plugin
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.7.0
true
- ossrh
- https://oss.sonatype.org/
- true
+ central
+ true
+ published
@@ -732,7 +734,7 @@
- Java 11+
+ java11plus
[11,)
diff --git a/src/main/java/net/bramp/ffmpeg/FFmpegUtils.java b/src/main/java/net/bramp/ffmpeg/FFmpegUtils.java
index 53ba3b8c..ca92a3a9 100644
--- a/src/main/java/net/bramp/ffmpeg/FFmpegUtils.java
+++ b/src/main/java/net/bramp/ffmpeg/FFmpegUtils.java
@@ -24,7 +24,7 @@ public final class FFmpegUtils {
static final Gson gson = FFmpegUtils.setupGson();
static final Pattern BITRATE_REGEX = Pattern.compile("(\\d+(?:\\.\\d+)?)kbits/s");
- static final Pattern TIME_REGEX = Pattern.compile("(\\d+):(\\d+):(\\d+(?:\\.\\d+)?)");
+ static final Pattern TIME_REGEX = Pattern.compile("(-?)(\\d+):(\\d+):(\\d+(?:\\.\\d+)?)");
static final CharMatcher ZERO = CharMatcher.is('0');
FFmpegUtils() {
@@ -55,9 +55,11 @@ public static String millisecondsToString(long milliseconds) {
* @return the timecode representation.
*/
public static String toTimecode(long duration, TimeUnit units) {
- // FIXME Negative durations are also supported.
- // https://www.ffmpeg.org/ffmpeg-utils.html#Time-duration
- checkArgument(duration >= 0, "duration must be positive");
+ String prefix = "";
+ if (duration < 0) {
+ prefix = "-";
+ duration = Math.abs(duration);
+ }
long nanoseconds = units.toNanos(duration); // TODO This will clip at Long.MAX_VALUE
long seconds = units.toSeconds(duration);
@@ -69,11 +71,14 @@ public static String toTimecode(long duration, TimeUnit units) {
long hours = MINUTES.toHours(minutes);
minutes -= HOURS.toMinutes(hours);
+ String result;
if (ns == 0) {
- return String.format("%02d:%02d:%02d", hours, minutes, seconds);
+ result = String.format("%02d:%02d:%02d", hours, minutes, seconds);
+ } else {
+ result = ZERO.trimTrailingFrom(String.format("%02d:%02d:%02d.%09d", hours, minutes, seconds, ns));
}
- return ZERO.trimTrailingFrom(String.format("%02d:%02d:%02d.%09d", hours, minutes, seconds, ns));
+ return prefix + result;
}
/**
@@ -95,11 +100,12 @@ public static long fromTimecode(String time) {
throw new IllegalArgumentException("invalid time '" + time + "'");
}
- long hours = Long.parseLong(m.group(1));
- long mins = Long.parseLong(m.group(2));
- double secs = Double.parseDouble(m.group(3));
+ long sign = m.group(1).equals("-") ? -1 : 1;
+ long hours = Long.parseLong(m.group(2));
+ long mins = Long.parseLong(m.group(3));
+ double secs = Double.parseDouble(m.group(4));
- return HOURS.toNanos(hours) + MINUTES.toNanos(mins) + (long) (SECONDS.toNanos(1) * secs);
+ return sign * (HOURS.toNanos(hours) + MINUTES.toNanos(mins) + (long) (SECONDS.toNanos(1) * secs));
}
/**
diff --git a/src/test/java/net/bramp/ffmpeg/FFmpegUtilsTest.java b/src/test/java/net/bramp/ffmpeg/FFmpegUtilsTest.java
index 4993fb1b..035bc69f 100644
--- a/src/test/java/net/bramp/ffmpeg/FFmpegUtilsTest.java
+++ b/src/test/java/net/bramp/ffmpeg/FFmpegUtilsTest.java
@@ -23,22 +23,23 @@ public void testMillisecondsToString() {
assertEquals("00:00:00.001", millisecondsToString(1));
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
@SuppressWarnings({"deprecation", "InlineMeInliner"})
public void testMillisecondsToStringNegative() {
- millisecondsToString(-1);
+ assertEquals("-00:00:00.001", millisecondsToString(-1));
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
@SuppressWarnings({"deprecation", "InlineMeInliner"})
- public void testMillisecondsToStringNegativeMinValue() {
- millisecondsToString(Long.MIN_VALUE);
+ public void testMillisecondsToStringNegativeLarge() {
+ assertEquals("-34:17:36.789", millisecondsToString(-123456789));
}
@Test
public void testToTimecode() {
assertEquals("00:00:00", toTimecode(0, TimeUnit.NANOSECONDS));
assertEquals("00:00:00.000000001", toTimecode(1, TimeUnit.NANOSECONDS));
+ assertEquals("-00:00:00.000000001", toTimecode(-1, TimeUnit.NANOSECONDS));
assertEquals("00:00:00.000001", toTimecode(1, TimeUnit.MICROSECONDS));
assertEquals("00:00:00.001", toTimecode(1, TimeUnit.MILLISECONDS));
assertEquals("00:00:01", toTimecode(1, TimeUnit.SECONDS));
@@ -49,6 +50,7 @@ public void testToTimecode() {
@Test
public void testFromTimecode() {
assertEquals(63123000000L, fromTimecode("00:01:03.123"));
+ assertEquals(-63123000000L, fromTimecode("-00:01:03.123"));
assertEquals(63000000000L, fromTimecode("00:01:03"));
assertEquals(5025678000000L, fromTimecode("01:23:45.678"));
assertEquals(0, fromTimecode("00:00:00"));