Skip to content
Merged
105 changes: 69 additions & 36 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -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 }}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ We currently support Java 11 and above. Use Maven to install the dependency.
<dependency>
<groupId>net.bramp.ffmpeg</groupId>
<artifactId>ffmpeg</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.9.0-SNAPSHOT</version>
</dependency>
```

Expand Down
34 changes: 18 additions & 16 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.bramp.ffmpeg</groupId>
<artifactId>ffmpeg</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.9.0-SNAPSHOT</version>

<name>FFmpeg Wrapper</name>
<description>Simple Java wrapper around FFmpeg command-line interface</description>
Expand All @@ -11,7 +11,6 @@
<scm>
<url>https://github.com/bramp/ffmpeg-cli-wrapper</url>
<connection>scm:git:git@github.com:bramp/ffmpeg-cli-wrapper.git</connection>
<tag>ffmpeg-0.7.0</tag>
</scm>

<issueManagement>
Expand Down Expand Up @@ -208,13 +207,9 @@

<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<id>central</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

<build>
Expand Down Expand Up @@ -314,9 +309,9 @@
<version>3.0.1</version>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version>
</plugin>
<plugin>
<groupId>com.spotify.fmt</groupId>
Expand Down Expand Up @@ -431,6 +426,12 @@
<goals>
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
</plugin>
Expand All @@ -447,13 +448,14 @@
</plugin>

<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
<waitUntil>published</waitUntil>
</configuration>
</plugin>

Expand Down Expand Up @@ -732,7 +734,7 @@
</profile>

<profile>
<id>Java 11+</id>
<id>java11plus</id>
<activation>
<jdk>[11,)</jdk>
</activation>
Expand Down
26 changes: 16 additions & 10 deletions src/main/java/net/bramp/ffmpeg/FFmpegUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

/**
Expand All @@ -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));
}

/**
Expand Down
12 changes: 7 additions & 5 deletions src/test/java/net/bramp/ffmpeg/FFmpegUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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"));
Expand Down
Loading