private void WriteMonitorSettings(Ini configIni) { if (configIni == null) return; // First read and delete all Monitor tasks - start with a clean slate (incase there were previous delete monitor tasks without writing) string[] searchRecords = configIni.ReadString("Engine", "SearchRecords", "").Split(','); foreach (string searchRecord in searchRecords) { if (String.IsNullOrEmpty(searchRecord)) continue; configIni.DeleteSection(searchRecord); } // Write the Monitor Tasks Settings foreach (MonitorJobOptions monitorTask in mceBuddyConfSettings.monitorTasks) { string section = monitorTask.taskName; configIni.Write(section, "SearchPath", monitorTask.searchPath); monitorTask.searchPattern = monitorTask.searchPattern.Replace(GlobalDefs.DEFAULT_VIDEO_STRING, GlobalDefs.DEFAULT_VIDEO_FILE_TYPES); configIni.Write(section, "SearchPattern", monitorTask.searchPattern); configIni.Write(section, "MonitorSubdirectories", monitorTask.monitorSubdirectories); configIni.Write(section, "MonitorConvertedFiles", monitorTask.monitorConvertedFiles); configIni.Write(section, "ReMonitorRecordedFiles", monitorTask.reMonitorRecordedFiles); configIni.Write(section, "DomainName", monitorTask.domainName); configIni.Write(section, "UserName", monitorTask.userName); if (!String.IsNullOrEmpty(monitorTask.password)) configIni.Write(section, "Password", Crypto.Encrypt(monitorTask.password)); // Password is written as encrypted } WriteMonitorTasksList(configIni); // this list goes in the Engine section }
/// <summary> /// Scans the Monitored directories and Manual Queue for new files to process /// Applies the filter tests /// Processes conversion task jobs for the files and adds them to the queue /// This function takes a lock on the Queue while modifying the queue and is thread safe /// </summary> public void ScanForFiles() { // Search the specific directories foreach (MonitorJobOptions monitorTask in MCEBuddyConf.GlobalMCEConfig.AllMonitorTasks) { IEnumerable<string> foundFiles = null; try { // Directory.EnumerateFiles throws an exception if it comes across a protected/unaccesible directory and the ENTIRE list is empty // Instead we need to handle exceptions, skip the protected file/directory and continue walking down the rest foundFiles = FilePaths.GetDirectoryFiles(monitorTask.searchPath, "*", (monitorTask.monitorSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)).OrderBy(File.GetLastWriteTime); // We sort the files by last modified time when scanning and adding (oldest to newest) if (foundFiles != null && foundFiles.Count() > 0) // Check for no files (could be due to security access/protection errors { foreach (string foundFile in foundFiles) { if (GlobalDefs.Shutdown) // Check for a shutdown command, this can be an intensive loop return; // Exit - we're done here Monitor.Enter(_monitorTaskFilterMismatchFiles); // Make this thread safe if (_monitorTaskFilterMismatchFiles.ContainsKey(foundFile)) // Check if this file has been processed by this monitor task and doesn't have a filter match, if so skip it (the filters do not change until the engine is stopped, settings changed, engine restarted which will create a new queue) { if (_monitorTaskFilterMismatchFiles[foundFile].Contains(monitorTask.taskName)) { Monitor.Exit(_monitorTaskFilterMismatchFiles); continue; } } Monitor.Exit(_monitorTaskFilterMismatchFiles); Monitor.Enter(_archivedFiles); if (_archivedFiles.Contains(foundFile)) // Check if the file has been marked as an archive file if so skip it { Monitor.Exit(_archivedFiles); continue; } Monitor.Exit(_archivedFiles); // First check if this file is in the MCEBuddyArchive directory (which contains converted files, if so skip them) if (Path.GetDirectoryName(foundFile).ToLower().Contains((string.IsNullOrEmpty(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.archivePath) ? GlobalDefs.MCEBUDDY_ARCHIVE.ToLower() : MCEBuddyConf.GlobalMCEConfig.GeneralOptions.archivePath.ToLower()))) { Monitor.Enter(_archivedFiles); _archivedFiles.Add(foundFile); // add to the list Monitor.Exit(_archivedFiles); Log.AppLog.WriteEntry(this, "File " + foundFile + " has been converted and archived, skipping", Log.LogEntryType.Debug); continue; } // 1st Level Pattern Check - Found a file, filename pattern match from monitor task if (Util.Text.WildcardRegexPatternMatch(Path.GetFileName(foundFile), monitorTask.searchPattern)) // Check pattern match for Monitor Locations filter { // Take a lock here for EACH file before processing and modifying queue // This is done per file and not for all files so that we don't lock up the engine interfaces which need the same lock to respond to GUI queries // CheckAndAddFile is very intensive time consuming process for each file as it extracts metadata and compares filters // Also EnumerateFiles is a very intensive process for very large nested and remote directories which will lock up the thread if the lock is taken Monitor.Enter(Queue); // Take a lock on the queue before modifying the queue CheckAndAddFile(foundFile, monitorTask, false); // Check history, monitor and conversion task filters and creates conversion job for file Monitor.Exit(Queue); // Release the lock on the queue after modifying the queue } else // File type mismatch, log and keep track of them for each monitor task processed { Monitor.Enter(_monitorTaskFilterMismatchFiles); // Make it thread safe if (!_monitorTaskFilterMismatchFiles.ContainsKey(foundFile)) /// Check if this file does not have a key, then create one _monitorTaskFilterMismatchFiles.Add(foundFile, new List<string>()); // Make a new key for the file _monitorTaskFilterMismatchFiles[foundFile].Add(monitorTask.taskName); // Add this task for the file as a filter mismatch Monitor.Exit(_monitorTaskFilterMismatchFiles); Log.AppLog.WriteEntry(this, "File " + Path.GetFileName(foundFile) + " did not match wildcard" + " " + monitorTask.searchPattern + " for monitor task " + monitorTask.taskName, Log.LogEntryType.Debug); } } } else Log.AppLog.WriteEntry("No accessible files founds in location " + monitorTask.searchPath + " for monitor task " + monitorTask.taskName, Log.LogEntryType.Information); } catch (Exception ex) { Log.AppLog.WriteEntry("Unable to search for files in location " + monitorTask.searchPath + " for monitor task " + monitorTask.taskName + "\r\nERROR : " + ex.Message, Log.LogEntryType.Warning); foundFiles = null; try { Monitor.Exit(Queue); } // Release queue lock catch { } try { Monitor.Exit(_monitorTaskFilterMismatchFiles); } // Release monitor mismatch list lock catch { } try { Monitor.Exit(_archivedFiles); } // Release archived list lock catch { } } } // Read the manual queue - manual selections are always first Ini iniQueue = new Ini(GlobalDefs.ManualQueueFile); SortedList<string, string> manualQueue = iniQueue.GetSectionKeyValuePairs("ManualQueue"); foreach (KeyValuePair<string, string> manualFile in manualQueue) { string filePath = manualFile.Value; // Due to INI restriction only the Value can hold special characters like ];= which maybe contained in the filename, hence the Value is used to capture the "TRUE" filename (Key and Section have restrictions) if (GlobalDefs.Shutdown) // Check for a shutdown command, this can be an intensive loop return; // Exit - we're done here // Check for valid entry if (String.IsNullOrWhiteSpace(filePath)) continue; string destinationPath = Path.GetDirectoryName(filePath); if (String.IsNullOrWhiteSpace(destinationPath)) // check for a null directory name here (happens with some root level network paths) { iniQueue.DeleteSection(filePath); // Remove the key from the manual file continue; } // Connect network drives if needed for each manual entry GeneralOptions go = MCEBuddyConf.GlobalMCEConfig.GeneralOptions; if (Util.Net.IsUNCPath(destinationPath)) { if (!String.IsNullOrWhiteSpace(go.userName)) { ConnectNet(destinationPath, go.domainName, go.userName, go.password); } else { Log.AppLog.WriteEntry("No network authentication username found, defaulting to Guest authentication", Log.LogEntryType.Warning); ConnectNet(destinationPath, "", GlobalDefs.DEFAULT_NETWORK_USERNAME, ""); } } if (Directory.Exists(filePath)) // After we are connected, check if the target is a directory (accidentally happens), we don't process it { Log.AppLog.WriteEntry("Manually selected file " + filePath + " is a directory, skipping", Log.LogEntryType.Warning); iniQueue.DeleteSection(filePath); // Remove the key from the manual file continue; } if (!File.Exists(filePath)) // After we are connected, check if the file actually exists { Log.AppLog.WriteEntry("Manually selected file " + filePath + " does not exist, skipping", Log.LogEntryType.Warning); iniQueue.DeleteSection(filePath); // Remove the key from the manual file continue; } Log.AppLog.WriteEntry("Manually selected file " + filePath + " added to queue", Log.LogEntryType.Debug); // Take a lock here for EACH file before processing and modifying queue // This is done per file and not for all files so that we don't lock up the engine interfaces which need the same lock to respond to GUI queries // CheckAndAddFile is very intensive time consuming process for each file as it extracts metadata and compares filters try { Monitor.Enter(Queue); // Take a lock on the queue before modifying the queue CheckAndAddFile(filePath, null, true); Monitor.Exit(Queue); // Release the lock on the queue after modifying the queue } catch (Exception ex) { Log.AppLog.WriteEntry("Add manual files terminated.\r\nERROR : " + ex.Message, Log.LogEntryType.Warning); try { Monitor.Exit(Queue); } // Release queue lock catch { } } } }
private void WriteConversionSettings(Ini configIni) { if (configIni == null) return; // First read and delete all Conversion tasks - start with a clean slate (incase there were previous delete conversion tasks without writing) string[] conversionRecords = configIni.ReadString("Engine", "Tasks", "").Split(','); foreach (string conversionRecord in conversionRecords) { if (String.IsNullOrEmpty(conversionRecord)) continue; configIni.DeleteSection(conversionRecord); } // Write the Converstion Task Settings foreach (ConversionJobOptions conversionTask in mceBuddyConfSettings.conversionTasks) { string section = conversionTask.taskName; configIni.Write(section, "Profile", conversionTask.profile); configIni.Write(section, "DestinationPath", conversionTask.destinationPath); configIni.Write(section, "WorkingPath", conversionTask.workingPath); configIni.Write(section, "FallbackDestination", conversionTask.fallbackToSourcePath); configIni.Write(section, "CheckReprocessingHistory", conversionTask.checkReprocessingHistory); configIni.Write(section, "AddToiTunesLibrary", conversionTask.addToiTunes); configIni.Write(section, "AddToWMPLibrary", conversionTask.addToWMP); configIni.Write(section, "AutoIncrementFilename", conversionTask.autoIncrementFilename); configIni.Write(section, "SkipReprocessing", conversionTask.skipReprocessing); configIni.Write(section, "MaxWidth", conversionTask.maxWidth); configIni.Write(section, "FPS", conversionTask.FPS); configIni.Write(section, "VolumeMultiplier", conversionTask.volumeMultiplier.ToString(CultureInfo.InvariantCulture)); configIni.Write(section, "QualityMultiplier", conversionTask.qualityMultiplier.ToString(CultureInfo.InvariantCulture)); configIni.Write(section, "RenameBySeries", conversionTask.renameBySeries); configIni.Write(section, "AltRenameBySeries", conversionTask.altRenameBySeries); configIni.Write(section, "CustomRenameBySeries", conversionTask.customRenameBySeries); configIni.Write(section, "RenameOnly", conversionTask.renameOnly); configIni.Write(section, "DownloadSeriesDetails", conversionTask.downloadSeriesDetails); configIni.Write(section, "DownloadBanner", conversionTask.downloadBanner); configIni.Write(section, "FileSelection", conversionTask.fileSelection); configIni.Write(section, "MetaSelection", conversionTask.metaShowSelection); configIni.Write(section, "MetaChannelSelection", conversionTask.metaNetworkSelection); configIni.Write(section, "MonitorTaskNames", (conversionTask.monitorTaskNames == null ? "" : String.Join(",", conversionTask.monitorTaskNames))); configIni.Write(section, "DRC", conversionTask.drc); configIni.Write(section, "AudioLanguage", conversionTask.audioLanguage); configIni.Write(section, "AudioOffset", conversionTask.audioOffset.ToString(CultureInfo.InvariantCulture)); configIni.Write(section, "InsertQueueTop", conversionTask.insertQueueTop); configIni.Write(section, "ExtractXML", conversionTask.extractXML); configIni.Write(section, "WriteMetadata", conversionTask.writeMetadata); configIni.Write(section, "AutoDeInterlace", conversionTask.autoDeInterlace); configIni.Write(section, "PreferHardwareEncoding", conversionTask.preferHardwareEncoding); configIni.Write(section, "StereoAudio", conversionTask.stereoAudio); configIni.Write(section, "EncoderSelectBestAudioTrack", conversionTask.encoderSelectBestAudioTrack); configIni.Write(section, "DisableCropping", conversionTask.disableCropping); configIni.Write(section, "StartTrim", conversionTask.startTrim); configIni.Write(section, "EndTrim", conversionTask.endTrim); configIni.Write(section, "ExtractCC", conversionTask.extractCC); configIni.Write(section, "CCOffset", conversionTask.ccOffset.ToString(CultureInfo.InvariantCulture)); configIni.Write(section, "EmbedSubtitlesChapters", conversionTask.embedSubtitlesChapters); configIni.Write(section, "PrioritizeOriginalBroadcastDateMatch", conversionTask.prioritizeOriginalBroadcastDateMatch); configIni.Write(section, "TaskCommercialSkipCut", conversionTask.commercialSkipCut); configIni.Write(section, "SkipCopyBackup", conversionTask.skipCopyBackup); configIni.Write(section, "SkipRemux", conversionTask.skipRemuxing); configIni.Write(section, "IgnoreCopyProtection", conversionTask.ignoreCopyProtection); configIni.Write(section, "TiVOMAKKey", conversionTask.tivoMAKKey); configIni.Write(section, "Enabled", conversionTask.enabled); configIni.Write(section, "ForceShowType", conversionTask.forceShowType.ToString()); configIni.Write(section, "MetaShowTypeSelection", conversionTask.metaShowTypeSelection.ToString()); configIni.Write(section, "MetaDRMTypeSelection", conversionTask.metaDRMSelection.ToString()); configIni.Write(section, "CommercialRemoval", conversionTask.commercialRemoval.ToString()); configIni.Write(section, "ComskipINI", conversionTask.comskipIni); configIni.Write(section, "DomainName", conversionTask.domainName); configIni.Write(section, "UserName", conversionTask.userName); if (!String.IsNullOrEmpty(conversionTask.password)) configIni.Write(section, "Password", Crypto.Encrypt(conversionTask.password)); // Password is written as encrypted // First wipe the MetaCorrectionEntries section clean, to remove old/redundant data and then start afresh since we don't know how many entries may exist configIni.DeleteSection(section + "-MetaCorrectionEntries"); if (conversionTask.metadataCorrections == null) configIni.Write(section, "MetaCorrectionsCount", 0); else { configIni.Write(section, "MetaCorrectionsCount", conversionTask.metadataCorrections.Length); for (int i = 0; i < conversionTask.metadataCorrections.Length; i++) // the Enteries are kept in their own section { configIni.Write(section + "-MetaCorrectionEntries", "OriginalTitle" + i.ToString(), conversionTask.metadataCorrections[i].originalTitle); configIni.Write(section + "-MetaCorrectionEntries", "CorrectedTitle" + i.ToString(), conversionTask.metadataCorrections[i].correctedTitle); configIni.Write(section + "-MetaCorrectionEntries", "TVDBSeriesId" + i.ToString(), conversionTask.metadataCorrections[i].tvdbSeriesId); configIni.Write(section + "-MetaCorrectionEntries", "IMDBSeriesId" + i.ToString(), conversionTask.metadataCorrections[i].imdbSeriesId); } } } WriteConversionTasksList(configIni); // this list goes in the Engine section }
public void DeleteHistoryItem(string sourceFileName) { if (String.IsNullOrWhiteSpace(sourceFileName)) { Log.AppLog.WriteEntry(this, "No filename to delete from History", Log.LogEntryType.Error, true); return; } Ini historyFile = new Ini(GlobalDefs.HistoryFile); historyFile.DeleteSection(sourceFileName); }