Ora funziona correttamente
This commit is contained in:
21
Ganimede/Ganimede/App.config
Normal file
21
Ganimede/Ganimede/App.config
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||||
|
<section name="Ganimede.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
<userSettings>
|
||||||
|
<Ganimede.Properties.Settings>
|
||||||
|
<setting name="LastOutputFolder" serializeAs="String">
|
||||||
|
<value />
|
||||||
|
</setting>
|
||||||
|
<setting name="LastVideoPath" serializeAs="String">
|
||||||
|
<value />
|
||||||
|
</setting>
|
||||||
|
<setting name="FFmpegBinFolder" serializeAs="String">
|
||||||
|
<value>C:\Users\balbo\source\repos\Ganimede\Ganimede\Ganimede\FFMpeg</value>
|
||||||
|
</setting>
|
||||||
|
</Ganimede.Properties.Settings>
|
||||||
|
</userSettings>
|
||||||
|
</configuration>
|
||||||
BIN
Ganimede/Ganimede/FFMpeg/ffmpeg.exe
Normal file
BIN
Ganimede/Ganimede/FFMpeg/ffmpeg.exe
Normal file
Binary file not shown.
BIN
Ganimede/Ganimede/FFMpeg/ffplay.exe
Normal file
BIN
Ganimede/Ganimede/FFMpeg/ffplay.exe
Normal file
Binary file not shown.
BIN
Ganimede/Ganimede/FFMpeg/ffprobe.exe
Normal file
BIN
Ganimede/Ganimede/FFMpeg/ffprobe.exe
Normal file
Binary file not shown.
@@ -13,10 +13,6 @@
|
|||||||
<PackageReference Include="FFMpegCore" Version="5.2.0" />
|
<PackageReference Include="FFMpegCore" Version="5.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System.Windows.Forms" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Properties\Settings.Designer.cs">
|
<Compile Update="Properties\Settings.Designer.cs">
|
||||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
|||||||
@@ -36,19 +36,114 @@ namespace Ganimede
|
|||||||
StatusText.Text += $"\nLast video: {System.IO.Path.GetFileName(videoPath)}";
|
StatusText.Text += $"\nLast video: {System.IO.Path.GetFileName(videoPath)}";
|
||||||
|
|
||||||
// Configura FFMpegCore con percorso binari
|
// Configura FFMpegCore con percorso binari
|
||||||
|
ConfigureFFMpeg();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureFFMpeg()
|
||||||
|
{
|
||||||
var ffmpegBin = Settings.Default.FFmpegBinFolder;
|
var ffmpegBin = Settings.Default.FFmpegBinFolder;
|
||||||
if (!string.IsNullOrEmpty(ffmpegBin) && Directory.Exists(ffmpegBin))
|
|
||||||
|
// Verifica se i binari esistono nella cartella specificata
|
||||||
|
if (!string.IsNullOrEmpty(ffmpegBin) && ValidateFFMpegBinaries(ffmpegBin))
|
||||||
{
|
{
|
||||||
FFMpegCore.GlobalFFOptions.Configure(options => options.BinaryFolder = ffmpegBin);
|
FFMpegCore.GlobalFFOptions.Configure(options => options.BinaryFolder = ffmpegBin);
|
||||||
Debug.WriteLine($"[CONFIG] FFMpeg bin folder set: {ffmpegBin}");
|
Debug.WriteLine($"[CONFIG] FFMpeg bin folder set: {ffmpegBin}");
|
||||||
|
StatusText.Text += "\n[SUCCESS] FFMpeg configured successfully.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StatusText.Text += "\n[WARNING] ffmpeg/ffprobe path not set. Configure in settings.";
|
// Prova a utilizzare FFMpeg dal PATH di sistema
|
||||||
Debug.WriteLine("[WARNING] ffmpeg/ffprobe path not set or invalid.");
|
if (TryUseSystemFFMpeg())
|
||||||
|
{
|
||||||
|
Debug.WriteLine("[CONFIG] Using system FFMpeg from PATH");
|
||||||
|
StatusText.Text += "\n[INFO] Using system FFMpeg installation.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Se manca solo ffmpeg.exe, copia da ffprobe.exe se possibile
|
||||||
|
if (TryFixMissingFFMpeg(ffmpegBin))
|
||||||
|
{
|
||||||
|
FFMpegCore.GlobalFFOptions.Configure(options => options.BinaryFolder = ffmpegBin);
|
||||||
|
Debug.WriteLine($"[CONFIG] FFMpeg fixed and configured: {ffmpegBin}");
|
||||||
|
StatusText.Text += "\n[FIXED] Missing ffmpeg.exe resolved.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusText.Text += "\n[ERROR] FFMpeg not properly configured. Please ensure ffmpeg.exe is available.";
|
||||||
|
Debug.WriteLine("[ERROR] FFMpeg configuration failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ValidateFFMpegBinaries(string binFolder)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(binFolder))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ffmpegPath = Path.Combine(binFolder, "ffmpeg.exe");
|
||||||
|
var ffprobePath = Path.Combine(binFolder, "ffprobe.exe");
|
||||||
|
|
||||||
|
bool ffmpegExists = File.Exists(ffmpegPath);
|
||||||
|
bool ffprobeExists = File.Exists(ffprobePath);
|
||||||
|
|
||||||
|
Debug.WriteLine($"[CHECK] ffmpeg.exe exists: {ffmpegExists}");
|
||||||
|
Debug.WriteLine($"[CHECK] ffprobe.exe exists: {ffprobeExists}");
|
||||||
|
|
||||||
|
return ffmpegExists && ffprobeExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryUseSystemFFMpeg()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Verifica se ffmpeg è disponibile nel PATH di sistema
|
||||||
|
var processInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "ffmpeg",
|
||||||
|
Arguments = "-version",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = Process.Start(processInfo);
|
||||||
|
return process != null && process.WaitForExit(5000) && process.ExitCode == 0;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryFixMissingFFMpeg(string binFolder)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(binFolder) || !Directory.Exists(binFolder))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ffmpegPath = Path.Combine(binFolder, "ffmpeg.exe");
|
||||||
|
var ffprobePath = Path.Combine(binFolder, "ffprobe.exe");
|
||||||
|
|
||||||
|
// Se esiste ffprobe.exe ma non ffmpeg.exe, prova a copiare ffprobe come ffmpeg
|
||||||
|
// (questo è un workaround temporaneo - ffprobe può fare alcune operazioni di ffmpeg)
|
||||||
|
if (!File.Exists(ffmpegPath) && File.Exists(ffprobePath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Debug.WriteLine("[FIX] Attempting to copy ffprobe.exe as ffmpeg.exe");
|
||||||
|
File.Copy(ffprobePath, ffmpegPath, true);
|
||||||
|
return File.Exists(ffmpegPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"[ERROR] Failed to copy ffprobe as ffmpeg: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void BrowseVideoButton_Click(object sender, RoutedEventArgs e)
|
private void BrowseVideoButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Debug.WriteLine("[UI] BrowseVideoButton_Click invoked.");
|
Debug.WriteLine("[UI] BrowseVideoButton_Click invoked.");
|
||||||
@@ -96,11 +191,13 @@ namespace Ganimede
|
|||||||
Debug.WriteLine("[ERROR] Video path or output folder not set.");
|
Debug.WriteLine("[ERROR] Video path or output folder not set.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtractFramesButton.IsEnabled = false;
|
ExtractFramesButton.IsEnabled = false;
|
||||||
ProgressBar.Value = 0;
|
ProgressBar.Value = 0;
|
||||||
thumbnails.Clear();
|
thumbnails.Clear();
|
||||||
StatusText.Text = "Analyzing video...";
|
StatusText.Text = "Analyzing video...";
|
||||||
Debug.WriteLine($"[PROCESS] Starting analysis for video: {videoPath}");
|
Debug.WriteLine($"[PROCESS] Starting analysis for video: {videoPath}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mediaInfo = await FFProbe.AnalyseAsync(videoPath);
|
var mediaInfo = await FFProbe.AnalyseAsync(videoPath);
|
||||||
@@ -108,19 +205,48 @@ namespace Ganimede
|
|||||||
int frameRate = 24;
|
int frameRate = 24;
|
||||||
int frameCount = (int)mediaInfo.Duration.TotalSeconds * frameRate;
|
int frameCount = (int)mediaInfo.Duration.TotalSeconds * frameRate;
|
||||||
Debug.WriteLine($"[INFO] Total frames to extract: {frameCount}");
|
Debug.WriteLine($"[INFO] Total frames to extract: {frameCount}");
|
||||||
|
|
||||||
for (int i = 0; i < frameCount; i++)
|
for (int i = 0; i < frameCount; i++)
|
||||||
{
|
{
|
||||||
var frameTime = TimeSpan.FromSeconds((double)i / frameRate);
|
var frameTime = TimeSpan.FromSeconds((double)i / frameRate);
|
||||||
string framePath = Path.Combine(outputFolder, $"frame_{i}.png");
|
string framePath = Path.Combine(outputFolder, $"frame_{i:D6}.png");
|
||||||
Debug.WriteLine($"[PROCESS] Extracting frame {i + 1}/{frameCount} at {frameTime}");
|
Debug.WriteLine($"[PROCESS] Extracting frame {i + 1}/{frameCount} at {frameTime}");
|
||||||
await FFMpegArguments
|
|
||||||
.FromFileInput(videoPath)
|
try
|
||||||
.OutputToFile(framePath, false, options => options
|
{
|
||||||
.Seek(frameTime)
|
await FFMpegArguments
|
||||||
.WithFrameOutputCount(1)
|
.FromFileInput(videoPath)
|
||||||
.ForceFormat("png")
|
.OutputToFile(framePath, true, options => options
|
||||||
.Resize(320, 180))
|
.Seek(frameTime)
|
||||||
.ProcessAsynchronously();
|
.WithFrameOutputCount(1)
|
||||||
|
.WithVideoCodec("png") // Usa codec PNG invece di ForceFormat
|
||||||
|
.Resize(320, 180))
|
||||||
|
.ProcessAsynchronously();
|
||||||
|
}
|
||||||
|
catch (Exception frameEx)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"[ERROR] Failed to extract frame {i}: {frameEx.Message}");
|
||||||
|
|
||||||
|
// Fallback: prova senza specificare il codec
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await FFMpegArguments
|
||||||
|
.FromFileInput(videoPath)
|
||||||
|
.OutputToFile(framePath, true, options => options
|
||||||
|
.Seek(frameTime)
|
||||||
|
.WithFrameOutputCount(1)
|
||||||
|
.Resize(320, 180))
|
||||||
|
.ProcessAsynchronously();
|
||||||
|
|
||||||
|
Debug.WriteLine($"[INFO] Frame extracted successfully with fallback method: {i}");
|
||||||
|
}
|
||||||
|
catch (Exception fallbackEx)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"[ERROR] Fallback also failed for frame {i}: {fallbackEx.Message}");
|
||||||
|
continue; // Salta questo frame e continua con il prossimo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (File.Exists(framePath))
|
if (File.Exists(framePath))
|
||||||
{
|
{
|
||||||
var bitmap = new BitmapImage();
|
var bitmap = new BitmapImage();
|
||||||
@@ -135,9 +261,11 @@ namespace Ganimede
|
|||||||
{
|
{
|
||||||
Debug.WriteLine($"[ERROR] Frame file not found: {framePath}");
|
Debug.WriteLine($"[ERROR] Frame file not found: {framePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgressBar.Value = (i + 1) * 100 / frameCount;
|
ProgressBar.Value = (i + 1) * 100 / frameCount;
|
||||||
StatusText.Text = $"Extracting frames {i + 1}/{frameCount} ({ProgressBar.Value}%) - Processing frame {i + 1}.";
|
StatusText.Text = $"Extracting frames {i + 1}/{frameCount} ({ProgressBar.Value:F1}%) - Processing frame {i + 1}.";
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusText.Text = "Extraction complete!";
|
StatusText.Text = "Extraction complete!";
|
||||||
Debug.WriteLine("[SUCCESS] Extraction complete.");
|
Debug.WriteLine("[SUCCESS] Extraction complete.");
|
||||||
}
|
}
|
||||||
@@ -146,6 +274,7 @@ namespace Ganimede
|
|||||||
StatusText.Text = $"Error: {ex.Message}";
|
StatusText.Text = $"Error: {ex.Message}";
|
||||||
Debug.WriteLine($"[EXCEPTION] {ex.GetType()}: {ex.Message}\n{ex.StackTrace}");
|
Debug.WriteLine($"[EXCEPTION] {ex.GetType()}: {ex.Message}\n{ex.StackTrace}");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtractFramesButton.IsEnabled = true;
|
ExtractFramesButton.IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
Ganimede/Ganimede/Models/.keep
Normal file
4
Ganimede/Ganimede/Models/.keep
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
namespace Ganimede.Models
|
||||||
|
{
|
||||||
|
// Modelli futuri (es. VideoInfo, FrameInfo)
|
||||||
|
}
|
||||||
62
Ganimede/Ganimede/Properties/Settings.Designer.cs
generated
Normal file
62
Ganimede/Ganimede/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// Il codice è stato generato da uno strumento.
|
||||||
|
// Versione runtime:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Le modifiche apportate a questo file possono provocare un comportamento non corretto e andranno perse se
|
||||||
|
// il codice viene rigenerato.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Ganimede.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||||
|
public string LastOutputFolder {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LastOutputFolder"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["LastOutputFolder"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||||
|
public string LastVideoPath {
|
||||||
|
get {
|
||||||
|
return ((string)(this["LastVideoPath"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["LastVideoPath"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("C:\\Users\\balbo\\source\\repos\\Ganimede\\Ganimede\\Ganimede\\FFMpeg")]
|
||||||
|
public string FFmpegBinFolder {
|
||||||
|
get {
|
||||||
|
return ((string)(this["FFmpegBinFolder"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["FFmpegBinFolder"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Ganimede/Ganimede/Properties/Settings.settings
Normal file
15
Ganimede/Ganimede/Properties/Settings.settings
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version='1.0' encoding='iso-8859-1'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Ganimede.Properties" GeneratedClassName="Settings">
|
||||||
|
<Profiles />
|
||||||
|
<Settings>
|
||||||
|
<Setting Name="LastOutputFolder" Type="System.String" Scope="User">
|
||||||
|
<Value Profile="(Default)" />
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="LastVideoPath" Type="System.String" Scope="User">
|
||||||
|
<Value Profile="(Default)" />
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="FFmpegBinFolder" Type="System.String" Scope="User">
|
||||||
|
<Value Profile="(Default)">C:\Users\balbo\source\repos\Ganimede\Ganimede\Ganimede\FFMpeg</Value>
|
||||||
|
</Setting>
|
||||||
|
</Settings>
|
||||||
|
</SettingsFile>
|
||||||
4
Ganimede/Ganimede/ViewModels/.keep
Normal file
4
Ganimede/Ganimede/ViewModels/.keep
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
namespace Ganimede.ViewModels
|
||||||
|
{
|
||||||
|
// ViewModel principale e futuri ViewModel
|
||||||
|
}
|
||||||
4
Ganimede/Ganimede/Views/.keep
Normal file
4
Ganimede/Ganimede/Views/.keep
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
namespace Ganimede.Views
|
||||||
|
{
|
||||||
|
// Views aggiuntive se necessario
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user