< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.Video
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/Video.cs
Line coverage
13%
Covered lines: 20
Uncovered lines: 128
Coverable lines: 148
Total lines: 554
Line coverage: 13.5%
Branch coverage
14%
Covered branches: 11
Total branches: 74
Branch coverage: 14.8%
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/Video.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 System.Threading.Tasks;
 12using Jellyfin.Data.Enums;
 13using Jellyfin.Extensions;
 14using MediaBrowser.Controller.Library;
 15using MediaBrowser.Controller.LiveTv;
 16using MediaBrowser.Controller.Persistence;
 17using MediaBrowser.Controller.Providers;
 18using MediaBrowser.Model.Dto;
 19using MediaBrowser.Model.Entities;
 20using MediaBrowser.Model.IO;
 21using MediaBrowser.Model.MediaInfo;
 22
 23namespace MediaBrowser.Controller.Entities
 24{
 25    /// <summary>
 26    /// Class Video.
 27    /// </summary>
 28    public class Video : BaseItem,
 29        IHasAspectRatio,
 30        ISupportsPlaceHolders,
 31        IHasMediaSources
 32    {
 42533        public Video()
 34        {
 42535            AdditionalParts = Array.Empty<string>();
 42536            LocalAlternateVersions = Array.Empty<string>();
 42537            SubtitleFiles = Array.Empty<string>();
 42538            AudioFiles = Array.Empty<string>();
 42539            LinkedAlternateVersions = Array.Empty<LinkedChild>();
 42540        }
 41
 42        [JsonIgnore]
 43        public string PrimaryVersionId { get; set; }
 44
 45        public string[] AdditionalParts { get; set; }
 46
 47        public string[] LocalAlternateVersions { get; set; }
 48
 49        public LinkedChild[] LinkedAlternateVersions { get; set; }
 50
 51        [JsonIgnore]
 052        public override bool SupportsPlayedStatus => true;
 53
 54        [JsonIgnore]
 055        public override bool SupportsPeople => true;
 56
 57        [JsonIgnore]
 058        public override bool SupportsInheritedParentImages => true;
 59
 60        [JsonIgnore]
 61        public override bool SupportsPositionTicksResume
 62        {
 63            get
 64            {
 065                var extraType = ExtraType;
 066                if (extraType.HasValue)
 67                {
 068                    if (extraType.Value == Model.Entities.ExtraType.Sample)
 69                    {
 070                        return false;
 71                    }
 72
 073                    if (extraType.Value == Model.Entities.ExtraType.ThemeVideo)
 74                    {
 075                        return false;
 76                    }
 77
 078                    if (extraType.Value == Model.Entities.ExtraType.Trailer)
 79                    {
 080                        return false;
 81                    }
 82                }
 83
 084                return true;
 85            }
 86        }
 87
 88        [JsonIgnore]
 089        public override bool SupportsThemeMedia => true;
 90
 91        /// <summary>
 92        /// Gets or sets the timestamp.
 93        /// </summary>
 94        /// <value>The timestamp.</value>
 95        public TransportStreamTimestamp? Timestamp { get; set; }
 96
 97        /// <summary>
 98        /// Gets or sets the subtitle paths.
 99        /// </summary>
 100        /// <value>The subtitle paths.</value>
 101        public string[] SubtitleFiles { get; set; }
 102
 103        /// <summary>
 104        /// Gets or sets the audio paths.
 105        /// </summary>
 106        /// <value>The audio paths.</value>
 107        public string[] AudioFiles { get; set; }
 108
 109        /// <summary>
 110        /// Gets or sets a value indicating whether this instance has subtitles.
 111        /// </summary>
 112        /// <value><c>true</c> if this instance has subtitles; otherwise, <c>false</c>.</value>
 113        public bool HasSubtitles { get; set; }
 114
 115        public bool IsPlaceHolder { get; set; }
 116
 117        /// <summary>
 118        /// Gets or sets the default index of the video stream.
 119        /// </summary>
 120        /// <value>The default index of the video stream.</value>
 121        public int? DefaultVideoStreamIndex { get; set; }
 122
 123        /// <summary>
 124        /// Gets or sets the type of the video.
 125        /// </summary>
 126        /// <value>The type of the video.</value>
 127        public VideoType VideoType { get; set; }
 128
 129        /// <summary>
 130        /// Gets or sets the type of the iso.
 131        /// </summary>
 132        /// <value>The type of the iso.</value>
 133        public IsoType? IsoType { get; set; }
 134
 135        /// <summary>
 136        /// Gets or sets the video3 D format.
 137        /// </summary>
 138        /// <value>The video3 D format.</value>
 139        public Video3DFormat? Video3DFormat { get; set; }
 140
 141        /// <summary>
 142        /// Gets or sets the aspect ratio.
 143        /// </summary>
 144        /// <value>The aspect ratio.</value>
 145        public string AspectRatio { get; set; }
 146
 147        [JsonIgnore]
 0148        public override bool SupportsAddingToPlaylist => true;
 149
 150        [JsonIgnore]
 151        public int MediaSourceCount
 152        {
 153            get
 154            {
 0155                if (!string.IsNullOrEmpty(PrimaryVersionId))
 156                {
 0157                    var item = LibraryManager.GetItemById(PrimaryVersionId);
 0158                    if (item is Video video)
 159                    {
 0160                        return video.MediaSourceCount;
 161                    }
 162                }
 163
 0164                return LinkedAlternateVersions.Length + LocalAlternateVersions.Length + 1;
 165            }
 166        }
 167
 168        [JsonIgnore]
 40169        public bool IsStacked => AdditionalParts.Length > 0;
 170
 171        [JsonIgnore]
 4172        public override bool HasLocalAlternateVersions => LocalAlternateVersions.Length > 0;
 173
 174        public static IRecordingsManager RecordingsManager { get; set; }
 175
 176        [JsonIgnore]
 177        public override SourceType SourceType
 178        {
 179            get
 180            {
 28181                if (IsActiveRecording())
 182                {
 0183                    return SourceType.LiveTV;
 184                }
 185
 28186                return base.SourceType;
 187            }
 188        }
 189
 190        [JsonIgnore]
 191        public bool IsCompleteMedia
 192        {
 193            get
 194            {
 0195                if (SourceType == SourceType.Channel)
 196                {
 0197                    return !Tags.Contains("livestream", StringComparison.OrdinalIgnoreCase);
 198                }
 199
 0200                return !IsActiveRecording();
 201            }
 202        }
 203
 204        [JsonIgnore]
 0205        protected virtual bool EnableDefaultVideoUserDataKeys => true;
 206
 207        [JsonIgnore]
 208        public override string ContainingFolderPath
 209        {
 210            get
 211            {
 40212                if (IsStacked)
 213                {
 0214                    return System.IO.Path.GetDirectoryName(Path);
 215                }
 216
 40217                if (!IsPlaceHolder)
 218                {
 40219                    if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
 220                    {
 1221                        return Path;
 222                    }
 223                }
 224
 39225                return base.ContainingFolderPath;
 226            }
 227        }
 228
 229        [JsonIgnore]
 230        public override string FileNameWithoutExtension
 231        {
 232            get
 233            {
 31234                if (IsFileProtocol)
 235                {
 31236                    if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
 237                    {
 0238                        return System.IO.Path.GetFileName(Path);
 239                    }
 240
 31241                    return System.IO.Path.GetFileNameWithoutExtension(Path);
 242                }
 243
 0244                return null;
 245            }
 246        }
 247
 248        /// <summary>
 249        /// Gets a value indicating whether [is3 D].
 250        /// </summary>
 251        /// <value><c>true</c> if [is3 D]; otherwise, <c>false</c>.</value>
 252        [JsonIgnore]
 0253        public bool Is3D => Video3DFormat.HasValue;
 254
 255        /// <summary>
 256        /// Gets the type of the media.
 257        /// </summary>
 258        /// <value>The type of the media.</value>
 259        [JsonIgnore]
 0260        public override MediaType MediaType => MediaType.Video;
 261
 262        public override List<string> GetUserDataKeys()
 263        {
 0264            var list = base.GetUserDataKeys();
 265
 0266            if (EnableDefaultVideoUserDataKeys)
 267            {
 0268                if (ExtraType.HasValue)
 269                {
 0270                    var key = this.GetProviderId(MetadataProvider.Tmdb);
 0271                    if (!string.IsNullOrEmpty(key))
 272                    {
 0273                        list.Insert(0, GetUserDataKey(key));
 274                    }
 275
 0276                    key = this.GetProviderId(MetadataProvider.Imdb);
 0277                    if (!string.IsNullOrEmpty(key))
 278                    {
 0279                        list.Insert(0, GetUserDataKey(key));
 280                    }
 281                }
 282                else
 283                {
 0284                    var key = this.GetProviderId(MetadataProvider.Imdb);
 0285                    if (!string.IsNullOrEmpty(key))
 286                    {
 0287                        list.Insert(0, key);
 288                    }
 289
 0290                    key = this.GetProviderId(MetadataProvider.Tmdb);
 0291                    if (!string.IsNullOrEmpty(key))
 292                    {
 0293                        list.Insert(0, key);
 294                    }
 295                }
 296            }
 297
 0298            return list;
 299        }
 300
 301        public void SetPrimaryVersionId(string id)
 302        {
 0303            if (string.IsNullOrEmpty(id))
 304            {
 0305                PrimaryVersionId = null;
 306            }
 307            else
 308            {
 0309                PrimaryVersionId = id;
 310            }
 311
 0312            PresentationUniqueKey = CreatePresentationUniqueKey();
 0313        }
 314
 315        public override string CreatePresentationUniqueKey()
 316        {
 0317            if (!string.IsNullOrEmpty(PrimaryVersionId))
 318            {
 0319                return PrimaryVersionId;
 320            }
 321
 0322            return base.CreatePresentationUniqueKey();
 323        }
 324
 325        public override bool CanDownload()
 326        {
 0327            if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
 328            {
 0329                return false;
 330            }
 331
 0332            return IsFileProtocol;
 333        }
 334
 335        protected override bool IsActiveRecording()
 336        {
 28337            return RecordingsManager.GetActiveRecordingInfo(Path) is not null;
 338        }
 339
 340        public override bool CanDelete()
 341        {
 0342            if (IsActiveRecording())
 343            {
 0344                return false;
 345            }
 346
 0347            return base.CanDelete();
 348        }
 349
 350        public IEnumerable<Guid> GetAdditionalPartIds()
 351        {
 0352            return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
 353        }
 354
 355        public IEnumerable<Guid> GetLocalAlternateVersionIds()
 356        {
 0357            return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
 358        }
 359
 360        private string GetUserDataKey(string providerId)
 361        {
 0362            var key = providerId + "-" + ExtraType.ToString().ToLowerInvariant();
 363
 364            // Make sure different trailers have their own data.
 0365            if (RunTimeTicks.HasValue)
 366            {
 0367                key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
 368            }
 369
 0370            return key;
 371        }
 372
 373        public IEnumerable<Video> GetLinkedAlternateVersions()
 374        {
 0375            return LinkedAlternateVersions
 0376                .Select(GetLinkedChild)
 0377                .Where(i => i is not null)
 0378                .OfType<Video>()
 0379                .OrderBy(i => i.SortName);
 380        }
 381
 382        /// <summary>
 383        /// Gets the additional parts.
 384        /// </summary>
 385        /// <returns>IEnumerable{Video}.</returns>
 386        public IOrderedEnumerable<Video> GetAdditionalParts()
 387        {
 0388            return GetAdditionalPartIds()
 0389                .Select(i => LibraryManager.GetItemById(i))
 0390                .Where(i => i is not null)
 0391                .OfType<Video>()
 0392                .OrderBy(i => i.SortName);
 393        }
 394
 395        internal override ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
 396        {
 0397            var updateType = base.UpdateFromResolvedItem(newItem);
 398
 0399            if (newItem is Video newVideo)
 400            {
 0401                if (!AdditionalParts.SequenceEqual(newVideo.AdditionalParts, StringComparer.Ordinal))
 402                {
 0403                    AdditionalParts = newVideo.AdditionalParts;
 0404                    updateType |= ItemUpdateType.MetadataImport;
 405                }
 406
 0407                if (!LocalAlternateVersions.SequenceEqual(newVideo.LocalAlternateVersions, StringComparer.Ordinal))
 408                {
 0409                    LocalAlternateVersions = newVideo.LocalAlternateVersions;
 0410                    updateType |= ItemUpdateType.MetadataImport;
 411                }
 412
 0413                if (VideoType != newVideo.VideoType)
 414                {
 0415                    VideoType = newVideo.VideoType;
 0416                    updateType |= ItemUpdateType.MetadataImport;
 417                }
 418            }
 419
 0420            return updateType;
 421        }
 422
 423        protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, IReadOnlyList<FileSystem
 424        {
 425            var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwa
 426
 427            if (IsStacked)
 428            {
 429                var tasks = AdditionalParts
 430                    .Select(i => RefreshMetadataForOwnedVideo(options, true, i, cancellationToken));
 431
 432                await Task.WhenAll(tasks).ConfigureAwait(false);
 433            }
 434
 435            // Must have a parent to have additional parts or alternate versions
 436            // In other words, it must be part of the Parent/Child tree
 437            // The additional parts won't have additional parts themselves
 438            if (IsFileProtocol && SupportsOwnedItems)
 439            {
 440                if (!IsStacked)
 441                {
 442                    RefreshLinkedAlternateVersions();
 443
 444                    var tasks = LocalAlternateVersions
 445                        .Select(i => RefreshMetadataForOwnedVideo(options, false, i, cancellationToken));
 446
 447                    await Task.WhenAll(tasks).ConfigureAwait(false);
 448                }
 449            }
 450
 451            return hasChanges;
 452        }
 453
 454        private void RefreshLinkedAlternateVersions()
 455        {
 0456            foreach (var child in LinkedAlternateVersions)
 457            {
 458                // Reset the cached value
 0459                if (child.ItemId.IsNullOrEmpty())
 460                {
 0461                    child.ItemId = null;
 462                }
 463            }
 0464        }
 465
 466        /// <inheritdoc />
 467        public override async Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationTo
 468        {
 469            await base.UpdateToRepositoryAsync(updateReason, cancellationToken).ConfigureAwait(false);
 470
 471            var localAlternates = GetLocalAlternateVersionIds()
 472                .Select(i => LibraryManager.GetItemById(i))
 473                .Where(i => i is not null);
 474
 475            foreach (var item in localAlternates)
 476            {
 477                item.ImageInfos = ImageInfos;
 478                item.Overview = Overview;
 479                item.ProductionYear = ProductionYear;
 480                item.PremiereDate = PremiereDate;
 481                item.CommunityRating = CommunityRating;
 482                item.OfficialRating = OfficialRating;
 483                item.Genres = Genres;
 484                item.ProviderIds = ProviderIds;
 485
 486                await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(fa
 487            }
 488        }
 489
 490        public override IEnumerable<FileSystemMetadata> GetDeletePaths()
 491        {
 0492            if (!IsInMixedFolder)
 493            {
 0494                return new[]
 0495                {
 0496                    new FileSystemMetadata
 0497                    {
 0498                        FullName = ContainingFolderPath,
 0499                        IsDirectory = true
 0500                    }
 0501                };
 502            }
 503
 0504            return base.GetDeletePaths();
 505        }
 506
 507        public virtual MediaStream GetDefaultVideoStream()
 508        {
 0509            if (!DefaultVideoStreamIndex.HasValue)
 510            {
 0511                return null;
 512            }
 513
 0514            return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
 0515            {
 0516                ItemId = Id,
 0517                Index = DefaultVideoStreamIndex.Value
 0518            }).FirstOrDefault();
 519        }
 520
 521        protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
 522        {
 0523            var list = new List<(BaseItem, MediaSourceType)>
 0524            {
 0525                (this, MediaSourceType.Default)
 0526            };
 527
 0528            list.AddRange(GetLinkedAlternateVersions().Select(i => ((BaseItem)i, MediaSourceType.Grouping)));
 529
 0530            if (!string.IsNullOrEmpty(PrimaryVersionId))
 531            {
 0532                if (LibraryManager.GetItemById(PrimaryVersionId) is Video primary)
 533                {
 0534                    var existingIds = list.Select(i => i.Item1.Id).ToList();
 0535                    list.Add((primary, MediaSourceType.Grouping));
 0536                    list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i 
 537                }
 538            }
 539
 0540            var localAlternates = list
 0541                .SelectMany(i =>
 0542                {
 0543                    return i.Item1 is Video video ? video.GetLocalAlternateVersionIds() : Enumerable.Empty<Guid>();
 0544                })
 0545                .Select(LibraryManager.GetItemById)
 0546                .Where(i => i is not null)
 0547                .ToList();
 548
 0549            list.AddRange(localAlternates.Select(i => (i, MediaSourceType.Default)));
 550
 0551            return list;
 552        }
 553    }
 554}