I have written a photoshop script to generate product image for a newsletter on runtime and a vbs script to call it which all work fine when I execute the vbs script throught the command line, but if I call it from a asp.net application it doesn't, then a get the error: There is no script engine for file extension ".jsx".
The jsx script, the vbs script and the code I use to execute can be found below. Can anyone help me to make it work through the asp.net application please.
UPDATE
I managed to fix the script engine error, that was just a problem with the c# process argument. I've fixed the code below. It would seem, the problem is the network service user, doesn't have permission to startup photoshop. I have tried all kind of stuff to give it the right permission, but it's still not working.
How do I allow the network service user to access photoshop?
JSX script:
#target photoshop
function getLayer(target, layerName)
{
try
{
var layer = doc.layers.getByName(layerName);
return layer;
}
catch(e)
{
alert("Unable to find layer: " + layerName); // only for debug
}
return false;
}
function changeTextOnTextLayer(target, layerName, textToInsert)
{
var textLayer = getLayer(target, layerName);
if(textLayer && textLayer.kind == LayerKind.TEXT)
{
textLayer.textItem.contents = textToInsert;
}
textLayer = null;
}
function setImageOnLayerFromFile(target, layerName, fullFileName)
{
var imageLayer = getLayer(target, layerName);
if(imageLayer)
{
var x = imageLayer.bounds[0];
var y = imageLayer.bounds[1];
var layerSize = getWidthAndHeight(imageLayer);
var width = layerSize[0];
var height = layerSize[1];
var file = File(fullFileName);
app.load(file);
var imageFile = app.activeDocument;
imageFile.resizeImage( width, height);
imageFile.selection.selectAll();
imageFile.selection.copy();
imageFile.close(SaveOptions.DONOTSAVECHANGES);
file.close();
file = null;
target.paste();
var newImageLayer = target.layers[0];
moveLayerTo(newImageLayer, x, y);
}
textLayer = null;
}
function getWidthAndHeight(layer)
{
var width = layer.bounds[2] - layer.bounds[0];
var height = layer.bounds[3] - layer.bounds[1];
return [width, height];
}
function moveLayerTo(fLayer,fX,fY)
{
var Position = fLayer.bounds;
Position[0] = fX - Position[0];
Position[1] = fY - Position[1];
fLayer.translate(Position[0],Position[1]);
}
function saveToPNG(doc, fileName)
{
var file = new File(fileName);
opts = new ExportOptionsSaveForWeb();
with (opts)
{
format = SaveDocumentType.PNG;
PNG8 = false;
}
doc.exportDocument(file, ExportType.SAVEFORWEB, opts);
// save for web
}
if(!arguments || arguments.length != 5 )
{
alert("Missing arguments!"); // only for debug
}
else
{
var file = File(arguments[0]);
app.load(file);
var doc = app.activeDocument;
setImageOnLayerFromFile(doc, 'Image', arguments[1]);
changeTextOnTextLayer(doc, 'ProductDescription', arguments[3]);
changeTextOnTextLayer(doc, 'Price', arguments[4]);
saveToPNG(doc, arguments[2]);
doc.close(SaveOptions.DONOTSAVECHANGES);
doc = null;
file.close();
file = null;
}
VBS script:
Set vbsArguments = WScript.Arguments
If vbsArguments.Length = 0 Then
WScript.Echo "Argument(0) is `your script to execute"
WScript.Echo "Arguments(0+n) are passed to your script as argument(0) to argument(n-1)"
Else
ReDim jsxArguments(vbsArguments.length-2)
for i = 1 to vbsArguments.length - 1
jsxArguments(i-1) = vbsArguments(i)
Next
Set photoshop = CreateObject( "Photoshop.Application" )
photoshop.BringToFront
Call photoshop.DoJavaScriptFile( vbsArguments(0), jsxArguments, 1)
End IF
C# asp.net
const string vbsScript = "cscript";
string arguments = "\"" + #"D:\script\createproduct.vbs" + "\" " + string.Format(#"D:\script\createproduct.jsx " +
"/d/script/test.psd \"{0}\" \"{1}\" \"{2}\" \"{3}\"",
imagePath,
fullSavePath,
requestSpecificProduct.ShortDescription,
requestSpecificProduct.DisplayPrice);
context.Response.Write(vbsScript + "\n");
context.Response.Write(arguments);
using (var scriptProc = new Process
{
StartInfo =
{
FileName = vbsScript,
Arguments = arguments,
WorkingDirectory = #"D:\",
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
}
})
{
scriptProc.Start();
scriptProc.WaitForExit();
Thank you for your help
Related
I have been working on a chess game and using the Stockfish chess engine to implement AI in it. I was successful in firing up the executable file, sending fen code as input and receiving the output from the engine. It works perfectly on unity editor and standalone build. But, It won't work for the android device. I have no idea why.
I have made sure that the file is copied/created to the right directory and is successful. Can someone please help me figure out this issue?
string fen;
public static Process mProcess;
void Start()
{
Setup();
}
public void Setup()
{
// since the apk file is archived this code retreives the stockfish binary data and
// creates a copy of it in the persistantdatapath location.
#if UNITY_EDITOR
string filepath = "D:\\Chess Projects\\StockFishTest\\Assets\\StreamingAssets\\stockfish_10_x64.exe";
#elif UNITY_ANDROID
string filepath = Application.persistentDataPath + "/" + "stockfish-10-armv7";
Debug.Log(filepath);
if (!File.Exists(filepath))
{
WWW executable = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "stockfish-10-armv7");
while (!executable.isDone)
{
}
File.WriteAllBytes(filepath, executable.bytes);
//change permissions via plugin
}
var plugin = new AndroidJavaClass("com.chessbattles.jeyasurya.consoleplugin.AndroidConsole");
string command = "chmod 777 "+filepath;
outPut = plugin.CallStatic<string>("ExecuteCommand",command);
#else
string filepath = Application.streamingAssetsPath+ "/" + "stockfish_10_x64.exe";
#endif
// creating the process and communicating with the engine
mProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = filepath,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
mProcess.StartInfo = si;
mProcess.OutputDataReceived += new DataReceivedEventHandler(MProcess_OutputDataReceived);
mProcess.Start();
mProcess.BeginErrorReadLine();
mProcess.BeginOutputReadLine();
SendLine("uci");
SendLine("isready");
}
public void GetMove(string fen, int processTime = 0, int DepthValue = 1)
{
if(fen ==null || fen == ""){
UnityEngine.Debug.LogError("Enter proper Fen");
Debug.Log("Enter proper Fen");
return;
}
SendLine("position fen "+ fen);
if(processTime != 0){
SendLine("go movetime "+processTime);
}
else if(DepthValue != 0)
{
SendLine("go depth "+ DepthValue);
}
else
{
SendLine("go depth " + DepthValue);
}
}
public string output = "";
public bool moveReady = false;
public void SendLine(string command) {
mProcess.StandardInput.WriteLine(command);
mProcess.StandardInput.Flush();
}
void MProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
output = "";
// UnityEngine.Debug.Log("Output: " + e.Data);
output = e.Data;
if (output.Length != 0)
if (output[0] == 'b' && output[3] == 't')
{
output = e.Data.Substring(9, 4);
// Debug.Log(output);
moveReady = true;
}
else
{
moveReady = false;
}
}
I have managed to make this work like this. You need to update the package name in the file path.
using System.Diagnostics;
using System.IO;
using UnityEngine;
namespace Assets.Scripts
{
public class EngineCommunicator
{
public static Process mProcess;
public static string fileName = "stockfish.android.armv7";
public static void Communicate()
{
#if UNITY_EDITOR
string filepath = "E:\\Personal\\Unity\\Chess2d\\Assets\\Plugins\\Windows\\stockfish_13_win_x64";
#elif UNITY_ANDROID
string filepath = "/data/data/com.tiringbring.Chess2d/lib/stockfish.android.armv7.so";
UI.Instance.AddJob(() =>
{
UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text = filepath;
});
if (!File.Exists(filepath))
{
UI.Instance.AddJob(() =>
{
UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text =UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text +" "+ "file not found";
});
}else{
UI.Instance.AddJob(() =>
{
UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text =UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text +" "+ "file found";
});
}
#else
string filepath = Application.streamingAssetsPath+ "/" + "stockfish_13_x64.exe";
#endif
// creating the process and communicating with the engine
mProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = filepath,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
mProcess.StartInfo = si;
mProcess.OutputDataReceived += new DataReceivedEventHandler(MProcess_OutputDataReceived);
mProcess.Start();
mProcess.BeginErrorReadLine();
mProcess.BeginOutputReadLine();
SendLine("uci");
SendLine("isready");
SendLine("ucinewgame");
SendLine("position startpos");
SendLine("go infinite searchmoves e2e4 d2d4");
}
private static void SendLine(string command)
{
mProcess.StandardInput.WriteLine(command);
mProcess.StandardInput.Flush();
}
private static void MProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string text = e.Data;
UI.Instance.AddJob(() =>
{
UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text = UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text +" "+ text;
});
UnityEngine.Debug.Log(text);
}
}
}
Put the file in Assets/Android directory with .so extension.
It will be copied automatically in lib folder on android mobile
I'm trying to open a place on my harddisk to store some licensing files.
So far I have tried diskpart. It looks easy to use but I could not format the unallocated partition with diskpart. I have found a way to create the unallocated space but I have to format it to use(correct me if I am wrong here. I'm really new on disk partition stuff)
This is my method to select the right volume. I've take it from here and it's working good. Link : C# and diskpart: how to select by disk label and not by a number? and the code I am using is this :
public int GetIndexOfDrive(string drive)
{
drive = drive.Replace(":", "").Replace(#"\", "");
// execute DiskPart programatically
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
if (label.Equals(drive))
{
return index;
}
}
}
return -1;
}
once I get the index I run my own code to shrink that selected volume with this code :
Process DiskPartProc = new Process();
DiskPartProc.StartInfo.CreateNoWindow = true;
DiskPartProc.StartInfo.UseShellExecute = false;
DiskPartProc.StartInfo.RedirectStandardOutput = true;
DiskPartProc.StartInfo.FileName = #"C:\Windows\System32\diskpart.exe";
DiskPartProc.StartInfo.RedirectStandardInput = true;
DiskPartProc.Start();
DiskPartProc.StandardInput.WriteLine("select volume "+index);
DiskPartProc.StandardInput.WriteLine("shrink desired=16 minimum=16");
DiskPartProc.StandardInput.WriteLine("exit");
string output = DiskPartProc.StandardOutput.ReadToEnd();
DiskPartProc.WaitForExit();
Once I do this the result is like this :
http://prntscr.com/mjwg0t (Picture of an unallocated partition only)
I can right click on it and create new simple volume from that unallocated partition but I have to do this with diskpart commands.
Can someone tell me which diskpart commands do I have to use to achieve this?
And how can I get detailed information about this volume?
I have solve my problem. This is my final code:
int index = GetIndexOfDrive(Path.GetPathRoot(#"E:\"));
Process DiskPartProc = new Process();
DiskPartProc.StartInfo.CreateNoWindow = true;
DiskPartProc.StartInfo.UseShellExecute = false;
DiskPartProc.StartInfo.RedirectStandardOutput = true;
DiskPartProc.StartInfo.FileName = #"C:\Windows\System32\diskpart.exe";
DiskPartProc.StartInfo.RedirectStandardInput = true;
DiskPartProc.Start();
DiskPartProc.StandardInput.WriteLine("select volume " + index);
DiskPartProc.StandardInput.WriteLine("shrink desired=16 minimum=16");
DiskPartProc.StandardInput.WriteLine("create partition primary size=16");
DiskPartProc.StandardInput.WriteLine("format fs=ntfs label=\"MyPlace\" quick");
DiskPartProc.StandardInput.WriteLine("exit");
string output = DiskPartProc.StandardOutput.ReadToEnd();
DiskPartProc.WaitForExit();
The problem was something about my own disk. I have put another spare one and did all my test on it and it work perfectly, now I can create a disk volume inside a disk and format it and then I can access it with its volume ID. I still have to find a way to do it in C#. I can do the last part from windows not from C# yet. I need a way to access that volume now. I tried Directory.Exist but it did not worked out.
EDIT : I found a way to check. Since I put only 1 file and nothing else in my volume I use this code :
Process MountProc = new Process();
MountProc.StartInfo.CreateNoWindow = true;
MountProc.StartInfo.UseShellExecute = false;
MountProc.StartInfo.RedirectStandardOutput = true;
MountProc.StartInfo.FileName = "mountvol";
MountProc.StartInfo.RedirectStandardInput = true;
MountProc.Start();
MountProc.StandardInput.WriteLine("mountvol");
MountProc.StandardInput.WriteLine("exit");
string MountOutput = MountProc.StandardOutput.ReadToEnd();
MountProc.WaitForExit();
string VolumeGUID = string.Empty;
List<string> VolList = MountOutput.Split(new string[] { "Possible values for VolumeName along with current mount points are:" }, StringSplitOptions.None)[1].Split('\n').Where(x => x != "\r").Where(x => x != "").ToList();
List<string> ClearList = VolList.Select(s => s.Trim().Replace("\r", "")).ToList();
for (int i = 0; i < ClearList.Count; i++)
{
if (ClearList[i].StartsWith(#"\\?\Volume") && ClearList[i + 1].StartsWith("***"))
{
string tmpPath = ClearList[i].Replace(#"\\?\", #"\\.\");
if (Directory.Exists(tmpPath))
{
string[] DirectoryList = Directory.GetDirectories(tmpPath, "*.*", SearchOption.TopDirectoryOnly);
string[] FileList = Directory.GetFiles(tmpPath, "*.*", SearchOption.TopDirectoryOnly);
if(DirectoryList.Length==0 && FileList.Length==1)
{
if (Path.GetExtension(FileList[0]) == ".license")
{
Console.WriteLine("\n\n\n\t\rLicense file found in : " + FileList[0]);
File.Copy(FileList[0], "LIC.license", true);
Console.WriteLine("\n\t\rContent of license file : " + File.ReadAllText("LIC.license"));
File.Delete("LIC.license");
}
}
}
}
}
Console.ReadKey();
The reason why I copied the file to another location and open it there is I can't open it with File class if it's accessed with it's ID(e.g. \.\Volume{UNIQUE_ID}\)
my application is in C# MVC with framework 4.5.2 and want to run external file(.m) and get output from that and it is written in Octave.
Note: Output result is in a string, so I have to show that output in C# html page.
Algorithm is written in octave and also in algorithm excel file is there to read data and external parameters are also passing through C#.
I tried the things which is already posted, the main concern is that my application is hosted in azure cloud. So, I can not install octave in cloud.
Can you suggest me any other way that directly run external file(.m) with dynamic excel file and parameters.
I can show my code what I have done. Its work in local with all but I can not install Octave in Azure cloud..
I put my installed folder to root path on server but its also not working.
public Octave(string pathToOctaveBinaries, bool createWindow)
{
StartOctave(pathToOctaveBinaries, createWindow);
}
private void StartOctave(string pathToOctaveBinaries, bool createWindow)
{
_ptob = pathToOctaveBinaries;
cw = createWindow;
this.OctaveEchoString = Guid.NewGuid().ToString();
OctaveProcess = new Process();
ProcessStartInfo pi = new ProcessStartInfo();
if (pathToOctaveBinaries[pathToOctaveBinaries.Length - 1] != '\\')
pathToOctaveBinaries = pathToOctaveBinaries + "\\";
pi.FileName = pathToOctaveBinaries + "octave-cli.exe";
pi.RedirectStandardInput = true;
pi.RedirectStandardOutput = true;
pi.RedirectStandardError = true;
pi.UseShellExecute = false;
pi.CreateNoWindow = !createWindow;
pi.Verb = "open";
//
pi.WorkingDirectory = ".";
OctaveProcess.StartInfo = pi;
OctaveProcess.Start();
OctaveProcess.OutputDataReceived += new DataReceivedEventHandler(OctaveProcess_OutputDataReceived);
OctaveProcess.BeginOutputReadLine();
OctaveEntryText = ExecuteCommand(null);
}
public string ExecuteCommand(string command, int timeout)
{
if (OctaveProcess.HasExited)
{
StartOctave(_ptob, cw);
if (OctaveRestarted != null) OctaveRestarted(this, EventArgs.Empty);
}
_exitError = false;
Thread tmp = new Thread(new ParameterizedThreadStart(WorkThread));
tmp.Start(command);
if (!tmp.Join(timeout))
{
tmp.Abort();
throw new Exception("Octave timeout");
}
if (_exitError)
{
throw new Exception(_errorMessage);
}
return SharedBuilder.ToString();
}
This above is Octave class file. Where I dynamically pass the path of .exe to be run as thread.
Controller Code:
Octave octave = new Octave(OctaveFilePath, false);
string fileData = result.Data.ToString();
fileData = fileData.Replace("#ExcelFilePath#", excelFilePath);
fileData = fileData.Replace("#ABCD#", historyData);
string rasp = octave.ExecuteCommand(fileData, 30000);
From here I get string and that I show into html page.
I have an issue with Files.
I am doing an image importer so clients put their files on an FTP server and then they can import it in the application.
During the import process I copy the file in the FTP Folder to another folder with File.copy
public List<Visuel> ImportVisuel(int galerieId, string[] images)
{
Galerie targetGalerie = MemoryCache.GetGaleriById(galerieId);
List<FormatImage> listeFormats = MemoryCache.FormatImageToList();
int i = 0;
List<Visuel> visuelAddList = new List<Visuel>();
List<Visuel> visuelUpdateList = new List<Visuel>();
List<Visuel> returnList = new List<Visuel>();
foreach (string item in images)
{
i++;
Progress.ImportProgress[Progress.Guid] = "Image " + i + " sur " + images.Count() + " importées";
string extension = Path.GetExtension(item);
string fileName = Path.GetFileName(item);
string originalPath = HttpContext.Current.Request.PhysicalApplicationPath + "Uploads\\";
string destinationPath = HttpContext.Current.Server.MapPath("~/Images/Catalogue") + "\\";
Visuel importImage = MemoryCache.GetVisuelByFilName(fileName);
bool update = true;
if (importImage == null) { importImage = new Visuel(); update = false; }
Size imageSize = importImage.GetJpegImageSize(originalPath + fileName);
FormatImage format = listeFormats.Where(f => f.width == imageSize.Width && f.height == imageSize.Height).FirstOrDefault();
string saveFileName = Guid.NewGuid() + extension;
File.Copy(originalPath + fileName, destinationPath + saveFileName);
if (format != null)
{
importImage.format = format;
switch (format.key)
{
case "Catalogue":
importImage.fileName = saveFileName;
importImage.originalFileName = fileName;
importImage.dossier = targetGalerie;
importImage.dossier_id = targetGalerie.id;
importImage.filePath = "Images/Catalogue/";
importImage.largeur = imageSize.Width;
importImage.hauteur = imageSize.Height;
importImage.isRoot = true;
if (update == false) { MemoryCache.Add(ref importImage); returnList.Add(importImage); }
if (update == true) visuelUpdateList.Add(importImage);
foreach (FormatImage f in listeFormats)
{
if (f.key.StartsWith("Catalogue_"))
{
string[] keys = f.key.Split('_');
string destinationFileName = saveFileName.Insert(saveFileName.IndexOf('.'), "-" + keys[1].ToString());
string destinationFileNameDeclinaison = destinationPath + destinationFileName;
VisuelResizer declinaison = new VisuelResizer();
declinaison.Save(originalPath + fileName, f.width, f.height, 1000, destinationFileNameDeclinaison);
Visuel visuel = MemoryCache.GetVisuelByFilName(fileName.Insert(fileName.IndexOf('.'), "-" + keys[1].ToString()));
update = true;
if (visuel == null) { visuel = new Visuel(); update = false; }
visuel.parent = importImage;
visuel.filePath = "Images/Catalogue/";
visuel.fileName = destinationFileName;
visuel.originalFileName = string.Empty;
visuel.format = f;
//visuel.dossier = targetGalerie; On s'en fout pour les déclinaisons
visuel.largeur = f.width;
visuel.hauteur = f.height;
if (update == false)
{
visuelAddList.Add(visuel);
}
else
{
visuelUpdateList.Add(visuel);
}
//importImage.declinaisons.Add(visuel);
}
}
break;
}
}
}
MemoryCache.Add(ref visuelAddList);
// FONCTION à implémenter
MemoryCache.Update(ref visuelUpdateList);
return returnList;
}
After some processes on the copy (the original file is no more used)
the client have a pop-up asking him if he wants to delete the original files in the ftp folder.
If he clicks on Ok another method is called on the same controller
and this method use
public void DeleteImageFile(string[] files)
{
for (int i = 0; i < files.Length; i++)
{
File.Delete(HttpContext.Current.Request.PhysicalApplicationPath + files[i].Replace(#"/", #"\"));
}
}
This method works fine and really delete the good files when I use it in other context.
But here I have an error message:
Process can't acces to file ... because it's used by another process.
Someone have an idea?
Thank you.
Here's the screenshot of Process Explorer
There are couple of thing you can do here.
1) If you can repro it, you can use Process Explorer at that moment and see which process is locking the file and if the process is ur process then making sure that you close the file handle after your work is done.
2) Use try/catch around the delete statement and retry after few seconds to see if the file handle was released.
3) If you can do it offline you can put in some queue and do the deletion on it later on.
You solve this by using c# locks. Just embed your code inside a lock statement and your threads will be safe and wait each other to complete processing.
I found the solution:
in my import method, there a call to that method
public void Save(string originalFile, int maxWidth, int maxHeight, int quality, string filePath)
{
Bitmap image = new Bitmap(originalFile);
Save(ref image, maxWidth, maxHeight, quality, filePath);
}
The bitmap maintains the file opened blocking delete.
just added
image.Dispose();
in the methos and it work fine.
Thank you for your help, and thank you for process explorer. Very useful tool
I want to get the video file duration in string using C#. I searched the internet and all i get is:
ffmpeg -i inputfile.avi
And every1 say that parse the output for duration.
Here is my code which is
string filargs = "-y -i " + inputavi + " -ar 22050 " + outputflv;
Process proc;
proc = new Process();
proc.StartInfo.FileName = spath;
proc.StartInfo.Arguments = filargs;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.RedirectStandardOutput = false;
try
{
proc.Start();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
try
{
proc.WaitForExit(50 * 1000);
}
catch (Exception ex)
{ }
finally
{
proc.Close();
}
Now please tell me how can i save the output string and parse it for the video duration.
Thanks and regards,
There is another Option to get Video Length ,by using Media Info DLL
Using Ffmpeg :
proc.StartInfo.RedirectErrorOutput = true;
string message = proc.ErrorOutput.ReadToEnd();
Filtering shouldn't be an issue ,so do it you're self.
PS : using ffmpeg you should not read the StandardOutput but ErrorOutput i dont know why ,but it work's only like that.
FFmpeg is a little bit of an adventure to parse. But in any case, here's what you need to know.
First, FFmpeg doesn't play well with RedirectOutput options
What you'll need to do is instead of launching ffmpeg directly, launch cmd.exe, passing in ffmpeg as an argument, and redirecting the output to a "monitor file" through a command line output like so... note that in the while (!proc.HasExited) loop you can read this file for real-time FFmpeg status, or just read it at the end if this is a quick operation.
FileInfo monitorFile = new FileInfo(Path.Combine(ffMpegExe.Directory.FullName, "FFMpegMonitor_" + Guid.NewGuid().ToString() + ".txt"));
string ffmpegpath = Environment.SystemDirectory + "\\cmd.exe";
string ffmpegargs = "/C " + ffMpegExe.FullName + " " + encodeArgs + " 2>" + monitorFile.FullName;
string fullTestCmd = ffmpegpath + " " + ffmpegargs;
ProcessStartInfo psi = new ProcessStartInfo(ffmpegpath, ffmpegargs);
psi.WorkingDirectory = ffMpegExe.Directory.FullName;
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.Verb = "runas";
var proc = Process.Start(psi);
while (!proc.HasExited)
{
System.Threading.Thread.Sleep(1000);
}
string encodeLog = System.IO.File.ReadAllText(monitorFile.FullName);
Great, now you've got the log of what FFmpeg just spit out. Now to get the duration. The duration line will look something like this:
Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
Clean up the results into a List<string>:
var encodingLines = encodeLog.Split(System.Environment.NewLine[0]).Where(line => string.IsNullOrWhiteSpace(line) == false && string.IsNullOrEmpty(line.Trim()) == false).Select(s => s.Trim()).ToList();
... then loop through them looking for Duration.
foreach (var line in encodingLines)
{
// Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
if (line.StartsWith("Duration"))
{
var duration = ParseDurationLine(line);
}
}
Here's some code that can do the parse for you:
private TimeSpan ParseDurationLine(string line)
{
var itemsOfData = line.Split(" "[0], "="[0]).Where(s => string.IsNullOrEmpty(s) == false).Select(s => s.Trim().Replace("=", string.Empty).Replace(",", string.Empty)).ToList();
string duration = GetValueFromItemData(itemsOfData, "Duration:");
return TimeSpan.Parse(duration);
}
private string GetValueFromItemData(List<string> items, string targetKey)
{
var key = items.FirstOrDefault(i => i.ToUpper() == targetKey.ToUpper());
if (key == null) { return null; }
var idx = items.IndexOf(key);
var valueIdx = idx + 1;
if (valueIdx >= items.Count)
{
return null;
}
return items[valueIdx];
}
Just check it out::
//Create varriables
string ffMPEG = System.IO.Path.Combine(Application.StartupPath, "ffMPEG.exe");
system.Diagnostics.Process mProcess = null;
System.IO.StreamReader SROutput = null;
string outPut = "";
string filepath = "D:\\source.mp4";
string param = string.Format("-i \"{0}\"", filepath);
System.Diagnostics.ProcessStartInfo oInfo = null;
System.Text.RegularExpressions.Regex re = null;
System.Text.RegularExpressions.Match m = null;
TimeSpan Duration = null;
//Get ready with ProcessStartInfo
oInfo = new System.Diagnostics.ProcessStartInfo(ffMPEG, param);
oInfo.CreateNoWindow = true;
//ffMPEG uses StandardError for its output.
oInfo.RedirectStandardError = true;
oInfo.WindowStyle = ProcessWindowStyle.Hidden;
oInfo.UseShellExecute = false;
// Lets start the process
mProcess = System.Diagnostics.Process.Start(oInfo);
// Divert output
SROutput = mProcess.StandardError;
// Read all
outPut = SROutput.ReadToEnd();
// Please donot forget to call WaitForExit() after calling SROutput.ReadToEnd
mProcess.WaitForExit();
mProcess.Close();
mProcess.Dispose();
SROutput.Close();
SROutput.Dispose();
//get duration
re = new System.Text.RegularExpressions.Regex("[D|d]uration:.((\\d|:|\\.)*)");
m = re.Match(outPut);
if (m.Success) {
//Means the output has cantained the string "Duration"
string temp = m.Groups(1).Value;
string[] timepieces = temp.Split(new char[] {':', '.'});
if (timepieces.Length == 4) {
// Store duration
Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
}
}
With thanks,
Gouranga Das.