< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.TV.Episode
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/TV/Episode.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 122
Coverable lines: 122
Total lines: 382
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 88
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/Entities/TV/Episode.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Globalization;
 8using System.Linq;
 9using System.Text.Json.Serialization;
 10using System.Threading;
 11using Jellyfin.Data.Enums;
 12using Jellyfin.Extensions;
 13using MediaBrowser.Controller.MediaEncoding;
 14using MediaBrowser.Controller.Providers;
 15using MediaBrowser.Model.Dlna;
 16using MediaBrowser.Model.Entities;
 17using MediaBrowser.Model.IO;
 18using Microsoft.Extensions.Logging;
 19
 20namespace MediaBrowser.Controller.Entities.TV
 21{
 22    /// <summary>
 23    /// Class Episode.
 24    /// </summary>
 25    public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries
 26    {
 27        public static IMediaEncoder MediaEncoder { get; set; }
 28
 29        /// <inheritdoc />
 30        [JsonIgnore]
 031        public IReadOnlyList<BaseItem> LocalTrailers => GetExtras()
 032            .Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer)
 033            .ToArray();
 34
 35        /// <summary>
 36        /// Gets or sets the season in which it aired.
 37        /// </summary>
 38        /// <value>The aired season.</value>
 39        public int? AirsBeforeSeasonNumber { get; set; }
 40
 41        public int? AirsAfterSeasonNumber { get; set; }
 42
 43        public int? AirsBeforeEpisodeNumber { get; set; }
 44
 45        /// <summary>
 46        /// Gets or sets the ending episode number for double episodes.
 47        /// </summary>
 48        /// <value>The index number.</value>
 49        public int? IndexNumberEnd { get; set; }
 50
 51        [JsonIgnore]
 052        protected override bool SupportsOwnedItems => IsStacked || MediaSourceCount > 1;
 53
 54        [JsonIgnore]
 055        public override bool SupportsInheritedParentImages => true;
 56
 57        [JsonIgnore]
 058        public override bool SupportsPeople => true;
 59
 60        [JsonIgnore]
 061        public int? AiredSeasonNumber => AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber;
 62
 63        [JsonIgnore]
 064        public override Folder LatestItemsIndexContainer => Series;
 65
 66        [JsonIgnore]
 067        public override Guid DisplayParentId => SeasonId;
 68
 69        [JsonIgnore]
 070        protected override bool EnableDefaultVideoUserDataKeys => false;
 71
 72        /// <summary>
 73        /// Gets the Episode's Series Instance.
 74        /// </summary>
 75        /// <value>The series.</value>
 76        [JsonIgnore]
 77        public Series Series
 78        {
 79            get
 80            {
 081                var seriesId = SeriesId;
 082                if (seriesId.IsEmpty())
 83                {
 084                    seriesId = FindSeriesId();
 85                }
 86
 087                return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series);
 88            }
 89        }
 90
 91        [JsonIgnore]
 92        public Season Season
 93        {
 94            get
 95            {
 096                var seasonId = SeasonId;
 097                if (seasonId.IsEmpty())
 98                {
 099                    seasonId = FindSeasonId();
 100                }
 101
 0102                return seasonId.IsEmpty() ? null : (LibraryManager.GetItemById(seasonId) as Season);
 103            }
 104        }
 105
 106        [JsonIgnore]
 0107        public bool IsInSeasonFolder => FindParent<Season>() is not null;
 108
 109        [JsonIgnore]
 110        public string SeriesPresentationUniqueKey { get; set; }
 111
 112        [JsonIgnore]
 113        public string SeriesName { get; set; }
 114
 115        [JsonIgnore]
 116        public string SeasonName { get; set; }
 117
 118        [JsonIgnore]
 119        public override bool SupportsRemoteImageDownloading
 120        {
 121            get
 122            {
 0123                if (IsMissingEpisode)
 124                {
 0125                    return false;
 126                }
 127
 0128                return true;
 129            }
 130        }
 131
 132        [JsonIgnore]
 0133        public bool IsMissingEpisode => LocationType == LocationType.Virtual;
 134
 135        [JsonIgnore]
 136        public Guid SeasonId { get; set; }
 137
 138        [JsonIgnore]
 139        public Guid SeriesId { get; set; }
 140
 141        public string FindSeriesSortName()
 142        {
 0143            var series = Series;
 0144            return series is null ? SeriesName : series.SortName;
 145        }
 146
 147        public override double GetDefaultPrimaryImageAspectRatio()
 148        {
 149            // hack for tv plugins
 0150            if (SourceType == SourceType.Channel)
 151            {
 0152                return 0;
 153            }
 154
 0155            return 16.0 / 9;
 156        }
 157
 158        public override List<string> GetUserDataKeys()
 159        {
 0160            var list = base.GetUserDataKeys();
 161
 0162            var series = Series;
 0163            if (series is not null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
 164            {
 0165                var seriesUserDataKeys = series.GetUserDataKeys();
 0166                var take = seriesUserDataKeys.Count;
 0167                if (seriesUserDataKeys.Count > 1)
 168                {
 0169                    take--;
 170                }
 171
 0172                var newList = seriesUserDataKeys.GetRange(0, take);
 0173                var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.T
 0174                for (int i = 0; i < take; i++)
 175                {
 0176                    newList[i] = newList[i] + suffix;
 177                }
 178
 0179                newList.AddRange(list);
 0180                list = newList;
 181            }
 182
 0183            return list;
 184        }
 185
 186        public string FindSeriesPresentationUniqueKey()
 0187            => Series?.PresentationUniqueKey;
 188
 189        public string FindSeasonName()
 190        {
 0191            var season = Season;
 192
 0193            if (season is null)
 194            {
 0195                if (ParentIndexNumber.HasValue)
 196                {
 0197                    return "Season " + ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture);
 198                }
 199
 0200                return "Season Unknown";
 201            }
 202
 0203            return season.Name;
 204        }
 205
 206        public string FindSeriesName()
 207        {
 0208            var series = Series;
 0209            return series is null ? SeriesName : series.Name;
 210        }
 211
 212        public Guid FindSeasonId()
 213        {
 0214            var season = FindParent<Season>();
 215
 216            // Episodes directly in series folder
 0217            if (season is null)
 218            {
 0219                var series = Series;
 220
 0221                if (series is not null && ParentIndexNumber.HasValue)
 222                {
 0223                    var findNumber = ParentIndexNumber.Value;
 224
 0225                    season = series.Children
 0226                        .OfType<Season>()
 0227                        .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber);
 228                }
 229            }
 230
 0231            return season is null ? Guid.Empty : season.Id;
 232        }
 233
 234        /// <summary>
 235        /// Creates the name of the sort.
 236        /// </summary>
 237        /// <returns>System.String.</returns>
 238        protected override string CreateSortName()
 239        {
 0240            return (ParentIndexNumber is not null ? ParentIndexNumber.Value.ToString("000 - ", CultureInfo.InvariantCult
 0241                    + (IndexNumber is not null ? IndexNumber.Value.ToString("0000 - ", CultureInfo.InvariantCulture) : s
 242        }
 243
 244        /// <summary>
 245        /// Determines whether [contains episode number] [the specified number].
 246        /// </summary>
 247        /// <param name="number">The number.</param>
 248        /// <returns><c>true</c> if [contains episode number] [the specified number]; otherwise, <c>false</c>.</returns>
 249        public bool ContainsEpisodeNumber(int number)
 250        {
 0251            if (IndexNumber.HasValue)
 252            {
 0253                if (IndexNumberEnd.HasValue)
 254                {
 0255                    return number >= IndexNumber.Value && number <= IndexNumberEnd.Value;
 256                }
 257
 0258                return IndexNumber.Value == number;
 259            }
 260
 0261            return false;
 262        }
 263
 264        public Guid FindSeriesId()
 265        {
 0266            var series = FindParent<Series>();
 0267            return series is null ? Guid.Empty : series.Id;
 268        }
 269
 270        public override IEnumerable<Guid> GetAncestorIds()
 271        {
 0272            var list = base.GetAncestorIds().ToList();
 273
 0274            var seasonId = SeasonId;
 275
 0276            if (!seasonId.IsEmpty() && !list.Contains(seasonId))
 277            {
 0278                list.Add(seasonId);
 279            }
 280
 0281            return list;
 282        }
 283
 284        public override IEnumerable<FileSystemMetadata> GetDeletePaths()
 285        {
 0286            return new[]
 0287            {
 0288                new FileSystemMetadata
 0289                {
 0290                    FullName = Path,
 0291                    IsDirectory = IsFolder
 0292                }
 0293            }.Concat(GetLocalMetadataFilesToDelete());
 294        }
 295
 296        public override UnratedItem GetBlockUnratedType()
 297        {
 0298            return UnratedItem.Series;
 299        }
 300
 301        public EpisodeInfo GetLookupInfo()
 302        {
 0303            var id = GetItemLookupInfo<EpisodeInfo>();
 304
 0305            var series = Series;
 306
 0307            if (series is not null)
 308            {
 0309                id.SeriesProviderIds = series.ProviderIds;
 0310                id.SeriesDisplayOrder = series.DisplayOrder;
 311            }
 312
 0313            if (Season is not null)
 314            {
 0315                id.SeasonProviderIds = Season.ProviderIds;
 316            }
 317
 0318            id.IsMissingEpisode = IsMissingEpisode;
 0319            id.IndexNumberEnd = IndexNumberEnd;
 320
 0321            return id;
 322        }
 323
 324        public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
 325        {
 0326            var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
 327
 0328            if (!IsLocked)
 329            {
 0330                if (SourceType == SourceType.Library || SourceType == SourceType.LiveTV)
 331                {
 0332                    var libraryOptions = LibraryManager.GetLibraryOptions(this);
 0333                    if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(Container, "mp4", StringComparison.Or
 334                    {
 335                        try
 336                        {
 0337                            var mediaInfo = MediaEncoder.GetMediaInfo(
 0338                                new MediaInfoRequest
 0339                                {
 0340                                    MediaSource = GetMediaSources(false)[0],
 0341                                    MediaType = DlnaProfileType.Video
 0342                                },
 0343                                CancellationToken.None).GetAwaiter().GetResult();
 0344                            if (mediaInfo.ParentIndexNumber > 0)
 345                            {
 0346                                ParentIndexNumber = mediaInfo.ParentIndexNumber;
 347                            }
 348
 0349                            if (mediaInfo.IndexNumber > 0)
 350                            {
 0351                                IndexNumber = mediaInfo.IndexNumber;
 352                            }
 353
 0354                            if (!string.IsNullOrEmpty(mediaInfo.ShowName))
 355                            {
 0356                                SeriesName = mediaInfo.ShowName;
 357                            }
 0358                        }
 0359                        catch (Exception ex)
 360                        {
 0361                            Logger.LogError(ex, "Error reading the episode information with ffprobe. Episode: {EpisodeIn
 0362                        }
 363                    }
 364
 365                    try
 366                    {
 0367                        if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetadata))
 368                        {
 0369                            hasChanges = true;
 370                        }
 0371                    }
 0372                    catch (Exception ex)
 373                    {
 0374                        Logger.LogError(ex, "Error in FillMissingEpisodeNumbersFromPath. Episode: {Episode}", Path ?? Na
 0375                    }
 376                }
 377            }
 378
 0379            return hasChanges;
 380        }
 381    }
 382}