2021-08-23 16:50:18 +02:00
using Flowframes.Data ;
using Flowframes.Magick ;
using Flowframes.Main ;
using Flowframes.Media ;
using Flowframes.MiscUtils ;
using Flowframes.Ui ;
using Force.Crc32 ;
using ImageMagick ;
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Security.Cryptography ;
using System.Text ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
namespace Flowframes.IO
{
class IoUtils
{
2024-01-09 16:23:14 +01:00
public static Image GetImage ( string path , bool allowMagickFallback = true , bool log = true )
{
2024-06-24 11:36:43 +02:00
var incompatibleExtensions = new List < string > ( ) { "EXR" } ;
2021-08-23 16:50:18 +02:00
try
{
2024-09-03 22:08:38 +02:00
string ext = new FileInfo ( path ) . Extension . TrimStart ( '.' ) . Upper ( ) ;
2024-06-24 11:36:43 +02:00
if ( incompatibleExtensions . Contains ( ext ) )
return null ;
2024-01-09 16:23:14 +01:00
using ( FileStream stream = new FileStream ( path , FileMode . Open , FileAccess . Read ) )
return Image . FromStream ( stream ) ;
}
catch
2021-08-23 16:50:18 +02:00
{
try
{
2024-01-09 16:23:14 +01:00
if ( log )
Logger . Log ( $"GetImage: Native image reading for '{Path.GetFileName(path)}' failed - Using Magick.NET fallback instead." , true ) ;
2025-12-14 15:27:46 +01:00
MagickImage img = new MagickImage ( path ) ;
// Bitmap bitmap = img.ToBitmap();
img . Format = MagickFormat . Bmp ;
using ( var memStream = new MemoryStream ( ) )
{
img . Write ( memStream ) ;
return new Bitmap ( memStream ) ;
}
2024-01-09 16:23:14 +01:00
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
if ( log )
Logger . Log ( $"GetImage failed: {e.Message}" , true ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return null ;
2021-08-23 16:50:18 +02:00
}
}
2024-01-09 16:23:14 +01:00
}
public static string [ ] ReadLines ( string path )
{
if ( ! File . Exists ( path ) )
return new string [ 0 ] ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
List < string > lines = new List < string > ( ) ;
using ( var stream = new FileStream ( path , FileMode . Open , FileAccess . Read , FileShare . ReadWrite , 0x1000 , FileOptions . SequentialScan ) )
2023-02-05 12:40:58 +01:00
2021-08-23 16:50:18 +02:00
using ( var reader = new StreamReader ( stream , Encoding . UTF8 ) )
2024-01-09 16:23:14 +01:00
{
string line ;
while ( ( line = reader . ReadLine ( ) ) ! = null )
lines . Add ( line ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return lines . ToArray ( ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static bool IsPathDirectory ( string path )
{
if ( path = = null )
throw new ArgumentNullException ( "path" ) ;
2021-08-23 16:50:18 +02:00
path = path . Trim ( ) ;
2024-01-09 16:23:14 +01:00
if ( Directory . Exists ( path ) )
return true ;
2021-08-23 16:50:18 +02:00
if ( File . Exists ( path ) )
2024-01-09 16:23:14 +01:00
return false ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( new string [ 2 ] { "\\" , "/" } . Any ( ( string x ) = > path . EndsWith ( x ) ) )
return true ;
2021-08-23 16:50:18 +02:00
return string . IsNullOrWhiteSpace ( Path . GetExtension ( path ) ) ;
2024-01-09 16:23:14 +01:00
}
public static bool IsFileValid ( string path )
{
if ( path = = null )
return false ;
if ( ! File . Exists ( path ) )
return false ;
return true ;
}
public static bool IsDirValid ( string path )
{
if ( path = = null )
return false ;
if ( ! Directory . Exists ( path ) )
return false ;
return true ;
}
public static bool IsPathValid ( string path )
{
if ( path = = null )
return false ;
if ( IsPathDirectory ( path ) )
return IsDirValid ( path ) ;
else
return IsFileValid ( path ) ;
}
2024-09-03 22:01:32 +02:00
public static bool IsPathOneDrive ( string path )
{
2024-12-03 00:40:24 +01:00
if ( path . IsEmpty ( ) )
return false ;
2024-09-03 22:01:32 +02:00
return path . Lower ( ) . Replace ( "\\" , "/" ) . MatchesWildcard ( "*:/users/*/onedrive*" ) ;
}
2024-01-09 16:23:14 +01:00
public static void CopyDir ( string sourceDirectory , string targetDirectory , bool move = false )
{
Directory . CreateDirectory ( targetDirectory ) ;
DirectoryInfo source = new DirectoryInfo ( sourceDirectory ) ;
DirectoryInfo target = new DirectoryInfo ( targetDirectory ) ;
CopyWork ( source , target , move ) ;
}
private static void CopyWork ( DirectoryInfo source , DirectoryInfo target , bool move )
{
DirectoryInfo [ ] directories = source . GetDirectories ( ) ;
foreach ( DirectoryInfo directoryInfo in directories )
{
CopyWork ( directoryInfo , target . CreateSubdirectory ( directoryInfo . Name ) , move ) ;
}
FileInfo [ ] files = source . GetFiles ( ) ;
foreach ( FileInfo fileInfo in files )
{
if ( move )
fileInfo . MoveTo ( Path . Combine ( target . FullName , fileInfo . Name ) ) ;
else
fileInfo . CopyTo ( Path . Combine ( target . FullName , fileInfo . Name ) , overwrite : true ) ;
}
}
/// <summary>
/// Async version of DeleteContentsOfDir, won't block main thread.
/// </summary>
public static async Task < bool > DeleteContentsOfDirAsync ( string path )
{
2024-08-25 21:18:38 +02:00
return await Task . Run ( ( ) = > DeleteContentsOfDir ( path ) ) ;
2024-01-09 16:23:14 +01:00
}
/// <summary>
/// Delete everything inside a directory except the dir itself.
/// </summary>
public static bool DeleteContentsOfDir ( string path )
{
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
DeleteIfExists ( path ) ;
Directory . CreateDirectory ( path ) ;
return true ;
}
catch ( Exception e )
{
Logger . Log ( "DeleteContentsOfDir Error: " + e . Message , true ) ;
return false ;
}
}
public static void ReplaceInFilenamesDir ( string dir , string textToFind , string textToReplace , bool recursive = true , string wildcard = "*" )
{
int counter = 1 ;
DirectoryInfo d = new DirectoryInfo ( dir ) ;
FileInfo [ ] files ;
if ( recursive )
files = d . GetFiles ( wildcard , SearchOption . AllDirectories ) ;
else
files = d . GetFiles ( wildcard , SearchOption . TopDirectoryOnly ) ;
foreach ( FileInfo file in files )
{
ReplaceInFilename ( file . FullName , textToFind , textToReplace ) ;
counter + + ;
}
}
public static void ReplaceInFilename ( string path , string textToFind , string textToReplace )
{
string ext = Path . GetExtension ( path ) ;
string newFilename = Path . GetFileNameWithoutExtension ( path ) . Replace ( textToFind , textToReplace ) ;
string targetPath = Path . Combine ( Path . GetDirectoryName ( path ) , newFilename + ext ) ;
if ( File . Exists ( targetPath ) )
{
//Program.Print("Skipped " + path + " because a file with the target name already exists.");
return ;
}
File . Move ( path , targetPath ) ;
}
public static int GetAmountOfFiles ( string path , bool recursive , string wildcard = "*" )
2021-08-23 16:50:18 +02:00
{
try
{
2024-01-09 16:23:14 +01:00
if ( ! Directory . Exists ( path ) )
return 0 ;
2023-01-31 11:45:12 +01:00
2021-08-23 16:50:18 +02:00
DirectoryInfo d = new DirectoryInfo ( path ) ;
2024-01-09 16:23:14 +01:00
FileInfo [ ] files = null ;
if ( recursive )
files = d . GetFiles ( wildcard , SearchOption . AllDirectories ) ;
else
files = d . GetFiles ( wildcard , SearchOption . TopDirectoryOnly ) ;
return files . Length ;
}
catch
{
return 0 ;
}
}
static bool TryCopy ( string source , string target , bool overwrite = true , bool showLog = false )
{
try
{
File . Copy ( source , target , overwrite ) ;
}
catch ( Exception e )
{
if ( showLog )
Logger . Log ( $"Failed to move '{source}' to '{target}' (Overwrite: {overwrite}): {e.Message}, !showLog" ) ;
return false ;
}
return true ;
}
public static bool TryMove ( string source , string target , bool overwrite = true , bool showLog = false )
{
try
{
if ( overwrite & & File . Exists ( target ) )
File . Delete ( target ) ;
File . Move ( source , target ) ;
}
catch ( Exception e )
{
Logger . Log ( $"Failed to move '{source}' to '{target}' (Overwrite: {overwrite}): {e.Message}" , ! showLog ) ;
return false ;
}
return true ;
}
public static async Task RenameCounterDir ( string path , int startAt = 0 , int zPad = 8 , bool inverse = false )
{
2021-08-23 16:50:18 +02:00
Stopwatch sw = new Stopwatch ( ) ;
sw . Restart ( ) ;
2024-01-09 16:23:14 +01:00
int counter = startAt ;
DirectoryInfo d = new DirectoryInfo ( path ) ;
FileInfo [ ] files = d . GetFiles ( ) ;
var filesSorted = files . OrderBy ( n = > n ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( inverse )
filesSorted . Reverse ( ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
foreach ( FileInfo file in files )
{
string dir = new DirectoryInfo ( file . FullName ) . Parent . FullName ;
File . Move ( file . FullName , Path . Combine ( dir , counter . ToString ( ) . PadLeft ( zPad , '0' ) + Path . GetExtension ( file . FullName ) ) ) ;
counter + + ;
2021-08-23 16:50:18 +02:00
if ( sw . ElapsedMilliseconds > 100 )
{
await Task . Delay ( 1 ) ;
sw . Restart ( ) ;
}
2024-01-09 16:23:14 +01:00
}
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static async Task ReverseRenaming ( string basePath , Dictionary < string , string > oldNewMap ) // Relative -> absolute paths
{
Dictionary < string , string > absPaths = oldNewMap . ToDictionary ( x = > Path . Combine ( basePath , x . Key ) , x = > Path . Combine ( basePath , x . Value ) ) ;
await ReverseRenaming ( absPaths ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static async Task ReverseRenaming ( Dictionary < string , string > oldNewMap ) // Takes absolute paths only
{
if ( oldNewMap = = null | | oldNewMap . Count < 1 ) return ;
int counter = 0 ;
int failCount = 0 ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
foreach ( KeyValuePair < string , string > pair in oldNewMap )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
bool success = TryMove ( pair . Value , pair . Key ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( ! success )
failCount + + ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( failCount > = 100 )
break ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
counter + + ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( counter % 1000 = = 0 )
await Task . Delay ( 1 ) ;
}
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static async Task < Fraction > GetVideoFramerate ( string path )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
string [ ] preferFfmpegReadoutFormats = new string [ ] { ".gif" , ".png" , ".apng" , ".webp" } ;
2024-09-03 22:08:38 +02:00
bool preferFfmpegReadout = preferFfmpegReadoutFormats . Contains ( Path . GetExtension ( path ) . Lower ( ) ) ;
2021-08-23 16:50:18 +02:00
Fraction fps = new Fraction ( ) ;
2024-01-09 16:23:14 +01:00
try
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
fps = await FfmpegCommands . GetFramerate ( path , preferFfmpegReadout ) ;
Logger . Log ( "Detected FPS of " + Path . GetFileName ( path ) + " as " + fps + " FPS" , true ) ;
}
catch
{
Logger . Log ( "Failed to read FPS - Please enter it manually." ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return fps ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static Fraction GetVideoFramerateForDir ( string path )
{
2021-08-23 16:50:18 +02:00
Fraction fps = new Fraction ( ) ;
try
{
2024-01-09 16:23:14 +01:00
string parentDir = path . GetParentDir ( ) ;
string fpsFile = Path . Combine ( parentDir , "fps.ini" ) ;
2021-08-23 16:50:18 +02:00
fps = new Fraction ( float . Parse ( ReadLines ( fpsFile ) [ 0 ] ) ) ;
2024-01-09 16:23:14 +01:00
Logger . Log ( $"Got {fps} FPS from file: " + fpsFile ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
Fraction guiFps = Program . mainForm . GetCurrentSettings ( ) . inFps ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
DialogResult dialogResult = UiUtils . ShowMessageBox ( "A frame rate file has been found in the parent directory.\n\n" +
$"Click \" Yes \ " to use frame rate from the file ({fps}) or \"No\" to use current FPS set in GUI ({guiFps})" , "Load Frame Rate From fps.ini?" , MessageBoxButtons . YesNo ) ;
if ( dialogResult = = DialogResult . Yes )
return fps ;
else if ( dialogResult = = DialogResult . No )
return guiFps ;
}
catch { }
return fps ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static async Task < Size > GetVideoOrFramesRes ( string path )
2021-08-23 16:50:18 +02:00
{
try
{
2024-01-09 16:23:14 +01:00
if ( ! IsPathDirectory ( path ) ) // If path is video
{
2024-08-25 21:43:06 +02:00
return GetVideoRes ( path ) ;
2024-01-09 16:23:14 +01:00
}
else // Path is frame folder
{
Image thumb = await MainUiFunctions . GetThumbnail ( path ) ;
2024-08-25 21:43:06 +02:00
return new Size ( thumb . Width , thumb . Height ) ;
2024-01-09 16:23:14 +01:00
}
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-08-25 21:43:06 +02:00
Logger . Log ( $"GetVideoOrFramesRes Error: {e.Message}" ) ;
return new Size ( ) ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static Size GetVideoRes ( string path )
{
try
{
if ( path . IsConcatFile ( ) )
2023-02-15 12:27:36 +01:00
path = ReadFileFirstLine ( path ) . Split ( '\'' ) [ 1 ] . Split ( '\'' ) [ 0 ] ;
2024-08-25 21:43:06 +02:00
return FfmpegCommands . GetSize ( path ) ;
2024-01-09 16:23:14 +01:00
}
catch ( Exception ex )
{
Logger . Log ( "Failed to read video size!" ) ;
Logger . Log ( ex . ToString ( ) , true ) ;
2024-08-25 21:43:06 +02:00
return new Size ( ) ;
2024-01-09 16:23:14 +01:00
}
}
/// <summary>
/// Async (background thread) version of TryDeleteIfExists. Safe to run without awaiting.
/// </summary>
public static async Task < bool > TryDeleteIfExistsAsync ( string path , int retries = 10 )
{
string renamedPath = path ;
try
{
if ( IsPathDirectory ( path ) )
{
while ( Directory . Exists ( renamedPath ) )
renamedPath + = "_" ;
if ( path ! = renamedPath )
Directory . Move ( path , renamedPath ) ;
}
else
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
while ( File . Exists ( renamedPath ) )
renamedPath + = "_" ;
if ( path ! = renamedPath )
File . Move ( path , renamedPath ) ;
}
path = renamedPath ;
2024-09-25 14:08:26 +02:00
TryDeleteIfExists ( path ) ;
return true ;
2024-01-09 16:23:14 +01:00
}
catch ( Exception e )
{
Logger . Log ( $"TryDeleteIfExistsAsync Move Exception: {e.Message} [{retries} retries left]" , true ) ;
if ( retries > 0 )
{
await Task . Delay ( 2000 ) ;
2024-09-25 14:08:26 +02:00
return await TryDeleteIfExistsAsync ( path , retries - 1 ) ;
2024-01-09 16:23:14 +01:00
}
2021-08-23 16:50:18 +02:00
else
{
2024-01-09 16:23:14 +01:00
return false ;
}
}
}
/// <summary>
/// Delete a path if it exists. Works for files and directories. Returns success status.
/// </summary>
2024-09-25 14:08:26 +02:00
public static bool TryDeleteIfExists ( string path ) // Returns true if no exception occurs
2024-01-09 16:23:14 +01:00
{
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
if ( path = = null )
return false ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
DeleteIfExists ( path ) ;
return true ;
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"TryDeleteIfExists: Error trying to delete {path}: {e.Message}" , true ) ;
return false ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static bool DeleteIfExists ( string path , bool log = false ) // Returns true if the file/dir exists
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
if ( log )
Logger . Log ( $"DeleteIfExists({path})" , true ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( ! IsPathDirectory ( path ) & & File . Exists ( path ) )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
File . Delete ( path ) ;
return true ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
if ( IsPathDirectory ( path ) & & Directory . Exists ( path ) )
{
Directory . Delete ( path , true ) ;
return true ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return false ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
/// <summary> Delete file at <paramref name="path"/> if it's smaller than <paramref name="thresholdKb"/> KB. </summary>
/// <returns> True if the path was valid and the file size was below the threshold, otherwise False. </returns>
public static bool DeleteIfSmallerThanKb ( string path , int thresholdKb = 4 )
{
if ( File . Exists ( path ) & & ( new FileInfo ( path ) . Length / 1024f ) < thresholdKb )
{
TryDeleteIfExists ( path ) ;
return true ;
}
2024-01-10 01:50:23 +01:00
else if ( Directory . Exists ( path ) & & GetDirSize ( path , true ) < thresholdKb )
{
TryDeleteIfExists ( path ) ;
return true ;
}
2024-01-09 16:23:14 +01:00
return false ;
}
/// <summary> Add ".old" suffix to an existing file to avoid it getting overwritten. If one already exists, it will be ".old.old" etc. </summary>
public static void RenameExistingFileOrDir ( string path , int deleteSmallFileThresh = 2 )
{
2021-08-23 16:50:18 +02:00
try
{
2023-02-20 19:30:23 +01:00
if ( File . Exists ( path ) )
{
2024-01-09 16:23:14 +01:00
if ( DeleteIfSmallerThanKb ( path , deleteSmallFileThresh ) )
return ;
2023-02-20 19:30:23 +01:00
string ext = Path . GetExtension ( path ) ;
string renamedPath = path ;
2021-08-23 16:50:18 +02:00
2023-02-20 19:30:23 +01:00
while ( File . Exists ( renamedPath ) )
renamedPath = Path . ChangeExtension ( renamedPath , null ) + ".old" + ext ;
2021-08-23 16:50:18 +02:00
2023-02-20 19:30:23 +01:00
File . Move ( path , renamedPath ) ;
}
else if ( Directory . Exists ( path ) )
2024-01-09 16:23:14 +01:00
{
2023-02-20 19:30:23 +01:00
string renamedPath = path ;
while ( Directory . Exists ( renamedPath ) )
renamedPath = renamedPath + ".old" ;
Directory . Move ( path , renamedPath ) ;
}
2024-01-09 16:23:14 +01:00
}
catch ( Exception e )
{
Logger . Log ( $"RenameExistingFileOrDir: Failed to rename '{path}': {e.Message}" , true ) ;
}
}
/// <summary>
/// Add ".old" suffix to an existing folder to avoid it getting overwritten. If one already exists, it will be ".old.old" etc.
/// </summary>
public static void RenameExistingFolder ( string path )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
if ( ! Directory . Exists ( path ) )
return ;
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
string renamedPath = path ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
while ( Directory . Exists ( renamedPath ) )
renamedPath + = ".old" ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
Directory . Move ( path , renamedPath ) ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"RenameExistingFolder: Failed to rename '{path}': {e.Message}" , true ) ;
2021-08-23 16:50:18 +02:00
}
}
2024-01-09 16:23:14 +01:00
/// <summary>
/// Easily rename a file without needing to specify the full move path
/// </summary>
public static bool RenameFile ( string path , string newName , bool alsoRenameExtension = false )
{
try
{
string dir = Path . GetDirectoryName ( path ) ;
string ext = Path . GetExtension ( path ) ;
string movePath = Path . Combine ( dir , newName ) ;
if ( ! alsoRenameExtension )
movePath + = ext ;
File . Move ( path , movePath ) ;
return true ;
}
catch ( Exception e )
{
Logger . Log ( $"Failed to rename '{path}' to '{newName}': {e.Message}" , true ) ;
return false ;
}
}
2024-11-08 11:54:26 +01:00
public static async Task < string > GetCurrentExportFilename ( bool fpsLimit , bool isImgSeq = false , bool includeExt = true )
2024-01-09 16:23:14 +01:00
{
InterpSettings curr = Interpolate . currentSettings ;
2024-11-28 16:08:04 +01:00
float fps = fpsLimit ? curr . outFpsResampled . Float : curr . outFps . Float ;
2021-08-23 16:50:18 +02:00
2024-11-12 22:13:59 +01:00
Size outRes = curr . OutputResolution ; // TODO: Replace with EncodeResolution once implemented?
2024-01-09 16:23:14 +01:00
string pattern = Config . Get ( Config . Key . exportNamePattern ) ;
string inName = Interpolate . currentSettings . inputIsFrames ? Path . GetFileName ( curr . inPath ) : Path . GetFileNameWithoutExtension ( curr . inPath ) ;
2021-08-23 16:50:18 +02:00
bool encodeBoth = Config . GetInt ( Config . Key . maxFpsMode ) = = 0 ;
2024-11-08 11:54:26 +01:00
bool addFpsLimitSuffix = fpsLimit & & ( ! pattern . Contains ( "[FPS]" ) & & ! pattern . Contains ( "[ROUNDFPS]" ) ) & & encodeBoth ;
2024-01-09 16:23:14 +01:00
string filename = pattern ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
filename = filename . Replace ( "[NAME]" , inName ) ;
filename = filename . Replace ( "[FULLNAME]" , Path . GetFileName ( curr . inPath ) ) ;
2024-11-26 12:14:44 +01:00
filename = filename . Replace ( "[FACTOR]" , curr . interpFactor . ToString ( ) ) ;
2024-09-03 22:08:38 +02:00
filename = filename . Replace ( "[AI]" , curr . ai . NameShort . Upper ( ) ) ;
2024-01-09 16:23:14 +01:00
filename = filename . Replace ( "[MODEL]" , curr . model . Name . Remove ( " " ) ) ;
2024-11-26 12:14:44 +01:00
filename = filename . Replace ( "[FPS]" , fps . ToString ( "0.###" ) ) ;
2021-08-23 16:50:18 +02:00
filename = filename . Replace ( "[ROUNDFPS]" , fps . RoundToInt ( ) . ToString ( ) ) ;
2024-01-09 16:23:14 +01:00
filename = filename . Replace ( "[RES]" , $"{outRes.Width}x{outRes.Height}" ) ;
filename = filename . Replace ( "[H]" , $"{outRes.Height}p" ) ;
2021-08-23 16:50:18 +02:00
2024-11-08 11:54:26 +01:00
if ( addFpsLimitSuffix )
{
2024-01-09 16:23:14 +01:00
filename + = Paths . fpsLimitSuffix ;
2024-11-08 11:54:26 +01:00
}
2021-08-23 16:50:18 +02:00
2024-11-08 11:54:26 +01:00
if ( includeExt )
{
string ext = FfmpegUtils . GetExt ( curr . outSettings ) ;
ext = isImgSeq ? ext . Replace ( "." , "-" ) : ext ;
filename + = ext ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return filename ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static string GetHighestFrameNumPath ( string path )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
FileInfo highest = null ;
int highestInt = - 1 ;
foreach ( FileInfo frame in new DirectoryInfo ( path ) . GetFiles ( "*.*" , SearchOption . TopDirectoryOnly ) )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
int num = frame . Name . GetInt ( ) ;
if ( num > highestInt )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
highest = frame ;
highestInt = frame . Name . GetInt ( ) ;
}
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
return highest . FullName ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
public static string FilenameSuffix ( string path , string suffix )
2021-08-23 16:50:18 +02:00
{
try
{
2024-01-09 16:23:14 +01:00
string ext = Path . GetExtension ( path ) ;
return Path . Combine ( path . GetParentDir ( ) , $"{Path.GetFileNameWithoutExtension(path)}{suffix}{ext}" ) ;
}
2021-08-23 16:50:18 +02:00
catch
{
2024-01-09 16:23:14 +01:00
return path ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static async Task < Fraction > GetFpsFolderOrVideo ( string path )
{
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
if ( IsPathDirectory ( path ) )
{
2021-08-23 16:50:18 +02:00
Fraction dirFps = GetVideoFramerateForDir ( path ) ;
2024-10-13 16:58:06 +02:00
if ( dirFps . Float > 0 )
2024-01-09 16:23:14 +01:00
return dirFps ;
}
else
{
2021-08-23 16:50:18 +02:00
Fraction vidFps = await GetVideoFramerate ( path ) ;
2024-10-13 16:58:06 +02:00
if ( vidFps . Float > 0 )
2024-01-09 16:23:14 +01:00
return vidFps ;
}
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( "GetFpsFolderOrVideo() Error: " + e . Message ) ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
return new Fraction ( ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public enum ErrorMode { HiddenLog , VisibleLog , Messagebox }
public static bool CanWriteToDir ( string dir , ErrorMode errMode )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
string tempFile = Path . Combine ( dir , "flowframes-testfile.tmp" ) ;
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
File . Create ( tempFile ) ;
File . Delete ( tempFile ) ;
return true ;
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"Can't write to {dir}! {e.Message}" , errMode = = ErrorMode . HiddenLog ) ;
if ( errMode = = ErrorMode . Messagebox & & ! BatchProcessing . busy )
UiUtils . ShowMessageBox ( $"Can't write to {dir}!\n\n{e.Message}" , UiUtils . MessageType . Error ) ;
return false ;
2021-08-23 16:50:18 +02:00
}
}
2024-01-09 16:23:14 +01:00
public static bool CopyTo ( string file , string targetFolder , bool overwrite = true )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
string targetPath = Path . Combine ( targetFolder , Path . GetFileName ( file ) ) ;
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
if ( ! Directory . Exists ( targetFolder ) )
Directory . CreateDirectory ( targetFolder ) ;
File . Copy ( file , targetPath , overwrite ) ;
return true ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"Failed to copy {file} to {targetFolder}: {e.Message}" ) ;
return false ;
2021-08-23 16:50:18 +02:00
}
}
2024-01-09 16:23:14 +01:00
public static bool MoveTo ( string file , string targetFolder , bool overwrite = true )
{
string targetPath = Path . Combine ( targetFolder , Path . GetFileName ( file ) ) ;
try
{
if ( ! Directory . Exists ( targetFolder ) )
Directory . CreateDirectory ( targetFolder ) ;
if ( overwrite )
DeleteIfExists ( targetPath ) ;
File . Move ( file , targetPath ) ;
return true ;
}
catch ( Exception e )
{
Logger . Log ( $"Failed to move {file} to {targetFolder}: {e.Message}" ) ;
return false ;
}
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public enum Hash { MD5 , CRC32 }
public static string GetHash ( string path , Hash hashType , bool log = true )
{
string hashStr = "" ;
NmkdStopwatch sw = new NmkdStopwatch ( ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( IsPathDirectory ( path ) )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"Path '{path}' is directory! Returning empty hash." , true ) ;
return hashStr ;
2021-08-23 16:50:18 +02:00
}
try
{
2024-01-09 16:23:14 +01:00
var stream = File . OpenRead ( path ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( hashType = = Hash . MD5 )
{
MD5 md5 = MD5 . Create ( ) ;
var hash = md5 . ComputeHash ( stream ) ;
2024-09-03 22:08:38 +02:00
hashStr = BitConverter . ToString ( hash ) . Replace ( "-" , "" ) . Lower ( ) ;
2024-01-09 16:23:14 +01:00
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( hashType = = Hash . CRC32 )
{
var crc = new Crc32Algorithm ( ) ;
var crc32bytes = crc . ComputeHash ( stream ) ;
hashStr = BitConverter . ToUInt32 ( crc32bytes , 0 ) . ToString ( ) ;
}
2021-08-23 16:50:18 +02:00
stream . Close ( ) ;
2024-01-09 16:23:14 +01:00
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"Error getting file hash for {Path.GetFileName(path)}: {e.Message}" , true ) ;
return "" ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
if ( log )
Logger . Log ( $"Computed {hashType} for '{Path.GetFileNameWithoutExtension(path).Trunc(40) + Path.GetExtension(path)}' ({GetFilesizeStr(path)}): {hashStr} ({sw})" , true ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return hashStr ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static async Task < string > GetHashAsync ( string path , Hash hashType , bool log = true )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
await Task . Delay ( 1 ) ;
return GetHash ( path , hashType , log ) ;
}
public static bool CreateDir ( string path ) // Returns whether the dir already existed
{
if ( string . IsNullOrWhiteSpace ( path ) )
return false ;
2022-06-04 12:43:48 +02:00
2024-01-09 16:23:14 +01:00
if ( ! Directory . Exists ( path ) )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Directory . CreateDirectory ( path ) ;
return false ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
return true ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
public static void ZeroPadDir ( string path , string ext , int targetLength , bool recursive = false )
{
FileInfo [ ] files ;
if ( recursive )
files = new DirectoryInfo ( path ) . GetFiles ( $"*.{ext}" , SearchOption . AllDirectories ) ;
else
files = new DirectoryInfo ( path ) . GetFiles ( $"*.{ext}" , SearchOption . TopDirectoryOnly ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
ZeroPadDir ( files . Select ( x = > x . FullName ) . ToList ( ) , targetLength ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static void ZeroPadDir ( List < string > files , int targetLength , List < string > exclude = null , bool noLog = true )
{
if ( exclude ! = null )
files = files . Except ( exclude ) . ToList ( ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
foreach ( string file in files )
{
string fname = Path . GetFileNameWithoutExtension ( file ) ;
string targetFilename = Path . Combine ( Path . GetDirectoryName ( file ) , fname . PadLeft ( targetLength , '0' ) + Path . GetExtension ( file ) ) ;
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
if ( targetFilename ! = file )
File . Move ( file , targetFilename ) ;
}
catch ( Exception e )
{
if ( ! noLog )
Logger . Log ( $"Failed to zero-pad {file} => {targetFilename}: {e.Message}" , true ) ;
}
}
}
public static bool CheckImageValid ( string path )
2021-08-23 16:50:18 +02:00
{
try
{
2024-01-09 16:23:14 +01:00
Image img = GetImage ( path ) ;
2021-08-23 16:50:18 +02:00
return ( img . Width > 0 & & img . Height > 0 ) ;
}
2024-01-09 16:23:14 +01:00
catch
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
return false ;
2021-08-23 16:50:18 +02:00
}
}
2024-01-09 16:23:14 +01:00
public static string [ ] GetFilesSorted ( string path , bool recursive = false , string pattern = "*" )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
SearchOption opt = recursive ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly ;
return Directory . GetFiles ( path , pattern , opt ) . OrderBy ( x = > Path . GetFileName ( x ) ) . ToArray ( ) ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
public static string [ ] GetFilesSorted ( string path , string pattern = "*" )
{
return GetFilesSorted ( path , false , pattern ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static string [ ] GetFilesSorted ( string path )
{
return GetFilesSorted ( path , false , "*" ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static FileInfo [ ] GetFileInfosSorted ( string path , bool recursive = false , string pattern = "*" )
{
if ( ! Directory . Exists ( path ) )
return new FileInfo [ 0 ] ;
2024-01-09 13:31:02 +01:00
2024-01-09 16:23:14 +01:00
SearchOption opt = recursive ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly ;
DirectoryInfo dir = new DirectoryInfo ( path ) ;
return dir . GetFiles ( pattern , opt ) . OrderBy ( x = > x . Name ) . ToArray ( ) ;
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static bool CreateFileIfNotExists ( string path )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
if ( File . Exists ( path ) )
return false ;
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
File . Create ( path ) . Close ( ) ;
return true ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"Failed to create file at '{path}': {e.Message}" , true ) ;
return false ;
2021-08-23 16:50:18 +02:00
}
}
2024-01-09 16:23:14 +01:00
public static long GetDirSize ( string path , bool recursive , string [ ] includedExtensions = null )
{
long size = 0 ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
try
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
string [ ] files ;
StringComparison ignCase = StringComparison . OrdinalIgnoreCase ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( includedExtensions = = null )
files = Directory . GetFiles ( path ) ;
else
files = Directory . GetFiles ( path ) . Where ( file = > includedExtensions . Any ( x = > file . EndsWith ( x , ignCase ) ) ) . ToArray ( ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
foreach ( string file in files )
{
try { size + = new FileInfo ( file ) . Length ; } catch { size + = 0 ; }
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
if ( ! recursive )
return size ;
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
// Add subdirectory sizes.
DirectoryInfo [ ] dis = new DirectoryInfo ( path ) . GetDirectories ( ) ;
foreach ( DirectoryInfo di in dis )
size + = GetDirSize ( di . FullName , true , includedExtensions ) ;
}
catch ( Exception e )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
Logger . Log ( $"GetDirSize Error: {e.Message}\n{e.StackTrace}" , true ) ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
return size ;
}
2021-08-23 16:50:18 +02:00
2023-02-15 12:27:36 +01:00
public static long GetPathSize ( string path )
{
try
{
2024-01-09 16:23:14 +01:00
bool isFile = File . Exists ( path ) ;
2023-02-15 12:27:36 +01:00
return isFile ? GetFilesize ( path ) : GetDirSize ( path , true ) ;
}
catch
{
return - 1 ;
}
}
public static long GetFilesize ( string path )
2024-01-09 16:23:14 +01:00
{
2021-08-23 16:50:18 +02:00
try
{
2024-01-09 16:23:14 +01:00
return new FileInfo ( path ) . Length ;
}
2021-08-23 16:50:18 +02:00
catch
{
2024-01-09 16:23:14 +01:00
return - 1 ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 16:23:14 +01:00
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static string GetFilesizeStr ( string path )
2021-08-23 16:50:18 +02:00
{
2024-01-09 16:23:14 +01:00
try
{
return FormatUtils . Bytes ( GetFilesize ( path ) ) ;
}
catch
{
return "?" ;
}
}
2021-08-23 16:50:18 +02:00
2024-01-09 16:23:14 +01:00
public static void OverwriteFileWithText ( string path , string text = "THIS IS A DUMMY FILE - DO NOT DELETE ME" )
2021-08-23 16:50:18 +02:00
{
try
{
2024-01-09 16:23:14 +01:00
File . WriteAllText ( path , text ) ;
}
catch ( Exception e )
{
Logger . Log ( $"OverwriteWithText failed for '{path}': {e.Message}" , true ) ;
}
}
public static long GetDiskSpace ( string path , bool mbytes = true )
{
try
{
string driveLetter = path . Substring ( 0 , 2 ) ; // Make 'C:/some/random/path' => 'C:' etc
DriveInfo [ ] allDrives = DriveInfo . GetDrives ( ) ;
foreach ( DriveInfo d in allDrives )
{
if ( d . IsReady & & d . Name . StartsWith ( driveLetter ) )
{
if ( mbytes )
return ( long ) ( d . AvailableFreeSpace / 1024f / 1000f ) ;
else
return d . AvailableFreeSpace ;
}
}
}
catch ( Exception e )
{
Logger . Log ( "Error trying to get disk space: " + e . Message , true ) ;
}
return 0 ;
}
public static string [ ] GetUniqueExtensions ( string path , bool recursive = false )
{
FileInfo [ ] fileInfos = GetFileInfosSorted ( path , recursive ) ;
List < string > exts = fileInfos . Select ( x = > x . Extension ) . ToList ( ) ;
return exts . Select ( x = > x ) . Distinct ( ) . ToArray ( ) ;
}
public static string ReadFile ( string path )
{
2023-02-15 12:27:36 +01:00
using ( var fileStream = new FileStream ( path , FileMode . Open , FileAccess . Read , FileShare . ReadWrite ) )
{
using ( var streamReader = new StreamReader ( fileStream ) )
{
return streamReader . ReadToEnd ( ) ;
}
}
}
public static string [ ] ReadFileLines ( string path )
{
return ReadFile ( path ) . SplitIntoLines ( ) ;
}
public static string ReadFileFirstLine ( string path )
{
using ( var fileStream = new FileStream ( path , FileMode . Open , FileAccess . Read , FileShare . ReadWrite ) )
{
using ( var streamReader = new StreamReader ( fileStream ) )
{
return streamReader . ReadLine ( ) ;
}
}
}
}
2021-08-23 16:50:18 +02:00
}