Exemplo n.º 1
0
        private ActionOutcome ReplaceMultipartFile()
        {
            ShowConfiguration si = Episode?.Show ?? SelectedShow;

            //We will replace the file as too difficult to update multiparts
            //We can't use XDocument as it's not fully valid XML
            List <XElement> episodeXmLs = new List <XElement>();

            if (Episode != null)
            {
                foreach (Episode ep in Episode.SourceEpisodes)
                {
                    XElement epNode = new XElement("episodedetails");
                    UpdateEpisodeFields(ep, si, epNode, true);
                    episodeXmLs.Add(epNode);
                }
            }

            try
            {
                using (StreamWriter writer = File.CreateText(Where.FullName))
                {
                    foreach (XElement ep in episodeXmLs)
                    {
                        writer.WriteLine(ep);
                    }
                }
            }
            catch (IOException e)
            {
                return(new ActionOutcome(e));
            }

            return(ActionOutcome.Success());
        }
Exemplo n.º 2
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            //if the directory is the root download folder do not delete
            if (TVSettings.Instance.MonitorFolders &&
                TVSettings.Instance.DownloadFolders.Contains(toRemove.FullName))
            {
                return(new ActionOutcome($@"Not removing {toRemove.FullName} as it is a Search Folder"));
            }

            try
            {
                if (toRemove.Exists)
                {
                    DeleteOrRecycleFolder(toRemove);
                    if (Tidyup != null && Tidyup.DeleteEmpty)
                    {
                        LOGGER.Info($"Testing {toRemove.Parent.FullName } to see whether it should be tidied up");
                        DoTidyUp(toRemove.Parent);
                    }
                }
                return(ActionOutcome.Success());
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 3
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                byte[]? theData = si.Provider == TVDoc.ProviderType.TheTVDB
                    ? TheTVDB.LocalCache.Instance.GetTvdbDownload(path)
                    : HttpHelper.Download(path, false);

                if (theData is null || theData.Length == 0)
                {
                    return(new ActionOutcome("Unable to download " + path));
                }

                if (shrinkLargeMede8ErImage)
                {
                    theData = ConvertBytes(theData);
                }

                System.IO.FileStream fs = new System.IO.FileStream(destination.FullName, System.IO.FileMode.Create);
                fs.Write(theData, 0, theData.Length);
                fs.Close();
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }

            return(ActionOutcome.Success());
        }
Exemplo n.º 4
0
        private ActionOutcome UpdateFile()
        {
            //We will replace the file as too difficult to update multiparts
            //We can't use XDocument as it's not fully valid XML
            if (Episode != null && Episode.Type == ProcessedEpisode.ProcessedEpisodeType.merged)
            {
                return(ReplaceMultipartFile());
            }

            XDocument doc  = XDocument.Load(Where.FullName);
            XElement  root = doc.Root;

            if (root is null)
            {
                return(new ActionOutcome($"Could not load {Where.FullName}"));
            }

            if (Episode != null) // specific episode
            {
                ShowItem si = Episode.Show ?? SelectedShow;
                UpdateEpisodeFields(Episode, si, root, false);
            }
            else if (SelectedShow != null) // show overview (tvshow.nfo)
            {
                UpdateShowFields(root);
            }

            doc.Save(Where.FullName);
            return(ActionOutcome.Success());
        }
Exemplo n.º 5
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                if (whereFile != null)
                {
                    ProcessFile(whereFile, updateTime);
                }

                if (whereDirectory != null)
                {
                    System.IO.Directory.SetLastWriteTimeUtc(whereDirectory.FullName, updateTime);
                }
            }
            catch (UnauthorizedAccessException uae)
            {
                return(new ActionOutcome(uae));
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }

            return(ActionOutcome.Success());
        }
Exemplo n.º 6
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            return(Episode != null?WriteEpisodeMetaDataFile() :
                       SelectedShow != null?WriteSeriesXml() :
                           ActionOutcome.Success());

            //todo WDTV Movie support WriteMovieXml();
        }
Exemplo n.º 7
0
        private ActionOutcome WriteSeriesXml()
        {
            try
            {
                XmlWriterSettings settings = new XmlWriterSettings
                {
                    Indent = true,
                    NewLineOnAttributes = true
                };

                using (XmlWriter writer = XmlWriter.Create(Where.FullName, settings))
                {
                    writer.WriteStartElement("details");
                    writer.WriteStartElement("show");

                    writer.WriteElement("title", SelectedShow !.ShowName);
                    writer.WriteElement("premiered", SelectedShow.CachedShow?.FirstAired);
                    writer.WriteElement("year", SelectedShow.CachedShow?.Year);
                    writer.WriteElement("status", SelectedShow.CachedShow?.Status);
                    writer.WriteElement("mpaa", SelectedShow.CachedShow?.ContentRating);
                    writer.WriteElement("tvdbid", SelectedShow.CachedShow?.TvdbCode);
                    writer.WriteElement("plot", SelectedShow.CachedShow?.Overview);

                    foreach (string genre in SelectedShow.Genres)
                    {
                        writer.WriteElement("genre", genre);
                    }

                    float siteRating = SelectedShow.CachedShow?.SiteRating ?? 0 * 10;

                    int intSiteRating = (int)siteRating;
                    if (intSiteRating > 0)
                    {
                        writer.WriteElement("rating", intSiteRating);
                    }

                    writer.WriteInfo("moviedb", "imdb", "id", SelectedShow.CachedShow?.Imdb);

                    string rt = SelectedShow.CachedShow?.Runtime;
                    if (!string.IsNullOrEmpty(rt))
                    {
                        writer.WriteElement("runtime", rt + " min");
                    }

                    writer.WriteEndElement(); // show
                    writer.WriteEndElement(); // tvshow
                }
                return(ActionOutcome.Success());
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 8
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                client.RemoveCompletedDownload(name);

                return(ActionOutcome.Success());
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 9
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            if (Episode is null)
            {
                return(new ActionOutcome("PyTivoMetaData Go called with no Episode Set"));
            }
            try
            {
                // create folder if it does not exist. (Only really applies when .meta\ folder is being used.)
                if (!Where.Directory.Exists)
                {
                    Directory.CreateDirectory(Where.Directory.FullName);
                }

                using (
                    System.IO.StreamWriter writer = new System.IO.StreamWriter(Where.FullName, false,
                                                                               System.Text.Encoding.GetEncoding(1252)))
                {
                    // See: http://pytivo.sourceforge.net/wiki/index.php/Metadata
                    writer.WriteLine($"title : {Episode.Show.ShowName}");
                    writer.WriteLine($"seriesTitle : {Episode.Show.ShowName}");
                    writer.WriteLine($"episodeTitle : {Episode.Name}");
                    writer.WriteLine(
                        $"episodeNumber : {Episode.AppropriateSeasonNumber}{Episode.AppropriateEpNum:0#}");
                    writer.WriteLine("isEpisode : true");
                    writer.WriteLine($"description : {Episode.Overview}");
                    if (Episode.FirstAired != null)
                    {
                        writer.WriteLine($"originalAirDate : {Episode.FirstAired.Value:yyyy-MM-dd}T00:00:00Z");
                    }

                    writer.WriteLine($"callsign : {Episode.Show.CachedShow?.Network}");

                    WriteEntries(writer, "vDirector", Episode.EpisodeDirector);
                    WriteEntries(writer, "vWriter", Episode.Writer);
                    WriteEntries(writer, "vActor", string.Join("|", Episode.Show.GetActorNames()));
                    WriteEntries(writer, "vGuestStar",
                                 Episode.EpisodeGuestStars); // not worrying about actors being repeated
                    WriteEntries(writer, "vProgramGenre", string.Join("|", Episode.Show.Genres));
                }

                return(ActionOutcome.Success());
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 10
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            bool isDownloadable = url.IsWebLink();

            try
            {
                try
                {
                    if (TVSettings.Instance.CheckuTorrent && isDownloadable)
                    {
                        FileInfo downloadedFile = DownloadFile();
                        new uTorrent().StartTorrentDownload(downloadedFile);
                        return(ActionOutcome.Success());
                    }

                    if (TVSettings.Instance.CheckqBitTorrent)
                    {
                        if (isDownloadable && TVSettings.Instance.qBitTorrentDownloadFilesFirst)
                        {
                            FileInfo downloadedFile = DownloadFile();
                            new qBitTorrent().StartTorrentDownload(downloadedFile);
                            return(ActionOutcome.Success());
                        }
                        else
                        {
                            new qBitTorrent().StartUrlDownload(url);
                            return(ActionOutcome.Success());
                        }
                    }
                }
                catch (DownloadFailedException e)
                {
                    //Don't worry about this error, we'll retry below
                }

                if (Helpers.OpenUrl(url))
                {
                    return(ActionOutcome.Success());
                }

                return(new ActionOutcome("No torrent clients enabled to download RSS - Tried to use system open, but failed"));
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 11
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                ProcessFile(WhereFile, UpdateTime);
            }
            catch (UnauthorizedAccessException uae)
            {
                return(new ActionOutcome(uae));
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }

            return(ActionOutcome.Success());
        }
        public override ActionOutcome Go(TVRenameStats stats)
        {
            DirectoryInfo source = new DirectoryInfo(sourceFolder);
            DirectoryInfo target = new DirectoryInfo(targetFolder);

            if (target.Exists && source.Exists && !target.EnumerateFiles().Any())
            {
                target.Delete();
                source.MoveTo(targetFolder);
                LOGGER.Info($"Moved whole directory {sourceFolder } to {targetFolder}");
                return(ActionOutcome.Success());
            }

            if (target.Exists)
            {
                if (target.GetFiles().Any(x => x.IsMovieFile()))
                {
                    throw new ActionFailedException("Target location has movie files - not copying just in case");
                }

                //Copy files
                foreach (FileInfo file in source.EnumerateFiles())
                {
                    string destFile = Path.Combine(targetFolder, file.Name);
                    if (!File.Exists(destFile))
                    {
                        file.MoveTo(destFile);

                        LOGGER.Info($"Moved {file.FullName} to {destFile}");
                    }
                }

                if (Directory.IsEmpty(source.FullName))
                {
                    source.Delete(false);
                    LOGGER.Info($"Deleted empty directory {source.FullName}");
                }
                return(ActionOutcome.Success());
            }

            source.MoveTo(targetFolder);
            LOGGER.Info($"Moved whole directory {sourceFolder } to {targetFolder}");
            return(ActionOutcome.Success());
        }
Exemplo n.º 13
0
        /// <summary>
        /// Processes an Action by running it.
        /// </summary>
        /// <param name="infoIn">A ProcessActionInfo to be processed. It will contain the Action to be processed</param>
        private void ProcessSingleAction(object infoIn)
        {
            if (!(infoIn is ProcessActionInfo info))
            {
                return;
            }

            try
            {
                info.Sem.WaitOne();     // don't start until we're allowed to
                actionStarting = false; // let our creator know we're started ok

                Action action = info.TheAction;
                if (action == null)
                {
                    return;
                }

                Logger.Trace("Triggering Action: {0} - {1} - {2}", action.Name, action.Produces, action.ToString());
                action.Outcome = action.Go(mStats);
                if (action.Outcome.Error)
                {
                    action.ErrorText = action.Outcome.LastError.Message;
                }

                if (!action.Outcome.Done)
                {
                    Logger.Error("Hlep");
                }
            }
            catch (ThreadAbortException)
            {
                //Thread has been killed off
            }
            catch (Exception e)
            {
                Logger.Fatal(e, "Unhandled Exception in Process Single Action");
                info.TheAction.Outcome = ActionOutcome.CompleteFail();
            }
            finally
            {
                info.Sem.Release();
            }
        }
Exemplo n.º 14
0
 public override ActionOutcome Go(TVRenameStats stats)
 {
     try
     {
         if (toRemove.Exists)
         {
             DeleteOrRecycleFile(toRemove);
             if (Tidyup != null && Tidyup.DeleteEmpty)
             {
                 LOGGER.Info($"Testing {toRemove.Directory.FullName } to see whether it should be tidied up");
                 DoTidyUp(toRemove.Directory);
             }
         }
     }
     catch (Exception e)
     {
         return(new ActionOutcome(e));
     }
     return(ActionOutcome.Success());
 }
Exemplo n.º 15
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                if (Episode != null) // specific episode
                {
                    WriteEpisodeXml();
                }
                else if (SelectedShow != null) // show overview (Series.xml)
                {
                    WriteSeriesXml();
                }

                return(ActionOutcome.Success());
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 16
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                if (!(TVSettings.Instance.CheckuTorrent || TVSettings.Instance.CheckqBitTorrent))
                {
                    return(new ActionOutcome("No torrent clients enabled to download RSS"));
                }

                if (!TVSettings.Instance.qBitTorrentDownloadFilesFirst && TVSettings.Instance.CheckqBitTorrent)
                {
                    qBitTorrentFinder.StartTorrentDownload(url, null, false);
                    return(ActionOutcome.Success());
                }

                byte[] r = HttpHelper.GetUrlBytes(url, true);

                if (r is null || r.Length == 0)
                {
                    return(new ActionOutcome($"No data downloaded from {url}"));
                }

                string saveTemp = SaveDownloadedData(r, SourceName);

                if (TVSettings.Instance.CheckuTorrent)
                {
                    uTorrentFinder.StartTorrentDownload(saveTemp, theFileNoExt);
                }

                if (TVSettings.Instance.CheckqBitTorrent)
                {
                    qBitTorrentFinder.StartTorrentDownload(url, saveTemp, TVSettings.Instance.qBitTorrentDownloadFilesFirst);
                }

                return(ActionOutcome.Success());
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
        }
Exemplo n.º 17
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            try
            {
                if (!Where.Exists)
                {
                    CreateBlankFile();
                }

                ActionOutcome actionOutcome = UpdateFile();
                Where.LastWriteTime = DateTimeOffset.FromUnixTimeSeconds(UpdateTime() ?? 0).UtcDateTime;
                return(actionOutcome);
            }
            catch (IOException ex)
            {
                return(new ActionOutcome(ex));
            }
            catch (UnauthorizedAccessException ex)
            {
                return(new ActionOutcome(ex));
            }
            catch (XmlException)
            {
                //Assume that the file needs to be recreated
                try
                {
                    Where.Delete(true);
                    return(Go(stats));
                }
                catch (IOException ex)
                {
                    return(new ActionOutcome(ex));
                }
                catch (UnauthorizedAccessException ex)
                {
                    return(new ActionOutcome(ex));
                }
            }
        }
Exemplo n.º 18
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            XmlWriterSettings settings = new XmlWriterSettings
            {
                Indent = true,
                NewLineOnAttributes = true
            };

            try
            {
                using (XmlWriter writer = XmlWriter.Create(Where.FullName, settings))
                {
                    writer.WriteStartElement("FolderTag");
                    writer.WriteElement("ViewMode", "Movie");
                    writer.WriteElement("ViewType", "Video");
                    writer.WriteEndElement();
                }
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }
            return(ActionOutcome.Success());
        }
Exemplo n.º 19
0
        protected override ActionOutcome UpdateFile()
        {
            //We will replace the file as too difficult to update multiparts
            //We can't use XDocument as it's not fully valid XML
            if (Episode != null && Episode.Type == ProcessedEpisode.ProcessedEpisodeType.merged)
            {
                return(ReplaceMultipartFile());
            }

            XDocument doc  = XDocument.Load(Where.FullName);
            XElement? root = doc.Root;

            if (root is null)
            {
                return(new ActionOutcome($"Could not load {Where.FullName}"));
            }

            ShowConfiguration si = Episode !.Show;

            UpdateEpisodeFields(Episode, si, root, false);

            doc.Save(Where.FullName);
            return(ActionOutcome.Success());
        }
Exemplo n.º 20
0
        public override ActionOutcome Go(TVRenameStats stats)
        {
            // read NTFS permissions (if any)
            FileSecurity security = null;

            try
            {
                security = From.GetAccessControl();
            }
            catch
            {
                // ignored
            }

            try
            {
                //we use a temp name just in case we are interrupted or some other problem occurs
                string tempName = TempFor(To);

                if (!Directory.Exists(To.Directory.FullName))
                {
                    Directory.CreateDirectory(To.Directory.FullName);
                }

                // If both full filenames are the same then we want to move it away and back
                //This deals with an issue on some systems (XP?) that case insensitive moves did not occur
                if (IsMoveRename() || FileHelper.Same(From, To))
                {
                    // This step could be slow, so report progress
                    CopyMoveResult moveResult = File.Move(From.FullName, tempName, MoveOptions.CopyAllowed | MoveOptions.ReplaceExisting, CopyProgressCallback, null);
                    if (moveResult.ErrorCode != 0)
                    {
                        throw new ActionFailedException(moveResult.ErrorMessage);
                    }
                }
                else
                {
                    //we are copying
                    Debug.Assert(Operation == Op.copy);

                    // This step could be slow, so report progress
                    CopyMoveResult copyResult = File.Copy(From.FullName, tempName, CopyOptions.None, true, CopyProgressCallback, null);
                    if (copyResult.ErrorCode != 0)
                    {
                        throw new ActionFailedException(copyResult.ErrorMessage);
                    }
                }

                // Copying the temp file into the correct name is very quick, so no progress reporting
                File.Move(tempName, To.FullName, MoveOptions.ReplaceExisting);
                LOGGER.Info($"{Name} completed: {From.FullName} to {To.FullName } ");

                UpdateStats(stats);

                if (To.IsMovieFile())
                {
                    //File is correct name
                    LOGGER.Debug($"Just copied {To.FullName} to the right place. Marking it as 'seen'.");

                    if (Episode != null)
                    {
                        //Record this episode as seen
                        TVSettings.Instance.PreviouslySeenEpisodes.EnsureAdded(SourceEpisode);

                        if (TVSettings.Instance.IgnorePreviouslySeen)
                        {
                            doc.SetDirty();
                        }
                    }

                    if (Movie != null)
                    {
                        //Record this movie as seen
                        TVSettings.Instance.PreviouslySeenMovies.EnsureAdded(Movie);

                        if (TVSettings.Instance.IgnorePreviouslySeenMovies)
                        {
                            doc.SetDirty();
                        }
                    }
                }
            }
            catch (Exception e)
            {
                LOGGER.Warn(e, $"Error occurred while {Name}: {From.FullName} to {To.FullName } ");
                return(new ActionOutcome(e));
            }

            // set NTFS permissions
            try
            {
                if (security != null)
                {
                    To.SetAccessControl(security);
                }
            }
            catch
            {
                // ignored
            }

            try
            {
                if (Operation == Op.move && Tidyup != null && Tidyup.DeleteEmpty)
                {
                    LOGGER.Info($"Testing {From.Directory.FullName} to see whether it should be tidied up");
                    DoTidyUp(From.Directory);
                }
            }
            catch (Exception e)
            {
                return(new ActionOutcome(e));
            }

            return(ActionOutcome.Success());
        }
Exemplo n.º 21
0
        private ActionOutcome WriteEpisodeMetaDataFile()
        {
            if (Episode != null)
            {
                // "try" and silently fail.  eg. when file is use by other...
                try
                {
                    XmlWriterSettings settings = new XmlWriterSettings
                    {
                        Indent = true,
                        NewLineOnAttributes = true
                    };

                    using (XmlWriter writer = XmlWriter.Create(Where.FullName, settings))
                    {
                        writer.WriteStartElement("details");

                        writer.WriteElement("title", TVSettings.Instance.NamingStyle.NameFor(Episode));
                        writer.WriteElement("mpaa", Episode.TheCachedSeries.ContentRating);

                        if (Episode.FirstAired.HasValue)
                        {
                            writer.WriteElement("year", Episode.FirstAired.Value.ToString("yyyy-MM-dd"));
                            writer.WriteElement("firstaired",
                                                Episode.FirstAired.Value.ToString("yyyy-MM-dd"));
                        }

                        writer.WriteElement("runtime", Episode.TheCachedSeries.Runtime, true);
                        writer.WriteElement("rating", Episode.EpisodeRating);
                        writer.WriteElement("studio", Episode.TheCachedSeries.Network);
                        writer.WriteElement("plot", Episode.TheCachedSeries.Overview);
                        writer.WriteElement("overview", Episode.Overview);
                        foreach (string director in Episode.Directors)
                        {
                            writer.WriteElement("directors", director);
                        }

                        foreach (string epwriter in Episode.Writers)
                        {
                            writer.WriteElement("writers", epwriter);
                        }

                        foreach (string genre in Episode.TheCachedSeries.Genres)
                        {
                            writer.WriteElement("genre", genre);
                        }

                        // actors...
                        foreach (Actor aa in Episode.TheCachedSeries.GetActors()
                                 .Where(aa => !string.IsNullOrEmpty(aa.ActorName)))
                        {
                            writer.WriteStartElement("actor");
                            writer.WriteElement("name", aa.ActorName);
                            writer.WriteElement("role", aa.ActorRole);
                            writer.WriteEndElement(); // actor
                        }

                        // guest stars...
                        foreach (string guest in Episode.GuestStars)
                        {
                            writer.WriteElement("guest", guest);
                        }

                        writer.WriteElement("thumbnail", TheTVDB.API.GetImageURL(Episode.Filename));
                        writer.WriteElement("banner",
                                            TheTVDB.API.GetImageURL(Episode.AppropriateProcessedSeason.GetWideBannerPath()));

                        writer.WriteElement("backdrop",
                                            TheTVDB.API.GetImageURL(Episode.TheCachedSeries.GetSeriesFanartPath()));

                        writer.WriteEndElement(); // details
                    }

                    return(ActionOutcome.Success());
                }
                catch (Exception e)
                {
                    return(new ActionOutcome(e));
                }
            }
            return(new ActionOutcome("Write WDTV Metadata called with no Episode provided"));
        }
Exemplo n.º 22
0
        protected override ActionOutcome UpdateFile()
        {
            XDocument doc  = XDocument.Load(Where.FullName);
            XElement? root = doc.Root;

            if (root is null)
            {
                return(new ActionOutcome($"Could not load {Where.FullName}"));
            }

            CachedSeriesInfo cachedSeries = SelectedShow !.CachedShow;

            root.UpdateElement("title", SelectedShow.ShowName);

            float?showRating = cachedSeries?.SiteRating;

            if (showRating.HasValue)
            {
                UpdateRatings(root, showRating.Value.ToString(CultureInfo.InvariantCulture), cachedSeries.SiteRatingVotes);
            }

            string lang = TVSettings.Instance.PreferredLanguageCode;

            if (SelectedShow.UseCustomLanguage && SelectedShow.PreferredLanguage != null)
            {
                lang = SelectedShow.PreferredLanguage.Abbreviation;
            }

            //https://forum.kodi.tv/showthread.php?tid=323588
            //says that we need a format like this:
            //<episodeguide><url post="yes" cache="auth.json">https://api.thetvdb.com/login?{&quot;apikey&quot;:&quot;((API-KEY))&quot;,&quot;id&quot;:((ID))}|Content-Type=application/json</url></episodeguide>

            XElement episodeGuideNode = root.GetOrCreateElement("episodeguide");
            XElement urlNode          = episodeGuideNode.GetOrCreateElement("url");

            urlNode.UpdateAttribute("post", "yes");
            urlNode.UpdateAttribute("cache", "auth.json");
            urlNode.SetValue(TheTVDB.API.BuildUrl(SelectedShow.TvdbCode, lang));

            if (!(cachedSeries is null))
            {
                root.UpdateElement("originaltitle", SelectedShow.ShowName);
                UpdateAmongstElements(root, "studio", cachedSeries.Network);
                root.UpdateElement("id", cachedSeries.TvdbCode);
                root.UpdateElement("runtime", cachedSeries.Runtime, true);
                root.UpdateElement("mpaa", cachedSeries.ContentRating, true);
                root.UpdateElement("premiered", cachedSeries.FirstAired);
                root.UpdateElement("year", cachedSeries.Year);
                root.UpdateElement("status", cachedSeries.Status);
                root.UpdateElement("plot", cachedSeries.Overview);

                UpdateId(root, "tvdb", "true", cachedSeries.TvdbCode);
                UpdateId(root, "imdb", "false", cachedSeries.Imdb);
            }

            root.ReplaceElements("genre", SelectedShow.Genres);

            ReplaceActors(root, SelectedShow.Actors);

            doc.Save(Where.FullName);
            return(ActionOutcome.Success());
        }