< Summary - Jellyfin

Information
Class: Jellyfin.Server.Migrations.Routines.RefreshInternalDateModified
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/RefreshInternalDateModified.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 52
Coverable lines: 52
Total lines: 131
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
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

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
Perform()0%272160%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Diagnostics;
 4using System.Linq;
 5using Jellyfin.Database.Implementations;
 6using Jellyfin.Database.Implementations.Entities;
 7using Jellyfin.Extensions;
 8using MediaBrowser.Controller;
 9using MediaBrowser.Controller.Configuration;
 10using MediaBrowser.Controller.Entities;
 11using MediaBrowser.Controller.Entities.Audio;
 12using MediaBrowser.Controller.Library;
 13using MediaBrowser.Model.IO;
 14using Microsoft.EntityFrameworkCore;
 15using Microsoft.Extensions.Logging;
 16
 17namespace Jellyfin.Server.Migrations.Routines;
 18
 19/// <summary>
 20/// Migration to re-read creation dates for library items with internal metadata paths.
 21/// </summary>
 22[JellyfinMigration("2025-04-20T23:00:00", nameof(RefreshInternalDateModified))]
 23public class RefreshInternalDateModified : IDatabaseMigrationRoutine
 24{
 25    private readonly ILogger<RefreshInternalDateModified> _logger;
 26    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
 27    private readonly IFileSystem _fileSystem;
 28    private readonly IServerApplicationHost _applicationHost;
 29    private readonly bool _useFileCreationTimeForDateAdded;
 30
 031    private IReadOnlyList<string> _internalTypes = [
 032         typeof(Genre).FullName!,
 033         typeof(MusicGenre).FullName!,
 034         typeof(MusicArtist).FullName!,
 035         typeof(People).FullName!,
 036         typeof(Studio).FullName!
 037    ];
 38
 39    private IReadOnlyList<string> _internalPaths;
 40
 41    /// <summary>
 42    /// Initializes a new instance of the <see cref="RefreshInternalDateModified"/> class.
 43    /// </summary>
 44    /// <param name="applicationHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
 45    /// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
 46    /// <param name="configurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
 47    /// <param name="dbProvider">Instance of the <see cref="IDbContextFactory{JellyfinDbContext}"/> interface.</param>
 48    /// <param name="logger">The logger.</param>
 49    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 50    public RefreshInternalDateModified(
 51        IServerApplicationHost applicationHost,
 52        IServerApplicationPaths applicationPaths,
 53        IServerConfigurationManager configurationManager,
 54        IDbContextFactory<JellyfinDbContext> dbProvider,
 55        ILogger<RefreshInternalDateModified> logger,
 56        IFileSystem fileSystem)
 57    {
 058        _dbProvider = dbProvider;
 059        _logger = logger;
 060        _fileSystem = fileSystem;
 061        _applicationHost = applicationHost;
 062        _internalPaths = [
 063            applicationPaths.ArtistsPath,
 064            applicationPaths.GenrePath,
 065            applicationPaths.MusicGenrePath,
 066            applicationPaths.StudioPath,
 067            applicationPaths.PeoplePath
 068        ];
 069        _useFileCreationTimeForDateAdded = configurationManager.GetMetadataConfiguration().UseFileCreationTimeForDateAdd
 070    }
 71
 72    /// <inheritdoc />
 73    public void Perform()
 74    {
 75        const int Limit = 5000;
 076        int itemCount = 0, offset = 0;
 77
 078        var sw = Stopwatch.StartNew();
 79
 080        using var context = _dbProvider.CreateDbContext();
 081        var records = context.BaseItems.Count(b => _internalTypes.Contains(b.Type));
 082        _logger.LogInformation("Checking if {Count} potentially internal items require refreshed DateModified", records)
 83
 84        do
 85        {
 086            var results = context.BaseItems
 087                            .Where(b => _internalTypes.Contains(b.Type))
 088                            .OrderBy(e => e.Id)
 089                            .Skip(offset)
 090                            .Take(Limit)
 091                            .ToList();
 92
 093            foreach (var item in results)
 94            {
 095                var itemPath = item.Path;
 096                if (itemPath is not null)
 97                {
 098                    var realPath = _applicationHost.ExpandVirtualPath(item.Path);
 099                    if (_internalPaths.Any(path => realPath.StartsWith(path, StringComparison.Ordinal)))
 100                    {
 0101                        var writeTime = _fileSystem.GetLastWriteTimeUtc(realPath);
 0102                        var itemModificationTime = item.DateModified;
 0103                        if (writeTime != itemModificationTime)
 104                        {
 0105                            _logger.LogDebug("Reset file modification date: Old: {Old} - New: {New} - Path: {Path}", ite
 0106                            item.DateModified = writeTime;
 0107                            if (_useFileCreationTimeForDateAdded)
 108                            {
 0109                                item.DateCreated = _fileSystem.GetCreationTimeUtc(realPath);
 110                            }
 111
 0112                            itemCount++;
 113                        }
 114                    }
 115                }
 116            }
 117
 0118            offset += Limit;
 0119            if (offset > records)
 120            {
 0121                offset = records;
 122            }
 123
 0124            _logger.LogInformation("Checked: {Count} - Refreshed: {Items} - Time: {Time}", offset, itemCount, sw.Elapsed
 0125        } while (offset < records);
 126
 0127        context.SaveChanges();
 128
 0129        _logger.LogInformation("Refreshed DateModified for {Count} items in {Time}", itemCount, sw.Elapsed);
 0130    }
 131}