Example #1
0
        /// <summary>
        /// Check if a file is in the queue, if not add it to the queue.
        /// This function does NOT take a lock on the queue before modifying it. This function is not thread safe
        /// </summary>
        /// <param name="filePath">Filename to check and add</param>
        /// <param name="monitorTask">Name of the monitor task adding the file, blank if manually added</param>
        /// <param name="manualFile">True is it's added manually to the queue</param>
        private void CheckAndAddFile(string filePath, MonitorJobOptions monitorTask, bool manualFile)
        {
            Ini iniHistory = new Ini(GlobalDefs.HistoryFile);
            if (!QueueContains(filePath))
            {
                if (!Util.FileIO.FileLocked(filePath))
                {
                    string historyRec = iniHistory.ReadString(filePath, "Status", "");

                    // If it's an output file (you can skip output file reconversion by setting SkipReconversion in Conversion Task Settings)
                    if ((monitorTask != null) && monitorTask.monitorConvertedFiles)
                        if (Core.ConvertedFileStatuses.Any(x => x.Equals(historyRec)))
                            historyRec = ""; // This is a converted file and we have been asked to process converted files also

                    // Are we asked to reconvert recorded files
                    if ((monitorTask != null) && monitorTask.reMonitorRecordedFiles)
                        if (Core.SourceFileStatuses.Any(x => x.Equals(historyRec))) // Check if the status matches any of the source file status
                            historyRec = "";

                    if (historyRec == "" || manualFile) // Either the file has not been processed (Status is missing) or readded manually (Status is blank)
                    {
                        if (File.Exists(filePath))
                        {
                            if (manualFile)
                            {
                                AddJobs(filePath, monitorTask, true); //add manual jobs for conversion at the head of queue (after the last currently active job)
                            }
                            else
                            {
                                // Check to see if the age of the file is old enough
                                DateTime timeStamp = Util.FileIO.GetFileModifiedTime(filePath);
                                if (timeStamp.AddHours(_minimumAge) < DateTime.Now)
                                {
                                    AddJobs(filePath, monitorTask, false); // Added by a monitor task
                                }
                            }
                        }
                        else if (manualFile) //delete only if a manual entry is made
                        {
                            Log.AppLog.WriteEntry(this, "Unable to queue file for conversion - file not found " + filePath + "\r\n", Log.LogEntryType.Warning);
                            Ini iniManualQueue = new Ini(GlobalDefs.ManualQueueFile);
                            iniManualQueue.DeleteKey("ManualQueue", filePath);
                        }
                    }
                    else
                    {
                        if (!_processedFiles.Contains(filePath)) // Keep a track of processed file so we don't overload the mcebuddy.log file
                        {
                            _processedFiles.Add(filePath); // to the list of processed file so we don't end up doing it again
                            Log.AppLog.WriteEntry(this, "File " + filePath + " already converted with status " + historyRec + "\r\n", Log.LogEntryType.Debug);
                        }

                        // Manual file entry may have been readded multiple times, each time we log it and remove it from the ini file
                        if (manualFile) // Delete the manual entry
                        {
                            Log.AppLog.WriteEntry(this, "Manual file " + filePath + " already converted with status " + historyRec + "\r\n", Log.LogEntryType.Debug);
                            Ini iniManualQueue = new Ini(GlobalDefs.ManualQueueFile);
                            iniManualQueue.DeleteKey("ManualQueue", filePath);
                        }
                    }
                }
                else
                {
                    if (!_processedFiles.Contains(filePath)) // Keep a track of processed file so we don't overload the mcebuddy.log file
                    {
                        _processedFiles.Add(filePath); // to the list of processed file so we don't end up doing it again
                        Log.AppLog.WriteEntry(this, "Unable to queue file for conversion - file inaccessible/locked by another process " + filePath + "\r\n", Log.LogEntryType.Debug);
                    }

                    // Manual file entry may have been readded multiple times, each time we log it and remove it from the ini file
                    if (manualFile) // Delete the manual entry
                    {
                        Ini iniManualQueue = new Ini(GlobalDefs.ManualQueueFile);
                        iniManualQueue.DeleteKey("ManualQueue", filePath);
                        Log.AppLog.WriteEntry(this, "Unable to queue manual file for conversion - file inaccessible/locked by another process " + filePath + "\r\n", Log.LogEntryType.Debug);
                    }
                }
            }
        }
Example #2
0
        public void CancelJob(int[] jobList)
        {
            if (jobList == null)
            {
                Log.AppLog.WriteEntry(this, "Empty job list passed to cancel jobs", Log.LogEntryType.Error, true);
                return;
            }

            Monitor.Enter(monitorLock); // Need to ensure when this function is called, the Engine is not in Starting or stopping mode
            Monitor.Enter(_queueManager.Queue);

            try
            {
                List<ConversionJob> convJob = new List<ConversionJob>();
                for (int i = 0; i < jobList.Length; i++)
                {
                    //Build the list of conversion jobs to cancel, don't use job numbers since they will change once removed from the queue
                    if ((jobList[i] >= 0) && (jobList[i] < _queueManager.Queue.Count))
                        convJob.Add(_queueManager.Queue[jobList[i]]);
                }

                foreach (ConversionJob cj in convJob)
                {
                    if (cj != null)
                    {
                        if (cj.Active) // if job is active, remove from manual queue and mark it cancelled for monitor thread to process
                        {
                            Log.AppLog.WriteEntry(this, Localise.GetPhrase("Active job signalled cancellation for") + " " + cj.OriginalFileName, Log.LogEntryType.Information, true);
                            cj.Status.Cancelled = true;
                        }
                        else
                        {
                            Log.AppLog.WriteEntry(this, Localise.GetPhrase("Inactive job cancelled") + " " + cj.OriginalFileName, Log.LogEntryType.Information, true);

                            cj.Status.Cancelled = true; // mark the job as cancelled (for write history and tracking)

                            // First delete the job from the manual file entry while the ref is active, if this is the last task for the job
                            if (_queueManager.JobCount(cj.OriginalFileName) <= 1)
                            {
                                Ini iniManualQueue = new Ini(GlobalDefs.ManualQueueFile);
                                iniManualQueue.DeleteKey("ManualQueue", cj.Status.SourceFile);
                            }

                            WriteHistory(cj); //  Write to history file (it will check for last task for job)

                            _queueManager.Queue.Remove(cj); // now remove the job from the queue
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Log.AppLog.WriteEntry(this, "Unable to cancel jobs. JobList = " + string.Join(",", jobList.Select(x => x.ToString()).ToArray()) + " MaxJobs = " + _maxConcurrentJobs.ToString() + " Conversion Jobs Initialized = " + _conversionJobs.Length.ToString(), Log.LogEntryType.Error, true);
                Log.AppLog.WriteEntry(this, "Error -> " + e.ToString(), Log.LogEntryType.Error, true);
            }

            Monitor.Exit(_queueManager.Queue);
            Monitor.Exit(monitorLock); // Need to ensure when this function is called, the Engine is not in Starting or stopping mode
        }
Example #3
0
        /// <summary>
        /// Adds a conversion job to the queue
        /// This function does NOT take a lock on the queue before modifying it. This function is not thread safe
        /// </summary>
        /// <param name="filePath">File to add</param>
        /// <param name="monitorTask">Monitor task name which found the file</param>
        /// <param name="manual">True if this is a manuall added entry, false if it's added by a monitor task</param>
        private void AddJobs(string filePath, MonitorJobOptions monitorTask, bool manual)
        {
            bool filterMatchSuccessful = false;

            // Check if the file has already been processed in the past if so skip
            if (_conversionTaskFilterMismatchFiles.Contains(filePath))
                return; //

            foreach (ConversionJobOptions conversionTask in MCEBuddyConf.GlobalMCEConfig.AllConversionTasks)
            {
                // Check if the task is disabled, which case skip
                if (!conversionTask.enabled)
                {
                    Log.AppLog.WriteEntry(this, Localise.GetPhrase("Conversion Task") + " " + conversionTask.taskName + " " + Localise.GetPhrase("disabled, skipping file") + " " + filePath, Log.LogEntryType.Debug, true);
                    continue;
                }

                conversionTask.sourceVideo = filePath;

                // Monitor Task name matching if not empty
                if ((monitorTask != null) && !String.IsNullOrWhiteSpace(monitorTask.taskName) && (conversionTask.monitorTaskNames != null))
                {
                    bool foundMatch = false;
                    foreach (string matchMonitorTaskName in conversionTask.monitorTaskNames)
                    {
                        if (monitorTask.taskName.ToLower().Trim() != matchMonitorTaskName.ToLower().Trim()) // match the list of a name
                            continue; // move onto next monitor task name
                        else
                        {
                            foundMatch = true;
                            break;
                        }
                    }

                    if (!foundMatch)
                    {
                        Log.AppLog.WriteEntry(this, "Skipping Conversion task " + conversionTask.taskName + " for file " + filePath + " since Monitor task " + monitorTask.taskName + " does not match the list of monitor tasks in the conversion task.", Log.LogEntryType.Debug, true);
                        continue; // move into next conversion task
                    }
                }

                // Metadata extract and pattern match from conversion task
                VideoMetaData metaData = MetadataMatchFilters(conversionTask);
                if (metaData != null) // We got a match - process the file
                {
                    // Calculate where to add the job in the queue
                    int idx;
                    if (manual || conversionTask.insertQueueTop) // Manual jobs to the head of the queue, just after the current active job, or check if the conversion task is asking to add the job at the head of the queue
                    {
                        for (idx = 0; idx < _jobQueue.Count; idx++)
                        {
                            if (!_jobQueue[idx].Active)
                                break;
                        }
                    }
                    else
                        idx = _jobQueue.Count; // Add the job to the end of the active queue

                    // If it's a manual entry then we need to convert, reset the skip reconverting flag if it's set
                    if (manual && conversionTask.skipReprocessing)
                    {
                        conversionTask.skipReprocessing = conversionTask.checkReprocessingHistory = false; // We can make a direct change since this is a copy object
                        Log.AppLog.WriteEntry(this, "Manually added file, resetting the skip reprocessing option, the file will converted even if it has been processed before", Log.LogEntryType.Debug, true);
                    }

                    Log.AppLog.WriteEntry(this, Localise.GetPhrase("Added new job to queue for") + " " + filePath, Log.LogEntryType.Information, true);
                    ConversionJob job = new ConversionJob(conversionTask, metaData);
                    _jobQueue.Insert(idx, job);
                    filterMatchSuccessful = true; // we cleared filters and processed the file

                    // Send an eMail if required
                    GeneralOptions go = MCEBuddyConf.GlobalMCEConfig.GeneralOptions;
                    bool sendEMail = go.sendEmail; // do we need to send an eMail after adding a job to the queue
                    bool sendQueue = go.eMailSettings.queueEvent;
                    string sendQueueSubject = go.eMailSettings.queueSubject;
                    bool skipBody = go.eMailSettings.skipBody;
                    if (sendEMail && sendQueue)
                    {
                        string subject = Localise.GetPhrase("MCEBuddy added a video conversion to the queue");
                        string message = Localise.GetPhrase("Source Video") + " -> " + job.OriginalFileName + "\r\n";
                        message += Localise.GetPhrase("Profile") + " -> " + job.Profile + "\r\n";
                        message += Localise.GetPhrase("Conversion Task") + " -> " + job.TaskName + "\r\n";

                        // Check for custom subject and process
                        if (!String.IsNullOrWhiteSpace(sendQueueSubject))
                            subject = UserCustomParams.CustomParamsReplace(sendQueueSubject, job.WorkingPath, "", "", job.OriginalFileName, "", "", "", job.Profile, job.TaskName, metaData.MetaData, Log.AppLog);

                        eMailSendEngine.AddEmailToSendQueue(subject, (skipBody ? "" : message)); // Send the eMail through the eMail engine
                    }
                }
                else if (manual) // Delete manual file entry if we didn't create a conversion job, otherwise the engine will clear it on completion
                {
                    // Manual files may be added multiple times and the filter may already have it from the last time, so be safe and delete it
                    Ini iniManualQueue = new Ini(GlobalDefs.ManualQueueFile); // Delete the entry from the manual queue
                    iniManualQueue.DeleteKey("ManualQueue", filePath);
                }
            }

            // If we have been through all the conversion tasks and not a single task has processed this file, hence we add it to the filter mismatch list so it won't be processed in future (MetadataExtract is very intensive and also prevent overburdening the log file)
            if (!filterMatchSuccessful)
                _conversionTaskFilterMismatchFiles.Add(filePath);
        }
Example #4
0
        /// <summary>
        /// Main CORE engine thread of MCEBuddy which runs in the background to check for new jobs, scans for new files, checks for updates to MCEBuddy etc
        /// </summary>
        private void MonitorThread() // Background thread that check for job starting, additions and completion
        {
            Thread updateCheckThread = null, scanThread = null, syncThread = null;

            try
            {
                DateTime lastUpdateCheck = DateTime.Now.AddDays(-1);
                DateTime lastPollCheck = DateTime.Now.AddSeconds(-_pollPeriod);
                GlobalDefs.Shutdown = false;
                while (!GlobalDefs.Shutdown)
                {
                    // Check for updated version and messages
                    if (DateTime.Now > lastUpdateCheck.AddHours(GlobalDefs.random.Next(12, 25)))
                    {
                        Log.AppLog.WriteEntry(this, Localise.GetPhrase("Checking for new version of MCEBuddy"), Log.LogEntryType.Information);
                        lastUpdateCheck = DateTime.Now;

                        if ((updateCheckThread != null) && updateCheckThread.IsAlive)
                            continue; // Don't start another one yet
                        updateCheckThread = new Thread(new ThreadStart(_updateCheck.Check));
                        updateCheckThread.SetApartmentState(ApartmentState.STA);
                        updateCheckThread.CurrentCulture = updateCheckThread.CurrentUICulture = Localise.MCEBuddyCulture;
                        updateCheckThread.IsBackground = true; // Kill the thread if the process exits
                        updateCheckThread.Start();
                    }

                    //Check for new files and clean up log files
                    if ((DateTime.Now > lastPollCheck.AddSeconds(_pollPeriod)) || (_rescan))
                    {
                        _rescan = false;
                        lastPollCheck = DateTime.Now;

                        // Check for new files in the monitor folders - run as thread to keep it responsive
                        if ((scanThread != null) && scanThread.IsAlive)
                            continue; // Don't start another one yet
                        scanThread = new Thread(() => _queueManager.ScanForFiles());
                        scanThread.CurrentCulture = scanThread.CurrentUICulture = Localise.MCEBuddyCulture;
                        scanThread.IsBackground = true; // Kill the thread if the process exits
                        scanThread.Start();

                        // check if converted files need to kept in sync with source files (deleted) - run as thread to keep it responsive
                        if (MCEBuddyConf.GlobalMCEConfig.GeneralOptions.deleteConverted)
                        {
                            if ((syncThread != null) && syncThread.IsAlive)
                                continue; // Don't start another one yet
                            syncThread = new Thread(() => _queueManager.SyncConvertedFiles(_useRecycleBin)); // Check for new files in the monitor folders
                            syncThread.CurrentCulture = syncThread.CurrentUICulture = Localise.MCEBuddyCulture;
                            syncThread.IsBackground = true; // Kill the thread if the process exits
                            syncThread.Start();
                        }

                        // check for log files clean up
                        CleanLogFiles();                        
                    }

                    // Resume jobs if we are within the conversion time
                    if (CheckIfWithinConversionTime()) // Call the internal function, cannot take a lock
                    {
                        // We are within the conversion time, if the engine has not auto resumed, then resume it
                        if (_autoPause) // If we are auto paused, then resume it since we are within the conversion time
                            _autoPause = GlobalDefs.Pause = false; // Resume it
                    }
                    else
                    {
                        // We are outside the conversion time, if the engine has not auto paused, then pause it
                        if (!_autoPause) // If we are not paused, then pause it since we are outside the conversion time
                            _autoPause = GlobalDefs.Pause = true; // Pause it
                    }

                    //Check the status of the jobs and start new ones 
                    bool SomeRunning = false;
                    for (int i = 0; i < _conversionJobs.Length; i++)
                    {
                        if (_conversionJobs[i] != null)
                        {
                            // Check for running and clean up completed jobs
                            if (_conversionJobs[i].Completed)
                            {
                                Monitor.Enter(_queueManager.Queue); //the lock is always on the jobQueue

                                Log.AppLog.WriteEntry(this, Localise.GetPhrase("Job for") + " " + _conversionJobs[i].OriginalFileName + " " + Localise.GetPhrase("completed"), Log.LogEntryType.Information, true);

                                // Now delete the job from the manual file entry, if it has succeeded great, if it has failed we don't want an endless loop of conversion
                                if ((_queueManager.JobCount(_conversionJobs[i].OriginalFileName) <= 1)) // Whether it is cancelled of finished delete from the manual queue either way
                                {
                                    Ini iniManualQueue = new Ini(GlobalDefs.ManualQueueFile);
                                    iniManualQueue.DeleteKey("ManualQueue", _conversionJobs[i].OriginalFileName);
                                }

                                if ((_archiveOriginal) && (_conversionJobs[i].Status.SuccessfulConversion) && (_queueManager.JobCount(_conversionJobs[i].OriginalFileName) <= 1) && (String.Compare(_conversionJobs[i].OriginalFileName, _conversionJobs[i].ConvertedFile, true) != 0))
                                {
                                    string pathName = Path.GetDirectoryName(_conversionJobs[i].OriginalFileName); //get the directory name
                                    if (!pathName.ToLower().Contains((string.IsNullOrEmpty(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.archivePath) ? GlobalDefs.MCEBUDDY_ARCHIVE.ToLower() : MCEBuddyConf.GlobalMCEConfig.GeneralOptions.archivePath.ToLower()))) //check if we are currently operating from the archive folder (manual queue), in which case don't archive
                                    {
                                        string archivePath = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.archivePath; // use the specified archive path

                                        if (archivePath == "") // Default archive location to be used
                                            archivePath = Path.Combine(pathName, GlobalDefs.MCEBUDDY_ARCHIVE); //update the path name for a new sub-directory called Archive

                                        Util.FilePaths.CreateDir(archivePath); //create the sub-directory if required
                                        string newFilePath = Path.Combine(archivePath, Path.GetFileName(_conversionJobs[i].OriginalFileName));

                                        Log.AppLog.WriteEntry(this, "Archiving original file " + _conversionJobs[i].OriginalFileName + " to Archive folder " + archivePath, Log.LogEntryType.Debug);

                                        try
                                        {
                                            // Archive the EDL, SRT, XML, NFO etc files also along with the original file if present
                                            foreach (string supportFileExt in GlobalDefs.supportFilesExt)
                                            {
                                                string extFile = Path.Combine(Path.GetDirectoryName(_conversionJobs[i].OriginalFileName), Path.GetFileNameWithoutExtension(_conversionJobs[i].OriginalFileName) + supportFileExt); // Saved support file
                                                if (File.Exists(extFile))
                                                    File.Move(extFile, Path.Combine(archivePath, Path.GetFileName(extFile)));
                                            }

                                            // Last file to move
                                            File.Move(_conversionJobs[i].OriginalFileName, newFilePath); //move the file into the archive folder
                                        }
                                        catch (Exception e)
                                        {
                                            Log.AppLog.WriteEntry(this, "Unable to move original file " + _conversionJobs[i].OriginalFileName + " to Archive folder " + archivePath, Log.LogEntryType.Error);
                                            Log.AppLog.WriteEntry(this, "Error : " + e.ToString(), Log.LogEntryType.Error);
                                        }
                                    }
                                }
                                // Delete/archive only if the conversion was successful and original marked for deletion and it is the last task for the job and the original file and converted file don't have the same name+path (since it's been replaced already)
                                else if ((_deleteOriginal) && (_conversionJobs[i].Status.SuccessfulConversion) && (_queueManager.JobCount(_conversionJobs[i].OriginalFileName) <= 1) && (String.Compare(_conversionJobs[i].OriginalFileName, _conversionJobs[i].ConvertedFile, true) != 0))
                                {
                                    // Delete the EDL, SRT, XML, NFO etc files also along with the original file if present
                                    foreach (string supportFileExt in GlobalDefs.supportFilesExt)
                                    {
                                        string extFile = Path.Combine(Path.GetDirectoryName(_conversionJobs[i].OriginalFileName), Path.GetFileNameWithoutExtension(_conversionJobs[i].OriginalFileName) + supportFileExt); // support file
                                        Util.FileIO.TryFileDelete(extFile, _useRecycleBin); // Delete support file
                                    }

                                    Util.FileIO.TryFileDelete(_conversionJobs[i].OriginalFileName, _useRecycleBin); // delete original file
                                    Log.AppLog.WriteEntry(this, "Deleting original file " + _conversionJobs[i].OriginalFileName, Log.LogEntryType.Debug, true);
                                }
                                // Check of it's a failure and the original file needs to be moved
                                if ((_failedMoveOriginal) && (_conversionJobs[i].Status.Error) && (_queueManager.JobCount(_conversionJobs[i].OriginalFileName) <= 1))
                                {
                                    string pathName = Path.GetDirectoryName(_conversionJobs[i].OriginalFileName); //get the directory name
                                    string failedPath = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.failedPath; // use the specified path to move the file conversion original file

                                    Util.FilePaths.CreateDir(failedPath); //create the sub-directory if required
                                    string newFilePath = Path.Combine(failedPath, Path.GetFileName(_conversionJobs[i].OriginalFileName));

                                    Log.AppLog.WriteEntry(this, "Moving original file " + _conversionJobs[i].OriginalFileName + " to Failed conversion folder " + failedPath, Log.LogEntryType.Debug);

                                    try
                                    {
                                        File.Move(_conversionJobs[i].OriginalFileName, newFilePath); //move the file into the archive folder
                                    }
                                    catch (Exception e)
                                    {
                                        Log.AppLog.WriteEntry(this, "Unable to move original file " + _conversionJobs[i].OriginalFileName + " to Failed conversion folder " + failedPath, Log.LogEntryType.Error);
                                        Log.AppLog.WriteEntry(this, "Error : " + e.ToString(), Log.LogEntryType.Error);
                                    }
                                }

                                //First write the history and finish the job, then remove from the queue
                                WriteHistory(_conversionJobs[i]);

                                _queueManager.Queue.Remove(_conversionJobs[i]);
                                _conversionJobs[i] = null;

                                Monitor.Exit(_queueManager.Queue);
                            }
                            else
                            {
                                SomeRunning = true;
                            }
                        }
                        else
                        {
                            // Start new jobs if conversions are not paused
                            if (!GlobalDefs.Pause)
                            {
                                Monitor.Enter(_queueManager.Queue);
                                for (int j = 0; j < _queueManager.Queue.Count; j++)
                                {
                                    if ((!_queueManager.Queue[j].Completed) && (!_queueManager.Queue[j].Active))
                                    {
                                        _conversionJobs[i] = _queueManager.Queue[j];
                                        
                                        // Checking for a custom temp working path for conversion (other drives/locations)
                                        if (String.IsNullOrWhiteSpace(_conversionJobs[i].WorkingPath)) // Local temp folder takes precedence
                                            _conversionJobs[i].WorkingPath = Path.Combine((String.IsNullOrEmpty(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.tempWorkingPath) ? GlobalDefs.AppPath : MCEBuddyConf.GlobalMCEConfig.GeneralOptions.tempWorkingPath), "working" + i.ToString(System.Globalization.CultureInfo.InvariantCulture));
                                        
                                        // Start the conversion
                                        _conversionJobs[i].StartConversionThread();

                                        SomeRunning = true; // Update status

                                        // Send an eMail if required
                                        GeneralOptions go = MCEBuddyConf.GlobalMCEConfig.GeneralOptions;
                                        bool sendEMail = go.sendEmail; // do we need to send an eMail after each job
                                        bool sendStart = go.eMailSettings.startEvent;
                                        string sendStartSubject = go.eMailSettings.startSubject;
                                        bool skipBody = go.eMailSettings.skipBody;
                                        if (sendEMail && sendStart)
                                        {
                                            string subject = Localise.GetPhrase("MCEBuddy started a video conversion");
                                            string message = Localise.GetPhrase("Source Video") + " -> " + _conversionJobs[i].OriginalFileName + "\r\n";
                                            message += Localise.GetPhrase("Profile") + " -> " + _conversionJobs[i].Profile + "\r\n";
                                            message += Localise.GetPhrase("Conversion Task") + " -> " + _conversionJobs[i].TaskName + "\r\n";
                                            message += Localise.GetPhrase("Conversion Started At") + " -> " + DateTime.Now.ToString("s", System.Globalization.CultureInfo.InvariantCulture) + "\r\n";

                                            // Check for custom subject and process
                                            if (!String.IsNullOrWhiteSpace(sendStartSubject))
                                                subject = UserCustomParams.CustomParamsReplace(sendStartSubject, _conversionJobs[i].WorkingPath, "", _conversionJobs[i].ConvertedFile, _conversionJobs[i].OriginalFileName, "", "", "", _conversionJobs[i].Profile, _conversionJobs[i].TaskName, _conversionJobs[i].MetaData, Log.AppLog);

                                            eMailSendEngine.AddEmailToSendQueue(subject, (skipBody ? "" : message)); // Send the eMail through the eMail engine
                                        }

                                        Log.AppLog.WriteEntry(this, "Job for " + _conversionJobs[i].OriginalFileName + " started using Conversion Task " + _conversionJobs[i].TaskName + " and Profile " + _conversionJobs[i].Profile, Log.LogEntryType.Information, true);
                                        Log.AppLog.WriteEntry(this, "Temp working path is " + _conversionJobs[i].WorkingPath, Log.LogEntryType.Debug, true);
                                        break;
                                    }
                                }
                                Monitor.Exit(_queueManager.Queue);
                            }
                        }
                    }

                    if ((GlobalDefs.Active) && !SomeRunning) // Was running jobs earlier, no active jobs now
                    {
                        Log.AppLog.WriteEntry(this, Localise.GetPhrase("No conversions running , allowing system sleep"), Log.LogEntryType.Debug, true);
                        Util.PowerManagement.AllowSleep();
                        _allowSleep = true;
                    }
                    else if ((!GlobalDefs.Active) && SomeRunning) // Wasn't running jobs earlier, has new active jobs now
                    {
                        if (_userAllowSleep) // User allows sleep while converting
                        {
                            Log.AppLog.WriteEntry(this, Localise.GetPhrase("Starting new conversions, allowing system sleep"), Log.LogEntryType.Debug, true);
                            Util.PowerManagement.AllowSleep();
                            _allowSleep = true;
                        }
                        else
                        {
                            Log.AppLog.WriteEntry(this, Localise.GetPhrase("Starting new conversions, preventing system sleep"), Log.LogEntryType.Debug, true);
                            Util.PowerManagement.PreventSleep();
                            _allowSleep = false;
                        }
                    }

                    // Check if the conversion is paused while the job is active, if so allow sleep
                    if (GlobalDefs.Pause && SomeRunning && !_allowSleep)
                    {
                        Log.AppLog.WriteEntry(this, Localise.GetPhrase("Active jobs paused, allowing system sleep"), Log.LogEntryType.Debug, true);
                        Util.PowerManagement.AllowSleep();
                        _allowSleep = true;
                    }
                    else if (!GlobalDefs.Pause && SomeRunning && !_userAllowSleep && _allowSleep) // disable sleep once the job has been resumed and user does not allow sleeping while converting
                    {
                        Log.AppLog.WriteEntry(this, Localise.GetPhrase("Active jobs resumed, user does not allow sleep while converting, preventing system sleep"), Log.LogEntryType.Debug, true);
                        Util.PowerManagement.PreventSleep();
                        _allowSleep = false;
                    }

                    GlobalDefs.Active = SomeRunning;
                    _engineCrashed = false; // we've reached here so, reset it

                    // Sleep then shutdown check
                    Thread.Sleep(GlobalDefs.ENGINE_CORE_SLEEP_PERIOD);
                }

                // Shutdown support threads
                if ((syncThread != null) && syncThread.IsAlive)
                    syncThread.Abort();
                if ((scanThread != null) && scanThread.IsAlive)
                    scanThread.Abort();
                if ((updateCheckThread != null) && updateCheckThread.IsAlive)
                    updateCheckThread.Abort();

                // Shut down the conversion threads
                Monitor.Enter(_queueManager.Queue);
                for (int i = 0; i < _conversionJobs.Length; i++)
                {
                    if (_conversionJobs[i] != null)
                    {
                        _conversionJobs[i].StopConversionThread();
                        _conversionJobs[i] = null;
                    }
                }
                _queueManager.Queue.Clear(); // Clear the entire queue
                Monitor.Exit(_queueManager.Queue);

                Util.PowerManagement.AllowSleep(); //reset to default
                _allowSleep = true;
                GlobalDefs.Active = false;
                _monitorThread = null; // this thread is done
            }
            catch (Exception e)
            {
                _autoPause = GlobalDefs.Pause = false; // Reset suspension state
                GlobalDefs.Shutdown = true; // Terminate everything since we had a major crash

                // Shutdown support threads
                if ((syncThread != null) && syncThread.IsAlive)
                    syncThread.Abort();
                if ((scanThread != null) && scanThread.IsAlive)
                    scanThread.Abort();
                if ((updateCheckThread != null) && updateCheckThread.IsAlive)
                    updateCheckThread.Abort();

                // Release the queue lock if taken
                try { Monitor.Exit(_queueManager.Queue); } // Incase it's taken release it, if not taken it will throw an exception
                catch { }

                Util.PowerManagement.AllowSleep(); //reset to default
                _allowSleep = true;
                GlobalDefs.Active = false;
                _engineCrashed = true;

                GeneralOptions go = MCEBuddyConf.GlobalMCEConfig.GeneralOptions;
                go.engineRunning = false;
                MCEBuddyConf.GlobalMCEConfig.UpdateGeneralOptions(go, true); // Write the stop engine settings, since it crashed

                Log.AppLog.WriteEntry(this, "MCEBuddy Monitor Thread Crashed. Error: " + e.ToString(), Log.LogEntryType.Error, true); // This may or may not work depending upon whether the llog has been initialized otherwise it goes into NULL log
                Log.WriteSystemEventLog("MCEBuddy Monitor Thread Crashed. Error: " + e.ToString(), EventLogEntryType.Error);

                _monitorThread = null; // this thread is done
            }
        }