Example #1
0
        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);
        }
Example #2
0
        /// <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);
        }
Example #3
0
        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"));
        }
Example #4
0
        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);
        }
Example #6
0
        /// <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);
        }