/// <summary> /// Compares 2 content instances based on their Name property. /// </summary> /// <param name="x">The first movie.</param> /// <param name="y">The second movie.</param> /// <returns>The comparison results </returns> public static int CompareByAirDate(TvEpisode x, TvEpisode y) { if (x == null) { if (y == null) { return(0); } else { return(-1); } } else { if (y == null) { return(1); } else { return(x.DisplayAirDate.CompareTo(y.DisplayAirDate)); } } }
public EpisodeEditorWindow(TvEpisode episode) { InitializeComponent(); viewModel = new EpisodeEditorWindowViewModel(episode); viewModel.ResultsSet += vm_ResultsSet; this.DataContext = viewModel; }
/// <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; } } }
public override List <TvEpisodeTorrent> GetAvailableTvTorrents(TvEpisode episode) { // Download show episodes page string showPageUrl = BASE_URL + "/search/" + episode.BuildEpString().Replace(" ", "%20"); Limiter.DoWait(); WebClient client = new WebClient(); client.Headers.Add("User-Agent: Other"); string data = client.DownloadString(showPageUrl); Regex baseRegex = new Regex("<div\\s+class=\\\"detName\\\">\\s+<a\\s+href=\\\"[^\\\"]+\\\"[^>]+>([^>]+)</a>\\s+</div>\\s+<a href=\\\"(magnet:[^\\\"]+)\\\""); MatchCollection matches = baseRegex.Matches(data); List <TvEpisodeTorrent> torrents = new List <TvEpisodeTorrent>(); foreach (Match match in matches) { string name = match.Groups[1].Value; string magnet = match.Groups[2].Value; MatchCollection showMatches = episode.Show.MatchFileToContent(name); bool matchShow = showMatches != null && showMatches.Count > 0; int season, ep1, ep2; if (matchShow && FileHelper.GetEpisodeInfo(name, episode.Show.DisplayName, out season, out ep1, out ep2, true) && episode.Season == season && episode.DisplayNumber == ep1) { // Don't want to get torrent with a bunch of episodes (double is okay) if (ep2 > 0 && ep2 - ep1 > 1) { continue; } TorrentQuality quality = TorrentQuality.Sd480p; if (name.ToLower().Contains("720p")) { quality = TorrentQuality.Hd720p; } else if (name.ToLower().Contains("1080p")) { quality = TorrentQuality.Hd1080p; } TvEpisodeTorrent torrentEp = new TvEpisodeTorrent(); torrentEp.Url = showPageUrl; torrentEp.Season = season; torrentEp.Episode = ep1; torrentEp.Episode2 = ep2; torrentEp.Quality = quality; torrentEp.Title = name; torrentEp.Magnet = magnet; torrents.Add(torrentEp); } } return(torrents); }
public TvEpisodeTorrent GetBestTvTorrent(TvEpisode episode) { List<TvEpisodeTorrent> availableTorrents = GetAvailableTvTorrents(episode); if (availableTorrents.Count == 0) return null; Dictionary<TorrentQuality, TvEpisodeTorrent> qualityTorrents = new Dictionary<TorrentQuality, TvEpisodeTorrent>(); foreach (TvEpisodeTorrent torrent in availableTorrents) { TorrentQuality quality = torrent.Quality; if (!qualityTorrents.ContainsKey(quality)) qualityTorrents.Add(quality, null); if (qualityTorrents[quality] == null) qualityTorrents[quality] = torrent; } // Go through matches to find closest quality to preferred TorrentQuality closestQuality = TorrentQuality.Sd480p; if (qualityTorrents.ContainsKey(Settings.General.PreferredTorrentQuality)) closestQuality = Settings.General.PreferredTorrentQuality; else { switch (Settings.General.PreferredTorrentQuality) { case TorrentQuality.Sd480p: if (qualityTorrents.ContainsKey(TorrentQuality.Hd720p)) closestQuality = TorrentQuality.Hd720p; else closestQuality = TorrentQuality.Hd1080p; break; case TorrentQuality.Hd720p: if (qualityTorrents.ContainsKey(TorrentQuality.Sd480p)) closestQuality = TorrentQuality.Sd480p; else closestQuality = TorrentQuality.Hd1080p; break; case TorrentQuality.Hd1080p: if (qualityTorrents.ContainsKey(TorrentQuality.Hd720p)) closestQuality = TorrentQuality.Hd720p; else closestQuality = TorrentQuality.Sd480p; break; default: throw new Exception("Unknown torrent quality"); } } TvEpisodeTorrent closestTorrent = qualityTorrents[closestQuality]; UpdateTorrentLinks(closestTorrent); return closestTorrent; }
/// <summary> /// Builds episode string for TV episode for both single or multi-episode files. /// </summary> /// <param name="ep1">First episode</param> /// <param name="ep2">Second episode (if double episode file)</param> /// <returns>Resulting episode string</returns> public string BuildEpisodeString(TvEpisode ep1, TvEpisode ep2) { // Start out with formatting string string epStr = string.Empty; // Build episode string for (int i = 0; i < 2; i++) { bool season = (i == 0 && this.SeasonFirst) || (i == 1 && !this.SeasonFirst); if (season) { if (this.ForceSeasonDoubleDigits) { epStr += this.SeasonHeader + ep1.Season.ToString("00"); } else { epStr += this.SeasonHeader + ep1.Season.ToString(); } } else { if (this.ForceEpisodeDoubleDigits) { epStr += this.EpisodeHeader + ep1.DisplayNumber.ToString("00"); } else { epStr += this.EpisodeHeader + ep1.DisplayNumber.ToString(); } if (ep2 != null && ep2.DisplayNumber > 0) { if (this.HeaderPerEpisode) { epStr += this.EpisodeHeader; } if (this.ForceEpisodeDoubleDigits) { epStr += ep2.DisplayNumber.ToString("00"); } else { epStr += ep2.DisplayNumber.ToString(); } } } } // Return file name return(epStr); }
public static TvEpisodeTorrent GetEpisodeTorrent(TvEpisode episode) { try { PirateBayTorrentSite site = new PirateBayTorrentSite(); TvEpisodeTorrent torrent = site.GetBestTvTorrent(episode); return(torrent); } catch { return(null); } }
/// <summary> /// Equals check for this episode and another episode /// </summary> /// <param name="obj">Episode to compare to</param> /// <returns></returns> public override bool Equals(object obj) { // False for null/invalid objects if (obj == null || !(obj is TvEpisode)) { return(false); } // Case object to episode TvEpisode ep = (TvEpisode)obj; // Compare is on season and episode number only (show name may not be set yet) return(ep.Season == this.Season && ep.DisplayNumber == this.DisplayNumber && ep.DisplayName == this.DisplayName); }
/// <summary> /// Builds formatted file name for TV episode file from an existing file path. /// </summary> /// <param name="episode1">First episode in file</param> /// <param name="episode2">Second episode in file</param> /// <param name="fullPath">Path of file to be formatted</param> /// <returns>Path with file name formatted</returns> public string BuildTvFileName(TvEpisode episode1, TvEpisode episode2, string fullPath) { // Get file name string fileName = Path.GetFileNameWithoutExtension(fullPath); // Get info from file using simplify function (pulls out file words and categorizes them) FileHelper.OptionalSimplifyRemoves simplifyOptions = FileHelper.OptionalSimplifyRemoves.Year; FileHelper.SimplifyStringResults simplifyResults = FileHelper.BuildSimplifyResults(fileName, false, false, simplifyOptions, true, false, false, false); // Build file name string buildName = BuildFileName(episode1, episode2, simplifyResults); // Remove unsafe file characters and add extension return(FileHelper.GetSafeFileName(buildName)); }
/// <summary> /// Recursive search a directory and mark missing episodes. /// </summary> /// <param name="directory">Path of directory to check for episode files</param> private void UpdateMissingRecursive(string directory) { string[] files = Directory.GetFiles(directory); foreach (string file in files) { int season, episode1, episode2; if (FileHelper.GetEpisodeInfo(file, this.DatabaseName, out season, out episode1, out episode2)) { // Mark episodes as not missing TvEpisode ep1 = null, ep2 = null; bool matched1 = FindEpisode(season, episode1, false, out ep1); bool matched2 = episode2 != -1 && FindEpisode(season, episode2, false, out ep2); if (matched1) { ep1.Missing = MissingStatus.Located; } if (matched2) { ep2.Missing = MissingStatus.Located; } // Update file info if (matched1 && matched2) { ep1.File = new TvFile(file, 1); ep2.File = new TvFile(file, 2); } else if (matched1) { ep1.File = new TvFile(file); } } } // Recursively check subdirectories string[] subDirs = Directory.GetDirectories(directory); foreach (string subDir in subDirs) { UpdateMissingRecursive(subDir); } }
/// <summary> /// Compare method for sorting epsiodes, uses airdate. /// </summary> /// <param name="obj"></param> /// <returns></returns> public int CompareTo(object obj) { if (obj is TvEpisode) { TvEpisode t2 = (TvEpisode)obj; if (this.Season == t2.Season && this.DisplayNumber == t2.DisplayNumber) { return(this.DisplayName.CompareTo(t2.DisplayName)); } else { return((this.Season * 1000 + this.DisplayNumber).CompareTo(t2.Season * 1000 + t2.DisplayNumber)); } } else { throw new ArgumentException("Object is not a TvShow."); } }
/// <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); } }
/// <summary> /// Updates this instance with information from another instance /// </summary> /// <param name="episode"></param> public void Clone(TvEpisode episode) { this.UseDatabaseName = episode.UseDatabaseName; this.UserName = episode.UserName; this.DatabaseName = episode.DatabaseName; this.Show = episode.Show; this.Season = episode.Season; this.UseDatabaseNumber = episode.UseDatabaseNumber; this.UserNumber = episode.UserNumber; this.DatabaseNumber = episode.DatabaseNumber; this.DatabaseDvdNumber = episode.DatabaseDvdNumber; this.UseDatabaseAirDate = episode.UseDatabaseAirDate; this.DatabaseAirDate = episode.DatabaseAirDate; this.UserAirDate = episode.UserAirDate; this.File = new TvFile(episode.File); this.Ignored = episode.Ignored; this.InDatabase = episode.InDatabase; this.Missing = episode.Missing; this.UseDatabaseOverview = episode.UseDatabaseOverview; this.DatabaseOverview = episode.DatabaseOverview; this.UserOverview = episode.UserOverview; this.Watched = episode.Watched; this.PreventDatabaseUpdates = episode.PreventDatabaseUpdates; }
/// <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; }
public EpisodeEditorWindowViewModel(TvEpisode episode) { this.Episode = new TvEpisode(episode); }
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); }
/// <summary> /// Builds episode string for TV episode for both single or multi-episode files. /// </summary> /// <param name="ep1">First episode</param> /// <param name="ep2">Second episode (if double episode file)</param> /// <returns>Resulting episode string</returns> public string BuildEpisodeString(TvEpisode ep1, TvEpisode ep2) { // Start out with formatting string string epStr = string.Empty; // Build episode string for (int i = 0; i < 2; i++) { bool season = (i == 0 && this.SeasonFirst) || (i == 1 && !this.SeasonFirst); if (season) { if (this.ForceSeasonDoubleDigits) epStr += this.SeasonHeader + ep1.Season.ToString("00"); else epStr += this.SeasonHeader + ep1.Season.ToString(); } else { if (this.ForceEpisodeDoubleDigits) epStr += this.EpisodeHeader + ep1.DisplayNumber.ToString("00"); else epStr += this.EpisodeHeader + ep1.DisplayNumber.ToString(); if (ep2 != null && ep2.DisplayNumber > 0) { if (this.HeaderPerEpisode) epStr += this.EpisodeHeader; if (this.ForceEpisodeDoubleDigits) epStr += ep2.DisplayNumber.ToString("00"); else epStr += ep2.DisplayNumber.ToString(); } } } // Return file name return epStr; }
/// <summary> /// Constructor for TV rename/missing check where file was not found. /// </summary> /// <param name="status">Status of rename/missing</param> /// <param name="action">action to be performed</param> /// <param name="episode">TV episode for file</param> /// <param name="episode2">2nd Tv epsidoe for file</param> /// <param name="category">file category</param> public OrgItem(OrgStatus status, OrgAction action, TvEpisode episode, TvEpisode episode2, FileCategory category, OrgFolder scanDir, TvEpisodeTorrent torrentEp) : this() { this.Status = status; this.Progress = 0; this.Action = action; this.SourcePath = string.Empty; if (action == OrgAction.Delete) this.DestinationPath = FileHelper.DELETE_DIRECTORY; else this.DestinationPath = string.Empty; this.TvEpisode = new TvEpisode(episode); if (episode2 != null) this.TvEpisode2 = new TvEpisode(episode2); this.TorrentTvEpisode = torrentEp; this.Category = category; this.Enable = action == OrgAction.Torrent; this.ScanDirectory = scanDir; this.Number = 0; }
/// <summary> /// Constructor for TV rename/missing check where file was found. /// </summary> /// <param name="status">Status of rename/missing</param> /// <param name="action">action to be performed</param> /// <param name="file">source path</param> /// <param name="destination">destination path</param> /// <param name="episode">TV episode for file</param> /// <param name="episode2">2nd Tv epsidoe for file</param> /// <param name="category">file category</param> public OrgItem(OrgStatus status, OrgAction action, string file, string destination, TvEpisode episode, TvEpisode episode2, FileCategory category, OrgFolder scanDir) : this(action, file, destination, episode, episode2, category, scanDir) { this.Status = status; this.Progress = 0; }
/// <summary> /// Loads instance from XML. /// </summary> /// <param name="itemNode">Node to load XML from</param> /// <returns>true if sucessfully loaded from XML</returns> public override bool Load(XmlNode showNode) { // Check that node is proper type if (showNode.Name != ROOT_XML) return false; // Read base properties out base.ReadContentElements(showNode); // Read other elements foreach (XmlNode propNode in showNode.ChildNodes) { XmlElements element; if (!Enum.TryParse<XmlElements>(propNode.Name, out element)) continue; string value = propNode.InnerText; switch (element) { case XmlElements.Seasons: // Support for older versions this.Episodes.Clear(); foreach (XmlNode seasNode in propNode.ChildNodes) { foreach (XmlNode seasPropNode in seasNode.ChildNodes) { if(seasPropNode.Name == "Episodes") { foreach(XmlNode epNode in seasPropNode.ChildNodes) { TvEpisode episode = new TvEpisode(this); episode.Load(epNode); Episodes.Add(episode); } } } } break; case XmlElements.Episodes: this.Episodes.Clear(); foreach(XmlNode epNode in propNode.ChildNodes) { TvEpisode episode = new TvEpisode(this); episode.Load(epNode); Episodes.Add(episode); } break; case XmlElements.DoMissing: bool doMissing; bool.TryParse(value, out doMissing); this.DoMissingCheck = doMissing; break; case XmlElements.IncludeInSchedule: bool include; bool.TryParse(value, out include); this.IncludeInSchedule = include; break; case XmlElements.AlternativeNameMatches: this.AlternativeNameMatches = new ObservableCollection<string>(); foreach (XmlNode matchNode in propNode.ChildNodes) if (!string.IsNullOrWhiteSpace(matchNode.InnerText)) this.AlternativeNameMatches.Add(matchNode.InnerText); break; case XmlElements.DvdEpisodeOrder: bool dvdOrder; bool.TryParse(value, out dvdOrder); this.DvdEpisodeOrder = dvdOrder; break; } } // Sucess return true; }
/// <summary> /// Constructor for cloning instance. /// </summary> /// <param name="episode">Instance to clone</param> public TvEpisode(TvEpisode episode) { this.Clone(episode); }
/// <summary> /// Get available torrents for episode /// </summary> /// <returns></returns> public virtual List<TvEpisodeTorrent> GetAvailableTvTorrents(TvEpisode episode) { throw new NotImplementedException(); }
/// <summary> /// Builds formatted file name for TV episode file from an existing file path. /// </summary> /// <param name="episode1">First episode in file</param> /// <param name="episode2">Second episode in file</param> /// <param name="fullPath">Path of file to be formatted</param> /// <returns>Path with file name formatted</returns> public string BuildTvFileName(TvEpisode episode1, TvEpisode episode2, string fullPath) { // Get file name string fileName = Path.GetFileNameWithoutExtension(fullPath); // Get info from file using simplify function (pulls out file words and categorizes them) FileHelper.OptionalSimplifyRemoves simplifyOptions = FileHelper.OptionalSimplifyRemoves.Year; FileHelper.SimplifyStringResults simplifyResults = FileHelper.BuildSimplifyResults(fileName, false, false, simplifyOptions, true, false, false, false); // Build file name string buildName = BuildFileName(episode1, episode2, simplifyResults); // Remove unsafe file characters and add extension return FileHelper.GetSafeFileName(buildName); }
/// <summary> /// Build a combined episode name for 2 episodes. Find commanality between both names /// and produces a joined name. e.g. From "The Final (part 1)" & "The Fina1 (part 2) the /// method will return "The Final (Part 1 & 2)" /// </summary> /// <param name="episode1">First episode</param> /// <param name="episode2">Second epsiode</param> /// <returns>Joined episod names from both episodes</returns> public string BuildEpisodeName(TvEpisode episode1, TvEpisode episode2) { string epName; if (episode2 != null && !string.IsNullOrEmpty(episode2.DisplayName) && episode2.DatabaseNumber > 0) { char[] ep1Chars = episode1.DisplayName.ToCharArray(); char[] ep2Chars = episode2.DisplayName.ToCharArray(); // Check if name have any similarities at start int matchLen = 0; for (int i = 0; i < Math.Min(ep1Chars.Length, ep2Chars.Length); i++) { if (ep1Chars[i] == ep2Chars[i]) { matchLen++; } else { break; } } if (matchLen > 3) { List <char> name1Build = ep1Chars.ToList(); List <char> name2Build = ep2Chars.ToList(); // Go through characters of episode names and remove identicals bool closingBracketFound = false; for (int i = Math.Min(ep1Chars.Length, ep2Chars.Length) - 1; i >= 0; i--) { if (ep1Chars[i] == ep2Chars[i]) { if (ep1Chars[i] == ')' && !closingBracketFound) { closingBracketFound = true; name1Build.RemoveAt(i); } name2Build.RemoveAt(i); } } epName = new String(name1Build.ToArray()) + " & " + new String(name2Build.ToArray()); if (closingBracketFound) { epName += ")"; } } else { epName = episode1.DisplayName + " & " + episode2.DisplayName; } } else { epName = episode1.DisplayName; } return(epName.Trim()); }
/// <summary> /// Build file name for TV or movie file. /// </summary> /// <param name="movieName">Name of movie</param> /// <param name="episode1">First episode in file</param> /// <param name="episode2">Second episode in file</param> /// <param name="date">Date item was released</param> /// <param name="simplifyResults">File name simplifying results</param> /// <param name="differentiator"></param> /// <returns></returns> private string BuildFileName(string movieName, TvEpisode episode1, TvEpisode episode2, int year, FileHelper.SimplifyStringResults simplifyResults, string differentiator) { // Init file name string fileName = string.Empty; // Loop through each portion of format and add it to the file names foreach (FileNamePortion portion in this.Format) { // Get string value for current portion string value = portion.Header; switch (portion.Type) { case FileWordType.MovieName: value = movieName; break; case FileWordType.ShowName: value = episode1.Show.DatabaseName; break; case FileWordType.EpisodeName: value = BuildEpisodeName(episode1, episode2); break; case FileWordType.EpisodeNumber: value = EpisodeFormat.BuildEpisodeString(episode1, episode2); break; case FileWordType.Year: value = year.ToString(); break; case FileWordType.FilePart: if (!GetFileWord(simplifyResults, portion.Type, out value)) { value = differentiator; } break; default: GetFileWord(simplifyResults, portion.Type, out value); break; } // Add portion with header/footer and apply option if (!string.IsNullOrWhiteSpace(value)) { // Add header fileName += portion.Header; // Apply case options switch (portion.CaseOption) { case CaseOptionType.None: break; case CaseOptionType.LowerCase: value = value.ToLower(); break; case CaseOptionType.UpperCase: value = value.ToUpper(); break; } // Apply whitespace options fileName += Regex.Replace(value, @"\s+", portion.Whitespace); // Add footer fileName += portion.Footer; } } // Return file name that was built return(fileName); }
/// <summary> /// Build file name for TV epiosode file. /// </summary> /// <param name="episode1">First episode in file</param> /// <param name="episode2">Second episode in file (if any)</param> /// <param name="simplifyResults">File name simplifying results</param> /// <returns>Resulting formatted file name string</returns> private string BuildFileName(TvEpisode episode1, TvEpisode episode2, FileHelper.SimplifyStringResults simplifyResults) { return(BuildFileName(string.Empty, episode1, episode2, 1, simplifyResults, string.Empty)); }
/// <summary> /// Build file name for TV epiosode file. /// </summary> /// <param name="episode1">First episode in file</param> /// <param name="episode2">Second episode in file (if any)</param> /// <param name="simplifyResults">File name simplifying results</param> /// <returns>Resulting formatted file name string</returns> private string BuildFileName(TvEpisode episode1, TvEpisode episode2, FileHelper.SimplifyStringResults simplifyResults) { return BuildFileName(string.Empty, episode1, episode2, 1, simplifyResults, string.Empty); }
/// <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); } }
/// <summary> /// Find an episode from the show. /// </summary> /// <param name="seasonNumber">The season of the episode</param> /// <param name="episodeNumber">The number of the epsiode within the season</param> /// <param name="episode">The matched episode if one is found, else null</param> /// <returns>true if the episode was found, false otherwise</returns> public bool FindEpisode(int seasonNumber, int episodeNumber, bool databaseNum, out TvEpisode episode) { episode = null; foreach (TvEpisode ep in this.Episodes) if (ep.Season == seasonNumber && ((ep.DisplayNumber == episodeNumber && !databaseNum) || (ep.DatabaseNumber == episodeNumber && databaseNum))) { episode = ep; return true; } return false; }
/// <summary> /// Open file dialog so user can locate episode in file directory. /// </summary> /// <param name="showActionModifer">Whether to display action modifier after path is selected</param> /// <param name="copyAction">Whether to copy file, move otherwise</param> /// <param name="items">Organization items to add move/copy action to once located</param> /// <returns>true if locate was sucessful</returns> public bool UserLocate(bool showActionModifer, bool copyAction, out List <OrgItem> items) { // Initialize org items items = new List <OrgItem>(); // Open file dialog VistaOpenFileDialog ofd = new VistaOpenFileDialog(); ofd.Filter = "All Files|*.*"; if (!(bool)ofd.ShowDialog()) { return(false); } // Try to get episode information from file int fileEpSeason, fileEpNumber1, fileEpNumber2; bool fileInfoFound = FileHelper.GetEpisodeInfo(ofd.FileName, this.Show.DatabaseName, out fileEpSeason, out fileEpNumber1, out fileEpNumber2); // Assign episodes TvEpisode ep1 = this; TvEpisode ep2 = null; // Locate could be for double episode file, only taken if one of the episode from file matches selected if (fileInfoFound && fileEpSeason == this.Season) { if (fileEpNumber1 == this.DisplayNumber) { if (fileEpNumber2 > 0) { show.FindEpisode(ep1.Season, fileEpNumber2, false, out ep2); } else { ep2 = null; } } else if (fileEpNumber2 == this.DisplayNumber) { if (fileEpNumber1 > 0 && show.FindEpisode(this.Season, fileEpNumber1, false, out ep1)) { ep2 = this; } else { ep1 = this; } } } // Build org item OrgAction action = copyAction ? OrgAction.Copy : OrgAction.Move; string destination = show.BuildFilePath(ep1, ep2, Path.GetExtension(ofd.FileName)); OrgItem item = new OrgItem(OrgStatus.Found, action, ofd.FileName, destination, ep1, ep2, FileCategory.TvVideo, null); // Display modifier if (showActionModifer) { OrgItemEditorWindow editor = new OrgItemEditorWindow(item); editor.ShowDialog(); // Add results if valid if (editor.Results == null) { return(false); } items.Add(editor.Results); return(true); } items.Add(item); return(true); }
/// <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); }
/// <summary> /// Build a combined episode name for 2 episodes. Find commanality between both names /// and produces a joined name. e.g. From "The Final (part 1)" & "The Fina1 (part 2) the /// method will return "The Final (Part 1 & 2)" /// </summary> /// <param name="episode1">First episode</param> /// <param name="episode2">Second epsiode</param> /// <returns>Joined episod names from both episodes</returns> public string BuildEpisodeName(TvEpisode episode1, TvEpisode episode2) { string epName; if (episode2 != null && !string.IsNullOrEmpty(episode2.DisplayName) && episode2.DatabaseNumber > 0) { char[] ep1Chars = episode1.DisplayName.ToCharArray(); char[] ep2Chars = episode2.DisplayName.ToCharArray(); // Check if name have any similarities at start int matchLen = 0; for (int i = 0; i < Math.Min(ep1Chars.Length, ep2Chars.Length); i++) if (ep1Chars[i] == ep2Chars[i]) matchLen++; else break; if (matchLen > 3) { List<char> name1Build = ep1Chars.ToList(); List<char> name2Build = ep2Chars.ToList(); // Go through characters of episode names and remove identicals bool closingBracketFound = false; for (int i = Math.Min(ep1Chars.Length, ep2Chars.Length) - 1; i >= 0; i--) if (ep1Chars[i] == ep2Chars[i]) { if (ep1Chars[i] == ')' && !closingBracketFound) { closingBracketFound = true; name1Build.RemoveAt(i); } name2Build.RemoveAt(i); } epName = new String(name1Build.ToArray()) + " & " + new String(name2Build.ToArray()); if (closingBracketFound) epName += ")"; } else epName = episode1.DisplayName + " & " + episode2.DisplayName; } else epName = episode1.DisplayName; return epName.Trim(); }
/// <summary> /// Update current update from another item's data /// </summary> /// <param name="item">item to be copied</param> public void Clone(OrgItem item) { this.Status = item.Status; this.Progress = 0; this.Action = item.Action; this.SourcePath = item.SourcePath; this.DestinationPath = item.DestinationPath; this.TvEpisode = item.TvEpisode; this.TvEpisode2 = item.TvEpisode2; this.TorrentTvEpisode = item.TorrentTvEpisode; this.Category = item.Category; this.Enable = item.Enable; this.Movie = item.Movie; this.ScanDirectory = item.ScanDirectory; this.Replace = item.Replace; this.Number = item.Number; this.QueueStatus = item.QueueStatus; this.ActionComplete = item.ActionComplete; this.Notes = item.Notes; this.ActionTime = item.ActionTime; }
/// <summary> /// Build the file name string for a given episode /// </summary> /// <param name="episode1">Episode to build file name for</param> /// <param name="episode2">Second episode for double-episode files</param> /// <returns>Episode file name string</returns> public string BuildFileName(TvEpisode episode1, TvEpisode episode2, string path) { return(Settings.TvFileFormat.BuildTvFileName(episode1, episode2, path)); }
/// <summary> /// Constructor for directory scan for file matched to TV episode. /// </summary> /// <param name="action">action to be performed</param> /// <param name="file">source path</param> /// <param name="destination">destination path</param> /// <param name="episode">TV episode for file</param> /// <param name="episode2">2nd Tv epsidoe for file</param> /// <param name="category">file category</param> public OrgItem(OrgAction action, string file, string destination, TvEpisode episode, TvEpisode episode2, FileCategory category, OrgFolder scanDir) : this() { this.Action = action; this.SourcePath = file; this.DestinationPath = destination; this.TvEpisode = new TvEpisode(episode); this.Category = category; if (episode2 != null) this.TvEpisode2 = new TvEpisode(episode2); this.Enable = false; this.ScanDirectory = scanDir; this.Number = 0; }
/// <summary> /// Loads instance from XML. /// </summary> /// <param name="itemNode">Node to load XML from</param> /// <returns>true if sucessfully loaded from XML</returns> public override bool Load(XmlNode showNode) { // Check that node is proper type if (showNode.Name != ROOT_XML) { return(false); } // Read base properties out base.ReadContentElements(showNode); // Read other elements foreach (XmlNode propNode in showNode.ChildNodes) { XmlElements element; if (!Enum.TryParse <XmlElements>(propNode.Name, out element)) { continue; } string value = propNode.InnerText; switch (element) { case XmlElements.Seasons: // Support for older versions this.Episodes.Clear(); foreach (XmlNode seasNode in propNode.ChildNodes) { foreach (XmlNode seasPropNode in seasNode.ChildNodes) { if (seasPropNode.Name == "Episodes") { foreach (XmlNode epNode in seasPropNode.ChildNodes) { TvEpisode episode = new TvEpisode(this); episode.Load(epNode); Episodes.Add(episode); } } } } break; case XmlElements.Episodes: this.Episodes.Clear(); foreach (XmlNode epNode in propNode.ChildNodes) { TvEpisode episode = new TvEpisode(this); episode.Load(epNode); Episodes.Add(episode); } break; case XmlElements.DoMissing: bool doMissing; bool.TryParse(value, out doMissing); this.DoMissingCheck = doMissing; break; case XmlElements.IncludeInSchedule: bool include; bool.TryParse(value, out include); this.IncludeInSchedule = include; break; case XmlElements.AlternativeNameMatches: this.AlternativeNameMatches = new ObservableCollection <string>(); foreach (XmlNode matchNode in propNode.ChildNodes) { if (!string.IsNullOrWhiteSpace(matchNode.InnerText)) { this.AlternativeNameMatches.Add(matchNode.InnerText); } } break; case XmlElements.DvdEpisodeOrder: bool dvdOrder; bool.TryParse(value, out dvdOrder); this.DvdEpisodeOrder = dvdOrder; break; } } // Sucess return(true); }
/// <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); }
private void AddNewEpisode() { if (show == null) return; TvEpisode ep = new TvEpisode("", show, 1, -1, DateTime.Now.ToString(), ""); ep.UseDatabaseAirDate = false; ep.UseDatabaseOverview = false; ep.UseDatabaseNumber = false; ep.UseDatabaseName = false; ep.UserName = ""; ep.UserNumber = 1; if (show.Episodes.Count > 0) { TvEpisode lastEp = show.Episodes.Last(); ep.Season = lastEp.Season; ep.UserNumber = lastEp.DisplayNumber + 1; } EpisodeEditorWindow editor = new EpisodeEditorWindow(ep); editor.ShowDialog(); if (editor.Results != null) { // Check if episode already exists if (this.show.FindEpisode(editor.Results.Season, editor.Results.DisplayNumber, false, out ep)) { MessageBox.Show("Episode with this season/number already exists. Select edit on existing episode to make changes to it (if not visible try looking with ignored episodes filter).", "Already exists"); return; } show.Episodes.Add(editor.Results); Organization.Shows.Save(); } }
/// <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; } }
/// <summary> /// Build file name for TV or movie file. /// </summary> /// <param name="movieName">Name of movie</param> /// <param name="episode1">First episode in file</param> /// <param name="episode2">Second episode in file</param> /// <param name="date">Date item was released</param> /// <param name="simplifyResults">File name simplifying results</param> /// <param name="differentiator"></param> /// <returns></returns> private string BuildFileName(string movieName, TvEpisode episode1, TvEpisode episode2, int year, FileHelper.SimplifyStringResults simplifyResults, string differentiator) { // Init file name string fileName = string.Empty; // Loop through each portion of format and add it to the file names foreach (FileNamePortion portion in this.Format) { // Get string value for current portion string value = portion.Header; switch (portion.Type) { case FileWordType.MovieName: value = movieName; break; case FileWordType.ShowName: value = episode1.Show.DatabaseName; break; case FileWordType.EpisodeName: value = BuildEpisodeName(episode1, episode2); break; case FileWordType.EpisodeNumber: value = EpisodeFormat.BuildEpisodeString(episode1, episode2); break; case FileWordType.Year: value = year.ToString(); break; case FileWordType.FilePart: if (!GetFileWord(simplifyResults, portion.Type, out value)) value = differentiator; break; default: GetFileWord(simplifyResults, portion.Type, out value); break; } // Add portion with header/footer and apply option if (!string.IsNullOrWhiteSpace(value)) { // Add header fileName += portion.Header; // Apply case options switch (portion.CaseOption) { case CaseOptionType.None: break; case CaseOptionType.LowerCase: value = value.ToLower(); break; case CaseOptionType.UpperCase: value = value.ToUpper(); break; } // Apply whitespace options fileName += Regex.Replace(value, @"\s+", portion.Whitespace); // Add footer fileName += portion.Footer; } } // Return file name that was built return fileName; }
/// <summary> /// Find an episode from the show. /// </summary> /// <param name="seasonNumber">The season of the episode</param> /// <param name="episodeNumber">The number of the epsiode within the season</param> /// <param name="episode">The matched episode if one is found, else null</param> /// <returns>true if the episode was found, false otherwise</returns> public bool FindEpisode(int seasonNumber, int episodeNumber, bool databaseNum, out TvEpisode episode) { episode = null; foreach (TvEpisode ep in this.Episodes) { if (ep.Season == seasonNumber && ((ep.DisplayNumber == episodeNumber && !databaseNum) || (ep.DatabaseNumber == episodeNumber && databaseNum))) { episode = ep; return(true); } } return(false); }
/// <summary> /// Compares 2 content instances based on their Name property. /// </summary> /// <param name="x">The first movie.</param> /// <param name="y">The second movie.</param> /// <returns>The comparison results </returns> public static int CompareByAirDate(TvEpisode x, TvEpisode y) { if (x == null) { if (y == null) return 0; else return -1; } else { if (y == null) return 1; else return x.DisplayAirDate.CompareTo(y.DisplayAirDate); } }
/// <summary> /// Build full path of file for moving/copying TV episode file. /// </summary> /// <param name="episode1">Episode to build file path for</param> /// <param name="episode2">Second episode for double-episode files</param> /// <param name="extension">File extension for path</param> /// <returns>Episode file path</returns> public string BuildFilePath(TvEpisode episode1, TvEpisode episode2, string path) { // New season created to prevent exception if episode doesn't fall into a valid season return(this.Path + "\\" + episode1.SeasonName + "\\" + BuildFileName(episode1, episode2, path) + System.IO.Path.GetExtension(path).ToLower()); }
public TvEpisodeTorrent GetBestTvTorrent(TvEpisode episode) { List <TvEpisodeTorrent> availableTorrents = GetAvailableTvTorrents(episode); if (availableTorrents.Count == 0) { return(null); } Dictionary <TorrentQuality, TvEpisodeTorrent> qualityTorrents = new Dictionary <TorrentQuality, TvEpisodeTorrent>(); foreach (TvEpisodeTorrent torrent in availableTorrents) { TorrentQuality quality = torrent.Quality; if (!qualityTorrents.ContainsKey(quality)) { qualityTorrents.Add(quality, null); } if (qualityTorrents[quality] == null) { qualityTorrents[quality] = torrent; } } // Go through matches to find closest quality to preferred TorrentQuality closestQuality = TorrentQuality.Sd480p; if (qualityTorrents.ContainsKey(Settings.General.PreferredTorrentQuality)) { closestQuality = Settings.General.PreferredTorrentQuality; } else { switch (Settings.General.PreferredTorrentQuality) { case TorrentQuality.Sd480p: if (qualityTorrents.ContainsKey(TorrentQuality.Hd720p)) { closestQuality = TorrentQuality.Hd720p; } else { closestQuality = TorrentQuality.Hd1080p; } break; case TorrentQuality.Hd720p: if (qualityTorrents.ContainsKey(TorrentQuality.Sd480p)) { closestQuality = TorrentQuality.Sd480p; } else { closestQuality = TorrentQuality.Hd1080p; } break; case TorrentQuality.Hd1080p: if (qualityTorrents.ContainsKey(TorrentQuality.Hd720p)) { closestQuality = TorrentQuality.Hd720p; } else { closestQuality = TorrentQuality.Sd480p; } break; default: throw new Exception("Unknown torrent quality"); } } TvEpisodeTorrent closestTorrent = qualityTorrents[closestQuality]; UpdateTorrentLinks(closestTorrent); return(closestTorrent); }
/// <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); }
public override List <TvEpisodeTorrent> GetAvailableTvTorrents(TvEpisode episode) { // Download show episodes page string showPageUrl = BASE_URL + "/torrents.php?search=" + episode.BuildEpString().Replace(" ", "+"); Console.WriteLine("RARBG Pre " + DateTime.Now.ToString()); Limiter.DoWait(); Console.WriteLine("RARBG Post " + DateTime.Now.ToString()); WebClient client = new WebClient(); client.Headers.Add("User-Agent: Other"); string data = client.DownloadString(showPageUrl); Regex baseRegex = new Regex(@"<tr\s+class=.lista2.>\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*</tr>"); Regex titleRegex = new Regex(@"title=""([^""]+)"""); Regex urlRegex = new Regex("href=\"(/torrent/[^\"]+)\""); Regex sizeRegex = new Regex(@">(\S+)\s+MB<"); MatchCollection matches = baseRegex.Matches(data); List <TvEpisodeTorrent> torrents = new List <TvEpisodeTorrent>(); foreach (Match match in matches) { string name; Match titleMatch = titleRegex.Match(match.Groups[2].Value); if (titleMatch.Success) { name = titleMatch.Groups[1].Value; } else { continue; } string pageUrl; Match urlMatch = urlRegex.Match(match.Groups[2].Value); if (urlMatch.Success) { pageUrl = BASE_URL + urlMatch.Groups[1].Value; } else { continue; } MatchCollection showMatches = episode.Show.MatchFileToContent(name); bool matchShow = showMatches != null && showMatches.Count > 0; int season, ep1, ep2; if (matchShow && FileHelper.GetEpisodeInfo(name, episode.Show.DisplayName, out season, out ep1, out ep2, true) && episode.Season == season && episode.DisplayNumber == ep1) { // Don't want to get torrent with a bunch of episodes (double is okay) if (ep2 > 0 && ep2 - ep1 > 1) { continue; } TorrentQuality quality = TorrentQuality.Sd480p; if (name.ToLower().Contains("720p")) { quality = TorrentQuality.Hd720p; } else if (name.ToLower().Contains("1080p")) { quality = TorrentQuality.Hd1080p; } TvEpisodeTorrent torrentEp = new TvEpisodeTorrent(); torrentEp.Url = showPageUrl; torrentEp.Season = season; torrentEp.Episode = ep1; torrentEp.Episode2 = ep2; torrentEp.Quality = quality; torrentEp.Title = name; torrentEp.PageUrl = pageUrl; torrents.Add(torrentEp); } } return(torrents); }
public static async Task <TvEpisodeTorrent> GetEpisodeTorrentAsync(TvEpisode episode) { TvEpisodeTorrent result = await Task.Run(() => GetEpisodeTorrent(episode)); return(result); }
/// <summary> /// Build the file name string for a given episode /// </summary> /// <param name="episode1">Episode to build file name for</param> /// <param name="episode2">Second episode for double-episode files</param> /// <returns>Episode file name string</returns> public string BuildFileName(TvEpisode episode1, TvEpisode episode2, string path) { return Settings.TvFileFormat.BuildTvFileName(episode1, episode2, path); }
/// <summary> /// Get available torrents for episode /// </summary> /// <returns></returns> public virtual List <TvEpisodeTorrent> GetAvailableTvTorrents(TvEpisode episode) { throw new NotImplementedException(); }
/// <summary> /// Build full path of file for moving/copying TV episode file. /// </summary> /// <param name="episode1">Episode to build file path for</param> /// <param name="episode2">Second episode for double-episode files</param> /// <param name="extension">File extension for path</param> /// <returns>Episode file path</returns> public string BuildFilePath(TvEpisode episode1, TvEpisode episode2, string path) { // New season created to prevent exception if episode doesn't fall into a valid season return this.Path + "\\" + episode1.SeasonName + "\\" + BuildFileName(episode1, episode2, path) + System.IO.Path.GetExtension(path).ToLower(); }
/// <summary> /// Runs an episode through filter. /// </summary> /// <param name="ep">The episode to filter</param> /// <returns>True if the episode makes it through filter</returns> public bool FilterEpisode(TvEpisode ep) { if (ep.Ignored && this.Type != FilterType.Ignored) { return(false); } switch (this.Type) { case FilterType.All: return(true); case FilterType.Regular: if (ep.Season > 0) { return(true); } break; case FilterType.Ignored: return(ep.Ignored); case FilterType.Located: if (ep.Missing != MissingStatus.Missing) { return(true); } break; case FilterType.Missing: if (ep.Season > 0 && ep.Missing == MissingStatus.Missing && ep.Aired) { return(true); } break; case FilterType.InScanDir: if (ep.Missing == MissingStatus.InScanDirectory) { return(true); } break; case FilterType.Season: if (ep.Season == this.Season) { return(true); } break; case FilterType.Unaired: if (!ep.Aired) { return(true); } break; default: throw new Exception("Unknown filter type!"); } return(false); }
/// <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); }
/// <summary> /// Runs an episode through filter. /// </summary> /// <param name="ep">The episode to filter</param> /// <returns>True if the episode makes it through filter</returns> public bool FilterEpisode(TvEpisode ep) { if (ep.Ignored && this.Type != FilterType.Ignored) return false; switch (this.Type) { case FilterType.All: return true; case FilterType.Regular: if (ep.Season > 0) return true; break; case FilterType.Ignored: return ep.Ignored; case FilterType.Located: if (ep.Missing != MissingStatus.Missing) return true; break; case FilterType.Missing: if (ep.Season > 0 && ep.Missing == MissingStatus.Missing && ep.Aired) return true; break; case FilterType.InScanDir: if (ep.Missing == MissingStatus.InScanDirectory) return true; break; case FilterType.Season: if (ep.Season == this.Season) return true; break; case FilterType.Unaired: if (!ep.Aired) return true; break; default: throw new Exception("Unknown filter type!"); } return false; }
public override List<TvEpisodeTorrent> GetAvailableTvTorrents(TvEpisode episode) { // Download show episodes page string showPageUrl = BASE_URL + "/torrents.php?search=" + episode.BuildEpString().Replace(" ", "+"); WebClient client = new WebClient(); client.Headers.Add("User-Agent: Other"); string data = client.DownloadString(showPageUrl); Regex baseRegex = new Regex(@"<tr\s+class=.lista2.>\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*(<td.*?/td>)\s*</tr>"); Regex titleRegex = new Regex(@"title=""([^""]+)"""); Regex urlRegex = new Regex("href=\"(/torrent/[^\"]+)\""); Regex sizeRegex = new Regex(@">(\S+)\s+MB<"); MatchCollection matches = baseRegex.Matches(data); List<TvEpisodeTorrent> torrents = new List<TvEpisodeTorrent>(); foreach (Match match in matches) { string name; Match titleMatch = titleRegex.Match(match.Groups[2].Value); if (titleMatch.Success) name = titleMatch.Groups[1].Value; else continue; string pageUrl; Match urlMatch = urlRegex.Match(match.Groups[2].Value); if (urlMatch.Success) pageUrl = BASE_URL + urlMatch.Groups[1].Value; else continue; MatchCollection showMatches = episode.Show.MatchFileToContent(name); bool matchShow = showMatches != null && showMatches.Count > 0; int season, ep1, ep2; if (matchShow && FileHelper.GetEpisodeInfo(name, episode.Show.DisplayName, out season, out ep1, out ep2, true) && episode.Season == season && episode.DisplayNumber == ep1) { // Don't want to get torrent with a bunch of episodes (double is okay) if (ep2 > 0 && ep2 - ep1 > 1) continue; TorrentQuality quality = TorrentQuality.Sd480p; if (name.ToLower().Contains("720p")) quality = TorrentQuality.Hd720p; else if (name.ToLower().Contains("1080p")) quality = TorrentQuality.Hd1080p; TvEpisodeTorrent torrentEp = new TvEpisodeTorrent(); torrentEp.Url = showPageUrl; torrentEp.Season = season; torrentEp.Episode = ep1; torrentEp.Episode2 = ep2; torrentEp.Quality = quality; torrentEp.Title = name; torrentEp.PageUrl = pageUrl; torrents.Add(torrentEp); } } return torrents; }