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> /// 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> /// 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); } }