/// <summary> /// Search thread operation. Performs a single search for content from string passed in through arguments. /// </summary> /// <param name="stateInfo">Arguments for thread</param> private void SearchThread(object stateInfo) { // Get arguments object[] args = (object[])stateInfo; int statusIndex = (int)args[0]; int searchIndex = (int)args[1]; string search = (string)args[2]; string folderPath = (string)args[3]; string movieFolder = (string)args[4]; int year = (int)args[5]; ContentSearchMod mods = (ContentSearchMod)args[6]; Content knownContent = (Content)args[7]; // Check search is still active List <SearchResult> matches = new List <SearchResult>(); if (searchStatus.ContainsKey(statusIndex)) { DoMatch(search, folderPath, movieFolder, year, mods, out matches, knownContent); } // Check search is still active then put results into status lock (searchLock) if (searchStatus.ContainsKey(statusIndex)) { searchStatus[statusIndex].SetSearchMatches(searchIndex, matches); } }
/// <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)); }
/// <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); }
/// <summary> /// Search database for content and find a match from results. /// </summary> /// <param name="search">Search string to match content to</param> /// <param name="folderPath">Path of folder containing content</param> /// <param name="rootFolder">Root folder containing content folder</param> /// <param name="year">Year to match to content</param> /// <param name="result">resulting match found from search</param> /// <returns>whether match was successful</returns> private bool DoMatch(string search, string folderPath, string rootFolder, int year, ContentSearchMod baseMods, out List<SearchResult> matches, Content knownContent) { /// Debug notification OnDebugNotificationd("Performing database search for: " + search); // Search for content List<Content> searchResults; if (knownContent == null) searchResults = PerformSearch(search, false); else searchResults = new List<Content>() { knownContent }; // Initialize resutls matches = new List<SearchResult>(); // Go through results if (searchResults != null) foreach (Content searchResult in searchResults) { OnDebugNotificationd("Attempting to match to database entry: " + searchResult); SearchResult result = new SearchResult(); result.Mods = baseMods; // Verify year in result matches year from folder (if any) if (year != -1 && Math.Abs(year - searchResult.DatabaseYear) > 2) continue; // Check if search string match results string string simplifiedSearch = FileHelper.SimplifyFileName(search); string dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName); bool theAddedToMatch; bool singleLetterDiff; // Try basic match bool match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; // Try match with year removed if (!match) { simplifiedSearch = FileHelper.SimplifyFileName(search, true, true, false); dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName, true, true, false); match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; } // Try match with country removed if (!match) { simplifiedSearch = FileHelper.SimplifyFileName(search, true, true, true); dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName, true, true, true); match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); if (match) result.Mods |= ContentSearchMod.WordsRemoved; result.MatchedString = simplifiedSearch; } // Try match with spaces removed if (!match) { string dirNoSpc = simplifiedSearch.Replace(" ", ""); string nameNoSpc = dbContentName.Replace(" ", ""); match = FileHelper.CompareStrings(dirNoSpc, nameNoSpc, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; if (match) result.Mods |= ContentSearchMod.SpaceRemoved; } // Try match with year added to content name if (!match) { simplifiedSearch = FileHelper.SimplifyFileName(search); dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName + " " + searchResult.DatabaseYear.ToString()); match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; } if (theAddedToMatch) result.Mods |= ContentSearchMod.TheAdded; if (singleLetterDiff) result.Mods |= ContentSearchMod.SingleLetterAdded; // Match notification if (match) OnDebugNotificationd("Matched with following mods: " + result.Mods); // No match, next result! if (!match) continue; // Set results folder/path result.Content = searchResult; result.Content.RootFolder = rootFolder; if (string.IsNullOrEmpty(folderPath)) result.Content.Path = result.Content.BuildFolderPath(); else result.Content.Path = folderPath; // Save results matches.Add(result); } return matches.Count > 0; }
/// <summary> /// Simplifies an input string with various options. /// </summary> /// <param name="input">String to be simplified</param> /// <param name="removeFirst">Whether to always remove first word during simplification</param> /// <param name="removeLast">Whether to always remove last word during simplification</param> /// <param name="options">Selection of optional remove words</param> /// <param name="disableRemAfter">Disable removing of words that follow defined words to remove</param> /// <param name="wordSplitEn">Enables splitting words in the string using dictionary (e.g. "howimetyourmother" split to "how i met your mother"</param> /// <param name="removeWhitespace">Whether to remove extra whitespace</param> /// <returns>Simplified string results</returns> public static SimplifyStringResults BuildSimplifyResults(string input, bool removeFirst, bool removeLast, OptionalSimplifyRemoves options, bool disableRemAfter, bool wordSplitEn, bool removeWhitespace, bool removeBrackContents) { // All lowercase string simplifiedName = input.ToLower().Replace("&", "and"); // Initialize string modifications ContentSearchMod mods = ContentSearchMod.None; // Remove contents inside any brackets if (removeBrackContents) { simplifiedName = Regex.Replace(simplifiedName, @"\([^\)]*\)", " "); mods |= ContentSearchMod.BrackRemoval; } // Remove unneeded characters: ',!,?,(,),: simplifiedName = Regex.Replace(simplifiedName, @"[']+", ""); simplifiedName = Regex.Replace(simplifiedName, @"[!\?\u0028\u0029\:\]\[]+", " "); // Replace seperators with spaces if (removeWhitespace) { simplifiedName = Regex.Replace(simplifiedName, @"\W+|_", " "); } // Initialize removed words dictionary Dictionary <FileWordType, List <string> > removeFileWords = new Dictionary <FileWordType, List <string> >(); // Process each optional remove word for (int j = 0; j < OptionalRemoveWords.Length; j++) { if (((int)options & (int)Math.Pow(2, j)) > 0) { bool removed; simplifiedName = RemoveWord(disableRemAfter, simplifiedName, removeFileWords, OptionalRemoveWords[j], out removed); if (removed) { if ((OptionalSimplifyRemoves)j == OptionalSimplifyRemoves.Year || (OptionalSimplifyRemoves)j == OptionalSimplifyRemoves.YearAndFollowing) { mods |= ContentSearchMod.YearRemoved; } else { mods |= ContentSearchMod.WordsRemoved; } } } } // Process always remove words foreach (RemoveFileWord remWord in AlwaysRemoveWords) { simplifiedName = RemoveWord(disableRemAfter, simplifiedName, removeFileWords, remWord); } // Remove first word if (removeFirst) { Match firstWordMatch = Regex.Match(simplifiedName, @"^\W*\w+"); if (firstWordMatch.Success) { simplifiedName = simplifiedName.Remove(firstWordMatch.Index, firstWordMatch.Length); } mods |= ContentSearchMod.WordsRemoved; } // Remove Last word if (removeLast) { Match lastWordMatch = Regex.Match(simplifiedName, @"(\w+\W*)$"); if (lastWordMatch.Success) { simplifiedName = simplifiedName.Remove(lastWordMatch.Index, lastWordMatch.Length); } mods |= ContentSearchMod.WordsRemoved; } //// Don't allow removal of both first and last words //else if (removeFirst && removeLast) // return null; // Word splitting if (wordSplitEn) { // Seperate input by whitespace string[] words = simplifiedName.Split(' '); // Build new string with words split up bool split = false; simplifiedName = string.Empty; foreach (string word in words) { string newWord; if (WordHelper.TrySplitWords(word, out newWord)) { split = true; } simplifiedName += newWord + " "; } if (split) { mods |= ContentSearchMod.WordSlit; } } // Trim simplifiedName = simplifiedName.Trim().Replace(" ", " "); return(new SimplifyStringResults(simplifiedName, removeFileWords, mods)); }
/// <summary> /// Constructor with known properties /// </summary> /// <param name="text">Simplified string</param> /// <param name="removedWords">Dictionary of words that were removed</param> /// <param name="mods">Modifications made during simplifcation</param> public SimplifyStringResults(string text, Dictionary <FileWordType, List <string> > removedWords, ContentSearchMod mods) { this.SimplifiedString = text; this.RemovedWords = removedWords; this.Modifications = mods; }
/// <summary> /// Search database for content and find a match from results. /// </summary> /// <param name="search">Search string to match content to</param> /// <param name="folderPath">Path of folder containing content</param> /// <param name="rootFolder">Root folder containing content folder</param> /// <param name="year">Year to match to content</param> /// <param name="result">resulting match found from search</param> /// <returns>whether match was successful</returns> private bool DoMatch(string search, string folderPath, string rootFolder, int year, ContentSearchMod baseMods, out List <SearchResult> matches, Content knownContent) { /// Debug notification OnDebugNotificationd("Performing database search for: " + search); // Search for content List <Content> searchResults; if (knownContent == null) { searchResults = PerformSearch(search, false); } else { searchResults = new List <Content>() { knownContent } }; // Initialize resutls matches = new List <SearchResult>(); // Go through results if (searchResults != null) { foreach (Content searchResult in searchResults) { OnDebugNotificationd("Attempting to match to database entry: " + searchResult); SearchResult result = new SearchResult(); result.Mods = baseMods; // Verify year in result matches year from folder (if any) if (year != -1 && Math.Abs(year - searchResult.DatabaseYear) > 2) { continue; } // Check if search string match results string string simplifiedSearch = FileHelper.SimplifyFileName(search); string dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName); bool theAddedToMatch; bool singleLetterDiff; // Try basic match bool match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; // Try match with year removed if (!match) { simplifiedSearch = FileHelper.SimplifyFileName(search, true, true, false); dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName, true, true, false); match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; } // Try match with country removed if (!match) { simplifiedSearch = FileHelper.SimplifyFileName(search, true, true, true); dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName, true, true, true); match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); if (match) { result.Mods |= ContentSearchMod.WordsRemoved; } result.MatchedString = simplifiedSearch; } // Try match with spaces removed if (!match) { string dirNoSpc = simplifiedSearch.Replace(" ", ""); string nameNoSpc = dbContentName.Replace(" ", ""); match = FileHelper.CompareStrings(dirNoSpc, nameNoSpc, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; if (match) { result.Mods |= ContentSearchMod.SpaceRemoved; } } // Try match with year added to content name if (!match) { simplifiedSearch = FileHelper.SimplifyFileName(search); dbContentName = FileHelper.SimplifyFileName(searchResult.DatabaseName + " " + searchResult.DatabaseYear.ToString()); match = FileHelper.CompareStrings(simplifiedSearch, dbContentName, out theAddedToMatch, out singleLetterDiff); result.MatchedString = simplifiedSearch; } if (theAddedToMatch) { result.Mods |= ContentSearchMod.TheAdded; } if (singleLetterDiff) { result.Mods |= ContentSearchMod.SingleLetterAdded; } // Match notification if (match) { OnDebugNotificationd("Matched with following mods: " + result.Mods); } // No match, next result! if (!match) { continue; } // Set results folder/path result.Content = searchResult; result.Content.RootFolder = rootFolder; if (string.IsNullOrEmpty(folderPath)) { result.Content.Path = result.Content.BuildFolderPath(); } else { result.Content.Path = folderPath; } // Save results matches.Add(result); } } return(matches.Count > 0); }
/// <summary> /// Constructor with known properties /// </summary> /// <param name="text">Simplified string</param> /// <param name="removedWords">Dictionary of words that were removed</param> /// <param name="mods">Modifications made during simplifcation</param> public SimplifyStringResults(string text, Dictionary<FileWordType, List<string>> removedWords, ContentSearchMod mods) { this.SimplifiedString = text; this.RemovedWords = removedWords; this.Modifications = mods; }