public override bool BeforeMetadataRefresh() { var hasChanges = base.BeforeMetadataRefresh(); var locationType = LocationType; if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path)) { IndexNumber = TVUtils.GetEpisodeNumberFromFile(Path, Parent is Season); // If a change was made record it if (IndexNumber.HasValue) { hasChanges = true; } } if (!IndexNumberEnd.HasValue && !string.IsNullOrEmpty(Path)) { IndexNumberEnd = TVUtils.GetEndingEpisodeNumberFromFile(Path); // If a change was made record it if (IndexNumberEnd.HasValue) { hasChanges = true; } } } if (!ParentIndexNumber.HasValue) { var season = Season; if (season != null) { ParentIndexNumber = season.IndexNumber; } if (!ParentIndexNumber.HasValue && !string.IsNullOrEmpty(Path)) { ParentIndexNumber = TVUtils.GetSeasonNumberFromEpisodeFile(Path); } // If a change was made record it if (ParentIndexNumber.HasValue) { hasChanges = true; } } return(hasChanges); }
/// <summary> /// Resolves the specified args. /// </summary> /// <param name="args">The args.</param> /// <returns>Episode.</returns> protected override Episode Resolve(ItemResolveArgs args) { var season = args.Parent as Season; // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something if (season != null || args.Parent is Series) { Episode episode = null; if (args.IsDirectory) { if (args.ContainsFileSystemEntryByName("video_ts")) { episode = new Episode { Path = args.Path, VideoType = VideoType.Dvd }; } if (args.ContainsFileSystemEntryByName("bdmv")) { episode = new Episode { Path = args.Path, VideoType = VideoType.BluRay }; } } if (episode == null) { episode = base.Resolve(args); } if (episode != null) { if (season != null) { episode.ParentIndexNumber = season.IndexNumber; } if (episode.ParentIndexNumber == null) { episode.ParentIndexNumber = TVUtils.GetSeasonNumberFromEpisodeFile(args.Path); } } return(episode); } return(null); }
public void TestGetSeasonNumberFromPath() { Assert.AreEqual(02, TVUtils.GetSeasonNumberFromEpisodeFile(@"\Show\Season 02\S02E03 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 02")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 02")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 02")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 02")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 02")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 02")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Seinfeld\S02")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Seinfeld\2")); //Four Digits seasons Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 2009")); }
public void TestGetSeasonNumberFromEpisodeFile() { Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\01x02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\S01x02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\S01E02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\S01xE02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname 01x02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname S01x02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname S01E02 blah.avi")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname S01xE02 blah.avi")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2\Elementary - 02x03 - 02x04 - 02x15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2\02x03 - 02x04 - 02x15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2\02x03-04-15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2\Elementary - 02x03-04-15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 02\02x03-E15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 02\Elementary - 02x03-E15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 02\02x03 - x04 - x15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 02\Elementary - 02x03 - x04 - x15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 02\02x03x04x15 - Ep Name.ext")); Assert.AreEqual(2, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 02\Elementary - 02x03x04x15 - Ep Name.ext")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\Elementary - S01E23-E24-E26 - The Woman.mp4")); Assert.AreEqual(1, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 1\S01E23-E24-E26 - The Woman.mp4")); //Four Digits seasons Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009x02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009E02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009xE02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname 2009x02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname S2009x02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname S2009E02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname S2009xE02 blah.avi")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03 - 2009x04 - 2009x15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03-04-15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03-04-15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03-E15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03-E15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03 - x04 - x15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03 - x04 - x15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03x04x15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03x04x15 - Ep Name.ext")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - S2009E23-E24-E26 - The Woman.mp4")); Assert.AreEqual(2009, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009E23-E24-E26 - The Woman.mp4")); Assert.AreEqual(25, TVUtils.GetSeasonNumberFromEpisodeFile(@"Season 25\The Simpsons.S25E09.Steal this episode.mp4")); Assert.AreEqual(25, TVUtils.GetSeasonNumberFromEpisodeFile(@"The Simpsons\The Simpsons.S25E09.Steal this episode.mp4")); }
/// <summary> /// Resolves the specified args. /// </summary> /// <param name="args">The args.</param> /// <returns>Episode.</returns> protected override Episode Resolve(ItemResolveArgs args) { var parent = args.Parent; if (parent == null) { return(null); } var season = parent as Season; // Just in case the user decided to nest episodes. // Not officially supported but in some cases we can handle it. if (season == null) { season = parent.Parents.OfType <Season>().FirstOrDefault(); } // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something if (season != null || parent is Series || parent.Parents.OfType <Series>().Any()) { Episode episode = null; if (args.IsDirectory) { if (args.ContainsFileSystemEntryByName("video_ts")) { episode = new Episode { Path = args.Path, VideoType = VideoType.Dvd }; } if (args.ContainsFileSystemEntryByName("bdmv")) { episode = new Episode { Path = args.Path, VideoType = VideoType.BluRay }; } } if (episode == null) { episode = base.Resolve(args); } if (episode != null) { if (season != null) { episode.ParentIndexNumber = season.IndexNumber; } if (episode.ParentIndexNumber == null) { episode.ParentIndexNumber = TVUtils.GetSeasonNumberFromEpisodeFile(args.Path); } } return(episode); } return(null); }
/// <summary> /// Fetches the episode data. /// </summary> /// <param name="seriesXml">The series XML.</param> /// <param name="episode">The episode.</param> /// <param name="seriesId">The series id.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{System.Boolean}.</returns> private async Task <ProviderRefreshStatus> FetchEpisodeData(XmlDocument seriesXml, Episode episode, string seriesId, CancellationToken cancellationToken) { var status = ProviderRefreshStatus.Success; if (episode.IndexNumber == null) { return(status); } var seasonNumber = episode.ParentIndexNumber ?? TVUtils.GetSeasonNumberFromEpisodeFile(episode.Path); if (seasonNumber == null) { return(status); } var usingAbsoluteData = false; var episodeNode = seriesXml.SelectSingleNode("//Episode[EpisodeNumber='" + episode.IndexNumber.Value + "'][SeasonNumber='" + seasonNumber.Value + "']"); if (episodeNode == null) { if (seasonNumber.Value == 1) { episodeNode = seriesXml.SelectSingleNode("//Episode[absolute_number='" + episode.IndexNumber.Value + "']"); usingAbsoluteData = true; } } // If still null, nothing we can do if (episodeNode == null) { return(status); } IEnumerable <XmlDocument> extraEpisodesNode = new XmlDocument[] { }; if (episode.IndexNumberEnd.HasValue) { var seriesXDocument = XDocument.Load(new XmlNodeReader(seriesXml)); if (usingAbsoluteData) { extraEpisodesNode = seriesXDocument.Descendants("Episode") .Where( x => int.Parse(x.Element("absolute_number").Value) > episode.IndexNumber && int.Parse(x.Element("absolute_number").Value) <= episode.IndexNumberEnd.Value).OrderBy(x => x.Element("absolute_number").Value).Select(x => x.ToXmlDocument()); } else { var all = seriesXDocument.Descendants("Episode").Where(x => int.Parse(x.Element("SeasonNumber").Value) == seasonNumber.Value); var xElements = all.Where(x => int.Parse(x.Element("EpisodeNumber").Value) > episode.IndexNumber && int.Parse(x.Element("EpisodeNumber").Value) <= episode.IndexNumberEnd.Value); extraEpisodesNode = xElements.OrderBy(x => x.Element("EpisodeNumber").Value).Select(x => x.ToXmlDocument()); } } var doc = new XmlDocument(); doc.LoadXml(episodeNode.OuterXml); if (!episode.HasImage(ImageType.Primary)) { var p = doc.SafeGetString("//filename"); if (p != null) { if (!Directory.Exists(episode.MetaLocation)) { Directory.CreateDirectory(episode.MetaLocation); } try { var url = TVUtils.BannerUrl + p; await _providerManager.SaveImage(episode, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken) .ConfigureAwait(false); } catch (HttpException) { status = ProviderRefreshStatus.CompletedWithErrors; } } } if (!episode.LockedFields.Contains(MetadataFields.Overview)) { var extraOverview = extraEpisodesNode.Aggregate("", (current, xmlDocument) => current + ("\r\n\r\n" + xmlDocument.SafeGetString("//Overview"))); episode.Overview = doc.SafeGetString("//Overview") + extraOverview; } if (usingAbsoluteData) { episode.IndexNumber = doc.SafeGetInt32("//absolute_number", -1); } if (episode.IndexNumber < 0) { episode.IndexNumber = doc.SafeGetInt32("//EpisodeNumber"); } if (!episode.LockedFields.Contains(MetadataFields.Name)) { var extraNames = extraEpisodesNode.Aggregate("", (current, xmlDocument) => current + (", " + xmlDocument.SafeGetString("//EpisodeName"))); episode.Name = doc.SafeGetString("//EpisodeName") + extraNames; } episode.CommunityRating = doc.SafeGetSingle("//Rating", -1, 10); var firstAired = doc.SafeGetString("//FirstAired"); DateTime airDate; if (DateTime.TryParse(firstAired, out airDate) && airDate.Year > 1850) { episode.PremiereDate = airDate.ToUniversalTime(); episode.ProductionYear = airDate.Year; } if (!episode.LockedFields.Contains(MetadataFields.Cast)) { episode.People.Clear(); var actors = doc.SafeGetString("//GuestStars"); if (actors != null) { // Sometimes tvdb actors have leading spaces //Regex Info: //The first block are the posible delimitators (open-parentheses should be there cause if dont the next block will fail) //The second block Allow the delimitators to be part of the text if they're inside parentheses var persons = Regex.Matches(actors, @"(?<delimitators>([^|,(])|(?<ignoreinParentheses>\([^)]*\)*))+") .Cast <Match>() .Select(m => m.Value) .Where(i => !string.IsNullOrWhiteSpace(i) && !string.IsNullOrEmpty(i)); foreach (var person in persons.Select(str => { var nameGroup = str.Split(new[] { '(' }, 2, StringSplitOptions.RemoveEmptyEntries); var name = nameGroup[0].Trim(); var roles = nameGroup.Count() > 1 ? nameGroup[1].Trim() : null; if (roles != null) { roles = roles.EndsWith(")") ? roles.Substring(0, roles.Length - 1) : roles; } return(new PersonInfo { Type = PersonType.GuestStar, Name = name, Role = roles }); })) { episode.AddPerson(person); } } foreach (var xmlDocument in extraEpisodesNode) { var extraActors = xmlDocument.SafeGetString("//GuestStars"); if (extraActors == null) { continue; } // Sometimes tvdb actors have leading spaces var persons = Regex.Matches(extraActors, @"(?<delimitators>([^|,(])|(?<ignoreinParentheses>\([^)]*\)*))+") .Cast <Match>() .Select(m => m.Value) .Where(i => !string.IsNullOrWhiteSpace(i) && !string.IsNullOrEmpty(i)); foreach (var person in persons.Select(str => { var nameGroup = str.Split(new[] { '(' }, 2, StringSplitOptions.RemoveEmptyEntries); var name = nameGroup[0].Trim(); var roles = nameGroup.Count() > 1 ? nameGroup[1].Trim() : null; if (roles != null) { roles = roles.EndsWith(")") ? roles.Substring(0, roles.Length - 1) : roles; } return(new PersonInfo { Type = PersonType.GuestStar, Name = name, Role = roles }); })) { episode.AddPerson(person); } } var directors = doc.SafeGetString("//Director"); if (directors != null) { // Sometimes tvdb actors have leading spaces foreach (var person in directors.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(str => new PersonInfo { Type = PersonType.Director, Name = str.Trim() })) { episode.AddPerson(person); } } var writers = doc.SafeGetString("//Writer"); if (writers != null) { // Sometimes tvdb actors have leading spaces foreach (var person in writers.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(str => new PersonInfo { Type = PersonType.Writer, Name = str.Trim() })) { episode.AddPerson(person); } } } return(status); }
public async Task <FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting, CancellationToken cancellationToken) { _logger.Info("Sorting file {0}", path); var result = new FileOrganizationResult { Date = DateTime.UtcNow, OriginalPath = path, OriginalFileName = Path.GetFileName(path), Type = FileOrganizerType.Episode, FileSize = new FileInfo(path).Length }; var seriesName = TVUtils.GetSeriesNameFromEpisodeFile(path); if (!string.IsNullOrEmpty(seriesName)) { var season = TVUtils.GetSeasonNumberFromEpisodeFile(path); result.ExtractedSeasonNumber = season; if (season.HasValue) { // Passing in true will include a few extra regex's var episode = TVUtils.GetEpisodeNumberFromFile(path, true); result.ExtractedEpisodeNumber = episode; if (episode.HasValue) { _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, season, episode); var endingEpisodeNumber = TVUtils.GetEndingEpisodeNumberFromFile(path); result.ExtractedEndingEpisodeNumber = endingEpisodeNumber; await OrganizeEpisode(path, seriesName, season.Value, episode.Value, endingEpisodeNumber, options, overwriteExisting, result, cancellationToken).ConfigureAwait(false); } else { var msg = string.Format("Unable to determine episode number from {0}", path); result.Status = FileSortingStatus.Failure; result.StatusMessage = msg; _logger.Warn(msg); } } else { var msg = string.Format("Unable to determine season number from {0}", path); result.Status = FileSortingStatus.Failure; result.StatusMessage = msg; _logger.Warn(msg); } } else { var msg = string.Format("Unable to determine series name from {0}", path); result.Status = FileSortingStatus.Failure; result.StatusMessage = msg; _logger.Warn(msg); } var previousResult = _organizationService.GetResultBySourcePath(path); if (previousResult != null) { // Don't keep saving the same result over and over if nothing has changed if (previousResult.Status == result.Status && result.Status != FileSortingStatus.Success) { return(previousResult); } } await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false); return(result); }