Skip to content

Commit

Permalink
Added support for transcoding large videos
Browse files Browse the repository at this point in the history
  • Loading branch information
seab authored and seab committed Mar 26, 2023
1 parent 2e3ba48 commit e87f55f
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 23 deletions.
24 changes: 22 additions & 2 deletions src/main/java/fun/seabird/FileNameCreationDateProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@ public class FileNameCreationDateProvider implements CreationDateProvider
{
static final DateTimeFormatter recForgeDtf1 = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm");
static final DateTimeFormatter recForgeDtf2 = DateTimeFormatter.ofPattern("yyyyMMdd-HHmm");
static final DateTimeFormatter merlinDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
static final DateTimeFormatter merlinOldDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
static final DateTimeFormatter merlinNewDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH_mm");

@Override
/**
* This method attempts to find the creation date of the given file by parsing the date and time from the file name.
* It supports three file name formats:
* - "yyyyMMdd_HHmm" or "yyyyMMdd-HHmm" for files created by RecForge on Android devices
* - "yyyy-MM-dd HHmm" for files created by Merlin
* - "yyyy-MM-dd HH_mm" for files created by a newer version of Merlin
*
* If the file name does not match any of these formats or if the date and time information is missing, this method
* returns null.
*
* @param f the file to find the creation date for
* @param hrsOffset the offset in hours to add or subtract from the creation date (can be null)
* @return the creation date of the file, or null if it cannot be determined
*/
public LocalDateTime findCreationDate(File f,Long hrsOffset)
{
String fileName = f.getName();
Expand All @@ -22,7 +37,12 @@ public LocalDateTime findCreationDate(File f,Long hrsOffset)
return null;

if (StringUtils.countMatches(dateTimeOrigStr,"-") == 2)
return parseTime(StringUtils.left(fileName,15),merlinDtf);
{
if (StringUtils.countMatches(dateTimeOrigStr,"_") == 1)
return parseTime(StringUtils.left(fileName,16),merlinNewDtf);

return parseTime(StringUtils.left(fileName,15),merlinOldDtf);
}

return dateTimeOrigStr.contains("_") ? parseTime(dateTimeOrigStr,recForgeDtf1) : parseTime(dateTimeOrigStr,recForgeDtf2);
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/fun/seabird/MediaSortCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class MediaSortCmd
private File csvFile;

private boolean sepYear = false;
private boolean transcodeVideos = false;
private boolean createParentDir = true;
private boolean useSymbolicLinks = false;
private FolderGroup folderGroup = FolderGroup.date;
Expand Down Expand Up @@ -57,4 +58,11 @@ public FolderGroup getFolderGroup() {
public void setFolderGroup(FolderGroup folderGroup) {
this.folderGroup = folderGroup;
}
public boolean isTranscodeVideos() {
return transcodeVideos;
}
public void setTranscodeVideos(boolean transcodeVideos) {
this.transcodeVideos = transcodeVideos;
}

}
61 changes: 52 additions & 9 deletions src/main/java/fun/seabird/MediaSortTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public class MediaSortTask extends Task<Path>

static final String OUTPUT_FOLDER_NAME = "ebird";

static final long MAX_ML_UPLOAD_SIZE_VIDEO = 250l;
static final String TRANSCODED_VIDEO_SUFFIX = "_s";

final String[] invalidChars = new String[] {" ",":",",",".","/","\\",">","<"};
final String[] validChars = new String[] {"-","--","-","-","-","-","-","-"};

Expand All @@ -74,6 +77,8 @@ public class MediaSortTask extends Task<Path>
private final Map<String,SubStats> checklistStatsMap = new TreeMap<>();
private final MediaSortCmd msc;

private transient Process process;

public MediaSortTask(MediaSortCmd msc) {
this.msc = msc;
}
Expand Down Expand Up @@ -260,7 +265,7 @@ private void checkMetadataAndMove(File f,Path outputPath,Long hrsOffset,Set<Stri
String subId = null;
if (mediaTime != null)
subId = rangeMap.get(mediaTime);
File movedFile;
String moveToFolder;

String mediaDateStr = mediaTime.format(folderDtf);

Expand Down Expand Up @@ -312,7 +317,7 @@ else if (FolderGroup.location == folderGroup)
if (!subIdDir.exists())
subIdDir.mkdir();

movedFile = new File(folderPath + File.separator + f.getName());
moveToFolder = folderPath;

subIds.add(subId);
ss.incNumAssetsLocal();
Expand All @@ -324,24 +329,58 @@ else if (FolderGroup.location == folderGroup)
if (!dateDir.exists())
dateDir.mkdir();

movedFile = new File(parentFolderPath + File.separator + f.getName());
moveToFolder = parentFolderPath;
}

boolean isImage = imageExtensions.contains(Files.getFileExtension(f.getName()).toLowerCase());
String fileName = f.getName();
String fileExt = Files.getFileExtension(fileName).toLowerCase();
boolean isImage = imageExtensions.contains(fileExt);
logger.debug("Processing " + f.getPath());
if (hrsOffset != 0l && isImage)
{
String newDateTime = mediaTime.format(imageDtf);
if (changeDateTimeOrig(f,movedFile,newDateTime))
if (changeDateTimeOrig(f,new File(moveToFolder + File.separator + fileName),newDateTime))
{
logger.debug("Adjusted EXIF date of " + f.getPath() + " to " + newDateTime);
f.delete();
return;
}
else
moveFile(f,movedFile);
}
else
moveFile(f,movedFile);

if (msc.isTranscodeVideos() && fileExt.equals("mp4") && !fileName.contains(TRANSCODED_VIDEO_SUFFIX))
{
long fileSizeInBytes = f.length();
long fileSizeInMB = fileSizeInBytes / (1024 * 1024);
if (fileSizeInMB > MAX_ML_UPLOAD_SIZE_VIDEO)
{
String outputFileName = fileName.replaceFirst("[.][^.]+$", "") + TRANSCODED_VIDEO_SUFFIX + ".mp4";
File outputFile = new File(f.getParentFile() + File.separator + outputFileName);
if (!outputFile.exists())
{
logger.info(fileName + " too large for ML upload, transcoding with ffmpeg...");
String convVideoPath = moveToFolder + File.separator + outputFileName;
String[] command = {"ffmpeg", "-i", f.getAbsolutePath(), "-map_metadata", "0:s:0", "-c:v", "libx264", "-crf", "22", "-preset", "medium", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", "-max_muxing_queue_size", "1024", convVideoPath};

ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectError(ProcessBuilder.Redirect.DISCARD);
pb.redirectOutput(ProcessBuilder.Redirect.DISCARD);

try
{
process = pb.start();
int res = process.waitFor();
if (res == 0)
logger.info("Saved converted video to: " + convVideoPath);
process.destroy();
}
catch (InterruptedException | IOException e) {
logger.error("Cannot transcode video to smaller size", e);
}
}
}
}

moveFile(f,new File(moveToFolder + File.separator + fileName));
}

@Override
Expand Down Expand Up @@ -452,5 +491,9 @@ protected Path call() throws Exception
return indexPath;

}

public Process getProcess() {
return process;
}

}
46 changes: 34 additions & 12 deletions src/main/java/fun/seabird/MediaSorterApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import fun.seabird.MediaSortCmd.FolderGroup;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
Expand Down Expand Up @@ -43,6 +42,7 @@ public class MediaSorterApplication extends Application
private static String locSortText = "Create Subfolders by Location";
private static String symbLinkText = "Generate Symbolic Links Instead of Moving Files";
private static String runBtnText = "Run";
private static String transcodeVidText = "Transcode large video (.mp4) files";

private ExtensionFilter csvFilter = new ExtensionFilter("CSV Files","*.csv");

Expand Down Expand Up @@ -73,7 +73,8 @@ public void start(Stage s) throws Exception
CheckBox sepYearDirCb = new CheckBox(sepYearText);
CheckBox parentDirCb = new CheckBox(subDirText);
parentDirCb.setSelected(true);
CheckBox symbLinkCb = new CheckBox(symbLinkText);
CheckBox symbLinkCb = new CheckBox(symbLinkText);
CheckBox transcodeVidCb = new CheckBox(transcodeVidText);

Button runBut = new Button(runBtnText);
runBut.setDisable(true);
Expand Down Expand Up @@ -170,6 +171,11 @@ public void start(Stage s) throws Exception
msc.setUseSymbolicLinks(symbLinkCb.isSelected());
});

transcodeVidCb.setOnAction(event ->
{
msc.setTranscodeVideos(transcodeVidCb.isSelected());
});

runBut.setOnAction(event ->
{
runBut.setDisable(true);
Expand All @@ -180,11 +186,13 @@ public void start(Stage s) throws Exception

OUTPUT_LOG.clear();

Task<Path> task = new MediaSortTask(msc);
MediaSortTask task = new MediaSortTask(msc);

pb.progressProperty().bind(task.progressProperty());

new Thread(task).start();
Thread taskThr = new Thread(task);
taskThr.setDaemon(true);
taskThr.start();

task.setOnSucceeded(success ->
{
Expand All @@ -197,8 +205,21 @@ public void start(Stage s) throws Exception
resBtn.setDisable(false);
resBtn.setVisible(true);
}
});
});

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (task.getProcess() != null)
{
try {
task.getProcess().waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
task.getProcess().destroy();
}
}));
}

);

resBtn.setOnAction(event ->
Expand Down Expand Up @@ -229,23 +250,24 @@ public void start(Stage s) throws Exception
gp.add(sepYearDirCb,0,4,3,1);
gp.add(locSortCb,0,5,3,1);
gp.add(symbLinkCb,0,6,3,1);
gp.add(transcodeVidCb,0,7,3,1);

gp.add(new HBox(10,offsetSlider,offsetLbl),0,7,3,1);
gp.add(new HBox(10,offsetSlider,offsetLbl),0,8,3,1);

gp.add(new Separator(),0,8,3,1);
gp.add(new Separator(),0,9,3,1);

gp.add(runBut,0,9,3,1);
gp.add(runBut,0,10,3,1);

gp.add(pb,0,10,3,2);
gp.add(pb,0,11,3,2);

gp.add(scroll,0,12,3,1);
gp.add(resBtn,0,16,3,1);
gp.add(scroll,0,13,3,1);
gp.add(resBtn,0,17,3,1);

Scene scene = new Scene(gp,FRAME_WIDTH,FRAME_HEIGHT);
Insets padding = new Insets(10,0,10,30); //padding on the left side
((Region) scene.getRoot()).setPadding(padding);
s.setScene(scene);
s.setResizable(true);
s.show();
s.show();
}
}

0 comments on commit e87f55f

Please sign in to comment.