< Summary - Jellyfin

Information
Class: Jellyfin.Server.Migrations.Routines.MoveTrickplayFiles
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/MoveTrickplayFiles.cs
Line coverage
49%
Covered lines: 25
Uncovered lines: 26
Coverable lines: 51
Total lines: 130
Line coverage: 49%
Branch coverage
31%
Covered branches: 5
Total branches: 16
Branch coverage: 31.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 48% (24/50) Branch coverage: 31.2% (5/16) Total lines: 1295/4/2026 - 12:15:16 AM Line coverage: 49% (25/51) Branch coverage: 31.2% (5/16) Total lines: 130 1/23/2026 - 12:11:06 AM Line coverage: 48% (24/50) Branch coverage: 31.2% (5/16) Total lines: 1295/4/2026 - 12:15:16 AM Line coverage: 49% (25/51) Branch coverage: 31.2% (5/16) Total lines: 130

Coverage delta

Coverage delta 1 -1

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Perform()41.66%221258.82%
GetOldTrickplayDirectory(...)0%620%
GetNewOldTrickplayDirectory(...)0%620%

File(s)

/srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/MoveTrickplayFiles.cs

#LineLine coverage
 1using System;
 2using System.Diagnostics;
 3using System.Globalization;
 4using System.IO;
 5using System.Linq;
 6using Jellyfin.Data.Enums;
 7using Jellyfin.Server.ServerSetupApp;
 8using MediaBrowser.Controller.Entities;
 9using MediaBrowser.Controller.Library;
 10using MediaBrowser.Controller.Trickplay;
 11using MediaBrowser.Model.IO;
 12using Microsoft.Extensions.Logging;
 13
 14namespace Jellyfin.Server.Migrations.Routines;
 15
 16/// <summary>
 17/// Migration to move trickplay files to the new directory.
 18/// </summary>
 19#pragma warning disable CS0618 // Type or member is obsolete
 20[JellyfinMigration("2025-04-20T23:00:00", nameof(MoveTrickplayFiles), RunMigrationOnSetup = true)]
 21public class MoveTrickplayFiles : IMigrationRoutine
 22#pragma warning restore CS0618 // Type or member is obsolete
 23{
 24    private readonly ITrickplayManager _trickplayManager;
 25    private readonly IFileSystem _fileSystem;
 26    private readonly ILibraryManager _libraryManager;
 27    private readonly IStartupLogger _logger;
 28
 29    /// <summary>
 30    /// Initializes a new instance of the <see cref="MoveTrickplayFiles"/> class.
 31    /// </summary>
 32    /// <param name="trickplayManager">Instance of the <see cref="ITrickplayManager"/> interface.</param>
 33    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 34    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 35    /// <param name="logger">The logger.</param>
 36    public MoveTrickplayFiles(
 37        ITrickplayManager trickplayManager,
 38        IFileSystem fileSystem,
 39        ILibraryManager libraryManager,
 40        IStartupLogger<MoveTrickplayFiles> logger)
 41    {
 2142        _trickplayManager = trickplayManager;
 2143        _fileSystem = fileSystem;
 2144        _libraryManager = libraryManager;
 2145        _logger = logger;
 2146    }
 47
 48    /// <inheritdoc />
 49    public void Perform()
 50    {
 51        const int Limit = 5000;
 4252        int itemCount = 0, offset = 0, previousCount;
 53
 2154        var sw = Stopwatch.StartNew();
 2155        var trickplayQuery = new InternalItemsQuery
 2156        {
 2157            MediaTypes = [MediaType.Video],
 2158            SourceTypes = [SourceType.Library],
 2159            IsVirtualItem = false,
 2160            IsFolder = false,
 2161            IncludeOwnedItems = true
 2162        };
 63
 64        do
 65        {
 2166            var trickplayInfos = _trickplayManager.GetTrickplayItemsAsync(Limit, offset).GetAwaiter().GetResult();
 2167            trickplayQuery.ItemIds = trickplayInfos.Select(i => i.ItemId).Distinct().ToArray();
 2168            var items = _libraryManager.GetItemList(trickplayQuery);
 4269            foreach (var trickplayInfo in trickplayInfos)
 70            {
 071                var item = items.OfType<Video>().FirstOrDefault(i => i.Id.Equals(trickplayInfo.ItemId));
 072                if (item is null)
 73                {
 74                    continue;
 75                }
 76
 077                var moved = false;
 078                var oldPath = GetOldTrickplayDirectory(item, trickplayInfo.Width);
 079                var newPath = _trickplayManager.GetTrickplayDirectory(item, trickplayInfo.TileWidth, trickplayInfo.TileH
 080                if (_fileSystem.DirectoryExists(oldPath))
 81                {
 082                    _fileSystem.MoveDirectory(oldPath, newPath);
 083                    moved = true;
 84                }
 85
 086                oldPath = GetNewOldTrickplayDirectory(item, trickplayInfo.TileWidth, trickplayInfo.TileHeight, trickplay
 087                if (_fileSystem.DirectoryExists(oldPath))
 88                {
 089                    _fileSystem.MoveDirectory(oldPath, newPath);
 090                    moved = true;
 91                }
 92
 093                if (moved)
 94                {
 095                    itemCount++;
 96                }
 97            }
 98
 2199            offset += Limit;
 21100            previousCount = trickplayInfos.Count;
 101
 21102            _logger.LogInformation("Checked: {Checked} - Moved: {Count} - Time: {Time}", offset, itemCount, sw.Elapsed);
 21103        } while (previousCount == Limit);
 104
 21105        _logger.LogInformation("Moved {Count} items in {Time}", itemCount, sw.Elapsed);
 21106    }
 107
 108    private string GetOldTrickplayDirectory(BaseItem item, int? width = null)
 109    {
 0110        var path = Path.Combine(item.GetInternalMetadataPath(), "trickplay");
 111
 0112        return width.HasValue ? Path.Combine(path, width.Value.ToString(CultureInfo.InvariantCulture)) : path;
 113    }
 114
 115    private string GetNewOldTrickplayDirectory(BaseItem item, int tileWidth, int tileHeight, int width, bool saveWithMed
 116    {
 0117        var path = saveWithMedia
 0118            ? Path.Combine(item.ContainingFolderPath, Path.ChangeExtension(item.Path, ".trickplay"))
 0119            : Path.Combine(item.GetInternalMetadataPath(), "trickplay");
 120
 0121        var subdirectory = string.Format(
 0122            CultureInfo.InvariantCulture,
 0123            "{0} - {1}x{2}",
 0124            width.ToString(CultureInfo.InvariantCulture),
 0125            tileWidth.ToString(CultureInfo.InvariantCulture),
 0126            tileHeight.ToString(CultureInfo.InvariantCulture));
 127
 0128        return Path.Combine(path, subdirectory);
 129    }
 130}