diff --git a/README.md b/README.md index 003cc43e..3d9962a9 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,16 @@ version. It is recommended to use the "main" repository, which will always conta compatible with the latest major Jellyfin server. Otherwise you can use the specific ones to avoid breakage with the server version you are using. -| Jellyfin version | Repo URL | -|------------------------|-------------------------------------------------------| -| Latest major (10.9.x) | `https://repo.xkrivo.net/jellyfin/manifest.json` | -| 10.8.x | `https://repo.xkrivo.net/jellyfin-10-8/manifest.json` | -| Unstable - development | `https://repo.xkrivo.net/jellyfin-dev/manifest.json` | +| Jellyfin version | Repo URL | +|--------------------------------|-------------------------------------------------------| +| Latest major version | `https://repo.xkrivo.net/jellyfin/manifest.json` | +| Jellyfin 10.9.x | `https://repo.xkrivo.net/jellyfin-10-9/manifest.json` | +| Jellyfin 10.8.x | `https://repo.xkrivo.net/jellyfin-10-8/manifest.json` | +| Unstable - development builds | `https://repo.xkrivo.net/jellyfin-dev/manifest.json` | -The development repo should not be used, unless you are fine with all the risks of running unstable releases of software -or you have been explicitly asked to (for example when testing fixes or new features). +The development repo is listed here just for the sake of completeness. +It should not be used, unless you are fine with all the risks of running unstable releases of software or you have been +explicitly asked to. ### Adding the repository @@ -62,16 +64,18 @@ there, using one the URLs above. Repository name does not matter, it has only an admin. After you add the repository, you should be able to see `ListenBrainz` plugin in the catalog under `General` category. -Select the version you want to install and restart the server as asked. Continue with plugin [configuration](doc/configuration.md). +Select the version you want to install and restart the server as asked. Continue with +plugin [configuration](doc/configuration.md). Plugin and Jellyfin versions compatibility table: -| Plugin | Jellyfin | Status | -|---------|----------|-------------| -| 1.x.y.z | 10.7.a | Unsupported | -| 2.x.y.z | 10.8.a | Unsupported | -| 3.x.y.z | 10.8.a | Unsupported | -| 4.x.y.z | 10.9.a | Active | +| Plugin | Jellyfin | Status | +|---------|----------|---------------| +| 1.x.y.z | 10.7.a | Unsupported | +| 2.x.y.z | 10.8.a | Unsupported | +| 3.x.y.z | 10.8.a | Unsupported | +| 4.x.y.z | 10.9.a | Bugfixes only | +| 5.x.y.z | 10.10.a | Active | ## Configuration diff --git a/build.yaml b/build.yaml index b76fde51..0178b90e 100644 --- a/build.yaml +++ b/build.yaml @@ -1,8 +1,8 @@ --- name: "ListenBrainz" guid: "59B20823-AAFE-454C-A393-17427F518631" -version: "4.0.2.0" -targetAbi: "10.9.0.0" +version: "5.0.0.2" +targetAbi: "10.10.0.0" framework: "net8.0" overview: "Track your music habits with ListenBrainz." description: > @@ -16,5 +16,5 @@ artifacts: - "Jellyfin.Plugin.ListenBrainz.Http.dll" - "Jellyfin.Plugin.ListenBrainz.MusicBrainzApi.dll" changelog: > - Fix - - Submission of multiple artist MBIDs (#103 @moisespr123) + Maintenance + - Compatibility for Jellyfin 10.10 (#108 @lyarenei) diff --git a/doc/how-it-works.md b/doc/how-it-works.md index 1155ed16..4b5a2c0c 100644 --- a/doc/how-it-works.md +++ b/doc/how-it-works.md @@ -1,8 +1,7 @@ # How does the plugin work Here is a general description of how particular plugin features work. The plugin configuration is documented separately -and can be found [here](configuration.md). If you are upgrading from versions 2 and below, it might be worth to check -out the [migration documentation](migration.md) as well. +and can be found [here](configuration.md). ## Sending listens diff --git a/doc/migration.md b/doc/migration.md deleted file mode 100644 index f69d3375..00000000 --- a/doc/migration.md +++ /dev/null @@ -1,40 +0,0 @@ -# Upgrading from version 2.x and earlier - -For version 3.x, the plugin has been completely rewritten and the plugin configuration from earlier versions is not -compatible. To make this transition as effortless as possible, the plugin provides a task, which takes the old -configuration file and migrates it to the new one. This task should automatically run on server start after installing -the v3 version. - -If the migration is successful, `.migrated` (hidden) file is created in the plugin directory. If you wish to -run the migration again for some reason, delete this file and either restart the server or go to the scheduled tasks and -run the migration task manually. - -If everything went well, you don't have to do anything. The plugin should work as usual, as if there was no migration in -the first place. - -In case of a failure, the plugin will simply start with empty config. A nice side effect of the plugin (assembly) name -change is that the old plugin configuration is automatically left as is (backup). You can either choose to investigate -what went wrong or do the migration manually. Of course, you can also just configure the plugin again. - -### Changes - -Here are all changes between plugin version 2.x (left) and 3.x (right), excluding source code changes: - -#### Global configuration - -- Plugin assembly name: `Jellyfin.Plugin.Listenbrainz` -> `Jellyfin.Plugin.ListenBrainz` -- ListenBrainz URL: `ListenbrainzBaseUrl` -> `ListenBrainzApiUrl` -- MusicBrainz URL: `MusicbrainzBaseUrl` -> `MusicBrainzApiUrl` -- MusicBrainz integration: `MusicbrainzEnabled` -> `IsMusicBrainzEnabled` -- Alternative mode: `AlternativeListenDetectionEnabled` -> `IsAlternativeModeEnabled` - -#### User configuration - -- List of user configs: `LbUsers` -> `UserConfigs` -- User config: `LbUser` -> `UserConfig` -- API token: `Token` -> `ApiToken` -- User ID: `MediaBrowserUserId` -> `JellyfinUserId` -- Enable listen submit: `ListenSubmitEnabled` -> `IsListenSubmitEnabled` -- Enable favorite sync: `SyncFavoritesEnabled` -> `IsFavoritesSyncEnabled` - -Additionally, the API token is now obfuscated as a base64 string. diff --git a/src/Jellyfin.Plugin.ListenBrainz/Jellyfin.Plugin.ListenBrainz.csproj b/src/Jellyfin.Plugin.ListenBrainz/Jellyfin.Plugin.ListenBrainz.csproj index 3f1ce881..37165be6 100644 --- a/src/Jellyfin.Plugin.ListenBrainz/Jellyfin.Plugin.ListenBrainz.csproj +++ b/src/Jellyfin.Plugin.ListenBrainz/Jellyfin.Plugin.ListenBrainz.csproj @@ -11,9 +11,9 @@ - - - + + + diff --git a/src/Jellyfin.Plugin.ListenBrainz/Tasks/LovedTracksSyncTask.cs b/src/Jellyfin.Plugin.ListenBrainz/Tasks/LovedTracksSyncTask.cs index 25d2d749..548b0e70 100644 --- a/src/Jellyfin.Plugin.ListenBrainz/Tasks/LovedTracksSyncTask.cs +++ b/src/Jellyfin.Plugin.ListenBrainz/Tasks/LovedTracksSyncTask.cs @@ -146,7 +146,7 @@ private async Task HandleFavoriteSync(IProgress progress, UserConfig use var items = _libraryManager .GetItemList(q, allowedLibraries.ToList()) - .Where(i => !_userDataManager.GetUserData(userConfig.JellyfinUserId, i).IsFavorite) + .Where(i => !_userDataManager.GetUserData(user, i).IsFavorite) .Where(i => i.ProviderIds.GetValueOrDefault("MusicBrainzTrack") is not null) .ToList(); diff --git a/src/Jellyfin.Plugin.ListenBrainz/Tasks/MigrationTask.cs b/src/Jellyfin.Plugin.ListenBrainz/Tasks/MigrationTask.cs deleted file mode 100644 index 4a992796..00000000 --- a/src/Jellyfin.Plugin.ListenBrainz/Tasks/MigrationTask.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System.Collections.ObjectModel; -using System.Xml.Linq; -using Jellyfin.Plugin.ListenBrainz.Configuration; -using MediaBrowser.Model.Tasks; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.ListenBrainz.Tasks; - -// TODO: To be removed in v5.x (JF 10.10.a) - -/// -/// Jellyfin scheduled task to migrate old plugin configuration. -/// -public class MigrationTask : IScheduledTask -{ - private const string OldPluginConfigName = "Jellyfin.Plugin.Listenbrainz.xml"; - private const string MigratedFileName = ".migrated"; - - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// Logger factory. - public MigrationTask(ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger($"{Plugin.LoggerCategory}.MigrationTask"); - } - - private static string MigratedFilePath => Path.Join(Plugin.GetDataPath(), MigratedFileName); - - /// - public string Name => "Migrate from version 2.x and below"; - - /// - public string Key => "MigrateConfig"; - - /// - public string Description => "Migrate plugin configuration from plugin version 2.x and below to a new one."; - - /// - public string Category => "ListenBrainz"; - - /// - public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) - { - var configDir = Plugin.GetConfigDirPath(); - var oldPluginConfig = Path.Join(configDir, OldPluginConfigName); - if (!File.Exists(oldPluginConfig)) - { - _logger.LogInformation("Old plugin configuration file is not available, nothing to do"); - return; - } - - if (File.Exists(MigratedFilePath)) - { - _logger.LogInformation("Plugin configuration has been already migrated, nothing to do"); - return; - } - - var configFileContent = await File.ReadAllTextAsync(oldPluginConfig, cancellationToken); - var newConfig = MigrateConfig(configFileContent); - if (newConfig is null) - { - _logger.LogWarning("Failed to migrate plugin configuration"); - return; - } - - try - { - cancellationToken.ThrowIfCancellationRequested(); - Plugin.UpdateConfig(newConfig); - CreateMigratedFile(); - } - catch (Exception) - { - _logger.LogInformation("Plugin config migration has been cancelled"); - } - } - - /// - public IEnumerable GetDefaultTriggers() - { - return new[] - { - new TaskTriggerInfo - { - Type = TaskTriggerInfo.TriggerStartup, - MaxRuntimeTicks = 10 * TimeSpan.TicksPerSecond - } - }; - } - - private static void CreateMigratedFile() => File.CreateText(MigratedFilePath).Close(); - - private PluginConfiguration? MigrateConfig(string content) - { - var root = (XElement?)XDocument.Parse(content).FirstNode; - if (root is null) - { - _logger.LogInformation("Could not migrate configuration, the file format is malformed"); - return null; - } - - PluginConfiguration? newConfig = null; - Collection userConfigs = new Collection(); - foreach (var element in root.Descendants()) - { - switch (element.Name.ToString()) - { - case "GlobalConfig": - newConfig = ParseGlobalConfig(element); - break; - case "LbUsers": - userConfigs = ParseUserConfigs(element); - break; - } - } - - if (newConfig is null) return null; - newConfig.UserConfigs = userConfigs; - return newConfig; - } - - private static PluginConfiguration ParseGlobalConfig(XContainer globalConfig) - { - var pluginConfig = new PluginConfiguration(); - foreach (var element in globalConfig.Descendants()) - { - switch (element.Name.ToString()) - { - case "ListenbrainzBaseUrl": - pluginConfig.ListenBrainzApiUrl = element.Value; - break; - case "MusicbrainzBaseUrl": - pluginConfig.MusicBrainzApiUrl = element.Value; - break; - case "MusicbrainzEnabled": - pluginConfig.IsMusicBrainzEnabled = bool.Parse(element.Value); - break; - case "AlternativeListenDetectionEnabled": - pluginConfig.IsAlternativeModeEnabled = bool.Parse(element.Value); - break; - } - } - - return pluginConfig; - } - - private static Collection ParseUserConfigs(XContainer userConfigsArray) - { - return new Collection( - userConfigsArray - .Descendants() - .Where(c => c.Name.ToString() == "LbUser") - .Select(ParseUserConfig) - .ToList()); - } - - private static UserConfig ParseUserConfig(XElement config) - { - var newUserConfig = new UserConfig(); - foreach (var configField in config.Descendants()) - { - switch (configField.Name.ToString()) - { - case "Token": - newUserConfig.PlaintextApiToken = configField.Value; - break; - case "MediaBrowserUserId": - newUserConfig.JellyfinUserId = new Guid(configField.Value); - break; - case "ListenSubmitEnabled": - newUserConfig.IsListenSubmitEnabled = bool.Parse(configField.Value); - break; - case "SyncFavoritesEnabled": - newUserConfig.IsFavoritesSyncEnabled = bool.Parse(configField.Value); - break; - } - } - - return newUserConfig; - } -}