/// <summary> /// Returns the final extension for the file to be converted /// </summary> /// <param name="conversionOptions">Conversion Options</param> /// <returns>Extension as specified in the Conversion Profile</returns> public static string GetConversionExtension(ConversionJobOptions conversionOptions) { Ini ini = new Ini(GlobalDefs.ProfileFile); string orderSetting = ini.ReadString(conversionOptions.profile, "order", "").ToLower().Trim(); if (!orderSetting.Contains("mencoder")) orderSetting = orderSetting.Replace("me","mencoder"); if (!orderSetting.Contains("handbrake")) orderSetting = orderSetting.Replace("hb", "handbrake"); if (!orderSetting.Contains("ffmpeg")) orderSetting = orderSetting.Replace("ff", "ffmpeg"); string[] tool = orderSetting.Split(','); // We can check the first tool since all tools will lead to the same final extension string extension = ini.ReadString(conversionOptions.profile, tool[0] + "-ext", "").ToLower().Trim(); string remuxTo = ini.ReadString(conversionOptions.profile, tool[0] + "-remuxto", "").ToLower().Trim(); if (!string.IsNullOrEmpty(remuxTo)) { if (remuxTo[0] != '.') remuxTo = "." + remuxTo; // Just in case someone does something dumb like forget the leading "." return remuxTo; } else { if (extension[0] != '.') extension = "." + extension; // Just in case someone does something dumb like forget the leading "." return extension; } }
/// <summary> /// Used to execute custom commands after the conversion process is compelte just before the file is moved to the desination directory /// </summary> /// <param name="prefix">Prefix for reading lines from profile</param> /// <param name="profile">Profile name</param> /// <param name="taskName">Task Name</param> /// <param name="workingPath">Temp working path</param> /// <param name="destinationPath">Destination path for converted file</param> /// <param name="convertedFile">Full path to final converted file</param> /// <param name="sourceFile">Full path to original source file</param> /// <param name="remuxFile">Full path to intermediate remuxed file</param> /// <param name="edlFile">Full path to EDL file</param> /// <param name="srtFile">Full path to SRT file</param> /// <param name="metaData">Video metadata structure for source file</param> /// <param name="jobStatus">ref to JobStatus</param> /// <param name="jobLog">JobLog</param> public CustomCommand(string prefix, string profile, string taskName, string workingPath, string destinationPath, string convertedFile, string sourceFile, string remuxFile, string edlFile, string srtFile, VideoTags metaData, JobStatus jobStatus, Log jobLog) { _profile = profile; _taskName = taskName; _jobLog = jobLog; _jobStatus = jobStatus; _workingPath = workingPath; _destinationPath = destinationPath; _convertedFile = convertedFile; _sourceFile = sourceFile; _remuxFile = remuxFile; _edlFile = edlFile; _srtFile = srtFile; _metaData = metaData; _prefix = prefix; Ini ini = new Ini(GlobalDefs.ProfileFile); commandPath = ini.ReadString(profile, prefix + "Path", "").ToLower().Trim(); commandParameters = ini.ReadString(profile, prefix + "Parameters", ""); hangPeriod = ini.ReadInteger(profile, prefix + "HangPeriod", GlobalDefs.HANG_PERIOD_DETECT); customCommandCritical = ini.ReadBoolean(profile, prefix + "Critical", false); // NOTE: if customCommandCritical is TRUE will need to return false in case it's a failure customCommandUISession = ini.ReadBoolean(profile, prefix + "UISession", false); // Does the custom command need a UI Session (Session 1) with admin privileges customCommandShowWindow = ini.ReadBoolean(profile, prefix + "ShowWindow", true); // Show the window or hide it customCommandExitCodeCheck = ini.ReadBoolean(profile, prefix + "ExitCodeCheck", false); // Don't check for exit code _jobLog.WriteEntry(this, "Custom command parameters read -> " + " \n" + _prefix + "Path = " + commandPath + " \n" + _prefix + "Parameters = " + commandParameters + " \n" + _prefix + "HangPeriod = " + hangPeriod.ToString(System.Globalization.CultureInfo.InvariantCulture) + " \n" + _prefix + "Critical = " + customCommandCritical.ToString() + " \n" + _prefix + "UISession = " + customCommandUISession.ToString() + " \n" + _prefix + "ShowWindow = " + customCommandShowWindow.ToString() + " \n" + _prefix + "ExitCodeCheck = " + customCommandExitCodeCheck.ToString(), Log.LogEntryType.Debug); }
public RemoteEngineForm() { InitializeComponent(); Ini tempIni = new Ini(GlobalDefs.TempSettingsFile); string remoteServerName = tempIni.ReadString("Engine", "RemoteServerName", GlobalDefs.MCEBUDDY_SERVER_NAME); int remoteServerPort = tempIni.ReadInteger("Engine", "RemoteServerPort", int.Parse(GlobalDefs.MCEBUDDY_SERVER_PORT)); engineNameTxt.Text = remoteServerName; portNoTxt.Text = remoteServerPort.ToString(System.Globalization.CultureInfo.InvariantCulture); }
/// <summary> /// Gets the order of the encoders in the specified profile /// </summary> /// <param name="profile">Profile to get encoders order</param> /// <returns>String array containing the encoders in execution order</returns> public static string[] GetProfileEncoderOrder(string profile) { Ini ini = new Ini(GlobalDefs.ProfileFile); string orderSetting = ini.ReadString(profile, "order", "").ToLower().Trim(); if (!orderSetting.Contains("mencoder")) orderSetting = orderSetting.Replace("me", "mencoder"); if (!orderSetting.Contains("handbrake")) orderSetting = orderSetting.Replace("hb", "handbrake"); if (!orderSetting.Contains("ffmpeg")) orderSetting = orderSetting.Replace("ff", "ffmpeg"); string[] order = orderSetting.Split(','); return order; }
public Remover(string profile, string uncutVideo, string workingPath, string edlFile, float initialSkipSeconds, VideoInfo remuxedVideoFileInfo, JobStatus jobStatus, Log jobLog) : base(profile, uncutVideo, remuxedVideoFileInfo.Duration, edlFile, initialSkipSeconds, jobStatus, jobLog) { _remuxedVideoFileInfo = remuxedVideoFileInfo; _uncutVideo = uncutVideo; _ext = Util.FilePaths.CleanExt(_uncutVideo); _workingPath = workingPath; _duration = _remuxedVideoFileInfo.Duration; _jobStatus = jobStatus; _jobLog = jobLog; // Read various profile parameters Ini configProfileIni = new Ini(GlobalDefs.ProfileFile); _cutMP4Alternative = configProfileIni.ReadBoolean(profile, "CutMP4Alternate", false); // for FFMPEG and Handbrake, we have commerical cutting options, for mEncoder cutting is done during conversion using -edl option jobLog.WriteEntry("MP4 Alternative Cutting -> " + _cutMP4Alternative.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug); _universalCommercialRemover = configProfileIni.ReadBoolean(profile, "UniversalCommercialRemover", false); // Forcing the use of CutFFMPEG which works on all video types jobLog.WriteEntry("Universal Commercial Remover -> " + _universalCommercialRemover.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug); string commercialMerge = configProfileIni.ReadString(profile, "CommercialMergeTool", ""); // Force tool to merge commercial segments jobLog.WriteEntry("Force Commercial Segment Merging Tool -> " + commercialMerge, Log.LogEntryType.Debug); switch (commercialMerge.ToLower()) { case "avidemux": _useAVIDemuxMerge = true; break; case "ffmpeg": _useFFMPEGMerge = true; break; case "": break; default: jobLog.WriteEntry("INVALID Force Commercial Segment Merging Tool -> " + commercialMerge, Log.LogEntryType.Warning); break; } }
public ConversionJob(ConversionJobOptions conversionJobOptions, VideoMetaData metaData) { _conversionOptions = conversionJobOptions; // First thing to do _metaData = metaData; // Save the metadata if present _originalFileNameBackup = _conversionOptions.sourceVideo; // This is what we use to report to the world what we're working on, _sourceVideo may change under certain conditions below if (String.IsNullOrEmpty(_conversionOptions.destinationPath)) _conversionOptions.destinationPath = Path.GetDirectoryName(_conversionOptions.sourceVideo); // No dest path = convert in place _jobStatus = new JobStatus(); _jobStatus.SourceFile = _conversionOptions.sourceVideo; _jobStatus.TaskName = _conversionOptions.taskName; // Read various engine parameters _maxConcurrentJobs = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.maxConcurrentJobs; _spaceCheck = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.spaceCheck; _subtitleSegmentOffset = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.subtitleSegmentOffset; // Read various profile parameters Ini configProfileIni = new Ini(GlobalDefs.ProfileFile); // Profile only parameters _preConversionCommercialRemover = configProfileIni.ReadBoolean(_conversionOptions.profile, "PreConversionCommercialRemover", false); // Check if the user wants to remove commercials before the actual conversion in which case we always return false - i.e. remove commercial during remux stage _copyLOGFile = configProfileIni.ReadBoolean(_conversionOptions.profile, "CopyLogFile", false); // Check if the user wants to save the log file generated by Comskip _copyPropertiesFile = configProfileIni.ReadBoolean(_conversionOptions.profile, "CopyPropertiesFile", false); // Check if the user wants to save the properties file for SageTV metadata if (configProfileIni.ReadString(_conversionOptions.profile, "AutoDeinterlace", "default") == "default") _autoDeinterlace = _conversionOptions.autoDeInterlace; else _autoDeinterlace = configProfileIni.ReadBoolean(_conversionOptions.profile, "AutoDeinterlace", false); if (_conversionOptions.renameOnly) _commercialSkipCut = true; //no cutting if we are renaming only else if (configProfileIni.ReadString(_conversionOptions.profile, "CommercialSkipCut", "default") == "default") _commercialSkipCut = _conversionOptions.commercialSkipCut; else _commercialSkipCut = configProfileIni.ReadBoolean(_conversionOptions.profile, "CommercialSkipCut", false); }
private void ReadEMailSettings(Ini configIni) { mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.smtpServer = configIni.ReadString("Engine", "eMailServer", ""); mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.port = configIni.ReadInteger("Engine", "eMailPort", 25); // default port is 25 mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.ssl = configIni.ReadBoolean("Engine", "eMailSSL", false); mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.fromAddress = configIni.ReadString("Engine", "eMailFrom", ""); mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.toAddresses = configIni.ReadString("Engine", "eMailTo", ""); mceBuddyConfSettings.generalOptions.eMailSettings.successEvent = configIni.ReadBoolean("Engine", "eMailSuccess", true); mceBuddyConfSettings.generalOptions.eMailSettings.failedEvent = configIni.ReadBoolean("Engine", "eMailFailed", true); mceBuddyConfSettings.generalOptions.eMailSettings.cancelledEvent = configIni.ReadBoolean("Engine", "eMailCancelled", true); mceBuddyConfSettings.generalOptions.eMailSettings.startEvent = configIni.ReadBoolean("Engine", "eMailStart", true); mceBuddyConfSettings.generalOptions.eMailSettings.downloadFailedEvent = configIni.ReadBoolean("Engine", "eMailDownloadFailed", true); mceBuddyConfSettings.generalOptions.eMailSettings.queueEvent = configIni.ReadBoolean("Engine", "eMailQueue", true); mceBuddyConfSettings.generalOptions.eMailSettings.successSubject = configIni.ReadString("Engine", "eMailSuccessSubject", ""); mceBuddyConfSettings.generalOptions.eMailSettings.failedSubject = configIni.ReadString("Engine", "eMailFailedSubject", ""); mceBuddyConfSettings.generalOptions.eMailSettings.cancelledSubject = configIni.ReadString("Engine", "eMailCancelledSubject", ""); mceBuddyConfSettings.generalOptions.eMailSettings.startSubject = configIni.ReadString("Engine", "eMailStartSubject", ""); mceBuddyConfSettings.generalOptions.eMailSettings.downloadFailedSubject = configIni.ReadString("Engine", "eMailDownloadFailedSubject", ""); mceBuddyConfSettings.generalOptions.eMailSettings.queueSubject = configIni.ReadString("Engine", "eMailQueueSubject", ""); mceBuddyConfSettings.generalOptions.eMailSettings.skipBody = configIni.ReadBoolean("Engine", "eMailSkipBody", false); mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.userName = configIni.ReadString("Engine", "eMailUsername", ""); mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.password = configIni.ReadString("Engine", "eMailPassword", ""); if (!String.IsNullOrEmpty(mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.password)) mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.password = Util.Crypto.Decrypt(mceBuddyConfSettings.generalOptions.eMailSettings.eMailBasicSettings.password); // password is stored encrypted }
private void ReadGeneralSettings(Ini configIni) { // Read the General Settings ReadHourMinute(configIni, ref mceBuddyConfSettings.generalOptions.wakeHour, ref mceBuddyConfSettings.generalOptions.wakeMinute, "Wake"); ReadHourMinute(configIni, ref mceBuddyConfSettings.generalOptions.startHour, ref mceBuddyConfSettings.generalOptions.startMinute, "Start"); ReadHourMinute(configIni, ref mceBuddyConfSettings.generalOptions.stopHour, ref mceBuddyConfSettings.generalOptions.stopMinute, "Stop"); mceBuddyConfSettings.generalOptions.domainName = configIni.ReadString("Engine", "DomainName", ""); mceBuddyConfSettings.generalOptions.userName = configIni.ReadString("Engine", "UserName", "Guest"); mceBuddyConfSettings.generalOptions.password = configIni.ReadString("Engine", "Password", ""); if (!String.IsNullOrEmpty(mceBuddyConfSettings.generalOptions.password)) mceBuddyConfSettings.generalOptions.password = Crypto.Decrypt(mceBuddyConfSettings.generalOptions.password); // Password is kept as encrypted mceBuddyConfSettings.generalOptions.daysOfWeek = configIni.ReadString("Engine", "DaysOfWeek", "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday"); mceBuddyConfSettings.generalOptions.maxConcurrentJobs = configIni.ReadInteger("Engine", "MaxConcurrentJobs", 1); mceBuddyConfSettings.generalOptions.logJobs = configIni.ReadBoolean("Engine", "LogJobs", true); mceBuddyConfSettings.generalOptions.logLevel = configIni.ReadInteger("Engine", "LogLevel", 3); mceBuddyConfSettings.generalOptions.logKeepDays = configIni.ReadInteger("Engine", "LogKeepDays", 15); mceBuddyConfSettings.generalOptions.deleteOriginal = configIni.ReadBoolean("Engine", "DeleteOriginal", false); mceBuddyConfSettings.generalOptions.useRecycleBin = configIni.ReadBoolean("Engine", "UseRecycleBin", false); mceBuddyConfSettings.generalOptions.archiveOriginal = configIni.ReadBoolean("Engine", "ArchiveOriginal", false); mceBuddyConfSettings.generalOptions.deleteConverted = configIni.ReadBoolean("Engine", "DeleteConverted", false); mceBuddyConfSettings.generalOptions.allowSleep = configIni.ReadBoolean("Engine", "AllowSleep", true); mceBuddyConfSettings.generalOptions.suspendOnBattery = configIni.ReadBoolean("Engine", "SuspendOnBattery", false); mceBuddyConfSettings.generalOptions.minimumAge = configIni.ReadInteger("Engine", "MinimumAge", 0); mceBuddyConfSettings.generalOptions.sendEmail = configIni.ReadBoolean("Engine", "SendEmail", false); mceBuddyConfSettings.generalOptions.locale = configIni.ReadString("Engine", "Locale", CultureInfo.CurrentCulture.Name); mceBuddyConfSettings.generalOptions.tempWorkingPath = configIni.ReadString("Engine", "TempWorkingPath", ""); CheckPathEnding(ref mceBuddyConfSettings.generalOptions.tempWorkingPath); mceBuddyConfSettings.generalOptions.archivePath = configIni.ReadString("Engine", "ArchivePath", ""); CheckPathEnding(ref mceBuddyConfSettings.generalOptions.archivePath); mceBuddyConfSettings.generalOptions.failedPath = configIni.ReadString("Engine", "FailedPath", ""); CheckPathEnding(ref mceBuddyConfSettings.generalOptions.failedPath); mceBuddyConfSettings.generalOptions.spaceCheck = configIni.ReadBoolean("Engine", "SpaceCheck", true); mceBuddyConfSettings.generalOptions.comskipPath = configIni.ReadString("Engine", "CustomComskipPath", ""); mceBuddyConfSettings.generalOptions.customProfilePath = configIni.ReadString("Engine", "CustomProfilePath", ""); mceBuddyConfSettings.generalOptions.hangTimeout = configIni.ReadInteger("Engine", "HangPeriod", GlobalDefs.HANG_PERIOD_DETECT); mceBuddyConfSettings.generalOptions.pollPeriod = configIni.ReadInteger("Engine", "PollPeriod", GlobalDefs.MONITOR_POLL_PERIOD); mceBuddyConfSettings.generalOptions.processPriority = configIni.ReadString("Engine", "ProcessPriority", "Normal"); mceBuddyConfSettings.generalOptions.CPUAffinity = (IntPtr) configIni.ReadLong("Engine", "CPUAffinity", 0); mceBuddyConfSettings.generalOptions.engineRunning = configIni.ReadBoolean("Engine", "EngineRunning", false); mceBuddyConfSettings.generalOptions.localServerPort = configIni.ReadInteger("Engine", "LocalServerPort", int.Parse(GlobalDefs.MCEBUDDY_SERVER_PORT)); mceBuddyConfSettings.generalOptions.uPnPEnable = configIni.ReadBoolean("Engine", "UPnPEnable", false); mceBuddyConfSettings.generalOptions.firewallExceptionEnabled = configIni.ReadBoolean("Engine", "FirewallExceptionEnable", false); string srtSegmentOffset = configIni.ReadString("Engine", "SubtitleSegmentOffset", GlobalDefs.SEGMENT_CUT_OFFSET_GOP_COMPENSATE); double.TryParse(srtSegmentOffset, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out mceBuddyConfSettings.generalOptions.subtitleSegmentOffset); ReadEMailSettings(configIni); }
/// <summary> /// Check the local file for updates downloaded earlier /// </summary> private void CheckForUpdates() { if (!_newUpdateDownloaded) // Nothing to process return; else _newUpdateDownloaded = false; // We have processed this new update Monitor.Enter(_announcementTextScrollTimer); // Take the lock before we modify anything Ini configIni = new Ini(GlobalDefs.TempSettingsFile); // 1st is Announcement [Announcement, Link, ToolTip] string announcement = configIni.ReadString("Engine", "Announcement", ""); string announcementLink = configIni.ReadString("Engine", "AnnouncementLink", ""); _announcementInfo[0, 0] = announcement; _announcementInfo[0, 1] = announcementLink; if (String.IsNullOrWhiteSpace(announcementLink)) _announcementInfo[0, 2] = ""; // Clear the pop up tooltip else _announcementInfo[0, 2] = Localise.GetPhrase("Click here to open link"); // Set a pop up tooltip // 2nd is Version [Announcement, Link, ToolTip] //Get the version of this app Version CurrentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; // Check for any new version and write to form if required (this comes in the end, it trumps all annoucements Version LatestVersion = CurrentVersion; string LatestVersionString = configIni.ReadString("Engine", "LatestVersion", ""); if (!String.IsNullOrWhiteSpace(LatestVersionString)) LatestVersion = new Version(LatestVersionString); if (LatestVersion > CurrentVersion) { string MCEBuddyVersion = LatestVersion.Major.ToString(CultureInfo.InvariantCulture) + "." + LatestVersion.Minor.ToString(CultureInfo.InvariantCulture) + "." + LatestVersion.Build.ToString(CultureInfo.InvariantCulture); if (LatestVersion.Revision == 1) //Revision version 1 = Release version else BETA version _announcementInfo[1, 0] = Localise.GetPhrase("New version") + " " + MCEBuddyVersion + " " + Localise.GetPhrase("available"); else _announcementInfo[1, 0] = Localise.GetPhrase("New Beta version") + " " + MCEBuddyVersion + " " + Localise.GetPhrase("available"); _announcementInfo[1, 1] = Crypto.Decrypt(GlobalDefs.MCEBUDDY_DOWNLOAD_NEW_VERSION); _announcementInfo[1, 2] = Localise.GetPhrase("Click here to download new version of MCEBuddy"); // Set a pop up tooltip } else _announcementInfo[1, 0] = _announcementInfo[1, 1] = _announcementInfo[1, 2] = ""; Monitor.Exit(_announcementTextScrollTimer); }
private void StatusForm_Load(object sender, EventArgs e) { // Check for changes in screen resolution and screen changes (multi monitor support) - this HAS to be in Load and NOT in the constructor since the form has not been initiatized yet this.MaximumSize = Screen.FromControl(this).WorkingArea.Size; // Set the maximum size for the form based on working areas so we don't end up with dead/inaccessible locations // Set the version properties (Versioning is driven as Major.Minor.Build.Revision) MajorTitle.Text = "MCEBuddy " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Major.ToString(System.Globalization.CultureInfo.InvariantCulture) + "." + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString(System.Globalization.CultureInfo.InvariantCulture); if (System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision == 0) // BETA versions have revision code (Major.Minor.Build.Revision) as 0 BetaTitle.Text = "Beta " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Build.ToString(System.Globalization.CultureInfo.InvariantCulture); else BetaTitle.Text = "Release " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Build.ToString(System.Globalization.CultureInfo.InvariantCulture); Ini tempIni = new Ini(GlobalDefs.TempSettingsFile); // Check if this is the first time the GUI is start after an installation if (tempIni.ReadBoolean("GUI", "FirstStartComplete", false) == false) { MessageBox.Show(Localise.GetPhrase("Rest your mouse on any button, box or item to get Instant Help on it.\n\nFor more information on how to use, troubleshoot or get support for MCEBuddy click on the <Getting Started> link on the top right corner.\n\nDonations are always welcome :) Enjoy!\n\n - Ramit, Derek & Goose"), Localise.GetPhrase("Welcome to MCEBuddy")); tempIni.Write("GUI", "FirstStartComplete", true); } // Check for server location and start the thread to keep the UI connected to the MCEBuddy engine string remoteServerName = tempIni.ReadString("Engine", "RemoteServerName", GlobalDefs.MCEBUDDY_SERVER_NAME); if (remoteServerName != GlobalDefs.MCEBUDDY_SERVER_NAME) // check if it's a remote machine _connectPeriod = GlobalDefs.REMOTE_ENGINE_POLL_PERIOD; // remote machine needs be pinged slower // If this thread is not the thread that created the control, we'll invoke a callback in a threadsafe way to fix the control _tryConnectThread = new Thread(TryConnect); _tryConnectThread.IsBackground = true; // Kill this thread if the process is closed _tryConnectThread.CurrentCulture = _tryConnectThread.CurrentUICulture = Localise.MCEBuddyCulture; _tryConnectThread.Start(); // Start the announcement text scroll timer _announcementTextScrollTimer.Tick += new EventHandler(this.ScrollAnnouncementLabel); _announcementTextScrollTimer.Interval = 250; _announcementTextScrollTimer.Start(); // Now start the core background job status thread backgroundUpdate.WorkerSupportsCancellation = true; backgroundUpdate.RunWorkerAsync(); this.Show(); // Show the form }
/// <summary> /// Remux the WTV file directly to TSMPEG using FFMPEG /// Uses 3 levels of remuxing, stream copy, video transcode and video+audio transcode with support for Auto FPS detection or manual FPS override /// Sets the fixCorruptedRemux flag is it falls back to transcodes the video and audio using remux /// </summary> /// <param name="remuxTypes">Type of remuxing to try, copy, slow or both</param> /// <returns>Success or Failure</returns> private bool RemuxFFmpeg(FFMPEGRemuxType remuxTypes) { float FPS = 0; bool skipCopyRemux = false; // Do we need to skip stream copy bool skipSlowRemux = false; // Do we need to skip the slow copy if (remuxTypes == FFMPEGRemuxType.Recode) // Check if user asked to skip the copy remux skipCopyRemux = true; else if (remuxTypes == FFMPEGRemuxType.Copy) skipSlowRemux = true; // Read ReMux Parameters from Config Profile Ini configProfileIni = new Ini(GlobalDefs.ConfigFile); string profile = "FFMpegBackupRemux"; // This is where the Fallback Remux parameters are stored // MediaInfo is more reliable than FFMPEG but it doesn't always succeed _jobLog.WriteEntry(this, "Reading MediaInfo from " + _RecordingFile, Log.LogEntryType.Debug); FPS = VideoParams.FPS(_RecordingFile); _jobLog.WriteEntry(this, "Video FPS : " + FPS.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug); if ((FPS <= 0) || ((FPS > _RecordingFileMediaInfo.MediaInfo.VideoInfo.FPS) && (_RecordingFileMediaInfo.MediaInfo.VideoInfo.FPS > 0))) // MediaInfo did not succeed or got wrong value { _jobLog.WriteEntry(this, "Reading FFMPEG info from " + _RecordingFile, Log.LogEntryType.Debug); if (_RecordingFileMediaInfo.Success && !_RecordingFileMediaInfo.ParseError) FPS = _RecordingFileMediaInfo.MediaInfo.VideoInfo.FPS; // Get the FPS else _jobLog.WriteEntry(this, "ERROR reading FFMPEG Media info from " + _RecordingFile + ", disabling AutoFPS support", Log.LogEntryType.Warning); } // First check if the video is mpeg1 or mpeg2 video, else we need to move on to Slow Remux which will convert the video to MPEG2VIDEO // If it's mpeg1 or mpeg2, we can stream copy it directly // Check if we are copying mpeg4 video or if we are asked to copy all video codecs the copy remux it if (_RecordingFileMediaInfo.Success && !_RecordingFileMediaInfo.ParseError) { if (remuxTypes != FFMPEGRemuxType.Copy) // If we are forced to do copy remux then skip the check { // Check if we have a valid copy remux configuration if (!( // If not any of the below, then skip the copy remux _allowAllCopyRemuxing || // Copy all (_allowH264CopyRemuxing && _mpeg4Codecs.Any(s => s.Contains(_RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower()))) || // Copy h264 _mpeg2Codecs.Any(s => s.Contains(_RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower())) || // mpeg2 _mpeg1Codecs.Any(s => s.Contains(_RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower())) // mpeg1 )) { _jobLog.WriteEntry(this, "Video codec is not supported for fast copy remux in current configuration, skipping to slow remux. Video Codec found -> " + _RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower(), Log.LogEntryType.Information); skipCopyRemux = true; } } else _jobLog.WriteEntry(this, "Fast copy remuxing forced, skipping check for valid copy remux configuration. Video Codec found -> " + _RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower(), Log.LogEntryType.Warning); } if (!skipCopyRemux) { int profileCount = 0; while (true) // We do this in a loop until all MPEG2 CopyRemux profiles have been consumed { // Check for Multiple Audio Streams // Copy all streams if there is an audio selection specified and we'll extract it later (make sure you use the -map 0:a command to copy ALL audio stream, -acodec copy copies only 1 audio stream withou the map command) // Use coptyb and copyts to avoid invalid DTS errors // Threads 0 causes an error in some streams, avoid _jobLog.WriteEntry(this, Localise.GetPhrase("Copy remux loop ") + profileCount.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information); // First try to copy all the streams directly (read parmeters for copy profile) string copyRemuxParams = configProfileIni.ReadString(profile, "CopyRemux" + profileCount.ToString(CultureInfo.InvariantCulture), ""); if (String.IsNullOrWhiteSpace(copyRemuxParams)) // Have we used up all the CopyRemux profiles, then we're done here - try something else break; // Some files have zero channel audio tracks in them, if so then copying all audio track will fails // So we check for these tracks and run the remux if (FFMPEGRemuxZeroChannelFix(copyRemuxParams, FPS)) break; // We're good, all done profileCount++; // try the next one } } if (!skipSlowRemux && !RemuxedFileOK()) //check if file is created and if we aren't skipping the slow remux { _jobStatus.PercentageComplete = 100; // reset it to try again _jobStatus.CurrentAction = Localise.GetPhrase("Slow Remuxing"); int profileCount = 0; while (true) // Now loop through all the SlowRemux profiles until we succeed or fail { _jobLog.WriteEntry(this, Localise.GetPhrase("Slow remux loop ") + profileCount.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information); // Now try to copy audio and transcode video (read parameters for Slow Remux profile) string slowRemuxParams = configProfileIni.ReadString(profile, "SlowRemux" + profileCount.ToString(CultureInfo.InvariantCulture), ""); if (String.IsNullOrWhiteSpace(slowRemuxParams)) // Have we used up all the SlowRemux profiles, then we're done here break; // Some files have zero channel audio tracks in them, if so then copying all audio track will fails // So we check for these tracks and run the remux if (FFMPEGRemuxZeroChannelFix(slowRemuxParams, FPS)) break; // We're good, all done profileCount++; // Move onto the next profile iteration } if (!RemuxedFileOK()) //check of file is created { _jobLog.WriteEntry(Localise.GetPhrase("Slow Remux using FFMPEG failed at") + " " + _jobStatus.PercentageComplete.ToString(CultureInfo.InvariantCulture) + "%", Log.LogEntryType.Error); Util.FileIO.TryFileDelete(RemuxedFile); return false; } else slowRemuxed = true; // We have successfully remuxed the file but using the slow method (i.e. possibly destorying any CC data) } else if (skipSlowRemux) // Check if the copy remuxed file is okay since we aren't doing slow remux { if (RemuxedFileOK()) return true; else { Util.FileIO.TryFileDelete(RemuxedFile); // Clean up return false; } } return true; }
/// <summary> /// Tries to keep connected to the MCEBuddy Engine over TCP/IP. Once connected it downloads the MCEBuddy Config options from the server. /// </summary> /// <returns>True if it is connected to the Pipe</returns> private void TryConnect() { while (!_exit) // Keep trying to connect as long as app is running { bool configLockTaken = false; // Flag to see state of lock try { Monitor.Enter(_configLock, ref configLockTaken); // Take a lock before getting settings, incase Settings are being changed by the user if (_pipeProxy.ServiceShutdownBySystem()) // Has the system initated shutdown, if so we need to shutdown the GUI (to avoid uninstall issues) { Monitor.Exit(_configLock); configLockTaken = false; _exit = true; // We are done, release all threads and resources this.Close(); // Close the GUI continue; } MCEBuddyConf.GlobalMCEConfig = new MCEBuddyConf(_pipeProxy.GetConfigParameters()); // Get the parameters from the Engine we are connected to _jobs = _pipeProxy.GetAllJobsInQueueStatus(); _isEngineRunning = _pipeProxy.EngineRunning(); _isEngineSuspended = _pipeProxy.IsSuspended(); _isEngineCrashed = _pipeProxy.EngineCrashed(); _isWithinConversionTimes = _pipeProxy.WithinConversionTimes(); Monitor.Exit(_configLock); configLockTaken = false; _isEngineConnected = true; // All good, we are connected Thread.Sleep(_connectPeriod * (WindowState == FormWindowState.Minimized ? GlobalDefs.GUI_MINIMIZED_ENGINE_POLL_SLOW_FACTOR : 1)); // Check again in x seconds, refresh y times slower continue; } catch (Exception) // Whoops pipe broken! { if (configLockTaken) { Monitor.Exit(_configLock); // Release the lock configLockTaken = false; } _isWithinConversionTimes = _isEngineConnected = _isEngineCrashed = _isEngineRunning = _isEngineSuspended = false; // We are no longer connected _pipeProxy = null; // We should start over } try // Try to reconnect { ChannelFactory<ICore> pipeFactory; string serverString; Ini tempIni = new Ini(GlobalDefs.TempSettingsFile); string remoteServerName = tempIni.ReadString("Engine", "RemoteServerName", GlobalDefs.MCEBUDDY_SERVER_NAME); int remoteServerPort = tempIni.ReadInteger("Engine", "RemoteServerPort", int.Parse(GlobalDefs.MCEBUDDY_SERVER_PORT)); if (serverInfoLbl.InvokeRequired) // Cannot access UI elements directly from a non UI thread serverInfoLbl.Invoke(new MethodInvoker(delegate { serverInfoLbl.Text = "Engine: " + remoteServerName + " Port: " + remoteServerPort.ToString(); })); else serverInfoLbl.Text = "Engine: " + remoteServerName + " Port: " + remoteServerPort.ToString(); // If it's LOCALHOST, we use local NAMED PIPE else network SOAP WEB SERVICES if (remoteServerName == GlobalDefs.MCEBUDDY_SERVER_NAME) { // local NAMED PIPE serverString = GlobalDefs.MCEBUDDY_LOCAL_NAMED_PIPE; NetNamedPipeBinding npb = new NetNamedPipeBinding(); TimeSpan timeoutPeriod = GlobalDefs.PIPE_TIMEOUT; npb.OpenTimeout = npb.CloseTimeout = npb.SendTimeout = npb.ReceiveTimeout = timeoutPeriod; npb.TransferMode = TransferMode.Buffered; npb.MaxReceivedMessageSize = npb.MaxBufferPoolSize = npb.MaxBufferSize = Int32.MaxValue; npb.ReaderQuotas = XmlDictionaryReaderQuotas.Max; pipeFactory = new ChannelFactory<ICore>(npb, new EndpointAddress(serverString)); } else { // network SOAP WEB SERVICES serverString = GlobalDefs.MCEBUDDY_WEB_SOAP_PIPE; serverString = serverString.Replace(GlobalDefs.MCEBUDDY_SERVER_NAME, remoteServerName); // Update the Server Name with that from the config file serverString = serverString.Replace(GlobalDefs.MCEBUDDY_SERVER_PORT, remoteServerPort.ToString(CultureInfo.InvariantCulture)); // Update the Server Port with that from the config file BasicHttpBinding ntb = new BasicHttpBinding(GlobalDefs.MCEBUDDY_PIPE_SECURITY); TimeSpan timeoutPeriod = GlobalDefs.PIPE_TIMEOUT; ntb.OpenTimeout = ntb.CloseTimeout = ntb.SendTimeout = ntb.ReceiveTimeout = timeoutPeriod; ntb.TransferMode = TransferMode.Buffered; ntb.MaxReceivedMessageSize = ntb.MaxBufferPoolSize = ntb.MaxBufferSize = Int32.MaxValue; ntb.ReaderQuotas = XmlDictionaryReaderQuotas.Max; pipeFactory = new ChannelFactory<ICore>(ntb, new EndpointAddress(serverString)); } // Increase the max objects allowed in serialization channel otherwise we lose the connection when there more than 5K objects in the queue foreach (OperationDescription operation in pipeFactory.Endpoint.Contract.Operations) { DataContractSerializerOperationBehavior dataContractBehavior = operation.Behaviors[typeof(DataContractSerializerOperationBehavior)] as DataContractSerializerOperationBehavior; if (dataContractBehavior != null) dataContractBehavior.MaxItemsInObjectGraph = Int32.MaxValue; } Monitor.Enter(_configLock, ref configLockTaken); ICore tempPipeProxy = pipeFactory.CreateChannel(); MCEBuddyConf.GlobalMCEConfig = new MCEBuddyConf(tempPipeProxy.GetConfigParameters()); // Get the parameters from the Engine we are connected to _jobs = tempPipeProxy.GetAllJobsInQueueStatus(); _isEngineRunning = tempPipeProxy.EngineRunning(); _isEngineSuspended = tempPipeProxy.IsSuspended(); _isEngineCrashed = tempPipeProxy.EngineCrashed(); _isWithinConversionTimes = tempPipeProxy.WithinConversionTimes(); GlobalDefs.profilesSummary = tempPipeProxy.GetProfilesSummary(); // Get a list of all the profiles and descriptions GlobalDefs.engineProcessorCount = tempPipeProxy.GetProcessorCount(); // Number of processors on the engine machine try // special case, ShowAnalyzer sometimes hangs the system and fails to respond, ignore and continue with log { GlobalDefs.showAnalyzerInstalled = tempPipeProxy.ShowAnalyzerInstalled(); // Check if Showanalyzer is installed } catch (Exception e) { Log.WriteSystemEventLog("Unable to get status of ShowAnalyzer, please reinstall ShowAnalyzer and reboot the system. Error " + e.ToString(), EventLogEntryType.Error); MessageBox.Show(Localise.GetPhrase("ShowAnalyzer is not responding. Please the reboot system or reinstall ShowAnalyzer."), Localise.GetPhrase("ShowAnalyzer Not Responding"), MessageBoxButtons.OK, MessageBoxIcon.Error); } Monitor.Exit(_configLock); configLockTaken = false; // all good again, update the status _pipeProxy = tempPipeProxy; _versionMismatch = false; // set after pipeProxy _isEngineConnected = true; } catch (Exception e) { // Check for version mistmatch if ((e.InnerException is System.Runtime.Serialization.SerializationException) || (e is System.ServiceModel.ActionNotSupportedException)) _versionMismatch = true; else _versionMismatch = false; //WriteEventLog(Localise.GetPhrase("MCEBuddy GUI: Unable to create pipe to MCEBuddy service"), EventLogEntryType.Warning); //not required since this call will fail when MCEBuddy Service has not started and will overflow the eventlog. The message will be shown on the GUI if (configLockTaken) { Monitor.Exit(_configLock); configLockTaken = false; } _isWithinConversionTimes = _isEngineConnected = _isEngineCrashed = _isEngineRunning = _isEngineSuspended = false; // Still broken _pipeProxy = null; // Last item Thread.Sleep(_connectPeriod * (WindowState == FormWindowState.Minimized ? GlobalDefs.GUI_MINIMIZED_ENGINE_POLL_SLOW_FACTOR : 1)); // Try to connect every x seconds } if (_isEngineConnected) // SetUserLocale should never be called from within a try catch statement since it can cause an exception if there is an application restart SetUserLocale(); // ReInitialize MCEBuddy (we may have been disconnected and parameters changed, connected to a new engine etc) } }
private void paypalButton_Click(object sender, EventArgs e) { Ini configIni = new Ini(GlobalDefs.TempSettingsFile); string donationLink = configIni.ReadString("Engine", "DonationLink", ""); if (String.IsNullOrWhiteSpace(donationLink)) donationLink = Crypto.Decrypt(GlobalDefs.MCEBUDDY_HOME_PAGE); // backup OpenLink(donationLink); }
/// <summary> /// Remux the source recorded file using the specified base parameters, but also check if the source file (or Remux file) has any zero channel audio streams in it. /// If so it tries to remux using FFMPEG and the given parameters but compensating to keep only one audio channel /// It also checks to see if it can locate the user identified audio language within the source recorded file if required /// </summary> /// <param name="baseRemuxParams">Base parameters used for remuxing</param> /// <param name="FPS">Frame rate for the Recorded file</param> /// <param name="fixRemuxedFile">True if Remuxed file file needs to be FIXED for Zero Channel Audio, false if Recorded file needs to be checked and then fixed</param> /// <param name="ffmpeg">Point to the last executed ffmpeg object (used for analysis) since this is a recursive reentrant function, we need to keep track</param> /// <returns>True if there are no zero channel audio tracks or on a successful remux</returns> private bool FFMPEGRemuxZeroChannelFix(string baseRemuxParams, float FPS, bool fixRemuxedFile, ref FFmpeg ffmpeg) { bool autoFPS = false; // Used to check if Auto FPS was used _jobLog.WriteEntry(this, "Verifying " + (fixRemuxedFile ? "Remuxed" : "Recorded") + " file audio streams for Zero Channel Audio", Log.LogEntryType.Debug); // Compensate for FFMPEG bug #2227 where the mjpeg is identified as a video stream hence breaking -map 0:v, rather replace 'v' with the actual video stream number if (_RecordingFileMediaInfo.Success && !_RecordingFileMediaInfo.ParseError) { if (baseRemuxParams.Contains("-map 0:v")) { if (_RecordingFileMediaInfo.MediaInfo.VideoInfo.Stream != -1) // Check if we have a file with only audio streams baseRemuxParams = baseRemuxParams.Replace("-map 0:v", "-map 0:" + _RecordingFileMediaInfo.MediaInfo.VideoInfo.Stream.ToString(CultureInfo.InvariantCulture)); // replace 0:v with the actual video stream number else { _jobLog.WriteEntry(this, "No Video stream detected in original file, removing support for video stream selection", Log.LogEntryType.Warning); baseRemuxParams = baseRemuxParams.Replace("-map 0:v", ""); // remove 0:v since we have no video stream } } // Check of the original file has no audio, then remove support for audio mappings if (_RecordingFileMediaInfo.AudioTracks < 1) { _jobLog.WriteEntry(this, "No Audio stream detected in original file, removing support for audio stream selection", Log.LogEntryType.Warning); baseRemuxParams = baseRemuxParams.Replace("-map 0:a", ""); // remove 0:v since we have no video stream } } else { _jobLog.WriteEntry(this, "Error reading audio and video streams, removing support for audio and video stream selection", Log.LogEntryType.Warning); baseRemuxParams = Regex.Replace(baseRemuxParams, @"-map 0:[\S]*", ""); // Remove all patterns like -map 0:v or -map 0:4 since we cannot read ffmpeg stream info } // We have a 0 channel audio we try to compensate for it by selecting the appropriate audio channel if (CheckForNoneOrZeroChannelAudioTrack((fixRemuxedFile ? RemuxedFile : _RecordingFile), _jobStatus, _jobLog)) { _jobLog.WriteEntry(this, Localise.GetPhrase("Found 0 channel audio while remuxing, re-remuxing using a single audio track"), Log.LogEntryType.Information); _jobStatus.CurrentAction = Localise.GetPhrase("Re-ReMuxing due to audio error"); // DO NOT USE MAP ALL commands, we only need to copy one audio and one video stream baseRemuxParams = Regex.Replace(baseRemuxParams, @"-map 0:[\S]*", ""); // Remove all patterns like -map 0:v or -map 0:4 if (_RecordingFileMediaInfo.Success && !_RecordingFileMediaInfo.ParseError) { // Audio parameters - find the best Audio channel for the selected language or best audio track if there are imparired tracks otherwise by default the encoder will select the best audio channel (encoders do not do a good job of ignoring imparired tracks) bool selectedTrack = false; int audioChannels = 0; int audioStream = -1; int videoStream = -1; bool selectedAudioImpaired = false; if ((!String.IsNullOrEmpty(_requestedAudioLanguage) || (_RecordingFileMediaInfo.ImpariedAudioTrackCount > 0)) && (_RecordingFileMediaInfo.AudioTracks > 1)) // More than 1 audio track to choose from and either we have a language match request or a presence of an imparied channel (likely no audio) { for (int i = 0; i < _RecordingFileMediaInfo.AudioTracks; i++) { bool processTrack = false; // By default we don't need to process // Language selection check, if the user has picked a specific language code, look for it // If we find a match, we look the one with the highest number of channels in it if (!String.IsNullOrEmpty(_requestedAudioLanguage)) { if ((_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Language.ToLower() == _requestedAudioLanguage) && (_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels > 0)) { if (selectedTrack) { if (!( // take into account impaired tracks (since impaired tracks typically have no audio) ((_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels > audioChannels) && !_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Impaired) || // PREFERENCE to non-imparied Audio tracks with the most channels ((_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels > audioChannels) && selectedAudioImpaired) || // PREFERENCE to Audio tracks with most channels if currently selected track is impaired (!_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Impaired && selectedAudioImpaired) // PREFER non impaired audio over currently selected impaired )) continue; // we have found a lang match, now we are looking for more channels only now } processTrack = true; // All conditions met, we need to process this track } } else if (_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels > 0)// we have a imparied audio track, select the non impaired track with the highest number of tracks or bitrate or frequency { if (selectedTrack) { if (!( // take into account impaired tracks (since impaired tracks typically have no audio) ((_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels > audioChannels) && !_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Impaired) || // PREFERENCE to non-imparied Audio tracks with the most channels ((_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels > audioChannels) && selectedAudioImpaired) || // PREFERENCE to Audio tracks with most channels if currently selected track is impaired (!_RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Impaired && selectedAudioImpaired) // PREFER non impaired audio over currently selected impaired )) continue; // we have found a lang match, now we are looking for more channels only now } processTrack = true; // All conditions met, we need to process this track } if (processTrack) // We need to process this track { audioChannels = _RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Channels; audioStream = _RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Stream; // store the stream number for the selected audio channel string audioCodec = _RecordingFileMediaInfo.MediaInfo.AudioInfo[i].AudioCodec; int audioTrack = i; // Store the audio track number we selected string audioLanguage = _RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Language.ToLower(); // this is what we selected selectedAudioImpaired = _RecordingFileMediaInfo.MediaInfo.AudioInfo[i].Impaired; // Is this an imparied track? videoStream = _RecordingFileMediaInfo.MediaInfo.VideoInfo.Stream; // Store the video information selectedTrack = true; // We found a suitable track if (!String.IsNullOrEmpty(_requestedAudioLanguage)) _jobLog.WriteEntry(this, Localise.GetPhrase("Found Audio Language match for language") + " " + _requestedAudioLanguage.ToUpper() + ", " + Localise.GetPhrase("Audio Stream") + " " + audioStream.ToString(CultureInfo.InvariantCulture) + ", " + Localise.GetPhrase("Audio Track") + " " + audioTrack.ToString(CultureInfo.InvariantCulture) + ", " + Localise.GetPhrase("Channels") + " " + audioChannels.ToString(CultureInfo.InvariantCulture) + ", " + Localise.GetPhrase("Codec") + "->" + audioCodec + ", Audio Impaired->" + selectedAudioImpaired.ToString(), Log.LogEntryType.Debug); else _jobLog.WriteEntry(this, Localise.GetPhrase("Compensating for audio impaired tracks, selected track with language") + " " + _requestedAudioLanguage.ToUpper() + ", " + Localise.GetPhrase("Audio Stream") + " " + audioStream.ToString(CultureInfo.InvariantCulture) + ", " + Localise.GetPhrase("Audio Track") + " " + audioTrack.ToString(CultureInfo.InvariantCulture) + ", " + Localise.GetPhrase("Channels") + " " + audioChannels.ToString(CultureInfo.InvariantCulture) + ", " + Localise.GetPhrase("Codec") + "->" + audioCodec + ", Audio Impaired->" + selectedAudioImpaired.ToString(), Log.LogEntryType.Debug); } } } // If we have a found a suitable language, select it else let FFMPEG select it automatically if (selectedTrack) { if (audioStream != -1) baseRemuxParams += " -map 0:" + audioStream.ToString(CultureInfo.InvariantCulture); // Select the Audiotrack we had isolated earlier if (videoStream != -1) baseRemuxParams += " -map 0:" + videoStream.ToString(CultureInfo.InvariantCulture); // Check if we have a video stream } else _jobLog.WriteEntry(this, "No audio language match found, letting encoder choose best audio language", Log.LogEntryType.Warning); } else _jobLog.WriteEntry(this, "Error reading audio streams, letting encoder choose best audio language", Log.LogEntryType.Warning); } // Build the command line to remux the file now string ffmpegParams = ""; // Check for auto frame rate and replace with video framerate if (baseRemuxParams.Contains("-r auto")) { if (FPS > 0) { _jobLog.WriteEntry(this, "Detected Auto FPS request, setting frame rate to " + FPS.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug); baseRemuxParams = baseRemuxParams.Replace("-r auto", "-r " + FPS.ToString(CultureInfo.InvariantCulture)); autoFPS = true; } else { _jobLog.WriteEntry(this, Localise.GetPhrase("Cannot read frame rate from file, skipping frame rate adjustment"), Log.LogEntryType.Warning); baseRemuxParams = baseRemuxParams.Replace("-r auto", ""); // no framerate since we can't read it autoFPS = false; } } else autoFPS = false; // Check for input file placeholders and substitute if (baseRemuxParams.Contains("-i <source>")) // Check if we have a input file placeholder (useful if we need to put commands before -i) ffmpegParams = "-y " + baseRemuxParams.Replace("-i <source>", "-i " + Util.FilePaths.FixSpaces(_RecordingFile) + " ") + " " + Util.FilePaths.FixSpaces(RemuxedFile); else ffmpegParams = "-y -i " + Util.FilePaths.FixSpaces(_RecordingFile) + " " + baseRemuxParams + " " + Util.FilePaths.FixSpaces(RemuxedFile); // DO NOT USE -async 1 with COPY FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(RemuxedFile), false, out ffmpeg); // Run ffmpeg and check for common errors, we will get back the final executed ffmpeg object which we can then check for errors/analyze if (!ffmpeg.Success || !RemuxedFileOK()) //check of file is created, outputhandler reports success (Percentage not requires since Success is more accurate) { // last ditch effort, try to fix for all errors if (ffmpeg.Success && !fixRemuxedFile) // avoid infinite loop, fix remuxed file only if we started out checking the recorded file { if (!FFMPEGRemuxZeroChannelFix(baseRemuxParams, FPS, true, ref ffmpeg)) // Call ZeroChannelAudioFix this time to fix the remuxed file { _jobLog.WriteEntry(Localise.GetPhrase("0 Channel ReMux using FFMPEG failed at") + " " + _jobStatus.PercentageComplete.ToString(CultureInfo.InvariantCulture) + "%", Log.LogEntryType.Error); Util.FileIO.TryFileDelete(RemuxedFile); return false; } } else { Util.FileIO.TryFileDelete(RemuxedFile); return false; // Avoid infinite loop, we are done here, nothing can be done, failed... } } else if (fixRemuxedFile) // If we are in the loop fixing the remuxed file, we did good, return now - don't process further yet return true; // Remux succeeded, check for Dropped or Duplicate packets due to incorrect FPS _jobLog.WriteEntry("Average rate of dropped frames :" + " " + ffmpeg.AverageDropROC.ToString("#0.00", CultureInfo.InvariantCulture), Log.LogEntryType.Debug); _jobLog.WriteEntry("Average rate of duplicate frames :" + " " + ffmpeg.AverageDupROC.ToString("#0.00", CultureInfo.InvariantCulture), Log.LogEntryType.Debug); // Read ReMux Parameters from Config Profile Ini configProfileIni = new Ini(GlobalDefs.ConfigFile); string profile = "FFMpegBackupRemux"; // This is where the Fallback Remux parameters are stored // Read the Drop frame threshhold double dropThreshold; double.TryParse(configProfileIni.ReadString(profile, "RemuxDropThreshold", "3.0"), System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out dropThreshold); // Read the Duplicate frame threshhold double dupThreshold; double.TryParse(configProfileIni.ReadString(profile, "RemuxDuplicateThreshold", "3.0"), System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out dupThreshold); if ((ffmpeg.AverageDropROC > dropThreshold) || (ffmpeg.AverageDupROC > dupThreshold)) { if (autoFPS) // Check if we used AutoFPS and also if this isn't a going into an infinite loop _jobLog.WriteEntry(Localise.GetPhrase("Remuxed file has too many dropped or duplicate frames, try to manually set the frame rate. Auto FPS used ->") + " " + FPS.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Warning); else _jobLog.WriteEntry(Localise.GetPhrase("Remuxed file has too many dropped or duplicate frames, check/set the manual remux frame rate"), Log.LogEntryType.Warning); } // Since we are successful, keep track of how many seconds we skipped while remuxing the file FFMpegMEncoderParams checkParams = new FFMpegMEncoderParams(baseRemuxParams); float.TryParse(checkParams.ParameterValue("-ss"), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out skipInitialSeconds); return true; // All done here }
private void ReadMonitorSettings(Ini configIni) { // Read the Monitor Tasks string[] searchRecords = configIni.ReadString("Engine", "SearchRecords", "").Split(','); foreach (string searchRecord in searchRecords) { if (String.IsNullOrEmpty(searchRecord)) continue; MonitorJobOptions mjo = new MonitorJobOptions(); mjo.taskName = searchRecord; mjo.searchPath = configIni.ReadString(searchRecord, "SearchPath", ""); CheckPathEnding(ref mjo.searchPath); mjo.searchPattern = configIni.ReadString(searchRecord, "SearchPattern", GlobalDefs.DEFAULT_VIDEO_STRING); mjo.searchPattern = mjo.searchPattern.Replace(GlobalDefs.DEFAULT_VIDEO_STRING, GlobalDefs.DEFAULT_VIDEO_FILE_TYPES); mjo.monitorSubdirectories = configIni.ReadBoolean(searchRecord, "MonitorSubdirectories", true); mjo.monitorConvertedFiles = configIni.ReadBoolean(searchRecord, "MonitorConvertedFiles", false); mjo.reMonitorRecordedFiles = configIni.ReadBoolean(searchRecord, "ReMonitorRecordedFiles", false); mjo.domainName = configIni.ReadString(searchRecord, "DomainName", ""); mjo.userName = configIni.ReadString(searchRecord, "UserName", "Guest"); mjo.password = configIni.ReadString(searchRecord, "Password", ""); if (!String.IsNullOrEmpty(mjo.password)) mjo.password = Crypto.Decrypt(mjo.password); // Password is kept as encrypted mceBuddyConfSettings.monitorTasks.Add(mjo); // Add the Monitor Task object } }
private bool CheckUnsupported(string profile, string tool) { _jobLog.WriteEntry(this, ("Checking for Unsupported profile for container / codec combination"), Log.LogEntryType.Information); Ini ini = new Ini(GlobalDefs.ProfileFile); string unsupportedCombinations = ini.ReadString(profile, tool + "-unsupported", "").ToLower().Trim(); if (unsupportedCombinations != "") { List<string> videoItems = new List<string>(); // Use properties for ORIGINAL video (not remuxed file, since the user is referring to original video properties, remuxed is always MPEG2 and TS) // Derive all possible combinations from the source video (video, audio and extension) videoItems.Add(_videoFile.OriginalVideoFFMPEGStreamInfo.MediaInfo.VideoInfo.VideoCodec); foreach (MediaInfo.Audio audioInfo in _videoFile.OriginalVideoFFMPEGStreamInfo.MediaInfo.AudioInfo) // check all audio codecs found videoItems.Add(audioInfo.AudioCodec); videoItems.Add(_videoFile.OriginalFileExtension.ToLower().Replace(".", "")); AddCombinations(ref videoItems); // Get the unsupported list and check to see if there are any matches string[] unsupported = unsupportedCombinations.Split(','); foreach (string combo in unsupported) { string c = combo.ToLower().Trim(); if (videoItems.Contains(c)) { _jobLog.WriteEntry(this, ("Unsupported profile for container / codec combination") + " " + c + " " + profile, Log.LogEntryType.Warning); _unsupported = true; return true; } } } return false; }
private void ReadConversionSettings(Ini configIni) { // Read the Conversion Tasks string[] conversionRecords = configIni.ReadString("Engine", "Tasks", "").Split(','); foreach (string conversionRecord in conversionRecords) { if (String.IsNullOrEmpty(conversionRecord)) continue; ConversionJobOptions cjo = new ConversionJobOptions(); cjo.taskName = conversionRecord; cjo.profile = configIni.ReadString(conversionRecord, "Profile", ""); cjo.destinationPath = configIni.ReadString(conversionRecord, "DestinationPath", ""); CheckPathEnding(ref cjo.destinationPath); cjo.workingPath = configIni.ReadString(conversionRecord, "WorkingPath", ""); CheckPathEnding(ref cjo.workingPath); cjo.fallbackToSourcePath = configIni.ReadBoolean(conversionRecord, "FallbackDestination", false); cjo.autoIncrementFilename = configIni.ReadBoolean(conversionRecord, "AutoIncrementFilename", false); cjo.skipReprocessing = configIni.ReadBoolean(conversionRecord, "SkipReprocessing", false); cjo.checkReprocessingHistory = configIni.ReadBoolean(conversionRecord, "CheckReprocessingHistory", false); cjo.addToiTunes = configIni.ReadBoolean(conversionRecord, "AddToiTunesLibrary", false); cjo.addToWMP = configIni.ReadBoolean(conversionRecord, "AddToWMPLibrary", false); cjo.maxWidth = configIni.ReadInteger(conversionRecord, "MaxWidth", 720); cjo.FPS = configIni.ReadString(conversionRecord, "FPS", ""); cjo.renameBySeries = configIni.ReadBoolean(conversionRecord, "RenameBySeries", true); cjo.altRenameBySeries = configIni.ReadBoolean(conversionRecord, "AltRenameBySeries", false); cjo.customRenameBySeries = configIni.ReadString(conversionRecord, "CustomRenameBySeries", ""); cjo.renameOnly = configIni.ReadBoolean(conversionRecord, "RenameOnly", false); cjo.fileSelection = configIni.ReadString(conversionRecord, "FileSelection", ""); cjo.metaShowSelection = configIni.ReadString(conversionRecord, "MetaSelection", ""); cjo.metaNetworkSelection = configIni.ReadString(conversionRecord, "MetaChannelSelection", ""); string monitorNameList = configIni.ReadString(conversionRecord, "MonitorTaskNames", ""); if (String.IsNullOrWhiteSpace(monitorNameList)) cjo.monitorTaskNames = null; // list should be empty if nothing is there else cjo.monitorTaskNames = monitorNameList.Split(','); cjo.audioLanguage = configIni.ReadString(conversionRecord, "AudioLanguage", ""); string audioOffsetStr = configIni.ReadString(conversionRecord, "AudioOffset", "0"); double.TryParse(audioOffsetStr, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out cjo.audioOffset); cjo.drc = configIni.ReadBoolean(conversionRecord, "DRC", true); cjo.stereoAudio = configIni.ReadBoolean(conversionRecord, "StereoAudio", true); cjo.encoderSelectBestAudioTrack = configIni.ReadBoolean(conversionRecord, "EncoderSelectBestAudioTrack", true); cjo.autoDeInterlace = configIni.ReadBoolean(conversionRecord, "AutoDeInterlace", true); cjo.preferHardwareEncoding = configIni.ReadBoolean(conversionRecord, "PreferHardwareEncoding", true); cjo.startTrim = configIni.ReadInteger(conversionRecord, "StartTrim", 0); cjo.endTrim = configIni.ReadInteger(conversionRecord, "EndTrim", 0); cjo.insertQueueTop = configIni.ReadBoolean(conversionRecord, "InsertQueueTop", false); cjo.extractXML = configIni.ReadBoolean(conversionRecord, "ExtractXML", false); cjo.writeMetadata = configIni.ReadBoolean(conversionRecord, "WriteMetadata", true); cjo.disableCropping = configIni.ReadBoolean(conversionRecord, "DisableCropping", false); cjo.commercialSkipCut = configIni.ReadBoolean(conversionRecord, "TaskCommercialSkipCut", false); cjo.skipCopyBackup = configIni.ReadBoolean(conversionRecord, "SkipCopyBackup", false); cjo.skipRemuxing = configIni.ReadBoolean(conversionRecord, "SkipRemux", false); cjo.ignoreCopyProtection = configIni.ReadBoolean(conversionRecord, "IgnoreCopyProtection", false); cjo.tivoMAKKey = configIni.ReadString(conversionRecord, "TiVOMAKKey", ""); cjo.downloadSeriesDetails = configIni.ReadBoolean(conversionRecord, "DownloadSeriesDetails", true); cjo.downloadBanner = configIni.ReadBoolean(conversionRecord, "DownloadBanner", true); cjo.enabled = configIni.ReadBoolean(conversionRecord, "Enabled", true); cjo.extractCC = configIni.ReadString(conversionRecord, "ExtractCC", ""); cjo.embedSubtitlesChapters = configIni.ReadBoolean(conversionRecord, "EmbedSubtitlesChapters", false); cjo.prioritizeOriginalBroadcastDateMatch = configIni.ReadBoolean(conversionRecord, "PrioritizeOriginalBroadcastDateMatch", false); string ccOffsetStr = configIni.ReadString(conversionRecord, "CCOffset", GlobalDefs.DefaultCCOffset); double.TryParse(ccOffsetStr, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out cjo.ccOffset); string qualityMultiplierStr = configIni.ReadString(conversionRecord, "QualityMultiplier", "1"); double.TryParse(qualityMultiplierStr, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out cjo.qualityMultiplier); if (cjo.qualityMultiplier <= 0.01) cjo.qualityMultiplier = 0.01F; if (cjo.qualityMultiplier > 4) cjo.qualityMultiplier = 4F; string volumeMultiplierStr = configIni.ReadString(conversionRecord, "VolumeMultiplier", "0"); double.TryParse(volumeMultiplierStr, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out cjo.volumeMultiplier); if (cjo.volumeMultiplier <= -20) cjo.volumeMultiplier = -20F; //-10db minimum if (cjo.volumeMultiplier > 60) cjo.volumeMultiplier = 60F; //30db max string metaShowType = configIni.ReadString(conversionRecord, "MetaShowTypeSelection", ShowType.Default.ToString()); if (String.Compare(metaShowType, ShowType.Movie.ToString(), true) == 0) cjo.metaShowTypeSelection = ShowType.Movie; else if (String.Compare(metaShowType, ShowType.Series.ToString(), true) == 0) cjo.metaShowTypeSelection = ShowType.Series; else if (String.Compare(metaShowType, ShowType.Sports.ToString(), true) == 0) cjo.metaShowTypeSelection = ShowType.Sports; else cjo.metaShowTypeSelection = ShowType.Default; string drmType = configIni.ReadString(conversionRecord, "MetaDRMTypeSelection", DRMType.All.ToString()); if (String.Compare(drmType, DRMType.Protected.ToString(), true) == 0) cjo.metaDRMSelection = DRMType.Protected; else if (String.Compare(drmType, DRMType.Unprotected.ToString(), true) == 0) cjo.metaDRMSelection = DRMType.Unprotected; else cjo.metaDRMSelection = DRMType.All; string showType = configIni.ReadString(conversionRecord, "ForceShowType", ShowType.Default.ToString()); if (String.Compare(showType, ShowType.Movie.ToString(), true) == 0) cjo.forceShowType = ShowType.Movie; else if (String.Compare(showType, ShowType.Series.ToString(), true) == 0) cjo.forceShowType = ShowType.Series; else if (String.Compare(showType, ShowType.Sports.ToString(), true) == 0) cjo.forceShowType = ShowType.Sports; else cjo.forceShowType = ShowType.Default; string commercialRemovalStr = configIni.ReadString(conversionRecord, "CommercialRemoval", CommercialRemovalOptions.Comskip.ToString()); if (String.Compare(commercialRemovalStr, CommercialRemovalOptions.Comskip.ToString(), true) == 0) cjo.commercialRemoval = CommercialRemovalOptions.Comskip; else if (String.Compare(commercialRemovalStr, CommercialRemovalOptions.ShowAnalyzer.ToString(), true) == 0) cjo.commercialRemoval = CommercialRemovalOptions.ShowAnalyzer; else cjo.commercialRemoval = CommercialRemovalOptions.None; cjo.comskipIni = configIni.ReadString(conversionRecord, "ComskipINI", ""); cjo.domainName = configIni.ReadString(conversionRecord, "DomainName", ""); cjo.userName = configIni.ReadString(conversionRecord, "UserName", "Guest"); cjo.password = configIni.ReadString(conversionRecord, "Password", ""); if (!String.IsNullOrEmpty(cjo.password)) cjo.password = Crypto.Decrypt(cjo.password); // Password is kept as encrypted int metaCorrectionsCount = configIni.ReadInteger(conversionRecord, "MetaCorrectionsCount", 0); if (metaCorrectionsCount < 1) cjo.metadataCorrections = null; else { cjo.metadataCorrections = new ConversionJobOptions.MetadataCorrectionOptions[metaCorrectionsCount]; for (int i = 0; i < metaCorrectionsCount; i++) // The entries are kept in their own section, easier to manage { cjo.metadataCorrections[i] = new ConversionJobOptions.MetadataCorrectionOptions(); cjo.metadataCorrections[i].originalTitle = configIni.ReadString(conversionRecord + "-MetaCorrectionEntries", "OriginalTitle" + i.ToString(), ""); cjo.metadataCorrections[i].correctedTitle = configIni.ReadString(conversionRecord + "-MetaCorrectionEntries", "CorrectedTitle" + i.ToString(), ""); cjo.metadataCorrections[i].tvdbSeriesId = configIni.ReadString(conversionRecord + "-MetaCorrectionEntries", "TVDBSeriesId" + i.ToString(), ""); cjo.metadataCorrections[i].imdbSeriesId = configIni.ReadString(conversionRecord + "-MetaCorrectionEntries", "IMDBSeriesId" + i.ToString(), ""); } } mceBuddyConfSettings.conversionTasks.Add(cjo); // Add the Monitor Task object } }
/// <summary> /// Checks if the converted files need to be kept in sync with source files. It deletes the converted file if the source file is deleted. /// This function is thread safe /// </summary> /// <param name="useRecycleBin">True to use Recycle Bin while deleting</param> public void SyncConvertedFiles(bool useRecycleBin) { try { Monitor.Enter(_monitorSyncFiles); // Make this thread safe // Build a list of files to be monitored from the History file to sync source with converted files Ini historyIni = new Ini(GlobalDefs.HistoryFile); try { List<string> convertedFiles = historyIni.GetSectionNames(); // Get list of all files (includes converted and source) foreach (string foundFile in convertedFiles) { if (!_monitorSyncFiles.Contains(foundFile)) // We just keep building the list here and check if they are deleted later { _monitorSyncFiles.Add(foundFile); // add to the list Log.AppLog.WriteEntry(this, "File " + foundFile + " is being monitored for syncing with output file", Log.LogEntryType.Debug); } } } catch (Exception e) { Log.AppLog.WriteEntry(this, "Unable to get History section names", Log.LogEntryType.Error, true); Log.AppLog.WriteEntry(this, "Error -> " + e.ToString(), Log.LogEntryType.Error, true); } // Check if the sources files are deleted and delete the converted files foreach (string sourceFile in new List<string>(_monitorSyncFiles)) // Create a new list to iterate through else it throws an exception when we modify it { if (File.Exists(sourceFile)) continue; // Source file still exists, nothing to do // Source file no longer, exists, it has been deleted // Check if the file has been converted and if so then get the location of the converted file from the history file int convCount = 0; string convFile = ""; while ((convFile = historyIni.ReadString(sourceFile, "ConvertedTo" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), "")) != "") { // 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(convFile), Path.GetFileNameWithoutExtension(convFile) + supportFileExt); // support file if (File.Exists(extFile)) // don't overburden the log Log.AppLog.WriteEntry(this, "Source File " + sourceFile + " deleted, deleting converted support file " + extFile, Log.LogEntryType.Debug); FileIO.TryFileDelete(extFile, useRecycleBin); // Delete support file } if (File.Exists(convFile)) // don't overburden the log Log.AppLog.WriteEntry(this, "Source File " + sourceFile + " deleted, deleting converted file " + convFile, Log.LogEntryType.Debug); FileIO.TryFileDelete(convFile, useRecycleBin); // Try to delete the converted file since the source file is deleted DeleteParentDirectoryChainIfEmpty(convFile); // Delete the parent directory chain if empty for the converted file convCount++; } } Monitor.Exit(_monitorSyncFiles); } catch (Exception e) // Incase the thread terminates, release the lock and exit gracefully { // Release the queue lock if taken try { Monitor.Exit(_monitorSyncFiles); } // Incase it's taken release it, if not taken it will throw an exception catch { } Log.AppLog.WriteEntry(this, "Sync Converted Files terminated", Log.LogEntryType.Warning, true); Log.AppLog.WriteEntry(this, "Error -> " + e.ToString(), Log.LogEntryType.Warning, true); } }
/// <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); } } } }
public List<string[]> GetProfilesSummary() { List<string[]> profileSummary = new List<string[]>(); // Open and read all profiles Ini profileIni = new Ini(GlobalDefs.ProfileFile); foreach (string profile in profileIni.GetSectionNames()) profileSummary.Add(new String[] { profile, profileIni.ReadString(profile, "Description", "") }); // 2 array string -> Profile Name, Description return profileSummary; }
private void WriteHistory(ConversionJob job) { Ini historyIni = new Ini(GlobalDefs.HistoryFile); GeneralOptions go = MCEBuddyConf.GlobalMCEConfig.GeneralOptions; bool sendEMail = go.sendEmail; // do we need to send an eMail after each job bool sendSuccess = go.eMailSettings.successEvent; bool sendFailed = go.eMailSettings.failedEvent; bool sendCancelled = go.eMailSettings.cancelledEvent; bool skipBody = go.eMailSettings.skipBody; string sendSuccessSubject = go.eMailSettings.successSubject; string sendFailedSubject = go.eMailSettings.failedSubject; string sendCancelledSubject = go.eMailSettings.cancelledSubject; string result = "Converted"; if (job.Status.Error) result = "Error"; if (job.Status.Cancelled) result = "Cancelled"; // Cancelled should be the last status to set because an error can be set if is cancelled int convCount = 0; while (historyIni.ReadString(job.OriginalFileName, result + "At" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), "") != "") convCount++; historyIni.Write(job.OriginalFileName, result + "At" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), DateTime.Now.ToString("s", System.Globalization.CultureInfo.InvariantCulture)); switch (result) { case "Converted": { if (job.Status.SuccessfulSkipConversion) Log.AppLog.WriteEntry(this, "Job for " + job.OriginalFileName + " Skipped ReConverting successfully using Conversion Task " + job.TaskName + " and Profile " + job.Profile, Log.LogEntryType.Information, true); else Log.AppLog.WriteEntry(this, "Job for " + job.OriginalFileName + " Converted successfully using Conversion Task " + job.TaskName + " and Profile " + job.Profile, Log.LogEntryType.Information, true); historyIni.Write(job.OriginalFileName, "ConvertedTo" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.ConvertedFile); historyIni.Write(job.OriginalFileName, "ConvertedUsingProfile" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.Profile); historyIni.Write(job.OriginalFileName, "ConvertedUsingTask" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.TaskName); historyIni.Write(job.OriginalFileName, "ConvertedStart" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.ConversionStartTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture)); //Ensure converted files are not then re-converted during a scan historyIni.Write(job.ConvertedFile, "Status", "OutputFromConversion"); // Status indicates destination file is output of an conversion, if the same file is added back for reconversion then it would log as converted if (job.Status.SuccessfulSkipConversion) historyIni.Write(job.ConvertedFile, "SkipConvertedToOutputAt" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.ConversionEndTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture)); else historyIni.Write(job.ConvertedFile, "ConvertedToOutputAt" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.ConversionEndTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture)); // Send an eMail if required if (sendEMail && sendSuccess) { string subject = Localise.GetPhrase("MCEBuddy successfully converted a video"); string message = Localise.GetPhrase("Source Video") + " -> " + job.OriginalFileName + "\r\n"; message += Localise.GetPhrase("Converted Video") + " -> " + job.ConvertedFile + "\r\n"; message += Localise.GetPhrase("Profile") + " -> " + job.Profile + "\r\n"; message += Localise.GetPhrase("Conversion Task") + " -> " + job.TaskName + "\r\n"; message += Localise.GetPhrase("Converted At") + " -> " + job.ConversionEndTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture) + "\r\n"; message += Localise.GetPhrase("Time taken to convert") + " (hh:mm) -> " + (job.ConversionEndTime - job.ConversionStartTime).Hours.ToString("00") + ":" + (job.ConversionEndTime - job.ConversionStartTime).Minutes.ToString("00") + "\r\n"; // Check for custom subject and process if (!String.IsNullOrWhiteSpace(sendSuccessSubject)) subject = UserCustomParams.CustomParamsReplace(sendSuccessSubject, job.WorkingPath, Path.GetDirectoryName(job.ConvertedFile), job.ConvertedFile, job.OriginalFileName, "", "", "", job.Profile, job.TaskName, job.MetaData, Log.AppLog); eMailSendEngine.AddEmailToSendQueue(subject, (skipBody ? "" : message)); // Send the eMail through the email engine } } break; case "Error": { Log.AppLog.WriteEntry(this, "Job for " + job.OriginalFileName + " has Error " + job.Status.ErrorMsg + " using Conversion Task " + job.TaskName + " and Profile " + job.Profile, Log.LogEntryType.Information, true); historyIni.Write(job.OriginalFileName, "ErrorMessage" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.Status.ErrorMsg); historyIni.Write(job.OriginalFileName, "ErrorUsingProfile" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.Profile); historyIni.Write(job.OriginalFileName, "ErrorUsingTask" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.TaskName); // Send an eMail if required if (sendEMail && sendFailed) { string subject = Localise.GetPhrase("MCEBuddy failed to converted a video"); 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"; message += Localise.GetPhrase("Error") + " -> " + job.Status.ErrorMsg + "\r\n"; message += Localise.GetPhrase("Failed At") + " -> " + job.ConversionEndTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture) + "\r\n"; // Check for custom subject and process if (!String.IsNullOrWhiteSpace(sendFailedSubject)) subject = UserCustomParams.CustomParamsReplace(sendFailedSubject, job.WorkingPath, "", job.ConvertedFile, job.OriginalFileName, "", "", "", job.Profile, job.TaskName, job.MetaData, Log.AppLog); eMailSendEngine.AddEmailToSendQueue(subject, (skipBody ? "" : message)); // Send the eMail through the eMail engine } } break; case "Cancelled": { Log.AppLog.WriteEntry(this, "Job for " + job.OriginalFileName + " Cancelled using Conversion Task " + job.TaskName + " and Profile " + job.Profile, Log.LogEntryType.Information, true); historyIni.Write(job.OriginalFileName, "CancelledUsingProfile" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.Profile); historyIni.Write(job.OriginalFileName, "CancelledUsingTask" + convCount.ToString(System.Globalization.CultureInfo.InvariantCulture), job.TaskName); // Send an eMail if required if (sendEMail && sendCancelled) { string subject = Localise.GetPhrase("MCEBuddy cancelled a video conversion"); 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"; message += Localise.GetPhrase("Cancelled At") + " -> " + DateTime.Now.ToString("s", System.Globalization.CultureInfo.InvariantCulture) + "\r\n"; // It can be cancelled before it starts so end time is not a good indicator // Check for custom subject and process if (!String.IsNullOrWhiteSpace(sendCancelledSubject)) subject = UserCustomParams.CustomParamsReplace(sendCancelledSubject, job.WorkingPath, "", job.ConvertedFile, job.OriginalFileName, "", "", "", job.Profile, job.TaskName, job.MetaData, Log.AppLog); eMailSendEngine.AddEmailToSendQueue(subject, (skipBody ? "" : message)); // Send the eMail through the eMail engine } } break; default: Log.AppLog.WriteEntry(this, "INVALID STATE (" + result + ") -> Job for " + job.OriginalFileName + " Converted using Conversion Task " + job.TaskName + " and Profile " + job.Profile, Log.LogEntryType.Error, true); break; } if ( _queueManager.JobCount(job.OriginalFileName) <= 1) { // This is the last job processing this file, so write the status to the history file STATUS historyIni.Write(job.OriginalFileName, "Status", result); // Source file status } }
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 }
/// <summary> /// Returns the extension of the RenameExt command for the profile used /// </summary> /// <param name="profile">Name of the profile to check</param> /// <returns>Extension as indicated by the profile or an empty string if none exists</returns> private static string GetRenameFileExtension(string profile) { Ini profileIni = new Ini(GlobalDefs.ProfileFile); string renameExt = profileIni.ReadString(profile, "RenameExt", ""); if (String.IsNullOrEmpty(renameExt)) return ""; if (renameExt[0] != '.') renameExt = "." + renameExt; // Just in case someone does something dumb like forget the leading "." return renameExt; // return the extension }
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> /// Remux the DVRMS file directly to TSMPEG using a special FFMPEG /// </summary> /// <returns>Success or Failure</returns> private bool RemuxDVRMSFFmpeg() { string profile = "DVRMSRemux"; // Reading the remux parameters from the file string ffmpegParams = ""; Ini configProfileIni = new Ini(GlobalDefs.ConfigFile); // Threads 0 causes an error in some streams, avoid _jobLog.WriteEntry(this, Localise.GetPhrase("DVRMS file, using special FFMPEG to remux"), Log.LogEntryType.Information); // First try with regular FFMPEG and GenPTs, since the DVRMS tends to created corrupted files _jobStatus.CurrentAction = Localise.GetPhrase("Fast Remuxing"); _jobLog.WriteEntry(this, Localise.GetPhrase("Reading DVRMS Remux parameters"), Log.LogEntryType.Information); // First try to copy all the streams directly (read parmeters for copy profile) string baseRemuxParams = configProfileIni.ReadString(profile, "Remux", ""); if (String.IsNullOrWhiteSpace(baseRemuxParams)) // Have we used up all the CopyRemux profiles, then we're done here - try something else { _jobLog.WriteEntry(Localise.GetPhrase("DVRMS Remuxing disabled in config file, no parameters"), Log.LogEntryType.Error); Util.FileIO.TryFileDelete(RemuxedFile); return false; } // Check for input file placeholders and substitute if (baseRemuxParams.Contains("-i <source>")) // Check if we have a input file placeholder (useful if we need to put commands before -i) { baseRemuxParams = baseRemuxParams.Replace("-i <source>", "-i " + Util.FilePaths.FixSpaces(_RecordingFile) + " "); ffmpegParams = "-fflags +genpts -y " + baseRemuxParams + " " + Util.FilePaths.FixSpaces(RemuxedFile); } else ffmpegParams = "-fflags +genpts -y -i " + Util.FilePaths.FixSpaces(_RecordingFile) + " " + baseRemuxParams + " " + Util.FilePaths.FixSpaces(RemuxedFile); // DO NOT USE -async 1 with COPY FFmpeg ffmpeg; FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(RemuxedFile), false, out ffmpeg); // Run and handle errors, don't need to check output file here if (!ffmpeg.Success || !RemuxedFileOK()) //check of file is created, outputhandler reports success (Percentage not requires since Success is more accurate) { // Use Special build of DVRMS FFMPEG to handle now _jobLog.WriteEntry(Localise.GetPhrase("DVRMS ReMux using FFMPEG GenPTS failed at") + " " + _jobStatus.PercentageComplete.ToString(CultureInfo.InvariantCulture) + "%. Retrying using special DVRMS FFMpeg", Log.LogEntryType.Warning); // DVR-MS supports only one audio stream ffmpegParams = ffmpegParams.Replace("-fflags +genpts", ""); // don't need genpts for special build dvrms ffmpeg ffmpeg = new FFmpeg(ffmpegParams, true, _jobStatus, _jobLog); // use SPECIAL BUILD DVRMS-FFMPEG, so we can't use FFMpegExecuteAndHandleErrors ffmpeg.Run(); if (!ffmpeg.Success || !RemuxedFileOK()) //check of file is created, outputhandler reports success (Percentage not requires since Success is more accurate) { _jobLog.WriteEntry(Localise.GetPhrase("DVRMS ReMux using FFMPEG failed at") + " " + _jobStatus.PercentageComplete.ToString(CultureInfo.InvariantCulture) + "%", Log.LogEntryType.Error); Util.FileIO.TryFileDelete(RemuxedFile); return false; } } // Since we are successful, keep track of how many seconds we skipped while remuxing the file FFMpegMEncoderParams checkParams = new FFMpegMEncoderParams(baseRemuxParams); float.TryParse(checkParams.ParameterValue("-ss"), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out skipInitialSeconds); return true; }
private string _renameConvertedFileWithOriginalName = ""; // Keep track incase of filename conflict protected ConvertBase(ConversionJobOptions conversionOptions, string tool, VideoInfo videoFile, JobStatus jobStatus, Log jobLog, Scanner commercialScan) : base (true) { //Setup log and status _jobStatus = jobStatus; _jobLog = jobLog; //Set the destination paths _workingPath = conversionOptions.workingPath; Util.FilePaths.CreateDir(_workingPath); // Check first up to see if the source video uses an unsupported combination for this profile // Container, Video Codec, Audio Codec and whether it was originally a Media Center recording or not _videoFile = videoFile; _commercialScan = commercialScan; if (CheckUnsupported(conversionOptions.profile, tool)) return; // Set the input params and get the standard settings _maxWidth = conversionOptions.maxWidth; _userQuality = conversionOptions.qualityMultiplier; _volume = conversionOptions.volumeMultiplier; _drc = conversionOptions.drc; _startTrim = conversionOptions.startTrim; _endTrim = conversionOptions.endTrim; _encoderChooseBestAudioTrack = conversionOptions.encoderSelectBestAudioTrack; _fps = conversionOptions.FPS; _preferHardwareEncoding = conversionOptions.preferHardwareEncoding; Ini ini = new Ini(GlobalDefs.ProfileFile); // Profile override parameters - if default (i.e. does not exist then use conversion options else use profile parameters) if (ini.ReadString(conversionOptions.profile, "2ChannelAudio", "default") == "default") _2ChannelAudio = conversionOptions.stereoAudio; else _2ChannelAudio = ini.ReadBoolean(conversionOptions.profile, "2ChannelAudio", false); // Fix output to 2 channels (from profile) if (ini.ReadString(conversionOptions.profile, "SkipCropping", "default") == "default") _skipCropping = conversionOptions.disableCropping; else _skipCropping = ini.ReadBoolean(conversionOptions.profile, "SkipCropping", false); // Cropping can be forced in the profile if (ini.ReadString(conversionOptions.profile, "AutoDeinterlace", "default") == "default") _autoDeInterlacing = conversionOptions.autoDeInterlace; else _autoDeInterlacing = ini.ReadBoolean(conversionOptions.profile, "AutoDeinterlace", false); if (conversionOptions.renameOnly) _commercialSkipCut = true; //no cutting if we are renaming only else if (ini.ReadString(conversionOptions.profile, "CommercialSkipCut", "default") == "default") _commercialSkipCut = conversionOptions.commercialSkipCut; else _commercialSkipCut = ini.ReadBoolean(conversionOptions.profile, "CommercialSkipCut", false); // Profile only parameters _fixedResolution = ini.ReadBoolean(conversionOptions.profile, "FixedResolution", false); _2Pass = ini.ReadBoolean(conversionOptions.profile, "2pass", false); _generalParams = ini.ReadString(conversionOptions.profile, tool + "-general", ""); _videoParams = ini.ReadString(conversionOptions.profile, tool + "-video", ""); _audioParams = ini.ReadString(conversionOptions.profile, tool + "-audio", ""); _extension = _videoFile.Extension = ini.ReadString(conversionOptions.profile, tool + "-ext", "").ToLower().Trim(); if (string.IsNullOrWhiteSpace(_extension)) // Special case copy converter if there is no specified extension, it will be using the source file extension _extension = FilePaths.CleanExt(SourceVideo); _remuxTo = _videoFile.RemuxTo = ini.ReadString(conversionOptions.profile, tool + "-remuxto", "").ToLower().Trim(); _audioDelay = ini.ReadString(conversionOptions.profile, tool + "-audiodelay", "skip").ToLower().Trim(); if (_audioDelay == "auto") // Use the audio delay specified in the file _toolAudioDelay = videoFile.AudioDelay; else if (_audioDelay != "skip") double.TryParse(_audioDelay, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _toolAudioDelay); if (conversionOptions.audioOffset != 0) // Conversion options Audio Delay takes priority over profile Audio Delay _toolAudioDelay = conversionOptions.audioOffset; // Audio select the AC3 audio option if the source video has AC3) if (((videoFile.AudioCodec == "ac-3") || (videoFile.AudioCodec == "ac3") || (videoFile.AudioCodec != "e-ac-3") || (videoFile.AudioCodec != "eac3")) && (ini.ReadString(conversionOptions.profile, tool + "-audioac3", "") != "")) _audioParams = ini.ReadString(conversionOptions.profile, tool + "-audioac3", _audioParams); // E-AC3 test option if the source video has E-AC3 - Not required as mencoder can handle eac3 audio /* if (videoFile.AudioCodec == "e-ac-3" || _videoFile.AudioCodec != "eac3") { _audioParams = ini.ReadString(conversionOptions.profile, tool + "-audioeac3", _audioParams); if ((_audioParams == "") && (tool == "mencoder")) _audioParams = "-noaudio "; }*/ // Important to use temp name while converting - sometimes the sources files are copied to the working directory and the converted files conflict with teh original filename, compensate. E.g. TS file input, TS file output with Copy converter _convertedFile = Path.Combine(_workingPath, Path.GetFileNameWithoutExtension(SourceVideo) + "-converted" + _extension); _renameConvertedFileWithOriginalName = Path.Combine(_workingPath, Path.GetFileNameWithoutExtension(SourceVideo) + _extension); }
private void facebookBtn_Click(object sender, EventArgs e) { Ini configIni = new Ini(GlobalDefs.TempSettingsFile); string facebookLink = configIni.ReadString("Engine", "FacebookLink", ""); if (String.IsNullOrWhiteSpace(facebookLink)) facebookLink = Crypto.Decrypt(GlobalDefs.MCEBUDDY_FACEBOOK_PAGE); // backup OpenLink(facebookLink); }