/// <summary> /// Match type and episode. e.g. "ED1", "OP4a", "OVA2". /// </summary> /// <param name="word">the word</param> /// <param name="token">the token</param> /// <returns>true if the token matched</returns> private bool MatchTypeAndEpisodePattern(string word, Token token) { var numberBegin = ParserHelper.IndexOfFirstDigit(word); var prefix = StringHelper.SubstringWithCheck(word, 0, numberBegin); var category = Element.ElementCategory.ElementAnimeType; var options = new KeywordOptions(); if (KeywordManager.Instance.FindAndSet(KeywordManager.Normalize(prefix), ref category, ref options)) { _parser.Elements.Add(new Element(Element.ElementCategory.ElementAnimeType, prefix)); var number = word.Substring(numberBegin); if (MatchEpisodePatterns(number, token) || SetEpisodeNumber(number, token, true)) { var foundIdx = _parser.Tokens.IndexOf(token); if (foundIdx != -1) { token.Content = number; _parser.Tokens.Insert(foundIdx, new Token(options.Identifiable ? Token.TokenCategory.Identifier : Token.TokenCategory.Unknown, prefix, token.Enclosed)); } return(true); } } return(false); }
/// Adds a <code>category</code>, <code>options</code>, and <code>keywords</code> to the internal keywords list. private static void Add(Element.ElementCategory category, KeywordOptions options, IEnumerable <string> keywords) { var keys = GetKeywordContainer(category); foreach (var key in keywords.Where(k => !string.IsNullOrEmpty(k)) .Where(k => !keys.ContainsKey(k))) { keys[key] = new Keyword(category, options); } }
/// <summary> /// Finds a particular <code>keyword</code>. If found sets <code>category</code> and <code>options</code> to the found search result. /// </summary> /// <param name="keyword">the keyword to search for</param> /// <param name="category">the reference that will be set/changed to the found keyword category</param> /// <param name="options">the reference that will be set/changed to the found keyword options</param> /// <returns>if the keyword was found</returns> public bool FindAndSet(string keyword, ref Element.ElementCategory category, ref KeywordOptions options) { var keys = GetKeywordContainer(category); if (!keys.TryGetValue(keyword, out var foundEntry)) { return(false); } if (category == Element.ElementCategory.ElementUnknown) { category = foundEntry.Category; } else if (foundEntry.Category != category) { return(false); } options = foundEntry.Options; return(true); }
/** Search for anime keywords. */ private void SearchForKeywords() { for (var i = 0; i < Tokens.Count; i++) { var token = Tokens[i]; if (token.Category != Token.TokenCategory.Unknown) continue; var word = token.Content; word = word.Trim(" -".ToCharArray()); if (string.IsNullOrEmpty(word)) continue; // Don't bother if the word is a number that cannot be CRC if (word.Length != 8 && StringHelper.IsNumericString(word)) continue; var keyword = KeywordManager.Normalize(word); var category = Element.ElementCategory.ElementUnknown; var options = new KeywordOptions(); if (KeywordManager.Instance.FindAndSet(keyword, ref category, ref options)) { if (!Options.ParseReleaseGroup && category == Element.ElementCategory.ElementReleaseGroup) continue; if (!ParseHelper.IsElementCategorySearchable(category) || !options.Searchable) continue; if (ParseHelper.IsElementCategorySingular(category) && !Empty(category)) continue; if (category == Element.ElementCategory.ElementAnimeSeasonPrefix) { ParseHelper.CheckAndSetAnimeSeasonKeyword(token, i); continue; } if (category == Element.ElementCategory.ElementEpisodePrefix) { if (options.Valid) { ParseHelper.CheckExtentKeyword(Element.ElementCategory.ElementEpisodeNumber, i, token); continue; } } else if (category == Element.ElementCategory.ElementReleaseVersion) { word = word.Substring(1); } else if (category == Element.ElementCategory.ElementVolumePrefix) { ParseHelper.CheckExtentKeyword(Element.ElementCategory.ElementVolumeNumber, i, token); continue; } } else { if (Empty(Element.ElementCategory.ElementFileChecksum) && ParserHelper.IsCrc32(word)) { category = Element.ElementCategory.ElementFileChecksum; } else if (Empty(Element.ElementCategory.ElementVideoResolution) && ParserHelper.IsResolution(word)) { category = Element.ElementCategory.ElementVideoResolution; } } if (category != Element.ElementCategory.ElementUnknown) { Elements.Add(new Element(category, word)); if (options.Identifiable) { token.Category = Token.TokenCategory.Identifier; } } } }
/// <summary> /// Constructs a new Keyword /// </summary> /// <param name="category">the category of the keyword</param> /// <param name="options">the keyword's options</param> public Keyword(Element.ElementCategory category, KeywordOptions options) { Category = category; Options = options; }
static KeywordManager() { var optionsDefault = new KeywordOptions(); var optionsInvalid = new KeywordOptions(true, true, false); var optionsUnidentifiable = new KeywordOptions(false, true, true); var optionsUnidentifiableInvalid = new KeywordOptions(false, true, false); var optionsUnidentifiableUnsearchable = new KeywordOptions(false, false, true); Add(Element.ElementCategory.ElementAnimeSeasonPrefix, optionsUnidentifiable, new List <string> { "SAISON", "SEASON" }); Add(Element.ElementCategory.ElementAnimeType, optionsUnidentifiable, new List <string> { "GEKIJOUBAN", "MOVIE", "OAD", "OAV", "ONA", "OVA", "SPECIAL", "SPECIALS", "TV" }); Add(Element.ElementCategory.ElementAnimeType, optionsUnidentifiableUnsearchable, new List <string> { "SP" }); // e.g. "Yumeiro Patissiere SP Professional" Add(Element.ElementCategory.ElementAnimeType, optionsUnidentifiableInvalid, new List <string> { "ED", "ENDING", "NCED", "NCOP", "OP", "OPENING", "PREVIEW", "PV" }); Add(Element.ElementCategory.ElementAudioTerm, optionsDefault, new List <string> { // Audio channels "2.0CH", "2CH", "5.1", "5.1CH", "DTS", "DTS-ES", "DTS5.1", "TRUEHD5.1", // Audio codec "AAC", "AACX2", "AACX3", "AACX4", "AC3", "EAC3", "E-AC-3", "FLAC", "FLACX2", "FLACX3", "FLACX4", "LOSSLESS", "MP3", "OGG", "VORBIS", // Audio language "DUALAUDIO", "DUAL AUDIO" }); Add(Element.ElementCategory.ElementDeviceCompatibility, optionsDefault, new List <string> { "IPAD3", "IPHONE5", "IPOD", "PS3", "XBOX", "XBOX360" }); Add(Element.ElementCategory.ElementDeviceCompatibility, optionsUnidentifiable, new List <string> { "ANDROID" }); Add(Element.ElementCategory.ElementEpisodePrefix, optionsDefault, new List <string> { "EP", "EP.", "EPS", "EPS.", "EPISODE", "EPISODE.", "EPISODES", "CAPITULO", "EPISODIO", "FOLGE" }); Add(Element.ElementCategory.ElementEpisodePrefix, optionsInvalid, new List <string> { "E", "\\x7B2C" }); // single-letter episode keywords are not valid tokens Add(Element.ElementCategory.ElementFileExtension, optionsDefault, new List <string> { "3GP", "AVI", "DIVX", "FLV", "M2TS", "MKV", "MOV", "MP4", "MPG", "OGM", "RM", "RMVB", "WEBM", "WMV" }); Add(Element.ElementCategory.ElementFileExtension, optionsInvalid, new List <string> { "AAC", "AIFF", "FLAC", "M4A", "MP3", "MKA", "OGG", "WAV", "WMA", "7Z", "RAR", "ZIP", "ASS", "SRT" }); Add(Element.ElementCategory.ElementLanguage, optionsDefault, new List <string> { "ENG", "ENGLISH", "ESPANO", "JAP", "PT-BR", "SPANISH", "VOSTFR" }); Add(Element.ElementCategory.ElementLanguage, optionsUnidentifiable, new List <string> { "ESP", "ITA" }); // e.g. "Tokyo ESP:, "Bokura ga Ita" Add(Element.ElementCategory.ElementOther, optionsDefault, new List <string> { "REMASTER", "REMASTERED", "UNCENSORED", "UNCUT", "TS", "VFR", "WIDESCREEN", "WS" }); Add(Element.ElementCategory.ElementReleaseGroup, optionsDefault, new List <string> { "THORA" }); Add(Element.ElementCategory.ElementReleaseInformation, optionsDefault, new List <string> { "BATCH", "COMPLETE", "PATCH", "REMUX" }); Add(Element.ElementCategory.ElementReleaseInformation, optionsUnidentifiable, new List <string> { "END", "FINAL" }); // e.g. "The End of Evangelion", 'Final Approach" Add(Element.ElementCategory.ElementReleaseVersion, optionsDefault, new List <string> { "V0", "V1", "V2", "V3", "V4" }); Add(Element.ElementCategory.ElementSource, optionsDefault, new List <string> { "BD", "BDRIP", "BLURAY", "BLU-RAY", "DVD", "DVD5", "DVD9", "DVD-R2J", "DVDRIP", "DVD-RIP", "R2DVD", "R2J", "R2JDVD", "R2JDVDRIP", "HDTV", "HDTVRIP", "TVRIP", "TV-RIP", "WEBCAST", "WEBRIP" }); Add(Element.ElementCategory.ElementSubtitles, optionsDefault, new List <string> { "ASS", "BIG5", "DUB", "DUBBED", "HARDSUB", "HARDSUBS", "RAW", "SOFTSUB", "SOFTSUBS", "SUB", "SUBBED", "SUBTITLED" }); Add(Element.ElementCategory.ElementVideoTerm, optionsDefault, new List <string> { // Frame rate "23.976FPS", "24FPS", "29.97FPS", "30FPS", "60FPS", "120FPS", // Video codec "8BIT", "8-BIT", "10BIT", "10BITS", "10-BIT", "10-BITS", "HI10", "HI10P", "HI444", "HI444P", "HI444PP", "H264", "H265", "H.264", "H.265", "X264", "X265", "X.264", "AVC", "HEVC", "HEVC2", "DIVX", "DIVX5", "DIVX6", "XVID", // Video format "AVI", "RMVB", "WMV", "WMV3", "WMV9", // Video quality "HQ", "LQ", // Video resolution "HD", "SD" }); Add(Element.ElementCategory.ElementVolumePrefix, optionsDefault, new List <string> { "VOL", "VOL.", "VOLUME" }); PeekEntries = new List <Tuple <Element.ElementCategory, List <string> > > { Tuple.Create(Element.ElementCategory.ElementAudioTerm, new List <string> { "Dual Audio" }), Tuple.Create(Element.ElementCategory.ElementVideoTerm, new List <string> { "H264", "H.264", "h264", "h.264" }), Tuple.Create(Element.ElementCategory.ElementVideoResolution, new List <string> { "480p", "720p", "1080p" }), Tuple.Create(Element.ElementCategory.ElementSource, new List <string> { "Blu-Ray" }) }; }