예제 #1
0
        public static PlexObject MetadataFromFile(string fileName, bool waitWindow = true, bool silent = false)
        {
            try
            {
                //there are two file-types: the legacy PMXML format and the new PXZ format
                var ext = Path.GetExtension(fileName);

                //decide which is which
                switch (ext)
                {
                case @".pxz":     //must be decompressed and processed first
                    return(MetadataFromFile(LoadMetadataArchive(fileName, waitWindow), waitWindow, silent));

                case @".pmxml":     //can be directly loaded and deserialised
                    var doc = new XmlDocument();
                    doc.LoadXml(File.ReadAllText(fileName));
                    return(FromXml(doc));

                default:
                    return(null);
                }
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, @"XmlMetadataLoadError");
            }

            //default
            return(null);
        }
예제 #2
0
        public static void SetRawTable(this DataSet data, DataTable RawData)
        {
            try
            {
                //if the dataset is null, then initialise it
                if (data == null)
                {
                    data = new DataSet(@"AutoFill");
                }

                //remove it if it already exists
                if (data.Tables.Contains(@"RawData"))
                {
                    data.Tables.Remove(@"RawData");
                }

                //copy the structure to avoid inheritance problems
                var toAdd = RawData.Copy();

                //table name fix
                toAdd.TableName = @"RawData";
                toAdd.Namespace = @"DataProvider";

                //commit the new table to the DataSet
                data.Tables.Add(toAdd);
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, @"SetRawDataError");
            }
        }
예제 #3
0
        public static int GetDaysOld(string filePath)
        {
            try
            {
                if (!File.Exists(filePath))
                {
                    return(0);
                }

                var fileCreation = File.GetCreationTime(filePath);
                var now          = DateTime.Now;
                var days         = (int)(now - fileCreation).TotalDays +
                                   1; //adding one ensures if it was created on the same day it counts as one day passed

                LoggingHelpers.RecordCacheEvent($"Requested XML record is {days} day(s) old", $"file://{filePath}");

                return(days);
                //default value
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "CacheAgeChkError");
                return(0);
            }
        }
예제 #4
0
        public static bool CheckCacheExpiry(string filePath, int interval)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    var days = GetDaysOld(filePath);
                    //DEBUG ONLY
                    //UIMessages.Info(days.ToString());
                    var result     = days >= interval;
                    var logMessage = $"XML record {(result ? @"has" : @"has not")} expired [{days}/{interval}]";
                    LoggingHelpers.RecordCacheEvent(logMessage, $"file://{filePath}");
                    return(result);
                }

                LoggingHelpers.RecordException(@"Specified cache file doesn't exist", @"CacheExpiryChkError");
                //default is true; this signifies that it has expired, so PlexDL will try and get a new copy.
                return(true);
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, @"CacheExpiryChkError");
                //default is true; this signifies that it has expired, so PlexDL will try and get a new copy.
                return(true);
            }
        }
예제 #5
0
        private void Startup()
        {
            try
            {
                //reset counter
                RefreshCount = 0;

                //poll rate GUI setup
                UpdatePollRate();

                //initial value refresh
                DoRefresh();

                //start the automatic refresh timer
                tmrAutoRefresh.Start();
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, @"DebugStartupError");

                //inform the user
                UIMessages.Error($@"Debug monitor startup error: {ex.Message}");
            }
        }
예제 #6
0
        public static bool CommitDefaultSettings(this ApplicationOptions settings, bool waitWindow = true)
        {
            try
            {
                if (waitWindow)
                {
                    return((bool)WaitWindow.WaitWindow.Show(CommitDefaultSettings, @"Saving settings", settings));
                }
                else
                {
                    //write all new settings
                    var protectedFile = new ProtectedFile(SettingsFile);
                    protectedFile.WriteAllText(settings.ProfileToXml(), ProtectedSettings);

                    return(true);
                }
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, @"CommitToDefaultError");
            }

            //default
            return(false);
        }
예제 #7
0
        public static DataTable AttributesFromObject(object content, bool silent = false)
        {
            var table = new DataTable();

            try
            {
                var contentType = content.GetType();
                var moviesType  = typeof(PlexMovie);
                var musicType   = typeof(PlexMusic);
                var tvShowType  = typeof(PlexTvShow);

                if (contentType == moviesType)
                {
                    table = MovieAttributesFromObject((PlexMovie)content, silent);
                }
                else if (contentType == musicType)
                {
                    table = MusicAttributesFromObject((PlexMusic)content, silent);
                }
                else if (contentType == tvShowType)
                {
                    table = TvAttributesFromObject((PlexTvShow)content, silent);
                }
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "AttributeTableError");
                if (!silent)
                {
                    UIMessages.Error("Error occurred whilst building content attribute table:\n\n" + ex, @"Data Error");
                }
            }

            return(table);
        }
예제 #8
0
        private void BtnCopy_Click(object sender, EventArgs e)
        {
            try
            {
                //update the button text
                btnCopy.Text = @"Copied!";

                //change the state for the timer
                CopyState = true;

                //save it to the clipboard, but only if the link is valid
                //i.e. not whitespace/null
                if (!string.IsNullOrWhiteSpace(Link))
                {
                    Clipboard.SetText(Link);
                }

                //restart the text change timer
                tmrBtnTxtUpdate.Stop();
                tmrBtnTxtUpdate.Start();
            }
            catch (Exception ex)
            {
                //log the error and then ignore it
                LoggingHelpers.RecordException(ex.Message, @"LinkCopyError");
            }
        }
예제 #9
0
        private void DoCancel()
        {
            try
            {
                if (!AlreadyClosing && !ChangesApplied)
                {
                    //revert changes by assigning the snapshot taken at the start to the main setting
                    //provider.
                    ObjectProvider.Settings = Snapshot;

                    //return of 'Cancel'
                    DialogResult = DialogResult.Cancel;

                    //disable further calls
                    AlreadyClosing = true;

                    //close the form
                    Close();
                }
            }
            catch (Exception ex)
            {
                //record error
                LoggingHelpers.RecordException(ex.Message, @"SettingsDialogCancelError");
            }
        }
예제 #10
0
        private void InitialSetup()
        {
            try
            {
                //metrics information must be valid to proceed
                if (Metrics != null)
                {
                    //caching data location
                    UpdateCachingDirectory();

                    //apply data
                    dgvMain.DataSource = GetMetrics();
                }
                else
                {
                    UIMessages.Warning(@"Metrics information was not configured on launch; data failed to load.");
                }
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, "CacheMetricsLoadError");

                //inform the user
                UIMessages.Error("There was an error whilst loading caching metrics:\n\n" + ex,
                                 @"Load Error");

                //exit the form
                Close();
            }
        }
예제 #11
0
        /// <summary>
        /// Attempts to load PlexDL's '.default' file and apply the settings contained within
        /// </summary>
        public static void TryLoadDefaultSettings()
        {
            try
            {
                //check if default settings have been created
                if (DefaultSettingsManager.SettingsExist)
                {
                    //try and load it with no messages
                    var defaultProfile = DefaultSettingsManager.LoadDefaultSettings();

                    //if it isn't null, then assign it to the global settings
                    if (defaultProfile != null)
                    {
                        ObjectProvider.Settings = defaultProfile;
                    }
                }
                else
                {
                    //create the file with no messages
                    new ApplicationOptions().CommitDefaultSettings();
                }
            }
            catch (Exception ex)
            {
                //log and ignore the error
                LoggingHelpers.RecordException(ex.Message, @"LoadDefaultProfileError");
            }
        }
예제 #12
0
        /// <summary>
        /// Attempts to run PlexDL in 'Open With' mode
        /// </summary>
        /// <param name="file"></param>
        /// <param name="appRun"></param>
        public static void OpenWith(string file, bool appRun = true)
        {
            //Windows has passed a file; we need to check what type it is
            var ext = Path.GetExtension(file);

            //check if it's a supported file-type
            if (CheckAgainstSupportedFiles(file))
            {
                //try the metadata import and then show it if successful
                try
                {
                    var metadata = MetadataIO.MetadataFromFile(file);
                    if (metadata != null)
                    {
                        UIUtils.RunMetadataWindow(metadata, appRun);
                    }
                    else
                    {
                        UIMessages.Error(@"Metadata parse failed; null result.");
                    }
                }
                catch (Exception ex)
                {
                    LoggingHelpers.RecordException(ex.Message, @"StartupLoadPxz");
                    UIMessages.Error($"Error occurred whilst loading PXZ file:\n\n{ex}");
                }
            }
            else
            {
                UIMessages.Error(@"PlexDL doesn't recognise this file-type: '" + ext + @"'",
                                 @"Validation Error");
            }
        }
예제 #13
0
파일: Cast.cs 프로젝트: mitchscobell/PlexDL
        private async void BtnPlayPause_Click(object sender, EventArgs e)
        {
            try
            {
                if (PlayState)
                {
                    //send pause command
                    await Controller.Pause();

                    //set UI
                    btnPlayPause.Text = @"Play";

                    //set flag
                    PlayState = false;
                }
                else
                {
                    //send play command
                    await Controller.Play();

                    //set UI
                    btnPlayPause.Text = @"Pause";

                    //set flag
                    PlayState = true;
                }
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, @"CastPlayStateError");
                UIMessages.Error($"An error occurred whilst trying to play/pause your media:\n\n{ex}");
            }
        }
예제 #14
0
        public static string FpsFromPlexStd(string std, bool includeSuffix = true)
        {
            var fps = "29.97"; //default NTSC

            try
            {
                foreach (var s in PlexFramerates)
                {
                    if (!string.Equals(s[0], std))
                    {
                        continue;
                    }

                    fps = s[1];
                    break;
                }

                if (includeSuffix)
                {
                    fps = FullFpsSuffix(fps);
                }
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "FpsFromStdError");
            }

            return(fps);
        }
예제 #15
0
        private void SetInterfaceViewingStatus()
        {
            try
            {
                //check for nulls
                if (ObjectProvider.PlexServers != null && dgvServers.DataSource != null)
                {
                    //data length counters
                    var gridCount  = dgvServers.Rows.Count;
                    var totalCount = ObjectProvider.PlexServers.Count;

                    //the total can't exceed the current amount
                    lblViewingValue.Text = totalCount < gridCount
                        ? $@"{gridCount}/{totalCount}"
                        : $@"{totalCount}/{totalCount}";
                }
                else
                {
                    //set the viewing values to 0
                    lblViewingValue.Text = @"0/0";
                }
            }
            catch (Exception ex)
            {
                //log the error but don't inform the user
                LoggingHelpers.RecordException(ex.Message, @"ServerManagerViewingStatusError");
            }
        }
예제 #16
0
        /// <summary>
        /// This method ensures the directory structure required is created and ready for use
        /// </summary>
        public static void CacheStructureBuilder()
        {
            //root caching directory for the current user
            var rootUserDir = $@"{CachingFileDir.RootCacheDirectory}\" +
                              $@"{MD5Helper.CalculateMd5Hash(ObjectProvider.Settings.ConnectionInfo.PlexAccountToken)}\" +
                              $@"{MD5Helper.CalculateMd5Hash(ObjectProvider.Settings.ConnectionInfo.PlexAddress)}";

            //root directory where all images are stored
            var thumbDir = $@"{rootUserDir}\{CachingFileDir.ThumbRelativeDirectory}";

            //root directory where all XML files are stored
            var xmlDir = $@"{rootUserDir}\{CachingFileDir.XmlRelativeDirectory}";

            try
            {
                //ensure the images directory has been created
                if (!Directory.Exists(thumbDir))
                {
                    Directory.CreateDirectory(thumbDir);
                }

                //ensure the XML directory has been created
                if (!Directory.Exists(xmlDir))
                {
                    Directory.CreateDirectory(xmlDir);
                }
            }
            catch (Exception ex)
            {
                //log the error and exit
                LoggingHelpers.RecordException(ex.Message, "CacheDirBuildError");
            }
        }
예제 #17
0
        private void DoLoad()
        {
            try
            {
                //validate queue
                if (QueueProvider != null)
                {
                    //go through each QueueElement
                }
                else
                {
                    //log the error
                    LoggingHelpers.RecordException(@"Null HTTP Queue provider", @"DownloadManagerLoadError");

                    //alert user
                    UIMessages.Warning(@"Null HTTP Queue provider was specified to the Download Manager; couldn't load the form correctly.");

                    //close the form
                    Close();
                }
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, @"DownloadManagerLoadError");

                //alert the user
                UIMessages.Error($"Error in download manager load:\n\n{ex}");

                //close the form
                Close();
            }
        }
예제 #18
0
        private void BtnTranslate_Click(object sender, EventArgs e)
        {
            try
            {
                //are both LogDel file name storage locations valid?
                if (!string.IsNullOrWhiteSpace(txtLogdel.Text) && !string.IsNullOrWhiteSpace(ofdLogdel.FileName))
                {
                    //reset progress bar
                    pbMain.Maximum = TotalCount;
                    pbMain.Value   = 0;

                    //setup background worker event handlers
                    bwTranslate.RunWorkerCompleted += BwTranslate_RunWorkerCompleted;
                    bwTranslate.ProgressChanged    += BwTranslate_ProgressChanged;

                    //start processing in the background
                    bwTranslate.RunWorkerAsync();
                }
                else
                {
                    //alert the user
                    UIMessages.Error(@"Incorrect value(s)");
                }
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, @"TokenTranslatorProcessingError");

                //alert the user
                UIMessages.Error(ex.ToString());
            }
        }
예제 #19
0
파일: LinkViewer.cs 프로젝트: VRCMG/PlexDL
        private void GenerateQRCode(bool waitWindow = true)
        {
            if (waitWindow)
            {
                WaitWindow.WaitWindow.Show(GenerateQRCode, @"Generating code");
            }
            else
            {
                try
                {
                    //code generation handler
                    var codeImage = new QRProvider(Link);

                    //generate code
                    if (codeImage.Fetch())
                    {
                        //apply new image if not null
                        if (codeImage.CodeImage != null)
                        {
                            picQRCode.BackgroundImage = codeImage.CodeImage;
                        }
                    }
                }
                catch (Exception ex)
                {
                    //log error
                    LoggingHelpers.RecordException(ex.Message, @"GenerateQRCodeError");
                }
            }
        }
예제 #20
0
        private void SetInterfaceCurrentLog(string currentLog)
        {
            try
            {
                //null check encompasses the validity
                var isValid = !string.IsNullOrWhiteSpace(currentLog);

                //just the file name (in-case we get given a path)
                var fileName = Path.GetFileName(currentLog);

                //set the fore colour dependent on status
                lblCurrentLogFileValue.ForeColor = isValid
                    ? Color.Black
                    : Color.DarkRed;

                //set text dependent on status
                lblCurrentLogFileValue.Text = isValid
                    ? fileName
                    : @"Not Loaded";
            }
            catch (Exception ex)
            {
                //log the error but don't inform the user
                LoggingHelpers.RecordException(ex.Message, @"LogViewerCurrentLogStatusError");
            }
        }
예제 #21
0
        private void DoReset()
        {
            try
            {
                //query user
                if (UIMessages.Question(@"Are you sure? This will clear all settings in the current session."))
                {
                    //do the reset
                    ObjectProvider.Settings = new ApplicationOptions();

                    //refresh PropertyGrid on this form
                    settingsGrid.SelectedObject = ObjectProvider.Settings;
                    settingsGrid.Refresh();

                    //show alert
                    UIMessages.Info(@"Settings reset");
                }
            }
            catch (Exception ex)
            {
                //record error
                LoggingHelpers.RecordException(ex.Message, @"ResetSettingsError");

                //alert user
                UIMessages.Error($"Error while resetting\n\n{ex}");
            }
        }
예제 #22
0
        private static void DoCommitDefault()
        {
            try
            {
                //null validation
                if (ObjectProvider.Settings != null)
                {
                    //returns true if the commit operation succeeded
                    if (ObjectProvider.Settings.CommitDefaultSettings())
                    {
                        //alert user
                        UIMessages.Info(@"Successfully saved settings");
                    }
                    else
                    {
                        //alert user
                        UIMessages.Error(@"An unknown error occurred whilst saving settings");
                    }
                }
                else
                {
                    //alert user
                    UIMessages.Error(@"Couldn't export settings because they were null");
                }
            }
            catch (Exception ex)
            {
                //record error
                LoggingHelpers.RecordException(ex.Message, @"SaveDefaultError");

                //alert user
                UIMessages.Error($"Error exporting to default\n\n{ex}");
            }
        }
예제 #23
0
        private void BtnStartSearch_Click(object sender, EventArgs e)
        {
            try
            {
                if (!string.IsNullOrEmpty(txtSearchTerm.Text) &&
                    cbxSearchColumn.SelectedItem != null &&
                    cbxSearchRule.SelectedIndex >= 0)
                {
                    //the result is OK, close the form
                    DialogResult = DialogResult.OK;
                    Close();
                }
                else
                {
                    //alert the user to the validation error
                    UIMessages.Error(@"Please enter all required values or exit the search",
                                     @"Validation Error");
                }
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, @"SearchFormStartSearchError");

                //alert the user
                UIMessages.Error(ex.ToString());
            }
        }
예제 #24
0
        public static Bitmap GetImageFromUrl(string url, bool forceNoCache = false)
        {
            try
            {
                CachingHelpers.CacheStructureBuilder();
                if (string.IsNullOrEmpty(url))
                {
                    return(Resources.unavailable);
                }

                if (!forceNoCache)
                {
                    if (ThumbCaching.ThumbInCache(url))
                    {
                        return(ThumbCaching.ThumbFromCache(url));
                    }
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                LoggingHelpers.RecordException(ex.Message, "ThumbIOAccessError");
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "ImageFetchError");
                return(Resources.unavailable);
            }

            return(ForceImageFromUrl(url));
        }
예제 #25
0
파일: Cast.cs 프로젝트: mitchscobell/PlexDL
        private async Task StopApplication()
        {
            try
            {
                //set UI
                btnCast.Enabled      = false;
                btnCast.Text         = @"Stopping";
                btnDiscover.Enabled  = true;
                btnPlayPause.Enabled = false;
                btnPlayPause.Text    = @"Play";

                //kill the application
                await Controller.StopApplication();

                //disconnect
                if (Service.ConnectedChromecast != null)
                {
                    await Client.DisconnectChromecast();
                }

                //restore UI
                btnCast.Enabled = true;
                btnCast.Text    = @"Cast";

                //set flags
                ConnectState = false;
                PlayState    = false;
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, @"CastStopError");
            }
        }
예제 #26
0
 private void ItmCommitToDefault_Click(object sender, EventArgs e)
 {
     try
     {
         if (ObjectProvider.Settings != null)
         {
             if (ObjectProvider.Settings.CommitDefaultSettings())
             {
                 UIMessages.Info(@"Successfully saved settings");
             }
             else
             {
                 UIMessages.Error(@"An unknown error occurred whilst saving settings");
             }
         }
         else
         {
             UIMessages.Error(@"Couldn't export settings because they were null");
         }
     }
     catch (Exception ex)
     {
         LoggingHelpers.RecordException(ex.Message, @"SaveDefaultError");
         UIMessages.Error($"Error exporting to default\n\n{ex}");
     }
 }
예제 #27
0
        private void Cast_Load(object sender, EventArgs e)
        {
            try
            {
                //setup service events
                if (Service != null)
                {
                    Service.ChromeCastClient.ConnectedChanged += Client_Connected;
                }

                //setup form title
                lblTitle.Text = StreamingContent.StreamInformation.ContentTitle;

                //setup the poster
                picPoster.BackgroundImage = ImageHandler.GetPoster(StreamingContent);
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, @"CastUILoadError");

                //alert the user
                UIMessages.Error($"Error occurred during cast load:\n\n{ex}");
            }
        }
예제 #28
0
        private static void DoDataBind(DataGridView target, DataTable bindData, GenericRenderStruct info)
        {
            try
            {
                //check if the DataGridView needs to be invoked first
                if (target.InvokeRequired)
                {
                    //invoke the DataGridView so we don't thread-lock
                    target.BeginInvoke((MethodInvoker) delegate { DoDataBind(target, bindData, info); });
                }
                else
                {
                    //we don't need to invoke, so just continue without it.
                    //bind the data to the grid ("render" the data)
                    target.DataSource = bindData;

                    //set the captions
                    Methods.SetHeaderText(target, info.Data);

                    //re-render the control
                    target.Refresh();
                }
            }
            catch (Exception ex)
            {
                //log and do nothing
                LoggingHelpers.RecordException(ex.Message, @"GenericViewRendererBindError");
            }
        }
예제 #29
0
        public static DataTable TvAttributesFromObject(PlexTvShow content, bool silent = false)
        {
            var table = new DataTable("TvAttributes");
            var columnAttributeName  = new DataColumn("Name", typeof(string));
            var columnAttributeValue = new DataColumn("Value");

            table.Columns.AddRange(
                new[]
            {
                columnAttributeName,
                columnAttributeValue
            });
            try
            {
                var season        = new[] { "Season Name", content.Season };
                var episodeCount  = new[] { "Episode Count", content.EpisodesInSeason.ToString() };
                var seasonNumber  = new[] { "Season #", content.SeasonNumber.ToString() };
                var episodeNumber = new[] { "Episode #", content.EpisodeNumber.ToString() };
                var genre         = new[] { "Genre", content.ContentGenre };
                var runtime       = new[]
                { "Runtime", Methods.CalculateTime(content.StreamInformation.ContentDuration) };
                var resolution = new[] { "Resolution", content.StreamResolution.ResolutionString() };
                var frameRate  = new[] { "Frame-rate", FormatFramerate(content) };
                var size       = new[] { "File size", Methods.FormatBytes(content.StreamInformation.ByteLength) };
                var container  = new[] { "Container", content.StreamInformation.Container };

                var newRows = new[]
                {
                    season,
                    episodeCount,
                    seasonNumber,
                    episodeNumber,
                    genre,
                    runtime,
                    resolution,
                    frameRate,
                    size,
                    container
                };

                foreach (object[] row in newRows)
                {
                    table.Rows.Add(row);
                }
            }
            catch (Exception ex)
            {
                LoggingHelpers.RecordException(ex.Message, "AttributeTableError");
                if (!silent)
                {
                    UIMessages.Error("Error occurred whilst building content attribute table:\n\n" + ex, @"Data Error");
                }
            }

            return(table);
        }
예제 #30
0
        private async Task StopApplication()
        {
            try
            {
                //multi-threaded
                if (InvokeRequired)
                {
                    BeginInvoke((MethodInvoker) delegate
                    {
                        StopApplication().GetAwaiter().GetResult();
                    });
                }
                else
                {
                    //set UI
                    btnCast.Enabled      = false;
                    btnDiscover.Enabled  = true;
                    btnPlayPause.Enabled = false;
                    btnCast.Text         = @"Stopping";
                    btnPlayPause.Text    = @"Play";

                    //kill the application
                    if (Controller != null)
                    {
                        await Controller.StopApplication();
                    }

                    //disconnect
                    if (Service?.ConnectedChromecast != null)
                    {
                        if (Service.ChromeCastClient != null)
                        {
                            await Service.ChromeCastClient.DisconnectChromecast();
                        }
                    }

                    //restore UI
                    btnCast.Enabled = true;
                    btnCast.Text    = @"Cast";

                    //set flags
                    ConnectState = false;
                    PlayState    = false;
                }
            }
            catch (Exception ex)
            {
                //log the error
                LoggingHelpers.RecordException(ex.Message, @"CastStopError");

                //alert the user
                UIMessages.Error($"Error occurred whilst stopping the application:\n\n{ex}");
            }
        }