< Summary - Jellyfin

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

File(s)

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

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.IO;
 4using System.Linq;
 5using System.Threading;
 6using Jellyfin.Data.Enums;
 7using MediaBrowser.Controller;
 8using MediaBrowser.Controller.Entities;
 9using MediaBrowser.Controller.Entities.Audio;
 10using MediaBrowser.Controller.Persistence;
 11using MediaBrowser.Model.Entities;
 12using Microsoft.Extensions.Logging;
 13
 14namespace Jellyfin.Server.Migrations.Routines
 15{
 16    /// <summary>
 17    /// Fixes the data column of audio types to be deserializable.
 18    /// </summary>
 19#pragma warning disable CS0618 // Type or member is obsolete
 20    [JellyfinMigration("2025-04-20T18:00:00", nameof(FixAudioData), "CF6FABC2-9FBE-4933-84A5-FFE52EF22A58")]
 21    internal class FixAudioData : IMigrationRoutine
 22#pragma warning restore CS0618 // Type or member is obsolete
 23    {
 24        private const string DbFilename = "library.db";
 25        private readonly ILogger<FixAudioData> _logger;
 26        private readonly IServerApplicationPaths _applicationPaths;
 27        private readonly IItemRepository _itemRepository;
 28
 29        public FixAudioData(
 30            IServerApplicationPaths applicationPaths,
 31            ILoggerFactory loggerFactory,
 32            IItemRepository itemRepository)
 33        {
 034            _applicationPaths = applicationPaths;
 035            _itemRepository = itemRepository;
 036            _logger = loggerFactory.CreateLogger<FixAudioData>();
 037        }
 38
 39        /// <inheritdoc/>
 40        public void Perform()
 41        {
 042            var dbPath = Path.Combine(_applicationPaths.DataPath, DbFilename);
 43
 44            // Back up the database before modifying any entries
 045            for (int i = 1; ; i++)
 46            {
 047                var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i);
 048                if (!File.Exists(bakPath))
 49                {
 50                    try
 51                    {
 052                        _logger.LogInformation("Backing up {Library} to {BackupPath}", DbFilename, bakPath);
 053                        File.Copy(dbPath, bakPath);
 054                        _logger.LogInformation("{Library} backed up to {BackupPath}", DbFilename, bakPath);
 055                        break;
 56                    }
 057                    catch (Exception ex)
 58                    {
 059                        _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPa
 060                        throw;
 61                    }
 62                }
 63            }
 64
 065            _logger.LogInformation("Backfilling audio lyrics data to database.");
 066            var startIndex = 0;
 067            var records = _itemRepository.GetCount(new InternalItemsQuery
 068            {
 069                IncludeItemTypes = [BaseItemKind.Audio],
 070            });
 71
 072            while (startIndex < records)
 73            {
 074                var results = _itemRepository.GetItemList(new InternalItemsQuery
 075                {
 076                    IncludeItemTypes = [BaseItemKind.Audio],
 077                    StartIndex = startIndex,
 078                    Limit = 5000,
 079                    SkipDeserialization = true
 080                })
 081                .Cast<Audio>()
 082                .ToList();
 83
 084                foreach (var audio in results)
 85                {
 086                    var lyricMediaStreams = audio.GetMediaStreams().Where(s => s.Type == MediaStreamType.Lyric).Select(s
 087                    if (lyricMediaStreams.Count > 0)
 88                    {
 089                        audio.HasLyrics = true;
 090                        audio.LyricFiles = lyricMediaStreams;
 91                    }
 92                }
 93
 094                _itemRepository.SaveItems(results, CancellationToken.None);
 095                startIndex += results.Count;
 096                _logger.LogInformation("Backfilled data for {UpdatedRecords} of {TotalRecords} audio records", startInde
 97            }
 098        }
 99    }
 100}