/// <summary>
        /// Gets season/episode information from database.
        /// </summary>
        /// <param name="show">Show to load episode information into</param>
        public void FullUpdate(TvShow show)
        {
            // Check for invalid ID
            if (show.Id <= 0)
                return;

            // Try multiple times - databases requests tend to fail randomly
            for (int  i = 0; i < 5; i++)
                if (Update(show))
                {

                    // Remove episodes that are no longer in the TvDb
                    for (int k = show.Episodes.Count - 1; k >= 0; k--)
                    {
                        TvEpisode ep = show.Episodes[k];
                        if (!ep.InDatabase && !ep.UserDefined && !ep.PreventDatabaseUpdates)
                            show.Episodes.Remove(ep);
                    }

                    // Update missing episodes for show
                    show.UpdateMissing();
                    show.LastUpdated = DateTime.Now;
                    break;
                }
        }
Beispiel #2
0
 /// <summary>
 /// Match a folder path to show in database
 /// </summary>
 /// <param name="rootFolder">Root folder content will belong to</param>
 /// <param name="path">Current path of content</param>
 /// <returns>Show from database that was matched, null if no match</returns>
 public bool PathMatch(string rootFolder, string path, bool fast, bool threaded, out TvShow match)
 {
     Content contentMatch;
     bool results = base.PathMatch(rootFolder, path, fast, threaded, out contentMatch, null);
     match = new TvShow(contentMatch);
     match.UpdateInfoFromDatabase();
     return results;
 }
Beispiel #3
0
 /// <summary>
 /// Attempts to match string to show from the online database.
 /// </summary>
 /// <param name="search">Search string to match against</param>
 /// <param name="rootFolder">The root folder the content will belong to</param>
 /// <param name="folderPath">Folder path where the content should be moved to</param>
 /// <param name="threaded">Whether search is threaded, setting to false can help with debugging</param>
 /// <returns>Match show item, null if no match</returns>
 public bool ContentMatch(string search, string rootFolder, string folderPath, bool fast, bool threaded, out TvShow match)
 {
     Content contentMatch;
     bool results = base.ContentMatch(search, rootFolder, folderPath, fast, threaded, out contentMatch, null);
     match = new TvShow(contentMatch);
     if (results)
         match.UpdateInfoFromDatabase();
     return results;
 }
Beispiel #4
0
 /// <summary>
 /// Constructor for episode with known properties.
 /// </summary>
 /// <param name="name">Name of the episode</param>
 /// <param name="show">Name of show the episode belongs to</param>
 /// <param name="season">Season number the episode belongs to</param>
 /// <param name="number">Number of the episode within the season</param>
 /// <param name="air">Air data string</param>
 /// <param name="overview">Episdoe overview/description</param>
 public TvEpisode(String name, TvShow show, int season, int number, string air, string overview) : this(show)
 {
     this.DatabaseName   = name;
     this.Season         = season;
     this.DatabaseNumber = number;
     DateTime airDate;
     DateTime.TryParse(air, out airDate);
     this.DatabaseAirDate  = airDate;
     this.DatabaseOverview = overview;
 }
Beispiel #5
0
        /// <summary>
        /// Attempts to match string to show from the online database.
        /// </summary>
        /// <param name="search">Search string to match against</param>
        /// <param name="rootFolder">The root folder the content will belong to</param>
        /// <param name="folderPath">Folder path where the content should be moved to</param>
        /// <param name="threaded">Whether search is threaded, setting to false can help with debugging</param>
        /// <returns>Match show item, null if no match</returns>
        public bool ContentMatch(string search, string rootFolder, string folderPath, bool fast, bool threaded, out TvShow match)
        {
            Content contentMatch;
            bool    results = base.ContentMatch(search, rootFolder, folderPath, fast, threaded, out contentMatch, null);

            match = new TvShow(contentMatch);
            if (results)
            {
                match.UpdateInfoFromDatabase();
            }
            return(results);
        }
Beispiel #6
0
        /// <summary>
        /// Attempts to find matches between a file name and name of this instance
        /// </summary>
        /// <param name="fileName">File name to match to</param>
        /// <returns>Collection of matches for file name and content<</returns>
        public MatchCollection MatchFileToContent(string fileName)
        {
            List <string> names = new List <string>();

            names.Add(this.DatabaseName);

            // Load in alternate name for TV shows
            if (this is TvShow)
            {
                TvShow show = (TvShow)this;
                foreach (string altName in show.AlternativeNameMatches)
                {
                    names.Add(altName);
                }
            }

            // Try to match to each name
            foreach (string name in names)
            {
                // Build regular expression to match content to - try first without removing whitespace
                string          re      = BuildNameRegularExpresionString(true, name);
                MatchCollection matches = null;
                if (!string.IsNullOrEmpty(re))
                {
                    matches = Regex.Matches(FileHelper.SimplifyFileName(System.IO.Path.GetFileNameWithoutExtension(FileHelper.GetSafeFileName(fileName))), re, RegexOptions.IgnoreCase);
                }

                // Return matches if found
                if (matches != null && matches.Count > 0)
                {
                    return(matches);
                }

                // Build regular expression to match content to - this time with removed whitespace
                re = BuildNameRegularExpresionString(false, name);
                if (!string.IsNullOrEmpty(re))
                {
                    matches = Regex.Matches(FileHelper.SimplifyFileName(System.IO.Path.GetFileNameWithoutExtension(fileName)), re, RegexOptions.IgnoreCase);
                }

                // Return matches if found
                if (matches != null && matches.Count > 0)
                {
                    return(matches);
                }
            }

            return(null);
        }
Beispiel #7
0
        /// <summary>
        /// Performs search for show in database. Should be overriden
        /// </summary>
        /// <param name="mirror">Mirror to use</param>
        /// <param name="searchString">Search string for show</param>
        /// <param name="includeSummaries">Whether to include summaries in search results (takes longer - set to false unless user is seeing them)</param>
        /// <returns>Results as list of shows</returns>
        protected override List <Content> DoSearch(string mirror, string searchString, bool includeSummaries)
        {
            // Init results
            List <Content> searchResults = new List <Content>();

            // Get search results from databasse
            string    seriesLookupUrl = mirror + "/search.php?key=" + this.API_KEY + "&show=" + searchString;
            WebClient webClient       = new WebClient();
            string    seriesList      = webClient.DownloadString(seriesLookupUrl);

            // Create xml object with text from mirrors url
            XmlDocument seriesDoc = new XmlDocument();

            seriesDoc.InnerXml = seriesList;

            // Get root element and children
            XmlElement  root  = seriesDoc.DocumentElement;
            XmlNodeList nodes = root.ChildNodes;

            // Go through each node and get parse into shows
            for (int i = 0; i < nodes.Count; i++)
            {
                // Create show
                TvShow searchResult = ParseShowInfo(nodes[i]);

                // Get summaries for each resulting show - this may be slow!
                if (includeSummaries)
                {
                    // Get show info from database
                    string showInfoUrl = mirror + "/showinfo.php?key=" + this.API_KEY + "&sid=" + searchResult.Id;
                    string showInfo    = webClient.DownloadString(showInfoUrl);

                    // Create XML object
                    XmlDocument showInfoDoc = new XmlDocument();
                    showInfoDoc.InnerXml = showInfo;

                    // Parse show info from XML
                    searchResult = ParseShowInfo(showInfoDoc.DocumentElement);
                }

                // Add parsed show to results
                searchResult.DatabaseSelection = (int)TvDataBaseSelection.TvRage;
                searchResults.Add(searchResult);
            }

            // Return results
            return(searchResults);
        }
Beispiel #8
0
        /// <summary>
        /// Parse show properties from show XML node from database
        /// </summary>
        /// <param name="node">XML node to parse info from</param>
        /// <returns>Parse TvShow object</returns>
        private static TvShow ParseShowInfo(XmlNode node)
        {
            // Init show
            TvShow show = new TvShow();

            // Parse properties out from child nodes
            XmlNodeList seriesNodes = node.ChildNodes;

            foreach (XmlNode subNode in seriesNodes)
            {
                switch (subNode.Name.ToLower())
                {
                case "showname":
                case "name":
                    show.DatabaseName = subNode.InnerText;
                    break;

                case "showid":
                    int id;
                    int.TryParse(subNode.InnerText, out id);
                    show.Id = id;
                    break;

                case "started":
                    int year;
                    int.TryParse(subNode.InnerText, out year);
                    if (year == 0)
                    {
                        year = 1;
                    }
                    show.DatabaseYear = year;
                    break;

                case "genres":
                    show.DatabaseGenres = new GenreCollection(GenreCollection.CollectionType.Tv);
                    foreach (XmlNode genreNode in subNode.ChildNodes)
                    {
                        show.DatabaseGenres.Add(genreNode.InnerText);
                    }
                    break;

                case "summary":
                    show.Overview = subNode.InnerText.Replace('\n', ' ');
                    break;
                }
            }
            return(show);
        }
        public EpisodeCollectionControlViewModel(ObservableCollection<TvEpisode> episodes, TvShow show = null )
        {
            this.show = show;

            List<TvEpisodeFilter> filters = TvEpisodeFilter.BuildFilters(show, show == null);
            this.EpisodeFilters = new ObservableCollection<TvEpisodeFilter>();
            foreach (TvEpisodeFilter filter in filters)
                this.EpisodeFilters.Add(filter);

            if (this.EpisodeFilters.Count > 0)
                this.SelectedEpisodeFilter = this.EpisodeFilters[0];

            this.EpisodesCollectionView = CollectionViewSource.GetDefaultView(episodes);
            this.EpisodesCollectionView.Filter = new Predicate<object>(FilterEpisode);

            // TODO: this make loading the control slow as f**k
            EnableLiveFiltering();

            this.Groupings = new ObservableCollection<TvGroupingType>();
            this.Groupings.Add(TvGroupingType.None);
            if (show == null)
            {
                this.Groupings.Add(TvGroupingType.Show);
                this.DisplayOverview = false;
            }
            else
            {
                this.Groupings.Add(TvGroupingType.Season);
                this.DisplayOverview = true;
            }
            this.Groupings.Add(TvGroupingType.Status);

            if (show == null)
            {
                this.SelectedGrouping = TvGroupingType.Show;
                this.DisplayShowName = Visibility.Visible;
            }
            else
            {
                this.SelectedGrouping = TvGroupingType.Season;
                this.DisplayShowName = Visibility.Collapsed;
            }
        }
Beispiel #10
0
        /// <summary>
        /// Build array of possible episode filters that can be used for a TvShow.
        /// </summary>
        /// <param name="show">The show to build filters for</param>
        /// <param name="displayIgnored">Whether to add ignored season filters</param>
        /// <returns></returns>
        public static List<TvEpisodeFilter> BuildFilters(TvShow show, bool schedule)
        {
            List<TvEpisodeFilter> filters = new List<TvEpisodeFilter>();

            filters.Add(new TvEpisodeFilter(FilterType.Regular, 0));
            filters.Add(new TvEpisodeFilter(FilterType.All, 0));
            if (!schedule)
                filters.Add(new TvEpisodeFilter(FilterType.Ignored, 0));
            filters.Add(new TvEpisodeFilter(FilterType.Missing, 0));
            filters.Add(new TvEpisodeFilter(FilterType.Located, 0));
            filters.Add(new TvEpisodeFilter(FilterType.InScanDir, 0));
            if (!schedule)
                filters.Add(new TvEpisodeFilter(FilterType.Unaired, 0));

            if (!schedule)
                foreach (int season in show.Seasons)
                    filters.Add(new TvEpisodeFilter(FilterType.Season, season));

            return filters;
        }
Beispiel #11
0
        public override bool Equals(object obj)
        {
            if (!(obj is TvShow))
            {
                return(false);
            }

            if (!base.Equals(obj))
            {
                return(false);
            }

            TvShow show = obj as TvShow;

            if (this.IncludeInSchedule != show.IncludeInSchedule ||
                this.DoMissingCheck != show.DoMissingCheck ||
                this.DvdEpisodeOrder != show.DvdEpisodeOrder ||
                this.Episodes.Count != show.Episodes.Count ||
                this.AlternativeNameMatches.Count != show.AlternativeNameMatches.Count)
            {
                return(false);
            }

            for (int i = 0; i < this.Episodes.Count; i++)
            {
                if (!this.Episodes[i].Equals(show.Episodes[i]))
                {
                    return(false);
                }
            }

            for (int i = 0; i < this.AlternativeNameMatches.Count; i++)
            {
                if (!this.AlternativeNameMatches[i].Equals(show.AlternativeNameMatches[i]))
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #12
0
            /// <summary>
            /// Get search match with lowest modification to search string
            /// </summary>
            /// <param name="status">Search status instance</param>
            /// <param name="lowestModsMatchStrLen">Length of best result's content name</param>
            /// <param name="results">Best resulting content</param>
            /// <returns>Whether a valid content match result was found</returns>
            public bool GetSearchResultWithLowestMods(out ContentSearchMod modsOnResultsSearch, out Content results)
            {
                int lowestModsMatchStrLen = 0;

                modsOnResultsSearch = ContentSearchMod.All;

                switch (this.ContentType)
                {
                case ContentType.Movie:
                    results = new Movie();
                    break;

                case ContentType.TvShow:
                    results = new TvShow();
                    break;

                default:
                    throw new Exception("Unknown content type");
                }

                // Use match with lowest amount of modification made to search string and longest length (I think this is the most likely to the content we actually want to match to)
                for (int i = 0; i < this.matches.Length; i++)
                {
                    if (this.matches[i] != null)
                    {
                        for (int j = 0; j < this.matches[i].Count; j++)
                        {
                            if (this.matches[i][j].Mods < modsOnResultsSearch || (this.matches[i][j].Mods == modsOnResultsSearch && this.matches[i][j].MatchedString.Length > lowestModsMatchStrLen))
                            {
                                results               = this.matches[i][j].Content;
                                modsOnResultsSearch   = this.matches[i][j].Mods;
                                lowestModsMatchStrLen = this.matches[i][j].MatchedString.Length;
                            }
                        }
                    }
                }
                return(!string.IsNullOrWhiteSpace(results.DatabaseName));
            }
Beispiel #13
0
        /// <summary>
        /// Copies properties from another instance into this instance.
        /// </summary>
        /// <param name="content">Instance to copy properties from</param>
        /// <param name="replacePath">Whether path related properties should be cloned or not</param>
        /// <param name="handleEmptyPath">Whether to build path if one being cloned is empty</param>
        public override void CloneAndHandlePath(Content content, bool replacePath, bool handleEmptyPath = true)
        {
            if (!(content is TvShow))
            {
                throw new Exception("Content must be TvShow");
            }
            TvShow show = content as TvShow;

            bool updateRequired = show.Id != this.Id;

            base.CloneAndHandlePath(show, replacePath, handleEmptyPath);

            this.IncludeInSchedule = show.IncludeInSchedule;
            this.DoMissingCheck    = show.DoMissingCheck;
            this.DvdEpisodeOrder   = show.DvdEpisodeOrder;

            if (updateRequired)
            {
                this.UpdateInfoFromDatabase();
            }

            // TODO: this is a hack
            App.Current.Dispatcher.BeginInvoke((Action) delegate
            {
                this.Episodes.Clear();
                foreach (TvEpisode episode in show.Episodes)
                {
                    TvEpisode copyEp = new TvEpisode(episode);
                    this.Episodes.Add(copyEp);
                    copyEp.Show = this;
                }
            });
            this.AlternativeNameMatches = new ObservableCollection <string>();
            foreach (string altName in show.AlternativeNameMatches)
            {
                this.AlternativeNameMatches.Add(altName);
            }
        }
Beispiel #14
0
 /// <summary>
 /// Constructor for episode with know show name only.
 /// </summary>
 /// <param name="show">Name of show the episode belongs to</param>
 public TvEpisode(TvShow show)
 {
     this.Show = show;
 }
Beispiel #15
0
 /// <summary>
 /// Constructor for episode with known properties.
 /// </summary>
 /// <param name="name">Name of the episode</param>
 /// <param name="show">Name of show the episode belongs to</param>
 /// <param name="season">Season number the episode belongs to</param>
 /// <param name="number">Number of the episode within the season</param>
 /// <param name="air">Air data string</param>
 /// <param name="overview">Episdoe overview/description</param>
 public TvEpisode(String name, TvShow show, int season, int number, string air, string overview)
     : this(show)
 {
     this.DatabaseName = name;
     this.Season = season;
     this.DatabaseNumber = number;
     DateTime airDate;
     DateTime.TryParse(air, out airDate);
     this.DatabaseAirDate = airDate;
     this.DatabaseOverview = overview;
 }
Beispiel #16
0
        /// <summary>
        /// Parse show properties from show XML node from database
        /// </summary>
        /// <param name="node">XML node to parse info from</param>
        /// <returns>Parse TvShow object</returns>
        private static TvShow ParseShowInfo(XmlNode node)
        {
            // Init show
            TvShow show = new TvShow();

            // Parse properties out from child nodes
            XmlNodeList seriesNodes = node.ChildNodes;
            foreach (XmlNode subNode in seriesNodes)
                switch (subNode.Name.ToLower())
                {
                    case "showname":
                    case "name":
                        show.DatabaseName = subNode.InnerText;
                        break;
                    case "showid":
                        int id;
                        int.TryParse(subNode.InnerText, out id);
                        show.Id = id;
                        break;
                    case "started":
                        int year;
                        int.TryParse(subNode.InnerText, out year);
                        if (year == 0) year = 1;
                        show.DatabaseYear = year;
                        break;
                    case "genres":
                        show.DatabaseGenres = new GenreCollection(GenreCollection.CollectionType.Tv);
                        foreach (XmlNode genreNode in subNode.ChildNodes)
                            show.DatabaseGenres.Add(genreNode.InnerText);
                        break;
                    case "summary":
                        show.Overview = subNode.InnerText.Replace('\n', ' ');
                        break;
                }
            return show;
        }
Beispiel #17
0
        /// <summary>
        /// Updates show's season/episode information from database. Use for newly
        /// added shows only, as it will replace all episode information in show.
        /// </summary>
        /// <param name="show">Show to load episode information into</param>
        protected override bool DoUpdate(string mirror, Content content)
        {
            TvShow show = (TvShow)content;

            bool   success  = true;
            string tempPath = string.Empty;

            try
            {
                // Download episodes XML string
                string showUrl  = mirror + "/api/" + API_KEY + "/series/" + show.Id + "/all/en.zip";
                string basePath = Organization.GetBasePath(true);
                Guid   guid     = Guid.NewGuid();
                tempPath = Path.Combine(basePath, guid.ToString());
                Directory.CreateDirectory(tempPath);
                string zipPath     = Path.Combine(tempPath, "data" + ".zip");
                string extractPath = Path.Combine(tempPath, "en.xml");

                WebClient webClient = new WebClient();
                webClient.DownloadFile(showUrl, zipPath);

                ZipFile zip = ZipFile.Read(zipPath);
                foreach (ZipEntry entry in zip.Entries)
                {
                    if (entry.FileName == "en.xml")
                    {
                        entry.Extract(tempPath, ExtractExistingFileAction.OverwriteSilently);
                        break;
                    }
                }
                zip.Dispose();

                XmlDocument showDoc = new XmlDocument();
                showDoc.Load(extractPath);

                // Get root element and children
                XmlElement  root      = showDoc.DocumentElement;
                XmlNodeList rootNodes = root.ChildNodes;

                // Go through each node and get info for each episode
                foreach (XmlNode node in rootNodes)
                {
                    if (node.Name == "Series")
                    {
                        XmlNodeList serNodes = node.ChildNodes;
                        foreach (XmlNode subNode in serNodes)
                        {
                            switch (subNode.Name)
                            {
                            case "Genre":
                                string[] genres = subNode.InnerText.Split('|');
                                foreach (string genre in genres)
                                {
                                    if (!string.IsNullOrWhiteSpace(genre) && !show.DatabaseGenres.Contains(genre))
                                    {
                                        show.DatabaseGenres.Add(genre);
                                    }
                                }
                                break;
                            }
                        }
                    }
                    else if (node.Name == "Episode")
                    {
                        TvEpisode ep     = new TvEpisode(show);
                        int       season = -1;

                        XmlNodeList subNodes = node.ChildNodes;
                        foreach (XmlNode subNode in subNodes)
                        {
                            switch (subNode.Name)
                            {
                            case "EpisodeNumber":
                                int epNumber = 0;
                                int.TryParse(subNode.InnerText, out epNumber);
                                ep.DatabaseNumber = epNumber;
                                break;

                            case "DVD_episodenumber":
                                double dvdNumber = -1;
                                if (double.TryParse(subNode.InnerText, out dvdNumber))
                                {
                                    ep.DatabaseDvdNumber = (int)dvdNumber;
                                }
                                break;

                            case "EpisodeName":
                                ep.DatabaseName = subNode.InnerText;
                                break;

                            case "SeasonNumber":
                                season    = Convert.ToInt32(subNode.InnerText);
                                ep.Season = season;
                                break;

                            case "FirstAired":
                                DateTime airData;
                                DateTime.TryParse(subNode.InnerText, out airData);
                                ep.DatabaseAirDate = airData;
                                break;

                            case "Overview":
                                ep.DatabaseOverview = subNode.InnerText;
                                break;
                            }
                        }
                        ep.InDatabase = true;

                        if (ep.DisplayNumber > -1 && season > -1)
                        {
                            // If episode already exists just update it, else add it
                            TvEpisode existingMatch;
                            if (show.FindEpisode(ep.Season, ep.DatabaseNumber, true, out existingMatch))
                            {
                                existingMatch.DatabaseName     = ep.DatabaseName;
                                existingMatch.DatabaseAirDate  = ep.DatabaseAirDate;
                                existingMatch.DatabaseOverview = ep.DatabaseOverview;
                                existingMatch.InDatabase       = true;
                            }
                            else
                            {
                                show.Episodes.Add(ep);
                            }
                        }
                    }
                }

                showDoc = null;
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception caught on TvDb update: " + e.ToString());
                success = false;
            }
            finally
            {
                if (Directory.Exists(tempPath))
                {
                    Directory.Delete(tempPath, true);
                }
            }

            return(success);
        }
Beispiel #18
0
        /// <summary>
        /// Match a folder path to show in database
        /// </summary>
        /// <param name="rootFolder">Root folder content will belong to</param>
        /// <param name="path">Current path of content</param>
        /// <returns>Show from database that was matched, null if no match</returns>
        public bool PathMatch(string rootFolder, string path, bool fast, bool threaded, out TvShow match)
        {
            Content contentMatch;
            bool    results = base.PathMatch(rootFolder, path, fast, threaded, out contentMatch, null);

            match = new TvShow(contentMatch);
            match.UpdateInfoFromDatabase();
            return(results);
        }
Beispiel #19
0
        /// <summary>
        /// Performs search for a show in TheTvDb.
        /// </summary>
        /// <param name="searchString">The string to search for</param>
        /// <returns>Array of results from the search</returns>
        protected override List<Content> DoSearch(string mirror, string searchString, bool includeSummaries)
        {
            // Get list of results from search
            List<Content> searchResults = new List<Content>();
            string seriesLookupUrl = mirror + "/api/GetSeries.php?seriesname=" + searchString;
            WebClient webClient = new WebClient();
            string seriesList = webClient.DownloadString(seriesLookupUrl);

            // Create xml object with text from mirrors url
            XmlDocument seriesDoc = new XmlDocument();
            seriesDoc.InnerXml = seriesList;

            // Get root element and children
            XmlElement root = seriesDoc.DocumentElement;
            XmlNodeList nodes = root.ChildNodes;

            // Go through each node and get the url for all mirrors

            for (int i = 0; i < nodes.Count; i++)
            {
                // Create show
                TvShow searchResult = new TvShow();

                XmlNodeList seriesNodes = nodes[i].ChildNodes;
                foreach (XmlNode subNode in seriesNodes)
                    switch (subNode.Name.ToLower())
                    {
                        case "seriesname":
                            searchResult.DatabaseName = subNode.InnerText;
                            char[] invalidChars = { '\\', '/', ':', '*', '?', '<', '>', '|' };
                            foreach (char c in invalidChars)
                                if (searchResult.DatabaseName.Contains(c))
                                    searchResult.DatabaseName = searchResult.DatabaseName.Replace(c.ToString(), " ");
                            break;
                        case "seriesid":
                            int id;
                            int.TryParse(subNode.InnerText, out id);
                            searchResult.Id = id;
                            break;
                        case "overview":
                            searchResult.Overview = subNode.InnerText;
                            break;
                        case "firstaired":
                            DateTime airDate;
                            DateTime.TryParse(subNode.InnerText, out airDate);
                            searchResult.DatabaseYear = airDate.Year;
                            break;
                    }

                searchResult.DatabaseSelection = (int)TvDataBaseSelection.TheTvDb;
                searchResults.Add(searchResult);
            }

            return searchResults;
        }
        /// <summary>
        /// Update processing method (thread) for single content folder in root.
        /// </summary>
        /// <param name="orgPath">Organization path instance to be processed</param>
        /// <param name="pathNum">The path's number out of total being processed</param>
        /// <param name="totalPaths">Total number of paths being processed</param>
        /// <param name="processNumber">The identifier for the OrgProcessing instance</param>
        /// <param name="numItemsProcessed">Number of paths that have been processed - used for progress updates</param>
        /// <param name="numItemsStarted">Number of paths that have had been added to thread pool for processing</param>
        /// <param name="processSpecificArgs">Arguments specific to this process</param>
        private void UpdateProcess(OrgPath orgPath, int pathNum, int totalPaths, int processNumber, ref int numItemsProcessed, ref int numItemsStarted, object processSpecificArgs)
        {
            // Check for cancellation - this method is called from thread pool, so cancellation could have occured by the time this is run
            if (updateCancelled || this.updateNumber != processNumber)
            {
                return;
            }

            // First pass run does quick folder update by skipping online database searching
            bool   firstPass  = (bool)processSpecificArgs;
            string passString = firstPass ? " (First Pass)" : " (Second Pass)";

            // Set processing messge
            string progressMsg = "Updating of '" + orgPath.RootFolder.FullPath + "'" + passString + " - '" + Path.GetFileName(orgPath.Path) + "' started";

            OnUpdateProgressChange(this, false, CalcProgress(numItemsProcessed, numItemsStarted, totalPaths), progressMsg);

            // Get content collection to add content to
            ContentCollection content = GetContentCollection();

            // Check if folder already has a match to existing content
            bool    contentExists   = false;
            bool    contentComplete = false;
            Content newContent      = null;
            int     index           = 0;

            for (int j = 0; j < content.Count; j++)
            {
                if (Path.Equals(orgPath.Path, content[j].Path))
                {
                    contentExists    = true;
                    content[j].Found = true;
                    if (!string.IsNullOrEmpty(content[j].DatabaseName))
                    {
                        contentComplete = true;
                    }
                    newContent = content[j];
                    index      = j;
                    break;
                }
            }

            // Set completed progess message
            progressMsg = "Updating of '" + orgPath.RootFolder.FullPath + "'" + passString + " - '" + Path.GetFileName(orgPath.Path) + "' complete";

            // Check if content found
            if (contentExists && contentComplete)
            {
                // Check if content needs updating
                if ((DateTime.Now - newContent.LastUpdated).TotalDays > 7 && this.ContentType == ContentType.TvShow)
                {
                    newContent.UpdateInfoFromDatabase();
                }

                // Update progress
                if (this.updateNumber == processNumber)
                {
                    OnUpdateProgressChange(this, false, CalcProgress(numItemsProcessed, numItemsStarted, totalPaths), progressMsg);
                }

                return;
            }

            // Folder wasn't matched to an existing content instance, try tmatch folder to content from online database
            Content match;
            bool    matchSucess;

            switch (this.ContentType)
            {
            case ContentType.TvShow:
                TvShow showMatch;
                matchSucess = SearchHelper.TvShowSearch.PathMatch(orgPath.RootFolder.FullPath, orgPath.Path, firstPass, true, out showMatch);
                match       = showMatch;
                break;

            case ContentType.Movie:
                Movie movieMatch;
                matchSucess = SearchHelper.MovieSearch.PathMatch(orgPath.RootFolder.FullPath, orgPath.Path, firstPass, true, out movieMatch);
                match       = movieMatch;
                break;

            default:
                throw new Exception("unknown content type");
            }


            // Check that current process hasn't been replaced - search can be slow, so update may have been cancelled by the time it gets here
            if (updateCancelled || this.updateNumber != processNumber)
            {
                return;
            }

            // Folder already existed, but wasn't previously match to valid content
            if (contentExists && matchSucess)
            {
                switch (this.ContentType)
                {
                case ContentType.TvShow:
                    ((TvShow)newContent).CloneAndHandlePath((TvShow)match, true);
                    ((TvShow)newContent).UpdateMissing();
                    break;

                case ContentType.Movie:
                    ((Movie)newContent).CloneAndHandlePath((Movie)match, true);
                    break;

                default:
                    throw new Exception("unknown content type");
                }
                newContent.LastUpdated = DateTime.Now;
            }
            else if (matchSucess)
            {
                newContent = match;
            }
            else
            {
                switch (this.ContentType)
                {
                case ContentType.TvShow:
                    newContent = new TvShow(string.Empty, 0, 0, orgPath.Path, orgPath.RootFolder.FullPath);
                    break;

                case ContentType.Movie:
                    newContent = new Movie(string.Empty, 0, 0, orgPath.Path, orgPath.RootFolder.FullPath);
                    break;

                default:
                    throw new Exception("unknown content type");
                }
            }

            // Set found flag
            newContent.Found = true;

            // Add content to list if new
            if (!contentExists)
            {
                content.Add(newContent);
            }

            // Update progress
            OnUpdateProgressChange(this, true, CalcProgress(numItemsProcessed, numItemsStarted, totalPaths), progressMsg);
        }
Beispiel #21
0
        public OrgItem ProcessPath(OrgPath orgPath, bool tvOnlyCheck, bool skipMatching, bool fast, bool threaded, int procNumber, bool allowFromLog, out bool fromLog)
        {
            fromLog = false;
            FileCategory fileCat = FileHelper.CategorizeFile(orgPath, orgPath.Path);

            // Search through dir scan log for matching source
            if (allowFromLog)
            {
                for (int i = 0; i < Organization.DirScanLog.Count; i++)
                {
                    if (Organization.DirScanLog[i].SourcePath == orgPath.Path)
                    {
                        fromLog = true;

                        if (fileCat != Organization.DirScanLog[i].Category)
                        {
                            break;
                        }

                        OrgItem newItem = UpdateItemFromPrevious(orgPath, Organization.DirScanLog[i], threaded, false, false);
                        if (newItem != null && newItem.Action != OrgAction.None)
                        {
                            if (newItem.CanEnable)
                            {
                                newItem.Enable = true;
                            }
                            return(newItem);
                        }
                    }
                }
            }

            // If similar to earlier item, wait for it to complete before continuing
            while (!skipMatching && orgPath.SimilarTo >= 0 && orgPath.SimilarTo < this.Items.Count && (this.Items[orgPath.SimilarTo].Action == OrgAction.TBD || this.Items[orgPath.SimilarTo].Action == OrgAction.Processing))
            {
                // Check for cancellation
                if (scanCanceled || procNumber < scanNumber)
                {
                    return(null);
                }

                Thread.Sleep(50);
            }

            // If similar to earlier item, check if we can use it's info
            if (orgPath.SimilarTo > 0 && orgPath.SimilarTo < this.Items.Count)
            {
                OrgItem newItem = UpdateItemFromPrevious(orgPath, this.Items[orgPath.SimilarTo], threaded, fast, skipMatching);
                if (newItem != null)
                {
                    return(newItem);
                }
            }

            // Create default none item
            OrgItem noneItem = new OrgItem(OrgAction.None, orgPath.Path, fileCat, orgPath.OrgFolder);

            // Setup match to filename and folder name (if it's in a folder inside of downloads)
            string pathBase;

            if (!string.IsNullOrEmpty(orgPath.OrgFolder.FolderPath))
            {
                pathBase = orgPath.Path.Replace(orgPath.OrgFolder.FolderPath, "");
            }
            else
            {
                pathBase = orgPath.Path;
            }
            string[] pathSplit;
            if (!string.IsNullOrEmpty(orgPath.OrgFolder.FolderPath))
            {
                pathSplit = pathBase.Split('\\');
            }
            else
            {
                pathSplit = orgPath.Path.Split('\\');
            }
            pathSplit[pathSplit.Length - 1] = Path.GetFileNameWithoutExtension(pathSplit[pathSplit.Length - 1]);

            List <string> possibleMatchPaths = new List <string>();

            // Looking to remove dummy video files by rip (e.g. "ERTG.mp4" inside "The Matrix 1999 hd ERGT rip/" folder)
            List <string> validSplits = new List <string>();

            for (int i = pathSplit.Length - 1; i > 0; i--)
            {
                if (string.IsNullOrWhiteSpace(pathSplit[i]))
                {
                    continue;
                }

                bool containedInOther = false;
                for (int j = i - 1; j > 0; j--)
                {
                    if (pathSplit[j].Length > pathSplit[i].Length && pathSplit[j].ToLower().Contains(pathSplit[i].ToLower()))
                    {
                        containedInOther = true;
                        break;
                    }
                }

                if (!containedInOther)
                {
                    validSplits.Add(pathSplit[i]);
                    possibleMatchPaths.Add(pathSplit[i] + Path.GetExtension(orgPath.Path));
                }
            }

            for (int i = validSplits.Count - 1; i >= 0; i--)
            {
                string build = string.Empty;
                for (int j = validSplits.Count - 1; j >= i; j--)
                {
                    build += validSplits[j] + " ";
                }

                build = build.Trim();

                build += Path.GetExtension(orgPath.Path);
                if (!possibleMatchPaths.Contains(build))
                {
                    possibleMatchPaths.Add(build);
                }
            }


            // Try to match to each string
            foreach (string matchString in possibleMatchPaths)
            {
                OnDebugNotificationd("Attempting to match to path: \" " + matchString + "\"");

                // Get simple file name
                string simpleFile = FileHelper.BasicSimplify(Path.GetFileNameWithoutExtension(matchString), false);
                OnDebugNotificationd("Simplifies to:  \" " + simpleFile + "\"");

                // Categorize current match string
                FileCategory matchFileCat = FileHelper.CategorizeFile(orgPath, matchString);
                OnDebugNotificationd("Classified as: " + matchFileCat);

                // Check tv
                if (tvOnlyCheck && matchFileCat != FileCategory.TvVideo)
                {
                    continue;
                }

                // Check for cancellation
                if (scanCanceled || procNumber < scanNumber)
                {
                    return(null);
                }

                // Set whether item is for new show
                bool newShow = false;

                // Check for cancellation
                if (scanCanceled || procNumber < scanNumber)
                {
                    return(null);
                }

                // Add appropriate action based on file category
                switch (matchFileCat)
                {
                // TV item
                case FileCategory.TvVideo:

                    // Try to match file to existing show
                    Dictionary <TvShow, MatchCollection> matches = new Dictionary <TvShow, MatchCollection>();
                    for (int j = 0; j < Organization.Shows.Count; j++)
                    {
                        MatchCollection match = Organization.Shows[j].MatchFileToContent(matchString);
                        if (match != null && match.Count > 0)
                        {
                            matches.Add((TvShow)Organization.Shows[j], match);
                        }
                    }

                    // Try to match to temporary show
                    lock (directoryScanLock)
                        foreach (TvShow show in temporaryShows)
                        {
                            MatchCollection match = show.MatchFileToContent(matchString);
                            if (match != null && match.Count > 0 && !matches.ContainsKey(show))
                            {
                                matches.Add(show, match);
                            }
                        }

                    // Debug notification for show matches
                    string matchNotification = "Show name matches found: ";
                    if (matches.Count == 0)
                    {
                        matchNotification += "NONE";
                    }
                    else
                    {
                        foreach (TvShow match in matches.Keys)
                        {
                            matchNotification += match.DisplayName + ", ";
                        }
                        matchNotification = matchNotification.Substring(0, matchNotification.Length - 2);
                    }
                    OnDebugNotificationd(matchNotification);

                    // Check for matches to existing show
                    TvShow bestMatch = null;
                    if (matches.Count > 0)
                    {
                        // Find best match, based on length
                        int longestMatch = 2;     // minimum of 3 letters must match (acronym)
                        foreach (KeyValuePair <TvShow, MatchCollection> match in matches)
                        {
                            foreach (Match m in match.Value)
                            {
                                // Check that match is not part of a word
                                int matchWordCnt = 0;
                                for (int l = m.Index; l < simpleFile.Length; l++)
                                {
                                    if (simpleFile[l] == ' ')
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        ++matchWordCnt;
                                    }
                                }


                                if (m.Value.Trim().Length > longestMatch && m.Length >= matchWordCnt)
                                {
                                    longestMatch = m.Length;
                                    bestMatch    = match.Key;
                                }
                            }
                        }
                        if (bestMatch != null)
                        {
                            OnDebugNotificationd("Matched to show " + bestMatch.DisplayName);
                        }
                    }

                    // Episode not matched to a TV show, search database!
                    if (bestMatch == null && !skipMatching)
                    {
                        OnDebugNotificationd("Not matched to existing show; searching database.");

                        // Setup search string
                        string showFile = Path.GetFileNameWithoutExtension(matchString);

                        // Perform search for matching TV show
                        if (SearchHelper.TvShowSearch.ContentMatch(showFile, string.Empty, string.Empty, fast, threaded, out bestMatch))
                        {
                            ContentRootFolder defaultTvFolder;
                            string            path = NO_TV_FOLDER;
                            if (Settings.GetTvFolderForContent(bestMatch, out defaultTvFolder))
                            {
                                path = defaultTvFolder.FullPath;
                            }

                            bestMatch.RootFolder = path;
                            bestMatch.Path       = bestMatch.BuildFolderPath();

                            // Save show in temporary shows list (in case there are more files that may match to it during scan)
                            lock (directoryScanLock)
                                if (!temporaryShows.Contains(bestMatch))
                                {
                                    temporaryShows.Add(bestMatch);
                                }
                            newShow = true;
                        }
                        else
                        {
                            bestMatch = null;
                        }
                    }
                    else if (temporaryShows.Contains(bestMatch))
                    {
                        newShow = true;
                    }

                    // Episode has been matched to a TV show
                    if (bestMatch != null)
                    {
                        // Try to get episode information from file
                        int seasonNum, episodeNum1, episodeNum2;
                        if (FileHelper.GetEpisodeInfo(matchString, bestMatch.DatabaseName, out seasonNum, out episodeNum1, out episodeNum2))
                        {
                            // No season match means file only had episode number, allow this for single season shows
                            if (seasonNum == -1)
                            {
                                int maxSeason = 0;
                                foreach (int season in bestMatch.Seasons)
                                {
                                    if (season > maxSeason)
                                    {
                                        maxSeason = season;
                                    }
                                }

                                if (maxSeason == 1)
                                {
                                    seasonNum = 1;
                                }
                            }

                            // Try to get the episode from the show
                            TvEpisode episode1, episode2 = null;
                            if (bestMatch.FindEpisode(seasonNum, episodeNum1, false, out episode1))
                            {
                                if (episodeNum2 != -1)
                                {
                                    bestMatch.FindEpisode(seasonNum, episodeNum2, false, out episode2);
                                }

                                OrgAction action = orgPath.Copy ? OrgAction.Copy : OrgAction.Move;

                                // If item episode already exists set action to duplicate
                                if (episode1.Missing == MissingStatus.Located)
                                {
                                    action = OrgAction.AlreadyExists;
                                }

                                // Build action and add it to results
                                string  destination = bestMatch.BuildFilePath(episode1, episode2, Path.GetExtension(orgPath.Path));
                                OrgItem newItem     = new OrgItem(action, orgPath.Path, destination, episode1, episode2, fileCat, orgPath.OrgFolder);
                                if (destination.StartsWith(NO_TV_FOLDER))
                                {
                                    newItem.Action = OrgAction.NoRootFolder;
                                }
                                if (newItem.Action == OrgAction.AlreadyExists || newItem.Action == OrgAction.NoRootFolder)
                                {
                                    newItem.Enable = false;
                                }
                                else
                                {
                                    newItem.Enable = true;
                                }
                                newItem.Category  = matchFileCat;
                                newItem.IsNewShow = newShow;
                                return(newItem);
                            }
                            else
                            {
                                OnDebugNotificationd("Couldn't find episode for season " + seasonNum + " episods " + episodeNum1);
                            }
                        }
                    }

                    // No match to TV show
                    if (!tvOnlyCheck && !fast)
                    {
                        // Try to match to a movie
                        OrgItem movieItem;
                        if (CreateMovieAction(orgPath, matchString, out movieItem, threaded, fast, skipMatching, null))
                        {
                            noneItem.Clone(movieItem);
                        }
                    }

                    break;

                // Movie item
                case FileCategory.MovieVideo:
                    // Create action
                    OrgItem item;
                    CreateMovieAction(orgPath, matchString, out item, threaded, fast, skipMatching, null);

                    // If delete action created (for sample file)
                    if (item.Action == OrgAction.Delete)
                    {
                        return(BuildDeleteAction(orgPath, fileCat));
                    }
                    else if (item.Action != OrgAction.None)
                    {
                        return(item);
                    }
                    break;

                // Trash
                case FileCategory.Trash:
                    return(BuildDeleteAction(orgPath, matchFileCat));

                // Ignore
                case FileCategory.Ignored:
                    return(new OrgItem(OrgAction.None, orgPath.Path, matchFileCat, orgPath.OrgFolder));

                // Unknown
                default:
                    return(new OrgItem(OrgAction.None, orgPath.Path, matchFileCat, orgPath.OrgFolder));
                }

                // Check for cancellation
                if (scanCanceled || procNumber < scanNumber)
                {
                    return(noneItem);
                }
            }

            // If no match on anything set none action
            return(noneItem);
        }
Beispiel #22
0
        /// <summary>
        /// Attempts to match string to content from the online database.
        /// </summary>
        /// <param name="search">Search string to match against</param>
        /// <param name="rootFolder">The root folder the content will belong to</param>
        /// <param name="folderPath">Folder path where the content should be moved to</param>
        /// <returns>Match content item, null if no match</returns>
        protected bool ContentMatch(string search, string rootFolder, string folderPath, bool fast, bool threaded, out Content match, Content knownContent)
        {
            // Create empty content
            Content emptyContent;
            switch (this.ContentType)
            {
                case ContentType.Movie:
                    emptyContent = new Movie();
                    break;
                case ContentType.TvShow:
                    emptyContent = new TvShow();
                    break;
                default:
                    throw new Exception("Unknown content type");
            }
            emptyContent.Path = folderPath;
            emptyContent.RootFolder = rootFolder;
            emptyContent.Found = true;

            // Check for empty search condition
            if (string.IsNullOrEmpty(search))
            {
                match = emptyContent;
                return false;
            }

            // Get year from search string
            int dirYear = FileHelper.GetYear(search);

            // Get list of simplified strings
            List<FileHelper.SimplifyStringResults> searches = new List<FileHelper.SimplifyStringResults>();

            // Get list of search bases
            List<string> searchBases = GetModifiedSearches(search);

            // Fast search: use first search base only
            if (fast)
            {
                FileHelper.SimplifyStringResults result = FileHelper.BuildSimplifyResults(searchBases[0], false, false, FileHelper.OptionalSimplifyRemoves.YearAndFollowing, true, false, true, false);
                searches.Add(result);
            }
            // Full search: Go through each search base and get simplified search options
            else
                foreach (string searchBase in searchBases)
                {
                    // Get results from current base
                    List<FileHelper.SimplifyStringResults> currSearches = FileHelper.SimplifyString(searchBase);
                    currSearches.Add(new FileHelper.SimplifyStringResults(searchBase, new Dictionary<FileWordType, List<string>>(), ContentSearchMod.None));

                    // Add each result to full list of searches
                    foreach (FileHelper.SimplifyStringResults results in currSearches)
                    {
                        // Check if search already exist
                        bool exists = false;
                        foreach (FileHelper.SimplifyStringResults s in searches)
                            if (s.SimplifiedString == results.SimplifiedString)
                            {
                                exists = true;
                                break;
                            }

                        // If doesn't exist add it to searches
                        if (!exists && !string.IsNullOrWhiteSpace(results.SimplifiedString))
                            searches.Add(results);
                    }
                }
            searches.Sort();

            // Create new status
            int currSeachCnt;
            MatchStatus status;
            lock (searchLock)
            {
                currSeachCnt = ++searchCount;
                status = new MatchStatus(searches.Count, this.ContentType);
                searchStatus.Add(currSeachCnt, status);
            }

            ContentSearchMod lowMods;
            Content lowestModsMatch;

            // Add thread to pool for each search that need to be performed
            int searchNum = 0;
            while (searchNum < searches.Count)
            {
                // Check for any search results so far
                if (status.GetSearchResultWithLowestMods(out lowMods, out lowestModsMatch))
                {
                    // If search results have no mods or just year removed use them as final results
                    if (lowMods == ContentSearchMod.None || lowMods == ContentSearchMod.YearRemoved)
                    {
                        match = lowestModsMatch;
                        return true;
                    }
                }

                // Limit number of search threads created
                if (status.NumStarted - status.NumCompleted >= Settings.General.NumSimultaneousSearches)
                {
                    Thread.Sleep(100);
                    continue;
                }

                // Build search arguments
                object[] args = { currSeachCnt, searchNum, searches[searchNum].SimplifiedString, folderPath, rootFolder, dirYear, searches[searchNum].Modifications, knownContent };

                // Threaded: add a search to thread pool
                if (threaded)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(SearchThread), args);
                    lock (searchLock)
                        status.SetSearchStarted(searchNum);
                }
                // Synchronized: call search method
                else
                    SearchThread(args);

                searchNum++;
            }

            // Wait for all search to complete
            while (status.NumCompleted < searches.Count)
            {
                // Check for any search results so far
                if (status.GetSearchResultWithLowestMods(out lowMods, out lowestModsMatch))
                {
                    // If search results have no mods or just year removed use them as final results
                    if (lowMods == ContentSearchMod.None || lowMods == ContentSearchMod.YearRemoved)
                    {
                        match = lowestModsMatch;
                        return true;
                    }
                }

                Thread.Sleep(100);
            }

            // Clear status
            lock (searchLock)
                searchStatus.Remove(currSeachCnt);

            // Return result with lowest mods to search string
            if (status.GetSearchResultWithLowestMods(out lowMods, out lowestModsMatch))
            {
                match = lowestModsMatch;
                return true;
            }
            else
            {
                match = emptyContent;
                return false;
            }
        }
Beispiel #23
0
        /// <summary>
        /// Attempts to match string to content from the online database.
        /// </summary>
        /// <param name="search">Search string to match against</param>
        /// <param name="rootFolder">The root folder the content will belong to</param>
        /// <param name="folderPath">Folder path where the content should be moved to</param>
        /// <returns>Match content item, null if no match</returns>
        protected bool ContentMatch(string search, string rootFolder, string folderPath, bool fast, bool threaded, out Content match, Content knownContent)
        {
            // Create empty content
            Content emptyContent;

            switch (this.ContentType)
            {
            case ContentType.Movie:
                emptyContent = new Movie();
                break;

            case ContentType.TvShow:
                emptyContent = new TvShow();
                break;

            default:
                throw new Exception("Unknown content type");
            }
            emptyContent.Path       = folderPath;
            emptyContent.RootFolder = rootFolder;
            emptyContent.Found      = true;

            // Check for empty search condition
            if (string.IsNullOrEmpty(search))
            {
                match = emptyContent;
                return(false);
            }

            // Get year from search string
            int dirYear = FileHelper.GetYear(search);

            // Get list of simplified strings
            List <FileHelper.SimplifyStringResults> searches = new List <FileHelper.SimplifyStringResults>();

            // Get list of search bases
            List <string> searchBases = GetModifiedSearches(search);

            // Fast search: use first search base only
            if (fast)
            {
                FileHelper.SimplifyStringResults result = FileHelper.BuildSimplifyResults(searchBases[0], false, false, FileHelper.OptionalSimplifyRemoves.YearAndFollowing, true, false, true, false);
                searches.Add(result);
            }
            // Full search: Go through each search base and get simplified search options
            else
            {
                foreach (string searchBase in searchBases)
                {
                    // Get results from current base
                    List <FileHelper.SimplifyStringResults> currSearches = FileHelper.SimplifyString(searchBase);
                    currSearches.Add(new FileHelper.SimplifyStringResults(searchBase, new Dictionary <FileWordType, List <string> >(), ContentSearchMod.None));

                    // Add each result to full list of searches
                    foreach (FileHelper.SimplifyStringResults results in currSearches)
                    {
                        // Check if search already exist
                        bool exists = false;
                        foreach (FileHelper.SimplifyStringResults s in searches)
                        {
                            if (s.SimplifiedString == results.SimplifiedString)
                            {
                                exists = true;
                                break;
                            }
                        }

                        // If doesn't exist add it to searches
                        if (!exists && !string.IsNullOrWhiteSpace(results.SimplifiedString))
                        {
                            searches.Add(results);
                        }
                    }
                }
            }
            searches.Sort();

            // Create new status
            int         currSeachCnt;
            MatchStatus status;

            lock (searchLock)
            {
                currSeachCnt = ++searchCount;
                status       = new MatchStatus(searches.Count, this.ContentType);
                searchStatus.Add(currSeachCnt, status);
            }

            ContentSearchMod lowMods;
            Content          lowestModsMatch;

            // Add thread to pool for each search that need to be performed
            int searchNum = 0;

            while (searchNum < searches.Count)
            {
                // Check for any search results so far
                if (status.GetSearchResultWithLowestMods(out lowMods, out lowestModsMatch))
                {
                    // If search results have no mods or just year removed use them as final results
                    if (lowMods == ContentSearchMod.None || lowMods == ContentSearchMod.YearRemoved)
                    {
                        match = lowestModsMatch;
                        return(true);
                    }
                }

                // Limit number of search threads created
                if (status.NumStarted - status.NumCompleted >= Settings.General.NumSimultaneousSearches)
                {
                    Thread.Sleep(100);
                    continue;
                }

                // Build search arguments
                object[] args = { currSeachCnt, searchNum, searches[searchNum].SimplifiedString, folderPath, rootFolder, dirYear, searches[searchNum].Modifications, knownContent };

                // Threaded: add a search to thread pool
                if (threaded)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(SearchThread), args);
                    lock (searchLock)
                        status.SetSearchStarted(searchNum);
                }
                // Synchronized: call search method
                else
                {
                    SearchThread(args);
                }

                searchNum++;
            }

            // Wait for all search to complete
            while (status.NumCompleted < searches.Count)
            {
                // Check for any search results so far
                if (status.GetSearchResultWithLowestMods(out lowMods, out lowestModsMatch))
                {
                    // If search results have no mods or just year removed use them as final results
                    if (lowMods == ContentSearchMod.None || lowMods == ContentSearchMod.YearRemoved)
                    {
                        match = lowestModsMatch;
                        return(true);
                    }
                }

                Thread.Sleep(100);
            }

            // Clear status
            lock (searchLock)
                searchStatus.Remove(currSeachCnt);

            // Return result with lowest mods to search string
            if (status.GetSearchResultWithLowestMods(out lowMods, out lowestModsMatch))
            {
                match = lowestModsMatch;
                return(true);
            }
            else
            {
                match = emptyContent;
                return(false);
            }
        }
Beispiel #24
0
        /// <summary>
        /// Run through all TV shows all looks for episodes that may need to be renamed and for missing episodes.
        /// For missing episodes it attempts to match them to files from the search directories.
        /// </summary>
        /// <param name="shows">Shows to scan</param>
        /// <param name="queuedItems">Items currently in queue (to be skipped)</param>
        /// <returns></returns>
        public List <OrgItem> RunScan(List <Content> shows, List <OrgItem> queuedItems)
        {
            // Set running flag
            scanRunning = true;

            // Initialiaze scan items
            List <OrgItem> missingCheckItem = new List <OrgItem>();

            // Initialize item numbers
            int number = 0;

            // Go through each show
            for (int i = 0; i < shows.Count; i++)
            {
                TvShow show = (TvShow)shows[i];

                if (cancelRequested)
                {
                    break;
                }

                OnProgressChange(ScanProcess.TvRename, shows[i].DatabaseName, (int)Math.Round((double)i / (shows.Count) * 30) + 70);

                // Go each show
                foreach (TvEpisode ep in show.Episodes)
                {
                    // Check for cancellation
                    if (cancelRequested)
                    {
                        break;
                    }

                    // Skipped ignored episodes
                    if (ep.Ignored)
                    {
                        continue;
                    }

                    // Init found flag
                    bool found = false;

                    // Rename check
                    if (ep.Missing == MissingStatus.Located)
                    {
                        if (shows[i].DoRenaming)
                        {
                            found = true;
                            TvEpisode ep2 = null;

                            if (ep.File.MultiPart)
                            {
                                if (ep.File.Part == 1)
                                {
                                    foreach (TvEpisode epEnumerated in show.Episodes)
                                    {
                                        if (epEnumerated.Season == ep.Season && epEnumerated.DisplayNumber == ep.DisplayNumber + 1)
                                        {
                                            ep2 = epEnumerated;
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    continue;
                                }
                            }

                            // Build desired path
                            string builtPath = show.BuildFilePath(ep, ep2, Path.GetExtension(ep.File.FilePath));

                            // Check if rename needed (or move within folder)
                            if (ep.File.FilePath != builtPath)
                            {
                                OrgItem newItem = new OrgItem(OrgStatus.Organization, OrgAction.Rename, ep.File.FilePath, builtPath, ep, ep2, FileCategory.TvVideo, null);
                                newItem.Enable = true;
                                if (!show.DoRenaming)
                                {
                                    newItem.Category = FileCategory.Ignored;
                                }
                                newItem.Number = number++;
                                missingCheckItem.Add(newItem);
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        continue;
                    }

                    // Add empty item for missing
                    if (!found && ep.Aired && show.DoMissingCheck)
                    {
                        OrgItem newItem = new OrgItem(OrgStatus.Missing, OrgAction.None, ep, null, FileCategory.TvVideo, null);
                        if (!show.DoRenaming)
                        {
                            newItem.Category = FileCategory.Ignored;
                        }
                        newItem.Number = number++;
                        missingCheckItem.Add(newItem);
                    }
                }
            }

            // Convert all TV folders to org folders
            List <OrgFolder> tvFoldersAsOrgFolders = new List <OrgFolder>();

            foreach (ContentRootFolder tvFolder in Settings.TvFolders)
            {
                OrgFolder orgFolder = new OrgFolder(tvFolder.FullPath, false, false, false, false);
                tvFoldersAsOrgFolders.Add(orgFolder);
            }

            // Update progress
            OnProgressChange(ScanProcess.TvRename, string.Empty, 100);

            // Clear flags
            scanRunning     = false;
            cancelRequested = false;

            // Return results
            return(missingCheckItem);
        }
        /// <summary>
        /// Update processing method (thread) for single content folder in root.
        /// </summary>
        /// <param name="orgPath">Organization path instance to be processed</param>
        /// <param name="pathNum">The path's number out of total being processed</param>
        /// <param name="totalPaths">Total number of paths being processed</param>
        /// <param name="processNumber">The identifier for the OrgProcessing instance</param>
        /// <param name="numItemsProcessed">Number of paths that have been processed - used for progress updates</param>
        /// <param name="numItemsStarted">Number of paths that have had been added to thread pool for processing</param>
        /// <param name="processSpecificArgs">Arguments specific to this process</param>
        private void UpdateProcess(OrgPath orgPath, int pathNum, int totalPaths, int processNumber, ref int numItemsProcessed, ref int numItemsStarted, object processSpecificArgs)
        {
            // Check for cancellation - this method is called from thread pool, so cancellation could have occured by the time this is run
            if (updateCancelled || this.updateNumber != processNumber)
                return;

            // First pass run does quick folder update by skipping online database searching
            bool firstPass = (bool)processSpecificArgs;
            string passString = firstPass ? " (First Pass)" : " (Second Pass)";

            // Set processing messge
            string progressMsg = "Updating of '" + orgPath.RootFolder.FullPath + "'" + passString + " - '" + Path.GetFileName(orgPath.Path) + "' started";
            OnUpdateProgressChange(this, false, CalcProgress(numItemsProcessed, numItemsStarted, totalPaths), progressMsg);

            // Get content collection to add content to
            ContentCollection content = GetContentCollection();

            // Check if folder already has a match to existing content
            bool contentExists = false;
            bool contentComplete = false;
            Content newContent = null;
            int index = 0;
            for (int j = 0; j < content.Count; j++)
                if (Path.Equals(orgPath.Path, content[j].Path))
                {
                    contentExists = true;
                    content[j].Found = true;
                    if (!string.IsNullOrEmpty(content[j].DatabaseName))
                        contentComplete = true;
                    newContent = content[j];
                    index = j;
                    break;
                }

            // Set completed progess message
            progressMsg = "Updating of '" + orgPath.RootFolder.FullPath + "'" + passString + " - '" + Path.GetFileName(orgPath.Path) + "' complete";

            // Check if content found
            if (contentExists && contentComplete)
            {
                // Check if content needs updating
                if ((DateTime.Now - newContent.LastUpdated).TotalDays > 7 && this.ContentType == ContentType.TvShow)
                    newContent.UpdateInfoFromDatabase();

                // Update progress
                if (this.updateNumber == processNumber)
                    OnUpdateProgressChange(this, false, CalcProgress(numItemsProcessed, numItemsStarted, totalPaths), progressMsg);

                return;
            }

            // Folder wasn't matched to an existing content instance, try tmatch folder to content from online database
            Content match;
            bool matchSucess;
            switch (this.ContentType)
            {
                case ContentType.TvShow:
                    TvShow showMatch;
                    matchSucess = SearchHelper.TvShowSearch.PathMatch(orgPath.RootFolder.FullPath, orgPath.Path, firstPass, true, out showMatch);
                    match = showMatch;
                    break;
                case ContentType.Movie:
                    Movie movieMatch;
                    matchSucess = SearchHelper.MovieSearch.PathMatch(orgPath.RootFolder.FullPath, orgPath.Path, firstPass, true, out movieMatch);
                    match = movieMatch;
                    break;
                default:
                    throw new Exception("unknown content type");
            }

            // Check that current process hasn't been replaced - search can be slow, so update may have been cancelled by the time it gets here
            if (updateCancelled || this.updateNumber != processNumber)
                return;

            // Folder already existed, but wasn't previously match to valid content
            if (contentExists && matchSucess)
            {
                switch (this.ContentType)
                {
                    case ContentType.TvShow:
                        ((TvShow)newContent).CloneAndHandlePath((TvShow)match, true);
                        ((TvShow)newContent).UpdateMissing();
                        break;
                    case ContentType.Movie:
                        ((Movie)newContent).CloneAndHandlePath((Movie)match, true);
                        break;
                    default:
                        throw new Exception("unknown content type");
                }
                newContent.LastUpdated = DateTime.Now;
            }
            else if (matchSucess)
                newContent = match;
            else
                switch (this.ContentType)
                {
                    case ContentType.TvShow:
                        newContent = new TvShow(string.Empty, 0, 0, orgPath.Path, orgPath.RootFolder.FullPath);
                        break;
                    case ContentType.Movie:
                        newContent = new Movie(string.Empty, 0, 0, orgPath.Path, orgPath.RootFolder.FullPath);
                        break;
                    default:
                        throw new Exception("unknown content type");
                }

            // Set found flag
            newContent.Found = true;

            // Add content to list if new
            if (!contentExists)
                content.Add(newContent);

            // Update progress
            OnUpdateProgressChange(this, true, CalcProgress(numItemsProcessed, numItemsStarted, totalPaths), progressMsg);
        }
Beispiel #26
0
 /// <summary>
 /// Gets season/episode information from database.
 /// </summary>
 /// <param name="show">Show to load episode information into</param>
 public static void FullShowSeasonsUpdate(TvShow show)
 {
     GetDataBaseAccess(show.Database).FullUpdate(show);
     show.LastUpdated = DateTime.Now;
 }
Beispiel #27
0
        /// <summary>
        /// Load collection from saved XML file
        /// </summary>
        public void Load(bool doUpdating)
        {
            XmlTextReader reader = null;
            XmlDocument   xmlDoc = new XmlDocument();

            try
            {
                string path = Path.Combine(Organization.GetBasePath(false), XML_ROOT + ".xml");

                if (File.Exists(path))
                {
                    // Use dummy collection to load into so that loading doesn't hog use of object
                    ContentCollection loadContent = new ContentCollection(this.ContentType, "Loading Shows");
                    lock (XmlLock)
                    {
                        // Load XML
                        reader = new XmlTextReader(path);
                        xmlDoc.Load(reader);

                        // Extract data
                        XmlNodeList contentNodes = xmlDoc.DocumentElement.ChildNodes;
                        for (int i = 0; i < contentNodes.Count; i++)
                        {
                            // Update loading progress
                            OnLoadProgressChange((int)(((double)i / contentNodes.Count) * 100));

                            // All elements will be content items or last update time
                            if (contentNodes[i].Name == "LastUpdate")
                            {
                                loadContent.LastUpdate = contentNodes[i].InnerText;
                            }
                            else
                            {
                                // Load content from element based on type
                                switch (this.ContentType)
                                {
                                case ContentType.TvShow:
                                    TvShow show = new TvShow();
                                    if (show.Load(contentNodes[i]))
                                    {
                                        bool rootFolderExists = false;
                                        foreach (ContentRootFolder folder in Settings.GetAllRootFolders(this.ContentType, true))
                                        {
                                            if (folder.FullPath == show.RootFolder)
                                            {
                                                rootFolderExists = true;
                                            }
                                        }

                                        if (!rootFolderExists || !Directory.Exists(show.Path))
                                        {
                                            continue;
                                        }

                                        loadContent.Add(show);
                                        if (doUpdating)
                                        {
                                            show.UpdateMissing();
                                        }
                                    }
                                    break;

                                case ContentType.Movie:
                                    Movie movie = new Movie();
                                    if (movie.Load(contentNodes[i]))
                                    {
                                        loadContent.Add(movie);
                                    }
                                    break;

                                default:
                                    throw new Exception("Unknown content type");
                                }
                            }
                        }
                    }
                    // Update progress
                    OnLoadProgressChange(100);

                    this.LastUpdate = loadContent.LastUpdate;
                    this.Clear();
                    AddMultiple(loadContent);
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString(), "Error loading " + this.ContentType + "s from saved data!");
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }

            // Start updating of TV episode in scan dirs.
            if (this.ContentType == ContentType.TvShow && doUpdating)
            {
                TvItemInScanDirHelper.DoUpdate(false);
                TvItemInScanDirHelper.StartUpdateTimer();
            }

            // Trigger load complete event
            OnLoadComplete();
        }
Beispiel #28
0
        /// <summary>
        /// Run through all TV shows all looks for episodes that may need to be renamed and for missing episodes.
        /// For missing episodes it attempts to match them to files from the search directories.
        /// </summary>
        /// <param name="shows">Shows to scan</param>
        /// <param name="queuedItems">Items currently in queue (to be skipped)</param>
        /// <returns></returns>
        public List <OrgItem> RunScan(List <Content> shows, List <OrgItem> queuedItems, bool fast)
        {
            // Set running flag
            scanRunning     = true;
            cancelRequested = false;

            // Do directory check on all directories (to look for missing episodes)
            while (!TvItemInScanDirHelper.Initialized)
            {
                Thread.Sleep(100);
            }

            List <OrgItem> directoryItems = TvItemInScanDirHelper.Items;

            // Initialiaze scan items
            List <OrgItem> missingCheckItem = new List <OrgItem>();

            // Initialize item numbers
            int number = 0;

            double progressPerShow = 1D / shows.Count * 100D;

            // Go through each show
            for (int i = 0; i < shows.Count; i++)
            {
                TvShow show = (TvShow)shows[i];

                if (cancelRequested)
                {
                    break;
                }

                double showsProgress = (double)i * progressPerShow;
                OnProgressChange(ScanProcess.TvMissing, shows[i].DatabaseName, (int)Math.Round(showsProgress));

                double progressPerEp = 1D / show.Episodes.Count * progressPerShow;

                // Go through missing episodes
                for (int j = 0; j < show.Episodes.Count; j++)
                {
                    // Get episode
                    TvEpisode ep = show.Episodes[j];

                    // Update progress
                    OnProgressChange(ScanProcess.TvMissing, shows[i].DatabaseName, (int)Math.Round(showsProgress + j * progressPerEp));

                    // Check for cancellation
                    if (cancelRequested)
                    {
                        break;
                    }

                    // Skipped ignored episodes
                    if (ep.Ignored || !show.DoMissingCheck)
                    {
                        continue;
                    }

                    // Init found flag
                    bool found = false;

                    // Check if episode is missing
                    if (ep.Missing == MissingStatus.Missing || ep.Missing == MissingStatus.InScanDirectory)
                    {
                        // Check directory item for episode
                        foreach (OrgItem item in directoryItems)
                        {
                            if ((item.Action == OrgAction.Move || item.Action == OrgAction.Copy) && item.TvEpisode != null && item.TvEpisode.Show.DatabaseName == show.DatabaseName)
                            {
                                // Only add item for first part of multi-part file
                                if (ep.Equals(item.TvEpisode))
                                {
                                    OrgItem newItem = new OrgItem(OrgStatus.Found, item.Action, item.SourcePath, item.DestinationPath, ep, item.TvEpisode2, FileCategory.TvVideo, item.ScanDirectory);
                                    newItem.Enable = true;
                                    newItem.Number = number++;
                                    if (!show.DoMissingCheck)
                                    {
                                        newItem.Category = FileCategory.Ignored;
                                    }
                                    missingCheckItem.Add(newItem);
                                    found = true;
                                    break;
                                }
                                else if (ep.Equals(item.TvEpisode2))
                                {
                                    found = true;
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        continue;
                    }

                    // Add empty item for missing
                    if (!found && ep.Aired && show.DoMissingCheck)
                    {
                        OrgItem          newItem;
                        TvEpisodeTorrent ezTvEpisode = TvTorrentHelper.GetEpisodeTorrent(ep);
                        if (ezTvEpisode != null)
                        {
                            newItem = new OrgItem(OrgStatus.Missing, OrgAction.Torrent, ep, null, FileCategory.TvVideo, null);
                            newItem.BuildDestination();
                        }
                        else
                        {
                            newItem = new OrgItem(OrgStatus.Missing, OrgAction.None, ep, null, FileCategory.TvVideo, null);
                        }

                        newItem.Number = number++;
                        missingCheckItem.Add(newItem);
                    }
                }
            }

            // Update progress
            OnProgressChange(ScanProcess.TvMissing, string.Empty, 100);

            // Clear flags
            scanRunning = false;

            // Return results
            return(missingCheckItem);
        }
Beispiel #29
0
 /// <summary>
 /// Constructor for cloning instance.
 /// </summary>
 /// <param name="show"></param>
 public TvShow(TvShow show)
     : this()
 {
     Clone(show);
 }
Beispiel #30
0
 /// <summary>
 /// Gets season/episode information from database.
 /// </summary>
 /// <param name="show">Show to load episode information into</param>
 public static void FullShowSeasonsUpdate(TvShow show)
 {
     GetDataBaseAccess(show.Database).FullUpdate(show);
     show.LastUpdated = DateTime.Now;
 }
Beispiel #31
0
 /// <summary>
 /// Constructor for cloning instance.
 /// </summary>
 /// <param name="show"></param>
 public TvShow(TvShow show)
     : this()
 {
     Clone(show);
 }
Beispiel #32
0
        /// <summary>
        /// Gets season/episode information from database
        /// </summary>
        /// <param name="show">Show to load episode information into</param>
        protected override bool DoUpdate(string mirror, Content content)
        {
            TvShow show = (TvShow)content;

            try
            {
                // Get show info from database
                string    showInfoUrl = mirror + "/showinfo.php?key=" + this.API_KEY + "&sid=" + show.Id;
                WebClient webClient   = new WebClient();
                string    showInfo    = webClient.DownloadString(showInfoUrl);

                // Create XML object from results
                XmlDocument seriesDoc = new XmlDocument();
                seriesDoc.InnerXml = showInfo;

                // Parse show info
                show.Overview = ParseShowInfo(seriesDoc.DocumentElement).Overview;

                // Get episode info from database
                string episodeListUrl = mirror + "/episode_list.php?key=" + this.API_KEY + "&sid=" + show.Id;
                webClient = new WebClient();
                string seriesList = webClient.DownloadString(episodeListUrl);

                // Create xml object with text from mirrors url
                seriesDoc          = new XmlDocument();
                seriesDoc.InnerXml = seriesList;

                // Get root element and children
                XmlElement  root  = seriesDoc.DocumentElement;
                XmlNodeList nodes = root.ChildNodes;

                // Go through each node and parse out episodes
                foreach (XmlNode subNode in nodes)
                {
                    switch (subNode.Name.ToLower())
                    {
                    case "episodelist":
                        foreach (XmlNode seasonNode in subNode.ChildNodes)
                        {
                            if (seasonNode.Name.ToLower() == "season")
                            {
                                string seasonNoStr = seasonNode.Attributes["no"].Value;
                                int    seasonNo;
                                int.TryParse(seasonNoStr, out seasonNo);

                                foreach (XmlNode epNode in seasonNode.ChildNodes)
                                {
                                    TvEpisode ep = new TvEpisode(show);
                                    ep.Season = seasonNo;
                                    foreach (XmlNode epPropNode in epNode.ChildNodes)
                                    {
                                        switch (epPropNode.Name.ToLower())
                                        {
                                        case "seasonnum":         // episode number within season
                                            int epNum;
                                            int.TryParse(epPropNode.InnerText, out epNum);
                                            ep.DatabaseNumber = epNum;
                                            break;

                                        case "airdate":
                                            DateTime airDate;
                                            DateTime.TryParse(epPropNode.InnerText, out airDate);
                                            ep.DatabaseAirDate = airDate;
                                            break;

                                        case "title":
                                            ep.DatabaseName = epPropNode.InnerText;
                                            break;

                                        case "summary":
                                            ep.DatabaseOverview = epPropNode.InnerText.Replace('\n', ' ');
                                            break;
                                        }
                                    }
                                    ep.InDatabase = true;

                                    // If episode already exists just update it, else add it
                                    TvEpisode existingMatch;
                                    if (show.FindEpisode(ep.Season, ep.DatabaseNumber, true, out existingMatch))
                                    {
                                        if (!existingMatch.PreventDatabaseUpdates)
                                        {
                                            existingMatch.DatabaseName     = ep.DatabaseName;
                                            existingMatch.DatabaseAirDate  = ep.DatabaseAirDate;
                                            existingMatch.DatabaseOverview = ep.DatabaseOverview;
                                            existingMatch.InDatabase       = true;
                                        }
                                    }
                                    else
                                    {
                                        show.Episodes.Add(ep);
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
                return(true);
            }
            catch
            {
                return(false);
            }
        }
Beispiel #33
0
 /// <summary>
 /// Constructor for episode with know show name only.
 /// </summary>
 /// <param name="show">Name of show the episode belongs to</param>
 public TvEpisode(TvShow show)
 {
     this.Show = show;
 }
Beispiel #34
0
            /// <summary>
            /// Get search match with lowest modification to search string
            /// </summary>
            /// <param name="status">Search status instance</param>
            /// <param name="lowestModsMatchStrLen">Length of best result's content name</param>
            /// <param name="results">Best resulting content</param>
            /// <returns>Whether a valid content match result was found</returns>
            public bool GetSearchResultWithLowestMods(out ContentSearchMod modsOnResultsSearch, out Content results)
            {
                int lowestModsMatchStrLen = 0;
                modsOnResultsSearch = ContentSearchMod.All;

                switch (this.ContentType)
                {
                    case ContentType.Movie:
                        results = new Movie();
                        break;
                    case ContentType.TvShow:
                        results = new TvShow();
                        break;
                    default:
                        throw new Exception("Unknown content type");
                }

                // Use match with lowest amount of modification made to search string and longest length (I think this is the most likely to the content we actually want to match to)
                for (int i = 0; i < this.matches.Length; i++)
                    if (this.matches[i] != null)
                        for (int j = 0; j < this.matches[i].Count; j++)
                        {
                            if (this.matches[i][j].Mods < modsOnResultsSearch || (this.matches[i][j].Mods == modsOnResultsSearch && this.matches[i][j].MatchedString.Length > lowestModsMatchStrLen))
                            {
                                results = this.matches[i][j].Content;
                                modsOnResultsSearch = this.matches[i][j].Mods;
                                lowestModsMatchStrLen = this.matches[i][j].MatchedString.Length;
                            }

                        }
                return !string.IsNullOrWhiteSpace(results.DatabaseName);
            }
Beispiel #35
0
        /// <summary>
        /// Performs search for a show in TheTvDb.
        /// </summary>
        /// <param name="searchString">The string to search for</param>
        /// <returns>Array of results from the search</returns>
        protected override List <Content> DoSearch(string mirror, string searchString, bool includeSummaries)
        {
            // Get list of results from search
            List <Content> searchResults   = new List <Content>();
            string         seriesLookupUrl = mirror + "/api/GetSeries.php?seriesname=" + searchString;
            WebClient      webClient       = new WebClient();
            string         seriesList      = webClient.DownloadString(seriesLookupUrl);

            // Create xml object with text from mirrors url
            XmlDocument seriesDoc = new XmlDocument();

            seriesDoc.InnerXml = seriesList;

            // Get root element and children
            XmlElement  root  = seriesDoc.DocumentElement;
            XmlNodeList nodes = root.ChildNodes;

            // Go through each node and get the url for all mirrors

            for (int i = 0; i < nodes.Count; i++)
            {
                // Create show
                TvShow searchResult = new TvShow();

                XmlNodeList seriesNodes = nodes[i].ChildNodes;
                foreach (XmlNode subNode in seriesNodes)
                {
                    switch (subNode.Name.ToLower())
                    {
                    case "seriesname":
                        searchResult.DatabaseName = subNode.InnerText;
                        char[] invalidChars = { '\\', '/', ':', '*', '?', '<', '>', '|' };
                        foreach (char c in invalidChars)
                        {
                            if (searchResult.DatabaseName.Contains(c))
                            {
                                searchResult.DatabaseName = searchResult.DatabaseName.Replace(c.ToString(), " ");
                            }
                        }
                        break;

                    case "seriesid":
                        int id;
                        int.TryParse(subNode.InnerText, out id);
                        searchResult.Id = id;
                        break;

                    case "overview":
                        searchResult.Overview = subNode.InnerText;
                        break;

                    case "firstaired":
                        DateTime airDate;
                        DateTime.TryParse(subNode.InnerText, out airDate);
                        searchResult.DatabaseYear = airDate.Year;
                        break;
                    }
                }

                searchResult.DatabaseSelection = (int)TvDataBaseSelection.TheTvDb;
                searchResults.Add(searchResult);
            }

            return(searchResults);
        }