aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server/Migrations/Routines/FixLibrarySubtitleDownloadLanguages.cs
blob: 2b1f549940485c602eea7342993a0ebe695ef380 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Server.ServerSetupApp;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Globalization;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Server.Migrations.Routines;

/// <summary>
/// Migration to fix broken library subtitle download languages.
/// </summary>
[JellyfinMigration("2026-02-06T20:00:00", nameof(FixLibrarySubtitleDownloadLanguages))]
internal class FixLibrarySubtitleDownloadLanguages : IAsyncMigrationRoutine
{
    private readonly ILocalizationManager _localizationManager;
    private readonly ILibraryManager _libraryManager;
    private readonly ILogger _logger;

    /// <summary>
    /// Initializes a new instance of the <see cref="FixLibrarySubtitleDownloadLanguages"/> class.
    /// </summary>
    /// <param name="localizationManager">The Localization manager.</param>
    /// <param name="startupLogger">The startup logger for Startup UI integration.</param>
    /// <param name="libraryManager">The Library manager.</param>
    /// <param name="logger">The logger.</param>
    public FixLibrarySubtitleDownloadLanguages(
        ILocalizationManager localizationManager,
        IStartupLogger<FixLibrarySubtitleDownloadLanguages> startupLogger,
        ILibraryManager libraryManager,
        ILogger<FixLibrarySubtitleDownloadLanguages> logger)
    {
        _localizationManager = localizationManager;
        _libraryManager = libraryManager;
        _logger = startupLogger.With(logger);
    }

    /// <inheritdoc />
    public Task PerformAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Starting to fix library subtitle download languages.");

        var virtualFolders = _libraryManager.GetVirtualFolders(false);

        foreach (var virtualFolder in virtualFolders)
        {
            var options = virtualFolder.LibraryOptions;
            if (options?.SubtitleDownloadLanguages is null || options.SubtitleDownloadLanguages.Length == 0)
            {
                continue;
            }

            // Some virtual folders don't have a proper item id.
            if (!Guid.TryParse(virtualFolder.ItemId, out var folderId))
            {
                continue;
            }

            var collectionFolder = _libraryManager.GetItemById<CollectionFolder>(folderId);
            if (collectionFolder is null)
            {
                _logger.LogWarning("Could not find collection folder for virtual folder '{LibraryName}' with id '{FolderId}'. Skipping.", virtualFolder.Name, folderId);
                continue;
            }

            var fixedLanguages = new List<string>();

            foreach (var language in options.SubtitleDownloadLanguages)
            {
                var foundLanguage = _localizationManager.FindLanguageInfo(language)?.ThreeLetterISOLanguageName;
                if (foundLanguage is not null)
                {
                    // Converted ISO 639-2/B to T (ger to deu)
                    if (!string.Equals(foundLanguage, language, StringComparison.OrdinalIgnoreCase))
                    {
                        _logger.LogInformation("Converted '{Language}' to '{ResolvedLanguage}' in library '{LibraryName}'.", language, foundLanguage, virtualFolder.Name);
                    }

                    if (fixedLanguages.Contains(foundLanguage, StringComparer.OrdinalIgnoreCase))
                    {
                        _logger.LogInformation("Language '{Language}' already exists for library '{LibraryName}'. Skipping duplicate.", foundLanguage, virtualFolder.Name);
                        continue;
                    }

                    fixedLanguages.Add(foundLanguage);
                }
                else
                {
                    _logger.LogInformation("Could not resolve language '{Language}' in library '{LibraryName}'. Skipping.", language, virtualFolder.Name);
                }
            }

            options.SubtitleDownloadLanguages = [.. fixedLanguages];
            collectionFolder.UpdateLibraryOptions(options);
        }

        _logger.LogInformation("Library subtitle download languages fixed.");

        return Task.CompletedTask;
    }
}