Example #1
0
        private Season AddVirtualSeason(int season, Series series)
        {
            string seasonName;

            if (season == 0)
            {
                seasonName = _libraryManager.GetLibraryOptions(series).SeasonZeroDisplayName;
            }
            else
            {
                seasonName = string.Format(
                    CultureInfo.InvariantCulture,
                    _localization.GetLocalizedString("NameSeasonNumber"),
                    season.ToString(CultureInfo.InvariantCulture));
            }

            _logger.LogInformation("Creating Season {SeasonName} entry for {SeriesName}", seasonName, series.Name);

            var newSeason = new Season
            {
                Name        = seasonName,
                IndexNumber = season,
                Id          = _libraryManager.GetNewItemId(
                    series.Id + season.ToString(CultureInfo.InvariantCulture) + seasonName,
                    typeof(Season)),
                IsVirtualItem = true,
                SeriesId      = series.Id,
                SeriesName    = series.Name,
                SeriesPresentationUniqueKey = series.GetPresentationUniqueKey()
            };

            series.AddChild(newSeason, CancellationToken.None);

            return(newSeason);
        }
        public static void CleanupGenres(Series series)
        {
            PluginConfiguration config = PluginConfiguration.Instance();

            if (config.TidyGenreList)
            {
                series.Genres = RemoveRedundantGenres(series.Genres)
                                           .Where(g => !"Animation".Equals(g) && !"Anime".Equals(g))
                                           .Distinct()
                                           .ToList();

                TidyGenres(series);
            }

            if (config.MaxGenres > 0)
            {
                if (config.MoveExcessGenresToTags)
                {
                    foreach (string genre in series.Genres.Skip(config.MaxGenres - 1))
                    {
                        if (!series.Tags.Contains(genre))
                            series.Tags.Add(genre);
                    }
                }

                series.Genres = series.Genres.Take(config.MaxGenres - 1).ToList();
            }

            if (!series.Genres.Contains("Anime"))
                series.Genres.Add("Anime");
        }
Example #3
0
        private IEnumerable <Season> AddMissingSeasons(Series series, List <Season> existingSeasons, IReadOnlyList <int> allSeasons)
        {
            var missingSeasons = allSeasons.Except(existingSeasons.Select(s => s.IndexNumber !.Value)).ToList();

            for (var i = 0; i < missingSeasons.Count; i++)
            {
                var season = missingSeasons[i];
                yield return(AddVirtualSeason(season, series));
            }
        }
        public static Series Convert(MazeSeries mazeSeries)
        {
            var series = new Series();

            SetProviderIds(series, mazeSeries.externals, mazeSeries.id);

            series.Name = mazeSeries.name;
            series.Genres = mazeSeries.genres.ToList();

            // TODO: Do we have a Series property for original language?
            //series = mazeSeries.language;

            if (mazeSeries.network != null && !string.IsNullOrWhiteSpace(mazeSeries.network.name))
            {
                var networkName = mazeSeries.network.name;
                if (mazeSeries.network.country != null && !string.IsNullOrWhiteSpace(mazeSeries.network.country.code))
                {
                    networkName = string.Format("{0} ({1})", mazeSeries.network.name, mazeSeries.network.country.code);
                }

                series.Studios.Add(networkName);
            }

            if (mazeSeries.premiered.HasValue)
            {
                series.PremiereDate = mazeSeries.premiered.Value;
                series.ProductionYear = mazeSeries.premiered.Value.Year;
            }

            if (mazeSeries.rating != null && mazeSeries.rating.average.HasValue)
            {
                series.CommunityRating = (float)mazeSeries.rating.average.Value;
            }

            if (mazeSeries.runtime.HasValue)
            {
                series.RunTimeTicks = TimeSpan.FromMinutes(mazeSeries.runtime.Value).Ticks;
            }

            switch (mazeSeries.status.ToLower())
            {
                case "running":
                    series.Status = SeriesStatus.Continuing;
                    break;
                case "ended":
                    series.Status = SeriesStatus.Ended;
                    break;
            }

            series.Overview = StripHtml(mazeSeries.summary);

            series.HomePageUrl = mazeSeries.url.ToString();

            return series;
        }
Example #5
0
        private async Task HandleSeries(Series series)
        {
            if (!series.TryGetProviderId(MetadataProvider.Tvdb.ToString(), out var tvdbIdTxt))
            {
                return;
            }

            var tvdbId = Convert.ToInt32(tvdbIdTxt, CultureInfo.InvariantCulture);

            var children         = series.GetRecursiveChildren();
            var existingSeasons  = new List <Season>();
            var existingEpisodes = new Dictionary <int, List <Episode> >();

            for (var i = 0; i < children.Count; i++)
            {
                switch (children[i])
                {
                case Season season:
                    if (season.IndexNumber.HasValue)
                    {
                        existingSeasons.Add(season);
                    }

                    break;

                case Episode episode:
                    var seasonNumber = episode.ParentIndexNumber ?? 1;
                    if (!existingEpisodes.ContainsKey(seasonNumber))
                    {
                        existingEpisodes[seasonNumber] = new List <Episode>();
                    }

                    existingEpisodes[seasonNumber].Add(episode);
                    break;
                }
            }

            var allEpisodes = await GetAllEpisodes(tvdbId, series.GetPreferredMetadataLanguage()).ConfigureAwait(false);

            var allSeasons = allEpisodes
                             .Where(ep => ep.AiredSeason.HasValue)
                             .Select(ep => ep.AiredSeason !.Value)
                             .Distinct()
                             .ToList();

            // Add missing seasons
            var newSeasons = AddMissingSeasons(series, existingSeasons, allSeasons);

            AddMissingEpisodes(existingEpisodes, allEpisodes, existingSeasons.Concat(newSeasons).ToList());
        }
        private RemoteImageInfo GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken)
        {
            var tvdbPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb));

            var actorXmlPath = Path.Combine(tvdbPath, "actors.xml");

            try
            {
                return GetImageInfo(actorXmlPath, personName, cancellationToken);
            }
            catch (FileNotFoundException)
            {
                return null;
            }
        }
        public async Task Run(Series series, CancellationToken cancellationToken)
        {
            await RemoveObsoleteSeasons(series).ConfigureAwait(false);
            
            var hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false);

            if (hasNewSeasons)
            {
                var directoryService = new DirectoryService(_fileSystem);

                //await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false);

                //await series.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService))
                //    .ConfigureAwait(false);
            }
        }
        public static void CleanupGenres(Series series)
        {
            PluginConfiguration config = PluginConfiguration.Instance();

            if (config.TidyGenreList)
            {
                series.Genres = RemoveRedundantGenres(series.Genres)
                                           .Distinct()
                                           .ToList();

                TidyGenres(series);
            }

            var max = config.MaxGenres;
            if (config.AddAnimeGenre)
            {
                series.Genres.Remove("Animation");
                series.Genres.Remove("Anime");

                max = Math.Max(max - 1, 0);
            }
            
            if (config.MaxGenres > 0)
            {
                if (config.MoveExcessGenresToTags)
                {
                    foreach (string genre in series.Genres.Skip(max))
                    {
                        if (!series.Tags.Contains(genre))
                            series.Tags.Add(genre);
                    }
                }

                series.Genres = series.Genres.Take(max).ToList();
            }

            if (!series.Genres.Contains("Anime") && config.AddAnimeGenre)
            {
                if (series.Genres.Contains("Animation"))
                    series.Genres.Remove("Animation");

                series.AddGenre("Anime");
            }

            series.Genres.Sort();
        }
        private bool IsEnabledForLibrary(BaseItem item)
        {
            Series series = item switch
            {
                Episode episode => episode.Series,
                Season season => season.Series,
                   _ => item as Series
            };

            if (series == null)
            {
                return(false);
            }

            var libraryOptions = _libraryManager.GetLibraryOptions(series);

            return(series.IsMetadataFetcherEnabled(libraryOptions, Plugin.MetadataProviderName));
        }
Example #10
0
        private async Task<bool> AddDummySeasonFolders(Series series, CancellationToken cancellationToken)
        {
            var episodesInSeriesFolder = series.GetRecursiveChildren()
                .OfType<Episode>()
                .Where(i => !i.IsInSeasonFolder)
                .ToList();

            var hasChanges = false;

            // Loop through the unique season numbers
            foreach (var seasonNumber in episodesInSeriesFolder.Select(i => i.ParentIndexNumber ?? -1)
                .Where(i => i >= 0)
                .Distinct()
                .ToList())
            {
                var hasSeason = series.Children.OfType<Season>()
                    .Any(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber);

                if (!hasSeason)
                {
                    await AddSeason(series, seasonNumber, cancellationToken).ConfigureAwait(false);

                    hasChanges = true;
                }
            }

            // Unknown season - create a dummy season to put these under
            if (episodesInSeriesFolder.Any(i => !i.ParentIndexNumber.HasValue))
            {
                var hasSeason = series.Children.OfType<Season>()
                    .Any(i => !i.IndexNumber.HasValue);

                if (!hasSeason)
                {
                    await AddSeason(series, null, cancellationToken).ConfigureAwait(false);

                    hasChanges = true;
                }
            }

            return hasChanges;
        }
        public async Task TestScrapePage()
        {
            var data = File.ReadAllText("TestData/anilist/9756.html", Encoding.UTF8);

            var series = new Series();

            AniListSeriesProvider.ParseTitle(series, data, "en");
            AniListSeriesProvider.ParseSummary(series, data);
            AniListSeriesProvider.ParseStudio(series, data);
            AniListSeriesProvider.ParseRating(series, data);
            AniListSeriesProvider.ParseGenres(series, data);
            AniListSeriesProvider.ParseDuration(series, data);
            AniListSeriesProvider.ParseAirDates(series, data);

            Assert.That(series.Name, Is.EqualTo("Mahou Shoujo Madoka★Magica"));
            Assert.That(series.Genres, Contains.Item("Drama"));
            Assert.That(series.Genres, Contains.Item("Fantasy"));
            Assert.That(series.Genres, Contains.Item("Psychological Thriller"));
            Assert.That(series.Genres, Contains.Item("Thriller"));
            Assert.That(series.PremiereDate, Is.EqualTo(new DateTime(2011, 1, 7)));
            Assert.That(series.EndDate, Is.EqualTo(new DateTime(2011, 4, 22)));

        }
        /// <summary>
        /// Downloads the image from series.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="series">The series.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task DownloadImageFromSeries(BaseItem item, Series series, CancellationToken cancellationToken)
        {
            var tvdbPath = RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb));

            var actorXmlPath = Path.Combine(tvdbPath, "actors.xml");

            var xmlDoc = new XmlDocument();

            xmlDoc.Load(actorXmlPath);

            var actorNodes = xmlDoc.SelectNodes("//Actor");

            if (actorNodes == null)
            {
                return;
            }

            foreach (var actorNode in actorNodes.OfType<XmlNode>())
            {
                var name = actorNode.SafeGetString("Name");

                if (string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase))
                {
                    var image = actorNode.SafeGetString("Image");

                    if (!string.IsNullOrEmpty(image))
                    {
                        var url = TVUtils.BannerUrl + image;

                        await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool,
                                                       ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
                    }

                    break;
                }
            }
        }
        /// <summary>
        /// Fetches the actors.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="actorsXmlPath">The actors XML path.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        private void FetchActors(Series series, string actorsXmlPath, CancellationToken cancellationToken)
        {
            var settings = new XmlReaderSettings
            {
                CheckCharacters = false,
                IgnoreProcessingInstructions = true,
                IgnoreComments = true,
                ValidationType = ValidationType.None
            };

            using (var streamReader = new StreamReader(actorsXmlPath, Encoding.UTF8))
            {
                // Use XmlReader for best performance
                using (var reader = XmlReader.Create(streamReader, settings))
                {
                    reader.MoveToContent();

                    // Loop through each element
                    while (reader.Read())
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            switch (reader.Name)
                            {
                                case "Actor":
                                    {
                                        using (var subtree = reader.ReadSubtree())
                                        {
                                            FetchDataFromActorNode(series, subtree);
                                        }
                                        break;
                                    }
                                default:
                                    reader.Skip();
                                    break;
                            }
                        }
                    }
                }
            }
        }
        private void FetchDataFromSeriesNode(Series item, XmlReader reader, CancellationToken cancellationToken)
        {
            reader.MoveToContent();

            // Loop through each element
            while (reader.Read())
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "SeriesName":
                            {
                                if (!item.LockedFields.Contains(MetadataFields.Name))
                                {
                                    item.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
                                }
                                break;
                            }

                        case "Overview":
                            {
                                if (!item.LockedFields.Contains(MetadataFields.Overview))
                                {
                                    item.Overview = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
                                }
                                break;
                            }

                        case "Airs_DayOfWeek":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    item.AirDays = TVUtils.GetAirDays(val);
                                }
                                break;
                            }

                        case "Airs_Time":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    item.AirTime = val;
                                }
                                break;
                            }

                        case "ContentRating":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    if (!item.LockedFields.Contains(MetadataFields.OfficialRating))
                                    {
                                        item.OfficialRating = val;
                                    }
                                }
                                break;
                            }

                        case "Rating":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    // Only fill this if it doesn't already have a value, since we get it from imdb which has better data
                                    if (!item.CommunityRating.HasValue || string.IsNullOrWhiteSpace(item.GetProviderId(MetadataProviders.Imdb)))
                                    {
                                        float rval;

                                        // float.TryParse is local aware, so it can be probamatic, force us culture
                                        if (float.TryParse(val, NumberStyles.AllowDecimalPoint, UsCulture, out rval))
                                        {
                                            item.CommunityRating = rval;
                                        }
                                    }
                                }
                                break;
                            }
                        case "RatingCount":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    int rval;

                                    // int.TryParse is local aware, so it can be probamatic, force us culture
                                    if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
                                    {
                                        item.VoteCount = rval;
                                    }
                                }

                                break;
                            }

                        case "IMDB_ID":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    item.SetProviderId(MetadataProviders.Imdb, val);
                                }

                                break;
                            }

                        case "zap2it_id":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    item.SetProviderId(MetadataProviders.Zap2It, val);
                                }

                                break;
                            }

                        case "Status":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    SeriesStatus seriesStatus;

                                    if (Enum.TryParse(val, true, out seriesStatus))
                                        item.Status = seriesStatus;
                                }

                                break;
                            }

                        case "FirstAired":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    DateTime date;
                                    if (DateTime.TryParse(val, out date))
                                    {
                                        date = date.ToUniversalTime();

                                        item.PremiereDate = date;
                                        item.ProductionYear = date.Year;
                                    }
                                }

                                break;
                            }

                        case "Runtime":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val) && !item.LockedFields.Contains(MetadataFields.Runtime))
                                {
                                    int rval;

                                    // int.TryParse is local aware, so it can be probamatic, force us culture
                                    if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
                                    {
                                        item.RunTimeTicks = TimeSpan.FromMinutes(rval).Ticks;
                                    }
                                }

                                break;
                            }

                        case "Genre":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    // Only fill this in if there's no existing genres, because Imdb data from Omdb is preferred
                                    if (!item.LockedFields.Contains(MetadataFields.Genres) && (item.Genres.Count == 0 || !string.Equals(item.GetPreferredMetadataLanguage(), "en", StringComparison.OrdinalIgnoreCase)))
                                    {
                                        var vals = val
                                            .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                                            .Select(i => i.Trim())
                                            .Where(i => !string.IsNullOrWhiteSpace(i))
                                            .ToList();

                                        if (vals.Count > 0)
                                        {
                                            item.Genres.Clear();

                                            foreach (var genre in vals)
                                            {
                                                item.AddGenre(genre);
                                            }
                                        }
                                    }
                                }

                                break;
                            }

                        case "Network":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    if (!item.LockedFields.Contains(MetadataFields.Studios))
                                    {
                                        var vals = val
                                            .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                                            .Select(i => i.Trim())
                                            .Where(i => !string.IsNullOrWhiteSpace(i))
                                            .ToList();

                                        if (vals.Count > 0)
                                        {
                                            item.Studios.Clear();

                                            foreach (var genre in vals)
                                            {
                                                item.AddStudio(genre);
                                            }
                                        }
                                    }
                                }

                                break;
                            }

                        default:
                            reader.Skip();
                            break;
                    }
                }
            }
        }
        private void FetchSeriesInfo(Series item, string seriesXmlPath, CancellationToken cancellationToken)
        {
            var settings = new XmlReaderSettings
            {
                CheckCharacters = false,
                IgnoreProcessingInstructions = true,
                IgnoreComments = true,
                ValidationType = ValidationType.None
            };

            var episiodeAirDates = new List<DateTime>();

            using (var streamReader = new StreamReader(seriesXmlPath, Encoding.UTF8))
            {
                // Use XmlReader for best performance
                using (var reader = XmlReader.Create(streamReader, settings))
                {
                    reader.MoveToContent();

                    // Loop through each element
                    while (reader.Read())
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            switch (reader.Name)
                            {
                                case "Series":
                                    {
                                        using (var subtree = reader.ReadSubtree())
                                        {
                                            FetchDataFromSeriesNode(item, subtree, cancellationToken);
                                        }
                                        break;
                                    }

                                case "Episode":
                                    {
                                        using (var subtree = reader.ReadSubtree())
                                        {
                                            var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken);

                                            if (date.HasValue)
                                            {
                                                episiodeAirDates.Add(date.Value);
                                            }
                                        }
                                        break;
                                    }

                                default:
                                    reader.Skip();
                                    break;
                            }
                        }
                    }
                }
            }

            if (item.Status.HasValue && item.Status.Value == SeriesStatus.Ended && episiodeAirDates.Count > 0)
            {
                item.EndDate = episiodeAirDates.Max();
            }
        }
        /// <summary>
        /// Fetches the series data.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="seriesId">The series id.</param>
        /// <param name="seriesDataPath">The series data path.</param>
        /// <param name="isForcedRefresh">if set to <c>true</c> [is forced refresh].</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task{System.Boolean}.</returns>
        private async Task FetchSeriesData(Series series, string seriesId, string seriesDataPath, bool isForcedRefresh, CancellationToken cancellationToken)
        {
            Directory.CreateDirectory(seriesDataPath);

            var files = Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.TopDirectoryOnly)
                .Select(Path.GetFileName)
                .ToList();

            var seriesXmlFilename = series.GetPreferredMetadataLanguage().ToLower() + ".xml";

            // Only download if not already there
            // The prescan task will take care of updates so we don't need to re-download here
            if (!files.Contains("banners.xml", StringComparer.OrdinalIgnoreCase) || !files.Contains("actors.xml", StringComparer.OrdinalIgnoreCase) || !files.Contains(seriesXmlFilename, StringComparer.OrdinalIgnoreCase))
            {
                await DownloadSeriesZip(seriesId, seriesDataPath, null, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
            }

            // Have to check this here since we prevent the normal enforcement through ProviderManager
            if (!series.DontFetchMeta)
            {
                // Examine if there's no local metadata, or save local is on (to get updates)
                if (isForcedRefresh || ConfigurationManager.Configuration.EnableTvDbUpdates || !HasLocalMeta(series))
                {
                    series.SetProviderId(MetadataProviders.Tvdb, seriesId);

                    var seriesXmlPath = Path.Combine(seriesDataPath, seriesXmlFilename);
                    var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml");

                    FetchSeriesInfo(series, seriesXmlPath, cancellationToken);

                    if (!series.LockedFields.Contains(MetadataFields.Cast))
                    {
                        series.People.Clear();

                        FetchActors(series, actorsXmlPath, cancellationToken);
                    }
                }
            }
        }
Example #17
0
 public IEnumerable <Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable <Episode> allSeriesEpisodes)
 {
     return(series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes));
 }
Example #18
0
        /// <summary>
        /// Add or remove a Show(Series) to/from the users trakt.tv library
        /// </summary>
        /// <param name="show">The show to remove</param>
        /// <param name="traktUser">The user who's library is being updated</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="eventType"></param>
        /// <returns>Task{TraktResponseDataContract}.</returns>
        public async Task<TraktSyncResponse> SendLibraryUpdateAsync(Series show, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType)
        {
            if (show == null)
                throw new ArgumentNullException("show");
            if (traktUser == null)
                throw new ArgumentNullException("traktUser");

            if (eventType == EventType.Update) return null;

            var showPayload = new List<TraktShowCollected>
            {
                new TraktShowCollected
                {
                    Title = show.Name,
                    Year = show.ProductionYear,
                    Ids = new TraktShowId
                    {
                        Tvdb = show.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(),
                        Imdb = show.GetProviderId(MetadataProviders.Imdb),
                        TvRage = show.GetProviderId(MetadataProviders.TvRage).ConvertToInt()
                    },
                }
            };

            var data = new TraktSyncCollected
            {
                Shows = showPayload.ToList()
            };

            var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
            var response = await PostToTrakt(url, data, cancellationToken, traktUser);
            return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response);
        }
        /// <summary>
        /// Gets the new path.
        /// </summary>
        /// <param name="sourcePath">The source path.</param>
        /// <param name="series">The series.</param>
        /// <param name="seasonNumber">The season number.</param>
        /// <param name="episodeNumber">The episode number.</param>
        /// <param name="endingEpisodeNumber">The ending episode number.</param>
        /// <param name="options">The options.</param>
        /// <returns>System.String.</returns>
        private async Task<string> GetNewPath(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, TvFileOrganizationOptions options, CancellationToken cancellationToken)
        {
            var episodeInfo = new EpisodeInfo
            {
                IndexNumber = episodeNumber,
                IndexNumberEnd = endingEpisodeNumber,
                MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
                MetadataLanguage = series.GetPreferredMetadataLanguage(),
                ParentIndexNumber = seasonNumber,
                SeriesProviderIds = series.ProviderIds
            };

            var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
            {
                SearchInfo = episodeInfo

            }, cancellationToken).ConfigureAwait(false);

            var episode = searchResults.FirstOrDefault();

            if (episode == null)
            {
                return null;
            }

            var newPath = GetSeasonFolderPath(series, seasonNumber, options);

            var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber, episodeNumber, endingEpisodeNumber, episode.Name, options);

            newPath = Path.Combine(newPath, episodeFileName);

            return newPath;
        }
Example #20
0
        /// <summary>
        /// Gets the new path.
        /// </summary>
        /// <param name="sourcePath">The source path.</param>
        /// <param name="series">The series.</param>
        /// <param name="seasonNumber">The season number.</param>
        /// <param name="episodeNumber">The episode number.</param>
        /// <param name="endingEpisodeNumber">The ending episode number.</param>
        /// <param name="premiereDate">The premiere date.</param>
        /// <param name="options">The options.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>System.String.</returns>
        private async Task<string> GetNewPath(string sourcePath,
            Series series,
            int? seasonNumber,
            int? episodeNumber,
            int? endingEpisodeNumber,
            DateTime? premiereDate,
            TvFileOrganizationOptions options,
            CancellationToken cancellationToken)
        {
            var episodeInfo = new EpisodeInfo
            {
                IndexNumber = episodeNumber,
                IndexNumberEnd = endingEpisodeNumber,
                MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
                MetadataLanguage = series.GetPreferredMetadataLanguage(),
                ParentIndexNumber = seasonNumber,
                SeriesProviderIds = series.ProviderIds,
                PremiereDate = premiereDate
            };

            var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
            {
                SearchInfo = episodeInfo

            }, cancellationToken).ConfigureAwait(false);

            var episode = searchResults.FirstOrDefault();

            if (episode == null)
            {
                var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
                _logger.Warn(msg);
                return null;
            }

            var episodeName = episode.Name;

            //if (string.IsNullOrWhiteSpace(episodeName))
            //{
            //    var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
            //    _logger.Warn(msg);
            //    return null;
            //}

            seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
            episodeNumber = episodeNumber ?? episode.IndexNumber;

            var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);

            // MAX_PATH - trailing <NULL> charachter - drive component: 260 - 1 - 3 = 256
            // Usually newPath would include the drive component, but use 256 to be sure
            var maxFilenameLength = 256 - newPath.Length;

            if (!newPath.EndsWith(@"\"))
            {
                // Remove 1 for missing backslash combining path and filename
                maxFilenameLength--;
            }

            // Remove additional 4 chars to prevent PathTooLongException for downloaded subtitles (eg. filename.ext.eng.srt)
            maxFilenameLength -= 4;

            var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options, maxFilenameLength);

            if (string.IsNullOrEmpty(episodeFileName))
            {
                // cause failure
                return string.Empty;
            }

            newPath = Path.Combine(newPath, episodeFileName);

            return newPath;
        }
Example #21
0
        private void SaveSmartMatchString(string matchString, Series series, AutoOrganizeOptions options)
        {
            if (string.IsNullOrEmpty(matchString) || matchString.Length < 3)
            {
                return;
            }

            SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, series.Name, StringComparison.OrdinalIgnoreCase));

            if (info == null)
            {
                info = new SmartMatchInfo();
                info.ItemName = series.Name;
                info.OrganizerType = FileOrganizerType.Episode;
                info.DisplayName = series.Name;
                var list = options.SmartMatchInfos.ToList();
                list.Add(info);
                options.SmartMatchInfos = list.ToArray();
            }

            if (!info.MatchStrings.Contains(matchString, StringComparer.OrdinalIgnoreCase))
            {
                var list = info.MatchStrings.ToList();
                list.Add(matchString);
                info.MatchStrings = list.ToArray();
                _config.SaveAutoOrganizeOptions(options);
            }
        }
Example #22
0
        public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, AutoOrganizeOptions options, CancellationToken cancellationToken)
        {
            var result = _organizationService.GetResult(request.ResultId);

            Series series = null;

            if (request.NewSeriesProviderIds.Count > 0)
            {
                // We're having a new series here
                SeriesInfo seriesRequest = new SeriesInfo();
                seriesRequest.ProviderIds = request.NewSeriesProviderIds;

                var refreshOptions = new MetadataRefreshOptions(_fileSystem);
                series = new Series();
                series.Id = Guid.NewGuid();
                series.Name = request.NewSeriesName;

                int year;
                if (int.TryParse(request.NewSeriesYear, out year))
                {
                    series.ProductionYear = year;
                }

                var seriesFolderName = series.Name;
                if (series.ProductionYear.HasValue)
                {
                    seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
                }

                series.Path = Path.Combine(request.TargetFolder, seriesFolderName);

                series.ProviderIds = request.NewSeriesProviderIds;

                await series.RefreshMetadata(refreshOptions, cancellationToken);
            }

            if (series == null)
            {
                // Existing Series
                series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
            }

            await OrganizeEpisode(result.OriginalPath,
                series,
                request.SeasonNumber,
                request.EpisodeNumber,
                request.EndingEpisodeNumber,
                null,
                options,
                true,
                request.RememberCorrection,
                result,
                cancellationToken).ConfigureAwait(false);

            await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);

            return result;
        }
Example #23
0
 public List <BaseItem> GetEpisodes()
 {
     return(Series.GetSeasonEpisodes(this, null, null, new DtoOptions(true)));
 }
Example #24
0
 public IEnumerable <Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
 {
     return(GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null));
 }
        private async Task OrganizeEpisode(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
        {
            _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);

            // Proceed to sort the file
            var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, cancellationToken).ConfigureAwait(false);

            if (string.IsNullOrEmpty(newPath))
            {
                var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
                result.Status = FileSortingStatus.Failure;
                result.StatusMessage = msg;
                _logger.Warn(msg);
                return;
            }

            _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
            result.TargetPath = newPath;

            var fileExists = File.Exists(result.TargetPath);
            var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);

            if (!overwriteExisting)
            {
                if (fileExists || otherDuplicatePaths.Count > 0)
                {
                    result.Status = FileSortingStatus.SkippedExisting;
                    result.StatusMessage = string.Empty;
                    result.DuplicatePaths = otherDuplicatePaths;
                    return;
                }

                if (options.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
                {
                    _logger.Info("File {0} already copied to new path {1}, stopping organization", sourcePath, newPath);
                    result.Status = FileSortingStatus.SkippedExisting;
                    result.StatusMessage = string.Empty;
                    return;
                }
            }

   

            PerformFileSorting(options, result);

            if (overwriteExisting)
            {
                foreach (var path in otherDuplicatePaths)
                {
                    _logger.Debug("Removing duplicate episode {0}", path);

                    _libraryMonitor.ReportFileSystemChangeBeginning(path);

                    try
                    {
                        File.Delete(path);
                    }
                    catch (IOException ex)
                    {
                        _logger.ErrorException("Error removing duplicate episode", ex, path);
                    }
                    finally
                    {
                        _libraryMonitor.ReportFileSystemChangeComplete(path, true);
                    }
                }
            }
        }
        private List<string> GetOtherDuplicatePaths(string targetPath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber)
        {
            var episodePaths = series.RecursiveChildren
                .OfType<Episode>()
                .Where(i =>
                {
                    var locationType = i.LocationType;

                    // Must be file system based and match exactly
                    if (locationType != LocationType.Remote &&
                        locationType != LocationType.Virtual &&
                        i.ParentIndexNumber.HasValue &&
                        i.ParentIndexNumber.Value == seasonNumber &&
                        i.IndexNumber.HasValue &&
                        i.IndexNumber.Value == episodeNumber)
                    {

                        if (endingEpisodeNumber.HasValue || i.IndexNumberEnd.HasValue)
                        {
                            return endingEpisodeNumber.HasValue && i.IndexNumberEnd.HasValue &&
                                   endingEpisodeNumber.Value == i.IndexNumberEnd.Value;
                        }

                        return true;
                    }

                    return false;
                })
                .Select(i => i.Path)
                .ToList();

            var folder = Path.GetDirectoryName(targetPath);
            var targetFileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);

            try
            {
                var filesOfOtherExtensions = Directory.EnumerateFiles(folder, "*", SearchOption.TopDirectoryOnly)
                    .Where(i => EntityResolutionHelper.IsVideoFile(i) && string.Equals(Path.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));

                episodePaths.AddRange(filesOfOtherExtensions);
            }
            catch (DirectoryNotFoundException)
            {
                // No big deal. Maybe the season folder doesn't already exist.
            }

            return episodePaths.Where(i => !string.Equals(i, targetPath, StringComparison.OrdinalIgnoreCase))
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToList();
        }
        /// <summary>
        /// Adds the episode.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="seasonNumber">The season number.</param>
        /// <param name="episodeNumber">The episode number.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task AddEpisode(Series series, int seasonNumber, int episodeNumber, CancellationToken cancellationToken)
        {
            var season = series.Children.OfType<Season>()
                .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber);

            if (season == null)
            {
                season = await AddSeason(series, seasonNumber, cancellationToken).ConfigureAwait(false);
            }

            var name = string.Format("Episode {0}", episodeNumber.ToString(UsCulture));

            var episode = new Episode
            {
                Name = name,
                IndexNumber = episodeNumber,
                ParentIndexNumber = seasonNumber,
                Parent = season,
                DisplayMediaType = typeof(Episode).Name,
                Id = (series.Id + seasonNumber.ToString(UsCulture) + name).GetMBId(typeof(Episode))
            };

            await season.AddChild(episode, cancellationToken).ConfigureAwait(false);

            await episode.RefreshMetadata(new MetadataRefreshOptions
            {
            }, cancellationToken).ConfigureAwait(false);
        }
        private int AdjustForSeriesOffset(Series series, int seasonNumber)
        {
            var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds);
            if (offset != null)
                return (seasonNumber + offset.Value);

            return seasonNumber;
        }
Example #29
0
 public List <BaseItem> GetEpisodes(Series series, User user, IEnumerable <Episode> allSeriesEpisodes, DtoOptions options)
 {
     return(series.GetSeasonEpisodes(this, user, allSeriesEpisodes, options));
 }
Example #30
0
        /// <summary>
        /// Adds the episode.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="seasonNumber">The season number.</param>
        /// <param name="episodeNumber">The episode number.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task AddEpisode(Series series, int seasonNumber, int episodeNumber, CancellationToken cancellationToken)
        {
            var season = series.Children.OfType<Season>()
                .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber);

            if (season == null)
            {
                var provider = new DummySeasonProvider(_config, _logger, _localization, _libraryManager, _fileSystem);
                season = await provider.AddSeason(series, seasonNumber, cancellationToken).ConfigureAwait(false);
            }

            var name = string.Format("Episode {0}", episodeNumber.ToString(_usCulture));

            var episode = new Episode
            {
                Name = name,
                IndexNumber = episodeNumber,
                ParentIndexNumber = seasonNumber,
                Id = _libraryManager.GetNewItemId((series.Id + seasonNumber.ToString(_usCulture) + name), typeof(Episode))
            };

            episode.SetParent(season);

            await season.AddChild(episode, cancellationToken).ConfigureAwait(false);

            await episode.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
            {
            }, cancellationToken).ConfigureAwait(false);
        }
        /// <summary>
        /// Fetches the data from actor node.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="reader">The reader.</param>
        private void FetchDataFromActorNode(Series series, XmlReader reader)
        {
            reader.MoveToContent();

            var personInfo = new PersonInfo();

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "Name":
                            {
                                personInfo.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
                                break;
                            }

                        case "Role":
                            {
                                personInfo.Role = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
                                break;
                            }

                        case "SortOrder":
                            {
                                var val = reader.ReadElementContentAsString();

                                if (!string.IsNullOrWhiteSpace(val))
                                {
                                    int rval;

                                    // int.TryParse is local aware, so it can be probamatic, force us culture
                                    if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
                                    {
                                        personInfo.SortOrder = rval;
                                    }
                                }
                                break;
                            }
                        
                        default:
                            reader.Skip();
                            break;
                    }
                }
            }

            personInfo.Type = PersonType.Actor;

            if (!string.IsNullOrEmpty(personInfo.Name))
            {
                series.AddPerson(personInfo);
            }
        }
Example #32
0
        private async Task OrganizeEpisode(string sourcePath,
            Series series,
            int? seasonNumber,
            int? episodeNumber,
            int? endingEpiosdeNumber,
            DateTime? premiereDate,
            AutoOrganizeOptions options,
            bool overwriteExisting,
            bool rememberCorrection,
            FileOrganizationResult result,
            CancellationToken cancellationToken)
        {
            _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);

            var originalExtractedSeriesString = result.ExtractedName;

            // Proceed to sort the file
            var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);

            if (string.IsNullOrEmpty(newPath))
            {
                var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
                result.Status = FileSortingStatus.Failure;
                result.StatusMessage = msg;
                _logger.Warn(msg);
                return;
            }

            _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
            result.TargetPath = newPath;

            var fileExists = _fileSystem.FileExists(result.TargetPath);
            var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);

            if (!overwriteExisting)
            {
                if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
                {
                    var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
                    _logger.Info(msg);
                    result.Status = FileSortingStatus.SkippedExisting;
                    result.StatusMessage = msg;
                    return;
                }

                if (fileExists)
                {
                    var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
                    _logger.Info(msg);
                    result.Status = FileSortingStatus.SkippedExisting;
                    result.StatusMessage = msg;
                    result.TargetPath = newPath;
                    return;
                }

                if (otherDuplicatePaths.Count > 0)
                {
                    var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
                    _logger.Info(msg);
                    result.Status = FileSortingStatus.SkippedExisting;
                    result.StatusMessage = msg;
                    result.DuplicatePaths = otherDuplicatePaths;
                    return;
                }
            }

            PerformFileSorting(options.TvOptions, result);

            if (overwriteExisting)
            {
                var hasRenamedFiles = false;

                foreach (var path in otherDuplicatePaths)
                {
                    _logger.Debug("Removing duplicate episode {0}", path);

                    _libraryMonitor.ReportFileSystemChangeBeginning(path);

                    var renameRelatedFiles = !hasRenamedFiles &&
                        string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);

                    if (renameRelatedFiles)
                    {
                        hasRenamedFiles = true;
                    }

                    try
                    {
                        DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
                    }
                    catch (IOException ex)
                    {
                        _logger.ErrorException("Error removing duplicate episode", ex, path);
                    }
                    finally
                    {
                        _libraryMonitor.ReportFileSystemChangeComplete(path, true);
                    }
                }
            }

            if (rememberCorrection)
            {
                SaveSmartMatchString(originalExtractedSeriesString, series, options);
            }
        }
 public static void RemoveDuplicateTags(Series series)
 {
     for (int i = series.Tags.Count - 1; i >= 0; i--)
     {
         if (series.Genres.Contains(series.Tags[i]))
             series.Tags.RemoveAt(i);
     }
 }
Example #34
0
        private List<string> GetOtherDuplicatePaths(string targetPath,
            Series series,
            int? seasonNumber,
            int? episodeNumber,
            int? endingEpisodeNumber)
        {
            // TODO: Support date-naming?
            if (!seasonNumber.HasValue || !episodeNumber.HasValue)
            {
                return new List<string>();
            }

            var episodePaths = series.GetRecursiveChildren()
                .OfType<Episode>()
                .Where(i =>
                {
                    var locationType = i.LocationType;

                    // Must be file system based and match exactly
                    if (locationType != LocationType.Remote &&
                        locationType != LocationType.Virtual &&
                        i.ParentIndexNumber.HasValue &&
                        i.ParentIndexNumber.Value == seasonNumber &&
                        i.IndexNumber.HasValue &&
                        i.IndexNumber.Value == episodeNumber)
                    {

                        if (endingEpisodeNumber.HasValue || i.IndexNumberEnd.HasValue)
                        {
                            return endingEpisodeNumber.HasValue && i.IndexNumberEnd.HasValue &&
                                   endingEpisodeNumber.Value == i.IndexNumberEnd.Value;
                        }

                        return true;
                    }

                    return false;
                })
                .Select(i => i.Path)
                .ToList();

            var folder = Path.GetDirectoryName(targetPath);
            var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath);

            try
            {
                var filesOfOtherExtensions = _fileSystem.GetFilePaths(folder)
                    .Where(i => _libraryManager.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));

                episodePaths.AddRange(filesOfOtherExtensions);
            }
            catch (DirectoryNotFoundException)
            {
                // No big deal. Maybe the season folder doesn't already exist.
            }

            return episodePaths.Where(i => !string.Equals(i, targetPath, StringComparison.OrdinalIgnoreCase))
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToList();
        }
        public static void TidyGenres(Series series)
        {
            var config = PluginConfiguration.Instance != null ? PluginConfiguration.Instance() : new PluginConfiguration();

            var genres = new HashSet<string>();
            var tags = new HashSet<string>(series.Tags);

            foreach (string genre in series.Genres)
            {
                string mapped;
                if (GenreMappings.TryGetValue(genre, out mapped))
                    genres.Add(mapped);
                else
                {
                    if (config.MoveExcessGenresToTags)
                        tags.Add(genre);
                    else
                        genres.Add(genre);
                }

                if (GenresAsTags.Contains(genre))
                {
                    if (config.MoveExcessGenresToTags)
                        tags.Add(genre);
                    else if (!genres.Contains(genre))
                        genres.Add(genre);
                }
            }

            series.Genres = genres.ToList();
            series.Tags = tags.ToList();
        }
Example #36
0
        /// <summary>
        /// Gets the season folder path.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="seasonNumber">The season number.</param>
        /// <param name="options">The options.</param>
        /// <returns>System.String.</returns>
        private string GetSeasonFolderPath(Series series, int seasonNumber, TvFileOrganizationOptions options)
        {
            // If there's already a season folder, use that
            var season = series
                .GetRecursiveChildren(i => i is Season && i.LocationType == LocationType.FileSystem && i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber)
                .FirstOrDefault();

            if (season != null)
            {
                return season.Path;
            }

            var path = series.Path;

            if (series.ContainsEpisodesWithoutSeasonFolders)
            {
                return path;
            }

            if (seasonNumber == 0)
            {
                return Path.Combine(path, _fileSystem.GetValidFilename(options.SeasonZeroFolderName));
            }

            var seasonFolderName = options.SeasonFolderPattern
                .Replace("%s", seasonNumber.ToString(_usCulture))
                .Replace("%0s", seasonNumber.ToString("00", _usCulture))
                .Replace("%00s", seasonNumber.ToString("000", _usCulture));

            return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName));
        }
Example #37
0
        private void AttachSoundtrackIds(BaseItemDto dto, Series item, User user)
        {
            var tvdb = item.GetProviderId(MetadataProviders.Tvdb);

            if (string.IsNullOrEmpty(tvdb))
            {
                return;
            }

            var recursiveChildren = user == null
                                        ? _libraryManager.RootFolder.RecursiveChildren
                                        : user.RootFolder.GetRecursiveChildren(user);

            dto.SoundtrackIds = recursiveChildren
                .Where(i =>
                {
                    if (!string.IsNullOrEmpty(tvdb) &&
                        string.Equals(tvdb, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase) &&
                        i is MusicAlbum)
                    {
                        return true;
                    }
                    return false;
                })
                .Select(GetClientItemId)
                .ToArray();
        }
        /// <summary>
        /// Adds the season.
        /// </summary>
        /// <param name="series">The series.</param>
        /// <param name="seasonNumber">The season number.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task{Season}.</returns>
        private async Task<Season> AddSeason(Series series, int seasonNumber, CancellationToken cancellationToken)
        {
            _logger.Info("Creating Season {0} entry for {1}", seasonNumber, series.Name);

            var name = seasonNumber == 0 ? _config.Configuration.SeasonZeroDisplayName : string.Format("Season {0}", seasonNumber.ToString(UsCulture));

            var season = new Season
            {
                Name = name,
                IndexNumber = seasonNumber,
                Parent = series,
                DisplayMediaType = typeof(Season).Name,
                Id = (series.Id + seasonNumber.ToString(UsCulture) + name).GetMBId(typeof(Season))
            };

            await series.AddChild(season, cancellationToken).ConfigureAwait(false);

            await season.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken).ConfigureAwait(false);

            return season;
        }
Example #39
0
 public List <BaseItem> GetEpisodes(Series series, User user, DtoOptions options)
 {
     return(GetEpisodes(series, user, null, options));
 }