/// <summary>
        /// Put the extracting raw streams back together in TS format using TsMuxer
        /// </summary>
        /// <param name="originalFile">Path to original source video</param>
        /// <param name="remuxedFile">Path to output remuxed file</param>
        /// <param name="extractGraphResults">Extract graph object which has extracts the stream into raw parts</param>
        /// <returns>True if successful</returns>
        public static bool RemuxRawTSMuxer(string originalFile, string remuxedFile, ExtractWithGraph extractGraphResults, JobStatus jobStatus, Log jobLog)
        {
            float audioDelay = 0, fps = 0;
            string vcodec = "";

            if (extractGraphResults == null || String.IsNullOrWhiteSpace(originalFile) || String.IsNullOrWhiteSpace(remuxedFile))
                return false;
            
            // Atleast 1 audio or video stream
            if (String.IsNullOrWhiteSpace(extractGraphResults.VideoPart) && (extractGraphResults.AudioParts.Count < 1))
                return false;

            Util.FileIO.TryFileDelete(remuxedFile); // Start clean

            // Audio Delay on the original file
            if (extractGraphResults.AudioParts.Count > 0)
            {
                audioDelay = VideoParams.AudioDelay(originalFile);
                jobLog.WriteEntry("Audio Delay : " + audioDelay.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }

            // fps and codec of video
            if (!String.IsNullOrWhiteSpace(extractGraphResults.VideoPart))
            {
                fps = VideoParams.FPS(extractGraphResults.VideoPart);
                vcodec = VideoParams.VideoFormat(extractGraphResults.VideoPart).ToLower();
                FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(extractGraphResults.VideoPart, jobStatus, jobLog);
                if (String.IsNullOrWhiteSpace(vcodec) || (fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0)))
                {
                    jobLog.WriteEntry("MediaInfo reading FPS/Vcodec failed, Reading FFMPEG info from " + extractGraphResults.VideoPart, Log.LogEntryType.Debug);

                    if (ffmpegStreamInfo.Success) // Don't check for parse error since this is raw video stream it will always give an parse error for N/A duration
                    {
                        if ((fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0))) // MediaInfo did not succeed or got wrong value
                            fps = ffmpegStreamInfo.MediaInfo.VideoInfo.FPS; // Get the FPS

                        if (String.IsNullOrWhiteSpace(vcodec))
                            vcodec = ffmpegStreamInfo.MediaInfo.VideoInfo.VideoCodec; // Get video codecc
                    }

                    if (fps <= 0)
                        jobLog.WriteEntry("Unable to read FPS from " + originalFile + ", assuming no video stream", Log.LogEntryType.Warning);

                    if (String.IsNullOrWhiteSpace(vcodec))
                        jobLog.WriteEntry("No Video Codec detected in raw stream, assuming no video stream", Log.LogEntryType.Warning);
                }

                jobLog.WriteEntry("Video Codec detected : " + vcodec + ", FPS : " + fps.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }

            // Setup the TSMuxer - http://forum.doom9.org/archive/index.php/t-142559.html
            string MetaFile = MCEBuddy.Util.FilePaths.GetFullPathWithoutExtension(remuxedFile) + ".meta";
            string MetaFileContents = "MUXOPT --no-pcr-on-video-pid --new-audio-pes --vbr --vbv-len=500\r\n";

            if (String.IsNullOrWhiteSpace(vcodec) || fps <= 0)
                jobLog.WriteEntry("No video stream detected, skipping video" + fps.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug); // Nothing to do, no video stream
            else if ((vcodec == "avc") || (vcodec == "h264"))
            {
                /*if (!_allowH264CopyRemuxing)
                {
                    _jobStatus.ErrorMsg = "TsMuxer incompatible settings, H264 video detected";
                    _jobLog.WriteEntry(_jobStatus.ErrorMsg, Log.LogEntryType.Error);
                    return false;
                }*/
                // There is no other recourse so mux it

                MetaFileContents += "V_MPEG4/ISO/AVC, " + Util.FilePaths.FixSpaces(extractGraphResults.VideoPart) + ", fps=" + fps.ToString(CultureInfo.InvariantCulture) + ", insertSEI, contSPS\r\n";
            }
            else
                MetaFileContents += "V_MPEG-2, " + Util.FilePaths.FixSpaces(extractGraphResults.VideoPart) + ", fps=" + fps.ToString(CultureInfo.InvariantCulture) + "\r\n";

            // Setup the Audio streams
            foreach (string AudioPart in extractGraphResults.AudioParts)
            {
                string acodec = VideoParams.AudioFormat(AudioPart).ToLower();
                if (String.IsNullOrWhiteSpace(acodec))
                {
                    jobLog.WriteEntry("No Audio Codec detected in raw stream", Log.LogEntryType.Error);
                    return false;
                }

                jobLog.WriteEntry("Audio Stream Codec detected : " + acodec, Log.LogEntryType.Debug);

                if (acodec.Contains("ac3") || acodec.Contains("ac-3"))
                    MetaFileContents += "A_AC3";
                else if (acodec.Contains("aac"))
                    MetaFileContents += "A_AAC";
                else if (acodec.Contains("dts"))
                    MetaFileContents += "A_DTS";
                else
                    MetaFileContents += "A_MP3";

                MetaFileContents += ", " + Util.FilePaths.FixSpaces(AudioPart);

                if (audioDelay == 0)
                    jobLog.WriteEntry("Skipping Audio Delay correction, cannnot read", Log.LogEntryType.Warning);
                else
                    MetaFileContents += ", timeshift=" + (-1 * audioDelay).ToString(CultureInfo.InvariantCulture) + "s";

                MetaFileContents += "\r\n";
            }

            try
            {
                jobLog.WriteEntry("Writing TsMuxer Meta commands : \r\n" + MetaFileContents + "\r\nTo : " + MetaFile, Log.LogEntryType.Debug);
                System.IO.File.WriteAllText(MetaFile, MetaFileContents);
            }
            catch (Exception e)
            {
                jobLog.WriteEntry(Localise.GetPhrase("ReMuxTsMuxer failed to write Metafile...") + "\r\nError : " + e.ToString(), Log.LogEntryType.Error);
                return false;
            }

            AppWrapper.TSMuxer tsmuxer = new TSMuxer(Util.FilePaths.FixSpaces(MetaFile) + " " + Util.FilePaths.FixSpaces(remuxedFile), jobStatus, jobLog);
            tsmuxer.Run();

            Util.FileIO.TryFileDelete(MetaFile);

            if (!tsmuxer.Success) // process was terminated or failed
            {
                jobLog.WriteEntry("ReMuxTsMuxer failed", Log.LogEntryType.Error);
                Util.FileIO.TryFileDelete(remuxedFile);
                return false;
            }

            return (FileIO.FileSize(remuxedFile) <= 0 ? false : true); // check if the file exists
        }
        /// <summary>
        /// Extract the Audio and Video Streams from the file using Windows DirectShow Graph Filter and then remux using TSMuxer and FFMPEG.
        /// Currently only support extracting 1 audio stream, so a good option for DVRMS (backup for WTV)
        /// </summary>
        /// <returns>True if successful in Remuxing to TS</returns>
        private bool DirectShowExtractAndRemuxStreams()
        {
            if (_jobStatus.Cancelled)
            {
                _jobLog.WriteEntry("Job cancelled, skipping DirectShow Streams Extraction", Log.LogEntryType.Error);
                return false;
            }

            switch (Util.FilePaths.CleanExt(_RecordingFile))
            {
                case ".wtv":
                case ".dvr-ms":
                case ".tivo":
                    break;

                default:
                    _jobLog.WriteEntry(this, "DirectShow Streams remuxing only supported for WTV, DVR-MS and TIVO files", Log.LogEntryType.Error);
                    return false;
            }

            try
            {
                _jobLog.WriteEntry("Extracting with Graph....", Log.LogEntryType.Debug);
                _extract = new ExtractWithGraph(_RecordingFile, _destinationPath, ExtractWithGraph.ExtractMediaType.Audio | ExtractWithGraph.ExtractMediaType.Video, _jobStatus, _jobLog); // Extract only audio and video streams, we don't need subtitle
            }
            catch (Exception e)
            {
                try
                {
                    if (_extract != null)
                        _extract.Dispose();
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Error Extracting with Graph....") + "\r\nError : " + e.ToString(), Log.LogEntryType.Error, true);
                }
                catch (Exception e1)
                {
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Error disposing objects after graph failure....") + "\r\nError : " + e1.ToString(), Log.LogEntryType.Error, true);
                }

                return false;
            }

            try
            {
                _jobLog.WriteEntry("Building with Graph....", Log.LogEntryType.Debug);
                _extract.BuildGraph(); // TODO: Currently this only returns 1 audio stream and doesn't extract other audio streams
            }
            catch (Exception e)
            {
                try
                {
                    _jobLog.WriteEntry(this, ("Error building graph.\r\nError : " + e.ToString()), Log.LogEntryType.Error);
                    _extract.DeleteParts();
                    _extract.Dispose();
                }
                catch (Exception e1)
                {
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Error disposing objects after graph failure....") + "\r\nError : " + e1.ToString(), Log.LogEntryType.Error, true);
                }

                return false;
            }

            // Atleast 1 audio or 1 video channel should be there
            if (String.IsNullOrWhiteSpace(_extract.VideoPart) && (_extract.AudioParts.Count < 1))
            {
                try
                {
                    _jobLog.WriteEntry(this, "Graph invalid number of video/audio streams", Log.LogEntryType.Error);
                    _extract.DeleteParts();
                    _extract.Dispose();
                }
                catch (Exception e1)
                {
                    _jobLog.WriteEntry(this, "Invalid Graph audio/video streams disposing.\r\nError : " + e1.ToString(), Log.LogEntryType.Error);
                }
                return false;
            }

            try
            {
                _jobLog.WriteEntry("Running with Graph....", Log.LogEntryType.Debug);
                _extract.RunGraph();
            }
            catch (Exception e)
            {
                try
                {
                    _jobLog.WriteEntry(this, ("Error running graph.\r\nError : " + e.ToString()), Log.LogEntryType.Error);
                    _extract.DeleteParts();
                    _extract.Dispose();
                }
                catch (Exception e1)
                {
                    _jobLog.WriteEntry(this, "Error disposing graph.\r\nError : " + e1.ToString(), Log.LogEntryType.Error);
                }
                return false;
            }

            // Did not abort
            if ((!_extract.SuccessfulExtraction))
            {
                try
                {
                    _jobLog.WriteEntry(this, "Error extracting streams using Graph", Log.LogEntryType.Error);
                    _extract.DeleteParts();
                    _extract.Dispose();
                }
                catch (Exception e1)
                {
                    _jobLog.WriteEntry(this, "Error extracting streams disposing.\r\nError : " + e1.ToString(), Log.LogEntryType.Error);
                }
                return false;
            }

            // Dispose the Graph object
            try
            {
                _jobLog.WriteEntry("Disposing Graph....", Log.LogEntryType.Debug);
                _extract.Dispose();
            }
            catch (Exception e)
            {
                _jobLog.WriteEntry(this, "Error disposing graph.\r\nError : " + e.ToString(), Log.LogEntryType.Error); // We can still continue here
            }

            // Put the streams back together in TS format
            // Check to see if we can use FFmpegParts to do the job
            _jobLog.WriteEntry(this, Localise.GetPhrase("Muxing streams with TsMuxer"), Log.LogEntryType.Information);
            _jobStatus.CurrentAction = Localise.GetPhrase("Muxing streams");
            if (!RemuxRawTSMuxer(_RecordingFile, RemuxedFile, _extract, _jobStatus, _jobLog) || !RemuxedFileOK())
            {
                _jobLog.WriteEntry(this, "TsMuxer Streams Muxing failed", Log.LogEntryType.Error);

                // Otherwise try tsMuxer
                _jobLog.WriteEntry(this, Localise.GetPhrase("Fallback muxing streams with FFMpegParts"), Log.LogEntryType.Information);
                _jobStatus.CurrentAction = Localise.GetPhrase("Fallback muxing streams");
                if (!RemuxRawPartsFFmpeg(_RecordingFile, RemuxedFile, _extract, _jobStatus, _jobLog) || !RemuxedFileOK())
                {
                    _extract.DeleteParts(); // Clean up
                    _jobLog.WriteEntry(this, "Fallback Remux Streams FFMpegParts failed", Log.LogEntryType.Error);
                    return false;
                }
            }

            _extract.DeleteParts(); // Clean up

            return true;
        }
        /// <summary>
        /// Remux with adjustments the extracted raw stream parts individually using ffmpeg
        /// </summary>
        /// <param name="originalFile">Path to original source video</param>
        /// <param name="remuxedFile">Path to output remuxed file</param>
        /// <param name="extractGraphResults">Extract graph object which has extracts the stream into raw parts</param>
        /// <returns>Success or Failure</returns>
        public static bool RemuxRawPartsFFmpeg(string originalFile, string remuxedFile, ExtractWithGraph extractGraphResults, JobStatus jobStatus, Log jobLog)
        {
            float audioDelay = 0, fps = 0;
            string vcodec = "";

            if (extractGraphResults == null || String.IsNullOrWhiteSpace(originalFile) || String.IsNullOrWhiteSpace(remuxedFile))
                return false;

            // Atleast 1 audio or video stream
            if (String.IsNullOrWhiteSpace(extractGraphResults.VideoPart) && (extractGraphResults.AudioParts.Count < 1))
                return false;

            Util.FileIO.TryFileDelete(remuxedFile); // Start clean

            // Audio Delay on the original file
            if (extractGraphResults.AudioParts.Count > 0)
            {
                audioDelay = VideoParams.AudioDelay(originalFile);
                jobLog.WriteEntry("Audio Delay : " + audioDelay.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }

            // FPS and codec of the video
            if (!String.IsNullOrWhiteSpace(extractGraphResults.VideoPart))
            {
                fps = VideoParams.FPS(extractGraphResults.VideoPart);
                vcodec = VideoParams.VideoFormat(extractGraphResults.VideoPart).ToLower();
                FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(extractGraphResults.VideoPart, jobStatus, jobLog);
                if (String.IsNullOrWhiteSpace(vcodec) || (fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0)))
                {
                    jobLog.WriteEntry("MediaInfo reading FPS/Vcodec failed, Reading FFMPEG info from " + extractGraphResults.VideoPart, Log.LogEntryType.Debug);

                    if (ffmpegStreamInfo.Success) // Don't check for parse error since this is raw video stream it will always give an parse error for N/A duration
                    {
                        if ((fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0))) // MediaInfo did not succeed or got wrong value
                            fps = ffmpegStreamInfo.MediaInfo.VideoInfo.FPS; // Get the FPS

                        if (String.IsNullOrWhiteSpace(vcodec))
                            vcodec = ffmpegStreamInfo.MediaInfo.VideoInfo.VideoCodec; // Get video codecc
                    }

                    if (fps <= 0)
                        jobLog.WriteEntry("Unable to read FPS from " + originalFile + ", assuming no video stream", Log.LogEntryType.Warning);

                    if (String.IsNullOrWhiteSpace(vcodec))
                        jobLog.WriteEntry("No Video Codec detected in raw stream, assuming no video stream", Log.LogEntryType.Warning);
                }

                jobLog.WriteEntry("Video Codec detected : " + vcodec + ", FPS : " + fps.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

                // TODO: Check what vcodec is returned is by MediaInfo.dll and do we really need to do this? This is last recourse
                /*if ((_mpeg2Codecs.Any(s => s.Contains(vcodec.ToLower()))) && (!_allowH264CopyRemuxing)) // Accept H264 direct remuxing only if allowed
                {
                    _jobLog.WriteEntry("FFMpegParts incompatible settings, non mpeg2video video detected and H264CopyRemuxing is not enabled", Log.LogEntryType.Error);
                    return false;
                }*/
            }

            string FFmpegParams = "";
            string ffmpegMapParams = ""; // Keep track of the tracks added since we need to map them
            int ffmpegMapCount = 0;

            // Generate the timestamps, framerate and specify input format since it's raw video and ffmpeg won't recognize it
            FFmpegParams += "-fflags +genpts -y";
            if (fps > 0) // Check if we have a video stream
                FFmpegParams += " -r " + fps.ToString(CultureInfo.InvariantCulture);

            // Check for valid video stream otherwise just audio
            if (!String.IsNullOrWhiteSpace(vcodec) && fps > 0)
            {
                if (audioDelay == 0)
                    jobLog.WriteEntry("Skipping Audio Delay correction, cannnot read", Log.LogEntryType.Warning);

                if (audioDelay > 0) // positive is used on video track
                    FFmpegParams += " -itsoffset " + audioDelay.ToString(CultureInfo.InvariantCulture);

                // Define input video stream type
                if ((vcodec == "avc") || (vcodec == "h264"))
                    FFmpegParams += " -f h264";
                else
                    FFmpegParams += " -f mpegvideo";

                FFmpegParams += " -i " + Util.FilePaths.FixSpaces(extractGraphResults.VideoPart); // Video
                ffmpegMapParams += " -map " + ffmpegMapCount.ToString() + ":v";
                ffmpegMapCount++;

            }

            // Insert the audio streams
            foreach (string audioPart in extractGraphResults.AudioParts)
            {
                if (!String.IsNullOrWhiteSpace(vcodec) && fps > 0)
                    if (audioDelay < 0) // Negative is used on audio tracks
                        FFmpegParams += " -itsoffset " + (-1 * audioDelay).ToString(CultureInfo.InvariantCulture);

                FFmpegParams += " -i " + Util.FilePaths.FixSpaces(audioPart); // Audio streams
                ffmpegMapParams += " -map " + ffmpegMapCount.ToString() + ":a";
                ffmpegMapCount++;
            }

            // Copy all Streams to output file
            FFmpegParams += ffmpegMapParams;
            
            // Audio
            if (extractGraphResults.AudioParts.Count > 0)
                FFmpegParams += " -acodec copy";
            else
                FFmpegParams += " -an";

            // Video
            if (!String.IsNullOrWhiteSpace(vcodec) && fps > 0)
                FFmpegParams += " -vcodec copy";
            else
                FFmpegParams += " -vn";

            // Output file
            FFmpegParams += " -f mpegts " + Util.FilePaths.FixSpaces(remuxedFile);

            if (!FFmpeg.FFMpegExecuteAndHandleErrors(FFmpegParams, jobStatus, jobLog, Util.FilePaths.FixSpaces(remuxedFile))) // process was terminated or failed
            {
                jobLog.WriteEntry("FFmpeg Remux Parts failed", Log.LogEntryType.Error);
                Util.FileIO.TryFileDelete(remuxedFile);
                return false;
            }

            return (FileIO.FileSize(remuxedFile) <= 0 ? false : true); // check if the file exists
        }
Beispiel #4
0
        /// <summary>
        /// Main conversion routine
        /// </summary>
        public void Convert()
        {
            _jobStatus.ErrorMsg = ""; //start clean
            _jobStatus.SuccessfulConversion = false; //no successful conversion yet
            _jobStatus.Completed = false;

            Log jobLog = CreateLog(_conversionOptions.sourceVideo);

            //Debug, dump all the conversion parameter before starting to help with debugging
            jobLog.WriteEntry("Starting conversion - DEBUG MESSAGES", Log.LogEntryType.Debug);
            jobLog.WriteEntry("Windows OS Version -> " + Util.OSVersion.TrueOSVersion.ToString() + " (" + Util.OSVersion.GetOSVersion().ToString() + ", " + Util.OSVersion.GetOSProductType() + ")", Log.LogEntryType.Information);
            jobLog.WriteEntry("Windows Platform -> " + (Environment.Is64BitOperatingSystem ? "64 Bit" : "32 Bit"), Log.LogEntryType.Information);
            jobLog.WriteEntry("MCEBuddy Build Platform -> " + ((IntPtr.Size == 4) ? "32 Bit" : "64 Bit"), Log.LogEntryType.Information);
            string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
            jobLog.WriteEntry("MCEBuddy Build Version : " + currentVersion, Log.LogEntryType.Information);
            jobLog.WriteEntry("MCEBuddy Build Date : " + File.GetLastWriteTime(System.Reflection.Assembly.GetExecutingAssembly().Location).ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);
            jobLog.WriteEntry("MCEBuddy Running as Service : " + GlobalDefs.IsEngineRunningAsService.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);
            jobLog.WriteEntry(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.ToString(), Log.LogEntryType.Debug);
            jobLog.WriteEntry(_conversionOptions.ToString(), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Max Concurrent Jobs -> " + _maxConcurrentJobs.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Commercial Skip Cut (profile + task) -> " + _commercialSkipCut.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Auto DeInterlacing (profile + task) -> " + _autoDeinterlace.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Pre-Conversion Commercial Remover -> " + _preConversionCommercialRemover.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Copy LOG File -> " + _copyLOGFile.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Free Space Check -> " + _spaceCheck.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Subtitle Cut Segment Incremental Offset -> " + _subtitleSegmentOffset.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Locale Language -> " + Localise.ThreeLetterISO().ToUpper(), Log.LogEntryType.Debug);
            
            try
            {
                RegistryKey installed_versions = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP");
                string[] version_names = installed_versions.GetSubKeyNames();
                //version names start with 'v', eg, 'v3.5' which needs to be trimmed off before conversion
                string Framework = version_names[version_names.Length - 1].Remove(0, 1);
                string SP = (string)installed_versions.OpenSubKey(version_names[version_names.Length - 1]).GetValue("SP", 0).ToString();
                jobLog.WriteEntry(".NET Framework Version -> " + Framework + ", Service Pack -> " + SP, Log.LogEntryType.Debug);
            }
            catch
            {
                jobLog.WriteEntry("Cannot get .NET Framework version from Registry", Log.LogEntryType.Warning);
            }

            try // the entire conversion process, incase of an abort signal
            {
                jobLog.WriteEntry(this, "Current System language is " + CultureInfo.CurrentCulture.DisplayName + " (" + CultureInfo.CurrentCulture.ThreeLetterISOLanguageName.ToString() + ")", Log.LogEntryType.Information);
                jobLog.WriteEntry(this, "Converting file -> " + _conversionOptions.sourceVideo, Log.LogEntryType.Information);

                // Create and clear the contents of the working folder
                Util.FilePaths.CreateDir(_conversionOptions.workingPath);
                Util.FileIO.ClearFolder(_conversionOptions.workingPath);
                if (!Directory.Exists(_conversionOptions.workingPath)) // Check if it exists, possible the folder isn't accesible or user put a bogus value here
                {
                    jobLog.WriteEntry(this, "Unable to create temp directory " + _conversionOptions.workingPath + ". Conversion will fail.", Log.LogEntryType.Error);
                    _jobStatus.ErrorMsg = "Unable to create temp directory";
                    Cleanup(jobLog);
                    return; // serious problem
                }

                // Print the vesion of FFMPEG or FFProbe being used (since FFProbe runs in silent mode) and dump file information
                if (!_jobStatus.Cancelled)
                    FFmpegMediaInfo.DumpFileInformation(OriginalFileName, _jobStatus, jobLog); // Dump the media information for debugging purposes

                // Run the pre metadata custom command from the user if configured
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                    string tmpSrt, tmpEdl;
                    tmpEdl = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl" : "");
                    tmpSrt = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt" : "");
                    CustomCommand customCommand = new CustomCommand("PreMetaCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, "", _convertedFile, OriginalFileName, _remuxedVideo, tmpEdl, tmpSrt, null, _jobStatus, jobLog); // EDl and SRT file may lie with the source video
                    if (!customCommand.Run())
                    {
                        jobLog.WriteEntry(this, ("Pre metadata Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("Pre metadata Custom command failed to run, critical failure");
                        Cleanup(jobLog);
                        return; // serious problem
                    }

                    // Check if the source file has been tampered with
                    if (!File.Exists(_conversionOptions.sourceVideo))
                    {
                        jobLog.WriteEntry(this, ("PreMeta Source file has been renamed or deleted by custom command") + " -> " + _conversionOptions.sourceVideo, Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("PreMeta Source file has been renamed or deleted by custom command");
                        Cleanup(jobLog);
                        return; // serious problem
                    }
                    else
                        jobLog.WriteEntry(this, ("Finished pre metadata custom command, source file size [KB]") + " " + (Util.FileIO.FileSize(_conversionOptions.sourceVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                }

                // Get and process Metadata
                if (!_jobStatus.Cancelled)
                {
                    // Download/extract the video metadata first
                    LogStatus(Localise.GetPhrase("Getting show information and banner from Internet sources"), jobLog);
                    _metaData = new VideoMetaData(_conversionOptions, _jobStatus, jobLog);
                    _metaData.Extract();

                    // Check metadata for Copy Protection
                    if (_metaData.MetaData.CopyProtected && !_conversionOptions.renameOnly) // If it's copy protected fail the conversion here with the right message and status, if we are renaming only then skip the check
                    {
                        if (_conversionOptions.ignoreCopyProtection) // If we are asked to ignore copy protection, we will just continue
                            jobLog.WriteEntry(this, ("ERROR: VIDEO IS COPYPROTECTED. CONVERSION WILL FAIL, BUT TRYING ANYWAYS."), Log.LogEntryType.Warning);
                        else
                        {
                            jobLog.WriteEntry(this, ("ERROR: VIDEO IS COPYPROTECTED. CONVERSION WILL FAIL, STOPPING CONVERSION"), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = ("ERROR: VIDEO IS COPYPROTECTED. CONVERSION WILL FAIL");
                            Cleanup(jobLog);
                            return;
                        }
                    }
                }

                // DO THIS HERE and NOT in in the queue manager because you need ALL METADATA before you can Generate Filename to compare with history file.
                // By now you have downloaded additional metadata which can be used to generate the filename
                // Now that we have metadata, figure out if we need to process the file
                // Check if the destination file exists (after calculating destination name and path) and check if are required to reprocess files that exist
                if (!_jobStatus.Cancelled)
                {
                    // Check if we need to check for destination file reprocessing
                    if (_conversionOptions.skipReprocessing)
                    {
                        jobLog.WriteEntry(this, "Checking for destination file skip reprocessing", Log.LogEntryType.Information);

                        string destinationFile = GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog);

                        // Check if we need to skip reprocessing this file
                        bool skipReprocessing = false;
                        if (File.Exists(destinationFile)) // Check if the destination file exists
                        {
                            jobLog.WriteEntry(this, "Destination file " + destinationFile + " EXISTS, skipping conversion - SUCCESSFUL processing", Log.LogEntryType.Warning);
                            skipReprocessing = true;
                        }

                        if (!skipReprocessing && _conversionOptions.checkReprocessingHistory) // If we aren't already skipping reprocessig (save disk IO/CPU), do we need to check the history file for reprocessing?
                        {
                            if (QueueManager.DoesConvertedFileExistCheckHistory(destinationFile))
                            {
                                jobLog.WriteEntry(this, "Destination file " + destinationFile + " found converted in HISTORY, skipping re-conversion - SUCCESSFUL processing", Log.LogEntryType.Warning);
                                skipReprocessing = true;
                            }
                        }
                        
                        // Check if the destination file exists
                        if (skipReprocessing)
                        {
                            // SPECIAL CONDITION, THIS IS A SUCCESSFUL EXIT - Let exit here since we don't need to process it
                            _convertedFile = destinationFile; // Update the link to the converted file as it will be required for post processing
                            _jobStatus.ErrorMsg = ""; // all done, no error here, skipping processing but successful
                            _jobStatus.SuccessfulConversion = _jobStatus.SuccessfulSkipConversion = true; // now the original file can be deleted if required
                            Cleanup(jobLog);
                            return;
                        }
                        else
                        {
                            jobLog.WriteEntry(this, "Destination file " + destinationFile + " does not exist, continuing with conversion", Log.LogEntryType.Information);
                        }
                    }
                }

                // Run the pre remuxing custom command from the user if configured
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                    string tmpSrt, tmpEdl;
                    tmpEdl = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl" : "");
                    tmpSrt = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt" : "");
                    CustomCommand customCommand = new CustomCommand("PreCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, tmpEdl, tmpSrt, _metaData.MetaData, _jobStatus, jobLog); // EDL and SRT file may lie with the source video
                    if (!customCommand.Run())
                    {
                        jobLog.WriteEntry(this, ("Pre remuxing Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("Pre remuxing Custom command failed to run, critical failure");
                        Cleanup(jobLog);
                        return; // serious problem
                    }

                    // Check if the source file has been tampered with
                    if (!File.Exists(WorkingVideo))
                    {
                        jobLog.WriteEntry(this, ("Pre remuxing Source file has been renamed or deleted by custom command") + " -> " + _conversionOptions.sourceVideo, Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("Pre rexmuing Source file has been renamed or deleted by custom command");
                        Cleanup(jobLog);
                        return; // serious problem
                    }
                    else
                        jobLog.WriteEntry(this, ("Finished pre remuxing custom command, source file size [KB]") + " " + (Util.FileIO.FileSize(_conversionOptions.sourceVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                }

                //Check for available disk space
                LogStatus(Localise.GetPhrase("Checking for disk space"), jobLog);
                if (!SufficientSpace(jobLog))
                {
                    jobLog.WriteEntry(this, ("Insufficient disk space"), Log.LogEntryType.Error);
                    _jobStatus.ErrorMsg = "Insufficient disk space";
                    Cleanup(jobLog); // close the joblog and clean up
                    return;
                }

                // Check for small test files);)
                if (Util.FileIO.FileSize(_conversionOptions.sourceVideo) < 100000000)
                {
                    jobLog.WriteEntry(this, ("This is a small video file less than 100MB. Some actions such as advertisement removal will not occur"), Log.LogEntryType.Warning);
                    _conversionOptions.commercialRemoval = CommercialRemovalOptions.None;
                }

                // Copy support files over
                if (!_jobStatus.Cancelled)
                {
                    // If the SRT File exists, copy it
                    jobLog.WriteEntry(this, ("Checking for SRT File"), Log.LogEntryType.Information);
                    {
                        string srtFile;
                        if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            srtFile = GlobalDefs.UNICODE_TEMPNAME + ".srt";
                        else
                            srtFile = Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt"; // SRT file along with original source video

                        if (File.Exists(srtFile) && (Util.FileIO.FileSize(srtFile) > 0)) // Exist and non empty
                        {
                            string srtDest = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(srtFile));
                            try
                            {
                                File.Copy(srtFile, srtDest);
                                _deleteSRTFile = true;
                                _srtFile = srtDest;
                                jobLog.WriteEntry(this, ("Found existing SRT file and saved it"), Log.LogEntryType.Information);
                            }
                            catch (Exception e)
                            {
                                jobLog.WriteEntry(this, ("Found existing SRT file but unable to save it") + "\r\nError : " + e.ToString(), Log.LogEntryType.Warning);
                            }
                        }
                    }

                    //If the EDL/EDLP file exists, copy it to working directory
                    jobLog.WriteEntry(this, Localise.GetPhrase("Checking for EDL File"), Log.LogEntryType.Information);
                    {
                        string edlFile;
                        if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            edlFile = GlobalDefs.UNICODE_TEMPNAME + ".edl";
                        else
                            edlFile = Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl";

                        if (File.Exists(edlFile)) // EDL File
                        {
                            string edlDest = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(edlFile));
                            try
                            {
                                File.Copy(edlFile, edlDest);
                                _saveEDLFile = true;
                                jobLog.WriteEntry(this, ("Found existing EDL file and saved it"), Log.LogEntryType.Information);
                            }
                            catch (Exception e)
                            {
                                jobLog.WriteEntry(this, ("Found existing EDL file but unable to save it") + "\r\nError : " + e.ToString(), Log.LogEntryType.Warning);
                            }
                        }
                    }

                    // Check for EDLP file
                    jobLog.WriteEntry(this, ("Checking for EDLP File"), Log.LogEntryType.Information);
                    {
                        string edlpFile;
                        if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            edlpFile = GlobalDefs.UNICODE_TEMPNAME + ".edlp";
                        else
                            edlpFile = Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edlp";

                        if (File.Exists(edlpFile)) // EDLP file
                        {
                            string edlpDest = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(edlpFile));
                            try
                            {
                                File.Copy(edlpFile, edlpDest);
                                _saveEDLFile = true;
                                jobLog.WriteEntry(this, ("Found existing EDLP file and saved it"), Log.LogEntryType.Information);
                            }
                            catch (Exception e)
                            {
                                jobLog.WriteEntry(this, ("Found existing EDLP file but unable to save it") + "\r\nError : " + e.ToString(), Log.LogEntryType.Warning);
                            }
                        }
                    }
                }

                if (!_jobStatus.Cancelled)
                {
                    // DO this last before starting the analysis/conversion process after copying SRT, EDL, NFO etc files
                    // If Commerical removal is set for TS files copy them to the temp directory (During Commercial removal, files (except TS) are remuxed later into their own temp working directories)
                    // We need exclusive access to each copy of the file in their respective temp working directories otherwise commercial skipping gets messed up when we have multiple simultaneous tasks for the same file (they all share/overwrite the same EDL file) which casuses a failure
                    // Check if the TS file (or any file if we are skipping remuxing) has any Zero Channel Audio tracks, in which case it needs to be remuxed to remove/compensate for them, remuxing will overwrite the original file if it's a TS file, so make a local copy
                    string sourceExt = FilePaths.CleanExt(_conversionOptions.sourceVideo);
                    if ((sourceExt == ".ts") || _conversionOptions.skipRemuxing)
                    {
                        FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(_conversionOptions.sourceVideo, _jobStatus, jobLog);
                        if (ffmpegStreamInfo.Success && !ffmpegStreamInfo.ParseError)
                        {
                            if ((ffmpegStreamInfo.ZeroChannelAudioTrackCount > 0)) // TODO: Do we need to check for Imparied Audio tracks here when not Remuxing? (imparied audio tracks are sometimes empty - can they cause the encoders to fail?)
                                _zeroChannelAudio = true;
                        }
                        else
                        {
                            jobLog.WriteEntry(this, ("Unable to read FFMPEG MediaInfo to verify remuxsupp audio streams"), Log.LogEntryType.Warning);
                        }
                    }

                    // Make a local copy only if we are renaming, has zero channel audio or skipping remuxing for non-TS or is a TS file with some conditions met (like commercial removal, etc where the original file may be modified or locked and accessed simultaneously)
                    // If the sourcefile has a Unicode name, always copy it since underlying programs cannot handle unicode names and then rename the local copy
                    if ((Text.ContainsUnicode(_conversionOptions.sourceVideo) && !_conversionOptions.renameOnly) || (!_conversionOptions.skipCopyBackup && (_zeroChannelAudio || _conversionOptions.renameOnly || (((sourceExt == ".ts") || ((sourceExt != ".ts") && _conversionOptions.skipRemuxing)) && (_conversionOptions.commercialRemoval != CommercialRemovalOptions.None)))))
                    {
                        if (_conversionOptions.skipCopyBackup)
                            jobLog.WriteEntry(this, "DANGER: Skip copying original files (skip original backup) is enabled. This could lead to conversion failures or unpredictable outcomes. Please DISABLE this option unless absolutely necessary.", Log.LogEntryType.Warning);

                        string newSource;
                        if (Text.ContainsUnicode(_conversionOptions.sourceVideo) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            newSource = Path.Combine(_conversionOptions.workingPath, GlobalDefs.UNICODE_TEMPNAME + Util.FilePaths.CleanExt(_conversionOptions.sourceVideo));
                        else
                            newSource = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(_conversionOptions.sourceVideo));

                        LogStatus(Localise.GetPhrase("Copying source file to working directory"), jobLog);
                        try
                        {
                            jobLog.WriteEntry(this, ("Copying source video to working directory") + " Source:" + _conversionOptions.sourceVideo + ", Target:" + newSource, Log.LogEntryType.Information);
                            File.Copy(_conversionOptions.sourceVideo, newSource, true); // Copy the file, overwrite if required
                            
                            _conversionOptions.sourceVideo = newSource; // replace the source file that we will work on going forward on success
                        }
                        catch (Exception e)
                        {
                            jobLog.WriteEntry(this, ("Unable to copy source video to working directory") + " Source:" + _conversionOptions.sourceVideo + ", Target:" + newSource + " Error : " + e.ToString(), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = ("Unable to copy source video to working directory");
                            Cleanup(jobLog);
                            return;
                        }
                    }
                }

                // If we are ONLY renaming NOT using free Comskip AND WITHOUT extracting subtitles, we skip the video processing, removal etc (i.e. no Remuxing)
                // If detecting commercials while only renaming, Showanalyzer and donator comskip support all formats, so no remuxing required
                if (!(_conversionOptions.renameOnly && !(_conversionOptions.commercialRemoval == CommercialRemovalOptions.Comskip && !(new Comskip(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.comskipPath, jobLog).IsDonator)) && String.IsNullOrEmpty(_conversionOptions.extractCC)))
                {
                    if (!_jobStatus.Cancelled)
                    {
                        if (_conversionOptions.skipRemuxing)
                            jobLog.WriteEntry(this, "SKIPPING REMUXING, this may lead to conversion failure since all underlying apps may not support all file formats.\r\nWTV commercial detection is only supported by donator version of Comskip (http://www.kaashoek.com/comskip/).", Log.LogEntryType.Warning);

                        // Remux media center recordings or any video if commercials are being removed to TS (except TS) and we are not asked to skip remuxing
                        // Unless TS tracks have zero channel audio in them, then remux them to remove the zero audio channel tracks, otherwise future functions fails (like trim) - NOTE while 0 TS channel audio is remuxed, it ends up replacing the original file
                        string sourceExt = FilePaths.CleanExt(_conversionOptions.sourceVideo);
                        if (((sourceExt != ".ts") && !_conversionOptions.skipRemuxing) || _zeroChannelAudio)
                        {
                            LogStatus(Localise.GetPhrase("Remuxing recording"), jobLog);
                            RemuxMediaCenter.RemuxMCERecording remux = new RemuxMCERecording(_conversionOptions, _jobStatus, jobLog);
                            bool res = remux.Remux();
                            jobLog.WriteEntry(this, ("Remuxing: Percentage Complete") + " " + _jobStatus.PercentageComplete.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                            if (!res)       
                            {
                                // Remux failed
                                _jobStatus.ErrorMsg = ("Remux failed");
                                jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                Cleanup(jobLog);
                                return;
                            }
                            WorkingVideo = remux.RemuxedFile;
                            _workingVideoRecoded = remux.VideoStreamRecoded; // Was the original video recoded? We lost CC data in remued file
                            _initialEDLSkipSeconds = (_saveEDLFile ? remux.SkipInitialSeconds : 0); // If we are using a saved EDL File, we need to compensate for any seconds that were trimmed while remuxing to compensate for EDL later
                            jobLog.WriteEntry(this, "Was remuxed video recoded : " + _workingVideoRecoded.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                            jobLog.WriteEntry(this, "Remuxed video file : " + WorkingVideo, Log.LogEntryType.Debug);
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished Remuxing, file size [KB]") + " " + (Util.FileIO.FileSize(WorkingVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                        }
                    }
                }

                // If we are ONLY renaming, we skip the video processing, trimming, removal etc
                if (!_conversionOptions.renameOnly)
                {
                    // If we are trimming the video, try to do it here, if it fails then try to do it during the conversion
                    if (!_jobStatus.Cancelled)
                    {
                        LogStatus(Localise.GetPhrase("Trimming video recording"), jobLog);
                        TrimVideo trimVideo = new TrimVideo(_conversionOptions.profile, _jobStatus, jobLog);
                        if (!trimVideo.Trim(WorkingVideo, _conversionOptions.workingPath, _conversionOptions.startTrim, _conversionOptions.endTrim))
                        {
                            // Trimming failed - just log the error here, we'll try to pick it up during conversion
                            jobLog.WriteEntry(this, "Trimming failed, will retry during conversion", Log.LogEntryType.Warning);
                        }
                        else
                        {
                            jobLog.WriteEntry(this, "Trimming successful, setting trim parameters to 0 to avoid retrimming", Log.LogEntryType.Debug);
                            _conversionOptions.startTrim = _conversionOptions.endTrim = 0; // Indicate that trimming is complete so we don't redo it later
                            WorkingVideo = trimVideo.TrimmedVideo; // Update the working video path with the new trimmed video
                        }
                    }
                }

                // Do commercial detection if there is not metadata copy protection since this can be run for rename only option also
                if (!_jobStatus.Cancelled && (!_metaData.MetaData.CopyProtected || _conversionOptions.ignoreCopyProtection))
                {
                    // Get the Video duration
                    float duration = VideoParams.VideoDuration(WorkingVideo);
                    if (duration <= 0)
                    {

                        FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(WorkingVideo, _jobStatus, jobLog);
                        if (!ffmpegStreamInfo.Success || ffmpegStreamInfo.ParseError)
                        {
                            // Getting Video Duration failed
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Unable to get Video Duration");
                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }

                        duration = ffmpegStreamInfo.MediaInfo.VideoInfo.Duration;
                    }

                    // If we are using ShowAnalyzer, do it here
                    jobLog.WriteEntry(this, "Checking for ShowAnalyzer", Log.LogEntryType.Information);
                    if (_conversionOptions.commercialRemoval == CommercialRemovalOptions.ShowAnalyzer)
                    {
                        LogStatus(Localise.GetPhrase("ShowAnalyzer advertisement scan"), jobLog);
                        _commercialScan = new Scanner(_conversionOptions, WorkingVideo, true, duration, _jobStatus, jobLog);
                        if (!_commercialScan.Scan())
                        {
                            _jobStatus.ErrorMsg = Localise.GetPhrase("ShowAnalyzer failed");
                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // If we are using comskip, do it here
                    jobLog.WriteEntry(this, "Checking for Comskip", Log.LogEntryType.Information);
                    if (_conversionOptions.commercialRemoval == CommercialRemovalOptions.Comskip)
                    {
                        LogStatus(Localise.GetPhrase("Comskip advertisement scan"), jobLog);
                        _commercialScan = new Scanner(_conversionOptions, WorkingVideo, false, duration, _jobStatus, jobLog);
                        if (!_commercialScan.Scan())
                        {
                            _jobStatus.ErrorMsg = "Comskip failed";
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // We extract the closed captions after the commerical scanning, trimming and video info is complete
                    // We check if the SRT file alread exists and needs to be adjusted for EDL commercials
                    // If not we check if commerical removal is enabled, if so we need to create a temp file which we will cut to compensate for EDL and THEN extract the CC (this helps keep the audio in sync with the CC due to cutting on non KeyFrames issues)
                    // If no commercial removal we just extract the CC from the remuxed file (TS)
                    if (!_jobStatus.Cancelled)
                    {
                        // CC Extractor only works on TS files or if WTV/DVRMS, we can extract the streams and remux into TS file
                        if (!String.IsNullOrEmpty(_conversionOptions.extractCC) && ((FilePaths.CleanExt(WorkingVideo) == ".ts") || (_conversionOptions.skipRemuxing && ((FilePaths.CleanExt(WorkingVideo) == ".wtv") || (FilePaths.CleanExt(WorkingVideo) == ".dvr-ms")))))
                        {
                            LogStatus(Localise.GetPhrase("Extracting closed captions"), jobLog);

                            // Setup closed captions for extraction
                            _cc = new ClosedCaptions(_conversionOptions.profile, _jobStatus, jobLog);

                            if (!(String.IsNullOrWhiteSpace(_srtFile))) // We already have a SRT file to work with
                            {
                                jobLog.WriteEntry(this, "Found saved SRT file -> " + _srtFile, Log.LogEntryType.Debug);

                                // We need to validate the clean up the SRT file 
                                LogStatus(Localise.GetPhrase("Validating closed captions"), jobLog);
                                if (!_cc.SRTValidateAndClean(_srtFile))
                                {
                                    // Validating CC failed
                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Validating closed captions failed");
                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                    Cleanup(jobLog);
                                    return;
                                }

                                if ((_commercialScan != null) && (!_commercialSkipCut)) // Incase we asked not to cut the video, just create the EDL file, let us not cut the SRT files also
                                {
                                    if (_commercialScan.CommercialsFound) // We just adjust the SRT file with the EDL file
                                    {
                                        LogStatus(Localise.GetPhrase("Trimming closed captions"), jobLog);
                                        if (!_cc.EDLTrim(_commercialScan.EDLFile, _srtFile, _conversionOptions.ccOffset, _subtitleSegmentOffset))
                                        {
                                            // Trimming CC failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Trimming closed captions failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }
                                    }
                                }
                            }
                            else // We need to create a SRT file
                            {
                                string tempCCFile = "";
                                tempCCFile = WorkingVideo; // If there are no commercials to remove, we can work directly on the remuxed file

                                // If we are skipping remuxing for wtv/dvrms files then we need to extract the streams data
                                // If the original video was recoded we have lost the CC data, try to extract the original video stream
                                if ((_workingVideoRecoded || _conversionOptions.skipRemuxing) && ((Util.FilePaths.CleanExt(_conversionOptions.sourceVideo) == ".wtv") || (Util.FilePaths.CleanExt(_conversionOptions.sourceVideo) == ".dvr-ms")))
                                {
                                    ExtractWithGraph extractVideo = null;

                                    try
                                    {
                                        LogStatus(Localise.GetPhrase("Extracting streams"), jobLog);
                                        jobLog.WriteEntry("Creating graph to extract streams", Log.LogEntryType.Debug);
                                        extractVideo = new ExtractWithGraph(_conversionOptions.sourceVideo, _conversionOptions.workingPath, ExtractWithGraph.ExtractMediaType.Video, _jobStatus, jobLog); // We only need video stream to extract CCData using ccExtractor
                                        jobLog.WriteEntry("Building graph", Log.LogEntryType.Debug);
                                        extractVideo.BuildGraph();
                                        jobLog.WriteEntry("Extracting streams", Log.LogEntryType.Debug);
                                        extractVideo.RunGraph();
                                        if ((!extractVideo.SuccessfulExtraction) || (String.IsNullOrWhiteSpace(extractVideo.VideoPart))) // Successful extraction and atleast one video stream
                                        {
                                            jobLog.WriteEntry(this, "Error extracting streams using Graph", Log.LogEntryType.Warning);
                                            throw new Exception("Graph extraction unsuccessful"); // It'll clean up automatically
                                        }

                                        // Dispose the Graph object
                                        try
                                        {
                                            jobLog.WriteEntry("Disposing Graph....", Log.LogEntryType.Debug);
                                            extractVideo.Dispose();
                                        }
                                        catch (Exception e)
                                        {
                                            jobLog.WriteEntry(this, "Error disposing graph.\r\nError : " + e.ToString(), Log.LogEntryType.Warning); // We can still continue
                                        }

                                        // We need to put the stream parts back together in a TS format so that ccExtractor can work on it
                                        // temp file name to be used by ccExtractor
                                        string ccStreamsFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + "-ccExtract.ts");

                                        // Put the streams back together in TS format
                                        jobLog.WriteEntry(this, Localise.GetPhrase("Muxing streams with TsMuxer"), Log.LogEntryType.Information);
                                        LogStatus(Localise.GetPhrase("Muxing streams"), jobLog);
                                        if (!RemuxMCERecording.RemuxRawTSMuxer(_conversionOptions.sourceVideo, ccStreamsFile, extractVideo, _jobStatus, jobLog))
                                        {
                                            jobLog.WriteEntry(this, "TsMuxer Streams Muxing failed", Log.LogEntryType.Error);

                                            // Otherwise try tsMuxer
                                            jobLog.WriteEntry(this, Localise.GetPhrase("Fallback muxing streams with FFMpegParts"), Log.LogEntryType.Information);
                                            LogStatus(Localise.GetPhrase("Fallback muxing streams"), jobLog);
                                            if (!RemuxMCERecording.RemuxRawPartsFFmpeg(_conversionOptions.sourceVideo, ccStreamsFile, extractVideo, _jobStatus, jobLog))
                                            {
                                                extractVideo.DeleteParts(); // Clean up
                                                FileIO.TryFileDelete(ccStreamsFile); // Clean up
                                                jobLog.WriteEntry(this, "Fallback Remux Streams FFMpegParts failed", Log.LogEntryType.Warning);
                                            }
                                        }

                                        extractVideo.DeleteParts(); // Clean up, we have the file now

                                        // All good we should have a video file now
                                        if (FileIO.FileSize(ccStreamsFile) > 0) // We have a good file
                                            tempCCFile = ccStreamsFile; // This is what we will work with now
                                        else
                                        {
                                            FileIO.TryFileDelete(ccStreamsFile); // Clean up
                                            jobLog.WriteEntry("Extracted video stream unsuccessful, continuing without closed captions", Log.LogEntryType.Warning);
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        try
                                        {
                                            jobLog.WriteEntry(this, ("Unable to extract video using DirectShow Graph to get closed captions.\r\nError : " + e.ToString()), Log.LogEntryType.Warning);
                                            if (extractVideo != null)
                                            {
                                                extractVideo.DeleteParts();
                                                extractVideo.Dispose();
                                            }
                                        }
                                        catch (Exception e1)
                                        {
                                            jobLog.WriteEntry(this, "Error disposing directshow graph.\r\nError : " + e1.ToString(), Log.LogEntryType.Warning);
                                        }

                                        // We couldn't extract the video - continue with Working Video - we can still use the remuxed TS file to extract CC possibly
                                    }
                                }

                                // DONT NEED TO ACTUALLY CUT THE VIDEO, RATHER JUST EXTRACT AND ADJUST, GIVES GREATER DEGREE OF CONTROL ON SYNC ISSUES WHEN THE ACTUAL VIDEO IS CUT
                                /*if ((_commercialScan != null) && (!commercialSkipCut)) // If we are not asked to skip cutting commercials (we don't need to adjust CC), for commerical cutting we need to create a special EDL adjusted file before extracting of CC
                                {
                                    jobLog.WriteEntry(this, "Checking if commercials were found", Log.LogEntryType.Information);
                                    if (_commercialScan.CommercialsFound) // If there are commericals we need to create a special EDL cut file to work with before extracting CC
                                    {
                                        // Copy the remuxed file to a temp file which we will then cut using the EDL file and then extract the closed captions
                                        // This helps keep the closed captions in sync with the cut video
                                        tempCCFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + "-CCTemp" + Path.GetExtension(SourceVideo));
                                        try
                                        {
                                            // Create a temp file for CC EDL cutting and extracting
                                            jobLog.WriteEntry(this, "Creating temporary remuxed file for extracting CC and adjusting for commercials", Log.LogEntryType.Debug);
                                            File.Copy(SourceVideo, tempCCFile);
                                        }
                                        catch (Exception e)
                                        {
                                            // Creating temp CC file failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Creating temporary file for CC extracting file failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg + "\r\nError : " + e.ToString(), Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }

                                        // Now adjust the file for commercial removal
                                        jobLog.WriteEntry(this, "Removing commercials from temp file before extracting CC", Log.LogEntryType.Debug);
                                        Remover edlCCAdjust = new Remover(_conversionOptions.profile, tempCCFile, _commercialScan.EDLFile, ref _videoFile, _jobStatus, jobLog); // Pass the original or remuxed video here
                                        edlCCAdjust.StripCommercials();
                                        _videoFile.AdsRemoved = false; // Reset it, since we didn't really remove the ad's, just on temp file for extracting CC's
                                        if (_jobStatus.PercentageComplete == 0) // for Commercial Stripping failure, this numbers is set to 0
                                        {
                                            // Adjusting EDL CC failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Removing commercials from temp file for CC extracting failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }
                                    }
                                }*/

                                // Trimming is already complete so we just need to extract 
                                _deleteSRTFile = true; // We are extracting a SRT file, delete it incase it's along with the original file
                                LogStatus(Localise.GetPhrase("Extracting closed captions"), jobLog);
                                if (!_cc.Extract(tempCCFile, _conversionOptions.workingPath, _conversionOptions.extractCC, 0, 0, _conversionOptions.ccOffset))
                                {
                                    // Extracting CC failed
                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Extracting closed captions failed");
                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                    Cleanup(jobLog);
                                    return;
                                }

                                // We need to validate the clean up the SRT file 
                                LogStatus(Localise.GetPhrase("Validating closed captions"), jobLog);
                                if (!_cc.SRTValidateAndClean(_cc.SRTFile))
                                {
                                    // Validating CC failed
                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Validating closed captions failed");
                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                    Cleanup(jobLog);
                                    return;
                                }

                                if (!String.IsNullOrWhiteSpace(_cc.SRTFile)) // If we have a valid SRT file
                                {
                                    if (String.Compare(tempCCFile, WorkingVideo, true) != 0) // If we created a temp file, lets get rid of it now
                                        Util.FileIO.TryFileDelete(tempCCFile);

                                    _srtFile = _cc.SRTFile; // Save the SRT file location

                                    jobLog.WriteEntry(this, "SRT file -> " + _srtFile, Log.LogEntryType.Debug);
                                }
                            }
                        }
                    }
                }

                // If we are ONLY renaming, we skip the video processing, trimming, removal etc
                if (!_conversionOptions.renameOnly)
                {
                    // Get the video properties
                    if (!_jobStatus.Cancelled)
                    {
                        // Create the Video File object and get the container + stream information
                        LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                        _videoFile = new VideoInfo(true, false, _conversionOptions.sourceVideo, _remuxedVideo, "", _conversionOptions.audioLanguage, _jobStatus, jobLog); // Skip cropping information for now, we'll get that later before conversion
                        if (_videoFile.Error)
                        {
                            _jobStatus.ErrorMsg = "Analyzing video information failed";
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // Run the pre commercial removal custom command from the user if configured
                    if (!_jobStatus.Cancelled)
                    {
                        LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                        CustomCommand customCommand = new CustomCommand("PreCommercialRemovalCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, (_commercialScan == null ? "" : _commercialScan.EDLFile), _srtFile, _metaData.MetaData, _jobStatus, jobLog); // EDL and SRT file may lie with the source video
                        if (!customCommand.Run())
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Pre commercial removal Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Pre commercial removal Custom command failed to run, critical failure");
                            Cleanup(jobLog);
                            return; // serious problem
                        }

                        // Check if the working file has been tampered with
                        if (!File.Exists(WorkingVideo))
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Pre commercial removal working file has been renamed or deleted by custom command") + " -> " + WorkingVideo, Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Pre commercial removal working file has been renamed or deleted by custom command");
                            Cleanup(jobLog);
                            return; // serious problem
                        }
                        else
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished pre commercial removal  custom command, source file size [KB]") + " " + (Util.FileIO.FileSize(_conversionOptions.sourceVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    }

                    // Remove the commercials incase they aren't supported format for after conversion for commercial removal
                    if (!_jobStatus.Cancelled)
                    {
                        if (_commercialScan != null)
                        {
                            // Check if the final conversion extension has a format that's supported by the commercial remover, else remove the commercials here itself during the TS file stage
                            // Check if the profile dicates to remove commercials before the actual conversion
                            if (_preConversionCommercialRemover || (!Remover.IsSupportedExtension(Transcode.Convert.GetConversionExtension(_conversionOptions), _conversionOptions.profile)))
                            {
                                jobLog.WriteEntry(this, "Final format is not a supported format for removing commercials, PRE-Removing commercials for Ext -> " + Transcode.Convert.GetConversionExtension(_conversionOptions), Log.LogEntryType.Information);
                                jobLog.WriteEntry(this, "Checking if commercials were found", Log.LogEntryType.Information);
                                if ((_commercialScan.CommercialsFound) && (!_videoFile.AdsRemoved)) //commercials might be stripped
                                {
                                    if (!_commercialSkipCut)
                                    {
                                        LogStatus(Localise.GetPhrase("Removing commercials"), jobLog);
                                        _commercialRemover = new Remover(_conversionOptions.profile, WorkingVideo, _conversionOptions.workingPath, _commercialScan.EDLFile, _initialEDLSkipSeconds, _videoFile, _jobStatus, jobLog);
                                        _commercialRemover.StripCommercials(true); // we need to select the language while stripping the TS file else we lose the language information

                                        //We dont' check for % completion here since some files are very short and % isn't reliable
                                        if (_videoFile.AdsRemoved) // for Commercial Stripping success, this is true
                                        {
                                            WorkingVideo = _commercialRemover.CommercialFreeVideo; // Since we have a new commercial free video, this will be our new source (remuxed) video
                                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished removing commercials, file size [KB]") + " " + (Util.FileIO.FileSize(WorkingVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                                            
                                            // Cut the SRT file also if it exists
                                            if (!String.IsNullOrWhiteSpace(_srtFile))
                                            {
                                                LogStatus(Localise.GetPhrase("Trimming closed captions"), jobLog);
                                                if (!_cc.EDLTrim(_commercialScan.EDLFile, _srtFile, 0, _subtitleSegmentOffset)) // Offset is already compensated for while extraction
                                                {
                                                    // Trimming CC failed
                                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Trimming closed captions failed");
                                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                                    Cleanup(jobLog);
                                                    return;
                                                }
                                            }

                                            // After removing commercials it's possible that the Audio/Video streams properties have changed - this is due to MEncoder and cutting TS, it only keeps 1 audio stream, so rescan
                                            // Create the Video File object and get the container + stream information
                                            jobLog.WriteEntry(this, "ReAnalyzing video information post commercial removal before video conversion", Log.LogEntryType.Information);
                                            LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                                            // While updating we don't need to pass EDL file anymore since the ad's have been removed and no cropping information here
                                            _videoFile.UpdateVideoInfo(true, false, _conversionOptions.sourceVideo, _remuxedVideo, "", _conversionOptions.audioLanguage, _jobStatus, jobLog);
                                            if (_videoFile.Error)
                                            {
                                                _jobStatus.ErrorMsg = "Analyzing video information failed";
                                                jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                                                Cleanup(jobLog);
                                                return;
                                            }
                                        }
                                        else
                                            jobLog.WriteEntry(this, "Not able to remove commercials, will try again after conversion using unsupported format cutter", Log.LogEntryType.Warning);
                                    }
                                    else
                                        jobLog.WriteEntry(this, Localise.GetPhrase("Skipping commercial cutting, preserving EDL file"), Log.LogEntryType.Information);
                                }
                                else
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Commercials not found or cutting already completed"), Log.LogEntryType.Information);
                            }
                        }
                    }

                    // Get the updated video information
                    if (!_jobStatus.Cancelled)
                    {
                        // Get information before/after removing commercials if required
                        // After removing commercials it's possible that the Audio/Video streams properties have changed - this is due to MEncoder and cutting TS, it only keeps 1 audio stream, so rescan
                        // Create the Video File object and get the container + stream information
                        LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                        // Get the updated video information, skip cropping for now (it will be handled during the conversion)
                        _videoFile.UpdateVideoInfo(true, _autoDeinterlace, _conversionOptions.sourceVideo, _remuxedVideo, (_videoFile.AdsRemoved ? "" : (_commercialScan != null ? _commercialScan.EDLFile : "")), _conversionOptions.audioLanguage, _jobStatus, jobLog); // Check if ad's have not been removed, if we have scanned for commercial pass along the EDL file to speed up the crop detect
                        if (_videoFile.Error)
                        {
                            _jobStatus.ErrorMsg = "Analyzing video information failed";
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // Convert the video
                    if (!_jobStatus.Cancelled)
                    {
                        // Convert the file
                        Transcode.Convert convertFile = new Transcode.Convert(_jobStatus, jobLog);
                        LogStatus(Localise.GetPhrase("Converting"), jobLog);
                        bool res = convertFile.Run(_conversionOptions, _videoFile, _commercialScan, _srtFile); // if we're using MEncoder, then we will complete the commercial stripping here itself
                        if (!res)
                        {
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Conversion failed");
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            // Conversion failed
                            Cleanup(jobLog);
                            return;
                        }

                        // If we burned the subtitles into the file, then delete the SRT file since it's no longer required
                        if (convertFile.SubtitleBurned)
                        {
                            jobLog.WriteEntry(this, "Subtitles were burned into the video while converting, deleting the SRT file", Log.LogEntryType.Information);
                            FileIO.TryFileDelete(_srtFile); // Delete the SRT
                            _srtFile = ""; // Point to nothing
                        }

                        _convertedFile = convertFile.ConvertedFile;
                        jobLog.WriteEntry(this, "Converted File : " + _convertedFile, Log.LogEntryType.Information);
                        jobLog.WriteEntry(this, Localise.GetPhrase("Finished conversion, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    }

                    // Remove the commercials incase they weren't removed earlier
                    if (!_jobStatus.Cancelled)
                    {
                        if (_commercialScan != null)
                        {
                            jobLog.WriteEntry(this, "Checking if commercials were found", Log.LogEntryType.Information);
                            if ((_commercialScan.CommercialsFound) && (!_videoFile.AdsRemoved)) //commercials might be stripped during conversion or before conversion
                            {
                                if (!_commercialSkipCut)
                                {
                                    LogStatus(Localise.GetPhrase("Removing commercials"), jobLog);
                                    _commercialRemover = new Remover(_conversionOptions.profile, _convertedFile, _conversionOptions.workingPath, _commercialScan.EDLFile, _initialEDLSkipSeconds, _videoFile, _jobStatus, jobLog);
                                    _commercialRemover.StripCommercials();

                                    //We dont' check for % completion here since some files are very short and % isn't reliable
                                    if (!_videoFile.AdsRemoved) // Commercial Stripping failure
                                    {
                                        _jobStatus.ErrorMsg = Localise.GetPhrase("Removing commercials failed");
                                        jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                                        Cleanup(jobLog);
                                        return;
                                    }
                                    
                                    _convertedFile = _commercialRemover.CommercialFreeVideo;
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Finished removing commercials, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

                                    // Cut the SRT file also if it exists (since commercials not were not cut earlier, the SRT wasn't cut earlier either
                                    if (!String.IsNullOrWhiteSpace(_srtFile))
                                    {
                                        LogStatus(Localise.GetPhrase("Trimming closed captions"), jobLog);
                                        if (!_cc.EDLTrim(_commercialScan.EDLFile, _srtFile, 0, _subtitleSegmentOffset)) // Offset is already compensated for while extraction
                                        {
                                            // Trimming CC failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Trimming closed captions failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }
                                    }
                                }
                                else
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Skipping commercial cutting, preserving EDL file"), Log.LogEntryType.Information);
                            }
                            else
                                jobLog.WriteEntry(this, Localise.GetPhrase("Commercials not found or cutting already completed"), Log.LogEntryType.Information);
                        }
                    }

                    // Add the subtitles and chapters to the container if asked
                    if (!_jobStatus.Cancelled)
                    {
                        if (_conversionOptions.embedSubtitlesChapters)
                        {
                            string chapFile = ""; // Nero Chapter file
                            string xmlChapFile = ""; // iTunes Chapter file

                            // Convert the EDL file to Chapters (EDLToChapter will adjust chapters based on whether the video is cut with the EDL file or not, i.e. CommercialSkipCut)
                            if (_commercialScan != null)
                            {
                                EDL edl = new EDL(_conversionOptions.profile, _convertedFile, _videoFile.Duration, _commercialScan.EDLFile, _initialEDLSkipSeconds, _jobStatus, jobLog);
                                if (edl.ConvertEDLToChapters(!_commercialSkipCut))
                                {
                                    chapFile = edl.CHAPFile; // Get the Nero chapter file
                                    xmlChapFile = edl.XMLCHAPFile; // Get the iTunes chapter file
                                }
                            }

                            LogStatus(Localise.GetPhrase("Adding subtitles and chapters to file"), jobLog);
                            if (!_metaData.AddSubtitlesAndChaptersToFile(_srtFile, chapFile, xmlChapFile, _convertedFile))
                            {
                                _jobStatus.ErrorMsg = Localise.GetPhrase("Adding subtitles and chapters failed");
                                jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                                Cleanup(jobLog);
                                return;
                            }
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished adding subtitles and chapters to file, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                        }
                    }

                    // This point onward there is not ETA or % so set ETA to working
                    _jobStatus.ETA = "Working...";

                    // Write the meta data
                    if (!_jobStatus.Cancelled)
                    {
                        if (_conversionOptions.writeMetadata)
                        {
                            LogStatus(Localise.GetPhrase("Writing show information"), jobLog);
                            _metaData.WriteTags(_convertedFile); // we can ignore failure of writing meta data, not critical
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished writing tags, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                        }
                    }
                }
                else // If we are ONLY RENAMING, then we are working direcly on the original file (ignore the remuxed video which was used to extract EDL, XML, NFO and SRT files)
                {
                    WorkingVideo = ""; // Disregard the remuxed file completely, we are working on the original video

                    // Get the video properties
                    if (!_jobStatus.Cancelled)
                    {
                        // Create the Video File object and get the container + stream information
                        LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                        _videoFile = new VideoInfo(true, false, _conversionOptions.sourceVideo, "", "", "", _jobStatus, jobLog); // No work done here just get basic properties

                        // Dont' need to worrk about errors/failues here since this is only used if we are extracting MC information
                    }

                    _jobStatus.ETA = "Working...";
                    _convertedFile = _conversionOptions.sourceVideo; // We are working directly on the source file here (copied)
                }

                // Create the XML file with Source video information for WTV and DVRMS file (XBMC compliant NFO file, http://wiki.xbmc.org/index.php?title=Import_-_Export_Library)
                if (!_jobStatus.Cancelled)
                {
                    if (_conversionOptions.extractXML)
                        _metaData.WriteXBMCXMLTags(OriginalFileName, WorkingVideo, _conversionOptions.workingPath, _videoFile);
                }

                // Processing complete, now rename the file based upon meta data
                string subDestinationPath = "";
                if (!_jobStatus.Cancelled)
                {
                    // Before renaming get the MediaInformation once to dump into the Log File for debugging purposes.
                    FFmpegMediaInfo.DumpFileInformation(_convertedFile, _jobStatus, jobLog);

                    LogStatus(Localise.GetPhrase("Renaming file using show information"), jobLog);
                    // Check if we are using a Unicode Temp file name and restore it before renaming it
                    if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                    {
                        try
                        {
                            string orgConvFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(OriginalFileName) + FilePaths.CleanExt(_convertedFile));
                            jobLog.WriteEntry(this, "Restoring temporary Unicode file to original filename -> " + orgConvFile, Log.LogEntryType.Debug);
                            FileIO.TryFileDelete(orgConvFile);
                            File.Move(_convertedFile, orgConvFile); // Restore original filename
                            _convertedFile = orgConvFile; // Restore the name
                        }
                        catch (Exception e)
                        {
                            jobLog.WriteEntry(this, "Unable to rename temporary Unicode file to original filename.\r\nError -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    RenameConvertedFile(out subDestinationPath, jobLog); // Rename the file
                }

                // Now run the post conversion custom command on the final file before it is moved
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                    CustomCommand customCommand = new CustomCommand("CustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, (_commercialScan == null ? "" : _commercialScan.EDLFile), _srtFile, _metaData.MetaData, _jobStatus, jobLog);
                    if (!customCommand.Run())
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = Localise.GetPhrase("Custom command failed to run, critical failure");
                        Cleanup(jobLog);
                        return; // serious problem
                    }

                    // Check if the converted file has been tampered with
                    if (!File.Exists(_convertedFile))
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Converted file has been renamed or deleted by custom command") + " -> " + _convertedFile, Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = Localise.GetPhrase("Converted file has been renamed or deleted by custom command");
                        Cleanup(jobLog);
                        return; // serious problem
                    }
                    else
                        jobLog.WriteEntry(this, Localise.GetPhrase("Finished custom command, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                }

                // Finally move the file
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Moving converted file to destination"), jobLog);
                    if (!MoveConvertedFile(subDestinationPath, jobLog))
                    {
                        _jobStatus.ErrorMsg = Localise.GetPhrase("Moving converted file to destination failed"); // don't set this unless you want to indicate failure up the chain and kill the conversion process
                        jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                        Cleanup(jobLog);
                        return;
                    }
                }

                // After moving, now add the destination file to the iTunes/WMP library if required
                if (!_jobStatus.Cancelled)
                {
                    if (_conversionOptions.addToiTunes)
                    {
                        LogStatus(Localise.GetPhrase("Adding file to the iTunes library"), jobLog);
                        VideoMetaData.AddFileToiTunesLibrary(_convertedFile, jobLog);
                    }

                    if (_conversionOptions.addToWMP)
                    {
                        LogStatus(Localise.GetPhrase("Adding file to the WMP library"), jobLog);
                        VideoMetaData.AddFileToWMPLibrary(_convertedFile, jobLog);
                    }
                }

                // Finally - Move the remaining files file
                if (!_jobStatus.Cancelled)
                {
                    // XML FILE (generated by Comskip or any other program)
                    string xmlFile = Path.Combine(_conversionOptions.workingPath, (Path.GetFileNameWithoutExtension(WorkingVideo) + ".xml")); // XML file created by 3rd Party in temp working directory
                    try
                    {
                        if (File.Exists(xmlFile))
                        {
                            if (String.Compare(xmlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".xml"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                            {
                                jobLog.WriteEntry(this, Localise.GetPhrase("Found XML file, moving to destination") + " XML:" + xmlFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".xml"));
                                File.Move(xmlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".xml")); //rename to match destination file
                            }
                        }
                    }
                    catch (Exception e) // Not critial to be unable to move SRT File
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move XML file to destination") + " XML:" + xmlFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                    }

                    // SRT FILE
                    try
                    {
                        if (File.Exists(_srtFile))
                        {
                            if (String.Compare(_srtFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                            {
                                jobLog.WriteEntry(this, Localise.GetPhrase("Found SRT file, moving to destination") + " SRT:" + _srtFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt"));
                                File.Move(_srtFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt")); //rename to match destination file
                            }
                        }
                    }
                    catch (Exception e) // Not critial to be unable to move SRT File
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move SRT file to destination") + " SRT:" + _srtFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                    }

                    // EDL FILE
                    if (_commercialScan != null)
                    {
                        try
                        {
                            if (File.Exists(_commercialScan.EDLFile) && _commercialSkipCut) // if we are asked to keep EDL file, we copy it out to output
                            {
                                if (String.Compare(_commercialScan.EDLFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"), true) != 0) // don't delete the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found EDL file, request to move to destination") + " EDL:" + _commercialScan.EDLFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"));
                                    File.Move(_commercialScan.EDLFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"));
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move EDL file to destination") + " EDL:" + _commercialScan.EDLFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }
                    else if (_saveEDLFile) // no commercial scan but we still found an EDL file with the source, we copy it to the output
                    {
                        // EDL File
                        string edlFile = Path.Combine(_conversionOptions.workingPath, (Path.GetFileNameWithoutExtension(WorkingVideo) + ".edl")); // Saved EDL file
                        try
                        {
                            if (File.Exists(edlFile))
                            {
                                if (String.Compare(edlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found EDL file, moving to destination") + " EDL:" + edlFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"));
                                    File.Move(edlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move EDL File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move EDL file to destination") + " EDL:" + edlFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }

                        //EDLP File
                        string edlpFile = Path.Combine(_conversionOptions.workingPath, (Path.GetFileNameWithoutExtension(WorkingVideo) + ".edlp")); // Saved EDLP file
                        try
                        {
                            if (File.Exists(edlpFile))
                            {
                                if (String.Compare(edlpFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edlp"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found EDLP file, moving to destination") + " EDLP:" + edlpFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edlp"));
                                    File.Move(edlpFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edlp")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move EDL File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move EDLP file to destination") + " EDLP:" + edlpFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    // NFO FILE
                    string nfoFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + ".nfo"); // Path\FileName.nfo
                    try
                    {
                        if (File.Exists(nfoFile))
                        {
                            if (String.Compare(nfoFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".nfo"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                            {
                                jobLog.WriteEntry(this, Localise.GetPhrase("Found NFO file, moving to destination") + " NFO:" + nfoFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".nfo"));
                                File.Move(nfoFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".nfo")); //rename to match destination file
                            }
                        }
                    }
                    catch (Exception e) // Not critial to be unable to move NFO File
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move NFO file to destination") + " NFO:" + nfoFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                    }

                    // COVER ART
                    if (_conversionOptions.extractXML) // Only if asked to save the information
                    {
                        string coverArt = _metaData.MetaData.BannerFile; // Path to cover art
                        try
                        {
                            if (File.Exists(coverArt))
                            {
                                if (String.Compare(coverArt, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + FilePaths.CleanExt(coverArt)), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, ("Found Cover Art file, copying to destination") + " CoverArt:" + coverArt + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + FilePaths.CleanExt(coverArt)));
                                    File.Copy(coverArt, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + FilePaths.CleanExt(coverArt))); // Copy (NOT move since it may be used by other conversions and we don't keep downloading cover art) and rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move Cover Art File
                        {
                            jobLog.WriteEntry(this, ("Unable to copy Cover Art file to destination") + " CoverArt:" + coverArt + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }

                    }

                    // LOG FILE
                    if (_copyLOGFile)
                    {
                        string logFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + ".log"); // Path\FileName.log
                        try
                        {
                            if (File.Exists(logFile))
                            {
                                if (String.Compare(logFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".log"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found LOG file, moving to destination") + " LOG:" + logFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".log"));
                                    File.Move(logFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".log")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move LOG File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move LOG file to destination") + " LOG:" + logFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    // PROPERTIES FILE
                    if (_copyPropertiesFile)
                    {
                        string propertiesFile = FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".properties"; // The properties file lies with the original file
                        try
                        {
                            if (File.Exists(propertiesFile))
                            {
                                if (String.Compare(propertiesFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".properties"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found SageTV Properties file, moving to destination") + " PROPERTIES:" + propertiesFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".properties"));
                                    File.Move(propertiesFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".properties")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move LOG File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move LOG file to destination") + " PROPERTIES:" + propertiesFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    // Last things - Run the end of conversion post rename and move custom command from the user if configured
                    if (!_jobStatus.Cancelled)
                    {
                        LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                        string tmpSrt, tmpEdl;
                        tmpEdl = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl") ? Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl" : "");
                        tmpSrt = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt") ? Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt" : "");
                        CustomCommand customCommand = new CustomCommand("PostCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, tmpEdl, tmpSrt, _metaData.MetaData, _jobStatus, jobLog);
                        if (!customCommand.Run())
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("End of Conversion Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = Localise.GetPhrase("End of Conversion Custom command failed to run, critical failure");
                            Cleanup(jobLog);
                            return; // serious problem
                        }

                        // Check if the converted file has been tampered with
                        if (!File.Exists(_convertedFile))
                        {
                            // WE WILL NOTE THIS AS AN SERIOUS ERROR BUT IT ISN'T A DEALBREAKER SINCE WE ARE ALL DONE HERE AND THE FILE HAS BEEN MOVED.
                            jobLog.WriteEntry(this, Localise.GetPhrase("Destination file has been renamed or deleted by post custom command") + " -> " + _convertedFile, Log.LogEntryType.Error);
                        }
                        else
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished end of conversion custom command, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    }

                    _jobStatus.ErrorMsg = ""; // all done, we are in the clear, success
                    _jobStatus.SuccessfulConversion = true; //now the original file can be deleted if required
                    LogStatus(Localise.GetPhrase("Success - All done!"), jobLog);
                }

                Cleanup(jobLog); // all done, clean it up, close jobLog and mark it inactive
            }
            catch(ThreadAbortException)
            {
                //incase the conversion thread is stopped due to the GUI stopping/cancelling, then release the logfile locks
                _jobStatus.ErrorMsg = "Conversion thread aborted, conversion cancelled";
                jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                Cleanup(jobLog);
            }
            catch (Exception e)
            {
                // Unhandled error, clean up and log error
                _jobStatus.ErrorMsg = "Unhanded error during conversion, conversion cancelled";
                jobLog.WriteEntry(this, _jobStatus.ErrorMsg + "\r\n" + e.ToString(), Log.LogEntryType.Error);
                Cleanup(jobLog);
            }
        }