diff options
Diffstat (limited to 'MediaBrowser.Providers/Plugins')
57 files changed, 2882 insertions, 401 deletions
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs index 49ece22a98..1903adfbdd 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs @@ -21,6 +21,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.Plugins.AudioDb @@ -77,7 +78,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { result.Item = new MusicAlbum(); result.HasMetadata = true; - ProcessResult(result.Item, obj.album[0], info.MetadataLanguage); + ProcessResult(result, obj.album[0], info.MetadataLanguage); } } } @@ -85,8 +86,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb return result; } - private void ProcessResult(MusicAlbum item, Album result, string preferredLanguage) + private void ProcessResult(MetadataResult<MusicAlbum> metadataResult, Album result, string preferredLanguage) { + var item = metadataResult.Item; + if (Plugin.Instance.Configuration.ReplaceAlbumName && !string.IsNullOrWhiteSpace(result.strAlbum)) { item.Album = result.strAlbum; @@ -113,41 +116,48 @@ namespace MediaBrowser.Providers.Plugins.AudioDb item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID); item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, result.strMusicBrainzID); - string overview = null; + var language = MetadataLanguageUtils.GetLanguageSubtag(preferredLanguage); + var overview = GetDescription(result, language); - if (string.Equals(preferredLanguage, "de", StringComparison.OrdinalIgnoreCase)) - { - overview = result.strDescriptionDE; - } - else if (string.Equals(preferredLanguage, "fr", StringComparison.OrdinalIgnoreCase)) - { - overview = result.strDescriptionFR; - } - else if (string.Equals(preferredLanguage, "nl", StringComparison.OrdinalIgnoreCase)) - { - overview = result.strDescriptionNL; - } - else if (string.Equals(preferredLanguage, "ru", StringComparison.OrdinalIgnoreCase)) - { - overview = result.strDescriptionRU; - } - else if (string.Equals(preferredLanguage, "it", StringComparison.OrdinalIgnoreCase)) - { - overview = result.strDescriptionIT; - } - else if ((preferredLanguage ?? string.Empty).StartsWith("pt", StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(overview)) { - overview = result.strDescriptionPT; - } + overview = string.IsNullOrWhiteSpace(result.strDescriptionEN) + ? result.strDescription + : result.strDescriptionEN; - if (string.IsNullOrWhiteSpace(overview)) + // The description is not in the requested language, mark it as English so it does not + // block a provider further down the list that can serve the requested language + metadataResult.ResultLanguage = "en"; + } + else { - overview = result.strDescriptionEN; + metadataResult.ResultLanguage = language; } item.Overview = (overview ?? string.Empty).StripHtml(); } + private static string GetDescription(Album result, string language) + => language switch + { + "de" => result.strDescriptionDE, + "en" => result.strDescriptionEN, + "es" => result.strDescriptionES, + "fr" => result.strDescriptionFR, + "he" => result.strDescriptionIL, + "hu" => result.strDescriptionHU, + "it" => result.strDescriptionIT, + "ja" => result.strDescriptionJP, + "nl" => result.strDescriptionNL, + "no" or "nb" or "nn" => result.strDescriptionNO, + "pl" => result.strDescriptionPL, + "pt" => result.strDescriptionPT, + "ru" => result.strDescriptionRU, + "sv" => result.strDescriptionSE, + "zh" => result.strDescriptionCN, + _ => null + }; + internal async Task EnsureInfo(string musicBrainzReleaseGroupId, CancellationToken cancellationToken) { var xmlPath = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId); @@ -240,6 +250,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public string strAlbumCDart { get; set; } + public string strDescription { get; set; } + public string strDescriptionEN { get; set; } public string strDescriptionDE { get; set; } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs index 88730f34d2..28cfc8f9a4 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs @@ -3,32 +3,24 @@ #pragma warning disable CS1591 using System.Collections.Generic; -using System.IO; using System.Net.Http; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; - public AudioDbArtistImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) + public AudioDbArtistImageProvider(IHttpClientFactory httpClientFactory) { - _config = config; _httpClientFactory = httpClientFactory; } @@ -54,22 +46,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb /// <inheritdoc /> public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - if (item.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out var id)) - { - await AudioDbArtistProvider.Current.EnsureArtistInfo(id, cancellationToken).ConfigureAwait(false); - - var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id); + item.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out var musicBrainzId); + item.TryGetProviderId(MetadataProvider.AudioDbArtist, out var audioDbId); - FileStream jsonStream = AsyncFile.OpenRead(path); - await using (jsonStream.ConfigureAwait(false)) - { - var obj = await JsonSerializer.DeserializeAsync<AudioDbArtistProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); + var artist = await AudioDbArtistProvider.Current.GetArtist(musicBrainzId, audioDbId, cancellationToken).ConfigureAwait(false); - if (obj is not null && obj.artists is not null && obj.artists.Count > 0) - { - return GetImages(obj.artists[0]); - } - } + if (artist is not null) + { + return GetImages(artist); } return []; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs index d8cb6b4b24..2d9fe4448f 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Json; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -20,6 +22,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.Plugins.AudioDb @@ -52,87 +55,225 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public int Order => 1; /// <inheritdoc /> - public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) - => Task.FromResult(Enumerable.Empty<RemoteSearchResult>()); - - /// <inheritdoc /> - public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken) + public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) { - var result = new MetadataResult<MusicArtist>(); - var id = info.GetMusicBrainzArtistId(); + // Prefer a known TheAudioDB artist id. + var audioDbId = searchInfo.GetProviderId(MetadataProvider.AudioDbArtist); + if (!string.IsNullOrWhiteSpace(audioDbId)) + { + var artists = await FetchArtists(BaseUrl + "/artist.php?i=" + audioDbId, cancellationToken).ConfigureAwait(false); + return artists.Select(ToRemoteSearchResult); + } - if (!string.IsNullOrWhiteSpace(id)) + // Fall back to the MusicBrainz artist id, reusing the on-disk cache also used by GetMetadata. + var musicBrainzId = searchInfo.GetMusicBrainzArtistId(); + if (!string.IsNullOrWhiteSpace(musicBrainzId)) { - await EnsureArtistInfo(id, cancellationToken).ConfigureAwait(false); + await EnsureArtistInfo(musicBrainzId, cancellationToken).ConfigureAwait(false); - var path = GetArtistInfoPath(_config.ApplicationPaths, id); + var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); FileStream jsonStream = AsyncFile.OpenRead(path); await using (jsonStream.ConfigureAwait(false)) { var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (obj is not null && obj.artists is not null && obj.artists.Count > 0) + if (obj is not null && obj.artists is not null) { - result.Item = new MusicArtist(); - result.HasMetadata = true; - ProcessResult(result.Item, obj.artists[0], info.MetadataLanguage); + return obj.artists.Select(ToRemoteSearchResult); } } + + return []; + } + + // Finally, search by name. + if (!string.IsNullOrWhiteSpace(searchInfo.Name)) + { + var artists = await FetchArtists(BaseUrl + "/search.php?s=" + Uri.EscapeDataString(searchInfo.Name), cancellationToken).ConfigureAwait(false); + return artists.Select(ToRemoteSearchResult); + } + + return []; + } + + private async Task<List<Artist>> FetchArtists(string url, CancellationToken cancellationToken) + { + using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + + var obj = await response.Content.ReadFromJsonAsync<RootObject>(_jsonOptions, cancellationToken).ConfigureAwait(false); + + return obj?.artists ?? []; + } + + private RemoteSearchResult ToRemoteSearchResult(Artist artist) + { + var result = new RemoteSearchResult + { + Name = artist.strArtist, + ImageUrl = artist.strArtistThumb, + SearchProviderName = Name, + Overview = (artist.strBiographyEN ?? string.Empty).StripHtml() + }; + + if (!string.IsNullOrEmpty(artist.idArtist)) + { + result.SetProviderId(MetadataProvider.AudioDbArtist, artist.idArtist); + } + + if (!string.IsNullOrEmpty(artist.strMusicBrainzID)) + { + result.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.strMusicBrainzID); + } + + if (int.TryParse(artist.intFormedYear, NumberStyles.Integer, CultureInfo.InvariantCulture, out var formedYear)) + { + result.ProductionYear = formedYear; } return result; } - private void ProcessResult(MusicArtist item, Artist result, string preferredLanguage) + /// <inheritdoc /> + public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken) { - // item.HomePageUrl = result.strWebsite; + var result = new MetadataResult<MusicArtist>(); - if (!string.IsNullOrEmpty(result.strGenre)) + var artist = await GetArtist( + info.GetMusicBrainzArtistId(), + info.GetProviderId(MetadataProvider.AudioDbArtist), + cancellationToken).ConfigureAwait(false); + + if (artist is not null) { - item.Genres = new[] { result.strGenre }; + result.Item = new MusicArtist(); + result.HasMetadata = true; + ProcessResult(result, artist, info.MetadataLanguage); } - item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist); - item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID); + return result; + } + + /// <summary> + /// Resolves the cached AudioDB artist, preferring the MusicBrainz id and falling back to the AudioDB id. + /// </summary> + /// <param name="musicBrainzId">The MusicBrainz artist id, if known.</param> + /// <param name="audioDbId">The TheAudioDB artist id, if known.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The matching artist, or <c>null</c> if none could be resolved.</returns> + internal async Task<Artist> GetArtist(string musicBrainzId, string audioDbId, CancellationToken cancellationToken) + { + string path; + if (!string.IsNullOrWhiteSpace(musicBrainzId)) + { + await EnsureArtistInfo(musicBrainzId, cancellationToken).ConfigureAwait(false); + path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); + } + else if (!string.IsNullOrWhiteSpace(audioDbId)) + { + await EnsureArtistInfoByAudioDbId(audioDbId, cancellationToken).ConfigureAwait(false); + path = GetArtistInfoPath(_config.ApplicationPaths, audioDbId); + } + else + { + return null; + } + + FileStream jsonStream = AsyncFile.OpenRead(path); + await using (jsonStream.ConfigureAwait(false)) + { + var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); - string overview = null; + if (obj is not null && obj.artists is not null && obj.artists.Count > 0) + { + return obj.artists[0]; + } + } - if (string.Equals(preferredLanguage, "de", StringComparison.OrdinalIgnoreCase)) + return null; + } + + private void ProcessResult(MetadataResult<MusicArtist> metadataResult, Artist result, string preferredLanguage) + { + var item = metadataResult.Item; + + if (!string.IsNullOrWhiteSpace(result.strWebsite)) { - overview = result.strBiographyDE; + item.HomePageUrl = result.strWebsite; } - else if (string.Equals(preferredLanguage, "fr", StringComparison.OrdinalIgnoreCase)) + + var genres = new List<string>(); + if (!string.IsNullOrWhiteSpace(result.strGenre)) { - overview = result.strBiographyFR; + genres.Add(result.strGenre); } - else if (string.Equals(preferredLanguage, "nl", StringComparison.OrdinalIgnoreCase)) + + if (!string.IsNullOrWhiteSpace(result.strSubGenre)) { - overview = result.strBiographyNL; + genres.Add(result.strSubGenre); } - else if (string.Equals(preferredLanguage, "ru", StringComparison.OrdinalIgnoreCase)) + + if (genres.Count > 0) { - overview = result.strBiographyRU; + item.Genres = genres.ToArray(); } - else if (string.Equals(preferredLanguage, "it", StringComparison.OrdinalIgnoreCase)) + + if (int.TryParse(result.intFormedYear, NumberStyles.Integer, CultureInfo.InvariantCulture, out var formedYear)) { - overview = result.strBiographyIT; + item.ProductionYear = formedYear; } - else if ((preferredLanguage ?? string.Empty).StartsWith("pt", StringComparison.OrdinalIgnoreCase)) + + if (!string.IsNullOrWhiteSpace(result.strCountry)) { - overview = result.strBiographyPT; + item.ProductionLocations = new[] { result.strCountry }; } + item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist); + item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID); + + var language = MetadataLanguageUtils.GetLanguageSubtag(preferredLanguage); + var overview = GetBiography(result, language); + if (string.IsNullOrWhiteSpace(overview)) { overview = string.IsNullOrWhiteSpace(result.strBiographyEN) ? result.strBiography : result.strBiographyEN; + + // The biography is not in the requested language, mark it as English so it does not + // block a provider further down the list that can serve the requested language + metadataResult.ResultLanguage = "en"; + } + else + { + metadataResult.ResultLanguage = language; } item.Overview = (overview ?? string.Empty).StripHtml(); } + private static string GetBiography(Artist result, string language) + => language switch + { + "de" => result.strBiographyDE, + "en" => result.strBiographyEN, + "es" => result.strBiographyES, + "fr" => result.strBiographyFR, + "he" => result.strBiographyIL, + "hu" => result.strBiographyHU, + "it" => result.strBiographyIT, + "ja" => result.strBiographyJP, + "nl" => result.strBiographyNL, + "no" or "nb" or "nn" => result.strBiographyNO, + "pl" => result.strBiographyPL, + "pt" => result.strBiographyPT, + "ru" => result.strBiographyRU, + "sv" => result.strBiographySE, + "zh" => result.strBiographyCN, + _ => null + }; + internal async Task EnsureArtistInfo(string musicBrainzId, CancellationToken cancellationToken) { var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); @@ -150,13 +291,32 @@ namespace MediaBrowser.Providers.Plugins.AudioDb internal async Task DownloadArtistInfo(string musicBrainzId, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - var url = BaseUrl + "/artist-mb.php?i=" + musicBrainzId; + await DownloadArtistInfo(url, GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId), cancellationToken).ConfigureAwait(false); + } + + internal async Task EnsureArtistInfoByAudioDbId(string audioDbId, CancellationToken cancellationToken) + { + var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, audioDbId); + + var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath); + + if (fileInfo.Exists + && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) + { + return; + } + + var url = BaseUrl + "/artist.php?i=" + audioDbId; + await DownloadArtistInfo(url, xmlPath, cancellationToken).ConfigureAwait(false); + } + + private async Task DownloadArtistInfo(string url, string path, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); - var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); Directory.CreateDirectory(Path.GetDirectoryName(path)); var fileStreamOptions = AsyncFile.WriteOptions; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs index 6c2ad0573e..bb6b67286d 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs @@ -5,12 +5,13 @@ using System; using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.AudioDb { - public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages + public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage { public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer) @@ -29,6 +30,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb // TODO remove when plugin removed from server. public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml"; + public string ImageResourceName => GetType().Namespace + ".jellyfin-plugin-tadb.svg"; + public IEnumerable<PluginPageInfo> GetPages() { yield return new PluginPageInfo diff --git a/MediaBrowser.Providers/Plugins/AudioDb/jellyfin-plugin-tadb.svg b/MediaBrowser.Providers/Plugins/AudioDb/jellyfin-plugin-tadb.svg new file mode 100644 index 0000000000..94fa55cc9c --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/jellyfin-plugin-tadb.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1920 1080" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect x="-0" y="-0" width="1920" height="1080" style="fill:#15191f;"/><path d="M537.166,347.444c8.07,-10.159 20.665,-17.047 33.734,-17.407c7.234,-0.375 14.064,3.372 19.136,8.286c7.925,7.81 8.69,21.499 1.816,30.217c-1.671,0.994 -2.666,2.406 -2.983,4.265c-6.023,7.061 -15.808,9.51 -24.741,7.954c0.129,-0.807 0.404,-2.406 0.547,-3.213c-0.288,0.692 -0.865,2.075 -1.153,2.767c-5.908,-2.19 -11.629,-5.576 -14.438,-11.47c-0.534,-0.663 -1.586,-1.989 -2.118,-2.651c1.513,-2.262 3.156,-4.438 4.813,-6.6c1.08,5.908 3.674,11.917 9.12,15.015c5.261,3.703 11.888,2.968 17.911,2.306c7.033,-4.856 13.603,-11.701 13.906,-20.765c1.672,-12.825 -11.052,-23.632 -23.329,-22.436c-12.551,0.418 -22.855,8.487 -31.384,16.931c-0.216,-0.793 -0.635,-2.406 -0.837,-3.199Z" style="fill-rule:nonzero;"/><path d="M594.388,339.719c13.79,10.995 11.341,35.506 -4.222,43.662c-0.259,-0.144 -0.777,-0.447 -1.023,-0.591c0.347,-2.277 0.101,-4.467 -0.734,-6.6c0.23,-0.231 0.677,-0.677 0.893,-0.893c2.581,-0.965 5.246,-2.147 5.188,-5.389c1.413,-5.029 3.891,-9.871 3.761,-15.217c0.288,-5.288 -2.089,-10.145 -3.862,-14.972Z" style="fill-rule:nonzero;"/><path d="M533.896,352.054c0.533,0.13 1.585,0.403 2.119,0.548c0.432,1.614 -0.692,2.997 -1.903,3.905c-0.62,-0.605 -1.844,-1.816 -2.45,-2.406c0.562,-0.519 1.671,-1.542 2.234,-2.046Z" style="fill-rule:nonzero;"/><path d="M527.399,362.127c0.867,0.145 2.602,0.448 3.469,0.593c-0.347,0.694 -1.055,2.082 -1.402,2.775c-0.636,-0.593 -1.908,-1.778 -2.559,-2.371l0.492,-0.997Z" style="fill-rule:nonzero;"/><path d="M541.691,374.548c1.325,-1.902 2.652,-3.804 3.919,-5.75c0.591,0.418 1.758,1.254 2.349,1.672c-1.096,2.161 -2.104,4.366 -3.113,6.556c-0.792,-0.62 -2.377,-1.859 -3.156,-2.478Z" style="fill-rule:nonzero;"/><path d="M548.693,415.501c-6.671,-13.113 -8.098,-29.9 0.375,-42.552c1.887,1.816 3.818,3.617 5.75,5.404c-7.493,11.038 -7.306,26.183 -0.345,37.437c-1.845,0.548 -5.232,3.285 -5.779,-0.288Z" style="fill-rule:nonzero;"/><path d="M555.382,379.275c8.617,3.343 18.603,5.706 27.191,0.908c1.283,1.888 2.911,3.458 5.346,3.43c-0.259,0.533 -0.778,1.614 -1.037,2.147c-10.591,4.237 -23.819,2.406 -31.5,-6.484Z" style="fill-rule:nonzero;"/><path d="M545.324,432.764c0.765,0.737 0.765,0.737 0,0Z" style="fill-rule:nonzero;"/><path d="M598.524,436.814c0.246,-0.159 0.737,-0.477 0.997,-0.636l-0.449,0.304l-0.549,0.332Z" style="fill-rule:nonzero;"/><path d="M603.119,441.538c0.781,0.853 0.781,0.853 0,0Z" style="fill-rule:nonzero;"/><path d="M647.1,441.308c1.557,1.038 2.291,3.185 1.584,4.957c-1.441,1.254 -2.939,2.464 -4.351,3.761c0.619,-2.997 0.966,-6.153 2.767,-8.718Z" style="fill-rule:nonzero;"/><path d="M612.63,449.046l0.693,0.029l-0.015,0.723l-0.723,-0.029l0.044,-0.723Z" style="fill-rule:nonzero;"/><path d="M640.572,472.908c2.897,0.606 -0.331,6.758 -2.708,5.173c-0.894,-2.003 0.951,-4.338 2.708,-5.173Z" style="fill-rule:nonzero;"/><path d="M448.849,474.551c0.708,0.765 0.708,0.765 0,0Z" style="fill-rule:nonzero;"/><path d="M451.63,477.55c0.766,0.723 0.766,0.723 0,0Z" style="fill-rule:nonzero;"/><path d="M443.057,503.803c0.766,0.809 0.766,0.809 0,0Z" style="fill-rule:nonzero;"/><path d="M457.653,506.586l0.246,0.043l0.101,0.101c-0.087,-0.043 -0.26,-0.116 -0.347,-0.143Z" style="fill-rule:nonzero;"/><path d="M454.569,509.249c0.781,0.708 0.781,0.708 0,0Z" style="fill-rule:nonzero;"/><path d="M460.377,509.077c0.781,0.723 0.781,0.723 0,0Z" style="fill-rule:nonzero;"/><path d="M457.481,511.945c0.737,0.766 0.737,0.766 0,0Z" style="fill-rule:nonzero;"/><path d="M443.201,520.72l0.015,-0.015l0.247,0.043c-0.073,-0.015 -0.189,-0.028 -0.262,-0.028Z" style="fill-rule:nonzero;"/><path d="M420.001,526.948l0.188,-0.145c-0.029,0.086 -0.087,0.246 -0.13,0.317l-0.058,-0.173Z" style="fill-rule:nonzero;"/><path d="M425.477,526.644c0.231,0.058 0.708,0.174 0.94,0.216l-0.679,0.333l-0.26,-0.55Z" style="fill-rule:nonzero;"/><path d="M417.061,529.48c0.781,0.796 0.781,0.796 0,0Z" style="fill-rule:nonzero;"/><path d="M636.898,539.382c0.59,-0.563 1.786,-1.687 2.392,-2.249c-0.938,2.752 0.316,5.217 1.642,7.565c-1.44,-0.663 -2.752,-1.527 -3.717,-2.767c-0.086,-0.634 -0.244,-1.917 -0.316,-2.55Z" style="fill-rule:nonzero;"/><path d="M634.347,540.173c0.274,-0.043 0.821,-0.143 1.096,-0.187c0.244,0.275 0.734,0.808 0.979,1.081c-0.114,0.707 -0.331,2.118 -0.446,2.84c-0.707,0.993 -2.118,3.011 -2.824,4.006c-0.145,3.17 4.048,1.7 5.965,2.594c-0.49,0.576 -1.484,1.729 -1.974,2.306c-1.225,-1.744 -2.882,-2.537 -4.957,-2.379c-0.145,-0.648 -0.433,-1.959 -0.576,-2.607l1.109,-0.332c0.057,-0.634 0.173,-1.902 0.216,-2.522c0.446,-1.614 0.922,-3.213 1.412,-4.799Z" style="fill-rule:nonzero;"/><path d="M440.204,604.558c4.51,-2.024 -0.217,4.9 0,0Z" style="fill-rule:nonzero;"/><path d="M461.529,619.04l0.419,-0.506l0.477,0.796l-0.636,0.143l-0.26,-0.433Z" style="fill-rule:nonzero;"/><path d="M464.571,621.776l0.347,0.304l0.289,0.376l-0.347,-0.304l-0.289,-0.376Z" style="fill-rule:nonzero;"/><path d="M461.487,624.702c0.361,0.418 0.361,0.418 0,0Z" style="fill-rule:nonzero;"/><path d="M467.741,624.976l0.26,0.246l-0.058,0.044c-0.058,-0.073 -0.159,-0.218 -0.202,-0.29Z" style="fill-rule:nonzero;"/><path d="M444.18,630.076c0.853,0.926 0.853,0.926 0,0Z" style="fill-rule:nonzero;"/><path d="M467.236,630.911l0.043,0.057l0.029,0.088c-0.014,-0.044 -0.058,-0.101 -0.072,-0.145Z" style="fill-rule:nonzero;"/><path d="M435.49,633.967c0.072,-0.028 0.231,-0.086 0.303,-0.116l-0.289,0.116l-0.014,0Z" style="fill-rule:nonzero;"/><path d="M441.486,633.087c0.867,0.911 0.867,0.911 0,0Z" style="fill-rule:nonzero;"/><path d="M447.249,633.116c0.925,0.867 0.925,0.867 0,0Z" style="fill-rule:nonzero;"/><path d="M453.259,633.015c0.867,0.926 0.867,0.926 0,0Z" style="fill-rule:nonzero;"/><path d="M432.465,636.317c0.925,0.867 0.925,0.867 0,0Z" style="fill-rule:nonzero;"/><path d="M461.919,635.739c0.462,0.39 0.462,0.39 0,0Z" style="fill-rule:nonzero;"/><path d="M478.981,636.026c0.824,0.766 0.824,0.766 0,0Z" style="fill-rule:nonzero;"/><path d="M459.195,638.997c0.911,0.838 0.911,0.838 0,0Z" style="fill-rule:nonzero;"/><path d="M476.098,639.054c0.824,0.766 0.824,0.766 0,0Z" style="fill-rule:nonzero;"/><path d="M481.878,638.925c0.781,0.809 0.781,0.809 0,0Z" style="fill-rule:nonzero;"/><path d="M499.153,638.997c0.824,0.765 0.824,0.765 0,0Z" style="fill-rule:nonzero;"/><path d="M484.73,641.878c0.809,0.78 0.809,0.78 0,0Z" style="fill-rule:nonzero;"/><path d="M490.508,641.762c0.824,0.766 0.824,0.766 0,0Z" style="fill-rule:nonzero;"/><path d="M496.359,641.733c0.795,0.81 0.795,0.81 0,0Z" style="fill-rule:nonzero;"/><path d="M487.77,644.846c0.81,0.781 0.81,0.781 0,0Z" style="fill-rule:nonzero;"/><path d="M493.519,644.73c0.795,0.81 0.795,0.81 0,0Z" style="fill-rule:nonzero;"/><path d="M499.182,644.716c0.766,0.825 0.766,0.825 0,0Z" style="fill-rule:nonzero;"/><path d="M496.315,647.64c0.838,0.752 0.838,0.752 0,0Z" style="fill-rule:nonzero;"/><path d="M459.052,650.638c0.376,0.376 0.376,0.376 0,0Z" style="fill-rule:nonzero;"/><path d="M522.368,667.872c0.752,0.781 0.752,0.781 0,0Z" style="fill-rule:nonzero;"/><path d="M541.964,668.94c0.983,0.809 0.983,0.809 0,0Z" style="fill-rule:nonzero;"/><path d="M524.73,674.673c3.602,-3.213 8.66,-3.126 13.156,-3.861c0.244,0.461 0.764,1.397 1.008,1.873c3.229,4.279 2.941,9.654 2.017,14.655c0.721,0.461 2.162,1.412 2.867,1.873c0.246,2.292 0.505,4.583 0.749,6.874c-7.334,1.57 -15.778,2.118 -21.484,-3.69c0.259,1.197 0.778,3.589 1.038,4.784c-4.337,-2.983 -6.47,-7.839 -8.487,-12.493c0.879,-3.242 2.089,-6.398 3.919,-9.237c-0.043,2.638 -0.101,5.333 -0.231,7.983c1.225,-3.227 2.45,-6.73 5.447,-8.762Z" style="fill-rule:nonzero;"/><path d="M592.328,684.388c1.397,-4.294 1.829,-9.006 4.711,-12.681c5.562,11.312 8.43,26.04 0.418,36.918c-0.332,0 -0.98,-0.015 -1.311,-0.029c-4.194,-3.444 -8.56,-6.829 -13.95,-8.142c4.626,-4.423 8.215,-9.957 10.131,-16.067Z" style="fill-rule:nonzero;"/><path d="M609.042,675.121l2.867,-0.721c3.978,7.205 7.565,14.857 8.215,23.185c2.767,22.321 -14.9,45.723 -37.638,47.538c-9.669,1.109 -20.16,-6.902 -19.123,-17.133c0.576,-11.168 14.05,-20.318 24.166,-14.078c6.311,3.415 7.58,11.095 5.634,17.478c8.488,-4.74 14.8,-12.782 18.372,-21.715c4.18,-11.255 1.975,-23.747 -2.493,-34.554Z" style="fill-rule:nonzero;"/><path d="M482.669,327.658c0.418,4.092 0.461,8.199 0.663,12.306c5.101,1.801 10.245,3.53 15.159,5.822c-5.144,1.657 -10.346,3.141 -15.519,4.712c0.216,3.055 0.461,6.124 0.735,9.179c0.288,3.17 0.562,6.34 0.98,9.51c-0.821,-2.651 -1.556,-5.303 -2.291,-7.968c-0.533,1.974 -1.081,3.948 -1.614,5.922c-2.277,-3.574 -4.885,-6.902 -7.392,-10.303c-0.476,-0.749 -1.398,-2.262 -1.873,-3.012l-0.331,-0.62c-5.476,1.383 -10.995,2.565 -16.586,3.343c1.873,-2.234 3.847,-4.366 5.85,-6.499c1.801,-1.96 3.574,-3.963 5.346,-5.951c-1.787,-2.896 -3.66,-5.735 -5.303,-8.704c-0.23,-0.62 -0.706,-1.859 -0.937,-2.478c4.51,0.994 8.862,2.579 13.401,3.401c3.646,-2.392 6.556,-5.692 9.712,-8.66Zm-21.6,25.462c3.848,-0.303 7.652,-0.908 11.47,-1.484c2.147,3.819 4.568,7.479 7.464,10.779c0.922,-4.337 1.398,-8.747 1.715,-13.171c4.15,-1.167 8.415,-2.046 12.248,-4.121c-11.369,-5.519 -24.122,0.403 -32.898,7.997Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M649.435,426.192c3.473,-1.47 5.144,5.072 1.369,5.461c-3.214,0.879 -4.799,-4.64 -1.369,-5.461Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M532.441,431.719c0.361,-0.679 1.099,-2.024 1.46,-2.689l1.041,0.347c0.303,2.009 -0.535,2.775 -2.501,2.342Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M483.433,448.974c1.859,-0.98 3.689,-2.032 5.605,-2.896c0.663,1.283 1.282,2.579 1.873,3.919l-1.095,1.081c-1.989,2.536 -4.539,-0.994 -6.384,-2.104Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M485.307,527.608c1.138,0.202 3.415,0.591 4.554,0.778c-0.821,1.586 -1.326,3.848 -3.631,3.113c-0.231,-0.966 -0.692,-2.911 -0.922,-3.891Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M654.883,535.089c1.403,-2.312 4.799,0.376 3.31,2.415c-1.445,2.298 -4.929,-0.332 -3.31,-2.415Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M678.181,535.432c1.599,-0.533 3.602,1.399 2.954,2.941c-0.158,1.93 -3.04,2.651 -4.136,1.311c-1.296,-1.268 -0.894,-4.035 1.182,-4.251Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M671.409,546.283c0.705,-3.804 6.311,-0.936 3.846,1.874c-1.368,1.368 -4.149,0.072 -3.846,-1.874Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M668.009,552.177c1.167,0.922 2.32,1.845 3.502,2.796c-1.182,1.037 -2.364,2.06 -3.545,3.084c-0.029,-1.96 -0.015,-3.921 0.043,-5.88Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M633.381,569.585c2.651,0.402 5.303,0.835 7.969,1.296c-0.98,3.314 -3.66,4.727 -6.974,4.409l-0.303,-1.773c-0.231,-1.311 -0.462,-2.636 -0.692,-3.933Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M653.374,578.52c2.63,-0.044 1.619,3.86 -0.491,3.801c-2.616,0.058 -1.692,-3.961 0.491,-3.801Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M637.143,582.61c1.628,-0.433 3.271,-0.822 4.942,-1.167c0.995,2.608 0.475,4.87 -2.017,6.268c-0.173,-2.146 -1.153,-3.846 -2.925,-5.1Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M635.037,589.296c1.716,0.894 3.805,2.176 2.998,4.481c-0.057,2.796 -3.358,2.061 -5.188,2.695c0.591,-2.436 1.369,-4.813 2.19,-7.176Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M562.47,626.576c4.064,6.974 4.251,15.577 2.695,23.3c-1.643,2.926 -3.486,5.75 -5.634,8.33c-4.986,2.262 -5.878,7.91 -8.371,12.219c1.094,-3.214 2.493,-6.297 3.919,-9.366c-4.741,0.547 -9.324,-0.822 -12.724,-4.208c3.314,0.375 6.541,1.311 9.812,1.93c2.998,0.189 5.346,-2.089 7.622,-3.688c0.98,-1.542 1.931,-3.084 2.796,-4.668c0.505,-1.513 0.951,-3.041 1.355,-4.569c0.303,-1.902 0.534,-3.818 0.707,-5.72c-0.216,-4.582 -1.067,-9.107 -2.177,-13.559Z" style="fill:#b2c7e5;fill-rule:nonzero;"/><path d="M547.643,335.597c9.654,-6.268 22.191,-9.611 33.359,-5.562c9.568,3.127 18.934,9.597 22.378,19.453c4.828,12.335 0.13,27.523 -10.879,34.901c-11.154,8.487 -27.509,6.47 -37.71,-2.579c-3.978,7.551 -4.885,16.571 -2.594,24.799c1.21,4.755 3.631,9.078 5.173,13.733l0.215,0.591c-1.065,-0.677 -3.818,-4.726 -4.336,-1.398c-1.47,2.104 1.743,4.121 1.052,6.355c-1.153,0.375 -2.349,0.562 -3.574,0.548c0.72,-0.836 2.133,-2.478 2.853,-3.3c-1.181,0 -3.516,0.014 -4.698,0.014c0.837,-0.807 2.493,-2.435 3.328,-3.257c-0.993,0.173 -2.968,0.504 -3.947,0.677c0.202,-0.519 0.591,-1.556 0.792,-2.075c-7.218,-11.571 -9.884,-26.067 -5.418,-39.166c0.303,-0.576 0.908,-1.729 1.21,-2.306c1.009,-2.19 2.017,-4.395 3.113,-6.556c0.274,-0.403 0.835,-1.21 1.124,-1.628c2.81,5.894 8.531,9.28 14.438,11.47c0.288,-0.692 0.865,-2.075 1.153,-2.767c-0.143,0.807 -0.417,2.406 -0.547,3.213c8.934,1.556 18.719,-0.893 24.742,-7.954l0.432,2.493c-0.216,0.216 -0.663,0.663 -0.893,0.893c-1.903,1.383 -3.833,2.738 -5.837,3.992c-8.588,4.798 -18.574,2.435 -27.191,-0.908c7.681,8.891 20.909,10.721 31.5,6.484c0.26,-0.533 0.778,-1.614 1.039,-2.147c0.301,-0.202 0.907,-0.62 1.225,-0.821c0.244,0.144 0.764,0.447 1.023,0.591c15.563,-8.156 18.012,-32.667 4.222,-43.662c1.773,4.827 4.15,9.683 3.862,14.972c0.129,5.346 -2.349,10.188 -3.761,15.217c-0.966,0.504 -2.897,1.528 -3.862,2.046c0.303,-0.865 0.923,-2.565 1.226,-3.415c6.873,-8.718 6.109,-22.407 -1.816,-30.217c-5.072,-4.914 -11.903,-8.66 -19.137,-8.286c-13.069,0.36 -25.664,7.248 -33.732,17.407c-1.441,1.225 -3.2,2.536 -3.272,4.611c-0.562,0.504 -1.671,1.528 -2.234,2.046c-1.772,2.478 -3.17,5.187 -4.265,8.026l-0.49,0.994c-1.484,2.565 -2.651,5.332 -3.012,8.314c-0.864,6.528 -2.795,12.954 -1.801,19.612c-4.035,-1.744 -1.441,-7.709 -1.729,-11.211c0.793,-9.366 4.986,-17.883 9.41,-26.01c1.11,-1.917 2.133,-3.905 3.113,-5.908c5.116,-3.876 9.237,-8.963 14.755,-12.32Zm1.052,79.902c0.547,3.574 3.934,0.836 5.779,0.288c-6.959,-11.254 -7.148,-26.399 0.345,-37.437c-1.931,-1.787 -3.861,-3.588 -5.75,-5.404c-8.472,12.652 -7.046,29.439 -0.375,42.552Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M538.003,350.642c8.531,-8.444 18.834,-16.514 31.385,-16.932c12.276,-1.196 25.001,9.611 23.329,22.436c-0.303,9.064 -6.873,15.908 -13.906,20.765c-6.023,0.663 -12.652,1.398 -17.911,-2.306c-5.447,-3.098 -8.041,-9.107 -9.12,-15.015c-1.658,2.161 -3.3,4.337 -4.813,6.6c-0.332,0.648 -1.009,1.96 -1.355,2.608c-1.268,1.945 -2.594,3.847 -3.919,5.749c-1.081,1.138 -1.499,2.479 -1.268,4.02c-2.363,6.715 -3.099,13.848 -2.421,20.923c-2.983,-1.182 -1.685,-4.856 -1.974,-7.277c0.619,-14.367 7.839,-28.258 18.761,-37.523c-3.286,10.404 5.447,21.226 16.067,21.24c2.739,0.475 4.914,-1.196 6.686,-3.069c1.052,0.144 2.118,0.288 3.185,0.447c1.47,-1.297 2.941,-2.579 4.338,-3.963c0.158,-0.173 0.49,-0.519 0.648,-0.692c4.957,-5.548 6.946,-13.905 4.15,-20.909c-1.917,-5.245 -6.673,-8.948 -11.686,-11.023c-6.715,-2.608 -14.223,-0.965 -20.65,1.7c-12.522,5.591 -22.132,16.831 -26.902,29.555c-0.634,1.96 -1.225,3.919 -1.888,5.85l-2.032,1.527c0.331,-1.124 0.98,-3.372 1.297,-4.496c0.547,-1.772 1.009,-3.574 1.455,-5.375c0.346,-0.692 1.052,-2.075 1.398,-2.767c1.153,-2.032 2.248,-4.092 3.257,-6.211c1.21,-0.908 2.335,-2.291 1.902,-3.905c0.505,-0.49 1.485,-1.47 1.988,-1.96Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M414.469,547.522c0.72,-1.628 2.176,-4.482 4.352,-3.978c1.254,2.291 -2.406,4.338 -4.352,3.978Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M422.565,543.431c2.277,-1.325 3.703,2.017 5.115,3.285c-0.346,0.274 -1.037,0.837 -1.383,1.11c-1.816,-0.418 -4.842,-2.162 -3.732,-4.395Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M407.55,574.426c0.951,0.013 2.853,0.028 3.804,0.028c6.845,1.312 11.297,7.163 15.663,12.105c1.412,1.195 0.029,2.608 -0.504,3.848c0.432,1.715 0.879,3.444 1.081,5.23c-0.216,0.404 -0.663,1.211 -0.893,1.599c0.879,1.067 1.744,2.162 2.594,3.271c-3.285,3.559 -4.64,-5.317 -6.859,-0.619c0.893,0.303 2.695,0.922 3.602,1.239c-0.288,0.72 -0.865,2.161 -1.153,2.882c-2.248,-1.283 -4.078,-3.156 -5.98,-4.857c-2.882,-0.865 -3.242,-4.437 -5.822,-5.735c-3.069,-1.555 -4.366,-5.188 -7.205,-6.844c-0.476,0.764 -1.427,2.276 -1.902,3.026c-2.651,-1.456 0.115,-3.126 1.859,-3.617c-0.951,-1.485 -2.147,-2.767 -3.343,-4.035c1.138,-0.533 2.277,-1.052 3.415,-1.557l-1.081,-2.506c0.778,-0.015 2.32,-0.058 3.098,-0.073c-0.086,-0.85 -0.274,-2.535 -0.375,-3.385Zm3.761,6.829c4.236,-2.363 0.389,4.771 0,0Zm-3.026,3.185c4.035,-2.608 0.562,4.784 0,0Zm15.62,8.459c4.784,-0.116 -1.917,4.437 0,0Zm-3.689,2.695c-0.692,1.988 -0.014,2.752 2.017,2.306c0.677,-2.004 0,-2.768 -2.017,-2.306Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M446.934,618.418c4.814,0.348 -2.399,4.193 0,0Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M464.268,647.47c4.25,-2.27 0.246,4.828 0,0Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M450.149,661.619c4.799,0.376 -2.385,4.206 0,0Z" style="fill:#a9a9aa;fill-rule:nonzero;"/><path d="M460.492,335.395c1.643,2.968 3.516,5.807 5.303,8.704c-1.772,1.989 -3.545,3.991 -5.346,5.951c-0.072,-4.885 -0.937,-9.813 0.043,-14.655Z" style="fill:#51667b;fill-rule:nonzero;"/><path d="M461.069,353.121c8.776,-7.594 21.528,-13.516 32.898,-7.997c-3.833,2.075 -8.098,2.954 -12.248,4.121c-0.317,4.424 -0.793,8.833 -1.715,13.171c-2.896,-3.3 -5.317,-6.96 -7.464,-10.779c-3.819,0.576 -7.623,1.182 -11.47,1.484Z" style="fill:#4c71b3;fill-rule:nonzero;"/><path d="M525.207,343.523c2.342,-0.781 3.151,0 2.414,2.371c-2.356,0.781 -3.166,-0.014 -2.414,-2.371Z" style="fill:#2d7eab;fill-rule:nonzero;"/><path d="M482.971,350.496c5.173,-1.571 10.375,-3.055 15.519,-4.712c-0.62,2.911 -1.182,5.822 -1.686,8.747c-4.164,2.176 -8.559,3.876 -13.098,5.144c-0.274,-3.055 -0.519,-6.124 -0.735,-9.179Z" style="fill:#5d83ab;fill-rule:nonzero;"/><path d="M533.895,352.054c0.072,-2.075 1.83,-3.386 3.271,-4.611c0.202,0.793 0.619,2.406 0.835,3.199c-0.505,0.49 -1.485,1.47 -1.988,1.96c-0.534,-0.144 -1.586,-0.418 -2.119,-0.547Z" style="fill:#2a95cd;fill-rule:nonzero;"/><path d="M517.051,353.278c-0.548,-4.798 7.407,-6.038 8.459,-1.513c0.029,0.994 0.101,3.012 0.13,4.006c1.038,-0.475 3.098,-1.455 4.136,-1.945c-4.424,8.127 -8.617,16.643 -9.41,26.01c-1.542,-0.922 -4.15,-1.052 -4.28,-3.357c-0.922,-3.847 4.294,-4.698 4.481,-8.142c-0.014,-4.337 3.833,-7.046 5.303,-10.851c-3.401,0.288 -9.078,0.807 -8.819,-4.208Z" style="fill:#2095d2;fill-rule:nonzero;"/><path d="M513.867,358.395c3.127,-2.594 8.286,1.196 6.643,4.928c-0.735,2.781 -4.51,3.833 -6.7,2.089c-2.378,-1.556 -2.306,-5.49 0.058,-7.018Z" style="fill:#2095d2;fill-rule:nonzero;"/><path d="M534.992,360.901c1.758,-3.732 8.501,-1.902 8.084,2.306c0.604,4.063 -3.921,4.943 -6.961,4.914c-0.865,-2.334 -3.53,-4.928 -1.124,-7.219Z" style="fill:#2095d2;fill-rule:nonzero;"/><path d="M530.625,367.976c4.136,-2.133 9.713,2.219 6.787,6.585c-2.132,3.473 -6.47,1.643 -8.674,-0.735c0.663,-1.931 1.254,-3.891 1.888,-5.85Z" style="fill:#2095d2;fill-rule:nonzero;"/><path d="M614.289,349.287c2.356,-0.636 3.137,0.217 2.327,2.53c-2.385,0.651 -3.165,-0.188 -2.327,-2.53Z" style="fill:#266f96;fill-rule:nonzero;"/><path d="M498.839,350.815c4.785,0.246 -2.226,4.322 0,0Z" style="fill:#27495e;fill-rule:nonzero;"/><path d="M542.772,352.817c2.594,-1.499 4.611,3.473 1.599,4.064c-2.407,1.297 -4.38,-3.257 -1.599,-4.064Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M588.868,372.804c0.317,-1.859 1.312,-3.271 2.983,-4.265c-0.301,0.85 -0.922,2.551 -1.225,3.415c0.966,-0.519 2.897,-1.542 3.862,-2.046c0.057,3.242 -2.608,4.424 -5.188,5.389l-0.433,-2.493Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M525.842,383.626c-3.026,-0.375 -2.839,-6.874 0.461,-5.663c0.403,1.888 0.144,3.833 -0.461,5.663Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M582.572,380.183c2.003,-1.254 3.934,-2.608 5.836,-3.991c0.837,2.133 1.081,4.323 0.736,6.6c-0.317,0.202 -0.923,0.62 -1.226,0.821c-2.434,0.029 -4.063,-1.542 -5.346,-3.43Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M623.452,420.745l0.85,-1.412c0.317,2.046 0.663,4.107 0.923,6.167c0.562,6.167 0.446,12.364 0.475,18.545c-0.029,6.917 -0.822,13.79 -2.017,20.592c-1.268,7.896 -3.473,15.591 -6.383,23.026l-1.053,-0.806c1.889,-10.174 4.209,-20.274 5.188,-30.606c0.418,-1.182 0.808,-2.378 1.182,-3.574c2.003,-8.487 1.672,-17.522 -0.044,-26.024c-0.028,-0.245 -0.072,-0.735 -0.1,-0.98c0.23,-1.657 0.547,-3.314 0.979,-4.928Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M494.371,479.306l2.075,-1.037c-1.801,4.395 -2.032,9.179 -1.354,13.833c0.504,-3.299 0.864,-6.614 1.398,-9.913c2.19,6.368 4.784,12.623 8.329,18.372c-0.865,0.116 -2.594,0.375 -3.458,0.505c-4.496,-1.628 -7.176,-5.922 -8.675,-10.245c-0.014,-1.917 0.029,-3.833 0.101,-5.75c0.375,-1.946 0.562,-4.02 1.585,-5.764Zm2.017,14.179c0.735,0.749 0.735,0.749 0,0Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M471.617,521.154c2.219,-0.865 4.467,-1.744 6.657,-2.709c-0.922,1.786 -1.845,3.587 -2.795,5.375c-1.225,0.663 -2.45,1.325 -3.675,1.974c-5.159,2.306 -9.971,5.333 -14.093,9.193l-0.548,0.086c-2.176,0.202 -4.366,0.361 -6.542,0.49c1.239,-1.542 2.478,-3.084 3.804,-4.553c2.637,-2.003 5.447,-3.747 8.314,-5.404c2.882,-1.628 5.822,-3.156 8.876,-4.452Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M685.427,541.269c2.19,-2.825 5.908,1.34 3.574,3.299c-1.715,2.638 -6.111,-1.21 -3.574,-3.299Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M669.868,566.011c1.656,0.116 3.285,0.231 4.942,0.375c0,1.182 -0.029,3.53 -0.043,4.712c-1.542,0.044 -3.084,0.259 -4.597,-0.015c-1.428,-1.34 -0.317,-3.429 -0.303,-5.072Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M696.451,582.508c1.484,-2.017 4.927,0.389 3.343,2.421c-1.441,2.234 -5.188,-0.331 -3.343,-2.421Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M642.143,596.877c2.356,-0.752 3.15,0.058 2.371,2.429c-2.343,0.693 -3.137,-0.116 -2.371,-2.429Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M637.226,601.53c3.805,0.361 6.788,3.271 5.692,7.35c-2.508,-1.917 -4.309,-4.554 -5.692,-7.35Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M624.681,608.618c1.936,0.926 1.936,1.879 0.015,2.891c-1.894,-0.939 -1.909,-1.894 -0.015,-2.891Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M646.004,609.63c2.089,-1.889 4.927,1.584 3.299,3.573c-1.843,2.133 -4.784,-1.786 -3.299,-3.573Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M454.598,356.549c5.591,-0.778 11.11,-1.96 16.586,-3.343l0.331,0.62c-0.115,3.487 -0.36,6.974 -0.605,10.447c-4.078,4.871 -7.752,10.159 -12.926,13.963c-1.599,-7.147 -2.536,-14.424 -3.386,-21.687Z" style="fill:#6497ca;fill-rule:nonzero;"/><path d="M527.397,362.126c1.095,-2.839 2.493,-5.548 4.265,-8.026c0.605,0.591 1.83,1.801 2.45,2.406c-1.009,2.118 -2.104,4.179 -3.257,6.211c-0.865,-0.144 -2.594,-0.447 -3.458,-0.591Z" style="fill:#6497ca;fill-rule:nonzero;"/><path d="M523.895,371.435c0.36,-2.983 1.527,-5.75 3.012,-8.314c0.649,0.591 1.917,1.772 2.551,2.363c-0.447,1.801 -0.908,3.602 -1.455,5.375c-1.023,0.144 -3.084,0.432 -4.107,0.576Z" style="fill:#6497ca;fill-rule:nonzero;"/><path d="M541.691,374.547c0.778,0.62 2.364,1.859 3.156,2.478c-0.303,0.576 -0.907,1.729 -1.21,2.306c-0.808,-0.187 -2.421,-0.576 -3.214,-0.764c-0.23,-1.542 0.189,-2.882 1.268,-4.02Z" style="fill:#6497ca;fill-rule:nonzero;"/><path d="M694.768,546.673c2.284,-0.794 3.093,-0.015 2.429,2.356c-2.328,0.823 -3.152,0.044 -2.429,-2.356Z" style="fill:#6497ca;fill-rule:nonzero;"/><path d="M540.999,654.011c6.282,-0.317 12.536,0.562 18.791,1.081c-2.276,1.599 -4.626,3.876 -7.624,3.688c-3.27,-0.619 -6.499,-1.557 -9.812,-1.931l-0.274,-0.215c-0.274,-0.649 -0.808,-1.96 -1.081,-2.623Z" style="fill:#6497ca;fill-rule:nonzero;"/><path d="M471.516,353.827c0.476,0.749 1.398,2.262 1.873,3.012c-0.634,6.6 -0.072,13.243 1.7,19.64c-3.919,5.548 -8.329,10.822 -13.617,15.102c-1.297,-4.409 -2.507,-8.848 -3.487,-13.343c5.173,-3.804 8.848,-9.093 12.926,-13.963c0.245,-3.473 0.49,-6.96 0.605,-10.447Z" style="fill:#608bb6;fill-rule:nonzero;"/><path d="M483.707,359.675c4.539,-1.268 8.934,-2.968 13.098,-5.144c-0.331,4.899 -0.418,9.799 -0.389,14.712c-3.386,1.398 -6.801,2.738 -10.231,4.006c-0.245,-0.62 -0.749,-1.873 -0.994,-2.507c-0.13,-0.389 -0.375,-1.167 -0.504,-1.556c-0.418,-3.17 -0.692,-6.34 -0.98,-9.51Z" style="fill:#597a9c;fill-rule:nonzero;"/><path d="M650.124,437.722c3.041,0.922 6.313,1.628 8.877,3.646c1.067,2.536 -0.231,5.461 -0.288,8.156c-0.649,2.334 -1.355,4.654 -2.061,6.989c-2.032,-2.435 -4.927,-3.588 -7.91,-4.337c1.124,-4.741 1.239,-9.611 1.383,-14.453Z" style="fill:#597a9c;fill-rule:nonzero;"/><path d="M473.388,356.838c2.507,3.401 5.115,6.729 7.392,10.303c1.398,5.058 2.147,10.26 3.113,15.418c-2.262,0.908 -4.366,2.147 -6.369,3.516c-0.908,-3.17 -1.7,-6.384 -2.435,-9.597c-1.772,-6.398 -2.334,-13.041 -1.7,-19.64Z" style="fill:#53708c;fill-rule:nonzero;"/><path d="M443.272,421.768c2.795,-1.989 5.605,-3.977 8.415,-5.951c2.723,4.842 5.591,9.64 8.949,14.078c-1.902,2.334 -3.804,4.669 -5.706,7.003c-4.481,-4.568 -7.666,-10.159 -11.657,-15.13Z" style="fill:#53708c;fill-rule:nonzero;"/><path d="M482.396,361.219c0.735,2.666 1.47,5.317 2.291,7.969c0.13,0.389 0.375,1.167 0.504,1.556c-1.196,1.556 -2.133,8.026 1.254,5.778c0.014,0.461 0.058,1.369 0.072,1.816c-0.288,7.219 2.363,14.021 4.467,20.779c0.879,2.623 1.744,5.389 3.775,7.392c0.403,0.475 1.239,1.426 1.657,1.902c-0.317,0.994 -0.98,2.997 -1.297,3.991c-0.202,0.432 -0.62,1.297 -0.821,1.729c-4.64,-10.087 -8.242,-20.678 -10.404,-31.572c-0.966,-5.159 -1.715,-10.361 -3.113,-15.418c0.533,-1.974 1.081,-3.948 1.614,-5.922Z" style="fill:#7199c2;fill-rule:nonzero;"/><path d="M581.461,364c0.908,-0.115 2.709,-0.346 3.617,-0.461c0.159,1.283 0.475,3.833 0.635,5.115c-0.159,0.173 -0.49,0.519 -0.648,0.692c-3.545,1.124 -6.399,-2.695 -3.603,-5.346Z" style="fill:#227cad;fill-rule:nonzero;"/><path d="M545.611,368.798c0.345,-0.649 1.023,-1.96 1.355,-2.608c0.533,0.663 1.584,1.989 2.117,2.651c-0.288,0.418 -0.85,1.225 -1.124,1.628c-0.591,-0.418 -1.757,-1.254 -2.348,-1.671Z" style="fill:#226f99;fill-rule:nonzero;"/><path d="M486.184,373.252c3.43,-1.268 6.845,-2.608 10.231,-4.006c0.36,5.361 1.066,10.678 1.859,15.981c-2.925,1.888 -5.865,3.746 -8.819,5.62c-1.167,-4.121 -2.089,-8.314 -2.94,-12.508c-0.014,-0.447 -0.058,-1.355 -0.072,-1.816c-0.072,-0.821 -0.202,-2.45 -0.259,-3.271Z" style="fill:#526d88;fill-rule:nonzero;"/><path d="M509.485,369.432c1.383,-2.104 5.332,0.072 3.502,2.262c-1.427,1.917 -5.173,-0.144 -3.502,-2.262Z" style="fill:#23678c;fill-rule:nonzero;"/><path d="M486.443,376.522c-3.386,2.248 -2.45,-4.222 -1.254,-5.778c0.245,0.634 0.749,1.888 0.994,2.507c0.058,0.821 0.187,2.45 0.259,3.271Z" style="fill:#eef1f2;fill-rule:nonzero;"/><path d="M622.574,426.653c1.715,8.502 2.045,17.537 0.043,26.024c0.404,-5.461 0.448,-10.923 0.418,-16.37c-0.086,-3.228 -0.231,-6.456 -0.461,-9.655Z" style="fill:#eef1f2;fill-rule:nonzero;"/><path d="M503.852,471.063c9.266,-4.942 19.799,-7.42 30.318,-6.469c6.052,0.734 12.047,1.931 17.94,3.516c-4.567,-0.663 -9.122,-1.484 -13.689,-2.146c-3.905,-0.591 -7.853,-0.75 -11.787,-0.505c-3.141,0.303 -6.268,0.764 -9.352,1.412c-5.548,1.557 -10.663,4.395 -15.188,7.925c-2.248,2.148 -4.121,4.67 -5.605,7.392c-0.533,3.3 -0.893,6.614 -1.398,9.915c-0.677,-4.655 -0.447,-9.439 1.354,-13.834c2.608,-2.247 4.971,-4.769 7.407,-7.205Z" style="fill:#eef1f2;fill-rule:nonzero;"/><path d="M550.395,482.894c2.695,-0.475 5.39,-0.907 8.112,-1.21c7.104,0.259 14.295,0.634 21.155,2.607c4.799,1.586 9.856,3.17 14.842,1.312c5.576,3.242 10.923,6.902 15.562,11.398c-0.375,0.806 -1.138,2.434 -1.513,3.242c4.237,5.951 8.876,11.743 11.672,18.56c2.017,4.929 3.286,10.116 4.885,15.189c0.274,0.993 0.835,2.968 1.11,3.962c-3.559,0.721 -7.191,0.936 -10.779,0.332c-1.729,-11.932 -7.58,-23.402 -16.701,-31.342c1.658,-0.634 3.3,-1.254 4.986,-1.83c0.448,-0.966 0.879,-1.946 1.327,-2.911c-1.802,-1.08 -3.387,-3.602 -5.75,-2.392c-0.549,2.292 -0.677,4.655 -0.808,7.003c-13.948,-13.992 -36.01,-17.998 -54.542,-11.931c1.542,-1.946 2.205,-4.121 2.004,-6.57c-0.534,-0.275 -1.586,-0.837 -2.104,-1.11c-0.202,-0.72 -0.576,-2.133 -0.765,-2.838c2.421,-0.505 4.857,-1.024 7.306,-1.47Z" style="fill:#eef1f2;fill-rule:nonzero;"/><path d="M549.501,533.068c4.309,-3.112 9.338,-5.202 14.728,-5.202c5.922,1.24 11.167,4.771 14.308,9.972c1.34,3.862 1.96,7.954 1.268,12.018c-0.259,1.182 -0.533,2.377 -0.793,3.587l-0.086,-1.181c-0.158,-3.055 -0.244,-6.096 -0.49,-9.136c-1.873,-4.107 -4.351,-8.199 -8.386,-10.475c-4.136,-2.682 -9.223,-2.594 -13.935,-3.084l0.029,0.85c-1.658,0.648 -3.286,1.296 -4.914,2.003l-1.729,0.648Z" style="fill:#eef1f2;fill-rule:nonzero;"/><path d="M657.747,375.225c0.781,0.752 0.781,0.752 0,0Z" style="fill:#265570;fill-rule:nonzero;"/><path d="M475.09,376.478c0.735,3.213 1.528,6.427 2.435,9.597c1.066,3.588 2.205,7.147 3.357,10.706c-1.326,-0.231 -2.637,-0.49 -3.977,-0.605c-3.415,3.473 -6.182,7.536 -9.669,10.937c-2.104,-5.101 -4.107,-10.26 -5.764,-15.534c5.288,-4.28 9.698,-9.554 13.617,-15.101Z" style="fill:#5b7ea2;fill-rule:nonzero;"/><path d="M526.086,375.902l1.96,-1.11c-0.331,3.559 -0.692,7.133 -0.879,10.707c-0.115,2.32 -0.101,4.654 -0.187,6.989c-2.003,-2.493 -1.888,-5.937 -1.138,-8.862c0.605,-1.83 0.865,-3.775 0.461,-5.663l-0.216,-2.06Z" style="fill:#b1b1b2;fill-rule:nonzero;"/><path d="M514.112,530.232c7.176,-20.808 29.136,-35.463 51.154,-33.517c15.418,0.979 30.059,9.682 38.547,22.551c8.156,11.859 10.302,27.277 6.743,41.126c-4.61,14.741 -15.288,27.825 -29.928,33.387c-3.53,1.571 -7.752,1.7 -10.866,4.05c4.309,1.296 8.617,-0.879 12.595,-2.349c7.608,-2.982 13.847,-8.371 20.26,-13.3c3.128,2.089 6.471,3.833 9.957,5.259c-3.472,4.382 -7.767,8.013 -12.061,11.558c-3.04,-1.571 -5.937,-3.761 -9.351,-4.382c-3.978,1.024 -7.565,3.128 -11.398,4.51c-3.89,1.557 -8.041,2.379 -12.191,2.796c-1.599,-0.562 -3.905,-0.216 -4.942,-1.7c0.764,-1.801 2.464,-2.262 5.115,-1.426l-0.013,-0.246c-1.514,-1.397 -8.316,-0.013 -5.836,-3.558c10.433,-1.268 20.952,-4.281 29.222,-11.053c6.557,-5.346 11.629,-12.435 14.627,-20.333c3.184,-8.775 2.146,-18.227 1.772,-27.349c-3.286,-11.211 -9.943,-21.759 -19.957,-28.07c-16.413,-11.095 -39.584,-9.799 -55.176,2.219c-12.594,9.684 -19.208,26.139 -17.076,41.861l2.161,-0.073l1.182,-0.072c1.081,-0.072 3.242,-0.244 4.323,-0.331c3.948,0.835 7.825,2.045 11.657,3.314c3.012,2.897 6.311,5.706 8.373,9.41c0.936,2.075 -1.037,3.804 -1.788,5.576c-2.031,-1.656 -4.135,-3.213 -6.326,-4.61c1.226,-1.226 2.436,-2.45 3.66,-3.66c-7.766,-8.689 -20.534,-8.127 -31.168,-7.45c0.389,-0.145 1.167,-0.433 1.556,-0.591c0.562,-0.216 1.671,-0.677 2.219,-0.908c-0.187,-7.637 0.115,-15.446 2.954,-22.637Z" style="fill:#b1b1b2;fill-rule:nonzero;"/><path d="M438.635,661.678c4.799,0.333 -2.269,4.265 0,0Z" style="fill:#b1b1b2;fill-rule:nonzero;"/><path d="M528.046,374.792c2.464,1.686 6.081,3.602 4.813,7.248c-0.375,2.795 -3.689,2.695 -5.692,3.458c0.187,-3.574 0.548,-7.147 0.879,-10.707Z" style="fill:#2896d1;fill-rule:nonzero;"/><path d="M490.984,399.117c-2.104,-6.758 -4.755,-13.56 -4.467,-20.779c0.85,4.193 1.772,8.387 2.94,12.508c1.571,5.274 3.199,10.562 5.303,15.663c-2.032,-2.003 -2.896,-4.77 -3.775,-7.392Z" style="fill:#eff1f3;fill-rule:nonzero;"/><path d="M497.627,558.576c2.334,-1.283 4.755,-2.392 7.248,-3.33c0.62,-0.23 1.873,-0.663 2.507,-0.878c10.634,-0.677 23.401,-1.24 31.168,7.45c-1.225,1.21 -2.434,2.434 -3.66,3.66c-17.968,-10.794 -43.964,1.037 -47.509,21.715c-1.816,0.417 -3.602,1.469 -5.505,1.081c-0.231,-10.88 7.234,-19.352 13.012,-27.769c0.893,-0.677 1.801,-1.325 2.738,-1.93Z" style="fill:#eff1f3;fill-rule:nonzero;"/><path d="M647.098,566.731c0.49,-0.547 1.47,-1.628 1.96,-2.176c0.619,0.057 1.858,0.173 2.478,0.231c0.375,0.316 1.109,0.951 1.484,1.282c0.347,0.389 1.039,1.197 1.384,1.599l-1.369,-0.878c-0.893,1.166 -1.801,2.334 -2.695,3.501c1.167,-2.133 -1.138,-4.237 -3.242,-3.559Z" style="fill:#eff1f3;fill-rule:nonzero;"/><path d="M477.525,386.075c2.003,-1.369 4.107,-2.608 6.369,-3.516c2.162,10.894 5.764,21.485 10.404,31.572c0.202,-0.432 0.62,-1.297 0.821,-1.729l1.556,1.499l0.836,1.355c-4.193,-1.254 -3.458,3.473 -3.141,5.706c1.96,9.136 6.182,17.695 11.153,25.577c-1.729,-0.231 -3.444,-0.447 -5.159,-0.663c-1.83,2.248 -3.646,4.51 -5.418,6.801c-0.461,-0.576 -1.412,-1.744 -1.873,-2.32c-0.101,-0.504 -0.288,-1.499 -0.375,-2.003c-0.447,0.403 -1.34,1.225 -1.787,1.643c-0.591,-1.34 -1.21,-2.637 -1.873,-3.919c-1.254,-2.479 -2.723,-4.842 -4.251,-7.147c2.94,-2.262 5.764,-4.654 8.487,-7.176c0.721,0.115 2.147,0.36 2.867,0.49c-3.386,-6.888 -6.744,-13.79 -10.015,-20.736c-2.075,-4.784 -3.66,-9.756 -5.245,-14.727c-1.153,-3.559 -2.291,-7.118 -3.357,-10.706Z" style="fill:#435668;fill-rule:nonzero;"/><path d="M630.326,382.501c0.823,0.636 0.823,0.636 0,0Z" style="fill:#243846;fill-rule:nonzero;"/><path d="M498.275,385.225l0.216,-0.13c0.807,4.726 2.637,9.251 3.069,14.035c-1.484,3.213 -3.545,6.124 -5.144,9.28c-0.418,-0.476 -1.254,-1.427 -1.657,-1.902c-2.104,-5.101 -3.732,-10.389 -5.303,-15.663c2.954,-1.873 5.894,-3.732 8.819,-5.62Z" style="fill:#496074;fill-rule:nonzero;"/><path d="M614.289,388.409c3.314,1.657 6.614,3.386 9.885,5.159c3.371,-2.032 6.728,-4.092 10.201,-5.951c0.044,3.602 -0.634,7.162 -1.067,10.721c3.963,1.542 6.456,5.418 10.245,7.392c-4.813,0.576 -9.625,1.009 -14.438,1.383c-1.239,4.208 -2.349,8.531 -4.813,12.22l-0.85,1.412c-0.432,1.614 -0.749,3.271 -0.98,4.928c-0.215,-2.017 -0.417,-4.02 -0.604,-6.023c-0.347,-4.409 -1.312,-8.718 -2.493,-12.954c-4.352,-0.418 -8.733,-0.36 -13.056,0.331l-0.793,0.101c3.214,-3.43 7.277,-5.85 11.125,-8.473c-0.778,-3.429 -1.584,-6.83 -2.363,-10.245Zm-4.136,16.528c3.271,0.591 7.161,-0.418 10.115,1.282c1.557,3.127 2.349,6.657 4.51,9.496c1.197,-3.53 2.392,-7.075 3.717,-10.562c3.128,-0.101 6.269,-0.245 9.395,-0.49c-8.228,-4.409 -20.432,-7.075 -27.738,0.274Z" style="fill:#b0c6e5;fill-rule:nonzero;"/><path d="M503.089,400.788c4.971,-6.499 13.416,-9.366 21.413,-8.228l-0.086,0.173c-6.067,13.79 -13.055,27.263 -17.854,41.572c-5.418,-8.257 -8.415,-19.511 -3.113,-28.56c2.867,-5.663 8.776,-8.415 14.323,-10.793c-7.868,0.49 -13.747,6.398 -17.393,12.897c-1.383,-2.795 1.34,-4.986 2.709,-7.061Z" style="fill:#f8cb25;fill-rule:nonzero;"/><path d="M499.572,406.061l0.807,1.787c-0.864,4.381 -1.974,8.862 -1.153,13.343c0.793,6.182 4.337,11.441 7.176,16.845c-3.718,-1.585 -5.115,-5.692 -6.816,-9.021c-2.089,-4.28 -2.723,-9.064 -2.075,-13.761l-0.836,-1.355c0.908,-2.637 2.075,-5.173 2.896,-7.839Z" style="fill:#f8cb25;fill-rule:nonzero;"/><path d="M506.56,434.305c4.798,-14.309 11.787,-27.782 17.854,-41.572c11.917,2.652 19.741,17.349 13.474,28.315c-3.055,6.355 -10.534,8.199 -17.018,7.522c0.937,1.153 1.873,2.32 2.81,3.502c-2.219,4.294 -4.366,8.617 -6.47,12.983c-4.035,-3.069 -7.623,-6.686 -10.649,-10.75Z" style="fill:#f8ba25;fill-rule:nonzero;"/><path d="M506.401,438.038c5.029,4.453 10.274,8.646 15.692,12.652c-3.775,0.072 -6.614,-2.723 -9.453,-4.784c-2.507,-2.205 -5.793,-4.251 -6.239,-7.868Z" style="fill:#f8ba25;fill-rule:nonzero;"/><path d="M476.905,396.177c1.34,0.115 2.651,0.375 3.977,0.605c1.585,4.971 3.17,9.943 5.245,14.727c-4.553,3.444 -7.003,8.617 -10.13,13.228c-3.156,-5.749 -6.211,-11.571 -8.761,-17.623c3.487,-3.401 6.254,-7.464 9.669,-10.937Z" style="fill:#54728f;fill-rule:nonzero;"/><path d="M494.368,479.306c1.83,-4.02 5.361,-6.816 9.482,-8.242c-2.435,2.434 -4.798,4.957 -7.407,7.205l-2.075,1.037Z" style="fill:#54728f;fill-rule:nonzero;"/><path d="M559.532,658.206c2.146,-2.579 3.991,-5.404 5.634,-8.329c-1.195,3.703 -2.752,7.292 -4.077,10.951c-0.505,-0.878 -1.024,-1.757 -1.557,-2.622Z" style="fill:#54728f;fill-rule:nonzero;"/><path d="M500.379,407.848c3.646,-6.499 9.525,-12.407 17.393,-12.897c-5.548,2.378 -11.456,5.13 -14.323,10.793c-5.303,9.049 -2.306,20.303 3.113,28.56c3.026,4.063 6.614,7.68 10.649,10.75c5.259,3.862 11.023,6.989 17.018,9.539c5.634,2.392 11.441,4.352 17.249,6.254c5.994,1.974 11.844,4.395 17.522,7.176c5.605,2.781 11.052,5.937 16.153,9.583c2.464,1.988 4.683,4.25 7.018,6.412c-0.361,-0.246 -1.081,-0.721 -1.426,-0.966c-9.597,-8.041 -21.155,-13.185 -32.711,-17.724c-2.003,-0.721 -3.976,-1.485 -5.966,-2.219c-3.011,-1.023 -6.038,-2.133 -9.221,-2.421l0.518,-0.244c-6.44,-4.366 -14.309,-6.254 -21.268,-9.756c-5.418,-4.006 -10.663,-8.199 -15.692,-12.652c-2.839,-5.404 -6.383,-10.663 -7.176,-16.845c-0.821,-4.481 0.288,-8.963 1.153,-13.343Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M571.274,419.938c0.636,0.824 0.636,0.824 0,0Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M615.528,458.305c-1.894,-2.833 3.15,-3.18 3.57,-0.578l-0.882,0.882c-0.679,-0.072 -2.024,-0.231 -2.688,-0.303Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M618.955,488.401c0.576,-0.015 1.744,-0.044 2.32,-0.058c0.475,0.446 1.399,1.369 1.859,1.816c-0.677,0.547 -2.032,1.628 -2.709,2.162c-0.086,1.599 -0.244,3.198 -0.461,4.799c-5.072,-0.966 -0.433,-5.549 -1.009,-8.718Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M413.906,541.11l1.388,-0.231l0.81,1.012c-0.506,0.274 -1.547,0.823 -2.067,1.098c-0.029,-0.462 -0.101,-1.403 -0.13,-1.879Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M627.158,578.158c0.028,0.202 0.072,0.592 0.101,0.794l-0.101,-0.794Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M403.17,594.858c1.614,-2.133 2.176,-5.995 5.778,-5.072c-1.369,2.262 -3.473,3.862 -5.778,5.072Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M406.197,597.713c1.153,-2.061 2.234,-4.15 3.213,-6.283c2.046,2.537 4.049,5.303 6.859,7.075c0.058,0.591 0.187,1.773 0.245,2.364c-0.447,0.028 -1.326,0.101 -1.772,0.129c-0.331,-0.893 -1.009,-2.68 -1.355,-3.558c-1.297,1.138 -2.608,2.276 -3.934,3.4c0.692,-1.931 1.47,-3.862 1.859,-5.893c-1.772,0.792 -3.444,1.801 -5.115,2.767Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M582.731,642.672c4.755,10.864 8.472,22.58 7.031,34.598c-2.708,7.319 -5.403,15.274 -11.844,20.23c-7.479,6.341 -19.828,5.173 -25.937,-2.535c4.035,2.363 8.213,5.115 13.127,4.841c7.551,0.332 13.876,-4.913 17.724,-10.98c1.584,-2.781 2.925,-5.72 4.006,-8.733c0.894,-3.257 1.412,-6.614 1.772,-9.972c0.159,-4.437 -0.288,-8.876 -1.498,-13.142c-1.441,-4.784 -2.939,-9.539 -4.38,-14.308Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M554.011,395.457c0.752,0.766 0.752,0.766 0,0Z" style="fill:#495663;fill-rule:nonzero;"/><path d="M428.978,396.091c2.032,2.478 3.617,5.288 5.043,8.156c4.064,-0.36 8.142,-0.793 12.205,-1.239c-2.075,3.415 -5.015,6.167 -8.084,8.689c1.124,3.17 2.565,6.211 4.366,9.064c-0.13,1.023 -0.375,3.084 -0.504,4.121c-3.084,-1.816 -6.268,-3.43 -9.467,-5.015c-0.389,-0.202 -1.196,-0.591 -1.585,-0.793c-3.502,2.262 -6.816,4.827 -10.274,7.147c-0.029,-0.62 -0.086,-1.844 -0.115,-2.464c0.231,-1.585 0.533,-3.141 0.908,-4.683c0.36,0.994 1.081,2.969 1.441,3.963c2.45,-1.96 4.885,-3.934 7.32,-5.922c3.343,1.672 6.715,3.3 10.101,4.914c-1.787,-4.237 -3.271,-8.588 -4.265,-13.07c-5.259,1.931 -10.072,4.928 -13.905,9.035c0.303,-1.066 0.893,-3.199 1.196,-4.265c-2.594,-1.556 -5.159,-3.141 -7.68,-4.813l-0.937,-0.62c3.386,-1.023 6.888,-1.528 10.317,-2.421c2.133,-2.839 2.623,-6.542 3.919,-9.784Z" style="fill:#b1c6e5;fill-rule:nonzero;"/><path d="M582.962,399.966c3.055,-2.349 6.671,1.427 4.582,4.496c-3.099,2.435 -7.335,-1.34 -4.582,-4.496Z" style="fill:#83b7e3;fill-rule:nonzero;"/><path d="M610.153,404.937c7.306,-7.349 19.51,-4.683 27.738,-0.274c-3.126,0.245 -6.268,0.389 -9.395,0.49c-1.325,3.487 -2.521,7.032 -3.717,10.562c-2.161,-2.839 -2.954,-6.369 -4.51,-9.496c-2.954,-1.7 -6.844,-0.692 -10.115,-1.282Z" style="fill:#4c70b2;fill-rule:nonzero;"/><path d="M422.061,478.284c1.989,-2.882 4.337,-5.533 7.536,-7.09c1.729,2.796 3.718,5.418 5.706,8.041c-2.666,-0.072 -5.332,-0.145 -7.997,-0.216c-1.182,2.306 -2.637,4.453 -4.496,6.282c-0.259,-2.348 -0.504,-4.683 -0.749,-7.017Z" style="fill:#4c70b2;fill-rule:nonzero;"/><path d="M446.716,402.285c1.412,4.597 3.055,9.121 4.971,13.531c-2.81,1.974 -5.62,3.963 -8.415,5.951l-0.764,-1.009c-1.801,-2.853 -3.242,-5.894 -4.366,-9.064c3.069,-2.522 6.009,-5.274 8.084,-8.689l0.49,-0.721Z" style="fill:#5a7c9f;fill-rule:nonzero;"/><path d="M643.554,405.729l0.174,-0.014c0.49,5.505 2.132,11.038 1.355,16.571c-0.49,0.375 -1.47,1.11 -1.96,1.47c-3.833,-2.306 -7.782,-4.38 -11.859,-6.211c-0.519,-3.516 -1.384,-6.974 -2.148,-10.433c4.813,-0.375 9.626,-0.807 14.438,-1.383Z" style="fill:#6496c9;fill-rule:nonzero;"/><path d="M442.51,420.758l0.764,1.009c3.991,4.971 7.176,10.562 11.657,15.13c4.251,4.424 8.531,8.862 13.286,12.753c3.747,3.041 7.565,6.009 11.571,8.704c6.297,4.179 12.508,8.53 19.208,12.047c-3.098,1.599 -5.98,-0.764 -8.603,-2.177c-8.56,-5.245 -17.162,-10.576 -24.626,-17.32c-8.949,-7.709 -16.326,-16.917 -23.762,-26.024c0.13,-1.038 0.375,-3.098 0.504,-4.121Z" style="fill:#6496c9;fill-rule:nonzero;"/><path d="M656.653,456.51c0.707,-2.334 1.412,-4.654 2.06,-6.989c0.116,1.297 0.361,3.876 0.49,5.173c-2.104,9.222 -6.095,17.868 -10.361,26.254c-6.412,11.384 -14.193,22.205 -23.689,31.211c5.101,-9.395 12.911,-16.974 18.2,-26.297c2.91,-4.626 5.403,-9.482 7.738,-14.396c2.205,-4.856 3.905,-9.899 5.562,-14.957Z" style="fill:#6496c9;fill-rule:nonzero;"/><path d="M606.319,407.027c4.323,-0.692 8.703,-0.749 13.056,-0.332c1.181,4.237 2.146,8.545 2.493,12.954c-3.559,0 -7.104,-0.014 -10.649,-0.029c-1.441,-4.265 -3.141,-8.444 -4.9,-12.594Z" style="fill:#5c80a6;fill-rule:nonzero;"/><path d="M562.7,408.467c0.708,0.766 0.708,0.766 0,0Z" style="fill:#333335;fill-rule:nonzero;"/><path d="M579.834,413.887c4.293,-2.313 0.303,4.77 0,0Z" style="fill:#333335;fill-rule:nonzero;"/><path d="M624.302,419.333c2.464,-3.689 3.574,-8.012 4.813,-12.219c0.764,3.458 1.628,6.917 2.146,10.433c0.086,2.608 0.073,5.245 0.073,7.882c-2.047,0.014 -4.079,0.043 -6.111,0.072c-0.259,-2.061 -0.604,-4.121 -0.922,-6.167Z" style="fill:#557391;fill-rule:nonzero;"/><path d="M441.199,488.919c4.035,2.19 8.17,4.221 12.436,5.937c0.115,4.12 -2.493,7.349 -6.845,7.262c-3.199,-0.216 -2.306,3.631 0.115,3.934c-3.833,2.276 -6.729,-1.096 -9.453,-3.358c0.821,-2.493 1.052,-5.072 -2.118,-5.85l0.13,-0.173c2.046,-2.48 3.891,-5.116 5.735,-7.752Zm-1.052,11.988c0.793,0.764 0.793,0.764 0,0Zm2.911,2.897c0.764,0.808 0.764,0.808 0,0Z" style="fill:#557391;fill-rule:nonzero;"/><path d="M415.678,408.915c2.522,1.671 5.087,3.257 7.68,4.813c-0.303,1.066 -0.893,3.199 -1.196,4.265l-0.721,0.994c-2.94,-2.695 -4.712,-6.283 -5.764,-10.072Z" style="fill:#3f4a56;fill-rule:nonzero;"/><path d="M422.161,417.992c3.833,-4.107 8.646,-7.104 13.905,-9.035c0.994,4.481 2.479,8.833 4.265,13.07c-3.386,-1.614 -6.758,-3.242 -10.101,-4.914c-2.435,1.989 -4.871,3.963 -7.32,5.922c-0.36,-0.994 -1.081,-2.968 -1.441,-3.963l-0.029,-0.086l0.72,-0.994Z" style="fill:#4a6fb1;fill-rule:nonzero;"/><path d="M559.689,411.264c0.766,0.708 0.766,0.708 0,0Z" style="fill:#323335;fill-rule:nonzero;"/><path d="M578.436,411.279c0.766,0.708 0.766,0.708 0,0Z" style="fill:#3a3b3d;fill-rule:nonzero;"/><path d="M475.998,424.737c3.127,-4.611 5.577,-9.784 10.13,-13.228c3.271,6.946 6.628,13.848 10.015,20.736c-0.721,-0.13 -2.147,-0.375 -2.868,-0.49c-2.723,2.522 -5.548,4.914 -8.487,7.176c-3.141,-4.597 -6.182,-9.28 -8.79,-14.194Z" style="fill:#4b6379;fill-rule:nonzero;"/><path d="M556.794,412.892c0.781,0.679 0.781,0.679 0,0Z" style="fill:#37383a;fill-rule:nonzero;"/><path d="M569.892,412.676c0.68,0.824 0.68,0.824 0,0Z" style="fill:#4d4f52;fill-rule:nonzero;"/><path d="M575.438,412.992c1.96,-0.259 3.502,3.084 2.508,4.683c-2.19,0.49 -3.473,-3.055 -2.508,-4.683Z" style="fill:#202122;fill-rule:nonzero;"/><path d="M588.508,412.733c0.781,0.723 0.781,0.723 0,0Z" style="fill:#494a4d;fill-rule:nonzero;"/><path d="M541.618,413.296c1.513,1.528 2.68,3.66 5.043,3.992c-0.475,0.346 -1.412,1.037 -1.873,1.369c2.161,3.631 4.366,7.234 6.469,10.908c-0.764,0.346 -2.276,1.066 -3.04,1.427l-0.244,-0.634c-0.995,-6.225 -7.71,-10.289 -6.355,-17.061Z" style="fill:#4d4e50;fill-rule:nonzero;"/><path d="M585.426,414.289c4.769,0.145 -2.154,4.365 0,0Z" style="fill:#303133;fill-rule:nonzero;"/><path d="M494.37,420.961c-0.317,-2.233 -1.052,-6.96 3.141,-5.706c-0.648,4.698 -0.014,9.482 2.075,13.761c1.7,3.329 3.098,7.435 6.816,9.021c0.447,3.617 3.732,5.663 6.239,7.868c2.839,2.061 5.677,4.856 9.453,4.784c0.793,2.162 2.968,3.026 4.885,3.948c5.274,2.406 10.692,4.554 16.384,5.807l-0.519,0.246c-2.867,1.355 -5.777,2.636 -8.674,3.905c-10.519,-0.951 -21.053,1.527 -30.318,6.469c-4.121,1.428 -7.652,4.222 -9.482,8.242c-1.023,1.744 -1.21,3.818 -1.585,5.764c-1.167,-1.052 -2.291,-2.133 -3.358,-3.257c0.36,-2.449 2.234,-4.351 3.43,-6.455c-10.361,-6.254 -19.799,-13.819 -29.54,-20.952c-0.591,-0.403 -1.772,-1.196 -2.363,-1.585c1.599,-0.649 3.199,-1.283 4.813,-1.917c7.464,6.744 16.067,12.076 24.626,17.32c2.623,1.412 5.505,3.776 8.603,2.176c5,-3.429 10.548,-5.85 15.995,-8.43c-2.954,-5.259 -6.009,-10.475 -9.467,-15.432c-4.971,-7.882 -9.193,-16.442 -11.153,-25.577Z" style="fill:#272e33;fill-rule:nonzero;"/><path d="M571.434,415.429c4.307,-2.298 0.275,4.77 0,0Z" style="fill:#38393c;fill-rule:nonzero;"/><path d="M599.936,422.578c4.192,-2.457 0.491,4.77 0,0Z" style="fill:#38393c;fill-rule:nonzero;"/><path d="M563.998,416.984c0.823,0.766 0.823,0.766 0,0Z" style="fill:#73757a;fill-rule:nonzero;"/><path d="M582.76,416.971c0.823,0.752 0.823,0.752 0,0Z" style="fill:#707276;fill-rule:nonzero;"/><path d="M595.916,416.984c0.741,0.726 0.741,0.726 0,0Z" style="fill:#313234;fill-rule:nonzero;"/><path d="M601.52,416.971c0.737,0.766 0.737,0.766 0,0Z" style="fill:#404144;fill-rule:nonzero;"/><path d="M561.39,417.848c0.98,2.565 3.213,5.591 0,7.579c1.441,1.571 2.897,3.17 4.338,4.77c1.397,-3.689 2.781,-3.819 4.149,-0.403c-0.821,0.144 -2.478,0.418 -3.299,0.548c0.778,2.176 1.426,4.453 -0.216,6.427c4.554,-2.824 5.908,3.674 8.848,5.865c10.561,10.13 21.398,20.419 28.329,33.488c0.216,0.778 0.648,2.349 0.85,3.142c-0.648,-0.477 -1.96,-1.441 -2.608,-1.917c-6.758,-12.176 -16.96,-21.918 -27.248,-31.111c-2.522,-2.147 -4.611,-4.741 -6.023,-7.738c-0.462,0.259 -1.355,0.793 -1.816,1.052c-2.003,-1.974 -4.035,-3.934 -6.111,-5.807c1.758,2.868 3.632,5.677 5.461,8.531c0.462,-0.245 1.399,-0.735 1.859,-0.98c8.661,8.66 17.739,16.975 25.447,26.528c2.68,3.286 4.655,7.306 8.43,9.525c-0.072,0.922 -0.202,2.767 -0.274,3.688c-8.127,-7.853 -17.118,-14.798 -26.989,-20.318c-4.567,-2.406 -8.142,-6.167 -11.903,-9.625c-5.274,-4.381 -10.26,-9.194 -13.776,-15.145l-0.158,-0.245c2.291,1.124 4.366,2.637 6.24,4.395c10.071,7.724 18.747,17.205 29.438,24.122c-3.516,-4.828 -8.199,-8.589 -12.002,-13.157c-5.232,-4.453 -11.183,-8.531 -14.209,-14.929c-0.044,-0.562 -0.158,-1.7 -0.216,-2.262c-0.619,-0.288 -1.873,-0.864 -2.508,-1.153c-0.432,-0.908 -1.296,-2.709 -1.729,-3.617c1.816,0.029 3.589,-0.85 4.453,-2.493c1.643,1.844 3.126,4.15 5.75,4.654c-1.845,-3.602 -5.015,-6.456 -6.311,-10.332l-0.216,-0.591c0.922,0.562 2.737,1.7 3.646,2.262c0.101,-1.196 0.288,-3.559 0.375,-4.755Zm2.911,14.957c0.792,0.807 0.792,0.807 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M568.35,422.79c0.81,0.809 0.81,0.809 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M594.488,427.042c0.752,0.867 0.752,0.867 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M571.318,430.919c0.677,-0.893 2.032,-2.709 2.709,-3.617c0.231,0.98 0.692,2.94 0.922,3.919c1.816,-1.412 6.888,-5.346 5.649,0.778c-0.907,-0.13 -2.723,-0.403 -3.631,-0.533c-0.043,0.548 -0.158,1.657 -0.215,2.205c1.252,-0.303 3.775,-0.922 5.043,-1.239c-0.576,1.081 -1.7,3.213 -2.276,4.28l1.542,0.245l0.301,-2.738c0.591,0.331 1.788,1.009 2.392,1.34c0.534,1.974 -1.469,3.055 -2.506,4.366c2.622,-1.513 4.856,-4.899 8.228,-4.208l0.303,0.807c0.274,0.461 0.821,1.369 1.094,1.816c0.519,0.533 1.527,1.599 2.047,2.133c0.043,0.058 0.158,0.173 0.215,0.231l0.13,-0.144l1.672,-1.801c2.607,1.974 4.611,4.597 6.499,7.234c-0.606,-1.527 -1.197,-3.055 -1.788,-4.582c1.399,-1.355 2.652,-2.868 3.2,-4.77c-1.729,1.138 -3.4,2.349 -5.058,3.574l-0.389,-2.234l1.124,-1.167l0.547,-0.332l0.448,-0.303c1.21,-0.259 3.646,-0.778 4.87,-1.038l-0.461,2.968c2.003,-2.781 3.617,-2.723 4.856,0.187c-3.17,-0.303 -6.067,2.378 -1.052,3.098c-3.055,2.968 -5.259,7.118 -9.713,8.242c4.597,-0.519 7.523,-4.597 11.615,-6.34l0.936,2.81c0.015,0.058 0.044,0.144 0.044,0.202c0.057,0.562 0.143,1.715 0.202,2.277c-0.995,1.599 -2.003,3.199 -3.012,4.77l-1.757,-0.288c0.821,0.634 2.478,1.931 3.314,2.565c0.057,0.173 0.173,0.504 0.231,0.663c-0.995,3.386 -2.234,6.744 -2.537,10.289c4.54,2.666 2.335,-6.024 7.003,-4.18c-0.029,0.908 -0.072,2.695 -0.101,3.603l-0.793,0.028l0.015,0.764l0.778,-0.015c-1.34,5.375 -2.623,10.779 -4.294,16.083l-0.36,1.181c-5.246,-12.637 -13.79,-23.689 -23.545,-33.171c-5.707,-5.173 -12.149,-9.943 -15.607,-17.032c1.773,-0.504 3.257,-1.441 4.439,-2.81l-3.271,-0.115Zm2.84,3.43c0.806,0.735 0.806,0.735 0,0Zm2.579,2.68c4.769,0.043 -2.061,4.409 0,0Zm10.317,1.499c0.764,0.749 0.764,0.749 0,0Zm-2.767,2.911c0.778,0.793 0.778,0.793 0,0Zm4.553,-0.202c-0.446,1.844 1.6,4.755 3.488,4.87c1.311,-2.017 -1.067,-5.793 -3.488,-4.87Zm14.28,0.303c0.778,0.85 0.778,0.85 0,0Zm-21.802,1.542c0.72,0.749 0.72,0.749 0,0Zm12.94,-0.346c4.193,-2.421 0.461,4.784 0,0Zm-8.444,1.47c4.265,-2.306 0.331,4.755 0,0Zm11.354,1.628c0.85,0.692 0.85,0.692 0,0Zm-3.083,1.455c4.769,0.187 -2.177,4.337 0,0Zm13.315,0.158c0.792,0.778 0.792,0.778 0,0Zm-17.292,2.536c4.222,-2.392 0.418,4.755 0,0Zm14.381,0.101c0.778,0.764 0.778,0.764 0,0Zm-8.632,1.484c0.707,0.764 0.707,0.764 0,0Zm5.418,-0.086c4.727,0.245 -2.205,4.352 0,0Zm-1.023,5.966c0.707,0.749 0.707,0.749 0,0Zm2.739,2.81c0.72,0.734 0.72,0.734 0,0Zm-3.2,1.555c4.769,0.13 -2.148,4.352 0,0Zm5.779,7.969c2.781,2.695 2.895,-3.574 0.013,-0.951c0,0.246 0,0.707 -0.013,0.951Zm3.285,4.9c0.721,0.764 0.721,0.764 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M569.819,431.091c0.825,0.795 0.825,0.795 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M588.235,431.624c0.766,0.853 0.766,0.853 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M585.253,432.936l1.272,-0.101c2.644,2.486 -2.805,3.122 -1.272,0.101Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M539.501,434.362c0.708,0.766 0.708,0.766 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M536.692,437.216c0.723,0.752 0.723,0.752 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M608.582,439.695c0.796,0.824 0.796,0.824 0,0Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M543.665,472.879c7.018,-0.072 14.064,0.879 20.548,3.704c-3.126,0.187 -6.253,0.475 -9.351,0.806c-2.377,0.275 -4.727,0.663 -7.046,1.197c-1.384,-1.902 -2.781,-3.804 -4.15,-5.707Z" style="fill:#7d8187;fill-rule:nonzero;"/><path d="M574.186,418.54c0.823,0.781 0.823,0.781 0,0Z" style="fill:#6f7176;fill-rule:nonzero;"/><path d="M579.606,418.613c4.784,0.217 -2.226,4.337 0,0Z" style="fill:#3d3f42;fill-rule:nonzero;"/><path d="M592.875,418.484c0.781,0.737 0.781,0.737 0,0Z" style="fill:#505256;fill-rule:nonzero;"/><path d="M631.262,417.547c4.079,1.83 8.027,3.905 11.86,6.211c0.489,-0.36 1.469,-1.095 1.959,-1.47l0.648,-0.475c0.073,3.775 0.231,7.536 0.404,11.297c0.044,2.623 0.173,5.231 0.576,7.839c-0.461,-0.447 -1.369,-1.311 -1.83,-1.758c-0.375,-0.836 -1.124,-2.507 -1.484,-3.343c-3.906,-2.392 -7.954,-4.525 -12.033,-6.571c0,-0.965 -0.013,-2.896 -0.028,-3.847c0,-2.637 0.015,-5.274 -0.073,-7.882Z" style="fill:#5f89b4;fill-rule:nonzero;"/><path d="M420.678,426.221c3.458,-2.32 6.773,-4.885 10.274,-7.147c0.389,0.202 1.196,0.591 1.585,0.793c2.695,5.62 5.692,11.211 10.159,15.663c-3.415,2.839 -6.758,5.75 -10.015,8.761c-4.582,-5.764 -9.554,-11.297 -13.07,-17.796c0.245,-0.692 0.721,-2.061 0.951,-2.738c0.029,0.62 0.086,1.845 0.115,2.464Z" style="fill:#5b7da1;fill-rule:nonzero;"/><path d="M565.526,419.766c4.336,-2.226 0.231,4.77 0,0Z" style="fill:#4d4e51;fill-rule:nonzero;"/><path d="M584.201,419.81c4.235,-2.414 0.42,4.785 0,0Z" style="fill:#414245;fill-rule:nonzero;"/><path d="M598.612,419.837c0.809,0.665 0.809,0.665 0,0Z" style="fill:#3e4043;fill-rule:nonzero;"/><path d="M602.904,419.795c4.206,-2.443 0.448,4.77 0,0Z" style="fill:#232426;fill-rule:nonzero;"/><path d="M611.22,419.622c3.545,0.014 7.09,0.029 10.649,0.029c0.187,2.003 0.389,4.006 0.606,6.023c0.028,0.245 0.072,0.735 0.101,0.98c0.23,3.199 0.375,6.427 0.461,9.655c-2.897,-0.49 -6.01,-1.081 -8.602,0.677c-0.938,-5.807 -1.99,-11.6 -3.214,-17.364Z" style="fill:#55728f;fill-rule:nonzero;"/><path d="M634.001,444.088c3.775,1.585 8.415,2.983 9.496,7.522c-0.015,0.908 -0.044,2.738 -0.073,3.646c-0.389,1.772 -0.734,3.545 -1.067,5.318c-4.87,-1.96 -11.037,-4.107 -11.512,-10.275c1.067,-2.075 2.104,-4.15 3.156,-6.211Z" style="fill:#55728f;fill-rule:nonzero;"/><path d="M654.852,419.808c0.823,0.867 0.823,0.867 0,0Z" style="fill:#6c9abf;fill-rule:nonzero;"/><path d="M432.538,419.867c3.199,1.585 6.383,3.199 9.467,5.015c7.435,9.107 14.813,18.315 23.762,26.024c-1.614,0.634 -3.213,1.268 -4.813,1.917c-7.32,-4.352 -12.335,-11.369 -18.257,-17.292c-4.467,-4.453 -7.464,-10.044 -10.159,-15.663Z" style="fill:#415364;fill-rule:nonzero;"/><path d="M472.02,442.448c4.337,3.602 8.631,7.248 12.911,10.937c-1.729,1.643 -3.43,3.314 -5.144,4.971c-4.006,-2.695 -7.825,-5.663 -11.571,-8.703c1.268,-2.406 2.536,-4.798 3.804,-7.205Z" style="fill:#415364;fill-rule:nonzero;"/><path d="M539.01,420.63c3.156,3.069 6.038,6.427 8.964,9.727l0.244,0.634c0.764,-0.36 2.276,-1.081 3.04,-1.427l0.677,-0.317c0.663,1.686 1.83,2.839 3.488,3.473c0.634,0.288 1.887,0.864 2.508,1.153c0.057,0.562 0.173,1.7 0.215,2.262c-1.052,1.34 -2.132,2.651 -3.227,3.963c-1.873,-1.758 -3.949,-3.271 -6.24,-4.395l-0.015,0c0.044,0.058 0.13,0.187 0.174,0.245c3.516,5.951 8.501,10.764 13.776,15.145c0.114,0.922 0.36,2.752 0.475,3.675c-1.485,-0.677 -2.941,-1.369 -4.382,-2.075l-0.432,-0.72c-2.075,-4.222 -5.807,-7.147 -9.381,-10.029c-0.446,-0.014 -1.34,-0.014 -1.786,-0.029c-1.816,-2.262 -3.949,-4.28 -6.5,-5.692c1.009,1.801 2.104,3.559 3.229,5.303c-2.278,-0.864 -4.611,-1.772 -7.09,-1.225c-2.868,0.259 -4.842,-2.435 -7.133,-3.703c2.608,-2.219 5.318,-4.309 8.099,-6.326c-0.692,-0.216 -2.076,-0.663 -2.781,-0.893l-1.038,-0.346c1.614,-1.441 3.344,-2.767 5.13,-3.991c0.202,0.879 0.62,2.651 0.821,3.545c1.83,1.081 3.66,2.162 5.577,3.113c-1.946,-3.819 -6.585,-6.369 -6.412,-11.067Zm6.311,12.133c0.764,0.735 0.764,0.735 0,0Zm-8.472,1.686c0.749,0.721 0.749,0.721 0,0Zm2.651,-0.086c0.707,0.764 0.707,0.764 0,0Zm-2.81,2.853c0.721,0.749 0.721,0.749 0,0Z" style="fill:#4a4b4d;fill-rule:nonzero;"/><path d="M576.85,421.35c0.796,0.781 0.796,0.781 0,0Z" style="fill:#6e7175;fill-rule:nonzero;"/><path d="M590.065,421.237c0.737,0.824 0.737,0.824 0,0Z" style="fill:#616468;fill-rule:nonzero;"/><path d="M608.724,421.379c0.766,0.766 0.766,0.766 0,0Z" style="fill:#525356;fill-rule:nonzero;"/><path d="M577.486,426.395c0.259,-2.666 3.126,-2.925 4.87,-4.136c1.47,1.513 2.853,3.098 4.077,4.813l-2.809,0.014c0.143,-1.888 -0.62,-3.17 -2.32,-3.819c-0.951,0.778 -2.853,2.334 -3.818,3.127Z" style="fill:#797678;fill-rule:nonzero;"/><path d="M573.998,423.843c1.772,-0.058 4.063,2.464 3.429,4.323c-2.205,1.239 -3.905,-2.551 -3.429,-4.323Z" style="fill:#797678;fill-rule:nonzero;"/><path d="M445.968,559.006c1.772,1.915 3.617,3.76 5.562,5.504c4.309,3.66 7.81,8.112 10.937,12.795c-0.533,0.246 -1.585,0.707 -2.118,0.938c1.816,1.614 3.545,3.343 5.072,5.245c-0.548,1.584 -1.081,3.17 -1.614,4.769c1.657,-0.446 3.329,-0.894 5,-1.325c1.066,2.247 1.772,4.654 1.715,7.191c1.758,1.355 3.53,2.695 5.332,4.035c1.095,2.622 2.205,5.23 3.329,7.839c1.787,3.949 3.3,8.07 3.631,12.435c-0.836,0.116 -2.522,0.345 -3.372,0.475c0.086,1.816 0.159,3.646 0.245,5.491c0.519,1.944 1.023,3.89 1.542,5.864c-1.167,0.058 -3.516,0.159 -4.698,0.202c-0.187,0.995 -0.591,2.968 -0.793,3.963c1.484,-0.894 2.968,-1.786 4.467,-2.68c1.974,4.035 4.899,8.617 10,8.602c5.908,0.808 8.747,-5.028 12.637,-8.213c-0.158,1.671 -0.331,3.358 -0.504,5.028c3.228,3.719 6.672,7.422 11.254,9.454c-0.548,0 -1.657,-0.015 -2.205,-0.015c-2.695,-0.044 -5.418,-0.158 -8.069,0.404c-3.026,1.296 -5.072,4.035 -7.45,6.21c0.303,1.254 0.605,2.508 0.908,3.775c-1.009,-0.562 -2.032,-1.124 -3.04,-1.671l-1.715,-0.923c1.239,-1.671 2.421,-3.385 3.545,-5.144c-2.046,0.923 -3.631,2.551 -5.317,3.992c-0.576,-2.522 0.793,-4.598 2.061,-6.601c-1.614,1.067 -2.911,2.81 -4.87,3.3c-0.389,-0.835 -1.196,-2.522 -1.599,-3.358c-1.859,1.845 -4.64,5.087 -5.577,0.62c0.807,-0.389 2.406,-1.167 3.199,-1.571c-1.369,-1.441 -2.81,-2.81 -4.236,-4.193c-1.83,1.917 -3.934,3.53 -6.009,5.159c-0.216,-0.692 -0.648,-2.061 -0.85,-2.752c-0.663,-0.246 -1.989,-0.707 -2.652,-0.938l-0.403,-2.434c0.836,0.547 2.493,1.614 3.329,2.146c-0.259,-1.166 -0.519,-2.319 -0.764,-3.457c0.922,-1.009 1.844,-2.004 2.767,-3.012c-1.888,-1.009 -3.804,-4.856 -5.879,-1.786c-0.519,-1.081 -0.994,-2.234 -1.455,-3.315l-0.029,-0.086l-0.043,-0.057c-0.504,-0.562 -1.513,-1.701 -2.003,-2.278l1.11,-1.353c0.389,-0.505 1.196,-1.514 1.599,-2.017l0.058,-0.044l-0.259,-0.244c-0.634,-0.62 -1.902,-1.889 -2.536,-2.522l-0.288,-0.375l-0.346,-0.303c-0.533,-0.619 -1.599,-1.845 -2.147,-2.449l-0.475,-0.793c0.648,-0.562 1.974,-1.685 2.637,-2.249c0.086,0.073 0.245,0.187 0.332,0.26c0.591,0.591 1.801,1.772 2.406,2.377l0.519,0.432c1.859,0.116 3.732,0.145 5.605,0.202c0.994,-1.325 2.017,-2.607 3.055,-3.905c-2.19,-1.498 -4.424,-2.882 -6.859,-3.905l1.038,-1.153c-0.014,-0.029 -0.058,-0.086 -0.072,-0.116c-0.562,-0.734 -1.7,-2.205 -2.277,-2.939l-0.115,-0.231c-2.306,-6.153 -7.435,-10.418 -11.067,-15.692c-0.692,-0.231 -2.075,-0.663 -2.781,-0.879c0.807,-0.85 1.599,-1.7 2.406,-2.535c-3.199,-4.352 -6.902,-8.316 -10.995,-11.846c0.389,-1.267 0.778,-2.521 1.167,-3.775c-2.334,-2.68 -4.755,-5.303 -7.032,-8.026c1.542,-0.778 3.069,-1.571 4.582,-2.392c0.375,-0.793 1.11,-2.364 1.47,-3.156Zm9.928,16.412c4.294,-2.276 0.317,4.769 0,0Zm11.614,26.01c0.692,0.777 0.692,0.777 0,0Zm5.836,0.086c0.721,0.764 0.721,0.764 0,0Zm-2.911,2.853c0.721,0.764 0.721,0.764 0,0Zm5.692,0.072c0.778,0.707 0.778,0.707 0,0Zm-2.795,2.767c0.764,0.692 0.764,0.692 0,0Zm2.839,2.897c0.692,0.778 0.692,0.778 0,0Zm2.925,2.882c0.735,0.764 0.735,0.764 0,0Zm-8.732,8.674c0.735,0.793 0.735,0.793 0,0Zm5.807,0.058c0.749,0.705 0.749,0.705 0,0Zm-2.839,2.781c0.749,0.749 0.749,0.749 0,0Zm-2.968,2.867c0.821,0.734 0.821,0.734 0,0Zm5.735,0.072c0.793,0.692 0.793,0.692 0,0Zm-2.911,2.897c0.821,0.749 0.821,0.749 0,0Zm5.793,5.691c0.821,0.764 0.821,0.764 0,0Zm-2.882,3.027c0.821,0.764 0.821,0.764 0,0Zm5.778,-0.13c0.778,0.808 0.778,0.808 0,0Zm17.277,0.072c0.821,0.764 0.821,0.764 0,0Zm-14.424,2.882c0.807,0.778 0.807,0.778 0,0Zm5.778,-0.116c0.821,0.764 0.821,0.764 0,0Zm5.85,-0.028c0.793,0.806 0.793,0.806 0,0Zm5.706,0.072c0.821,0.749 0.821,0.749 0,0Zm-14.295,3.04c0.807,0.778 0.807,0.778 0,0Zm5.75,-0.116c0.793,0.808 0.793,0.808 0,0Zm5.663,-0.013c0.764,0.821 0.764,0.821 0,0Zm-2.868,2.925c0.836,0.749 0.836,0.749 0,0Z" style="fill:#797678;fill-rule:nonzero;"/><path d="M597.04,671.706c7.752,12.119 9.539,29.713 -1.081,40.779c1.6,1.672 3.2,3.343 4.813,5.03c3.89,-6.01 7.594,-12.58 7.435,-19.973c0.75,-8.545 -2.478,-16.571 -5.432,-24.381c4.107,8.011 7.22,16.874 6.412,26.009c-0.216,8.142 -4.496,15.391 -9.452,21.572c-3.128,-8.156 -11.6,-13.761 -20.333,-13.214c-11.615,0.461 -21.485,11.643 -20.505,23.215c0.505,7.42 5.908,13.732 12.393,16.946c7.622,3.227 16.485,2.031 23.949,-1.11c20.72,-8.573 33.791,-32.84 28.546,-54.814c-1.067,-7.205 -5.318,-13.287 -7.508,-20.103c4.611,8.589 9.063,17.551 9.654,27.481c2.421,24.208 -16.498,48.661 -40.678,51.889c-11.139,1.744 -22.306,-4.15 -28.79,-13.012c-6.513,-8.934 -5.16,-22.335 2.493,-30.16c1.023,-0.677 2.06,-1.327 3.126,-1.931c-2.867,3.703 -6.225,7.305 -7.363,11.989c-1.801,6.297 -0.677,13.227 2.867,18.732c0,-3.877 -0.446,-7.752 0.158,-11.585c2.104,-10.534 12.176,-19.41 23.185,-18.646c5.289,-0.202 9.871,2.693 14.554,4.711c-2.752,-5.59 -8.718,-7.94 -14.236,-9.856c0.23,-0.215 0.705,-0.619 0.951,-0.821c5.39,1.311 9.755,4.698 13.948,8.142c0.332,0.015 0.98,0.029 1.312,0.029c8.011,-10.88 5.144,-25.607 -0.418,-36.918Z" style="fill:#797678;fill-rule:nonzero;"/><path d="M611.276,673.175c5.966,8.358 10.203,18.359 9.944,28.777c1.584,22.306 -17.711,44.526 -40.276,44.757c-10.073,0.705 -20.303,-8.228 -19.136,-18.662c0.764,-11.253 12.955,-20.533 23.949,-16.456c6.715,2.278 10.662,9.252 10.231,16.168c9.583,-7.565 17.018,-18.949 16.658,-31.528c0.606,-7.479 -2.249,-14.497 -3.804,-21.643l0.202,0.533c4.467,10.807 6.671,23.301 2.493,34.554c-3.574,8.934 -9.885,16.975 -18.374,21.717c1.946,-6.384 0.677,-14.065 -5.634,-17.48c-10.115,-6.24 -23.588,2.911 -24.164,14.078c-1.039,10.231 9.452,18.243 19.121,17.133c22.739,-1.816 40.405,-25.218 37.638,-47.537c-0.648,-8.329 -4.237,-15.982 -8.213,-23.185l-0.635,-1.226Z" style="fill:#797678;fill-rule:nonzero;"/><path d="M587.067,422.95c0.781,0.824 0.781,0.824 0,0Z" style="fill:#717479;fill-rule:nonzero;"/><path d="M540.612,436.222c2.551,1.412 4.683,3.43 6.499,5.692c0.448,0.014 1.34,0.014 1.788,0.029c3.573,2.882 7.305,5.807 9.381,10.029c-2.276,-0.807 -4.54,-1.643 -6.801,-2.435c0.116,-0.519 0.331,-1.542 0.446,-2.061c-3.574,1.917 -7.839,-1.686 -8.142,-5.36l-0.116,3.127c-2.521,-1.34 -4.885,-2.94 -6.916,-4.943c2.478,-0.548 4.812,0.36 7.09,1.225c-1.125,-1.744 -2.219,-3.502 -3.229,-5.303Z" style="fill:#717479;fill-rule:nonzero;"/><path d="M605.873,422.95c0.766,0.737 0.766,0.737 0,0Z" style="fill:#47484a;fill-rule:nonzero;"/><path d="M663.583,422.778c1.959,1.182 2.407,3.141 1.225,5.115c1.21,1.47 2.434,2.954 3.675,4.438c3.257,-0.62 6.541,-1.095 9.856,-1.47c-1.197,2.306 -2.407,4.611 -3.545,6.946l-1.773,-0.692c-4.035,-1.542 -7.969,-3.329 -11.83,-5.231c-2.017,1.369 -4.15,2.651 -6.557,3.199c0.764,-3.314 3.113,-5.505 6.686,-4.928c0.173,-2.623 0.418,-5.332 2.263,-7.378Z" style="fill:#9cb7dd;fill-rule:nonzero;"/><path d="M565.222,424.463c4.815,0.231 -2.283,4.322 0,0Z" style="fill:#676a6f;fill-rule:nonzero;"/><path d="M588.482,426.869c-0.231,-2.205 3.141,-3.473 4.942,-2.882c-0.015,1.931 -3.185,3.905 -4.942,2.882Z" style="fill:#46474a;fill-rule:nonzero;"/><path d="M553.694,429.103c-1.412,-1.974 4.077,-5.26 4.452,-2.493c-0.863,1.643 -2.636,2.522 -4.452,2.493Z" style="fill:#414345;fill-rule:nonzero;"/><path d="M488.62,514.379c3.3,0.029 6.686,-0.475 9.957,0.332c1.009,2.003 -2.19,2.449 -3.156,3.675c0.418,0.475 0.836,0.966 1.254,1.441c-0.49,2.19 -0.98,4.366 -1.412,6.57c-0.85,3.213 -1.758,6.412 -2.608,9.612c-1.917,0.562 -3.833,1.124 -5.735,1.7c-0.043,2.146 -0.086,4.294 -0.115,6.455c-1.614,-0.028 -3.242,-0.028 -4.856,-0.043c-1.888,-3.126 -4.222,-5.937 -6.859,-8.444c0.231,-1.153 0.461,-2.291 0.721,-3.429c2.795,-1.426 5.231,-3.617 5.159,-7.018c-3.055,0.202 -6.124,0.36 -9.165,0.562c1.225,-0.648 2.45,-1.311 3.674,-1.974c3.574,-1.355 7.219,-2.537 10.851,-3.732c0.749,-1.441 1.499,-2.882 2.19,-4.352l0.101,-1.355Zm-3.314,13.228c0.231,0.98 0.692,2.926 0.922,3.891c2.306,0.734 2.81,-1.527 3.631,-3.113c-1.138,-0.187 -3.415,-0.576 -4.554,-0.778Z" style="fill:#414345;fill-rule:nonzero;"/><path d="M569.934,425.676c4.278,-2.371 0.332,4.813 0,0Z" style="fill:#6c6f73;fill-rule:nonzero;"/><path d="M602.948,425.659c0.823,0.766 0.823,0.766 0,0Z" style="fill:#76787d;fill-rule:nonzero;"/><path d="M607.371,425.603c4.366,-2.125 0.073,4.813 0,0Z" style="fill:#29292b;fill-rule:nonzero;"/><path d="M625.226,425.5c2.032,-0.029 4.063,-0.058 6.109,-0.072c0.015,0.951 0.029,2.882 0.029,3.848c0.072,4.856 0.259,9.727 0.086,14.597c-1.931,0.058 -3.848,0.115 -5.75,0.173c-0.029,-6.182 0.086,-12.378 -0.475,-18.545Z" style="fill:#4e667d;fill-rule:nonzero;"/><path d="M598.394,430.113c0.793,-1.311 1.614,-2.594 2.464,-3.876c0.057,0.735 0.158,2.219 0.216,2.954c2.06,0.476 3.011,1.744 2.853,3.79c0.116,0.533 0.345,1.614 0.461,2.161c-1.225,0.259 -3.66,0.778 -4.87,1.038c-0.259,0.158 -0.749,0.475 -0.995,0.634l-1.124,1.167c-0.331,-0.346 -1.008,-1.023 -1.355,-1.355c-0.274,0.533 -0.821,1.599 -1.109,2.133l-1.672,1.801l-0.345,-0.086c-0.519,-0.533 -1.527,-1.599 -2.047,-2.133c-0.274,-0.447 -0.821,-1.355 -1.094,-1.816l-0.303,-0.807l-0.116,-1.801c0.591,-0.461 1.786,-1.369 2.392,-1.816c-0.375,-1.182 -1.124,-3.53 -1.513,-4.712c1.988,0.98 3.227,3.141 5.447,3.703c0.677,-0.245 2.032,-0.735 2.709,-0.98Zm-5.375,4.265c0.778,0.706 0.778,0.706 0,0Z" style="fill:#525458;fill-rule:nonzero;"/><path d="M585.699,428.255c4.336,-2.197 0.159,4.842 0,0Z" style="fill:#777b80;fill-rule:nonzero;"/><path d="M604.402,428.356c4.395,-2.125 0.145,4.77 0,0Z" style="fill:#4c4e51;fill-rule:nonzero;"/><path d="M609.909,428.728c4.769,0.159 -2.14,4.38 0,0Z" style="fill:#45464a;fill-rule:nonzero;"/><path d="M585.815,444.205c4.278,-2.313 0.332,4.77 0,0Z" style="fill:#45464a;fill-rule:nonzero;"/><path d="M460.637,429.896c3.631,4.337 7.407,8.545 11.384,12.551c-1.268,2.406 -2.536,4.798 -3.804,7.205c-4.755,-3.891 -9.035,-8.329 -13.286,-12.753c1.902,-2.334 3.804,-4.669 5.706,-7.003Z" style="fill:#4a6176;fill-rule:nonzero;"/><path d="M595.687,431.097c0.433,-0.434 1.271,-1.286 1.691,-1.72l1.027,0.737c-0.68,0.246 -2.039,0.737 -2.717,0.983Z" style="fill:#686b70;fill-rule:nonzero;"/><path d="M631.364,429.276c4.079,2.046 8.127,4.179 12.033,6.571c0.36,0.836 1.109,2.507 1.484,3.343c0.461,0.447 1.369,1.311 1.83,1.758l0.389,0.36c-1.801,2.565 -2.148,5.721 -2.767,8.718l-0.519,1.009l-0.316,0.576c-1.081,-4.539 -5.722,-5.937 -9.496,-7.522c-1.052,2.061 -2.089,4.136 -3.156,6.211c0.244,-2.147 0.446,-4.28 0.604,-6.427c0.174,-4.87 -0.013,-9.741 -0.086,-14.597Z" style="fill:#5b7da0;fill-rule:nonzero;"/><path d="M607.17,431.438c0.838,0.781 0.838,0.781 0,0Z" style="fill:#7b7e84;fill-rule:nonzero;"/><path d="M661.193,431.883c3.861,1.902 7.795,3.689 11.83,5.231c0.576,1.268 1.182,2.55 1.801,3.833c1.672,2.551 3.372,5.087 5.015,7.666c-4.178,-1.282 -8.343,-2.911 -12.767,-2.954c-2.723,2.925 -5.375,5.922 -7.868,9.035c-0.129,-1.297 -0.375,-3.876 -0.489,-5.173c0.057,-2.695 1.353,-5.62 0.288,-8.156c-2.566,-2.017 -5.837,-2.723 -8.877,-3.646c-0.143,4.842 -0.259,9.712 -1.383,14.453c-0.347,0.692 -1.009,2.075 -1.34,2.767c-0.995,-1.499 -2.19,-2.796 -3.589,-3.905l0.519,-1.009c1.412,-1.297 2.91,-2.507 4.351,-3.761c0.707,-1.772 -0.028,-3.919 -1.584,-4.957l-0.389,-0.36c-0.404,-2.608 -0.534,-5.216 -0.576,-7.839c0.966,0.706 2.897,2.104 3.862,2.81c1.153,-0.202 3.486,-0.634 4.639,-0.836c2.407,-0.548 4.54,-1.83 6.557,-3.199Zm-2.551,6.311c4.294,3.069 1.384,8.545 1.658,12.81c2.522,-2.349 4.74,-5 7.262,-7.349c2.926,0.576 5.735,1.585 8.632,2.363c-1.513,-8.084 -11.283,-7.522 -17.551,-7.825Z" style="fill:#b5c9e7;fill-rule:nonzero;"/><path d="M523.679,432.072c3.847,4.928 8.804,8.804 14.136,12.018c-1.254,3.487 -2.449,6.974 -3.588,10.505c-5.994,-2.551 -11.758,-5.677 -17.018,-9.539c2.104,-4.366 4.251,-8.689 6.47,-12.983Z" style="fill:#f6a926;fill-rule:nonzero;"/><path d="M522.094,450.689c6.96,3.502 14.828,5.389 21.268,9.755c-5.691,-1.253 -11.11,-3.4 -16.383,-5.807c-1.917,-0.922 -4.092,-1.787 -4.885,-3.948Z" style="fill:#f6a926;fill-rule:nonzero;"/><path d="M529.43,432.85c0.809,0.795 0.809,0.795 0,0Z" style="fill:#797c82;fill-rule:nonzero;"/><path d="M564.3,432.806c0.796,0.809 0.796,0.809 0,0Z" style="fill:#030303;fill-rule:nonzero;"/><path d="M603.927,432.979c0.521,0.231 1.547,0.708 2.053,0.94l-0.462,1.185l-1.128,0.043c-0.116,-0.549 -0.348,-1.633 -0.464,-2.168Z" style="fill:#6e7277;fill-rule:nonzero;"/><path d="M536.849,434.448c0.752,0.723 0.752,0.723 0,0Z" style="fill:#0b0b0c;fill-rule:nonzero;"/><path d="M560.583,433.741c2.075,1.873 4.107,3.833 6.109,5.807c0.461,-0.259 1.355,-0.793 1.816,-1.052c1.412,2.997 3.502,5.591 6.023,7.738c10.289,9.193 20.491,18.934 27.25,31.111c-3.776,-2.219 -5.75,-6.24 -8.43,-9.525c-7.71,-9.553 -16.788,-17.867 -25.449,-26.528c-0.461,0.245 -1.397,0.735 -1.858,0.98c-1.83,-2.853 -3.703,-5.663 -5.461,-8.531Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M534.169,464.594c2.896,-1.268 5.807,-2.551 8.675,-3.905c3.185,0.288 6.21,1.397 9.223,2.42c1.988,0.736 3.962,1.499 5.965,2.219c11.558,4.54 23.114,9.684 32.711,17.724c-7.321,-2.161 -14.583,-4.626 -22.205,-5.476c-1.456,-0.316 -2.897,-0.648 -4.323,-0.993c-6.484,-2.825 -13.531,-3.776 -20.549,-3.704c1.369,1.902 2.768,3.805 4.15,5.707c-1.383,0.345 -2.781,0.692 -4.164,1.037c-5.663,2.767 -7.163,-5.101 -11.658,-6.759c1.455,1.946 2.997,3.833 4.495,5.764c-3.443,2.162 -6.268,-1.109 -9.207,-2.464c-2.608,-1.628 -5.822,-1.325 -8.761,-1.47c2.824,-0.663 5.649,-1.353 8.502,-1.959c3.516,-0.734 7.061,-1.268 10.62,-1.83c0.187,-1.239 0.591,-3.703 0.778,-4.942c4.567,0.663 9.122,1.484 13.689,2.146c-5.893,-1.584 -11.888,-2.781 -17.94,-3.516Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M654.088,500.863c2.335,-3.501 4.597,-7.176 8.185,-9.568c-0.822,1.325 -1.628,2.666 -2.421,4.006c-2.017,3.387 -4.323,6.6 -7.06,9.439c-1.887,2.003 -3.69,4.092 -5.346,6.297c-0.216,-3.949 2.607,-6.629 5.388,-8.962c0.317,-0.303 0.938,-0.908 1.254,-1.211Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M532.382,510.402c15.591,-12.017 38.762,-13.315 55.175,-2.218c10.014,6.311 16.672,16.859 19.957,28.07c0.376,9.12 1.413,18.574 -1.772,27.349c-1.859,-5.908 -1.672,-12.595 -5.418,-17.781c-10.058,-15.65 -26.025,-27.927 -44.368,-31.861c-7.681,-2.464 -15.98,-0.778 -23.575,-3.559Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M495.263,526.397c1.124,2.695 2.104,5.448 2.421,8.373c-0.663,4.208 -1.196,8.444 -1.225,12.709c0.115,3.719 0.62,7.407 1.167,11.097c-0.937,0.604 -1.845,1.252 -2.738,1.93l-1.225,0.894c-0.951,-3.429 -2.104,-6.787 -3.631,-10.001c-3.199,1.514 -6.701,1.946 -10.159,1.211c-0.562,0.865 -1.11,1.729 -1.643,2.608c-1.888,2.117 -3.732,4.25 -5.562,6.426c-2.882,-2.708 -5.721,-5.461 -8.804,-7.94c0.375,-3.833 0.36,-8.689 4.51,-10.49c4.366,-2.32 8.689,0.793 11.427,4.077c0.548,-0.792 1.614,-2.377 2.147,-3.17c1.614,0.015 3.242,0.015 4.856,0.044c0.029,-2.161 0.072,-4.309 0.115,-6.456c1.902,-0.576 3.819,-1.138 5.735,-1.7c0.85,-3.198 1.758,-6.398 2.608,-9.612Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M543.017,552.293c-1.009,-5.232 1.081,-10.073 4.712,-13.748c0.518,3.502 0.634,7.133 1.858,10.506c2.421,5.302 8.128,9.265 14.122,8.516c5.548,0.331 8.992,-4.683 13.776,-6.513c-1.584,4.856 -3.011,10.332 -7.651,13.214c-0.922,0.331 -2.781,0.979 -3.703,1.311c-1.744,0.303 -3.488,0.547 -5.246,0.72c-7.204,-0.417 -13.616,-4.841 -17.047,-11.11c-0.215,-0.72 -0.619,-2.176 -0.821,-2.895Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M437.466,555.219c0.115,0.749 0.346,2.262 0.461,3.011c1.931,-0.086 3.876,-0.158 5.822,-0.23c0.187,1.037 0.562,3.126 0.749,4.164c-1.513,0.822 -3.04,1.615 -4.582,2.392c-1.11,-1.944 -1.859,-4.063 -2.536,-6.181c-0.807,0.13 -2.435,0.389 -3.257,0.519c0.533,-0.692 1.599,-2.061 2.133,-2.753c0.303,-0.23 0.908,-0.691 1.21,-0.922Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M445.967,559.006c2.233,-0.619 4.467,-1.498 6.83,-1.456c2.089,2.335 -0.072,4.857 -1.268,6.961c-1.945,-1.744 -3.79,-3.589 -5.562,-5.505Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M487.38,587.194c3.545,-20.678 29.54,-32.509 47.51,-21.715c2.19,1.397 4.294,2.954 6.326,4.61c1.225,1.369 2.348,2.81 3.501,4.295c-7.435,0.36 -14.9,-1.399 -22.277,0.072c-11.802,2.377 -23.012,8.79 -30.174,18.589c-2.248,3.905 -2.478,8.573 -3.559,12.867c-1.47,-5.36 -2.291,-10.936 -1.801,-16.498c0.115,-0.562 0.36,-1.672 0.475,-2.219Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M625.167,584.901c-3.198,-0.922 -0.086,-6.499 1.99,-6.743l0.101,0.792c-0.692,1.874 0.158,5.03 -2.091,5.951Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M408.287,584.442c4.047,-2.616 0.564,4.799 0,0Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M528.434,584.6c0.519,0.562 1.556,1.671 2.075,2.218c1.83,3.618 2.68,7.638 3.213,11.644c-0.504,1.195 -1.066,2.363 -1.686,3.516c-1.052,1.369 -2.147,2.739 -3.3,4.035c-2.219,1.296 -4.582,2.348 -7.032,3.084c-1.902,-0.029 -3.79,-0.116 -5.677,-0.274c-2.637,-1.096 -5.144,-2.493 -7.551,-4.02l-0.259,-0.591c0.158,-0.778 0.461,-2.349 0.62,-3.141c5,1.729 11.153,4.048 15.62,-0.216c5.26,-3.732 4.856,-10.605 3.977,-16.254Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M560.223,595.319c0.259,2.276 0.505,4.553 0.606,6.859c-0.663,3.3 -1.527,6.557 -2.335,9.814c9.179,-0.015 18.473,-1.024 27.033,-4.54c4.336,-1.83 8.472,-4.063 12.507,-6.455c2.493,-0.159 5,-0.216 7.508,-0.376c-0.894,1.514 -1.773,3.027 -2.695,4.525c2.796,2.853 6.254,4.9 9.524,7.148c-0.173,-1.125 -0.331,-2.249 -0.475,-3.358c0.938,-1.081 1.889,-2.148 2.854,-3.214c2.607,1.153 7.277,1.441 6.627,5.419c0.707,4.452 -4.38,4.222 -7.305,5.014c-0.288,6.038 7.593,3.877 10.418,7.666c-4.279,4.943 -7.104,12.667 -1.628,17.897c-1.023,1.211 -2.047,2.421 -3.055,3.646c-4.799,-1.786 -10.203,-2.565 -13.805,-6.528c-0.49,1.946 -0.519,4.079 -1.542,5.85c-1.685,1.889 -4.309,0.418 -6.383,0.361c0.619,4.235 4.208,6.916 6.715,10.071c1.527,0.389 3.055,0.778 4.582,1.182c-2.003,6.629 6.469,9.237 6.902,15.361c2.19,6.816 6.44,12.896 7.507,20.101c5.246,21.974 -7.824,46.241 -28.546,54.814c-7.464,3.142 -16.326,4.338 -23.949,1.11c-6.484,-3.214 -11.887,-9.525 -12.392,-16.946c-0.98,-11.571 8.891,-22.754 20.505,-23.215c8.731,-0.547 17.205,5.058 20.331,13.214c4.958,-6.181 9.237,-13.43 9.454,-21.572c0.806,-9.135 -2.306,-17.998 -6.412,-26.009c2.954,7.811 6.181,15.837 5.432,24.381c0.158,7.392 -3.545,13.964 -7.435,19.973c-1.615,-1.685 -3.214,-3.358 -4.813,-5.03c10.62,-11.066 8.834,-28.66 1.081,-40.779c-2.882,3.675 -3.315,8.387 -4.712,12.681c-1.917,6.109 -5.505,11.643 -10.131,16.067c-0.244,0.202 -0.72,0.606 -0.951,0.822c5.52,1.915 11.484,4.265 14.238,9.856c-4.683,-2.017 -9.266,-4.914 -14.554,-4.712c-11.009,-0.764 -21.082,8.112 -23.185,18.646c-0.606,3.833 -0.159,7.71 -0.159,11.585c-3.545,-5.504 -4.668,-12.435 -2.867,-18.732c1.138,-4.683 4.496,-8.286 7.365,-11.989c-1.067,0.606 -2.105,1.254 -3.128,1.931c-3.876,-2.882 -7.982,-5.476 -11.455,-8.834c-0.303,-0.259 -0.908,-0.764 -1.211,-1.023c-7.349,1.643 -15.187,2.579 -22.205,-0.822c-0.259,-1.195 -0.778,-3.587 -1.038,-4.784c5.706,5.808 14.15,5.259 21.485,3.69c-0.246,-2.291 -0.505,-4.582 -0.749,-6.874c-0.707,-0.461 -2.148,-1.412 -2.867,-1.873c0.922,-5 1.21,-10.375 -2.017,-14.655c1.397,-0.244 2.794,-0.475 4.193,-0.692c0.764,1.211 1.527,2.436 2.306,3.66c1.109,-0.995 2.233,-1.988 3.372,-2.968c-3.747,-3.516 -7.277,-7.306 -9.9,-11.729c2.017,0.878 4.077,1.728 6.139,2.593c-0.98,-2.306 -1.96,-4.61 -2.926,-6.916l0.274,0.215c3.401,3.387 7.983,4.756 12.725,4.208c-1.426,3.069 -2.825,6.153 -3.919,9.367c2.493,-4.309 3.385,-9.957 8.371,-12.22c0.534,0.865 1.052,1.744 1.557,2.623c1.325,-3.66 2.882,-7.248 4.077,-10.952c1.557,-7.723 1.369,-16.326 -2.695,-23.301c-3.703,-2.334 -4.309,3.891 -5.23,6.269c-1.685,-4.178 -3.141,-8.444 -4.251,-12.81c-3.356,3.458 -6.469,7.248 -10.374,10.115c-3.3,1.975 -7.047,2.998 -10.606,4.38c-0.072,1.889 -0.13,3.791 -0.173,5.692c-3.098,1.426 -5.735,3.617 -8.689,5.289c-3.084,0.98 -6.355,1.067 -9.554,1.109c-4.582,-2.032 -8.026,-5.735 -11.254,-9.452c0.173,-1.672 0.346,-3.358 0.504,-5.03c-3.891,3.185 -6.729,9.021 -12.637,8.215c-5.101,0.013 -8.026,-4.569 -10,-8.604c-1.499,0.894 -2.983,1.788 -4.467,2.68c0.202,-0.993 0.605,-2.968 0.793,-3.962c1.182,-0.044 3.53,-0.145 4.698,-0.202c-0.519,-1.974 -1.023,-3.919 -1.542,-5.865c-0.086,-1.845 -0.158,-3.674 -0.245,-5.489c0.85,-0.13 2.536,-0.361 3.372,-0.477c-0.332,-4.366 -1.845,-8.487 -3.631,-12.435c2.075,2.508 3.948,5.159 5.75,7.868c1.585,2.377 3.343,4.641 5.216,6.816c2.003,2.133 4.222,4.064 6.571,5.836c2.147,1.399 4.381,2.68 6.7,3.804c3.862,1.744 8.012,2.68 12.205,3.214c17.551,1.959 35.563,-9.482 41.024,-26.312c0.663,-2.407 1.225,-4.857 1.801,-7.277c0.549,-1.557 1.14,-3.099 1.773,-4.626Zm35.377,13.963c-0.793,0.116 -2.349,0.345 -3.142,0.461c1.283,0.62 2.579,1.211 3.906,1.773c-2.882,3.76 -2.032,8.544 1.239,11.672c-4.121,1.57 -8.516,2.176 -12.868,1.254l-1.426,-0.749c-0.058,-1.283 -0.116,-2.551 -0.173,-3.82c-0.086,0.923 -0.274,2.739 -0.361,3.66c-5.058,-1.643 -8.703,-5.577 -13.199,-8.184c3.978,4.437 7.624,10.273 12.999,12.766c6.844,2.652 14.596,3.229 21.355,-0.057l-0.549,-0.116c5.274,-2.291 8.516,-9.006 5.491,-14.266c-1.917,-4.02 -6.902,-4.826 -10.793,-3.631c-0.576,-1.412 -1.268,-2.897 -2.032,-4.178c-0.101,0.85 -0.332,2.565 -0.446,3.415Zm-13.301,4.409c-0.835,2.262 -2.737,4.251 -3.659,0.734c-0.462,0.347 -1.384,1.067 -1.845,1.413c1.167,1.353 2.335,2.693 3.516,4.063c2.739,-2.723 5.908,-5.072 8.099,-8.285c-2.061,0.634 -4.093,1.325 -6.111,2.075Zm-25.908,3.069c5.028,4.611 9.972,9.351 13.876,15c2.205,3.747 4.15,7.651 5.922,11.629c1.6,4.856 2.594,9.856 3.17,14.928c-0.173,4.05 -0.606,8.084 -1.426,12.061c-0.908,2.017 -1.816,4.05 -2.724,6.082c-2.838,-3.646 -6.6,-7.148 -11.571,-6.975c-8.847,-0.648 -16.759,6.513 -17.55,15.231c0.446,2.781 0.951,5.577 1.614,8.329c5.201,10.721 20.346,13.863 29.8,7.018c6.6,-4.841 11.224,-12.479 12.766,-20.447c4.914,-16.889 -2.045,-34.655 -10.951,-48.965l-0.505,0.057c-3.646,-5.36 -8.213,-10.245 -13.675,-13.746c-2.897,-0.461 -5.85,-0.231 -8.747,-0.202Zm13.574,40.996c-1.023,2.853 -1.124,6.412 -0.072,9.28c1.974,-1.628 1.873,-7.536 0.072,-9.28Zm41.313,15.418l0.634,1.225l-2.867,0.721l-0.202,-0.534c1.557,7.148 4.409,14.165 3.805,21.643c0.36,12.58 -7.076,23.964 -16.659,31.529c0.433,-6.917 -3.516,-13.891 -10.231,-16.168c-10.994,-4.079 -23.185,5.201 -23.949,16.456c-1.166,10.433 9.065,19.367 19.137,18.66c22.565,-0.23 41.861,-22.45 40.275,-44.757c0.259,-10.418 -3.976,-20.418 -9.943,-28.776Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M467.828,613.417c0.461,-0.417 1.355,-1.252 1.816,-1.671c2.435,1.023 4.669,2.407 6.859,3.905c-1.037,1.298 -2.06,2.579 -3.055,3.905c-1.873,-0.057 -3.746,-0.086 -5.605,-0.201l-0.519,-0.433c0.13,-1.845 0.288,-3.688 0.504,-5.505Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M446.212,641.213c-2.666,3.458 -6.34,-2.132 -2.162,-3.343c0.533,0.837 1.614,2.508 2.162,3.343Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M495.868,653.219c2.378,-2.177 4.424,-4.914 7.45,-6.21c2.651,-0.563 5.375,-0.448 8.069,-0.404c3.285,2.666 6.845,5.979 6.384,10.662c0.706,4.409 -2.723,7.58 -5.26,10.664c1.009,0.634 2.032,1.268 3.055,1.931c-0.418,0.663 -1.225,1.988 -1.628,2.651l-2.378,-1.643c-0.086,-0.057 -0.274,-0.173 -0.375,-0.23c-2.623,-1.816 -5.836,-1.614 -8.848,-1.441c-1.11,2.234 -2.219,4.481 -3.703,6.513c-3.156,-1.499 -5.865,-4.18 -7.104,-7.464l-0.144,-0.692c0.014,-1.197 0.029,-3.589 0.029,-4.784l0.101,-0.734c0.519,-0.519 1.571,-1.571 2.089,-2.091l0.504,-0.288l-0.058,-0.489l-0.576,-0.073c-0.893,-0.173 -2.666,-0.503 -3.559,-0.677l1.167,-1.195l0.591,-0.49c0.519,-0.345 1.556,-1.052 2.061,-1.412c1.009,0.547 2.032,1.109 3.041,1.671c-0.303,-1.267 -0.605,-2.521 -0.908,-3.775Z" style="fill:#050708;fill-rule:nonzero;"/><path d="M574.157,434.348c0.809,0.737 0.809,0.737 0,0Z" style="fill:#212122;fill-rule:nonzero;"/><path d="M597.169,445.833c0.854,0.694 0.854,0.694 0,0Z" style="fill:#212122;fill-rule:nonzero;"/><path d="M593.019,434.377c0.781,0.708 0.781,0.708 0,0Z" style="fill:#101010;fill-rule:nonzero;"/><path d="M610.57,446.105l0.044,0.103l0,0.103c0,-0.059 -0.029,-0.148 -0.044,-0.207Z" style="fill:#101010;fill-rule:nonzero;"/><path d="M609.36,455.632l0.361,0.275l-0.13,0.39c-0.057,-0.159 -0.174,-0.491 -0.231,-0.665Z" style="fill:#101010;fill-rule:nonzero;"/><path d="M608.724,434.32c4.323,-2.298 0.275,4.814 0,0Z" style="fill:#696c71;fill-rule:nonzero;"/><path d="M442.697,435.53c5.922,5.922 10.937,12.94 18.257,17.292c0.591,0.389 1.772,1.182 2.363,1.585l-0.605,1.052c-1.787,-0.173 -3.559,-0.476 -5.346,-0.519c-2.608,1.398 -3.934,4.28 -5.577,6.585c-7.003,-4.971 -13.387,-10.836 -19.107,-17.234c3.257,-3.012 6.6,-5.922 10.015,-8.761Z" style="fill:#506a83;fill-rule:nonzero;"/><path d="M548.667,435.703l0.015,0l0.159,0.247c-0.044,-0.058 -0.13,-0.189 -0.174,-0.247Z" style="fill:#111112;fill-rule:nonzero;"/><path d="M592.919,440.471l0.347,0.087l-0.13,0.145c-0.057,-0.058 -0.174,-0.173 -0.216,-0.231Z" style="fill:#111112;fill-rule:nonzero;"/><path d="M614.431,436.984c2.594,-1.758 5.706,-1.167 8.602,-0.677c0.029,5.447 -0.015,10.908 -0.418,16.37c-0.375,1.196 -0.764,2.392 -1.181,3.574c-0.591,0.375 -1.758,1.11 -2.349,1.47c-0.418,-2.594 -5.447,-2.248 -3.559,0.576c0.663,0.072 2.003,0.231 2.68,0.303c-0.993,0.951 -2.003,1.887 -2.996,2.825l-0.534,-0.519c-2.464,-3.084 -0.907,-7.45 -1.369,-11.11l0.015,-0.721l-0.692,-0.029l-0.043,0.721c-0.072,1.095 -0.202,3.285 -0.274,4.38c-1.125,-0.202 -3.387,-0.591 -4.51,-0.793c1.008,-1.571 2.017,-3.17 3.011,-4.77c-0.057,-0.562 -0.143,-1.715 -0.201,-2.277l0,-0.101l-0.044,-0.101l-0.936,-2.81l-0.62,-1.816c0.85,0.158 2.537,0.447 3.387,0.591l0.663,2.219l1.426,-0.072c-2.104,-2.205 -1.744,-4.914 -0.057,-7.234Z" style="fill:#496075;fill-rule:nonzero;"/><path d="M554.92,440.097c1.096,-1.311 2.176,-2.623 3.227,-3.963c3.027,6.398 8.978,10.476 14.209,14.929c3.804,4.568 8.487,8.329 12.004,13.156c-10.693,-6.917 -19.367,-16.398 -29.44,-24.122Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M643.495,451.612l0.317,-0.576c1.397,1.11 2.594,2.406 3.587,3.905c-0.316,0.331 -0.951,1.009 -1.267,1.34c-0.677,-0.259 -2.032,-0.764 -2.709,-1.023c0.029,-0.908 0.057,-2.738 0.072,-3.646Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M492.077,489.178l0.605,1.643c1.499,4.323 4.179,8.617 8.675,10.245c1.787,0.49 3.602,0.907 5.433,1.267c-0.115,1.226 -0.259,2.436 -0.375,3.675c-3.127,-1.311 -6.542,-1.47 -9.871,-1.456c-6.095,-0.375 -12.047,-1.757 -18.012,-2.895c-6.067,-1.11 -11.917,-3.041 -17.825,-4.655c1.7,-0.606 3.43,-1.167 5.159,-1.7c7.911,3.026 16.312,4.222 24.612,5.678c2.493,0.677 5.043,0.979 7.623,1.166c-2.19,-2.579 -4.467,-5.1 -6.701,-7.637c0.173,-1.34 0.519,-3.991 0.677,-5.331Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M619.851,507.765c0.576,-0.259 1.729,-0.764 2.291,-1.008c1.974,1.628 2.291,4.611 -0.707,5.274c-0.432,-1.311 -2.954,-3.012 -1.584,-4.266Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M515.307,552.263c-2.133,-15.721 4.481,-32.177 17.076,-41.861c7.594,2.781 15.894,1.096 23.575,3.559c18.343,3.934 34.31,16.211 44.367,31.86c3.747,5.188 3.559,11.874 5.419,17.783c-2.998,7.896 -8.07,14.985 -14.626,20.331c-8.272,6.774 -18.791,9.784 -29.223,11.053c-2.478,3.545 4.323,2.162 5.836,3.559l0.015,0.244c-2.652,-0.835 -4.352,-0.375 -5.116,1.426c1.037,1.485 3.343,1.14 4.942,1.701c-2.247,0.259 -4.509,0.23 -6.743,0.259c-0.101,-2.306 -0.347,-4.582 -0.606,-6.859c-0.634,1.527 -1.225,3.069 -1.772,4.625c0.015,-0.907 0.072,-2.723 0.101,-3.631c0.028,-2.247 0,-4.496 -0.116,-6.743c-0.36,-2.263 -0.764,-4.496 -1.34,-6.7c-0.36,-0.923 -1.052,-2.768 -1.399,-3.675c-1.037,-2.464 -1.368,-5.116 -1.34,-7.752c3.761,5.865 6.01,12.522 7.365,19.323c1.166,-0.129 3.516,-0.404 4.683,-0.533c1.181,-0.187 3.545,-0.549 4.726,-0.736c1.96,-0.446 3.906,-0.979 5.85,-1.599c5.52,-2.205 10.39,-5.75 14.669,-9.842c3.473,-3.646 8.314,-8.387 6.139,-13.891c-2.508,-7.796 -6.974,-14.827 -12.061,-21.183c-1.83,-2.407 -4.554,-3.804 -7.191,-5.144c-3.141,-5.202 -8.386,-8.733 -14.309,-9.972c-9.755,-6.772 -22.435,-6.499 -33.776,-5.779c-7.118,0.375 -9.323,8.602 -11.067,14.223c-1.124,5.216 -1.542,10.561 -1.917,15.879l-2.161,0.072Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M641.925,549.727l0.23,1.298c-0.057,1.946 -0.619,3.775 -1.656,5.491c1.656,0.936 3.429,1.555 5.259,2.132l0.446,1.47c-2.594,-0.505 -5.173,-0.951 -7.767,-1.384c1.167,-2.996 2.377,-5.979 3.488,-9.006Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M548.479,603.432l0.806,0.505l0.317,1.096c-2.104,6.728 -7.292,11.657 -12.09,16.527c-0.49,-0.519 -0.966,-1.023 -1.441,-1.542c1.628,-1.254 3.214,-2.535 4.784,-3.862c1.283,-1.383 2.493,-2.867 3.646,-4.366c0.461,-0.808 1.399,-2.436 1.859,-3.257c0.764,-1.672 1.412,-3.401 2.118,-5.101Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M555.25,633.578c1.296,1.988 2.55,4.035 3.775,6.095c-0.72,2.003 -1.412,4.035 -2.089,6.067c-5.331,-4.338 -15.332,-3.905 -16.845,4.035c0.244,1.412 0.547,2.824 0.907,4.237c0.275,0.663 0.808,1.974 1.081,2.622c0.966,2.306 1.946,4.611 2.926,6.917c-2.061,-0.865 -4.121,-1.715 -6.139,-2.594c-0.648,-0.49 -1.917,-1.456 -2.551,-1.931c0.879,-0.591 1.773,-1.167 2.667,-1.729c-0.073,-4.424 0.057,-8.832 0.331,-13.228c3.141,-1.555 6.398,-2.867 9.741,-3.934c1.917,0.231 3.833,0.433 5.75,0.62c0.404,0.576 1.211,1.729 1.615,2.306c-0.477,-3.156 -0.894,-6.311 -1.167,-9.482Z" style="fill:#1d1e1e;fill-rule:nonzero;"/><path d="M576.737,437.028c4.784,0.043 -2.068,4.423 0,0Z" style="fill:#363739;fill-rule:nonzero;"/><path d="M470.364,627.366c0.824,0.738 0.824,0.738 0,0Z" style="fill:#080808;fill-rule:nonzero;"/><path d="M596.046,436.626c0.345,0.332 1.023,1.009 1.355,1.355l0.389,2.234c1.656,-1.225 3.328,-2.435 5.058,-3.574c-0.549,1.902 -1.801,3.415 -3.2,4.77c0.591,1.527 1.182,3.055 1.788,4.582c-1.889,-2.637 -3.891,-5.259 -6.5,-7.234c0.288,-0.533 0.837,-1.599 1.11,-2.133Z" style="fill:#222;fill-rule:nonzero;"/><path d="M522.438,574.455c7.378,-1.47 14.842,0.288 22.277,-0.072l0.519,0.692c1.225,1.772 2.377,3.602 3.444,5.489c2.651,5.52 3.3,11.787 2.464,17.825c-0.23,1.254 -0.677,3.761 -0.907,5.03c-0.375,-2.19 -0.736,-4.382 -0.879,-6.586c-0.173,-4.423 0.202,-9.524 -3.099,-12.983c-6.08,-3.617 -13.473,-2.104 -20.173,-2.636c-7.652,-3.243 -17.306,-0.088 -20.087,8.112c-0.418,0.505 -1.239,1.527 -1.657,2.032c-4.107,4.336 -8.819,8.573 -10.779,14.409c-0.994,2.767 1.081,5.289 2.363,7.565c2.06,2.407 4.352,4.626 6.845,6.572c2.723,1.513 5.533,2.867 8.444,4.006c1.124,0.173 3.343,0.547 4.467,0.72c1.066,0.058 3.213,0.187 4.294,0.246c1.182,-0.086 3.53,-0.246 4.712,-0.332c3.804,-0.764 7.392,-2.291 10.923,-3.905c-0.303,0.634 -0.908,1.902 -1.211,2.55c-3.4,1.052 -6.772,2.162 -10.26,2.854c-8.026,0.475 -16.283,-0.677 -23.113,-5.173c-1.917,-1.312 -3.775,-2.724 -5.533,-4.222c-2.795,-2.867 -4.928,-6.298 -6.528,-9.944l-0.259,-0.792c1.081,-4.294 1.311,-8.964 3.559,-12.868c7.162,-9.799 18.372,-16.211 30.174,-18.589Z" style="fill:#222;fill-rule:nonzero;"/><path d="M576.894,601.617c0.98,-0.015 2.926,-0.044 3.891,-0.058c-0.029,1.889 0.331,4.121 -1.946,4.799c-1.383,-1.225 -1.571,-3.069 -1.946,-4.74Z" style="fill:#222;fill-rule:nonzero;"/><path d="M611.507,437.561c0.825,0.781 0.825,0.781 0,0Z" style="fill:#7d8086;fill-rule:nonzero;"/><path d="M587.053,438.526c0.766,0.752 0.766,0.752 0,0Z" style="fill:#c9d8e5;fill-rule:nonzero;"/><path d="M658.643,438.197c6.268,0.303 16.037,-0.259 17.55,7.825c-2.895,-0.778 -5.706,-1.787 -8.632,-2.363c-2.521,2.349 -4.74,5 -7.262,7.349c-0.274,-4.265 2.638,-9.741 -1.656,-12.81Z" style="fill:#4b6fb2;fill-rule:nonzero;"/><path d="M584.288,441.438c0.78,0.795 0.78,0.795 0,0Z" style="fill:#0f0f0f;fill-rule:nonzero;"/><path d="M588.841,441.235c2.421,-0.922 4.799,2.853 3.486,4.87c-1.887,-0.115 -3.933,-3.026 -3.486,-4.87Z" style="fill:#5b5d61;fill-rule:nonzero;"/><path d="M581.319,443.08c0.723,0.752 0.723,0.752 0,0Z" style="fill:#424346;fill-rule:nonzero;"/><path d="M594.258,442.737c4.206,-2.428 0.462,4.799 0,0Z" style="fill:#bcc7d0;fill-rule:nonzero;"/><path d="M537.816,444.088c5.735,3.17 11.585,6.168 17.579,8.833c-1.311,2.637 -2.608,5.274 -3.919,7.926c-5.807,-1.902 -11.615,-3.862 -17.249,-6.254c1.138,-3.53 2.335,-7.018 3.589,-10.505Z" style="fill:#f49426;fill-rule:nonzero;"/><path d="M625.702,444.046c1.902,-0.058 3.818,-0.115 5.75,-0.173c-0.159,2.147 -0.36,4.28 -0.606,6.427c-0.606,4.669 -1.225,9.352 -2.089,13.991c-1.7,0.101 -3.387,0.231 -5.072,0.347c1.195,-6.802 1.988,-13.675 2.017,-20.592Z" style="fill:#435566;fill-rule:nonzero;"/><path d="M659.204,454.694c2.493,-3.113 5.144,-6.11 7.867,-9.035c-0.663,4.654 -1.311,9.352 -2.939,13.79c-1.786,4.625 -3.775,9.179 -5.893,13.675c-1.83,3.848 -3.704,7.709 -6.139,11.211c-1.096,-1.138 -2.177,-2.263 -3.257,-3.387c4.265,-8.386 8.257,-17.032 10.361,-26.254Z" style="fill:#435566;fill-rule:nonzero;"/><path d="M640.715,500.907c3.732,-4.712 6.643,-10.001 10.159,-14.857c4.496,-0.231 8.516,1.83 12.494,3.631c-0.274,0.404 -0.822,1.21 -1.096,1.614c-3.587,2.392 -5.85,6.067 -8.184,9.568c-0.317,0.303 -0.938,0.908 -1.254,1.211c-1.384,-1.153 -2.84,-2.205 -4.38,-3.113c-2.638,0.418 -5.173,1.254 -7.739,1.946Z" style="fill:#435566;fill-rule:nonzero;"/><path d="M500.365,445.875c1.715,0.216 3.43,0.432 5.159,0.663c3.458,4.957 6.513,10.173 9.467,15.433c-5.447,2.579 -10.995,5 -15.995,8.43c-6.7,-3.516 -12.911,-7.868 -19.208,-12.047c1.715,-1.657 3.415,-3.329 5.144,-4.971l0.259,-0.259c2.118,1.455 4.28,2.868 6.513,4.164c0.101,-2.075 0.216,-4.136 0.332,-6.196l-2.219,-0.014l1.095,-1.081c0.447,-0.418 1.34,-1.239 1.787,-1.643c0.086,0.504 0.274,1.499 0.375,2.003c-0.014,2.565 -0.072,5.159 0.346,7.695c0.159,0.403 0.476,1.21 0.634,1.614c2.551,1.614 5.389,2.724 8.257,3.689c-0.965,-1.658 -1.902,-3.3 -2.925,-4.886c-1.398,-2.003 -2.896,-3.919 -4.438,-5.793c1.772,-2.291 3.588,-4.553 5.418,-6.801Z" style="fill:#394653;fill-rule:nonzero;"/><path d="M667.071,445.659c4.424,0.043 8.589,1.671 12.767,2.954c-1.384,5.692 -3.227,11.24 -4.971,16.816c-3.429,-2.262 -7.018,-4.25 -10.735,-5.98c1.628,-4.438 2.276,-9.136 2.939,-13.79Z" style="fill:#5b7fa3;fill-rule:nonzero;"/><path d="M594.088,447.287c4.784,0.188 -2.182,4.351 0,0Z" style="fill:#585a5e;fill-rule:nonzero;"/><path d="M607.4,447.446c0.796,0.781 0.796,0.781 0,0Z" style="fill:#0d0d0d;fill-rule:nonzero;"/><path d="M440.146,500.908c0.795,0.765 0.795,0.765 0,0Z" style="fill:#0d0d0d;fill-rule:nonzero;"/><path d="M536.82,664.92c2.176,-0.576 5.23,1.729 5.144,4.02c-2.104,0.345 -5.432,-1.744 -5.144,-4.02Z" style="fill:#0d0d0d;fill-rule:nonzero;"/><path d="M590.108,449.984c4.235,-2.4 0.42,4.77 0,0Z" style="fill:#a1a7ad;fill-rule:nonzero;"/><path d="M604.49,450.084c0.781,0.766 0.781,0.766 0,0Z" style="fill:#1e1e1f;fill-rule:nonzero;"/><path d="M547.902,634.874c1.98,-0.651 2.732,0.044 2.27,2.096c-1.995,0.651 -2.747,-0.058 -2.27,-2.096Z" style="fill:#1e1e1f;fill-rule:nonzero;"/><path d="M612.588,449.766l0.721,0.029c0.461,3.66 -1.096,8.026 1.369,11.109l0.533,0.519c0.303,0.303 0.908,0.908 1.197,1.21c-0.576,-0.057 -1.758,-0.173 -2.349,-0.23c-4.668,-1.845 -2.464,6.844 -7.003,4.178c0.303,-3.545 1.542,-6.902 2.537,-10.288l0.129,-0.389l-0.36,-0.274c-0.835,-0.634 -2.493,-1.931 -3.314,-2.565l1.757,0.288c1.125,0.202 3.387,0.591 4.51,0.793c0.073,-1.095 0.202,-3.285 0.274,-4.381Z" style="fill:#fffcfd;fill-rule:nonzero;"/><path d="M628.754,464.291c0.865,-4.64 1.485,-9.323 2.091,-13.991c0.475,6.167 6.642,8.314 11.512,10.274c-0.028,0.936 -0.072,2.796 -0.086,3.732c-0.114,1.672 -0.259,3.343 -0.446,5.015c-0.475,-4.05 -3.776,0.158 -0.475,1.094l0.519,0.677c-4.943,0.058 -8.791,-4.063 -13.834,-3.486c0.173,-0.837 0.533,-2.493 0.72,-3.315Z" style="fill:#4e6880;fill-rule:nonzero;"/><path d="M493.073,450.356c0.461,0.576 1.412,1.744 1.873,2.32c1.542,1.873 3.041,3.79 4.438,5.793c-1.499,-0.101 -4.481,-0.317 -5.966,-0.418c-0.418,-2.536 -0.36,-5.13 -0.346,-7.695Z" style="fill:#83a6c6;fill-rule:nonzero;"/><path d="M595.857,451.567c0.708,0.766 0.708,0.766 0,0Z" style="fill:#444649;fill-rule:nonzero;"/><path d="M601.279,451.481c4.741,0.246 -2.211,4.365 0,0Z" style="fill:#46484b;fill-rule:nonzero;"/><path d="M555.395,452.923c5.491,2.507 10.851,5.274 16.182,8.113c-0.865,2.319 -1.729,4.654 -2.579,6.989c-5.676,-2.781 -11.527,-5.202 -17.522,-7.176c1.311,-2.652 2.608,-5.289 3.919,-7.926Z" style="fill:#f27e25;fill-rule:nonzero;"/><path d="M648.743,452.174c2.983,0.749 5.88,1.902 7.912,4.337c-1.658,5.057 -3.358,10.102 -5.562,14.957c-2.19,-1.138 -4.367,-2.247 -6.542,-3.372c-0.057,-0.922 -0.202,-2.737 -0.274,-3.66c2.594,-1.786 3.574,-5.446 1.859,-8.155c0.316,-0.331 0.951,-1.009 1.267,-1.34c0.332,-0.692 0.995,-2.075 1.34,-2.767Z" style="fill:#516c87;fill-rule:nonzero;"/><path d="M457.365,454.939c1.787,0.043 3.559,0.346 5.346,0.519l0.605,-1.052c9.741,7.133 19.179,14.698 29.54,20.952c-1.196,2.104 -3.069,4.006 -3.43,6.455c1.066,1.124 2.19,2.205 3.358,3.257c-0.072,1.917 -0.115,3.833 -0.101,5.75l-0.605,-1.643c-0.072,-0.806 -0.231,-2.407 -0.303,-3.213c-0.62,0.173 -1.844,0.49 -2.464,0.648c-1.599,-2.32 -3.257,-4.597 -4.928,-6.844c-0.447,0.576 -1.34,1.729 -1.787,2.306c-1.945,-1.889 -3.934,-3.761 -6.066,-5.433c-2.45,-1.094 -5.029,-1.873 -7.695,-2.19c-4.971,-5.245 -11.946,-7.91 -17.047,-12.926c1.643,-2.306 2.968,-5.187 5.577,-6.585Z" style="fill:#3f4f5e;fill-rule:nonzero;"/><path d="M643.425,455.257c0.677,0.259 2.032,0.764 2.709,1.023c1.715,2.709 0.734,6.369 -1.859,8.155c-0.503,-0.028 -1.498,-0.101 -2.003,-0.129c0.015,-0.936 0.058,-2.796 0.086,-3.732c0.332,-1.773 0.677,-3.545 1.067,-5.318Z" style="fill:#b7d2eb;fill-rule:nonzero;"/><path d="M600.253,457.448c0.709,0.752 0.709,0.752 0,0Z" style="fill:#afb4ba;fill-rule:nonzero;"/><path d="M619.088,457.721c0.591,-0.36 1.757,-1.095 2.348,-1.47c-0.979,10.332 -3.299,20.433 -5.188,30.606c-2.291,0.202 -2.146,4.395 0.317,2.911c0.173,-0.519 0.547,-1.586 0.734,-2.104c2.911,-7.436 5.116,-15.13 6.384,-23.027c1.685,-0.116 3.372,-0.244 5.072,-0.345c-0.187,0.821 -0.547,2.478 -0.721,3.314c-0.951,4.121 -1.858,8.242 -2.809,12.378c-0.793,2.983 -1.527,6.196 -3.949,8.357c-0.576,0.015 -1.744,0.044 -2.32,0.058c0.576,3.17 -4.063,7.752 1.009,8.718c-0.404,0.936 -1.197,2.796 -1.6,3.732c-1.786,-1.687 -3.53,-3.415 -5.245,-5.159l1.153,-0.778c2.81,-0.749 1.21,-4.309 -1.268,-3.328c-2.118,-2.379 -2.781,-5.678 -3.343,-8.718c1.672,-5.303 2.954,-10.708 4.294,-16.081l0,-0.778c0.029,-0.908 0.073,-2.695 0.101,-3.603c0.591,0.058 1.773,0.173 2.349,0.231c-0.288,-0.303 -0.894,-0.908 -1.197,-1.21c0.995,-0.938 2.003,-1.874 2.998,-2.825l0.879,-0.879Z" style="fill:#36424d;fill-rule:nonzero;"/><path d="M494.052,459.666c-0.159,-0.403 -0.476,-1.21 -0.634,-1.614c1.484,0.101 4.467,0.317 5.966,0.418c1.023,1.585 1.96,3.228 2.925,4.884c-2.868,-0.966 -5.706,-2.075 -8.257,-3.688Z" style="fill:#151719;fill-rule:nonzero;"/><path d="M484.613,492.519c1.254,-2.148 7.248,-1.369 4.179,1.296c-1.052,-0.331 -3.141,-0.979 -4.179,-1.296Z" style="fill:#151719;fill-rule:nonzero;"/><path d="M602.992,460.257c0.726,0.741 0.726,0.741 0,0Z" style="fill:#48494c;fill-rule:nonzero;"/><path d="M658.236,473.125c2.118,-4.496 4.108,-9.049 5.894,-13.675c3.717,1.729 7.306,3.717 10.735,5.981c-1.167,2.622 -2.133,5.403 -3.89,7.709c-1.096,0.865 -2.234,1.685 -3.344,2.522c-0.259,0.764 -0.806,2.306 -1.065,3.084c-0.475,-0.793 -1.413,-2.349 -1.874,-3.128c-2.161,-0.835 -4.309,-1.671 -6.456,-2.493Z" style="fill:#53718d;fill-rule:nonzero;"/><path d="M571.576,461.036c5.491,3.084 10.794,6.484 15.866,10.231c-0.778,2.104 -1.542,4.222 -2.291,6.341c-5.101,-3.646 -10.548,-6.801 -16.154,-9.583c0.85,-2.335 1.715,-4.668 2.579,-6.989Z" style="fill:#f16624;fill-rule:nonzero;"/><path d="M599.794,461.813c4.785,0.13 -2.154,4.364 0,0Z" style="fill:#585b5f;fill-rule:nonzero;"/><path d="M421.167,463.514c1.917,1.6 3.819,3.229 5.706,4.885c2.954,-1.498 5.908,-2.954 8.92,-4.336c-1.398,2.895 -2.925,5.735 -3.991,8.775c1.816,2.666 4.179,4.9 6.398,7.233c-0.692,0.044 -2.075,0.13 -2.767,0.189c-2.19,-0.26 -4.366,-0.477 -6.542,-0.664c-2.306,2.623 -4.309,5.491 -6.34,8.329c-0.303,-1.959 -0.548,-3.933 -0.778,-5.893c-0.115,-0.908 -0.331,-2.752 -0.447,-3.66c-1.859,-0.835 -3.761,-1.57 -5.62,-2.377l0.418,-1.34c1.571,-1.096 3.415,-1.83 4.813,-3.156c0.245,-2.666 0.187,-5.331 0.231,-7.983Zm0.893,14.77c0.245,2.335 0.49,4.668 0.749,7.018c1.859,-1.83 3.314,-3.978 4.496,-6.283c2.666,0.073 5.332,0.145 7.997,0.216c-1.989,-2.622 -3.977,-5.245 -5.706,-8.041c-3.199,1.557 -5.548,4.208 -7.536,7.09Z" style="fill:#b9cce7;fill-rule:nonzero;"/><path d="M435.793,464.061c1.196,1.498 2.392,2.983 3.646,4.452c2.464,2.926 5.188,5.678 8.214,8.041c-1.427,1.153 -2.853,2.335 -4.265,3.502c0.476,1.715 0.951,3.444 1.412,5.173c-3.055,-1.788 -6.153,-3.488 -9.366,-4.971c0.692,-0.058 2.075,-0.145 2.767,-0.187c-2.219,-2.335 -4.582,-4.569 -6.398,-7.234c1.066,-3.04 2.594,-5.88 3.991,-8.775Z" style="fill:#5d81a7;fill-rule:nonzero;"/><path d="M540.091,649.775c7.493,0 15.001,0.187 22.495,0.648c-0.865,1.584 -1.816,3.126 -2.796,4.668c-6.254,-0.518 -12.508,-1.397 -18.791,-1.08c-0.36,-1.413 -0.663,-2.825 -0.908,-4.237Z" style="fill:#5d81a7;fill-rule:nonzero;"/><path d="M642.272,464.306c0.505,0.029 1.498,0.101 2.003,0.13c0.072,0.922 0.216,2.737 0.274,3.659c-0.505,0.764 -1.542,2.291 -2.06,3.069l-0.62,-0.072l-0.518,-0.677l0.475,-1.094c0.187,-1.672 0.331,-3.343 0.446,-5.015Z" style="fill:#131516;fill-rule:nonzero;"/><path d="M443.044,465.935c0.694,0.781 0.694,0.781 0,0Z" style="fill:#3a3a3b;fill-rule:nonzero;"/><path d="M448.806,465.992c0.694,0.794 0.694,0.794 0,0Z" style="fill:#424142;fill-rule:nonzero;"/><path d="M517.282,466.87c3.084,-0.648 6.211,-1.109 9.352,-1.412c0.144,2.421 0.274,4.841 0.389,7.277c-2.853,0.606 -5.677,1.296 -8.502,1.959c-0.403,-2.622 -0.821,-5.23 -1.239,-7.824Z" style="fill:#34404b;fill-rule:nonzero;"/><path d="M526.633,465.458c3.934,-0.244 7.882,-0.086 11.787,0.505c-0.187,1.239 -0.591,3.703 -0.778,4.942c-3.559,0.563 -7.103,1.096 -10.619,1.83c-0.115,-2.434 -0.245,-4.856 -0.389,-7.277Z" style="fill:#22282d;fill-rule:nonzero;"/><path d="M613.164,466.035l0.794,-0.028l0,0.78l-0.78,0.015l-0.015,-0.766Z" style="fill:#fef4f8;fill-rule:nonzero;"/><path d="M502.094,474.795c4.525,-3.53 9.64,-6.368 15.188,-7.925c0.418,2.594 0.836,5.202 1.239,7.824c-3.141,0.764 -5.533,2.983 -7.853,5.087c2.392,1.614 4.798,3.229 7.205,4.828c0.403,2.32 0.778,4.639 1.182,6.974c-0.965,0.778 -1.902,1.614 -2.81,2.464c-1.657,1.586 -3.256,3.214 -4.942,4.756c-2.291,-8.286 -5.908,-16.096 -9.208,-24.008Z" style="fill:#516b86;fill-rule:nonzero;"/><path d="M628.035,467.606c5.044,-0.575 8.892,3.545 13.834,3.488l0.619,0.072c0.519,-0.778 1.557,-2.306 2.061,-3.069c2.176,1.125 4.352,2.234 6.542,3.372c-2.335,4.914 -4.828,9.77 -7.739,14.396c-2.565,-1.96 -5.129,-3.89 -7.651,-5.894c0.677,2.695 1.931,5.692 -0.49,7.926c-1.599,-4.54 -5.489,-6.989 -9.985,-7.912c0.951,-4.136 1.858,-8.257 2.809,-12.378Zm12.538,5.303c-1.758,0.835 -3.603,3.17 -2.709,5.173c2.377,1.586 5.605,-4.567 2.709,-5.173Z" style="fill:#475c70;fill-rule:nonzero;"/><path d="M453.633,494.853c2.32,0.822 4.698,1.485 7.075,2.148c5.908,1.614 11.758,3.545 17.825,4.654c-2.248,2.004 -4.222,4.295 -5.922,6.788l-0.922,-0.101c-4.597,-0.534 -9.179,-1.226 -13.79,-1.715l-0.245,-0.044c-2.623,-1.037 -5.187,-2.161 -7.781,-3.227c-0.764,-0.303 -2.306,-0.923 -3.084,-1.239c4.352,0.086 6.96,-3.142 6.845,-7.264Z" style="fill:#475c70;fill-rule:nonzero;"/><path d="M641.354,470.424c-3.31,-0.939 0,-5.16 0.477,-1.098l-0.477,1.098Z" style="fill:#bacedf;fill-rule:nonzero;"/><path d="M439.441,468.513c2.551,1.052 4.871,2.579 7.075,4.237c0.965,-0.952 1.931,-1.889 2.911,-2.84c0.576,0.808 1.7,2.421 2.277,3.214c1.052,-0.865 2.118,-1.744 3.184,-2.608c1.974,1.786 3.934,3.602 5.836,5.461c-0.821,2.521 -1.585,5.807 2.205,5.979c-0.187,0.606 -0.562,1.83 -0.749,2.436c-0.418,-0.015 -1.254,-0.057 -1.671,-0.072c-4.568,-2.104 -8.833,-4.784 -12.854,-7.767c-3.026,-2.364 -5.75,-5.116 -8.214,-8.041Zm9.41,6.038c0.706,0.764 0.706,0.764 0,0Zm2.781,2.996c0.764,0.721 0.764,0.721 0,0Z" style="fill:#39383a;fill-rule:nonzero;"/><path d="M445.823,468.902c0.766,0.737 0.766,0.737 0,0Z" style="fill:#414143;fill-rule:nonzero;"/><path d="M463.274,474.551c0.723,0.752 0.723,0.752 0,0Z" style="fill:#414143;fill-rule:nonzero;"/><path d="M476.53,476.64c2.133,1.672 4.121,3.545 6.067,5.432c0.447,-0.576 1.34,-1.729 1.787,-2.306c1.672,2.249 3.329,4.525 4.928,6.845c0.62,-0.158 1.844,-0.475 2.464,-0.648c0.072,0.806 0.231,2.407 0.303,3.213c-0.159,1.34 -0.504,3.992 -0.677,5.333c-0.648,-0.174 -1.96,-0.519 -2.608,-0.692c3.069,-2.666 -2.925,-3.444 -4.179,-1.298c-2.363,-0.778 -4.77,-1.426 -7.162,-2.06c-3.069,-0.808 -6.124,-1.687 -9.049,-2.897c-1.066,-2.06 -2.104,-4.121 -3.127,-6.196c1.239,1.008 2.478,2.032 3.718,3.055c2.493,-2.608 5.015,-5.202 7.536,-7.782Z" style="fill:#414143;fill-rule:nonzero;"/><path d="M615.73,540.189c0.215,1.571 0.402,3.156 0.562,4.755c0,2.508 -0.13,5.03 -0.361,7.537c-0.244,2.031 -0.59,4.035 -0.878,6.08c0.764,-0.505 2.262,-1.513 3.026,-2.017c0.778,0.418 2.32,1.268 3.084,1.7c0.317,1.167 0.648,2.349 0.98,3.545c-0.995,0.389 -1.99,0.793 -2.968,1.197c-1.615,-1.397 -3.214,-2.781 -4.828,-4.165c-0.317,0.995 -0.966,2.983 -1.296,3.992c-1.931,3.646 -3.66,7.392 -4.842,11.354c-1.484,2.926 -3.242,5.72 -5.591,8.013c-6.412,4.927 -12.651,10.317 -20.26,13.3c-3.976,1.469 -8.285,3.646 -12.595,2.348c3.113,-2.348 7.335,-2.478 10.866,-4.05c14.64,-5.561 25.318,-18.646 29.93,-33.387c0.417,0.418 1.252,1.268 1.671,1.685c2.061,-7.176 0.317,-14.985 3.502,-21.888Z" style="fill:#414143;fill-rule:nonzero;"/><path d="M582.298,613.693c2.017,-0.749 4.05,-1.441 6.109,-2.075c-2.19,3.213 -5.36,5.561 -8.098,8.285c-1.182,-1.369 -2.349,-2.709 -3.516,-4.064c0.461,-0.345 1.383,-1.065 1.843,-1.412c0.923,3.517 2.825,1.527 3.66,-0.734Z" style="fill:#414143;fill-rule:nonzero;"/><path d="M451.602,468.845c0.752,0.723 0.752,0.723 0,0Z" style="fill:#2e2f30;fill-rule:nonzero;"/><path d="M457.41,468.917c0.824,0.649 0.824,0.649 0,0Z" style="fill:#434243;fill-rule:nonzero;"/><path d="M605.571,469.787c0.015,-0.246 0.015,-0.709 0.015,-0.955l-0.015,0.955Z" style="fill:#404245;fill-rule:nonzero;"/><path d="M605.586,468.833c2.891,-2.63 2.776,3.658 -0.015,0.955l0.015,-0.955Z" style="fill:#b3afb5;fill-rule:nonzero;"/><path d="M678.759,468.383c1.138,2.047 2.233,4.107 3.314,6.196c1.816,0.677 3.617,1.327 5.447,2.003c-1.023,1.413 -2.032,2.825 -3.026,4.237c-2.162,-1.21 -4.468,-2.132 -6.816,-2.867l-1.211,0.433c-0.576,0.187 -1.728,0.576 -2.304,0.778c-2.219,-1.096 -4.367,-2.335 -6.528,-3.502c1.109,-0.835 2.247,-1.658 3.343,-2.522l-0.216,1.687c1.384,-0.044 4.178,-0.116 5.577,-0.145c0.793,-2.104 1.599,-4.208 2.421,-6.297Z" style="fill:#c5d5ec;fill-rule:nonzero;"/><path d="M460.391,471.77c0.781,0.708 0.781,0.708 0,0Z" style="fill:#434244;fill-rule:nonzero;"/><path d="M587.442,471.265c10.937,8.604 21.529,18.575 27.956,31.068c-1.744,-1.816 -3.502,-3.617 -5.333,-5.331c-4.639,-4.496 -9.985,-8.156 -15.562,-11.398c-0.576,-0.389 -1.758,-1.197 -2.335,-1.586c-2.334,-2.161 -4.553,-4.423 -7.018,-6.412c0.75,-2.118 1.514,-4.235 2.291,-6.341Z" style="fill:#ef4823;fill-rule:nonzero;"/><path d="M531.995,472.866c4.496,1.658 5.995,9.525 11.657,6.759c-0.143,1.181 -0.432,3.559 -0.562,4.74c-2.68,0.923 -5.346,1.859 -7.94,2.998c-0.173,-1.124 -0.518,-3.387 -0.692,-4.51c0.98,-0.404 2.955,-1.21 3.934,-1.614c-0.634,-0.879 -1.267,-1.744 -1.902,-2.608c-1.498,-1.931 -3.04,-3.818 -4.496,-5.764Z" style="fill:#8d8d8e;fill-rule:nonzero;"/><path d="M652.099,484.334c2.436,-3.501 4.309,-7.363 6.139,-11.211c2.148,0.822 4.295,1.658 6.456,2.493c0.461,0.778 1.397,2.335 1.873,3.128c-0.316,1.109 -0.936,3.358 -1.252,4.481c-0.576,2.176 -1.197,4.338 -1.946,6.456c-3.978,-1.802 -7.998,-3.862 -12.494,-3.632l1.225,-1.715Z" style="fill:#4b6278;fill-rule:nonzero;"/><path d="M413.833,476.539c0.749,-0.649 1.513,-1.268 2.291,-1.889l-0.418,1.34c1.859,0.808 3.761,1.542 5.62,2.377c0.115,0.908 0.331,2.753 0.447,3.66c-2.867,-1.498 -5.447,-3.444 -7.94,-5.489Z" style="fill:#4d5b6a;fill-rule:nonzero;"/><path d="M462.927,481.959c0.677,-3.328 2.161,-6.845 5.908,-7.508c2.666,0.317 5.245,1.096 7.695,2.19c-2.522,2.579 -5.043,5.173 -7.536,7.782c-1.239,-1.023 -2.478,-2.047 -3.718,-3.055c1.023,2.075 2.06,4.136 3.127,6.196c-2.089,-1.037 -4.121,-2.176 -6.225,-3.17c0.187,-0.604 0.562,-1.83 0.749,-2.434Z" style="fill:#212021;fill-rule:nonzero;"/><path d="M510.667,479.781c2.32,-2.104 4.712,-4.323 7.853,-5.087c2.94,0.145 6.153,-0.158 8.761,1.47c2.94,1.355 5.764,4.626 9.208,2.464c0.635,0.865 1.268,1.729 1.902,2.608c-0.979,0.404 -2.954,1.21 -3.934,1.614c-1.801,0.734 -3.602,1.441 -5.389,2.176c-4.553,-2.594 -9.208,-5.159 -14.338,-6.383c0.893,1.197 1.787,2.407 2.709,3.602l0.432,2.364c-2.406,-1.6 -4.813,-3.214 -7.205,-4.828Z" style="fill:#242a2f;fill-rule:nonzero;"/><path d="M608.856,474.68c0.723,0.766 0.723,0.766 0,0Z" style="fill:#c4c9ce;fill-rule:nonzero;"/><path d="M682.072,474.58c2.695,0 5.39,0.114 8.084,0.288c-1.037,2.737 -2.118,5.447 -3.214,8.155l-0.734,-0.663c-0.418,-0.375 -1.283,-1.153 -1.715,-1.542c0.995,-1.412 2.003,-2.824 3.026,-4.235c-1.83,-0.677 -3.631,-1.325 -5.447,-2.003Z" style="fill:#596472;fill-rule:nonzero;"/><path d="M496.487,482.189c1.484,-2.724 3.358,-5.245 5.605,-7.392c3.3,7.912 6.917,15.721 9.208,24.008c-1.47,1.225 -2.954,2.42 -4.51,3.53c-1.83,-0.361 -3.646,-0.778 -5.433,-1.268c0.865,-0.13 2.594,-0.389 3.458,-0.505c-3.545,-5.75 -6.139,-12.004 -8.329,-18.372Z" style="fill:#5e87af;fill-rule:nonzero;"/><path d="M667.631,475.66c2.162,1.166 4.309,2.405 6.528,3.501c0.576,-0.202 1.729,-0.591 2.306,-0.778c-0.389,0.62 -1.195,1.845 -1.599,2.464c-1.268,2.003 -2.508,4.02 -3.646,6.111c-2.104,-1.052 -4.481,-1.758 -5.908,-3.732c0.316,-1.125 0.936,-3.372 1.254,-4.482c0.259,-0.777 0.806,-2.319 1.065,-3.083Z" style="fill:#5d85ad;fill-rule:nonzero;"/><path d="M447.654,476.554c4.02,2.983 8.286,5.663 12.854,7.767c-1.571,1.325 -3.084,2.724 -4.582,4.121c4.784,2.364 9.669,4.626 14.885,5.88c6.528,2.205 14.251,1.917 19.669,6.657c-8.3,-1.454 -16.701,-2.651 -24.612,-5.676c-7.162,-3.055 -14.424,-5.951 -21.067,-10.073c-0.461,-1.729 -0.937,-3.458 -1.412,-5.173c1.412,-1.167 2.839,-2.349 4.265,-3.502Z" style="fill:#526e89;fill-rule:nonzero;"/><path d="M547.815,478.587c2.32,-0.534 4.668,-0.923 7.047,-1.197c-1.499,1.83 -2.983,3.66 -4.468,5.505c-2.449,0.446 -4.885,0.966 -7.306,1.469c0.13,-1.181 0.418,-3.558 0.563,-4.74c1.383,-0.345 2.781,-0.692 4.164,-1.037Z" style="fill:#a2a4a5;fill-rule:nonzero;"/><path d="M554.861,477.391c3.099,-0.332 6.225,-0.62 9.353,-0.808c1.426,0.347 2.867,0.677 4.323,0.995c-3.69,0.345 -8.114,0.274 -10.03,4.107c-2.723,0.303 -5.418,0.734 -8.112,1.211c1.484,-1.845 2.968,-3.675 4.467,-5.505Z" style="fill:#bdc0c2;fill-rule:nonzero;"/><path d="M514.732,478.644c5.13,1.225 9.784,3.789 14.338,6.383c1.787,-0.734 3.588,-1.441 5.389,-2.176c0.173,1.124 0.519,3.387 0.692,4.51c-2.551,1.21 -5.043,2.55 -7.507,3.934l-0.836,0.518c-0.173,-1.195 -0.533,-3.587 -0.706,-4.784l-0.187,-0.936c-2.781,-1.397 -5.663,-2.55 -8.473,-3.848c-0.922,-1.195 -1.816,-2.407 -2.709,-3.602Z" style="fill:#727373;fill-rule:nonzero;"/><path d="M558.507,481.684c1.917,-3.833 6.341,-3.761 10.03,-4.107c7.622,0.85 14.885,3.314 22.205,5.475c0.345,0.246 1.067,0.721 1.426,0.966c0.576,0.389 1.758,1.197 2.335,1.586c-4.986,1.858 -10.045,0.274 -14.843,-1.311c-6.859,-1.975 -14.049,-2.349 -21.153,-2.608Z" style="fill:#d6dadd;fill-rule:nonzero;"/><path d="M604.388,479.262l0.721,-0.993c1.671,3.299 3.257,6.671 3.919,10.346c-2.45,-2.104 -5.924,-6.168 -4.641,-9.353Z" style="fill:#686b6f;fill-rule:nonzero;"/><path d="M676.466,478.384l1.211,-0.432c2.348,0.734 4.654,1.656 6.816,2.867c0.432,0.389 1.296,1.167 1.715,1.542l0.865,2.55c-2.623,0.404 -4.914,-0.879 -7.119,-2.047c-1.296,0.951 -2.579,1.917 -3.89,2.825l-1.485,0.692c0.072,-1.384 0.216,-4.15 0.288,-5.534c0.404,-0.619 1.211,-1.843 1.599,-2.464Z" style="fill:#4e6dae;fill-rule:nonzero;"/><path d="M422.553,487.924c2.032,-2.838 4.035,-5.706 6.34,-8.329c3.833,3.445 7.767,6.831 12.306,9.324c-1.844,2.638 -3.689,5.274 -5.735,7.752c-5.548,0.433 -8.502,-6.095 -13.142,-8.43l0.231,-0.317Z" style="fill:#5f88b2;fill-rule:nonzero;"/><path d="M428.892,479.595c2.176,0.189 4.352,0.404 6.542,0.664c3.213,1.484 6.311,3.184 9.366,4.971c6.643,4.121 13.905,7.017 21.067,10.071c-1.729,0.534 -3.458,1.096 -5.159,1.701c-2.378,-0.663 -4.755,-1.325 -7.075,-2.148c-4.265,-1.715 -8.401,-3.747 -12.436,-5.937c-4.539,-2.493 -8.473,-5.878 -12.306,-9.324Z" style="fill:#3d4d5b;fill-rule:nonzero;"/><path d="M621.278,488.341c2.42,-2.162 3.156,-5.375 3.947,-8.358c4.496,0.922 8.387,3.372 9.986,7.91c2.421,-2.233 1.167,-5.23 0.49,-7.925c2.522,2.003 5.087,3.934 7.651,5.894c-5.289,9.322 -13.098,16.902 -18.2,26.297c-0.576,0.547 -1.729,1.628 -2.291,2.176c-0.36,-0.576 -1.081,-1.729 -1.426,-2.306c2.998,-0.663 2.68,-3.644 0.707,-5.273c-0.677,-1.355 -1.369,-2.695 -2.392,-3.805c-0.347,-0.533 -1.039,-1.57 -1.384,-2.104c0.404,-0.936 1.195,-2.796 1.599,-3.732c0.216,-1.599 0.375,-3.198 0.461,-4.799c0.677,-0.533 2.032,-1.614 2.709,-2.161c-0.461,-0.446 -1.383,-1.369 -1.858,-1.816Zm3.011,2.276c4.769,0.029 -2.047,4.395 0,0Zm-3.804,9.713c4.309,-2.249 0.303,4.74 0,0Zm4.035,6.297c0.734,0.764 0.734,0.764 0,0Z" style="fill:#3e4d5c;fill-rule:nonzero;"/><path d="M648.844,480.95c1.08,1.124 2.161,2.247 3.257,3.385l-1.226,1.715c-3.516,4.857 -6.426,10.146 -10.158,14.857c-1.903,2.566 -3.732,5.202 -5.289,8.013c3.861,1.325 10.201,-0.475 11.426,4.625c-0.907,-0.432 -2.737,-1.311 -3.644,-1.757c-1.298,1.282 -2.594,2.579 -3.862,3.89c0.951,-0.331 2.853,-0.993 3.804,-1.325c0.015,0.475 0.044,1.426 0.058,1.902c-3.271,0.044 -4.554,1.399 -3.833,4.035c0.979,0.187 2.939,0.547 3.933,0.734c-0.705,2.739 -0.562,6.197 -3.429,7.739c0.591,-1.83 1.138,-3.675 1.643,-5.534c0.749,-3.415 -4.294,-2.607 -4.309,0.015c-0.778,2.003 -1.426,4.05 -2.089,6.096c1.283,0.013 3.862,0.057 5.144,0.072c-0.518,2.42 -1.067,4.885 -0.591,7.378l-0.389,0.345c-0.604,0.562 -1.801,1.687 -2.392,2.249c0.073,0.634 0.231,1.917 0.317,2.55l-0.793,-0.865c-0.244,-0.274 -0.734,-0.808 -0.979,-1.081c-0.275,0.044 -0.822,0.145 -1.096,0.187c-0.835,0.145 -2.508,0.433 -3.343,0.591c-0.274,-3.126 -0.576,-6.253 -1.167,-9.322c4.828,2.781 3.227,-5.591 -0.259,-1.312l-1.037,0.894c0.389,-2.075 0.792,-4.136 1.225,-6.196c1.369,0.951 2.767,1.917 4.15,2.882c0.114,-0.519 0.36,-1.557 0.475,-2.076c-2.825,-1.282 -5.606,-2.666 -8.402,-3.976c0.389,-0.98 1.182,-2.954 1.571,-3.949c-1.628,-1.052 -3.328,-2.003 -4.698,-3.372c0.562,-0.547 1.715,-1.628 2.291,-2.176c9.496,-9.006 17.278,-19.828 23.69,-31.211Zm-18.532,38.387c0.736,0.793 0.736,0.793 0,0Zm4.525,11.427c-1.974,1.383 0.043,5.115 2.219,3.53c2.233,-1.355 -0.029,-5.274 -2.219,-3.53Z" style="fill:#323b45;fill-rule:nonzero;"/><path d="M671.22,486.957c1.138,-2.089 2.377,-4.107 3.646,-6.109c-0.072,1.383 -0.216,4.15 -0.288,5.533l1.484,-0.692c-1.037,1.254 -2.06,2.537 -3.04,3.833c-0.446,-0.634 -1.355,-1.931 -1.801,-2.565Z" style="fill:#849cc2;fill-rule:nonzero;"/><path d="M517.441,482.246c2.81,1.298 5.692,2.45 8.473,3.848l0.187,0.936c-2.378,1.456 -4.77,2.911 -7.046,4.554c-0.403,-2.335 -0.778,-4.655 -1.182,-6.974l-0.432,-2.364Z" style="fill:#101214;fill-rule:nonzero;"/><path d="M663.368,489.681c0.749,-2.118 1.368,-4.279 1.944,-6.455c1.426,1.974 3.805,2.68 5.909,3.732c0.446,0.634 1.353,1.93 1.801,2.565c-2.709,3.66 -5.058,7.565 -7.651,11.297c-0.116,-1.599 -0.216,-3.185 -0.303,-4.769c-1.744,-0.26 -3.488,-0.505 -5.217,-0.749c0.793,-1.341 1.6,-2.682 2.421,-4.007c0.274,-0.402 0.821,-1.21 1.096,-1.614Z" style="fill:#577797;fill-rule:nonzero;"/><path d="M676.061,485.689c1.311,-0.908 2.594,-1.874 3.89,-2.825c2.205,1.167 4.496,2.45 7.119,2.047c0.389,1.283 0.806,2.55 1.225,3.833c-2.306,4.698 -4.971,9.193 -7.796,13.603c-2.579,-1.456 -5.144,-2.925 -7.681,-4.453c2.608,-3.703 4.51,-7.824 6.153,-12.017c-0.72,-0.044 -2.176,-0.145 -2.91,-0.187Z" style="fill:#5f89b3;fill-rule:nonzero;"/><path d="M460.507,484.322c0.418,0.015 1.254,0.057 1.671,0.072c2.104,0.995 4.136,2.133 6.225,3.17c2.925,1.21 5.98,2.089 9.049,2.897c-2.219,1.267 -4.438,2.55 -6.643,3.861c-5.216,-1.254 -10.101,-3.516 -14.885,-5.878c1.499,-1.399 3.012,-2.796 4.582,-4.121Z" style="fill:#465b6f;fill-rule:nonzero;"/><path d="M535.149,487.362c2.594,-1.138 5.259,-2.075 7.94,-2.996c0.187,0.705 0.562,2.118 0.764,2.838c-2.306,0.461 -3.861,1.816 -3.962,4.208c1.325,1.225 2.636,2.464 3.949,3.717c-4.079,1.6 -8.142,3.271 -11.831,5.663c-1.455,-3.17 -2.911,-6.326 -4.366,-9.496c2.464,-1.383 4.957,-2.723 7.507,-3.934Z" style="fill:#d6d8db;fill-rule:nonzero;"/><path d="M676.063,485.69c0.734,0.044 2.19,0.145 2.911,0.187c-1.643,4.193 -3.545,8.314 -6.153,12.018c-2.68,3.89 -5.36,7.824 -8.589,11.283c-2.954,2.983 -5.965,5.908 -8.731,9.049c2.434,1.298 5.014,2.263 7.651,3.069c-6.673,4.784 -12.839,10.304 -19.771,14.728l-1.109,0.23c-0.072,-1.153 -0.13,-2.306 -0.173,-3.444c1.195,-1.859 2.276,-3.775 3.328,-5.72c1.484,0.705 2.983,1.369 4.51,1.988c0.259,-1.167 0.806,-3.516 1.067,-4.698c-1.34,0.101 -4.02,0.317 -5.36,0.418c0.402,-1.024 1.181,-3.084 1.584,-4.107l0.894,1.21c2.767,4.841 5.965,-3.055 0.547,-1.124c4.525,-7.422 12.277,-12.334 16.701,-19.958c2.594,-3.732 4.942,-7.637 7.651,-11.297c0.979,-1.296 2.003,-2.579 3.04,-3.833Zm-29.453,45.233c0.863,0.734 0.863,0.734 0,0Z" style="fill:#3f505f;fill-rule:nonzero;"/><path d="M519.054,491.583c2.277,-1.643 4.669,-3.099 7.046,-4.554c0.173,1.197 0.533,3.589 0.706,4.784c-3.113,1.931 -5.807,4.396 -8.444,6.902l-0.403,-0.792c-0.591,-1.298 -1.153,-2.579 -1.715,-3.877c0.908,-0.85 1.844,-1.685 2.81,-2.464Z" style="fill:#606061;fill-rule:nonzero;"/><path d="M539.89,491.41c0.101,-2.392 1.658,-3.747 3.963,-4.208c0.519,0.274 1.571,0.835 2.104,1.109c0.202,2.45 -0.461,4.626 -2.003,6.572c18.53,-6.067 40.592,-2.061 54.54,11.931c0.13,-2.348 0.26,-4.712 0.808,-7.003c2.363,-1.21 3.949,1.312 5.75,2.392c-0.446,0.966 -0.879,1.946 -1.325,2.911c-1.687,0.576 -3.328,1.197 -4.986,1.83c9.122,7.94 14.972,19.41 16.701,31.342c0.072,0.475 0.215,1.426 0.288,1.902c-3.185,6.902 -1.441,14.712 -3.502,21.888c-0.418,-0.417 -1.254,-1.268 -1.671,-1.685c3.558,-13.849 1.412,-29.267 -6.744,-41.126c-8.487,-12.867 -23.128,-21.572 -38.547,-22.551c-22.018,-1.946 -43.978,12.709 -51.154,33.517c-2.839,7.191 -3.141,15 -2.954,22.637c-0.548,0.231 -1.657,0.692 -2.219,0.908c-0.058,-0.707 -0.187,-2.148 -0.245,-2.867c-0.677,-13.906 3.747,-27.595 11.686,-38.936c1.268,-1.801 2.637,-3.516 4.121,-5.144c2.363,-2.161 4.856,-4.208 7.507,-6.038c3.689,-2.392 7.753,-4.063 11.83,-5.663c-1.311,-1.254 -2.622,-2.493 -3.949,-3.717Z" style="fill:#232020;fill-rule:nonzero;"/><path d="M616.57,489.777c-2.472,1.489 -2.616,-2.717 -0.317,-2.919l1.055,0.809c-0.187,0.521 -0.563,1.591 -0.737,2.11Z" style="fill:#b0a2b3;fill-rule:nonzero;"/><path d="M477.452,490.46c2.392,0.635 4.798,1.283 7.162,2.061c1.038,0.317 3.127,0.966 4.179,1.296c0.648,0.174 1.96,0.519 2.608,0.692c2.234,2.537 4.51,5.058 6.7,7.637c-2.579,-0.187 -5.13,-0.49 -7.623,-1.166c-5.418,-4.741 -13.142,-4.453 -19.669,-6.658c2.205,-1.311 4.424,-2.594 6.643,-3.862Z" style="fill:#3c4a58;fill-rule:nonzero;"/><path d="M624.289,490.618c4.785,0.029 -2.052,4.408 0,0Z" style="fill:#6f6e7e;fill-rule:nonzero;"/><path d="M422.869,491.987c0.737,0.752 0.737,0.752 0,0Z" style="fill:#383738;fill-rule:nonzero;"/><path d="M470.609,610.48c0.015,0.029 0.06,0.089 0.075,0.118l-0.075,-0.118Z" style="fill:#383738;fill-rule:nonzero;"/><path d="M526.807,491.814l0.836,-0.519c1.455,3.17 2.911,6.326 4.366,9.496c-2.652,1.83 -5.144,3.876 -7.508,6.038c-2.234,-2.551 -4.409,-5.188 -6.139,-8.112c2.637,-2.508 5.332,-4.973 8.444,-6.902Z" style="fill:#afafb0;fill-rule:nonzero;"/><path d="M613.005,491.584c2.486,-0.983 4.091,2.587 1.272,3.339l-1.258,-0.607c0,-0.679 -0.015,-2.052 -0.015,-2.732Z" style="fill:#efcadb;fill-rule:nonzero;"/><path d="M496.388,493.485c0.737,0.752 0.737,0.752 0,0Z" style="fill:#bed2ea;fill-rule:nonzero;"/><path d="M425.837,494.955c0.81,0.709 0.81,0.709 0,0Z" style="fill:#595759;fill-rule:nonzero;"/><path d="M511.301,498.803c1.686,-1.542 3.285,-3.17 4.943,-4.755c0.562,1.296 1.124,2.579 1.715,3.876c-1.787,2.032 -3.574,4.064 -5.375,6.082c-1.009,1.109 -2.003,2.234 -2.954,3.4c-0.807,-0.345 -2.406,-1.052 -3.213,-1.397c0.115,-1.239 0.259,-2.449 0.375,-3.675c1.556,-1.109 3.04,-2.306 4.51,-3.53Z" style="fill:#464645;fill-rule:nonzero;"/><path d="M652.791,504.74c2.737,-2.838 5.043,-6.052 7.06,-9.439c1.729,0.246 3.473,0.49 5.217,0.75c0.086,1.584 0.187,3.17 0.301,4.769c-4.423,7.622 -12.175,12.536 -16.7,19.957l-0.547,1.125l-0.894,-1.211c0.547,-1.555 1.182,-3.069 1.83,-4.567c3.185,-2.767 6.21,-5.764 8.371,-9.425c-1.555,-0.663 -3.097,-1.311 -4.639,-1.959Z" style="fill:#4e677f;fill-rule:nonzero;"/><path d="M428.747,497.852c0.781,0.752 0.781,0.752 0,0Z" style="fill:#5d5b5d;fill-rule:nonzero;"/><path d="M610.066,497.002c1.83,1.715 3.589,3.516 5.333,5.331c1.225,1.586 2.579,2.853 4.351,0.62c1.023,1.109 1.715,2.449 2.392,3.804c-0.562,0.246 -1.715,0.749 -2.291,1.009c-1.369,1.252 1.153,2.954 1.586,4.265c0.345,0.576 1.065,1.729 1.426,2.306c1.368,1.369 3.069,2.32 4.698,3.372c-0.389,0.993 -1.182,2.968 -1.571,3.947c2.796,1.312 5.577,2.695 8.401,3.978c-0.116,0.519 -0.36,1.557 -0.475,2.075c-1.383,-0.966 -2.781,-1.931 -4.149,-2.882c-0.433,2.061 -0.837,4.121 -1.226,6.197c-1.138,0.993 -2.291,1.974 -3.429,2.968c-1.599,-5.072 -2.867,-10.26 -4.885,-15.189c-2.796,-6.816 -7.435,-12.608 -11.672,-18.56c0.375,-0.806 1.138,-2.434 1.513,-3.242Z" style="fill:#d1d2d6;fill-rule:nonzero;"/><path d="M664.233,509.178c3.227,-3.458 5.908,-7.392 8.588,-11.283c2.537,1.527 5.101,2.996 7.681,4.452c-2.853,3.789 -5.562,7.782 -9.166,10.923c-2.42,-1.283 -4.769,-2.68 -7.103,-4.092Z" style="fill:#54728e;fill-rule:nonzero;"/><path d="M512.583,504.005c1.801,-2.017 3.588,-4.05 5.375,-6.08l0.403,0.792c1.729,2.926 3.905,5.562 6.139,8.114c-1.484,1.628 -2.853,3.343 -4.121,5.144c-0.922,-0.231 -2.781,-0.692 -3.703,-0.923c-1.369,-2.348 -2.738,-4.711 -4.092,-7.046Z" style="fill:#939293;fill-rule:nonzero;"/><path d="M485.35,596.229c0.519,-2.278 1.052,-4.54 1.556,-6.816c-0.49,5.562 0.332,11.138 1.801,16.498l0.259,0.793l-1.744,1.11c-5.894,-0.418 -6.787,-6.471 -7.09,-11.196c1.729,-0.13 3.473,-0.26 5.216,-0.389Z" style="fill:#939293;fill-rule:nonzero;"/><path d="M470.349,647.555c0.853,0.823 0.853,0.823 0,0Z" style="fill:#939293;fill-rule:nonzero;"/><path d="M640.715,500.907c2.565,-0.691 5.101,-1.527 7.738,-1.944c1.542,0.907 2.998,1.959 4.382,3.112c-2.781,2.335 -5.606,5.015 -5.39,8.964c-0.202,0.835 -0.404,1.671 -0.591,2.506c-1.225,-5.1 -7.565,-3.299 -11.427,-4.625c1.557,-2.81 3.387,-5.447 5.289,-8.013Z" style="fill:#394754;fill-rule:nonzero;"/><path d="M431.371,500.216c2.038,-0.636 2.689,0.072 1.966,2.11c-2.038,0.636 -2.703,-0.073 -1.966,-2.11Z" style="fill:#454445;fill-rule:nonzero;"/><path d="M620.485,500.332c4.323,-2.254 0.304,4.756 0,0Z" style="fill:#ba9baf;fill-rule:nonzero;"/><path d="M478.533,501.657c5.966,1.138 11.917,2.521 18.012,2.895c-6.456,1.614 -12.162,7.047 -19.179,4.597c-1.182,-0.173 -3.559,-0.533 -4.755,-0.705c1.7,-2.493 3.674,-4.784 5.922,-6.787Z" style="fill:#384653;fill-rule:nonzero;"/><path d="M449.873,503.357c2.594,1.067 5.159,2.19 7.781,3.227c0.086,0.029 0.259,0.101 0.346,0.145l-0.101,-0.101c4.611,0.49 9.193,1.182 13.79,1.715c-0.764,0.116 -2.277,0.345 -3.04,0.446c-0.49,1.99 -0.908,4.007 -1.34,6.023c-3.026,1.11 -6.052,2.292 -8.977,3.675c-2.392,1.153 -4.741,2.392 -7.104,3.617c-2.565,-0.619 -5.159,-1.023 -7.767,-1.355l-0.245,-0.043l-0.014,0.015c-3.761,1.931 -7.263,4.336 -11.038,6.24c-0.144,-0.015 -0.432,-0.044 -0.562,-0.058c-0.634,-0.086 -1.888,-0.288 -2.507,-0.389c-0.893,0.749 -1.787,1.485 -2.666,2.234l-0.692,-1.557l0.677,-0.331c-0.231,-0.044 -0.706,-0.159 -0.937,-0.216c-0.504,-0.044 -1.499,-0.13 -1.989,-0.173c-1.11,0.158 -3.386,4.063 -3.43,0.648c0.043,-0.072 0.101,-0.231 0.13,-0.317c0.605,-0.475 1.816,-1.426 2.421,-1.902c2.075,-1.369 4.525,-3.458 6.398,-0.518c1.787,-0.404 3.559,-0.822 5.36,-1.211c-0.115,-0.907 -0.346,-2.709 -0.461,-3.617c0.965,0.591 2.882,1.758 3.833,2.348c2.046,-1.829 3.991,-3.775 5.807,-5.836c0.864,0.923 1.729,1.845 2.608,2.767l0.98,-0.129c-0.865,-1.485 -1.7,-2.968 -2.536,-4.453c2.262,-1.225 3.934,0.793 5.634,1.946c-0.836,-1.557 -1.599,-3.141 -2.291,-4.755c1.945,-0.116 3.732,0.547 5.332,1.974c-1.239,-2.19 -3.285,-4.683 -0.072,-6.269c-0.231,-0.417 -0.721,-1.239 -0.966,-1.656c-0.965,1.614 -2.32,2.824 -4.035,3.602c-0.49,-2.19 0.303,-4.077 1.671,-5.735Zm4.698,5.894c0.778,0.705 0.778,0.705 0,0Zm5.807,-0.174c0.778,0.721 0.778,0.721 0,0Zm-2.896,2.867c0.735,0.764 0.735,0.764 0,0Zm-2.968,2.638c4.352,-2.162 0.13,4.769 0,0Z" style="fill:#454546;fill-rule:nonzero;"/><path d="M496.545,504.552c3.329,-0.013 6.744,0.145 9.871,1.456c0.807,0.347 2.406,1.052 3.213,1.399c-2.176,2.882 -4.121,5.922 -5.908,9.049c-0.202,-2.709 -0.504,-5.418 -0.778,-8.112c-4.755,3.097 -10.678,2.882 -15.966,4.452c0.403,0.404 1.225,1.197 1.643,1.586l-0.101,1.355c-3.271,0.648 -6.542,1.282 -9.827,1.873c-0.158,-2.003 -0.375,-3.991 -0.692,-5.979c-0.187,-0.837 -0.403,-1.658 -0.634,-2.48c7.018,2.45 12.724,-2.982 19.179,-4.597Z" style="fill:#2f3841;fill-rule:nonzero;"/><path d="M509.63,507.407c0.951,-1.167 1.945,-2.291 2.954,-3.401c1.354,2.335 2.723,4.698 4.092,7.047c0.922,0.23 2.781,0.692 3.703,0.922c-7.94,11.341 -12.364,25.03 -11.686,38.936c-0.634,-5.347 -0.461,-10.836 -2.161,-16.009c-0.072,-0.274 -0.231,-0.835 -0.303,-1.11c2.435,0.101 2.421,-2.737 3.257,-4.279c1.499,-4.813 3.919,-9.251 6.484,-13.559c-4.179,0.129 -8.343,0.417 -12.479,1.08l0.231,-0.576c1.787,-3.126 3.732,-6.167 5.908,-9.049Z" style="fill:#7b7a7a;fill-rule:nonzero;"/><path d="M647.444,511.038c1.658,-2.205 3.458,-4.295 5.346,-6.298c1.542,0.649 3.084,1.298 4.641,1.96c-2.161,3.66 -5.188,6.657 -8.373,9.423c-0.518,-0.489 -1.555,-1.484 -2.075,-1.988c-0.606,2.133 -1.845,3.891 -3.646,5.159l-0.129,-3.04c-0.015,-0.475 -0.044,-1.426 -0.058,-1.902c-0.951,0.332 -2.853,0.995 -3.804,1.325c1.268,-1.311 2.565,-2.608 3.862,-3.89c0.907,0.446 2.737,1.325 3.644,1.757c0.187,-0.835 0.389,-1.671 0.591,-2.506Z" style="fill:#45596c;fill-rule:nonzero;"/><path d="M437.308,506.324c0.737,0.781 0.737,0.781 0,0Z" style="fill:#575556;fill-rule:nonzero;"/><path d="M624.52,506.628c0.737,0.766 0.737,0.766 0,0Z" style="fill:#af8ea2;fill-rule:nonzero;"/><path d="M486.979,512.795c5.288,-1.57 11.211,-1.355 15.966,-4.452c0.274,2.695 0.576,5.403 0.778,8.112l-0.231,0.576c-0.908,2.205 -1.816,4.409 -2.695,6.643c-1.038,2.794 -1.917,5.649 -2.824,8.501c0.504,0.187 1.484,0.562 1.989,0.749c0.677,-0.461 2.017,-1.397 2.695,-1.859c1.268,0.793 2.493,1.643 3.574,2.724c0.072,0.274 0.231,0.835 0.303,1.109c-1.009,1.125 -1.974,2.32 -2.954,3.473c-1.599,-1.744 -3.761,-2.68 -5.894,-3.602c-0.317,-2.926 -1.297,-5.678 -2.421,-8.373c0.432,-2.205 0.922,-4.38 1.412,-6.57c-0.418,-0.475 -0.836,-0.966 -1.254,-1.441c0.965,-1.225 4.164,-1.672 3.156,-3.675c-3.271,-0.806 -6.657,-0.303 -9.957,-0.331c-0.418,-0.389 -1.239,-1.182 -1.643,-1.586Z" style="fill:#232224;fill-rule:nonzero;"/><path d="M440.291,509.249c0.723,0.781 0.723,0.781 0,0Z" style="fill:#4f4e4f;fill-rule:nonzero;"/><path d="M445.881,509.234c0.838,0.68 0.838,0.68 0,0Z" style="fill:#535152;fill-rule:nonzero;"/><path d="M468.649,508.789c0.764,-0.101 2.277,-0.331 3.041,-0.446l0.922,0.101c1.196,0.173 3.574,0.533 4.755,0.705c0.231,0.822 0.447,1.643 0.634,2.478c-3.242,0.851 -6.47,1.744 -9.64,2.81l-1.052,0.375c0.432,-2.017 0.85,-4.035 1.34,-6.023Z" style="fill:#0d1012;fill-rule:nonzero;"/><path d="M655.5,518.228c2.767,-3.141 5.779,-6.067 8.733,-9.049c2.334,1.412 4.683,2.809 7.104,4.092c-2.537,2.867 -5.347,5.461 -8.185,8.026c-2.636,-0.806 -5.216,-1.772 -7.651,-3.069Z" style="fill:#485e73;fill-rule:nonzero;"/><path d="M443.044,512.262c0.737,0.766 0.737,0.766 0,0Z" style="fill:#4f4d4e;fill-rule:nonzero;"/><path d="M468.361,514.439c3.17,-1.067 6.398,-1.96 9.64,-2.81c0.317,1.988 0.533,3.978 0.692,5.981l-0.418,0.835c-2.19,0.966 -4.438,1.845 -6.657,2.709c-0.62,-0.347 -1.859,-1.039 -2.478,-1.384c-0.288,-1.786 -0.548,-3.559 -0.778,-5.331Z" style="fill:#3a4856;fill-rule:nonzero;"/><path d="M422.94,515c0.741,0.726 0.741,0.726 0,0Z" style="fill:#313031;fill-rule:nonzero;"/><path d="M440.203,529.407c0.723,0.752 0.723,0.752 0,0Z" style="fill:#313031;fill-rule:nonzero;"/><path d="M440.262,515.057c0.795,0.723 0.795,0.723 0,0Z" style="fill:#525153;fill-rule:nonzero;"/><path d="M454.511,514.584c4.365,-2.169 0.13,4.784 0,0Z" style="fill:#1a191a;fill-rule:nonzero;"/><path d="M467.308,514.811l1.052,-0.375c0.231,1.773 0.49,3.545 0.778,5.333c0.62,0.345 1.859,1.037 2.478,1.383c-3.055,1.298 -5.994,2.825 -8.876,4.453c-1.47,-2.377 -2.94,-4.756 -4.409,-7.119c2.925,-1.383 5.951,-2.565 8.977,-3.675Z" style="fill:#465a6e;fill-rule:nonzero;"/><path d="M646.983,514.135c0.519,0.505 1.557,1.499 2.076,1.99c-0.649,1.498 -1.283,3.011 -1.83,4.567c-0.404,1.023 -1.182,3.084 -1.586,4.107c1.34,-0.101 4.02,-0.317 5.36,-0.418c-0.259,1.182 -0.806,3.53 -1.067,4.698c-1.527,-0.619 -3.026,-1.282 -4.509,-1.988c-1.052,1.946 -2.133,3.862 -3.33,5.72c0.044,1.138 0.101,2.291 0.174,3.444c-0.649,0.13 -1.946,0.389 -2.594,0.534c-0.475,-2.493 0.072,-4.957 0.59,-7.378c-1.282,-0.015 -3.861,-0.058 -5.144,-0.072c0.663,-2.047 1.311,-4.093 2.089,-6.096c0.015,-2.622 5.059,-3.429 4.309,-0.015c-0.503,1.859 -1.052,3.704 -1.643,5.534c2.869,-1.542 2.724,-5 3.431,-7.739c-0.995,-0.187 -2.954,-0.547 -3.934,-0.734c-0.721,-2.636 0.562,-3.991 3.833,-4.035l0.129,3.04c1.802,-1.267 3.041,-3.026 3.646,-5.159Z" style="fill:#cb8dac;fill-rule:nonzero;"/><path d="M478.692,517.607c3.285,-0.59 6.556,-1.225 9.827,-1.873c-0.692,1.47 -1.441,2.911 -2.19,4.351c-3.631,1.197 -7.277,2.379 -10.851,3.733c0.951,-1.788 1.873,-3.589 2.795,-5.375l0.418,-0.837Z" style="fill:#596d81;fill-rule:nonzero;"/><path d="M503.491,517.032c4.136,-0.663 8.3,-0.951 12.479,-1.08c-2.565,4.309 -4.986,8.746 -6.484,13.559c-3.113,-1.599 -6.067,-3.516 -8.689,-5.836c0.879,-2.234 1.787,-4.439 2.695,-6.643Z" style="fill:#f2f2f3;fill-rule:nonzero;"/><path d="M414.381,518.025c0.708,0.766 0.708,0.766 0,0Z" style="fill:#373637;fill-rule:nonzero;"/><path d="M437.322,532.291c0.726,0.741 0.726,0.741 0,0Z" style="fill:#373637;fill-rule:nonzero;"/><path d="M432.281,557.127c-1.344,-1.778 -0.853,-2.385 1.474,-1.822c-0.361,0.464 -1.113,1.373 -1.474,1.822Z" style="fill:#373637;fill-rule:nonzero;"/><path d="M420.103,517.997c0.766,0.766 0.766,0.766 0,0Z" style="fill:#575657;fill-rule:nonzero;"/><path d="M425.592,517.71c4.394,-2.11 0.072,4.799 0,0Z" style="fill:#3d3d3e;fill-rule:nonzero;"/><path d="M455.998,613.115c0.781,0.724 0.781,0.724 0,0Z" style="fill:#3d3d3e;fill-rule:nonzero;"/><path d="M437.322,517.882c0.81,0.724 0.81,0.724 0,0Z" style="fill:#5d5c5d;fill-rule:nonzero;"/><path d="M451.226,522.104c2.363,-1.225 4.712,-2.464 7.104,-3.617c1.47,2.363 2.94,4.74 4.409,7.118c-2.868,1.658 -5.677,3.401 -8.314,5.404c-0.922,-2.623 -1.787,-5.259 -2.767,-7.853l-0.432,-1.052Z" style="fill:#516b85;fill-rule:nonzero;"/><path d="M630.312,519.336c0.737,0.796 0.737,0.796 0,0Z" style="fill:#a77e96;fill-rule:nonzero;"/><path d="M411.256,520.662c4.799,0.216 -2.212,4.322 0,0Z" style="fill:#363637;fill-rule:nonzero;"/><path d="M417.162,520.893c0.838,0.723 0.838,0.723 0,0Z" style="fill:#717071;fill-rule:nonzero;"/><path d="M625.497,559.498c0.936,-4.338 0.692,-10.447 6.109,-11.672c0.145,0.648 0.433,1.959 0.576,2.607c0.058,4.611 -2.377,8.762 -2.723,13.329c-0.332,1.384 0.057,3.328 -1.744,3.717c-5.893,2.349 -7.666,9.107 -11.989,13.243c-1.513,-3.099 -3.126,-6.139 -4.726,-9.179c-0.692,0.648 -2.104,1.974 -2.796,2.622c1.181,-3.962 2.91,-7.709 4.841,-11.354c-0.143,2.219 -0.216,4.453 -0.288,6.7c3.084,-0.116 6.326,0.446 9.294,-0.663c2.032,-2.709 2.796,-6.095 3.444,-9.351Z" style="fill:#717071;fill-rule:nonzero;"/><path d="M422.609,520.794c4.308,-2.284 0.246,4.842 0,0Z" style="fill:#5f5d5f;fill-rule:nonzero;"/><path d="M428.862,520.735c0.679,0.853 0.679,0.853 0,0Z" style="fill:#666465;fill-rule:nonzero;"/><path d="M432.162,526.958c3.775,-1.902 7.277,-4.309 11.038,-6.238c0.072,0 0.187,0.013 0.259,0.028c2.608,0.332 5.202,0.736 7.767,1.355l0.432,1.052c-2.017,1.081 -4.035,2.176 -6.038,3.286c-0.605,-0.116 -1.816,-0.332 -2.421,-0.433c0.144,0.519 0.447,1.571 0.605,2.104l0.331,0.534c-0.187,2.666 -0.49,5.346 -0.793,8.011c-2.147,0.576 -4.28,1.153 -6.427,1.744c2.032,1.283 4.092,2.522 5.951,4.064c0.447,2.276 0.475,4.61 0.634,6.93c0.245,-0.288 0.749,-0.835 0.994,-1.109c0.648,-0.707 1.974,-2.089 2.623,-2.796c3.069,0.951 6.124,2.032 8.977,3.545l0.216,0.173c0.677,0.519 2.032,1.542 2.709,2.061c-0.49,0.402 -1.455,1.195 -1.945,1.599c2.032,0.072 4.078,0.143 6.11,0.36c-1.081,0.98 -2.161,1.975 -3.228,2.968c0.879,0.159 2.637,0.475 3.516,0.635c0.014,0.503 0.043,1.498 0.058,1.988c3.804,4.279 8.415,8.501 9.121,14.525c0.447,0.274 1.311,0.85 1.744,1.138c1.556,2.335 3.516,4.352 5.764,6.023c-0.778,5.347 -0.115,10.75 -0.029,16.11c-1.427,0.519 -2.853,1.023 -4.28,1.542c-1.801,-1.34 -3.574,-2.68 -5.332,-4.035c0.058,-2.535 -0.648,-4.942 -1.715,-7.191c-1.671,0.433 -3.343,0.879 -5,1.325c0.533,-1.599 1.066,-3.184 1.614,-4.769c-1.528,-1.902 -3.257,-3.631 -5.072,-5.245c0.533,-0.231 1.585,-0.692 2.118,-0.936c-3.127,-4.683 -6.628,-9.136 -10.937,-12.797c1.196,-2.104 3.358,-4.625 1.268,-6.959c-2.363,-0.044 -4.597,0.835 -6.83,1.456c-0.36,0.793 -1.095,2.363 -1.47,3.156c-0.187,-1.037 -0.562,-3.126 -0.749,-4.165c-1.945,0.073 -3.891,0.145 -5.822,0.231c-0.115,-0.749 -0.346,-2.262 -0.461,-3.012c-0.303,0.231 -0.908,0.692 -1.21,0.923c-0.62,-0.202 -1.873,-0.62 -2.507,-0.835c-2.32,-0.563 -2.81,0.043 -1.47,1.816c-1.182,0.101 -2.378,0.187 -3.545,0.244c-1.052,-1.225 -2.104,-2.436 -3.098,-3.675c0.519,-0.158 1.585,-0.475 2.118,-0.634l-0.014,-1.067c0.274,-1.917 -0.605,-3.112 -2.651,-3.602l1.21,-0.562c0.346,-0.275 1.037,-0.837 1.383,-1.11c0.749,-2.781 -2.752,-4.121 -4.193,-6.038c-0.749,1.513 -1.902,2.565 -3.444,3.128c-1.311,-2.695 -3.804,-4.439 -5.951,-6.399c-0.159,0.707 -0.476,2.118 -0.634,2.825l0.447,0.879c0.029,0.475 0.101,1.412 0.13,1.873c-0.389,0.707 -1.167,2.118 -1.556,2.825c-0.231,-0.908 -0.706,-2.695 -0.951,-3.589c-0.764,0.101 -2.32,0.303 -3.098,0.404c-0.014,-0.375 -0.058,-1.11 -0.086,-1.47c0.965,0.086 2.896,0.288 3.862,0.375c-1.527,-2.276 -3.069,-4.538 -4.553,-6.829c0.965,0.23 2.911,0.663 3.876,0.893c-0.317,-1.037 -0.965,-3.112 -1.282,-4.149c0.677,0 2.061,0.015 2.752,0.015c1.412,-3.891 4.813,-6.456 7.868,-9.006l1.528,0.777l0.216,1.672c-0.605,0.475 -1.816,1.426 -2.421,1.902l-0.187,0.145l0.058,0.173c0.043,3.415 2.32,-0.49 3.43,-0.648c0.49,0.043 1.484,0.129 1.989,0.173l0.259,0.547l0.692,1.557c0.879,-0.749 1.772,-1.485 2.666,-2.234c0.62,0.101 1.873,0.303 2.507,0.389l0.418,0.13l0.144,-0.073Zm13.79,-3.343c0.663,0.808 0.663,0.808 0,0Zm-8.545,2.911c0.749,0.749 0.749,0.749 0,0Zm-20.347,2.954c0.778,0.793 0.778,0.793 0,0Zm17.436,-0.057c0.778,0.72 0.778,0.72 0,0Zm5.706,-0.015c0.721,0.749 0.721,0.749 0,0Zm-20.087,2.91c0.735,0.764 0.735,0.764 0,0Zm5.706,0.015c0.749,0.75 0.749,0.75 0,0Zm5.591,-0.244c4.294,-2.263 0.303,4.755 0,0Zm5.908,0.202c0.721,0.734 0.721,0.734 0,0Zm-21.024,2.521c0.418,2.753 2.651,4.237 5.043,5.217c-0.13,-1.239 -0.389,-3.747 -0.519,-5c-1.138,-0.057 -3.386,-0.158 -4.525,-0.216Zm6.6,0.49c0.778,0.692 0.778,0.692 0,0Zm5.692,-0.316c4.366,-2.133 0.115,4.769 0,0Zm-2.868,2.968c4.366,-2.091 0.072,4.769 0,0Zm2.752,2.867c4.294,-2.263 0.259,4.769 0,0Zm3.084,8.819c0.764,0.749 0.764,0.749 0,0Z" style="fill:#696768;fill-rule:nonzero;"/><path d="M648.671,520.78c5.435,-1.936 2.226,5.985 -0.549,1.128l0.549,-1.128Z" style="fill:#eb9ec1;fill-rule:nonzero;"/><path d="M519.385,536.311c1.744,-5.619 3.948,-13.847 11.067,-14.222c11.341,-0.721 24.021,-0.995 33.776,5.777c-5.388,0 -10.418,2.089 -14.726,5.202l1.729,-0.648c-1.873,2.118 -3.905,4.077 -6.095,5.864c0.389,-1.628 0.749,-3.242 1.109,-4.856c-9.092,-1.34 -18.127,0.418 -26.86,2.882Z" style="fill:#383737;fill-rule:nonzero;"/><path d="M408.302,523.574c4.799,0.145 -2.183,4.351 0,0Z" style="fill:#2c2b2c;fill-rule:nonzero;"/><path d="M414.525,523.977c0.838,0.766 0.838,0.766 0,0Z" style="fill:#807e7f;fill-rule:nonzero;"/><path d="M445.954,523.617c0.665,0.809 0.665,0.809 0,0Z" style="fill:#2a292a;fill-rule:nonzero;"/><path d="M445.621,526.442c2.003,-1.109 4.02,-2.205 6.038,-3.286c0.98,2.594 1.844,5.232 2.767,7.853c-1.326,1.47 -2.565,3.012 -3.804,4.554c-3.184,-1.096 -4.582,-4.424 -6.484,-6.917l-0.331,-0.533c0.447,-0.418 1.354,-1.254 1.816,-1.672Z" style="fill:#597a9b;fill-rule:nonzero;"/><path d="M497.971,532.177c0.908,-2.853 1.787,-5.706 2.824,-8.501c2.623,2.32 5.577,4.237 8.689,5.836c-0.836,1.542 -0.821,4.38 -3.257,4.279c-1.081,-1.08 -2.306,-1.931 -3.574,-2.723c-0.677,0.461 -2.017,1.397 -2.695,1.858c-0.504,-0.187 -1.484,-0.562 -1.989,-0.749Z" style="fill:#d0ced0;fill-rule:nonzero;"/><path d="M471.804,525.794c3.04,-0.202 6.11,-0.36 9.165,-0.562c0.072,3.4 -2.363,5.59 -5.159,7.018c-2.32,1.34 -4.683,2.579 -6.917,4.02c-0.346,-1.845 -0.663,-3.69 -0.98,-5.52c-3.184,1.902 -6.456,3.791 -10.202,4.237c4.121,-3.862 8.934,-6.888 14.093,-9.193Z" style="fill:#516c86;fill-rule:nonzero;"/><path d="M411.213,526.312c1.894,-1.821 3.151,1.879 1.749,3.051c-1.72,1.575 -3.021,-1.951 -1.749,-3.051Z" style="fill:#898789;fill-rule:nonzero;"/><path d="M433.748,555.302c0.634,0.216 1.888,0.635 2.507,0.837c-0.533,0.691 -1.599,2.06 -2.133,2.752c0.821,-0.13 2.45,-0.389 3.257,-0.519c0.677,2.118 1.427,4.237 2.536,6.183c2.277,2.723 4.698,5.346 7.032,8.026c-0.389,1.254 -0.778,2.508 -1.167,3.776c4.092,3.53 7.796,7.492 10.995,11.844c-0.807,0.835 -1.599,1.685 -2.406,2.535c0.706,0.216 2.089,0.649 2.781,0.879c3.631,5.274 8.761,9.54 11.067,15.692l-0.13,-0.086c0.058,0.086 0.187,0.246 0.245,0.317c0.576,0.734 1.715,2.205 2.277,2.939l0.072,0.116l-1.038,1.153c-0.461,0.418 -1.354,1.254 -1.816,1.672c-0.216,1.816 -0.375,3.659 -0.504,5.504c-0.605,-0.606 -1.816,-1.786 -2.406,-2.377l-0.432,-0.49l0.101,0.231c-0.663,0.562 -1.989,1.685 -2.637,2.247l-0.418,0.505l0.259,0.432l0.634,-0.143c0.548,0.606 1.614,1.83 2.147,2.449l0.288,0.375l0.346,0.303c0.634,0.635 1.902,1.902 2.536,2.522c0.043,0.072 0.144,0.216 0.202,0.288c-0.403,0.505 -1.21,1.513 -1.599,2.017c-1.499,-0.446 -4.51,-1.311 -6.009,-1.758c0.058,0.648 0.159,1.96 0.216,2.623c-0.634,-0.721 -1.888,-2.133 -2.522,-2.84c0.893,-1.023 1.772,-2.045 2.623,-3.083c-1.931,-0.938 -3.43,-2.32 -4.453,-4.165c-0.793,1.153 -1.571,2.32 -2.349,3.488c-3.905,-1.283 -1.47,-3.804 0.663,-5.39c-4.049,1.067 -3.761,-4.611 -6.845,-5.937c2.824,-0.995 4.626,-3.257 6.11,-5.692c-2.406,-3.011 -5.778,-3.573 -9.395,-2.708c0.331,-3.242 -1.873,-4.193 -4.568,-4.382c-0.548,-0.59 -1.643,-1.801 -2.19,-2.392c0.461,1.34 1.383,4.035 1.845,5.39c-2.507,-1.167 -4.539,-3.069 -6.528,-4.957c0.706,-0.62 2.104,-1.859 2.796,-2.478l0.072,0.101c0.101,-0.13 0.317,-0.389 0.418,-0.519c0.649,-0.648 1.96,-1.931 2.608,-2.579l0.375,0.389l0,-0.749c-0.014,-0.606 -0.014,-1.801 -0.029,-2.407c-1.571,-1.873 -3.271,-3.631 -4.943,-5.39c0.634,-0.533 1.917,-1.614 2.551,-2.146c-1.845,-2.205 -3.732,-4.367 -5.649,-6.485c0.418,-1.065 0.85,-2.117 1.282,-3.169c-1.455,0.446 -2.911,0.907 -4.366,1.368c0.432,-1.369 0.922,-2.695 1.383,-4.035c-3.242,0.404 -6.484,-0.143 -9.539,-1.225c-2.493,-2.622 3.329,-4.51 4.755,-6.326c0.331,-2.176 1.7,-2.998 4.078,-2.464c1.167,-0.058 2.363,-0.145 3.545,-0.246c0.36,-0.446 1.11,-1.353 1.47,-1.816Zm-2.277,5.505c4.38,-2.118 0.086,4.769 0,0Zm-2.824,2.983c4.352,-2.148 0.173,4.769 0,0Zm5.865,-0.116c4.453,-1.931 -0.115,4.769 0,0Zm-2.94,3.069c4.309,-2.234 0.216,4.769 0,0Zm5.706,-0.13c4.265,-2.291 0.303,4.784 0,0Zm2.911,2.911c4.309,-2.219 0.187,4.769 0,0Zm2.867,2.882c4.309,-2.263 0.274,4.755 0,0Zm-2.752,2.998c4.208,-2.392 0.418,4.769 0,0Zm5.634,5.649c4.323,-2.191 0.202,4.784 0,0Zm2.925,2.881c4.208,-2.405 0.432,4.756 0,0Zm-7.392,3.113c0.749,0.734 0.749,0.734 0,0Zm4.496,-0.13c4.179,-2.464 0.519,4.741 0,0Zm5.879,-0.114c4.294,-2.234 0.245,4.769 0,0Zm-8.646,2.881c4.438,-2.003 -0.029,4.771 0,0Zm-1.772,3.229c0.764,0.734 0.764,0.734 0,0Zm2.868,2.796c0.749,0.734 0.749,0.734 0,0Zm5.937,0.043c0.735,0.749 0.735,0.749 0,0Zm2.954,2.767c0.735,0.749 0.735,0.749 0,0Zm5.678,0.086c0.735,0.721 0.735,0.721 0,0Zm-2.853,2.825c0.793,0.692 0.793,0.692 0,0Zm5.764,0.072c0.764,0.734 0.764,0.734 0,0Zm-2.882,2.825c0.663,0.806 0.663,0.806 0,0Zm-2.896,2.895c0.793,0.692 0.793,0.692 0,0Zm5.778,-0.028c0.692,0.792 0.692,0.792 0,0Zm-8.617,2.982c0.764,0.721 0.764,0.721 0,0Zm5.807,-0.057c0.735,0.734 0.735,0.734 0,0Zm5.764,-0.043c0.735,0.764 0.735,0.764 0,0Zm-8.732,3.04c0.778,0.72 0.778,0.72 0,0Zm5.764,-0.072c0.807,0.691 0.807,0.691 0,0Zm-2.954,2.982c0.778,0.793 0.778,0.793 0,0Zm2.68,8.675c0.36,0.417 0.36,0.417 0,0Z" style="fill:#898789;fill-rule:nonzero;"/><path d="M659.939,562.38c1.426,-2.638 5.808,0.503 6.816,2.132c-2.247,0.534 -5.922,0.448 -6.816,-2.132Z" style="fill:#898789;fill-rule:nonzero;"/><path d="M431.601,526.901c0.13,0.015 0.419,0.044 0.564,0.058l-0.145,0.072l-0.419,-0.13Z" style="fill:#0b0b0b;fill-rule:nonzero;"/><path d="M437.407,526.527c0.752,0.752 0.752,0.752 0,0Z" style="fill:#1b1a1b;fill-rule:nonzero;"/><path d="M443.201,526.009c0.607,0.101 1.821,0.317 2.429,0.433c-0.463,0.42 -1.373,1.258 -1.821,1.677c-0.159,-0.535 -0.462,-1.591 -0.607,-2.11Z" style="fill:#3b3c3f;fill-rule:nonzero;"/><path d="M660.344,526.557c2.312,-0.809 3.15,-0.029 2.529,2.327c-2.356,0.81 -3.209,0.044 -2.529,-2.327Z" style="fill:#788897;fill-rule:nonzero;"/><path d="M408.43,529.727c0.809,0.766 0.809,0.766 0,0Z" style="fill:#737274;fill-rule:nonzero;"/><path d="M434.496,529.425c0.781,0.723 0.781,0.723 0,0Z" style="fill:#1f1e1f;fill-rule:nonzero;"/><path d="M444.136,528.646c1.902,2.493 3.3,5.821 6.484,6.916c2.176,-0.129 4.366,-0.288 6.542,-0.489c-1.369,1.887 -2.752,3.775 -4.136,5.676c0.98,2.767 1.96,5.548 3.069,8.286c-2.853,-1.513 -5.908,-2.594 -8.977,-3.545l0.475,-0.448c2.334,0.576 4.64,1.283 6.96,1.96c-0.893,-1.672 -1.772,-3.328 -2.666,-4.986c0.259,-1.037 0.764,-3.126 1.009,-4.178c-2.882,1.254 -6.239,2.031 -8.3,4.597c-0.922,1.801 -0.086,3.905 -0.101,5.85c-0.245,0.274 -0.749,0.821 -0.994,1.109c-0.159,-2.32 -0.187,-4.654 -0.634,-6.932c-1.859,-1.542 -3.919,-2.781 -5.951,-4.063c2.147,-0.591 4.28,-1.167 6.427,-1.744c0.303,-2.666 0.605,-5.346 0.793,-8.011Z" style="fill:#c4d4ec;fill-rule:nonzero;"/><path d="M629.576,530.133c3.498,-4.293 5.103,4.105 0.26,1.315l-0.26,-1.315Z" style="fill:#e798bc;fill-rule:nonzero;"/><path d="M457.711,534.987c3.747,-0.446 7.018,-2.335 10.202,-4.237c0.317,1.83 0.634,3.675 0.98,5.518c-1.556,1.096 -3.041,2.278 -4.481,3.517c-3.271,2.608 -5.865,5.908 -8.098,9.423l-0.216,-0.173c-1.11,-2.737 -2.089,-5.518 -3.069,-8.285c1.383,-1.902 2.767,-3.791 4.136,-5.678l0.548,-0.086Z" style="fill:#5d82a8;fill-rule:nonzero;"/><path d="M556.115,529.568c4.712,0.49 9.799,0.404 13.934,3.084c4.035,2.276 6.513,6.368 8.387,10.476c-0.764,1.037 -1.527,2.075 -2.291,3.097c-1.009,-9.351 -12.191,-12.204 -19.987,-10.389c-2.348,4.193 -2.824,10.49 1.384,13.718c3.978,3.055 9.309,1.57 13.891,1.397c-5.23,5.908 -16.081,3.66 -18.877,-3.646c-2.853,-5.303 0.101,-11.154 4.208,-14.741c-0.143,-0.534 -0.461,-1.614 -0.619,-2.148l-0.029,-0.85Z" style="fill:#4a4243;fill-rule:nonzero;"/><path d="M628.539,531.022l1.037,-0.893l0.259,1.311c0.591,3.069 0.894,6.196 1.167,9.324c0.835,-0.159 2.508,-0.448 3.343,-0.591c-0.49,1.584 -0.966,3.184 -1.412,4.799c-0.332,0.705 -0.995,2.132 -1.325,2.853c-5.419,1.225 -5.173,7.334 -6.109,11.672c-1.817,-1.412 -3.632,-2.838 -5.505,-4.136c-0.475,0.288 -1.441,0.879 -1.917,1.182c-0.764,0.503 -2.262,1.513 -3.026,2.017c0.288,-2.047 0.634,-4.05 0.878,-6.082c3.099,-0.562 6.643,-0.202 9.353,-2.118c1.858,-3.861 1.557,-8.3 0.936,-12.406c-0.274,-0.995 -0.835,-2.968 -1.109,-3.963c1.138,-0.993 2.291,-1.974 3.429,-2.968Z" style="fill:#9f9fa0;fill-rule:nonzero;"/><path d="M634.837,530.765c2.19,-1.744 4.452,2.176 2.219,3.53c-2.176,1.586 -4.193,-2.146 -2.219,-3.53Z" style="fill:#d189ab;fill-rule:nonzero;"/><path d="M646.61,530.924c0.867,0.737 0.867,0.737 0,0Z" style="fill:#f398bf;fill-rule:nonzero;"/><path d="M405.549,532.161c4.799,0.376 -2.385,4.235 0,0Z" style="fill:#2f2f2f;fill-rule:nonzero;"/><path d="M615.615,582.985c1.786,0.202 3.558,0.402 5.36,0.634c-0.966,1.758 -1.903,3.545 -2.825,5.331c-3.963,4.121 -7.853,8.474 -12.608,11.673c-2.508,0.158 -5.015,0.215 -7.508,0.375c0.62,-0.505 1.859,-1.499 2.478,-2.003c4.294,-3.545 8.589,-7.177 12.061,-11.558c1.052,-1.456 2.061,-2.939 3.041,-4.452Z" style="fill:#2f2f2f;fill-rule:nonzero;"/><path d="M420.116,532.32c0.737,0.766 0.737,0.766 0,0Z" style="fill:#212121;fill-rule:nonzero;"/><path d="M425.822,532.335c0.752,0.752 0.752,0.752 0,0Z" style="fill:#202021;fill-rule:nonzero;"/><path d="M431.413,532.09c4.308,-2.27 0.304,4.771 0,0Z" style="fill:#3b3b3c;fill-rule:nonzero;"/><path d="M461.76,613.045c0.81,0.693 0.81,0.693 0,0Z" style="fill:#3b3b3c;fill-rule:nonzero;"/><path d="M468.892,536.269c2.234,-1.441 4.597,-2.68 6.917,-4.02c-0.259,1.138 -0.49,2.276 -0.72,3.429c2.637,2.508 4.971,5.318 6.859,8.444c-0.533,0.793 -1.599,2.377 -2.147,3.17c-2.738,-3.286 -7.061,-6.398 -11.427,-4.077c-4.15,1.801 -4.136,6.657 -4.51,10.49l-0.677,-0.475c-2.032,-0.216 -4.078,-0.288 -6.11,-0.36c0.49,-0.404 1.455,-1.197 1.945,-1.6c3.631,-2.666 4.237,-7.464 5.389,-11.484c1.441,-1.239 2.925,-2.42 4.481,-3.516Z" style="fill:#4c4b4c;fill-rule:nonzero;"/><path d="M551.233,532.422c1.628,-0.705 3.257,-1.355 4.913,-2.003c0.159,0.533 0.475,1.614 0.62,2.148c-4.107,3.587 -7.06,9.438 -4.208,14.741c2.796,7.305 13.645,9.553 18.877,3.646c1.7,-1.441 3.213,-3.069 4.712,-4.727c0.764,-1.023 1.527,-2.06 2.291,-3.099c0.244,3.041 0.331,6.082 0.49,9.136l-1.441,-1.21c-4.784,1.829 -8.228,6.844 -13.777,6.513c-5.994,0.749 -11.7,-3.214 -14.121,-8.516c-1.225,-3.372 -1.34,-7.003 -1.859,-10.506c-3.631,3.675 -5.72,8.516 -4.711,13.748c-0.404,-1.542 -0.765,-3.099 -1.081,-4.655c0.043,-1.902 0.173,-3.804 0.389,-5.691c0.893,-1.254 1.843,-2.465 2.809,-3.66c2.191,-1.786 4.222,-3.747 6.096,-5.865Z" style="fill:#2f2d2d;fill-rule:nonzero;"/><path d="M519.386,536.312c8.732,-2.464 17.767,-4.222 26.859,-2.882c-0.36,1.614 -0.72,3.229 -1.109,4.857c-0.966,1.195 -1.917,2.407 -2.81,3.659c-7.479,-0.288 -15.173,0.865 -21.845,4.382c-2.061,1.067 -1.254,3.934 -1.83,5.792l-1.182,0.072c0.375,-5.317 0.793,-10.662 1.917,-15.879Z" style="fill:#515151;fill-rule:nonzero;"/><path d="M475.853,598.159c1.427,-0.518 2.853,-1.023 4.28,-1.541c0.303,4.726 1.196,10.778 7.09,11.196c-1.081,1.873 -1.974,3.89 -2.291,6.051c-1.801,-2.708 -3.674,-5.36 -5.75,-7.867c-1.124,-2.608 -2.233,-5.217 -3.329,-7.84Z" style="fill:#515151;fill-rule:nonzero;"/><path d="M692.274,533.761c0.781,0.781 0.781,0.781 0,0Z" style="fill:#49677f;fill-rule:nonzero;"/><path d="M402.68,535.303c0.752,0.708 0.752,0.708 0,0Z" style="fill:#333;fill-rule:nonzero;"/><path d="M476.098,627.439c0.795,0.695 0.795,0.695 0,0Z" style="fill:#333;fill-rule:nonzero;"/><path d="M416.298,534.813c1.138,0.058 3.386,0.159 4.525,0.216c0.13,1.254 0.389,3.761 0.519,5c-2.392,-0.98 -4.626,-2.465 -5.043,-5.217Z" style="fill:#141516;fill-rule:nonzero;"/><path d="M422.898,535.303c0.781,0.693 0.781,0.693 0,0Z" style="fill:#2e2e2f;fill-rule:nonzero;"/><path d="M428.59,534.988c4.38,-2.14 0.116,4.784 0,0Z" style="fill:#434344;fill-rule:nonzero;"/><path d="M441.645,618.708c0.723,0.765 0.723,0.765 0,0Z" style="fill:#434344;fill-rule:nonzero;"/><path d="M497.685,534.769c2.133,0.923 4.294,1.859 5.894,3.603c0.98,-1.153 1.945,-2.349 2.954,-3.473c1.7,5.173 1.527,10.664 2.161,16.009c0.058,0.72 0.187,2.161 0.245,2.867c-0.389,0.158 -1.167,0.446 -1.556,0.591c-0.634,0.215 -1.888,0.648 -2.507,0.878c0.231,-0.821 0.706,-2.449 0.937,-3.257c-3.041,-1.642 -6.167,-3.156 -9.352,-4.509c0.029,-4.266 0.562,-8.503 1.225,-12.71Z" style="fill:#aeabac;fill-rule:nonzero;"/><path d="M556.159,535.836c7.795,-1.816 18.978,1.037 19.986,10.39c-1.498,1.656 -3.011,3.285 -4.712,4.726c-4.582,0.174 -9.913,1.658 -13.891,-1.397c-4.208,-3.227 -3.732,-9.525 -1.383,-13.718Zm1.7,6.082c1.426,6.023 9.151,9.64 14.497,6.167c-3.646,-4.006 -8.804,-7.536 -14.497,-6.167Z" style="fill:#7c7475;fill-rule:nonzero;"/><path d="M639.678,536.787c0.649,-0.145 1.946,-0.404 2.594,-0.533l1.11,-0.231c3.198,-0.332 4.222,3.617 3.271,5.951c-1.327,1.008 -2.638,2.045 -3.934,3.097c-0.13,0.995 -0.389,2.998 -0.519,4.007l-0.044,1.944l-0.23,-1.296c-0.072,-0.317 -0.202,-0.966 -0.274,-1.283c-1.672,-0.562 -3.33,-1.124 -4.971,-1.7c-0.174,-0.707 -0.534,-2.133 -0.707,-2.838c0.116,-0.721 0.332,-2.133 0.446,-2.84l0.793,0.865c0.966,1.239 2.276,2.104 3.717,2.767c-1.325,-2.348 -2.579,-4.812 -1.643,-7.565l0.389,-0.345Z" style="fill:#d6e8f8;fill-rule:nonzero;"/><path d="M405.433,537.955c4.799,0.216 -2.226,4.322 0,0Z" style="fill:#2c2c2c;fill-rule:nonzero;"/><path d="M419.971,552.41c4.25,-2.356 0.361,4.784 0,0Z" style="fill:#2c2c2c;fill-rule:nonzero;"/><path d="M425.722,537.955c4.38,-2.096 0.072,4.785 0,0Z" style="fill:#404041;fill-rule:nonzero;"/><path d="M461.774,607.193c0.694,0.796 0.694,0.796 0,0Z" style="fill:#404041;fill-rule:nonzero;"/><path d="M578.538,537.838c2.638,1.34 5.36,2.737 7.191,5.144c5.087,6.355 9.553,13.388 12.061,21.183c2.176,5.505 -2.666,10.245 -6.139,13.891c-1.643,-10.201 -5.403,-20.044 -11.844,-28.199c0.692,-4.064 0.072,-8.156 -1.268,-12.018Z" style="fill:#363635;fill-rule:nonzero;"/><path d="M615.442,538.286c3.587,0.606 7.218,0.389 10.778,-0.331c-0.417,2.247 -0.806,4.51 -1.195,6.772c-2.911,0.058 -5.836,0.13 -8.733,0.216c-0.158,-1.599 -0.345,-3.185 -0.562,-4.755c-0.072,-0.475 -0.216,-1.428 -0.288,-1.903Z" style="fill:#d2d4d6;fill-rule:nonzero;"/><path d="M626.22,537.955c0.62,4.107 0.922,8.545 -0.936,12.406c-2.709,1.917 -6.254,1.557 -9.353,2.118c0.231,-2.508 0.361,-5.028 0.361,-7.536c2.895,-0.086 5.821,-0.158 8.733,-0.216c0.388,-2.262 0.777,-4.525 1.195,-6.772Z" style="fill:#bababc;fill-rule:nonzero;"/><path d="M663.946,538.848c3.574,-1.802 5.72,4.481 1.873,5.317c-3.328,1.153 -5.014,-4.237 -1.873,-5.317Z" style="fill:#a5c9eb;fill-rule:nonzero;"/><path d="M444.598,542.435c2.061,-2.565 5.418,-3.343 8.3,-4.597c-0.245,1.052 -0.749,3.141 -1.009,4.178c0.893,1.658 1.772,3.315 2.666,4.986c-2.32,-0.677 -4.626,-1.383 -6.96,-1.959l-0.476,0.446c-0.648,0.707 -1.974,2.089 -2.623,2.796c0.014,-1.946 -0.821,-4.05 0.101,-5.85Z" style="fill:#5172b3;fill-rule:nonzero;"/><path d="M428.475,540.823c4.308,-2.268 0.26,4.785 0,0Z" style="fill:#414041;fill-rule:nonzero;"/><path d="M456.314,549.207c2.234,-3.516 4.827,-6.815 8.098,-9.423c-1.153,4.02 -1.758,8.819 -5.389,11.484c-0.677,-0.519 -2.032,-1.542 -2.709,-2.061Z" style="fill:#141719;fill-rule:nonzero;"/><path d="M647.143,545.345c-0.475,-3.04 3.011,-5.75 5.807,-4.452c2.276,0.634 2.55,3.141 3.126,5.043c-2.636,2.377 -5.85,4.222 -9.15,1.744c0.057,-0.576 0.173,-1.744 0.216,-2.335Z" style="fill:#bed9f2;fill-rule:nonzero;"/><path d="M520.482,546.325c6.672,-3.516 14.367,-4.668 21.846,-4.38c-0.216,1.887 -0.347,3.791 -0.389,5.692c-6.557,0.101 -12.912,1.685 -18.964,4.149c-1.081,0.088 -3.242,0.26 -4.323,0.332c0.576,-1.859 -0.23,-4.727 1.83,-5.793Z" style="fill:#656565;fill-rule:nonzero;"/><path d="M557.859,541.916c5.692,-1.368 10.851,2.162 14.497,6.168c-5.346,3.473 -13.07,-0.145 -14.497,-6.168Z" style="fill:#c1bdbe;fill-rule:nonzero;"/><path d="M631.609,547.834c0.332,-0.723 0.997,-2.154 1.329,-2.862c-0.044,0.622 -0.159,1.894 -0.216,2.53l-1.113,0.332Z" style="fill:#eb88b3;fill-rule:nonzero;"/><path d="M633.15,547.911c0.705,-0.995 2.118,-3.012 2.824,-4.006c0.173,0.705 0.534,2.133 0.707,2.838c1.643,0.576 3.299,1.138 4.971,1.7c0.072,0.317 0.202,0.966 0.274,1.283c-1.109,3.027 -2.32,6.009 -3.488,9.006c2.594,0.433 5.173,0.879 7.767,1.384l-0.446,-1.47c-1.83,-0.576 -3.602,-1.197 -5.259,-2.133c1.037,-1.715 1.599,-3.545 1.658,-5.489l0.043,-1.946c3.069,-0.246 6.24,0.303 8.689,2.291c1.542,1.7 -0.086,3.026 -1.267,4.279c-0.865,3.113 -1.441,6.326 -2.724,9.309c-1.355,-1.094 -2.651,-2.247 -3.89,-3.458c-0.995,3.084 -3.992,6.124 -1.658,9.381c-2.666,-0.461 -5.317,-0.893 -7.969,-1.296c0.231,1.296 0.461,2.622 0.692,3.934c-1.21,0.215 -3.646,0.619 -4.87,0.821c0.995,-2.824 1.917,-5.678 2.478,-8.617c1.974,0.375 3.818,1.239 5.591,2.219c0.057,-0.677 0.187,-2.047 0.244,-2.724c-2.535,1.153 -4.251,0.462 -5.144,-2.06c-0.721,0.158 -2.176,0.461 -2.911,0.606c0.347,-4.569 2.781,-8.718 2.724,-13.329c2.075,-0.159 3.732,0.634 4.957,2.377c0.49,-0.576 1.485,-1.729 1.974,-2.306c-1.917,-0.894 -6.109,0.576 -5.965,-2.594Zm13.113,4.496c-1.946,0.936 -2.061,1.946 -0.317,3.027c1.946,-0.952 2.047,-1.96 0.317,-3.027Zm-11.758,2.436c-0.778,2.075 -0.159,2.767 1.843,2.075c0.808,-2.075 0.187,-2.767 -1.843,-2.075Zm-2.219,4.467c-0.606,1.96 0.116,2.651 2.161,2.075c0.62,-2.003 -0.101,-2.693 -2.161,-2.075Zm4.467,1.557c-0.606,1.917 0.116,2.666 2.176,2.262c0.649,-1.959 -0.072,-2.709 -2.176,-2.262Z" style="fill:#d475a2;fill-rule:nonzero;"/><path d="M646.081,548.254c-2.717,-0.043 -0.954,-4.74 1.069,-2.905c-0.043,0.592 -0.158,1.764 -0.216,2.342l-0.853,0.563Z" style="fill:#b06889;fill-rule:nonzero;"/><path d="M408.603,546.817c0.766,0.709 0.766,0.709 0,0Z" style="fill:#2d2d2e;fill-rule:nonzero;"/><path d="M411.528,549.613c0.708,0.752 0.708,0.752 0,0Z" style="fill:#2d2d2e;fill-rule:nonzero;"/><path d="M453.13,638.852c0.81,0.853 0.81,0.853 0,0Z" style="fill:#2d2d2e;fill-rule:nonzero;"/><path d="M420.119,546.498c4.842,0.26 -2.27,4.307 0,0Z" style="fill:#696869;fill-rule:nonzero;"/><path d="M496.46,547.481c3.185,1.353 6.311,2.867 9.352,4.509c-0.231,0.808 -0.706,2.436 -0.937,3.257c-2.493,0.938 -4.914,2.047 -7.248,3.33c-0.548,-3.69 -1.052,-7.378 -1.167,-11.095Z" style="fill:#989697;fill-rule:nonzero;"/><path d="M522.972,551.788c6.052,-2.465 12.406,-4.05 18.963,-4.15c0.317,1.557 0.677,3.112 1.081,4.654c0.202,0.721 0.604,2.177 0.821,2.897c-3.069,0.015 -6.139,0.057 -9.208,-0.086c-3.833,-1.268 -7.709,-2.478 -11.657,-3.314Z" style="fill:#777879;fill-rule:nonzero;"/><path d="M416.932,549.512c4.813,0.058 -2.096,4.395 0,0Z" style="fill:#292929;fill-rule:nonzero;"/><path d="M431.559,549.639c0.766,0.752 0.766,0.752 0,0Z" style="fill:#1c1c1d;fill-rule:nonzero;"/><path d="M479.874,552.609c3.458,0.736 6.96,0.303 10.159,-1.21c1.527,3.213 2.68,6.57 3.631,10.001c-2.81,1.988 -5.231,4.423 -7.565,6.945c-0.663,-0.116 -1.989,-0.317 -2.666,-0.418c0.548,-0.835 1.095,-1.671 1.643,-2.493c-1.455,-3.905 -3.343,-7.738 -6.845,-10.216c0.533,-0.879 1.081,-1.744 1.643,-2.608Z" style="fill:#484849;fill-rule:nonzero;"/><path d="M455.998,607.222c0.795,0.695 0.795,0.695 0,0Z" style="fill:#484849;fill-rule:nonzero;"/><path d="M579.015,553.446c0.259,-1.211 0.533,-2.407 0.792,-3.589c6.442,8.156 10.203,17.998 11.846,28.201c-4.281,4.092 -9.151,7.637 -14.669,9.842c0.345,-8.084 0.143,-18.545 -7.148,-23.632c4.641,-2.882 6.067,-8.358 7.653,-13.214l1.44,1.21l0.088,1.182Z" style="fill:#525352;fill-rule:nonzero;"/><path d="M425.477,552.48c0.564,-0.116 1.706,-0.376 2.269,-0.491l0.014,1.07c-0.535,0.158 -1.604,0.477 -2.125,0.636l-0.159,-1.215Z" style="fill:#818082;fill-rule:nonzero;"/><path d="M646.268,552.407c1.735,1.07 1.634,2.081 -0.317,3.036c-1.749,-1.084 -1.634,-2.096 0.317,-3.036Z" style="fill:#c8cbe7;fill-rule:nonzero;"/><path d="M653.036,552.78c2.089,-1.541 4.294,1.975 2.262,3.387c-2.176,1.845 -4.668,-2.06 -2.262,-3.387Z" style="fill:#985071;fill-rule:nonzero;"/><path d="M463.186,553.228l0.677,0.475c3.084,2.478 5.922,5.23 8.804,7.94c1.83,-2.176 3.675,-4.309 5.562,-6.427c3.502,2.478 5.389,6.311 6.845,10.216c-0.548,0.822 -1.095,1.658 -1.643,2.493c0.677,0.101 2.003,0.303 2.666,0.418c-2.464,3.791 -4.496,7.868 -5.937,12.162c-2.248,-1.671 -4.208,-3.688 -5.764,-6.023c-0.432,-0.288 -1.297,-0.865 -1.744,-1.138c-0.706,-6.023 -5.317,-10.245 -9.121,-14.525c-0.014,-0.49 -0.043,-1.485 -0.058,-1.99c-0.879,-0.158 -2.637,-0.475 -3.516,-0.634c1.066,-0.995 2.147,-1.988 3.228,-2.968Zm17.09,6.24c4.813,0.028 -2.075,4.38 0,0Z" style="fill:#595758;fill-rule:nonzero;"/><path d="M534.631,555.103c3.069,0.145 6.139,0.101 9.208,0.086c3.429,6.269 9.842,10.693 17.047,11.11c5.159,6.917 5.836,15.606 5.518,23.935c-1.166,0.13 -3.516,0.404 -4.683,0.534c-1.355,-6.801 -3.602,-13.459 -7.363,-19.323c-2.478,-4.641 -6.08,-8.791 -11.125,-10.664c1.801,2.983 3.934,5.793 5.476,8.934c-0.303,2.19 -2.306,3.602 -3.473,5.36l-0.518,-0.692c-1.153,-1.485 -2.276,-2.925 -3.502,-4.294c0.75,-1.773 2.724,-3.502 1.788,-5.577c-2.061,-3.703 -5.36,-6.513 -8.373,-9.41Z" style="fill:#949495;fill-rule:nonzero;"/><path d="M618.077,556.543c0.475,-0.303 1.441,-0.894 1.917,-1.182c1.874,1.298 3.69,2.724 5.505,4.136c-0.648,3.257 -1.412,6.643 -3.444,9.353c-2.968,1.109 -6.21,0.547 -9.294,0.663c0.072,-2.249 0.143,-4.482 0.288,-6.7c0.331,-1.009 0.98,-2.998 1.296,-3.992c1.614,1.384 3.214,2.767 4.828,4.165c0.98,-0.404 1.974,-0.808 2.968,-1.197c-0.332,-1.195 -0.663,-2.377 -0.98,-3.545c-0.764,-0.432 -2.306,-1.282 -3.084,-1.7Z" style="fill:#848384;fill-rule:nonzero;"/><path d="M634.505,554.843c2.039,-0.693 2.66,0 1.85,2.083c-2.009,0.693 -2.631,0 -1.85,-2.083Z" style="fill:#35212b;fill-rule:nonzero;"/><path d="M675.009,556.976c4.222,-3.271 8.516,4.265 4.05,6.269c-3.862,2.767 -7.868,-3.805 -4.05,-6.269Z" style="fill:#77b1e1;fill-rule:nonzero;"/><path d="M650.124,559.339c0.446,-3.458 3.214,-2.478 5.116,-0.576c1.239,0.606 4.826,-0.547 3.573,2.104c-1.282,2.249 -2.535,4.626 0.086,6.471l1.024,-1.729c0.461,0.619 1.397,1.887 1.858,2.508c-2.464,0.215 -4.957,-0.015 -7.378,-0.448c-0.345,-0.404 -1.037,-1.21 -1.383,-1.599l-0.159,-0.98c0.793,-0.591 2.393,-1.757 3.185,-2.348c-1.887,-1.211 -4.971,-1.254 -5.922,-3.401Z" style="fill:#aa537b;fill-rule:nonzero;"/><path d="M480.277,559.467c4.828,0.029 -2.081,4.395 0,0Z" style="fill:#6f9dc4;fill-rule:nonzero;"/><path d="M632.286,559.311c2.067,-0.622 2.789,0.072 2.168,2.081c-2.052,0.578 -2.774,-0.116 -2.168,-2.081Z" style="fill:#3e2632;fill-rule:nonzero;"/><path d="M431.471,560.81c4.394,-2.125 0.087,4.784 0,0Z" style="fill:#5a595a;fill-rule:nonzero;"/><path d="M443.199,589.687c4.452,-2.009 -0.029,4.785 0,0Z" style="fill:#5a595a;fill-rule:nonzero;"/><path d="M493.664,561.398l1.225,-0.893c-5.778,8.415 -13.243,16.887 -13.012,27.768c1.902,0.389 3.689,-0.663 5.505,-1.081c-0.115,0.547 -0.36,1.658 -0.476,2.219c-0.504,2.276 -1.037,4.538 -1.556,6.816c-1.744,0.129 -3.487,0.259 -5.216,0.389c-0.086,-5.36 -0.749,-10.765 0.029,-16.11c1.441,-4.294 3.473,-8.373 5.937,-12.162c2.334,-2.522 4.755,-4.957 7.565,-6.946Z" style="fill:#c2c2c4;fill-rule:nonzero;"/><path d="M543.233,560.78c5.043,1.873 8.646,6.023 11.125,10.664c-0.029,2.636 0.303,5.287 1.34,7.752c-2.32,0.331 -6.456,-2.104 -7.018,1.368c-1.067,-1.887 -2.219,-3.717 -3.444,-5.489c1.167,-1.758 3.17,-3.17 3.473,-5.36c-1.542,-3.142 -3.675,-5.951 -5.476,-8.934Z" style="fill:#757475;fill-rule:nonzero;"/><path d="M636.753,560.866c2.11,-0.448 2.833,0.304 2.182,2.27c-2.067,0.405 -2.789,-0.347 -2.182,-2.27Z" style="fill:#39232e;fill-rule:nonzero;"/><path d="M684.232,568.286c-4.958,-1.369 -1.369,-8.588 2.781,-6.917c4.971,1.34 2.262,9.669 -2.753,7.234c0.635,3.227 2.205,8.386 -2.306,9.423c-5.375,1.527 -7.666,-6.009 -3.242,-8.617c1.946,0.49 3.848,-0.244 5.52,-1.124Zm-3.444,4.395c0.734,0.764 0.734,0.764 0,0Z" style="fill:#5ea6db;fill-rule:nonzero;"/><path d="M698.341,560.895c4.38,-2.168 0.159,4.771 0,0Z" style="fill:#315b76;fill-rule:nonzero;"/><path d="M649.104,561.919c0.622,0.159 1.865,0.477 2.501,0.636l0.376,1.199l-0.434,1.041c-0.622,-0.058 -1.865,-0.174 -2.486,-0.231l-0.664,-1.344l0.708,-1.302Z" style="fill:#ef6fa9;fill-rule:nonzero;"/><path d="M428.645,563.793c4.365,-2.154 0.173,4.784 0,0Z" style="fill:#616061;fill-rule:nonzero;"/><path d="M434.511,563.676c4.467,-1.938 -0.116,4.784 0,0Z" style="fill:#636163;fill-rule:nonzero;"/><path d="M437.278,566.618c4.279,-2.299 0.304,4.799 0,0Z" style="fill:#636163;fill-rule:nonzero;"/><path d="M566.131,565.578c0.922,-0.332 2.781,-0.98 3.703,-1.311c7.292,5.087 7.493,15.547 7.148,23.632c-1.946,0.619 -3.891,1.153 -5.85,1.599c0.446,-8.285 -0.49,-16.744 -5,-23.92Z" style="fill:#717172;fill-rule:nonzero;"/><path d="M642.632,567.517c-0.895,-2.27 2.79,-3.787 3.528,-1.344c-0.55,0.491 -1.677,1.474 -2.226,1.966l-1.302,-0.622Z" style="fill:#f06da8;fill-rule:nonzero;"/><path d="M409.944,572.048c4.611,0.303 5.36,-7.594 9.957,-5.893c3.055,1.08 6.297,1.628 9.539,1.225c-0.461,1.34 -0.951,2.666 -1.383,4.035c1.455,-0.461 2.911,-0.923 4.366,-1.369c-0.432,1.052 -0.864,2.104 -1.282,3.17c1.917,2.118 3.804,4.279 5.649,6.484c-0.634,0.534 -1.917,1.614 -2.551,2.148c1.671,1.758 3.372,3.516 4.943,5.39c0.014,0.604 0.014,1.801 0.029,2.405l-0.375,0.361c-0.648,0.648 -1.96,1.931 -2.608,2.579l-0.49,0.417c-0.692,0.62 -2.089,1.859 -2.796,2.48c-2.968,1.642 0.043,2.55 1.931,3.486l-0.721,1.599c1.009,0.303 3.041,0.938 4.049,1.239l-2.406,1.903c-3.069,-2.32 -5.62,-5.246 -8.199,-8.07c-0.202,-1.786 -0.648,-3.516 -1.081,-5.23c0.533,-1.24 1.917,-2.652 0.504,-3.848c-4.366,-4.943 -8.819,-10.794 -15.663,-12.105c-0.36,-0.606 -1.066,-1.801 -1.412,-2.407Zm6.989,-2.464c4.352,-2.19 0.202,4.74 0,0Zm16.009,20.447c0.764,0.721 0.764,0.721 0,0Zm-2.925,2.825c0.677,0.778 0.677,0.778 0,0Z" style="fill:#9b999b;fill-rule:nonzero;"/><path d="M481.879,652.945c4.813,0.449 -2.429,4.149 0,0Z" style="fill:#9b999b;fill-rule:nonzero;"/><path d="M484.513,656.044c4.322,-2.125 0.087,4.842 0,0Z" style="fill:#9b999b;fill-rule:nonzero;"/><path d="M431.571,566.747c4.322,-2.241 0.217,4.784 0,0Z" style="fill:#646364;fill-rule:nonzero;"/><path d="M560.886,566.299c1.758,-0.173 3.502,-0.418 5.245,-0.721c4.51,7.176 5.448,15.635 5,23.92c-1.181,0.189 -3.545,0.549 -4.726,0.736c0.316,-8.329 -0.361,-17.019 -5.52,-23.935Z" style="fill:#808182;fill-rule:nonzero;"/><path d="M647.102,566.731c2.11,-0.679 4.423,1.432 3.253,3.57c-2.039,1.619 -5.233,-1.749 -3.253,-3.57Z" style="fill:#db659c;fill-rule:nonzero;"/><path d="M663.267,567.048c2.104,-1.599 4.18,2.076 2.291,3.502c-2.19,1.845 -4.467,-2.104 -2.291,-3.502Z" style="fill:#9b4a70;fill-rule:nonzero;"/><path d="M692.303,566.76c4.496,-2.983 8.588,4.726 3.89,6.873c-4.265,2.55 -8.084,-4.496 -3.89,-6.873Z" style="fill:#399cd6;fill-rule:nonzero;"/><path d="M615.729,580.723c4.323,-4.136 6.095,-10.893 11.989,-13.243l-0.562,1.773c-0.231,0.777 -0.677,2.319 -0.894,3.097l-0.389,1.197c-1.34,3.486 -3.099,6.801 -4.9,10.073c-1.801,-0.231 -3.573,-0.433 -5.36,-0.635c0.029,-0.562 0.086,-1.685 0.116,-2.262Z" style="fill:#504e4e;fill-rule:nonzero;"/><path d="M416.931,569.587c4.365,-2.198 0.202,4.755 0,0Z" style="fill:#4f4f50;fill-rule:nonzero;"/><path d="M456.128,647.368c4.828,0.376 -2.27,4.265 0,0Z" style="fill:#4f4f50;fill-rule:nonzero;"/><path d="M440.189,569.527c4.322,-2.226 0.188,4.785 0,0Z" style="fill:#626162;fill-rule:nonzero;"/><path d="M430.017,592.857c0.679,0.78 0.679,0.78 0,0Z" style="fill:#626162;fill-rule:nonzero;"/><path d="M627.16,569.253c3.007,-0.333 1.445,4.943 -0.896,3.108c0.216,-0.781 0.665,-2.328 0.896,-3.108Z" style="fill:#905771;fill-rule:nonzero;"/><path d="M641.58,570.723c4.307,-2.284 0.246,4.857 0,0Z" style="fill:#b1517e;fill-rule:nonzero;"/><path d="M443.057,572.41c4.322,-2.27 0.275,4.769 0,0Z" style="fill:#5c5c5d;fill-rule:nonzero;"/><path d="M608.206,574.166c0.692,-0.648 2.104,-1.975 2.796,-2.623c1.6,3.04 3.214,6.082 4.727,9.179c-0.029,0.576 -0.086,1.701 -0.116,2.263c-0.98,1.513 -1.988,2.996 -3.04,4.452c-3.488,-1.426 -6.831,-3.17 -9.957,-5.259c2.348,-2.291 4.107,-5.087 5.59,-8.011Z" style="fill:#e6e6e7;fill-rule:nonzero;"/><path d="M680.788,572.682c0.738,0.766 0.738,0.766 0,0Z" style="fill:#3d5f77;fill-rule:nonzero;"/><path d="M625.875,573.547c1.887,1.325 4.092,1.557 6.368,1.47l-0.547,1.383c-0.85,1.801 -1.167,3.791 -1.34,5.764c-0.332,0.576 -0.995,1.729 -1.312,2.306c-0.173,0.907 -0.503,2.709 -0.663,3.602c-2.247,1.413 -4.351,-1.138 -6.455,-1.729c1.426,3.257 -0.145,6.139 -3.949,5.059l0.173,-2.45c0.923,-1.786 1.859,-3.574 2.825,-5.331c1.801,-3.271 3.559,-6.585 4.9,-10.073Zm-0.707,11.354c2.247,-0.922 1.397,-4.077 2.089,-5.95c-0.029,-0.202 -0.072,-0.591 -0.101,-0.793c-2.075,0.244 -5.188,5.821 -1.988,6.743Z" style="fill:#ce5790;fill-rule:nonzero;"/><path d="M700.891,573.806c2.363,-1.757 5.388,-0.143 4.006,2.911c-1.931,1.729 -5.533,-0.26 -4.006,-2.911Z" style="fill:#1e6891;fill-rule:nonzero;"/><path d="M440.304,575.408c4.221,-2.399 0.419,4.785 0,0Z" style="fill:#626163;fill-rule:nonzero;"/><path d="M455.896,575.421c4.307,-2.284 0.318,4.784 0,0Z" style="fill:#393a3b;fill-rule:nonzero;"/><path d="M686.639,575.477c2.219,-1.816 6.744,-0.648 6.542,2.594c0.966,3.141 -2.752,5.937 -5.533,4.409c-2.853,-1.024 -3.2,-5.144 -1.009,-7.003Z" style="fill:#3b9cd6;fill-rule:nonzero;"/><path d="M631.696,576.4l0.85,-0.029c1.369,2.076 0.044,4.497 -1.441,6.082l-0.749,-0.288c0.173,-1.974 0.49,-3.963 1.34,-5.764Z" style="fill:#bbadd3;fill-rule:nonzero;"/><path d="M632.546,576.372c2.017,-1.584 5.159,1.586 3.717,3.617c-0.894,2.493 -1.225,7.163 -5.188,5.461c0,-0.749 0.015,-2.247 0.029,-2.996c1.484,-1.586 2.809,-4.007 1.441,-6.082Z" style="fill:#c55e94;fill-rule:nonzero;"/><path d="M660.343,577.091c4.769,0.029 -2.053,4.424 0,0Z" style="fill:#492838;fill-rule:nonzero;"/><path d="M664.707,581.386c-2.825,-1.052 0.375,-5.318 2.421,-4.02c1.181,1.599 -0.303,4.553 -2.421,4.02Z" style="fill:#833b61;fill-rule:nonzero;"/><path d="M548.681,580.564c0.562,-3.472 4.698,-1.037 7.017,-1.368c0.347,0.907 1.039,2.752 1.399,3.674c-1.456,1.268 -2.882,2.537 -4.323,3.805c-1.384,-2.032 -2.781,-4.05 -4.092,-6.111Z" style="fill:#e3e2e4;fill-rule:nonzero;"/><path d="M505.998,589.325c2.781,-8.199 12.436,-11.354 20.087,-8.112c1.657,1.7 3.184,3.559 4.424,5.605c-0.519,-0.547 -1.556,-1.658 -2.075,-2.219c-0.893,-0.547 -2.709,-1.656 -3.602,-2.205l0.562,-0.259c-3.502,-0.375 -7.205,-1.138 -10.577,0.332c-4.136,1.383 -6.47,5.317 -9.294,8.329l0.475,-1.47Z" style="fill:#cecfd1;fill-rule:nonzero;"/><path d="M418.26,600.911c1.98,0.823 2.053,1.806 0.246,2.948c-1.937,-0.823 -2.009,-1.806 -0.246,-2.948Z" style="fill:#cecfd1;fill-rule:nonzero;"/><path d="M437.178,604.238c2.507,-1.181 5.288,-1.166 7.997,-1.225c-0.865,2.133 -1.715,4.281 -2.55,6.427c0.937,-0.244 2.81,-0.734 3.746,-0.993c-1.167,3.472 2.205,4.999 4.381,6.829c0.058,0.879 0.173,2.623 0.245,3.502c-1.383,-1.268 -2.594,-2.68 -3.646,-4.222c-0.389,0.764 -1.153,2.276 -1.542,3.026c-0.504,-0.143 -1.528,-0.446 -2.032,-0.606c1.052,-1.368 1.787,-2.895 2.205,-4.567c-1.628,0.635 -3.17,1.456 -4.726,2.219c-0.173,-0.274 -0.519,-0.821 -0.677,-1.096c1.038,-0.835 2.061,-1.671 3.069,-2.535c-2.19,-2.162 -5.144,-3.89 -6.47,-6.759Zm3.026,0.317c4.496,-2.017 -0.216,4.885 0,0Z" style="fill:#cecfd1;fill-rule:nonzero;"/><path d="M434.712,632.209c-0.663,-3.415 3.199,-4.178 5.13,-5.979c-0.836,1.917 -1.542,3.933 -1.614,6.051l-0.072,0.736c-0.591,0.215 -1.772,0.634 -2.363,0.835c-0.072,0.029 -0.23,0.086 -0.303,0.116l0.014,0c0.735,0.576 2.219,1.729 2.968,2.306l0.418,0.331l0.62,-0.23c1.614,0.072 3.17,0.634 4.539,1.498c-4.179,1.21 -0.504,6.801 2.162,3.343c0.908,2.017 1.989,3.949 3.055,5.893c-0.677,0.98 -1.34,1.96 -1.989,2.955c-0.937,2.291 -0.086,3.242 2.551,2.867c-0.086,0.778 -0.259,2.319 -0.331,3.084c0.864,-0.029 2.623,-0.101 3.502,-0.13c-0.519,1.584 -1.556,2.709 -3.113,3.372c-0.36,-0.75 -1.095,-2.234 -1.455,-2.983c-3.761,-0.894 0.101,3.156 1.398,3.789c-0.634,0.448 -1.902,1.355 -2.536,1.801c-0.576,-2.723 -2.046,-3.53 -4.424,-2.392c1.124,1.182 2.262,2.379 3.43,3.559c-2.017,-0.288 -7.003,-1.412 -5.663,-4.409c0.865,-0.288 2.608,-0.893 3.487,-1.195c-2.968,-2.377 -6.023,2.19 -7.637,4.38c-0.389,-0.418 -1.182,-1.268 -1.571,-1.685c1.96,-2.205 4.726,-4.079 5.159,-7.234c-1.787,1.34 -3.502,2.767 -5.361,4.006c-0.144,0.576 -0.432,1.758 -0.562,2.349c-0.648,-0.534 -1.931,-1.614 -2.565,-2.148c0.85,-0.663 2.551,-2.003 3.401,-2.666c-0.692,-0.475 -2.075,-1.426 -2.767,-1.902c-0.274,0.951 -0.836,2.853 -1.11,3.804c-3.357,-1.513 -1.527,-3.257 0.807,-4.683c-1.354,-1.296 -2.709,-2.55 -3.833,-4.05c-2.363,-1.397 -2.868,-3.573 0.519,-4.034c0.086,1.065 0.288,3.198 0.375,4.265c0.663,-0.692 1.989,-2.089 2.666,-2.796l-2.349,-1.195l-0.648,-3.156c1.888,0.993 3.689,2.161 5.548,3.227c-1.138,-3.113 -4.395,-4.799 -5.62,-7.796c0.821,-0.259 2.435,-0.792 3.242,-1.052c-0.274,-2.045 0.692,-2.996 2.896,-2.853Zm-2.248,4.107c0.922,0.865 0.922,0.865 0,0Zm3.257,2.68c0.764,0.894 0.764,0.894 0,0Zm-3.041,8.92c0.836,0.835 0.836,0.835 0,0Zm2.824,2.392c4.712,0.389 -2.392,4.279 0,0Zm8.761,2.926c4.755,0.043 -2.017,4.437 0,0Z" style="fill:#cecfd1;fill-rule:nonzero;"/><path d="M411.312,581.257c4.25,-2.371 0.39,4.784 0,0Z" style="fill:#7f7f80;fill-rule:nonzero;"/><path d="M445.938,581.056c4.337,-2.198 0.202,4.799 0,0Z" style="fill:#5e5c5e;fill-rule:nonzero;"/><path d="M526.085,581.212c6.7,0.534 14.093,-0.979 20.173,2.638c3.3,3.458 2.926,8.56 3.099,12.983c-4.54,-2.075 -9.237,-3.818 -14.078,-4.999c-0.187,1.08 -0.549,3.227 -0.721,4.309c-0.216,0.59 -0.634,1.743 -0.835,2.319c-0.533,-4.006 -1.383,-8.026 -3.213,-11.643c-1.239,-2.047 -2.767,-3.905 -4.424,-5.606Z" style="fill:#434342;fill-rule:nonzero;"/><path d="M548.681,580.564c1.311,2.061 2.708,4.079 4.092,6.109c1.441,-1.267 2.867,-2.535 4.323,-3.804c0.576,2.205 0.98,4.439 1.34,6.7c-1.887,0.663 -3.791,1.34 -5.663,2.017c-0.576,2.249 -1.138,4.51 -1.628,6.801c0.835,-6.038 0.187,-12.305 -2.464,-17.825Z" style="fill:#c5c3c5;fill-rule:nonzero;"/><path d="M658.701,581.604c4.799,0.288 -2.284,4.307 0,0Z" style="fill:#86335c;fill-rule:nonzero;"/><path d="M505.523,590.796c2.824,-3.012 5.159,-6.946 9.294,-8.329c3.372,-1.47 7.075,-0.707 10.577,-0.332l-0.562,0.259c-4.971,2.335 -11.038,4.064 -13.718,9.324c-1.672,4.755 4.078,7.622 8.069,6.7c3.847,-0.288 4.395,-4.799 5.879,-7.521l0.721,-1.628l0.749,0.518c0.764,5.144 -2.363,10.304 -7.623,11.225c-5.634,1.671 -8.747,-4.237 -11.888,-7.681c-1.153,3.703 -0.865,7.551 1.196,10.88l0.259,0.59c-3.17,4.482 -4.885,9.713 -5.706,15.102c-2.493,-1.946 -4.784,-4.165 -6.845,-6.572c2.421,-7.507 9.323,-13.631 8.415,-21.974c0.418,-0.505 1.239,-1.527 1.657,-2.032l-0.476,1.47Z" style="fill:#4f4f4f;fill-rule:nonzero;"/><path d="M661.58,582.049c2.579,0 5.115,0.288 4.726,3.574c1.009,-0.158 3.012,-0.505 4.02,-0.663c-0.404,0.778 -1.195,2.348 -1.599,3.126c-0.562,-0.49 -1.672,-1.484 -2.234,-1.988c-1.181,0.173 -3.53,0.49 -4.712,0.648c-0.057,-1.166 -0.143,-3.53 -0.201,-4.698Z" style="fill:#ac3b73;fill-rule:nonzero;"/><path d="M448.862,583.938c4.221,-2.415 0.434,4.769 0,0Z" style="fill:#666566;fill-rule:nonzero;"/><path d="M511.115,591.717c2.68,-5.259 8.747,-6.989 13.718,-9.324c0.562,2.45 1.182,4.914 1.7,7.392l-0.749,-0.518c-0.677,-0.389 -2.017,-1.153 -2.68,-1.542c-4.006,1.167 -7.536,3.602 -9.006,7.651c4.453,1.311 8.084,-1.426 10.966,-4.481c-1.484,2.723 -2.032,7.233 -5.879,7.521c-3.991,0.922 -9.741,-1.946 -8.069,-6.7Z" style="fill:#7d7576;fill-rule:nonzero;"/><path d="M524.832,582.394c0.893,0.549 2.709,1.658 3.602,2.205c0.879,5.649 1.282,12.523 -3.977,16.255c-4.467,4.265 -10.62,1.944 -15.62,0.215c-0.159,0.793 -0.461,2.364 -0.62,3.142c-2.061,-3.33 -2.349,-7.177 -1.196,-10.88c3.141,3.444 6.254,9.353 11.888,7.681c5.26,-0.922 8.386,-6.082 7.623,-11.225c-0.519,-2.478 -1.138,-4.942 -1.7,-7.392Z" style="fill:#2e2d2c;fill-rule:nonzero;"/><path d="M441.473,587.048c0.752,0.737 0.752,0.737 0,0Z" style="fill:#4b4a4b;fill-rule:nonzero;"/><path d="M453.186,598.519c0.737,0.752 0.737,0.752 0,0Z" style="fill:#4b4a4b;fill-rule:nonzero;"/><path d="M453.157,610.176c0.766,0.724 0.766,0.724 0,0Z" style="fill:#4b4a4b;fill-rule:nonzero;"/><path d="M445.967,586.92c4.192,-2.472 0.52,4.756 0,0Z" style="fill:#605f61;fill-rule:nonzero;"/><path d="M451.847,586.805c4.307,-2.241 0.246,4.784 0,0Z" style="fill:#656465;fill-rule:nonzero;"/><path d="M630.053,586.862c2.371,-0.752 3.15,0.058 2.371,2.4c-2.356,0.752 -3.152,-0.044 -2.371,-2.4Z" style="fill:#bc457f;fill-rule:nonzero;"/><path d="M514.096,595.376c1.47,-4.05 5,-6.484 9.006,-7.651c0.663,0.389 2.003,1.153 2.68,1.542l-0.72,1.628c-2.882,3.055 -6.513,5.792 -10.966,4.481Z" style="fill:#b1acad;fill-rule:nonzero;"/><path d="M432.941,590.031c0.766,0.723 0.766,0.723 0,0Z" style="fill:#595859;fill-rule:nonzero;"/><path d="M438.835,590.003l0.376,-0.361l0,0.752l-0.376,-0.39Z" style="fill:#4a494b;fill-rule:nonzero;"/><path d="M552.773,591.587c1.874,-0.677 3.776,-1.355 5.663,-2.017c0.116,2.249 0.143,4.496 0.116,6.744c-3.805,1.498 -6.153,4.726 -8.2,8.098l-0.114,-0.993c0.23,-1.268 0.677,-3.776 0.907,-5.03c0.49,-2.291 1.052,-4.553 1.628,-6.801Z" style="fill:#aca9aa;fill-rule:nonzero;"/><path d="M623.137,589.628c1.917,-2.55 5.562,1.312 3.099,3.17c-1.902,1.758 -4.683,-1.282 -3.099,-3.17Z" style="fill:#cd4688;fill-rule:nonzero;"/><path d="M423.907,592.899c4.799,-0.116 -1.923,4.452 0,0Z" style="fill:#848485;fill-rule:nonzero;"/><path d="M435.737,593.002l0.494,-0.421c-0.102,0.131 -0.32,0.392 -0.421,0.523l-0.073,-0.102Z" style="fill:#49494a;fill-rule:nonzero;"/><path d="M441.429,592.915c0.766,0.737 0.766,0.737 0,0Z" style="fill:#424243;fill-rule:nonzero;"/><path d="M493.564,605.767c1.96,-5.836 6.672,-10.073 10.778,-14.41c0.908,8.343 -5.994,14.468 -8.415,21.976c-1.282,-2.276 -3.357,-4.799 -2.363,-7.565Z" style="fill:#3a3939;fill-rule:nonzero;"/><path d="M535.28,591.833c4.841,1.182 9.539,2.926 14.078,5c0.143,2.205 0.505,4.395 0.879,6.585l0.114,0.995l-1.065,-0.475l-0.808,-0.505c-4.366,-2.925 -9.035,-5.36 -13.92,-7.292c0.174,-1.08 0.534,-3.227 0.721,-4.309Z" style="fill:#606060;fill-rule:nonzero;"/><path d="M619.07,592.395c2.867,2.176 3.027,5.649 3.271,8.948c-2.579,-0.604 -4.841,-1.83 -5.792,-4.452c0.85,-1.499 1.685,-2.998 2.521,-4.496Z" style="fill:#97c1e7;fill-rule:nonzero;"/><path d="M579.761,599.124c3.833,-1.384 7.42,-3.488 11.398,-4.51c3.415,0.619 6.311,2.809 9.351,4.38c-0.62,0.505 -1.859,1.498 -2.478,2.003c-4.035,2.392 -8.171,4.626 -12.508,6.456c-1.166,-3.271 -1.887,-7.292 -5.763,-8.329Z" style="fill:#989596;fill-rule:nonzero;"/><path d="M501.028,620.869c6.83,4.496 15.087,5.649 23.113,5.173l1.47,1.397c-0.893,0.288 -2.651,0.85 -3.545,1.125c-6.225,-0.015 -13.444,-3.17 -18.646,1.757c-2.32,-1.124 -4.554,-2.407 -6.7,-3.804c1.412,-0.995 2.824,-1.974 4.251,-2.939c0.014,-0.677 0.043,-2.032 0.058,-2.709Z" style="fill:#989596;fill-rule:nonzero;"/><path d="M420.217,595.594c2.024,-0.464 2.703,0.303 2.024,2.312c-2.038,0.448 -2.718,-0.317 -2.024,-2.312Z" style="fill:#777779;fill-rule:nonzero;"/><path d="M444.295,595.708c0.752,0.737 0.752,0.737 0,0Z" style="fill:#494849;fill-rule:nonzero;"/><path d="M458.893,604.327c0.665,0.81 0.665,0.81 0,0Z" style="fill:#494849;fill-rule:nonzero;"/><path d="M450.232,595.752c0.737,0.752 0.737,0.752 0,0Z" style="fill:#474747;fill-rule:nonzero;"/><path d="M534.559,596.141c4.885,1.931 9.553,4.366 13.919,7.292c-0.705,1.7 -1.355,3.429 -2.118,5.1c-3.804,-3.991 -8.731,-6.556 -14.323,-6.556c0.62,-1.153 1.182,-2.32 1.686,-3.517c0.202,-0.575 0.62,-1.728 0.836,-2.319Z" style="fill:#767677;fill-rule:nonzero;"/><path d="M550.352,604.412c2.047,-3.372 4.395,-6.601 8.199,-8.099c-0.028,0.908 -0.086,2.724 -0.101,3.631c-0.576,2.421 -1.138,4.87 -1.801,7.277c-2.06,0.231 -4.121,0.475 -6.181,0.721c-0.216,-0.721 -0.649,-2.177 -0.865,-2.911l-0.317,-1.094l1.067,0.475Z" style="fill:#8e8c8d;fill-rule:nonzero;"/><path d="M628.05,597.006c3.227,-0.418 6.095,1.239 6.455,4.654c-2.853,0.634 -5.259,2.377 -7.982,3.299c-0.36,-0.907 -1.096,-2.737 -1.47,-3.644c1.023,-1.441 2.017,-2.869 2.998,-4.309Z" style="fill:#85b8e3;fill-rule:nonzero;"/><path d="M458.864,598.605c0.741,0.726 0.741,0.726 0,0Z" style="fill:#585758;fill-rule:nonzero;"/><path d="M567.573,601.92c4.149,-0.418 8.3,-1.239 12.191,-2.796c3.876,1.037 4.597,5.058 5.763,8.329c-8.559,3.516 -17.853,4.525 -27.032,4.538c0.806,-3.257 1.671,-6.513 2.334,-9.812c2.234,-0.029 4.496,0 6.744,-0.259Zm9.322,-0.303c0.376,1.671 0.563,3.516 1.946,4.74c2.276,-0.677 1.917,-2.91 1.946,-4.799c-0.966,0.015 -2.911,0.044 -3.891,0.058Z" style="fill:#767373;fill-rule:nonzero;"/><path d="M429.856,601.588c0.766,0.867 0.766,0.867 0,0Z" style="fill:#8d8d8f;fill-rule:nonzero;"/><path d="M456.012,601.43c0.795,0.693 0.795,0.693 0,0Z" style="fill:#4a494a;fill-rule:nonzero;"/><path d="M461.774,601.501c0.766,0.737 0.766,0.737 0,0Z" style="fill:#424242;fill-rule:nonzero;"/><path d="M467.51,601.43c0.694,0.78 0.694,0.78 0,0Z" style="fill:#3c3c3d;fill-rule:nonzero;"/><path d="M473.347,601.515c0.723,0.766 0.723,0.766 0,0Z" style="fill:#353435;fill-rule:nonzero;"/><path d="M476.127,604.442c0.781,0.708 0.781,0.708 0,0Z" style="fill:#353435;fill-rule:nonzero;"/><path d="M532.038,601.976c5.591,0 10.52,2.565 14.324,6.557c-0.461,0.821 -1.399,2.449 -1.859,3.257c-4.51,-3.631 -10.014,-5.491 -15.764,-5.779c1.153,-1.298 2.248,-2.666 3.3,-4.035Z" style="fill:#8c8c8d;fill-rule:nonzero;"/><path d="M421.314,604.541c0.781,0.737 0.781,0.737 0,0Z" style="fill:#545455;fill-rule:nonzero;"/><path d="M470.435,604.369c0.723,0.766 0.723,0.766 0,0Z" style="fill:#343435;fill-rule:nonzero;"/><path d="M502.77,619.903c0.821,-5.39 2.536,-10.62 5.706,-15.101c2.406,1.527 4.914,2.925 7.551,4.02c-3.257,4.395 -5.245,9.553 -4.813,15.086c-2.911,-1.138 -5.721,-2.493 -8.444,-4.006Z" style="fill:#707071;fill-rule:nonzero;"/><path d="M549.602,605.032c0.216,0.734 0.648,2.19 0.865,2.911c-5.028,9.77 -14.007,17.219 -24.857,19.496l-1.47,-1.397c3.487,-0.692 6.859,-1.802 10.26,-2.854c0.303,-0.648 0.907,-1.917 1.21,-2.55c-3.53,1.614 -7.118,3.141 -10.922,3.905c-0.259,-0.806 -0.793,-2.434 -1.052,-3.242c2.061,-2.737 4.208,-6.844 8.343,-5.937c2.724,-0.202 3.848,2.335 4.092,4.655c0.475,0.518 0.951,1.023 1.441,1.541c4.799,-4.87 9.986,-9.798 12.09,-16.527Z" style="fill:#d4d8db;fill-rule:nonzero;"/><path d="M435.751,607.237c0.723,0.781 0.723,0.781 0,0Z" style="fill:#4d4c4d;fill-rule:nonzero;"/><path d="M468.086,607.222l0.13,0.088l0.116,0.231c-0.058,-0.073 -0.188,-0.231 -0.246,-0.319Z" style="fill:#383839;fill-rule:nonzero;"/><path d="M470.364,659.141c0.752,0.723 0.752,0.723 0,0Z" style="fill:#383839;fill-rule:nonzero;"/><path d="M473.331,607.208c0.766,0.695 0.766,0.695 0,0Z" style="fill:#464547;fill-rule:nonzero;"/><path d="M487.224,607.813l1.744,-1.109c1.599,3.646 3.732,7.075 6.528,9.943c-1.787,1.34 -3.574,2.695 -5.346,4.035c-1.873,-2.177 -3.631,-4.439 -5.216,-6.816c0.317,-2.162 1.21,-4.18 2.291,-6.052Z" style="fill:#e6e5e7;fill-rule:nonzero;"/><path d="M521.705,609.095c2.45,-0.734 4.813,-1.786 7.032,-3.084c5.749,0.288 11.254,2.148 15.765,5.779c-1.153,1.498 -2.364,2.983 -3.646,4.366c-3.833,-5.475 -12.465,-7.118 -17.191,-1.93c-3.242,2.565 -3.199,6.916 -3.689,10.648c-1.081,-0.057 -3.228,-0.187 -4.294,-0.244c-0.072,-5.836 2.234,-11.182 6.023,-15.534Z" style="fill:#a3a5a6;fill-rule:nonzero;"/><path d="M550.466,607.943c2.06,-0.246 4.121,-0.49 6.181,-0.721c-5.461,16.83 -23.473,28.272 -41.024,26.312c2.089,-1.715 4.208,-3.429 6.441,-4.971c0.893,-0.274 2.651,-0.835 3.545,-1.124c10.851,-2.276 19.828,-9.726 24.857,-19.496Z" style="fill:#636161;fill-rule:nonzero;"/><path d="M595.598,609.283c0.116,-0.85 0.347,-2.565 0.448,-3.415c0.764,1.283 1.454,2.767 2.031,4.178l0.029,0.433c0.49,5.418 0.288,10.879 0.721,16.298c1.484,0.49 2.996,0.878 4.553,1.166l0.547,0.116c-6.758,3.286 -14.51,2.709 -21.355,0.058c1.744,-0.663 3.502,-1.355 5.259,-2.032c-1.052,-0.562 -2.075,-1.11 -3.097,-1.643c4.351,0.922 8.746,0.317 12.867,-1.254c-3.271,-3.126 -4.121,-7.91 -1.239,-11.672c-1.325,-0.562 -2.622,-1.153 -3.905,-1.773c0.793,-0.114 2.349,-0.345 3.141,-0.461Z" style="fill:#f7b127;fill-rule:nonzero;"/><path d="M438.532,610.233c0.824,0.823 0.824,0.823 0,0Z" style="fill:#8a8a8c;fill-rule:nonzero;"/><path d="M458.965,610.12c0.741,0.741 0.741,0.741 0,0Z" style="fill:#474748;fill-rule:nonzero;"/><path d="M491.531,668.251c-0.043,-0.174 -0.101,-0.521 -0.145,-0.695l0.145,0.695Z" style="fill:#474748;fill-rule:nonzero;"/><path d="M464.73,610.076c0.737,0.765 0.737,0.765 0,0Z" style="fill:#3f3e3f;fill-rule:nonzero;"/><path d="M476.171,610.106c0.694,0.78 0.694,0.78 0,0Z" style="fill:#464647;fill-rule:nonzero;"/><path d="M511.215,623.908c-0.432,-5.533 1.556,-10.692 4.813,-15.086c1.888,0.158 3.775,0.244 5.677,0.274c-3.79,4.352 -6.095,9.698 -6.023,15.534c-1.124,-0.173 -3.343,-0.549 -4.467,-0.721Z" style="fill:#8f8f90;fill-rule:nonzero;"/><path d="M598.078,610.048c3.89,-1.197 8.876,-0.389 10.793,3.631c3.027,5.259 -0.215,11.975 -5.489,14.266c-1.557,-0.288 -3.069,-0.677 -4.554,-1.167c-0.432,-5.418 -0.23,-10.879 -0.72,-16.298l-0.029,-0.432Z" style="fill:#f8db24;fill-rule:nonzero;"/><path d="M523.665,614.226c4.726,-5.188 13.358,-3.545 17.191,1.93c-1.57,1.327 -3.156,2.608 -4.784,3.862c-0.244,-2.32 -1.369,-4.856 -4.092,-4.654c-4.136,-0.908 -6.283,3.198 -8.343,5.937c0.259,0.806 0.793,2.434 1.052,3.242c-1.182,0.086 -3.53,0.244 -4.712,0.331c0.49,-3.732 0.447,-8.083 3.689,-10.648Z" style="fill:#bcbec0;fill-rule:nonzero;"/><path d="M479.096,612.986c0.737,0.766 0.737,0.766 0,0Z" style="fill:#313132;fill-rule:nonzero;"/><path d="M473.331,624.5c0.752,0.752 0.752,0.752 0,0Z" style="fill:#313132;fill-rule:nonzero;"/><path d="M438.776,615.926c0.741,0.741 0.741,0.741 0,0Z" style="fill:#39393a;fill-rule:nonzero;"/><path d="M484.816,667.772c0.766,0.708 0.766,0.708 0,0Z" style="fill:#39393a;fill-rule:nonzero;"/><path d="M458.806,616.026c0.781,0.794 0.781,0.794 0,0Z" style="fill:#181819;fill-rule:nonzero;"/><path d="M464.585,616.288l-0.102,-0.232l0.436,0.494c-0.087,-0.073 -0.247,-0.19 -0.334,-0.262Z" style="fill:#2a2b2c;fill-rule:nonzero;"/><path d="M569.574,615.348c4.496,2.608 8.142,6.542 13.199,8.185c0.086,-0.922 0.274,-2.739 0.36,-3.66c0.058,1.268 0.116,2.537 0.174,3.818l1.426,0.749c1.023,0.534 2.045,1.081 3.097,1.643c-1.757,0.677 -3.516,1.369 -5.259,2.032c-5.375,-2.493 -9.021,-8.329 -12.997,-12.767Z" style="fill:#ad4e23;fill-rule:nonzero;"/><path d="M495.494,616.645c1.758,1.498 3.617,2.911 5.533,4.222c-0.014,0.677 -0.043,2.032 -0.058,2.709c-1.427,0.964 -2.839,1.944 -4.251,2.939c-2.349,-1.773 -4.568,-3.703 -6.571,-5.836c1.772,-1.34 3.559,-2.695 5.346,-4.035Z" style="fill:#b7b4b5;fill-rule:nonzero;"/><path d="M556.39,616.762c2.897,-0.029 5.85,-0.259 8.747,0.202c5.461,3.501 10.029,8.386 13.675,13.746c-2.867,0.347 -5.706,0.692 -8.545,1.052c-3.905,-5.649 -8.847,-10.389 -13.876,-15Z" style="fill:#ef4023;fill-rule:nonzero;"/><path d="M542.613,630.147c3.905,-2.867 7.018,-6.657 10.374,-10.115c1.11,4.366 2.566,8.632 4.251,12.81c0.922,-2.377 1.527,-8.602 5.23,-6.268c1.11,4.452 1.96,8.977 2.177,13.559c-1.874,-0.158 -3.761,-0.303 -5.621,-0.461c-1.225,-2.061 -2.478,-4.107 -3.775,-6.095c0.274,3.169 0.692,6.325 1.167,9.482c-0.404,-0.576 -1.211,-1.729 -1.614,-2.306c-1.917,-0.187 -3.833,-0.389 -5.75,-0.62c-3.344,1.067 -6.601,2.377 -9.742,3.934c-0.778,-0.36 -2.306,-1.052 -3.069,-1.397c-0.086,-1.658 -0.143,-3.271 -0.202,-4.9c-1.383,0.85 -2.795,1.671 -4.207,2.449c0.043,-1.902 0.101,-3.804 0.173,-5.691c3.559,-1.384 7.305,-2.407 10.606,-4.382Zm8.415,-5.375c-1.182,2.047 0.015,4.885 1.786,6.24c1.801,1.254 1.744,-1.542 1.009,-2.407c-0.764,-1.195 -0.966,-4.006 -2.796,-3.833Zm-3.126,10.102c-0.475,2.032 0.274,2.737 2.262,2.089c0.461,-2.045 -0.288,-2.737 -2.262,-2.089Zm-2.753,2.737c4.799,0.332 -2.319,4.222 0,0Z" style="fill:#3e4244;fill-rule:nonzero;"/><path d="M438.72,621.66c0.781,0.708 0.781,0.708 0,0Z" style="fill:#353536;fill-rule:nonzero;"/><path d="M444.483,621.719c0.781,0.838 0.781,0.838 0,0Z" style="fill:#818184;fill-rule:nonzero;"/><path d="M464.585,653.29c0.795,0.839 0.795,0.839 0,0Z" style="fill:#818184;fill-rule:nonzero;"/><path d="M450.218,621.776c0.853,0.854 0.853,0.854 0,0Z" style="fill:#9e9c9e;fill-rule:nonzero;"/><path d="M455.594,621.273c4.857,0.347 -2.327,4.263 0,0Z" style="fill:#7e7c7e;fill-rule:nonzero;"/><path d="M470.364,621.66c0.737,0.794 0.737,0.794 0,0Z" style="fill:#1a1a1b;fill-rule:nonzero;"/><path d="M476.171,621.719c0.752,0.708 0.752,0.708 0,0Z" style="fill:#444446;fill-rule:nonzero;"/><path d="M435.795,624.63c0.824,0.708 0.824,0.708 0,0Z" style="fill:#5a5b5c;fill-rule:nonzero;"/><path d="M441.486,624.687c0.882,0.794 0.882,0.794 0,0Z" style="fill:#949496;fill-rule:nonzero;"/><path d="M447.293,624.643c0.824,0.895 0.824,0.895 0,0Z" style="fill:#a4a3a5;fill-rule:nonzero;"/><path d="M453.013,624.643c0.81,0.867 0.81,0.867 0,0Z" style="fill:#9c9b9d;fill-rule:nonzero;"/><path d="M551.03,624.772c1.83,-0.173 2.032,2.636 2.796,3.833c0.734,0.865 0.793,3.66 -1.009,2.407c-1.772,-1.355 -2.968,-4.193 -1.786,-6.24Z" style="fill:#0d0f10;fill-rule:nonzero;"/><path d="M432.912,627.584c0.795,0.796 0.795,0.796 0,0Z" style="fill:#757577;fill-rule:nonzero;"/><path d="M443.158,629.068c0.202,-0.619 0.605,-1.858 0.807,-2.478l1.671,-0.043c0.202,0.749 0.576,2.233 0.764,2.968c0.576,-0.145 1.7,-0.418 2.262,-0.562c0.749,0.907 2.234,2.737 2.968,3.659c0.159,-0.979 0.461,-2.939 0.62,-3.919l2.205,0.865c0.144,0.62 0.447,1.845 0.591,2.45c0.864,0.187 2.579,0.547 3.444,0.72c-1.196,1.167 -2.551,2.118 -4.092,2.84c0.764,0.734 2.306,2.218 3.069,2.954c0.216,-0.908 0.634,-2.709 0.85,-3.603l2.507,0.534c-0.677,-2.003 -1.326,-3.992 -2.003,-5.966c2.277,1.355 4.237,3.358 6.657,4.439c0.836,-2.061 1.124,-4.251 -0.735,-5.836l0.49,0.547c0.49,0.576 1.499,1.715 2.003,2.276c0.014,0.044 0.058,0.101 0.072,0.145c0.461,1.08 0.937,2.233 1.455,3.314c0.346,0.461 1.038,1.412 1.383,1.887c-2.637,-1.037 -4.64,1.758 -7.277,1.34c0.231,0.736 0.692,2.19 0.922,2.926c-0.692,0.086 -2.09,0.259 -2.781,0.345c-0.043,0.778 -0.159,2.335 -0.202,3.113c-2.32,0.663 -3.502,-1.715 -5.044,-2.897c0,0.98 0.014,2.954 0.014,3.949c1.888,0.619 3.775,1.282 5.677,1.902l-1.21,2.434c1.34,0.475 2.68,0.98 4.02,1.485c-1.787,1.801 -3.646,3.53 -5.663,5.058c-0.259,-1.037 -0.764,-3.097 -1.009,-4.136c-3.631,1.715 -1.297,3.747 0.504,5.491c-1.225,3.387 -2.479,-0.734 -3.66,-1.456l-1.441,0.072c-0.879,0.029 -2.637,0.101 -3.502,0.13c0.072,-0.764 0.245,-2.306 0.331,-3.084c0.447,-2.247 -0.403,-3.213 -2.551,-2.867c0.649,-0.993 1.311,-1.974 1.989,-2.954c-1.066,-1.946 -2.147,-3.876 -3.055,-5.893c-0.548,-0.837 -1.628,-2.508 -2.161,-3.344c-1.369,-0.863 -2.925,-1.426 -4.539,-1.498c-0.259,-0.029 -0.778,-0.072 -1.038,-0.101c-0.749,-0.576 -2.234,-1.729 -2.968,-2.306l0.288,-0.116c0.591,-0.201 1.772,-0.619 2.363,-0.835l0.072,-0.734c1.167,-1.931 2.824,-3.012 4.928,-3.214Zm1.023,1.009c0.85,0.922 0.85,0.922 0,0Zm-2.695,3.011c0.865,0.908 0.865,0.908 0,0Zm5.764,0.029c0.922,0.865 0.922,0.865 0,0Zm6.009,-0.101c0.865,0.923 0.865,0.923 0,0Zm-3.055,3.04c0.879,0.822 0.879,0.822 0,0Zm11.715,-0.316c0.461,0.389 0.461,0.389 0,0Zm-8.79,3.112c0.807,0.85 0.807,0.85 0,0Zm6.066,0.145c0.908,0.835 0.908,0.835 0,0Zm-6.009,5.663c0.793,0.792 0.793,0.792 0,0Zm2.94,2.708c4.813,0.375 -2.262,4.251 0,0Zm-3.055,2.941c4.784,0.187 -2.075,4.366 0,0Zm5.98,0.331c0.375,0.375 0.375,0.375 0,0Z" style="fill:#b9b9ba;fill-rule:nonzero;"/><path d="M449.887,627.7c0.853,0.853 0.853,0.853 0,0Z" style="fill:#aaa9ab;fill-rule:nonzero;"/><path d="M455.737,627.714c0.911,0.794 0.911,0.794 0,0Z" style="fill:#a3a1a2;fill-rule:nonzero;"/><path d="M503.42,630.321c5.202,-4.929 12.421,-1.773 18.646,-1.758c-2.234,1.542 -4.352,3.257 -6.441,4.971c-4.193,-0.533 -8.343,-1.469 -12.205,-3.213Z" style="fill:#7d7a7a;fill-rule:nonzero;"/><path d="M430.059,630.479c0.838,0.781 0.838,0.781 0,0Z" style="fill:#818284;fill-rule:nonzero;"/><path d="M473.186,630.336c0.824,0.75 0.824,0.75 0,0Z" style="fill:#0c0c0c;fill-rule:nonzero;"/><path d="M570.266,631.762c2.838,-0.36 5.678,-0.705 8.545,-1.052l0.505,-0.057c8.905,14.308 15.865,32.076 10.951,48.965l-0.503,-2.349c1.441,-12.017 -2.278,-23.733 -7.033,-34.598c-0.734,-1.685 -1.859,-3.156 -2.882,-4.639c0.145,1.557 0.288,3.112 0.461,4.683c-1.023,0.173 -3.084,0.518 -4.121,0.677c-1.772,-3.978 -3.717,-7.883 -5.922,-11.629Z" style="fill:#f16424;fill-rule:nonzero;"/><path d="M427.133,633.363c0.795,0.78 0.795,0.78 0,0Z" style="fill:#737476;fill-rule:nonzero;"/><path d="M424.109,635.856c4.741,0.592 -2.53,4.164 0,0Z" style="fill:#3b3c3d;fill-rule:nonzero;"/><path d="M438.473,636.273c0.26,0.029 0.781,0.072 1.041,0.101l-0.622,0.231l-0.419,-0.332Z" style="fill:#303032;fill-rule:nonzero;"/><path d="M450.205,636.055c0.882,0.825 0.882,0.825 0,0Z" style="fill:#19191a;fill-rule:nonzero;"/><path d="M536.044,637.771c0.057,1.628 0.114,3.242 0.202,4.9c0.764,0.345 2.291,1.037 3.068,1.397c-0.274,4.395 -0.402,8.804 -0.331,13.228c-0.894,0.562 -1.786,1.138 -2.666,1.729c0.634,0.475 1.902,1.441 2.55,1.931c2.623,4.423 6.153,8.213 9.9,11.729c-1.138,0.98 -2.263,1.975 -3.372,2.968c-0.778,-1.225 -1.542,-2.449 -2.306,-3.66c-1.397,0.216 -2.796,0.448 -4.193,0.692c-0.246,-0.475 -0.764,-1.412 -1.009,-1.873c-4.495,0.734 -9.553,0.648 -13.156,3.861c-2.997,2.032 -4.222,5.534 -5.447,8.762c0.13,-2.651 0.187,-5.346 0.231,-7.983c-1.83,2.84 -3.041,5.995 -3.919,9.237c2.017,4.654 4.15,9.51 8.487,12.493c7.018,3.401 14.856,2.465 22.206,0.822c0.303,0.259 0.907,0.764 1.21,1.023c-7.681,1.83 -16.225,3.343 -23.531,-0.619c-8.631,-4.828 -14.986,-16.399 -10.029,-25.895c0.403,-0.663 1.21,-1.988 1.628,-2.651c-1.023,-0.663 -2.046,-1.298 -3.055,-1.931c2.536,-3.084 5.966,-6.254 5.26,-10.664c0.461,-4.683 -3.098,-7.997 -6.383,-10.664c0.548,0 1.657,0.015 2.205,0.015c3.199,-0.043 6.47,-0.129 9.554,-1.109c2.954,-1.672 5.591,-3.862 8.689,-5.289c1.412,-0.778 2.824,-1.599 4.208,-2.449Zm-12.35,9.294c-0.461,1.441 -0.922,2.882 -1.383,4.322c1.931,-0.086 4.049,0.376 5.721,-0.863c0.418,-3.128 -1.845,-3.559 -4.337,-3.458Zm1.628,17.796c0.706,0.778 0.706,0.778 0,0Zm5.576,-0.187c4.323,-2.177 0.173,4.784 0,0Zm5.923,0.244c-0.288,2.276 3.04,4.367 5.144,4.02c0.086,-2.291 -2.968,-4.597 -5.144,-4.02Zm-14.454,2.954c0.749,0.778 0.749,0.778 0,0Zm19.598,1.067c0.98,0.808 0.98,0.808 0,0Zm-22.609,0.433c-1.138,1.426 -2.565,2.521 -4.251,3.257c-0.389,2.132 -3.285,7.334 0.245,7.507c1.196,-3.646 4.222,-6.427 5.548,-9.827l-1.542,-0.936Z" style="fill:#59585a;fill-rule:nonzero;"/><path d="M545.151,637.612c4.813,0.332 -2.328,4.235 0,0Z" style="fill:#1b1b1c;fill-rule:nonzero;"/><path d="M426.891,638.735c4.785,0.086 -2.11,4.452 0,0Z" style="fill:#999c9e;fill-rule:nonzero;"/><path d="M435.722,638.997c0.766,0.895 0.766,0.895 0,0Z" style="fill:#3c3d3e;fill-rule:nonzero;"/><path d="M467.741,639.139c0.867,0.809 0.867,0.809 0,0Z" style="fill:#9d9b9c;fill-rule:nonzero;"/><path d="M579.848,638.031c1.023,1.485 2.146,2.954 2.882,4.641c1.441,4.769 2.939,9.524 4.38,14.308c-2.594,0.433 -5.173,0.879 -7.752,1.34c-0.576,-5.072 -1.571,-10.071 -3.17,-14.928c1.037,-0.158 3.099,-0.503 4.121,-0.677c-0.173,-1.57 -0.317,-3.126 -0.461,-4.683Z" style="fill:#f38625;fill-rule:nonzero;"/><path d="M559.028,639.673c1.859,0.158 3.747,0.301 5.619,0.461c-0.173,1.902 -0.404,3.818 -0.705,5.72c-2.335,-0.029 -4.67,-0.057 -7.003,-0.116c0.677,-2.032 1.368,-4.063 2.089,-6.066Z" style="fill:#485e72;fill-rule:nonzero;"/><path d="M424.295,642.021c0.838,0.781 0.838,0.781 0,0Z" style="fill:#7a7c7e;fill-rule:nonzero;"/><path d="M464.902,641.69c0.853,0.867 0.853,0.867 0,0Z" style="fill:#a3a2a3;fill-rule:nonzero;"/><path d="M502.066,641.806c0.824,0.752 0.824,0.752 0,0Z" style="fill:#050506;fill-rule:nonzero;"/><path d="M540.091,649.775c1.514,-7.94 11.514,-8.371 16.846,-4.035c2.334,0.058 4.668,0.086 7.002,0.116c-0.402,1.527 -0.85,3.055 -1.353,4.567c-7.493,-0.461 -15.001,-0.648 -22.495,-0.648Z" style="fill:#526f8b;fill-rule:nonzero;"/><path d="M453.186,644.659c0.795,0.796 0.795,0.796 0,0Z" style="fill:#434446;fill-rule:nonzero;"/><path d="M461.213,644.631c4.235,-2.5 0.448,4.857 0,0Z" style="fill:#898889;fill-rule:nonzero;"/><path d="M476.113,653.333c0.867,0.781 0.867,0.781 0,0Z" style="fill:#898889;fill-rule:nonzero;"/><path d="M467.552,644.789c0.824,0.853 0.824,0.853 0,0Z" style="fill:#9b9a9b;fill-rule:nonzero;"/><path d="M479.008,644.818c0.853,0.737 0.853,0.737 0,0Z" style="fill:#767475;fill-rule:nonzero;"/><path d="M424.05,647.411c4.756,0.159 -2.125,4.408 0,0Z" style="fill:#525455;fill-rule:nonzero;"/><path d="M432.682,647.917c0.838,0.838 0.838,0.838 0,0Z" style="fill:#333334;fill-rule:nonzero;"/><path d="M475.955,647.484c4.785,0.26 -2.269,4.307 0,0Z" style="fill:#282829;fill-rule:nonzero;"/><path d="M523.693,647.065c2.493,-0.101 4.755,0.331 4.337,3.458c-1.671,1.239 -3.79,0.778 -5.721,0.865c0.461,-1.441 0.922,-2.882 1.383,-4.323Z" style="fill:#151718;fill-rule:nonzero;"/><path d="M426.962,650.294c4.741,-0.029 -1.937,4.538 0,0Z" style="fill:#939697;fill-rule:nonzero;"/><path d="M435.506,650.307c4.727,0.39 -2.399,4.294 0,0Z" style="fill:#5b5d5e;fill-rule:nonzero;"/><path d="M447.28,650.064c2.154,-0.348 3.007,0.62 2.559,2.875c-2.645,0.376 -3.498,-0.578 -2.559,-2.875Z" style="fill:#0e0f10;fill-rule:nonzero;"/><path d="M453.072,650.307c4.799,0.189 -2.081,4.38 0,0Z" style="fill:#5b5c5d;fill-rule:nonzero;"/><path d="M441.544,664.921c0.766,0.765 0.766,0.765 0,0Z" style="fill:#5b5c5d;fill-rule:nonzero;"/><path d="M467.496,650.423c0.838,0.796 0.838,0.796 0,0Z" style="fill:#878789;fill-rule:nonzero;"/><path d="M473.274,650.496c0.867,0.781 0.867,0.781 0,0Z" style="fill:#89898b;fill-rule:nonzero;"/><path d="M478.981,650.452c0.896,0.781 0.896,0.781 0,0Z" style="fill:#989597;fill-rule:nonzero;"/><path d="M484.903,650.739c0.853,0.796 0.853,0.796 0,0Z" style="fill:#908d8f;fill-rule:nonzero;"/><path d="M424.095,653.204c4.756,0.519 -2.486,4.192 0,0Z" style="fill:#2b2c2d;fill-rule:nonzero;"/><path d="M444.268,653.233c4.77,0.043 -2.024,4.452 0,0Z" style="fill:#6b6c6d;fill-rule:nonzero;"/><path d="M470.435,653.362c0.766,0.81 0.766,0.81 0,0Z" style="fill:#6c6c6e;fill-rule:nonzero;"/><path d="M426.92,656.057c4.77,0.216 -2.212,4.351 0,0Z" style="fill:#424244;fill-rule:nonzero;"/><path d="M461.674,656.188c0.81,0.823 0.81,0.823 0,0Z" style="fill:#848487;fill-rule:nonzero;"/><path d="M467.538,656.287c0.824,0.651 0.824,0.651 0,0Z" style="fill:#414142;fill-rule:nonzero;"/><path d="M473.318,656.202c0.752,0.78 0.752,0.78 0,0Z" style="fill:#575658;fill-rule:nonzero;"/><path d="M479.008,656.214c0.867,0.766 0.867,0.766 0,0Z" style="fill:#878688;fill-rule:nonzero;"/><path d="M490.566,656.548c0.275,0.044 0.838,0.145 1.113,0.187l-0.593,0.491l-0.52,-0.679Z" style="fill:#8d8a8c;fill-rule:nonzero;"/><path d="M569.964,657.758c1.801,1.743 1.902,7.651 -0.072,9.28c-1.052,-2.867 -0.951,-6.427 0.072,-9.28Z" style="fill:#4b4c4f;fill-rule:nonzero;"/><path d="M579.359,658.32c2.579,-0.462 5.159,-0.908 7.752,-1.34c1.21,4.265 1.656,8.703 1.498,13.141c-3.559,0.073 -7.119,0.159 -10.677,0.26c0.821,-3.978 1.254,-8.013 1.426,-12.061Z" style="fill:#f6a927;fill-rule:nonzero;"/><path d="M429.816,658.952c4.756,0.101 -2.11,4.409 0,0Z" style="fill:#484a4b;fill-rule:nonzero;"/><path d="M453.015,658.895c4.77,0.232 -2.197,4.409 0,0Z" style="fill:#979799;fill-rule:nonzero;"/><path d="M458.791,659.155c0.81,0.794 0.81,0.794 0,0Z" style="fill:#808082;fill-rule:nonzero;"/><path d="M476.186,659.17c0.795,0.737 0.795,0.737 0,0Z" style="fill:#565657;fill-rule:nonzero;"/><path d="M481.933,659.04c0.824,0.781 0.824,0.781 0,0Z" style="fill:#787779;fill-rule:nonzero;"/><path d="M487.813,659.097c0.781,0.838 0.781,0.838 0,0Z" style="fill:#868586;fill-rule:nonzero;"/><path d="M493.477,659.097l0.578,0.072l0.058,0.491l-0.506,0.29l-0.13,-0.853Z" style="fill:#868486;fill-rule:nonzero;"/><path d="M433.013,662.095c0.737,0.796 0.737,0.796 0,0Z" style="fill:#5b5c5e;fill-rule:nonzero;"/><path d="M455.925,662.008c0.795,0.737 0.795,0.737 0,0Z" style="fill:#585858;fill-rule:nonzero;"/><path d="M473.301,662.022c0.723,0.766 0.723,0.766 0,0Z" style="fill:#373838;fill-rule:nonzero;"/><path d="M479.111,661.994c0.679,0.825 0.679,0.825 0,0Z" style="fill:#4b4b4c;fill-rule:nonzero;"/><path d="M484.903,661.994c0.737,0.796 0.737,0.796 0,0Z" style="fill:#585859;fill-rule:nonzero;"/><path d="M491.416,662.775c0.029,-0.187 0.072,-0.549 0.101,-0.737l-0.101,0.737Z" style="fill:#777678;fill-rule:nonzero;"/><path d="M435.78,665.063c0.781,0.693 0.781,0.693 0,0Z" style="fill:#353637;fill-rule:nonzero;"/><path d="M453.1,664.921c0.766,0.723 0.766,0.723 0,0Z" style="fill:#39393b;fill-rule:nonzero;"/><path d="M481.919,664.877c0.708,0.794 0.708,0.794 0,0Z" style="fill:#4a4a4b;fill-rule:nonzero;"/><path d="M487.784,664.848c0.766,0.752 0.766,0.752 0,0Z" style="fill:#535253;fill-rule:nonzero;"/><path d="M525.323,664.862c0.708,0.78 0.708,0.78 0,0Z" style="fill:#1e1e1e;fill-rule:nonzero;"/><path d="M530.899,664.675c4.336,-2.183 0.173,4.799 0,0Z" style="fill:#282728;fill-rule:nonzero;"/><path d="M505.135,670.625c0.752,0.752 0.752,0.752 0,0Z" style="fill:#464546;fill-rule:nonzero;"/><path d="M519.357,669.37l1.542,0.938c-1.326,3.4 -4.352,6.181 -5.548,9.827c-3.53,-0.173 -0.634,-5.375 -0.245,-7.508c1.686,-0.734 3.113,-1.829 4.251,-3.257Z" style="fill:#0a0a0b;fill-rule:nonzero;"/><path d="M546.087,684.717c0.792,-8.718 8.703,-15.879 17.55,-15.231c4.971,-0.173 8.733,3.33 11.571,6.974c0.908,-2.031 1.817,-4.063 2.724,-6.08c3.559,-0.101 7.119,-0.187 10.678,-0.259c-0.361,3.358 -0.879,6.715 -1.773,9.972c-13.675,0.547 -27.162,3.169 -40.75,4.625Z" style="fill:#f8c125;fill-rule:nonzero;"/><path d="M577.918,697.499c6.44,-4.957 9.135,-12.911 11.844,-20.232l0.505,2.349c-1.542,7.969 -6.168,15.606 -12.767,20.447c-9.452,6.845 -24.597,3.704 -29.8,-7.017c11.644,-1.801 23.387,-3.141 35.131,-4.222c-3.846,6.066 -10.172,11.312 -17.724,10.98c-4.913,0.274 -9.092,-2.478 -13.127,-4.841c6.111,7.709 18.46,8.876 25.939,2.535Z" style="fill:#f8eb25;fill-rule:nonzero;"/><path d="M546.087,684.718c13.588,-1.456 27.076,-4.079 40.75,-4.626c-1.081,3.011 -2.421,5.951 -4.006,8.733c-11.743,1.08 -23.488,2.42 -35.131,4.222c-0.663,-2.753 -1.167,-5.548 -1.614,-8.329Z" style="fill:#f8d623;fill-rule:nonzero;"/><path d="M845.634,405.489c19.716,-0.47 39.447,0.059 59.178,-0.279c0.147,4.216 0.177,8.433 0.162,12.65c-7.552,0.015 -15.104,0 -22.641,0.015c-0.088,19.863 0.029,39.727 -0.044,59.59c-4.613,-0.044 -9.197,-0.029 -13.781,0.073c-0.352,-19.878 -0.058,-39.771 -0.162,-59.663c-7.536,-0.015 -15.073,-0.015 -22.61,0c-0.029,-4.128 -0.058,-8.257 -0.102,-12.385Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M912.157,405.24c4.216,0.059 8.447,0.088 12.678,0.088c-0.073,9.021 0.044,18.027 -0.088,27.033c9.27,-9.976 29.074,-8.83 34.261,4.775c4.421,12.855 1.322,26.828 2.307,40.168c-4.188,-0.015 -8.507,0.749 -12.605,-0.294c-0.545,-9.903 0.425,-19.849 -0.441,-29.722c-0.501,-4.907 -4.452,-9.388 -9.565,-9.491c-7.257,-1.146 -13.766,5.392 -13.781,12.473c-0.323,9.05 0.029,18.1 -0.146,27.165c-4.305,0.06 -8.596,0.031 -12.886,-0.146c0.25,-24.007 -0.279,-48.042 0.266,-72.049Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M981.794,428.57c9.946,-5.348 23.405,-5.069 32.411,2.086c7.477,6.244 8.58,16.705 8.139,25.799c-13.267,0.22 -26.534,0.059 -39.785,0.088c0.793,3.144 2.276,6.317 5.23,7.977c7.228,4.394 16.763,2.733 23.432,-1.998c2.719,2.571 5.569,5.024 7.949,7.933c-10.314,10.094 -28.371,11.093 -40.094,2.865c-14.016,-10.696 -12.988,-36.068 2.717,-44.751Zm0.823,17.145c9.123,0.309 18.262,0.162 27.4,0.103c-2.806,-13.12 -23.595,-12.135 -27.4,-0.103Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M778.768,460.819c9.593,0.015 19.188,-0.029 28.781,0.029c20.76,46.514 41.696,92.94 62.484,139.425c-9.579,0.058 -19.143,-0.073 -28.708,0.088c-4.113,-8.711 -7.991,-17.526 -11.811,-26.341c-24.227,-0.177 -48.453,-0.045 -72.68,-0.074c-3.953,8.771 -7.993,17.513 -11.916,26.299c-9.579,0.088 -19.157,0.029 -28.737,0.029c20.877,-46.471 41.696,-92.985 62.588,-139.454Zm14.207,30.118c-8.594,19.436 -17.014,38.963 -25.55,58.429c17.13,0 34.246,0.015 51.363,-0.015c-8.683,-19.437 -16.647,-39.214 -25.814,-58.415Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M874.31,501.94c8.153,0.029 16.307,0.015 24.476,0.015c0,17.014 0,34.013 -0.015,51.025c-0.104,7.155 1.777,14.721 6.861,20.011c6.728,7.007 18.027,8.285 26.694,4.407c10.329,-4.598 15.603,-16.396 14.956,-27.283c-0.029,-16.057 0,-32.101 -0.015,-48.16c8.037,-0.015 16.073,0.045 24.11,-0.029c0.175,32.821 0.029,65.644 0.073,98.465c-7.258,0 -14.53,-0.015 -21.788,0c-0.572,-4.348 -1.086,-8.698 -1.484,-13.061c-7.464,7.038 -16.485,13.149 -26.96,14.104c-14.06,2.116 -29.486,-3.202 -37.698,-15.146c-8.478,-11.607 -9.726,-26.548 -9.212,-40.462c0,-14.618 -0.015,-29.251 0,-43.885Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M1070.255,461.171c8.183,-0.058 16.381,-0.175 24.579,0.074c-0.147,46.352 -0.029,92.705 -0.073,139.042c-7.67,-0.102 -15.353,0.266 -23.007,-0.25c-0.397,-4.334 -0.94,-8.667 -1.308,-13.001c-5.127,7.242 -12.811,12.635 -21.582,14.339c-16.101,3.071 -34.113,-0.822 -46.088,-12.429c-9.329,-8.889 -14.001,-21.788 -14.486,-34.497c-0.66,-13.384 2.087,-27.606 10.77,-38.183c8.432,-10.535 21.934,-15.927 35.187,-16.514c13.398,-0.808 27.87,4.099 36.2,15.044c-0.381,-17.88 -0.015,-35.744 -0.19,-53.625Zm-36.686,62.059c-6.318,1.44 -12.179,5.127 -15.853,10.504c-8.241,12.033 -6.508,30.412 4.981,39.815c10.094,8.447 26.107,8.139 36.098,-0.309c12.135,-9.887 13.252,-30.029 2.763,-41.489c-6.832,-7.728 -18.042,-10.858 -27.988,-8.521Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M1115.565,501.779c8.14,-0.235 16.308,-0.073 24.462,-0.117l0,98.626c-8.153,-0.058 -16.292,0.133 -24.417,-0.131c0.029,-32.792 0.117,-65.585 -0.045,-98.377Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M1121.661,461.421c9.27,-4.481 21.817,2.894 20.613,13.649c0.381,10.666 -12.532,16.968 -21.392,12.135c-10.328,-4.599 -9.901,-21.847 0.78,-25.785Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M1198.057,501.236c16.558,-3.452 35.172,0.22 47.675,12.062c19.261,17.836 20.921,50.569 4.848,70.976c-17.395,22.038 -53.565,24.11 -74.575,6.2c-17.777,-15.499 -21.303,-43.385 -11.004,-64.042c6.346,-12.943 18.967,-22.39 33.056,-25.196Zm4.569,22.067c-21.45,6.098 -25.196,38.478 -8.33,51.539c10.49,7.905 27.268,6.686 35.79,-3.614c9.872,-12.048 8.859,-31.719 -2.719,-42.341c-6.493,-6.127 -16.249,-8.037 -24.741,-5.584Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M1277.937,460.73c19.173,0.102 38.361,-0.147 57.549,0.117c17.923,0.279 36.304,6.626 48.659,20.01c15.837,16.911 20.965,41.798 17.087,64.219c-2.91,16.939 -11.931,33.189 -26.226,43.076c-11.915,8.419 -26.68,11.96 -41.122,12.106c-18.674,0.235 -37.346,0.06 -56.005,0.089c0.015,-46.544 -0.102,-93.073 0.058,-139.616Zm26.592,25.152c-0.294,29.618 -0.308,59.251 0.015,88.856c10.271,-0.177 20.554,0.058 30.838,-0.104c12.62,-0.294 25.593,-5.832 32.777,-16.542c9.917,-14.707 10.431,-34.835 2.675,-50.584c-5.54,-11.475 -17.19,-19.321 -29.752,-20.921c-12.121,-1.425 -24.373,-0.251 -36.553,-0.705Z" style="fill:#6599cd;fill-rule:nonzero;"/><path d="M1418.773,460.701c22.2,0.133 44.399,-0.044 66.599,0.089c12.516,0.175 25.872,2.688 35.569,11.195c10.298,8.903 13.604,23.83 11.033,36.818c-1.866,8.403 -8.228,14.854 -15.588,18.849c1.161,2.322 4.423,2.646 6.347,4.306c18.834,13.03 18.865,44.383 0.838,58.12c-11.269,8.551 -26.005,10.065 -39.698,10.24c-21.728,0.104 -43.444,0.045 -65.172,0.031c0.015,-46.544 -0.133,-93.103 0.073,-139.647Zm25.799,55.697c14.501,0.337 29.016,0.191 43.531,0.102c7.552,-0.029 16.91,-3.555 18.174,-12.017c1.866,-10.564 -8.374,-19.159 -18.291,-19.35c-14.355,-0.514 -28.737,0.104 -43.092,-0.294c-0.602,10.504 -0.058,21.039 -0.323,31.558Zm0.044,23.757c0.177,11.856 -0.308,23.727 0.264,35.569c13.443,-0.308 26.9,0.044 40.344,-0.162c7.508,-0.337 15.749,-1.527 21.435,-6.92c5.113,-4.745 5.245,-13.311 0.896,-18.599c-5.642,-6.965 -14.971,-10.065 -23.713,-9.932c-13.076,0.029 -26.151,-0.015 -39.227,0.044Z" style="fill:#6599cd;fill-rule:nonzero;"/></svg>
\ No newline at end of file diff --git a/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs b/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs deleted file mode 100644 index 8cbd1f89a7..0000000000 --- a/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; - -namespace MediaBrowser.Providers.Plugins.ComicVine -{ - /// <inheritdoc /> - public class ComicVineExternalId : IExternalId - { - /// <inheritdoc /> - public string ProviderName => "Comic Vine"; - - /// <inheritdoc /> - public string Key => "ComicVine"; - - /// <inheritdoc /> - public ExternalIdMediaType? Type => null; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Book; - } -} diff --git a/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs deleted file mode 100644 index 9122399179..0000000000 --- a/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Providers.Plugins.ComicVine; - -/// <inheritdoc/> -public class ComicVineExternalUrlProvider : IExternalUrlProvider -{ - /// <inheritdoc/> - public string Name => "Comic Vine"; - - /// <inheritdoc /> - public IEnumerable<string> GetExternalUrls(BaseItem item) - { - if (item.TryGetProviderId("ComicVine", out var externalId)) - { - switch (item) - { - case Person: - case Book: - yield return $"https://comicvine.gamespot.com/{externalId}"; - break; - } - } - } -} diff --git a/MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs b/MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs deleted file mode 100644 index 26b8e11380..0000000000 --- a/MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; - -namespace MediaBrowser.Providers.Plugins.ComicVine -{ - /// <inheritdoc /> - public class ComicVinePersonExternalId : IExternalId - { - /// <inheritdoc /> - public string ProviderName => "Comic Vine"; - - /// <inheritdoc /> - public string Key => "ComicVine"; - - /// <inheritdoc /> - public ExternalIdMediaType? Type => ExternalIdMediaType.Person; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Person; - } -} diff --git a/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs b/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs deleted file mode 100644 index 02d3b36974..0000000000 --- a/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; - -namespace MediaBrowser.Providers.Plugins.GoogleBooks -{ - /// <inheritdoc /> - public class GoogleBooksExternalId : IExternalId - { - /// <inheritdoc /> - public string ProviderName => "Google Books"; - - /// <inheritdoc /> - public string Key => "GoogleBooks"; - - /// <inheritdoc /> - public ExternalIdMediaType? Type => null; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Book; - } -} diff --git a/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs deleted file mode 100644 index 95047ee83e..0000000000 --- a/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Providers.Plugins.GoogleBooks; - -/// <inheritdoc/> -public class GoogleBooksExternalUrlProvider : IExternalUrlProvider -{ - /// <inheritdoc /> - public string Name => "Google Books"; - - /// <inheritdoc /> - public IEnumerable<string> GetExternalUrls(BaseItem item) - { - if (item.TryGetProviderId("GoogleBooks", out var externalId)) - { - if (item is Book) - { - yield return $"https://books.google.com/books?id={externalId}"; - } - } - } -} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Api/ListenBrainzLabsClient.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/Api/ListenBrainzLabsClient.cs new file mode 100644 index 0000000000..e080370b8c --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Api/ListenBrainzLabsClient.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Providers.Plugins.ListenBrainz.Api.Models; +using MediaBrowser.Providers.Plugins.ListenBrainz.Configuration; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Plugins.ListenBrainz.Api; + +/// <summary> +/// Client for the ListenBrainz Labs API. +/// </summary> +public class ListenBrainzLabsClient : IDisposable +{ + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<ListenBrainzLabsClient> _logger; + private readonly SemaphoreSlim _rateLimitLock = new(1, 1); + + private DateTime _lastRequestTime = DateTime.MinValue; + + /// <summary> + /// Initializes a new instance of the <see cref="ListenBrainzLabsClient"/> class. + /// </summary> + /// <param name="httpClientFactory">The HTTP client factory.</param> + /// <param name="logger">The logger.</param> + public ListenBrainzLabsClient( + IHttpClientFactory httpClientFactory, + ILogger<ListenBrainzLabsClient> logger) + { + _httpClientFactory = httpClientFactory; + _logger = logger; + } + + /// <summary> + /// Gets similar artists for the given MusicBrainz artist ID. + /// </summary> + /// <param name="artistMbid">The MusicBrainz artist ID.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>A list of similar artist MusicBrainz IDs ordered by similarity score.</returns> + public async Task<IReadOnlyList<Guid>> GetSimilarArtistsAsync( + Guid artistMbid, + CancellationToken cancellationToken) + { + var config = ListenBrainzPlugin.Instance?.Configuration; + var baseUrl = config?.LabsServer ?? PluginConfiguration.DefaultLabsServer; + var algorithm = config?.AlgorithmString ?? new PluginConfiguration().AlgorithmString; + var rateLimit = config?.RateLimit ?? PluginConfiguration.DefaultRateLimit; + + // Enforce rate limit + await EnforceRateLimitAsync(rateLimit, cancellationToken).ConfigureAwait(false); + + var url = $"{baseUrl}/similar-artists/json?artist_mbids={artistMbid}&algorithm={algorithm}"; + + _logger.LogDebug("Fetching similar artists from ListenBrainz Labs: {Url}", url); + + try + { + var httpClient = _httpClientFactory.CreateClient(NamedClient.Default); + var response = await httpClient.GetFromJsonAsync<List<SimilarArtistData>>(url, cancellationToken).ConfigureAwait(false); + + if (response is null || response.Count == 0) + { + _logger.LogDebug("No similar artists found for {ArtistMbid}", artistMbid); + return []; + } + + var similarMbids = response + .Where(a => !a.ArtistMbid.Equals(artistMbid)) // Exclude the source artist + .OrderByDescending(a => a.Score) + .Select(a => a.ArtistMbid) + .ToList(); + + _logger.LogDebug("Found {Count} similar artists for {ArtistMbid}", similarMbids.Count, artistMbid); + + return similarMbids; + } + catch (HttpRequestException ex) + { + _logger.LogWarning(ex, "Failed to fetch similar artists from ListenBrainz Labs for {ArtistMbid}", artistMbid); + return []; + } + } + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _rateLimitLock.Dispose(); + } + } + + private async Task EnforceRateLimitAsync(double rateLimitSeconds, CancellationToken cancellationToken) + { + await _rateLimitLock.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + var timeSinceLastRequest = DateTime.UtcNow - _lastRequestTime; + var requiredDelay = TimeSpan.FromSeconds(rateLimitSeconds) - timeSinceLastRequest; + + if (requiredDelay > TimeSpan.Zero) + { + await Task.Delay(requiredDelay, cancellationToken).ConfigureAwait(false); + } + + _lastRequestTime = DateTime.UtcNow; + } + finally + { + _rateLimitLock.Release(); + } + } +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Api/Models/SimilarArtistData.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/Api/Models/SimilarArtistData.cs new file mode 100644 index 0000000000..237f33ee3a --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Api/Models/SimilarArtistData.cs @@ -0,0 +1,28 @@ +using System; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Providers.Plugins.ListenBrainz.Api.Models; + +/// <summary> +/// A similar artist data entry from the ListenBrainz Labs API. +/// </summary> +public class SimilarArtistData +{ + /// <summary> + /// Gets or sets the MusicBrainz artist ID. + /// </summary> + [JsonPropertyName("artist_mbid")] + public Guid ArtistMbid { get; set; } + + /// <summary> + /// Gets or sets the artist name. + /// </summary> + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// <summary> + /// Gets or sets the similarity score. + /// </summary> + [JsonPropertyName("score")] + public double Score { get; set; } +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Api/Models/SimilarArtistsResponse.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/Api/Models/SimilarArtistsResponse.cs new file mode 100644 index 0000000000..12e8f25dcc --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Api/Models/SimilarArtistsResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Providers.Plugins.ListenBrainz.Api.Models; + +/// <summary> +/// Response from ListenBrainz Labs similar-artists endpoint. +/// </summary> +public class SimilarArtistsResponse +{ + /// <summary> + /// Gets or sets the list of similar artists. + /// </summary> + [JsonPropertyName("data")] + public IReadOnlyList<SimilarArtistData>? Data { get; set; } +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/ListenBrainz_logo.svg b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/ListenBrainz_logo.svg new file mode 100644 index 0000000000..416a097f9c --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/ListenBrainz_logo.svg @@ -0,0 +1,60 @@ +<svg version="1.2" baseProfile="tiny-ps" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450" width="800" height="450"> + <title>ListenBrainz_logo-svg</title> + <defs> + <image width="800" height="450" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAHCAQMAAAAtrT+LAAAAAXNSR0IB2cksfwAAAANQTFRF9tmZzxpnDgAAAENJREFUeJztwYEAAAAAw6D5U1/hAFUBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALwGsYoAAQbjkYoAAAAASUVORK5CYII="/> + <clipPath clipPathUnits="userSpaceOnUse" id="cp1"> + <path d="M173.35 178.24L224.79 178.24L224.79 269.03L173.35 269.03L173.35 178.24Z" /> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="cp2"> + <path d="M173.35 178.24L224.79 178.24L224.79 269.03L173.35 269.03L173.35 178.24Z" /> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="cp3"> + <path d="M173.35 178.24L224.79 178.24L224.79 269.03L173.35 269.03L173.35 178.24Z" /> + </clipPath> + </defs> + <style> + tspan { white-space:pre } + .shp0 { fill: #eb743b } + .shp1 { fill: #353070 } + .shp2 { fill: #000000 } + .shp3 { fill: #d3562c } + .shp4 { fill: #fffedb } + .shp5 { opacity: 0.251;fill: #000000 } + </style> + <use id="Background" href="#img1" x="0" y="0" /> + <path id="Layer" class="shp0" d="M173.35 152.82L173.35 296.82L234.35 261.82L234.35 187.82L173.35 152.82Z" /> + <path id="Layer" class="shp1" d="M168.35 152.82L107.35 187.82L107.35 261.82L168.35 296.82L168.35 152.82Z" /> + <g id="Layer" style="opacity: 0.071"> + <g id="Clip-Path" clip-path="url(#cp1)"> + <path id="Layer" class="shp2" d="M190.66 244.56C190.75 244.4 190.86 244.26 190.99 244.13C191.12 244 191.27 243.89 191.43 243.8C191.59 243.71 191.76 243.64 191.93 243.59C192.11 243.55 192.29 243.53 192.48 243.53C192.85 243.53 193.23 243.63 193.55 243.82C193.79 243.97 194 244.15 194.17 244.38C194.33 244.6 194.45 244.85 194.52 245.12C194.59 245.39 194.6 245.67 194.56 245.94C194.52 246.22 194.43 246.48 194.29 246.72C194.2 246.88 194.08 247.02 193.95 247.15C193.82 247.28 193.68 247.39 193.52 247.48C193.36 247.57 193.19 247.64 193.01 247.69C192.84 247.73 192.65 247.75 192.47 247.75C192.09 247.75 191.72 247.65 191.39 247.45C191.27 247.38 191.16 247.3 191.05 247.21C190.95 247.12 190.86 247.01 190.78 246.9C190.69 246.79 190.62 246.67 190.56 246.55C190.5 246.42 190.46 246.29 190.42 246.16C190.39 246.02 190.37 245.89 190.36 245.75C190.35 245.61 190.36 245.47 190.38 245.33C190.4 245.2 190.43 245.06 190.48 244.93C190.53 244.8 190.59 244.68 190.66 244.56M203.26 266.92C203.05 267.1 202.8 267.24 202.54 267.32C202.28 267.41 202 267.44 201.72 267.42C201.45 267.4 201.18 267.33 200.93 267.2C200.68 267.08 200.46 266.9 200.28 266.69C200.1 266.48 199.96 266.24 199.87 265.97C199.79 265.71 199.76 265.43 199.78 265.16C199.8 264.88 199.87 264.61 200 264.36C200.13 264.11 200.3 263.89 200.51 263.71C200.6 263.63 200.7 263.56 200.81 263.49C200.92 263.43 201.03 263.38 201.15 263.33C201.27 263.29 201.39 263.26 201.51 263.23C201.63 263.21 201.76 263.2 201.88 263.2C202.17 263.2 202.46 263.26 202.73 263.38C203 263.5 203.24 263.67 203.43 263.88C203.63 264.09 203.78 264.35 203.88 264.62C203.98 264.9 204.01 265.19 203.99 265.48C203.98 265.62 203.96 265.75 203.92 265.89C203.88 266.02 203.83 266.15 203.77 266.27C203.71 266.4 203.63 266.51 203.55 266.62C203.46 266.73 203.36 266.83 203.26 266.92M193.73 208.08C193.88 207.85 194.08 207.65 194.3 207.49C194.53 207.33 194.79 207.21 195.06 207.15C195.33 207.09 195.61 207.09 195.88 207.13C196.15 207.18 196.41 207.28 196.65 207.42C196.88 207.57 197.09 207.77 197.25 207.99C197.41 208.22 197.52 208.48 197.58 208.75C197.64 209.02 197.65 209.3 197.6 209.57C197.56 209.85 197.46 210.11 197.31 210.34C197.16 210.58 196.97 210.78 196.74 210.94C196.51 211.1 196.26 211.22 195.99 211.28C195.72 211.34 195.44 211.35 195.16 211.3C194.89 211.25 194.63 211.15 194.39 211C194.16 210.85 193.95 210.66 193.79 210.43C193.63 210.21 193.52 209.95 193.46 209.68C193.4 209.41 193.39 209.13 193.44 208.85C193.49 208.58 193.59 208.32 193.73 208.08ZM200.11 187.89C199.51 188.84 198.18 189.16 197.2 188.55L197.1 188.48C196.89 188.33 196.7 188.13 196.55 187.91C196.41 187.68 196.31 187.43 196.26 187.17C196.21 186.91 196.2 186.64 196.25 186.37C196.3 186.11 196.4 185.86 196.55 185.63C196.64 185.48 196.76 185.35 196.88 185.22C197.01 185.1 197.16 185 197.31 184.91C197.47 184.83 197.63 184.76 197.81 184.72C197.98 184.67 198.15 184.65 198.33 184.65C198.71 184.65 199.08 184.75 199.41 184.95C199.73 185.14 200 185.42 200.18 185.75C200.36 186.08 200.45 186.45 200.44 186.83C200.43 187.21 200.32 187.57 200.11 187.89M216.88 199.49C216.97 199.34 217.09 199.2 217.22 199.08C217.35 198.96 217.49 198.86 217.64 198.77C217.8 198.68 217.97 198.62 218.14 198.58C218.31 198.53 218.49 198.51 218.66 198.51C219.06 198.51 219.45 198.62 219.79 198.84C220.27 199.14 220.6 199.61 220.72 200.17C220.76 200.3 220.77 200.44 220.78 200.58C220.78 200.71 220.77 200.85 220.74 200.99C220.72 201.12 220.68 201.25 220.63 201.38C220.58 201.51 220.52 201.63 220.44 201.75C219.85 202.7 218.51 203.02 217.53 202.4L217.44 202.34C217.22 202.19 217.03 201.99 216.89 201.77C216.74 201.54 216.64 201.29 216.59 201.03C216.54 200.77 216.54 200.49 216.59 200.23C216.64 199.97 216.74 199.72 216.88 199.49M216.88 222.95C216.97 222.8 217.09 222.67 217.22 222.54C217.35 222.42 217.49 222.32 217.64 222.23C217.8 222.15 217.97 222.08 218.14 222.04C218.31 222 218.49 221.97 218.66 221.97C219.06 221.97 219.45 222.09 219.79 222.3C220.27 222.6 220.6 223.07 220.72 223.63C220.76 223.76 220.77 223.9 220.78 224.04C220.78 224.18 220.77 224.31 220.74 224.45C220.72 224.59 220.68 224.72 220.63 224.85C220.58 224.98 220.52 225.1 220.44 225.21C219.85 226.16 218.51 226.48 217.53 225.87L217.44 225.8C217.22 225.65 217.03 225.45 216.89 225.23C216.74 225 216.64 224.75 216.59 224.49C216.54 224.23 216.54 223.96 216.59 223.69C216.64 223.43 216.74 223.18 216.88 222.95M216.88 249.26C216.97 249.11 217.09 248.97 217.22 248.85C217.35 248.73 217.49 248.62 217.64 248.54C217.8 248.45 217.97 248.39 218.14 248.34C218.31 248.3 218.49 248.28 218.66 248.28C219.06 248.28 219.45 248.39 219.79 248.6C220.27 248.91 220.6 249.38 220.72 249.93C220.76 250.07 220.77 250.2 220.78 250.34C220.78 250.48 220.77 250.62 220.74 250.75C220.72 250.89 220.68 251.02 220.63 251.15C220.58 251.28 220.52 251.4 220.44 251.52C219.85 252.47 218.51 252.79 217.53 252.17L217.44 252.11C217.22 251.95 217.03 251.76 216.89 251.53C216.74 251.31 216.64 251.06 216.59 250.79C216.54 250.53 216.54 250.26 216.59 250C216.64 249.73 216.74 249.48 216.88 249.26" /> + </g> + <g id="Clip-Path" clip-path="url(#cp2)"> + <path id="Layer" fill-rule="evenodd" class="shp2" d="M206.53 258.93C206.79 259.23 207.02 259.56 207.22 259.91C207.42 260.26 207.58 260.63 207.71 261.01C207.83 261.39 207.92 261.78 207.96 262.18C208.01 262.58 208.02 262.98 207.99 263.38C207.96 263.78 207.89 264.17 207.78 264.56C207.67 264.95 207.53 265.32 207.35 265.68C207.17 266.04 206.95 266.38 206.7 266.69C206.45 267.01 206.17 267.3 205.87 267.56C205.59 267.79 205.3 268 204.99 268.19C204.68 268.37 204.35 268.52 204.01 268.65C203.67 268.78 203.32 268.87 202.96 268.94C202.61 269 202.25 269.03 201.89 269.03C201.49 269.03 201.1 268.99 200.71 268.92C200.32 268.84 199.94 268.73 199.57 268.58C199.21 268.43 198.85 268.24 198.52 268.02C198.19 267.81 197.88 267.56 197.6 267.28C195.75 267.98 192.69 269 190.58 269C190.48 269 190.37 269 190.27 269C188.3 268.89 187.03 268.03 186.01 267.34C184.44 266.28 182.95 265.27 177.6 266.63C175.24 267.71 173.63 267.79 173.36 267.82L173.36 263.82C173.86 263.72 174.73 263.55 175.91 263.01C175.94 263 175.97 262.98 176 262.97C176.03 262.95 176.06 262.94 176.09 262.92C176.12 262.91 176.15 262.89 176.18 262.88C176.21 262.87 176.24 262.86 176.27 262.85C179.62 261.24 184.79 257.38 188.84 248.16C188.56 247.95 188.29 247.72 188.05 247.47C187.8 247.22 187.58 246.94 187.39 246.65C187.19 246.36 187.02 246.05 186.88 245.73C186.74 245.41 186.62 245.07 186.54 244.73C186.44 244.34 186.38 243.95 186.36 243.55C186.33 243.15 186.35 242.74 186.41 242.35C186.47 241.95 186.57 241.56 186.7 241.18C186.84 240.8 187.01 240.44 187.22 240.1C187.49 239.64 187.82 239.23 188.19 238.86C188.57 238.49 188.99 238.17 189.45 237.91C189.91 237.65 190.4 237.45 190.91 237.32C191.42 237.18 191.95 237.11 192.48 237.11C192.75 237.11 193.02 237.13 193.3 237.17C193.57 237.2 193.84 237.26 194.1 237.33C194.37 237.4 194.62 237.5 194.88 237.6C195.13 237.71 195.37 237.83 195.61 237.97C198.51 239.71 199.46 243.47 197.73 246.37C197.47 246.8 197.16 247.21 196.79 247.56C196.43 247.92 196.03 248.23 195.59 248.49C195.15 248.75 194.68 248.95 194.2 249.1C193.71 249.24 193.2 249.32 192.69 249.34C190.12 255.34 187.05 259.41 184.11 262.18C185.84 262.51 187.09 263.23 188.26 264.02C189.18 264.65 189.66 264.95 190.48 264.99C191.55 265.03 193.86 264.36 195.81 263.66C195.75 263.16 195.75 262.66 195.81 262.16C195.87 261.66 196 261.17 196.18 260.7C196.36 260.23 196.6 259.79 196.89 259.38C197.18 258.97 197.52 258.59 197.9 258.26C198.51 257.74 199.22 257.34 199.98 257.09C200.74 256.84 201.55 256.74 202.35 256.8C203.15 256.87 203.94 257.08 204.65 257.45C205.37 257.81 206.01 258.32 206.53 258.93ZM190.66 242.15C190.59 242.27 190.53 242.4 190.48 242.53C190.43 242.66 190.4 242.79 190.38 242.93C190.36 243.07 190.35 243.2 190.36 243.34C190.37 243.48 190.39 243.62 190.42 243.75C190.46 243.89 190.5 244.02 190.56 244.14C190.62 244.27 190.69 244.39 190.78 244.5C190.86 244.61 190.95 244.71 191.05 244.8C191.16 244.89 191.27 244.98 191.39 245.05C191.72 245.24 192.09 245.35 192.47 245.35C192.65 245.35 192.84 245.33 193.01 245.28C193.19 245.23 193.36 245.16 193.52 245.07C193.68 244.98 193.82 244.87 193.95 244.75C194.08 244.62 194.2 244.47 194.29 244.32C194.58 243.83 194.66 243.26 194.52 242.71C194.38 242.17 194.04 241.7 193.56 241.42C193.07 241.13 192.5 241.05 191.95 241.18C191.41 241.32 190.94 241.67 190.66 242.15ZM203.26 264.51C203.36 264.42 203.46 264.32 203.55 264.22C203.63 264.11 203.71 263.99 203.77 263.87C203.83 263.74 203.88 263.62 203.92 263.48C203.96 263.35 203.98 263.21 203.99 263.07C204.01 262.78 203.98 262.49 203.88 262.22C203.78 261.94 203.63 261.69 203.43 261.47C203.24 261.26 203 261.09 202.73 260.97C202.46 260.86 202.17 260.8 201.88 260.8C201.76 260.8 201.63 260.81 201.51 260.83C201.39 260.85 201.27 260.88 201.15 260.93C201.03 260.97 200.92 261.02 200.81 261.09C200.7 261.15 200.6 261.23 200.51 261.31C200.3 261.49 200.13 261.71 200 261.96C199.87 262.2 199.8 262.47 199.78 262.75C199.76 263.03 199.79 263.31 199.87 263.57C199.96 263.83 200.1 264.08 200.28 264.29C200.46 264.5 200.68 264.67 200.93 264.8C201.18 264.92 201.45 265 201.72 265.02C202 265.04 202.28 265 202.54 264.92C202.8 264.83 203.05 264.69 203.26 264.51ZM204.3 183.03C204.39 183.42 204.44 183.82 204.45 184.22C204.46 184.62 204.43 185.02 204.36 185.41C204.3 185.8 204.19 186.19 204.04 186.56C203.9 186.94 203.72 187.29 203.5 187.63C203.23 188.07 202.9 188.47 202.52 188.82C202.15 189.17 201.73 189.48 201.28 189.72C200.83 189.97 200.35 190.16 199.85 190.29C199.36 190.42 198.84 190.48 198.33 190.48C198.18 190.48 198.03 190.47 197.88 190.46C197.73 190.45 197.58 190.43 197.43 190.41C197.29 190.39 197.14 190.36 196.99 190.33C196.85 190.29 196.7 190.26 196.56 190.21C194.18 193.04 191.3 194.53 188.78 195.31C190.8 196.86 192.59 198.72 194.12 200.88C195.7 200.52 197.41 200.76 198.79 201.63C201.65 203.44 202.5 207.23 200.7 210.08C200.42 210.52 200.09 210.92 199.72 211.27C199.34 211.63 198.93 211.93 198.47 212.18C198.02 212.43 197.54 212.62 197.04 212.74C196.54 212.87 196.03 212.94 195.52 212.93C194.36 212.93 193.23 212.61 192.25 211.98C189.4 210.18 188.55 206.39 190.34 203.54C190.37 203.49 190.4 203.45 190.43 203.4C190.46 203.36 190.49 203.31 190.53 203.27C190.56 203.22 190.59 203.18 190.62 203.13C190.66 203.09 190.69 203.04 190.72 203C188.45 199.89 185.56 197.52 182.12 195.96C182.11 195.96 182.11 195.96 182.1 195.96C178.91 194.5 175.8 193.96 173.35 193.82L173.35 189.82C176.13 189.98 179.67 190.54 183.36 192.14C184.59 192.16 189.66 191.97 193.3 187.85C192.96 187.36 192.69 186.82 192.51 186.25C192.32 185.68 192.22 185.09 192.21 184.49C192.2 183.89 192.27 183.3 192.43 182.72C192.59 182.14 192.84 181.59 193.16 181.09C193.43 180.65 193.76 180.25 194.13 179.9C194.51 179.55 194.92 179.24 195.38 178.99C195.83 178.74 196.31 178.55 196.81 178.43C197.3 178.3 197.82 178.24 198.33 178.24C198.62 178.24 198.91 178.26 199.19 178.3C199.47 178.34 199.76 178.4 200.03 178.48C200.31 178.56 200.58 178.66 200.84 178.78C201.1 178.89 201.35 179.03 201.6 179.18C201.94 179.4 202.26 179.64 202.55 179.92C202.84 180.2 203.1 180.5 203.34 180.83C203.57 181.16 203.77 181.51 203.93 181.88C204.09 182.25 204.22 182.63 204.3 183.03ZM193.73 205.67C193.59 205.91 193.49 206.17 193.44 206.45C193.39 206.72 193.4 207 193.46 207.27C193.52 207.54 193.63 207.8 193.79 208.03C193.95 208.25 194.16 208.45 194.39 208.6C194.63 208.75 194.89 208.85 195.16 208.89C195.44 208.94 195.72 208.93 195.99 208.87C196.26 208.81 196.51 208.7 196.74 208.54C196.97 208.38 197.16 208.17 197.31 207.94C197.46 207.7 197.56 207.44 197.6 207.17C197.65 206.89 197.64 206.61 197.58 206.34C197.52 206.07 197.41 205.81 197.25 205.59C197.09 205.36 196.88 205.17 196.65 205.02C196.41 204.87 196.15 204.77 195.88 204.73C195.61 204.68 195.33 204.69 195.06 204.75C194.79 204.81 194.53 204.92 194.3 205.08C194.08 205.24 193.88 205.44 193.73 205.67ZM200.11 185.49C200.32 185.17 200.43 184.8 200.44 184.42C200.45 184.05 200.36 183.67 200.18 183.34C200 183.01 199.73 182.73 199.41 182.54C199.08 182.35 198.71 182.25 198.33 182.25C198.15 182.25 197.98 182.27 197.81 182.31C197.63 182.35 197.47 182.42 197.31 182.51C197.16 182.59 197.01 182.7 196.88 182.82C196.76 182.94 196.64 183.08 196.55 183.23C196.4 183.45 196.3 183.7 196.25 183.97C196.2 184.23 196.21 184.5 196.26 184.76C196.31 185.03 196.41 185.28 196.55 185.5C196.7 185.73 196.89 185.92 197.11 186.08L197.2 186.14C198.18 186.76 199.51 186.44 200.11 185.49Z" /> + </g> + <g id="Clip-Path" clip-path="url(#cp3)"> + <path id="Layer" fill-rule="evenodd" class="shp2" d="M213 224.01C210.76 224.39 209.57 225.15 208.14 226.08C206.98 226.83 205.74 227.64 203.93 228.27C208.47 231.6 214.56 237.87 217.12 242.06C217.52 241.95 217.94 241.89 218.35 241.87C218.77 241.85 219.19 241.87 219.6 241.93C220.01 242 220.42 242.11 220.81 242.25C221.2 242.4 221.58 242.58 221.93 242.81C222.27 243.02 222.59 243.27 222.88 243.55C223.17 243.82 223.44 244.13 223.67 244.46C223.9 244.79 224.1 245.14 224.26 245.51C224.43 245.88 224.55 246.26 224.64 246.65C224.73 247.04 224.78 247.44 224.79 247.84C224.8 248.24 224.77 248.64 224.7 249.03C224.63 249.43 224.52 249.81 224.38 250.19C224.23 250.56 224.05 250.92 223.83 251.25C223.56 251.69 223.23 252.09 222.86 252.44C222.48 252.8 222.07 253.1 221.62 253.35C221.16 253.6 220.68 253.79 220.19 253.92C219.69 254.04 219.17 254.11 218.66 254.1C217.5 254.1 216.37 253.78 215.39 253.16C215.26 253.07 215.18 253.02 215.11 252.97C212.46 251.1 211.75 247.47 213.49 244.71C213.59 244.56 213.69 244.42 213.8 244.28C211.13 239.63 201.64 230.6 199.03 229.96C195.63 229.11 193.67 229.18 191.18 229.27C189.73 229.32 188.08 229.39 185.97 229.27C182.89 229.11 181.2 227.19 179.71 225.49C178.15 223.71 176.65 222.12 173.36 221.82L173.36 217.82C178.5 218.2 180.92 220.79 182.72 222.85C184.08 224.39 184.85 225.2 186.18 225.27C188.12 225.37 189.6 225.32 191.03 225.27C192.77 225.2 194.36 225.15 196.32 225.38C202 225.29 203.92 224.04 205.96 222.72C207.58 221.66 209.41 220.48 212.78 219.99C212.82 219.85 212.87 219.71 212.92 219.58C212.97 219.44 213.02 219.31 213.08 219.17C213.14 219.04 213.2 218.91 213.27 218.78C213.34 218.66 213.41 218.53 213.49 218.41C213.54 218.34 213.59 218.27 213.64 218.19C213.69 218.12 213.74 218.05 213.79 217.98C213.84 217.92 213.9 217.85 213.95 217.78C214.01 217.71 214.07 217.65 214.12 217.58C214.26 217.44 214.41 217.32 214.55 217.19C213.44 213.09 213.24 207.29 214.59 202.77C214.56 202.75 214.54 202.73 214.51 202.71C213.99 202.23 213.56 201.66 213.23 201.03C212.9 200.4 212.69 199.72 212.59 199.01C212.5 198.31 212.53 197.59 212.68 196.9C212.84 196.21 213.11 195.55 213.49 194.95C213.76 194.51 214.09 194.11 214.46 193.76C214.84 193.4 215.26 193.1 215.71 192.85C216.16 192.6 216.64 192.41 217.14 192.28C217.64 192.16 218.15 192.09 218.66 192.1C218.95 192.1 219.24 192.12 219.52 192.16C219.81 192.2 220.09 192.26 220.36 192.34C220.64 192.42 220.91 192.52 221.17 192.63C221.43 192.75 221.69 192.89 221.93 193.04C222.27 193.25 222.59 193.5 222.88 193.78C223.17 194.06 223.44 194.36 223.67 194.69C223.9 195.02 224.1 195.37 224.26 195.74C224.42 196.11 224.55 196.49 224.64 196.88C224.73 197.27 224.78 197.67 224.79 198.07C224.8 198.47 224.77 198.87 224.7 199.27C224.63 199.66 224.52 200.05 224.38 200.42C224.23 200.79 224.05 201.15 223.83 201.49C223.56 201.92 223.23 202.32 222.86 202.68C222.48 203.03 222.07 203.33 221.62 203.58C221.16 203.83 220.68 204.02 220.19 204.15C219.69 204.27 219.17 204.34 218.66 204.34C218.55 204.34 218.44 204.32 218.32 204.31C217.37 207.81 217.58 212.42 218.29 215.58C218.41 215.57 218.54 215.56 218.66 215.56C218.95 215.56 219.24 215.58 219.52 215.62C219.81 215.66 220.09 215.72 220.36 215.8C220.64 215.88 220.91 215.98 221.17 216.1C221.43 216.21 221.69 216.35 221.93 216.5C222.27 216.72 222.59 216.96 222.88 217.24C223.17 217.52 223.44 217.82 223.67 218.15C223.9 218.48 224.1 218.83 224.26 219.2C224.42 219.57 224.55 219.95 224.64 220.35C224.73 220.74 224.78 221.14 224.79 221.54C224.8 221.94 224.77 222.34 224.7 222.73C224.63 223.12 224.52 223.51 224.38 223.88C224.23 224.26 224.05 224.61 223.83 224.95C223.56 225.39 223.23 225.79 222.86 226.14C222.48 226.49 222.07 226.8 221.62 227.05C221.16 227.29 220.68 227.48 220.19 227.61C219.69 227.74 219.17 227.8 218.66 227.8C218.34 227.8 218.03 227.78 217.72 227.73C217.4 227.68 217.1 227.6 216.8 227.51C216.49 227.41 216.2 227.29 215.92 227.15C215.64 227.01 215.37 226.84 215.11 226.66C214.88 226.5 214.65 226.31 214.45 226.12C214.24 225.92 214.04 225.71 213.87 225.49C213.69 225.26 213.52 225.03 213.38 224.78C213.24 224.53 213.11 224.28 213 224.01ZM216.88 197.09C216.74 197.31 216.64 197.56 216.59 197.83C216.54 198.09 216.54 198.36 216.59 198.62C216.64 198.88 216.74 199.14 216.89 199.36C217.03 199.59 217.22 199.78 217.44 199.94L217.53 200C218.51 200.62 219.85 200.3 220.44 199.34C220.52 199.23 220.58 199.11 220.63 198.98C220.68 198.85 220.72 198.72 220.74 198.58C220.77 198.44 220.78 198.31 220.78 198.17C220.77 198.03 220.76 197.89 220.73 197.76C220.6 197.2 220.27 196.73 219.79 196.43C219.45 196.22 219.06 196.1 218.66 196.1C218.49 196.1 218.31 196.13 218.14 196.17C217.97 196.21 217.8 196.28 217.64 196.36C217.49 196.45 217.35 196.55 217.22 196.68C217.09 196.8 216.97 196.93 216.88 197.09ZM216.88 220.55C216.74 220.77 216.64 221.03 216.59 221.29C216.54 221.55 216.54 221.82 216.59 222.09C216.64 222.35 216.74 222.6 216.89 222.82C217.03 223.05 217.22 223.24 217.44 223.4L217.53 223.46C218.51 224.08 219.85 223.76 220.44 222.81C220.52 222.69 220.58 222.57 220.63 222.44C220.68 222.31 220.72 222.18 220.74 222.04C220.77 221.91 220.78 221.77 220.78 221.63C220.77 221.49 220.76 221.36 220.72 221.22C220.6 220.67 220.27 220.2 219.79 219.89C219.45 219.68 219.06 219.57 218.66 219.57C218.49 219.57 218.31 219.59 218.14 219.63C217.97 219.68 217.8 219.74 217.64 219.83C217.49 219.91 217.35 220.02 217.22 220.14C217.09 220.26 216.97 220.4 216.88 220.55ZM216.88 246.85C216.74 247.08 216.64 247.33 216.59 247.59C216.54 247.86 216.54 248.13 216.59 248.39C216.64 248.65 216.74 248.9 216.89 249.13C217.03 249.35 217.22 249.55 217.44 249.7L217.53 249.76C218.51 250.38 219.85 250.06 220.44 249.11C220.52 249 220.58 248.87 220.63 248.74C220.68 248.62 220.72 248.48 220.74 248.35C220.77 248.21 220.78 248.07 220.78 247.94C220.77 247.8 220.76 247.66 220.72 247.53C220.6 246.97 220.27 246.5 219.79 246.2C219.45 245.98 219.06 245.87 218.66 245.87C218.49 245.87 218.31 245.89 218.14 245.94C217.97 245.98 217.8 246.05 217.64 246.13C217.49 246.22 217.35 246.32 217.22 246.44C217.09 246.56 216.97 246.7 216.88 246.85Z" /> + </g> + </g> + <path id="Layer" fill-rule="evenodd" class="shp3" d="M206.53 261.93C206.79 262.23 207.02 262.56 207.22 262.91C207.42 263.26 207.58 263.63 207.71 264.01C207.83 264.39 207.92 264.78 207.96 265.18C208.01 265.58 208.02 265.98 207.99 266.38C207.96 266.78 207.89 267.17 207.78 267.56C207.67 267.95 207.53 268.32 207.35 268.68C207.17 269.04 206.95 269.38 206.7 269.69C206.45 270.01 206.17 270.3 205.87 270.56C205.59 270.79 205.3 271 204.99 271.19C204.68 271.37 204.35 271.52 204.01 271.65C203.67 271.78 203.32 271.87 202.96 271.94C202.61 272 202.25 272.03 201.89 272.03C201.49 272.03 201.1 271.99 200.71 271.92C200.32 271.84 199.94 271.73 199.57 271.58C199.21 271.43 198.85 271.24 198.52 271.02C198.19 270.81 197.88 270.56 197.6 270.28C195.75 270.98 192.69 272 190.58 272C190.48 272 190.37 272 190.27 272C188.3 271.89 187.03 271.03 186.01 270.34C184.44 269.28 182.95 268.27 177.6 269.63C175.24 270.71 173.63 270.79 173.36 270.82L173.36 266.82C173.86 266.72 174.73 266.55 175.91 266.01C175.94 266 175.97 265.98 176 265.97C176.03 265.95 176.06 265.94 176.09 265.92C176.12 265.91 176.15 265.89 176.18 265.88C176.21 265.87 176.24 265.86 176.27 265.85C179.62 264.24 184.79 260.38 188.84 251.16C188.56 250.95 188.29 250.72 188.05 250.47C187.8 250.22 187.58 249.94 187.39 249.65C187.19 249.36 187.02 249.05 186.88 248.73C186.74 248.41 186.62 248.07 186.54 247.73C186.44 247.34 186.38 246.95 186.36 246.55C186.33 246.15 186.35 245.74 186.41 245.35C186.47 244.95 186.57 244.56 186.7 244.18C186.84 243.8 187.01 243.44 187.22 243.1C187.49 242.64 187.82 242.23 188.19 241.86C188.57 241.49 188.99 241.17 189.45 240.91C189.91 240.65 190.4 240.45 190.91 240.32C191.42 240.18 191.95 240.11 192.48 240.11C192.75 240.11 193.02 240.13 193.3 240.17C193.57 240.2 193.84 240.26 194.1 240.33C194.37 240.4 194.62 240.5 194.88 240.6C195.13 240.71 195.37 240.83 195.61 240.97C198.51 242.71 199.46 246.47 197.73 249.37C197.47 249.8 197.16 250.21 196.79 250.56C196.43 250.92 196.03 251.23 195.59 251.49C195.15 251.75 194.68 251.95 194.2 252.1C193.71 252.24 193.2 252.32 192.69 252.34C190.12 258.35 187.05 262.41 184.11 265.18C185.84 265.51 187.09 266.23 188.26 267.02C189.18 267.65 189.66 267.95 190.48 267.99C191.55 268.03 193.86 267.36 195.81 266.66C195.75 266.16 195.75 265.66 195.81 265.16C195.87 264.66 196 264.17 196.18 263.7C196.36 263.23 196.6 262.79 196.89 262.38C197.18 261.97 197.52 261.59 197.9 261.26C198.51 260.74 199.22 260.34 199.98 260.09C200.74 259.84 201.55 259.74 202.35 259.8C203.15 259.87 203.94 260.08 204.65 260.45C205.37 260.81 206.01 261.32 206.53 261.93ZM190.66 245.15C190.59 245.27 190.53 245.4 190.48 245.53C190.43 245.66 190.4 245.79 190.38 245.93C190.36 246.07 190.35 246.2 190.36 246.34C190.37 246.48 190.39 246.62 190.42 246.75C190.46 246.89 190.5 247.02 190.56 247.14C190.62 247.27 190.69 247.39 190.78 247.5C190.86 247.61 190.95 247.71 191.05 247.8C191.16 247.89 191.27 247.98 191.39 248.05C191.72 248.24 192.09 248.35 192.47 248.35C192.65 248.35 192.84 248.33 193.01 248.28C193.19 248.23 193.36 248.16 193.52 248.07C193.68 247.98 193.82 247.87 193.95 247.75C194.08 247.62 194.2 247.47 194.29 247.32C194.58 246.83 194.66 246.26 194.52 245.71C194.38 245.17 194.04 244.7 193.56 244.42C193.07 244.13 192.5 244.05 191.95 244.18C191.41 244.32 190.94 244.67 190.66 245.15ZM203.26 267.51C203.36 267.42 203.46 267.32 203.55 267.22C203.63 267.11 203.71 266.99 203.77 266.87C203.83 266.74 203.88 266.62 203.92 266.48C203.96 266.35 203.98 266.21 203.99 266.07C204.01 265.78 203.98 265.49 203.88 265.22C203.78 264.94 203.63 264.69 203.43 264.47C203.24 264.26 203 264.09 202.73 263.97C202.46 263.86 202.17 263.8 201.88 263.8C201.76 263.8 201.63 263.81 201.51 263.83C201.39 263.85 201.27 263.88 201.15 263.93C201.03 263.97 200.92 264.02 200.81 264.09C200.7 264.15 200.6 264.23 200.51 264.31C200.3 264.49 200.13 264.71 200 264.96C199.87 265.2 199.8 265.47 199.78 265.75C199.76 266.03 199.79 266.31 199.87 266.57C199.96 266.83 200.1 267.08 200.28 267.29C200.46 267.5 200.68 267.67 200.93 267.8C201.18 267.92 201.45 268 201.72 268.02C202 268.04 202.28 268 202.54 267.92C202.8 267.83 203.05 267.69 203.26 267.51Z" /> + <path id="Layer" class="shp2" d="" /> + <path id="Layer" fill-rule="evenodd" class="shp3" d="M204.3 185.03C204.39 185.42 204.44 185.82 204.45 186.22C204.46 186.62 204.43 187.02 204.36 187.41C204.3 187.81 204.19 188.19 204.04 188.57C203.9 188.94 203.72 189.3 203.5 189.63C203.23 190.07 202.9 190.47 202.53 190.82C202.15 191.18 201.73 191.48 201.28 191.73C200.83 191.98 200.35 192.17 199.85 192.29C199.36 192.42 198.84 192.48 198.33 192.48C198.18 192.48 198.03 192.48 197.88 192.46C197.73 192.45 197.58 192.44 197.43 192.41C197.29 192.39 197.14 192.36 196.99 192.33C196.85 192.3 196.7 192.26 196.56 192.22C194.18 195.04 191.3 196.53 188.78 197.31C190.8 198.86 192.59 200.72 194.12 202.88C195.7 202.52 197.41 202.76 198.79 203.63C201.65 205.44 202.5 209.24 200.7 212.09C200.42 212.52 200.09 212.92 199.72 213.28C199.34 213.63 198.93 213.93 198.47 214.18C198.02 214.43 197.54 214.62 197.04 214.75C196.54 214.88 196.03 214.94 195.52 214.94C194.36 214.94 193.23 214.61 192.25 213.99C189.4 212.19 188.55 208.4 190.34 205.54C190.37 205.5 190.4 205.45 190.43 205.4C190.46 205.36 190.49 205.31 190.53 205.27C190.56 205.22 190.59 205.18 190.62 205.14C190.66 205.09 190.69 205.05 190.72 205C188.45 201.89 185.56 199.53 182.12 197.96C182.11 197.96 182.11 197.96 182.1 197.96C178.91 196.51 175.8 195.97 173.35 195.82L173.35 191.83C176.13 191.98 179.67 192.54 183.36 194.14C184.59 194.16 189.66 193.97 193.3 189.85C192.96 189.36 192.69 188.82 192.51 188.25C192.32 187.68 192.22 187.09 192.21 186.49C192.2 185.9 192.27 185.3 192.43 184.72C192.59 184.15 192.84 183.6 193.16 183.09C193.43 182.66 193.76 182.26 194.13 181.9C194.51 181.55 194.92 181.25 195.38 181C195.83 180.75 196.31 180.56 196.81 180.43C197.3 180.3 197.82 180.24 198.33 180.24C198.62 180.24 198.91 180.26 199.19 180.3C199.47 180.34 199.76 180.4 200.03 180.48C200.31 180.56 200.58 180.66 200.84 180.78C201.1 180.9 201.35 181.03 201.6 181.19C201.94 181.4 202.26 181.65 202.55 181.93C202.84 182.2 203.1 182.51 203.34 182.84C203.57 183.17 203.77 183.52 203.93 183.89C204.09 184.25 204.22 184.64 204.3 185.03ZM193.73 207.68C193.59 207.91 193.49 208.18 193.44 208.45C193.39 208.72 193.4 209 193.46 209.28C193.52 209.55 193.63 209.8 193.79 210.03C193.95 210.26 194.16 210.45 194.39 210.6C194.63 210.75 194.89 210.85 195.16 210.9C195.44 210.95 195.72 210.94 195.99 210.88C196.26 210.82 196.51 210.7 196.74 210.54C196.97 210.38 197.16 210.18 197.31 209.94C197.46 209.71 197.56 209.44 197.6 209.17C197.65 208.9 197.64 208.62 197.58 208.35C197.52 208.07 197.41 207.82 197.25 207.59C197.09 207.36 196.88 207.17 196.65 207.02C196.41 206.88 196.15 206.78 195.88 206.73C195.61 206.68 195.33 206.69 195.06 206.75C194.79 206.81 194.53 206.93 194.3 207.08C194.08 207.24 193.88 207.44 193.73 207.68ZM200.11 187.49C200.32 187.17 200.43 186.8 200.44 186.43C200.45 186.05 200.36 185.68 200.18 185.34C200 185.01 199.73 184.74 199.41 184.55C199.08 184.35 198.71 184.25 198.33 184.25C198.15 184.25 197.98 184.27 197.81 184.32C197.63 184.36 197.47 184.42 197.31 184.51C197.16 184.59 197.01 184.7 196.88 184.82C196.76 184.94 196.64 185.08 196.55 185.23C196.4 185.46 196.3 185.71 196.25 185.97C196.2 186.23 196.21 186.5 196.26 186.77C196.31 187.03 196.41 187.28 196.55 187.51C196.7 187.73 196.89 187.93 197.11 188.08L197.2 188.14C198.18 188.76 199.51 188.44 200.11 187.49Z" /> + <path id="Layer" fill-rule="evenodd" class="shp3" d="M213 227.01C210.76 227.39 209.57 228.15 208.14 229.08C206.98 229.83 205.74 230.64 203.93 231.26C208.47 234.6 214.56 240.87 217.12 245.06C217.52 244.95 217.94 244.89 218.35 244.87C218.77 244.85 219.19 244.87 219.6 244.93C220.01 245 220.42 245.1 220.81 245.25C221.2 245.4 221.58 245.58 221.93 245.81C222.27 246.02 222.59 246.27 222.88 246.55C223.17 246.82 223.44 247.13 223.67 247.46C223.9 247.79 224.1 248.14 224.26 248.51C224.43 248.87 224.55 249.26 224.64 249.65C224.73 250.04 224.78 250.44 224.79 250.84C224.8 251.24 224.77 251.64 224.7 252.03C224.63 252.43 224.52 252.81 224.38 253.19C224.23 253.56 224.05 253.92 223.83 254.25C223.56 254.69 223.23 255.09 222.86 255.44C222.48 255.8 222.07 256.1 221.62 256.35C221.16 256.6 220.68 256.79 220.19 256.91C219.69 257.04 219.17 257.1 218.66 257.1C217.5 257.1 216.37 256.78 215.39 256.15C215.26 256.07 215.18 256.02 215.11 255.96C212.46 254.1 211.75 250.47 213.49 247.71C213.59 247.56 213.69 247.42 213.8 247.28C211.13 242.63 201.64 233.6 199.03 232.96C195.63 232.11 193.67 232.18 191.18 232.27C189.73 232.32 188.08 232.39 185.97 232.27C182.89 232.1 181.2 230.18 179.71 228.49C178.15 226.71 176.65 225.12 173.36 224.82L173.36 220.82C178.5 221.2 180.92 223.79 182.72 225.84C184.08 227.39 184.85 228.2 186.18 228.27C188.12 228.37 189.6 228.32 191.03 228.27C192.77 228.2 194.36 228.15 196.32 228.38C202 228.29 203.92 227.04 205.96 225.72C207.58 224.66 209.41 223.48 212.78 222.99C212.82 222.85 212.87 222.71 212.92 222.58C212.97 222.44 213.02 222.31 213.08 222.17C213.14 222.04 213.2 221.91 213.27 221.78C213.34 221.66 213.41 221.53 213.49 221.41C213.54 221.34 213.59 221.27 213.64 221.19C213.69 221.12 213.74 221.05 213.79 220.98C213.84 220.92 213.9 220.85 213.95 220.78C214.01 220.71 214.07 220.65 214.12 220.58C214.26 220.44 214.41 220.32 214.55 220.19C213.44 216.09 213.24 210.29 214.59 205.77C214.56 205.75 214.54 205.73 214.51 205.71C213.99 205.23 213.56 204.66 213.23 204.03C212.9 203.4 212.69 202.72 212.59 202.01C212.5 201.31 212.53 200.59 212.68 199.9C212.84 199.21 213.11 198.55 213.49 197.95C213.76 197.51 214.09 197.11 214.46 196.76C214.84 196.4 215.26 196.1 215.71 195.85C216.16 195.6 216.64 195.41 217.14 195.28C217.64 195.16 218.15 195.09 218.66 195.1C218.95 195.1 219.24 195.12 219.52 195.16C219.81 195.2 220.09 195.26 220.36 195.34C220.64 195.42 220.91 195.52 221.17 195.63C221.43 195.75 221.69 195.89 221.93 196.04C222.27 196.25 222.59 196.5 222.88 196.78C223.17 197.06 223.44 197.36 223.67 197.69C223.9 198.02 224.1 198.37 224.26 198.74C224.42 199.11 224.55 199.49 224.64 199.88C224.73 200.27 224.78 200.67 224.79 201.07C224.8 201.47 224.77 201.87 224.7 202.27C224.63 202.66 224.52 203.05 224.38 203.42C224.23 203.79 224.05 204.15 223.83 204.49C223.56 204.92 223.23 205.32 222.86 205.68C222.48 206.03 222.07 206.33 221.62 206.58C221.16 206.83 220.68 207.02 220.19 207.15C219.69 207.27 219.17 207.34 218.66 207.34C218.55 207.34 218.44 207.32 218.32 207.31C217.37 210.81 217.58 215.42 218.29 218.58C218.41 218.57 218.54 218.56 218.66 218.56C218.95 218.56 219.24 218.58 219.52 218.62C219.81 218.66 220.09 218.72 220.36 218.8C220.64 218.88 220.91 218.98 221.17 219.1C221.43 219.21 221.69 219.35 221.93 219.5C222.27 219.72 222.59 219.96 222.88 220.24C223.17 220.52 223.44 220.82 223.67 221.15C223.9 221.48 224.1 221.83 224.26 222.2C224.42 222.57 224.55 222.95 224.64 223.35C224.73 223.74 224.78 224.14 224.79 224.54C224.8 224.94 224.77 225.34 224.7 225.73C224.63 226.12 224.52 226.51 224.38 226.88C224.23 227.26 224.05 227.61 223.83 227.95C223.56 228.39 223.23 228.79 222.86 229.14C222.48 229.49 222.07 229.8 221.62 230.05C221.16 230.29 220.68 230.48 220.19 230.61C219.69 230.74 219.17 230.8 218.66 230.8C218.34 230.8 218.03 230.78 217.72 230.73C217.4 230.68 217.1 230.6 216.8 230.51C216.49 230.41 216.2 230.29 215.92 230.15C215.64 230.01 215.37 229.84 215.11 229.66C214.88 229.5 214.65 229.31 214.45 229.12C214.24 228.92 214.04 228.71 213.87 228.49C213.69 228.26 213.52 228.03 213.38 227.78C213.24 227.53 213.11 227.28 213 227.01ZM216.88 200.09C216.74 200.31 216.64 200.56 216.59 200.83C216.54 201.09 216.54 201.36 216.59 201.62C216.64 201.88 216.74 202.14 216.89 202.36C217.03 202.59 217.22 202.78 217.44 202.94L217.53 203C218.51 203.62 219.85 203.3 220.44 202.34C220.52 202.23 220.58 202.11 220.63 201.98C220.68 201.85 220.72 201.72 220.74 201.58C220.77 201.44 220.78 201.31 220.78 201.17C220.77 201.03 220.76 200.89 220.73 200.76C220.6 200.2 220.27 199.73 219.79 199.43C219.45 199.22 219.06 199.1 218.66 199.1C218.49 199.1 218.31 199.13 218.14 199.17C217.97 199.21 217.8 199.28 217.64 199.36C217.49 199.45 217.35 199.55 217.22 199.68C217.09 199.8 216.97 199.93 216.88 200.09ZM216.88 223.55C216.74 223.77 216.64 224.03 216.59 224.29C216.54 224.55 216.54 224.82 216.59 225.09C216.64 225.35 216.74 225.6 216.89 225.82C217.03 226.05 217.22 226.24 217.44 226.4L217.53 226.46C218.51 227.08 219.85 226.76 220.44 225.81C220.52 225.69 220.58 225.57 220.63 225.44C220.68 225.31 220.72 225.18 220.74 225.04C220.77 224.91 220.78 224.77 220.78 224.63C220.77 224.49 220.76 224.36 220.72 224.22C220.6 223.67 220.27 223.2 219.79 222.89C219.45 222.68 219.06 222.57 218.66 222.57C218.49 222.57 218.31 222.59 218.14 222.63C217.97 222.68 217.8 222.74 217.64 222.83C217.49 222.91 217.35 223.02 217.22 223.14C217.09 223.26 216.97 223.4 216.88 223.55ZM216.88 249.85C216.74 250.08 216.64 250.33 216.59 250.59C216.54 250.86 216.54 251.13 216.59 251.39C216.64 251.65 216.74 251.9 216.89 252.13C217.03 252.35 217.22 252.55 217.44 252.7L217.53 252.76C218.51 253.38 219.85 253.06 220.44 252.11C220.52 252 220.58 251.87 220.63 251.74C220.68 251.62 220.72 251.48 220.74 251.35C220.77 251.21 220.78 251.07 220.78 250.94C220.77 250.8 220.76 250.66 220.72 250.53C220.6 249.97 220.27 249.5 219.79 249.2C219.45 248.98 219.06 248.87 218.66 248.87C218.49 248.87 218.31 248.89 218.14 248.94C217.97 248.98 217.8 249.05 217.64 249.13C217.49 249.22 217.35 249.32 217.22 249.44C217.09 249.56 216.97 249.7 216.88 249.85Z" /> + <path id="Layer" fill-rule="evenodd" class="shp4" d="M206.53 258.93C206.79 259.23 207.02 259.56 207.22 259.91C207.42 260.26 207.58 260.63 207.71 261.01C207.83 261.39 207.92 261.78 207.96 262.18C208.01 262.58 208.02 262.98 207.99 263.38C207.96 263.78 207.89 264.17 207.78 264.56C207.67 264.95 207.53 265.32 207.35 265.68C207.17 266.04 206.95 266.38 206.7 266.69C206.45 267.01 206.17 267.3 205.87 267.56C205.59 267.79 205.3 268 204.99 268.19C204.68 268.37 204.35 268.52 204.01 268.65C203.67 268.78 203.32 268.87 202.96 268.94C202.61 269 202.25 269.03 201.89 269.03C201.49 269.03 201.1 268.99 200.71 268.92C200.32 268.84 199.94 268.73 199.57 268.58C199.21 268.43 198.85 268.24 198.52 268.02C198.19 267.81 197.88 267.56 197.6 267.28C195.75 267.98 192.69 269 190.58 269C190.48 269 190.37 269 190.27 269C188.3 268.89 187.03 268.03 186.01 267.34C184.44 266.28 182.95 265.27 177.6 266.63C175.24 267.71 173.63 267.79 173.36 267.82L173.36 263.82C173.86 263.72 174.73 263.55 175.91 263.01C175.94 263 175.97 262.98 176 262.97C176.03 262.95 176.06 262.94 176.09 262.92C176.12 262.91 176.15 262.89 176.18 262.88C176.21 262.87 176.24 262.86 176.27 262.85C179.62 261.24 184.79 257.38 188.84 248.16C188.56 247.95 188.29 247.72 188.05 247.47C187.8 247.22 187.58 246.94 187.39 246.65C187.19 246.36 187.02 246.05 186.88 245.73C186.74 245.41 186.62 245.07 186.54 244.73C186.44 244.34 186.38 243.95 186.36 243.55C186.33 243.15 186.35 242.74 186.41 242.35C186.47 241.95 186.57 241.56 186.7 241.18C186.84 240.8 187.01 240.44 187.22 240.1C187.49 239.64 187.82 239.23 188.19 238.86C188.57 238.49 188.99 238.17 189.45 237.91C189.91 237.65 190.4 237.45 190.91 237.32C191.42 237.18 191.95 237.11 192.48 237.11C192.75 237.11 193.02 237.13 193.3 237.17C193.57 237.2 193.84 237.26 194.1 237.33C194.37 237.4 194.62 237.5 194.88 237.6C195.13 237.71 195.37 237.83 195.61 237.97C198.51 239.71 199.46 243.47 197.73 246.37C197.47 246.8 197.16 247.21 196.79 247.56C196.43 247.92 196.03 248.23 195.59 248.49C195.15 248.75 194.68 248.95 194.2 249.1C193.71 249.24 193.2 249.32 192.69 249.34C190.12 255.34 187.05 259.41 184.11 262.18C185.84 262.51 187.09 263.23 188.26 264.02C189.18 264.65 189.66 264.95 190.48 264.99C191.55 265.03 193.86 264.36 195.81 263.66C195.75 263.16 195.75 262.66 195.81 262.16C195.87 261.66 196 261.17 196.18 260.7C196.36 260.23 196.6 259.79 196.89 259.38C197.18 258.97 197.52 258.59 197.9 258.26C198.51 257.74 199.22 257.34 199.98 257.09C200.74 256.84 201.55 256.74 202.35 256.8C203.15 256.87 203.94 257.08 204.65 257.45C205.37 257.81 206.01 258.32 206.53 258.93ZM190.66 242.15C190.59 242.27 190.53 242.4 190.48 242.53C190.43 242.66 190.4 242.79 190.38 242.93C190.36 243.07 190.35 243.2 190.36 243.34C190.37 243.48 190.39 243.62 190.42 243.75C190.46 243.89 190.5 244.02 190.56 244.14C190.62 244.27 190.69 244.39 190.78 244.5C190.86 244.61 190.95 244.71 191.05 244.8C191.16 244.89 191.27 244.98 191.39 245.05C191.72 245.24 192.09 245.35 192.47 245.35C192.65 245.35 192.84 245.33 193.01 245.28C193.19 245.23 193.36 245.16 193.52 245.07C193.68 244.98 193.82 244.87 193.95 244.75C194.08 244.62 194.2 244.47 194.29 244.32C194.58 243.83 194.66 243.26 194.52 242.71C194.38 242.17 194.04 241.7 193.56 241.42C193.07 241.13 192.5 241.05 191.95 241.18C191.41 241.32 190.94 241.67 190.66 242.15ZM203.26 264.51C203.36 264.42 203.46 264.32 203.55 264.22C203.63 264.11 203.71 263.99 203.77 263.87C203.83 263.74 203.88 263.62 203.92 263.48C203.96 263.35 203.98 263.21 203.99 263.07C204.01 262.78 203.98 262.49 203.88 262.22C203.78 261.94 203.63 261.69 203.43 261.47C203.24 261.26 203 261.09 202.73 260.97C202.46 260.86 202.17 260.8 201.88 260.8C201.76 260.8 201.63 260.81 201.51 260.83C201.39 260.85 201.27 260.88 201.15 260.93C201.03 260.97 200.92 261.02 200.81 261.09C200.7 261.15 200.6 261.23 200.51 261.31C200.3 261.49 200.13 261.71 200 261.96C199.87 262.2 199.8 262.47 199.78 262.75C199.76 263.03 199.79 263.31 199.87 263.57C199.96 263.83 200.1 264.08 200.28 264.29C200.46 264.5 200.68 264.67 200.93 264.8C201.18 264.92 201.45 265 201.72 265.02C202 265.04 202.28 265 202.54 264.92C202.8 264.83 203.05 264.69 203.26 264.51ZM204.3 183.03C204.39 183.42 204.44 183.82 204.45 184.22C204.46 184.62 204.43 185.02 204.36 185.41C204.3 185.8 204.19 186.19 204.04 186.56C203.9 186.94 203.72 187.29 203.5 187.63C203.23 188.07 202.9 188.47 202.52 188.82C202.15 189.17 201.73 189.48 201.28 189.72C200.83 189.97 200.35 190.16 199.85 190.29C199.36 190.42 198.84 190.48 198.33 190.48C198.18 190.48 198.03 190.47 197.88 190.46C197.73 190.45 197.58 190.43 197.43 190.41C197.29 190.39 197.14 190.36 196.99 190.33C196.85 190.29 196.7 190.26 196.56 190.21C194.18 193.04 191.3 194.53 188.78 195.31C190.8 196.86 192.59 198.72 194.12 200.88C195.7 200.52 197.41 200.76 198.79 201.63C201.65 203.44 202.5 207.23 200.7 210.08C200.42 210.52 200.09 210.92 199.72 211.27C199.34 211.63 198.93 211.93 198.47 212.18C198.02 212.43 197.54 212.62 197.04 212.74C196.54 212.87 196.03 212.94 195.52 212.93C194.36 212.93 193.23 212.61 192.25 211.98C189.4 210.18 188.55 206.39 190.34 203.54C190.37 203.49 190.4 203.45 190.43 203.4C190.46 203.36 190.49 203.31 190.53 203.27C190.56 203.22 190.59 203.18 190.62 203.13C190.66 203.09 190.69 203.04 190.72 203C188.45 199.89 185.56 197.52 182.12 195.96C182.11 195.96 182.11 195.96 182.1 195.96C178.91 194.5 175.8 193.96 173.35 193.82L173.35 189.82C176.13 189.98 179.67 190.54 183.36 192.14C184.59 192.16 189.66 191.97 193.3 187.85C192.96 187.36 192.69 186.82 192.51 186.25C192.32 185.68 192.22 185.09 192.21 184.49C192.2 183.89 192.27 183.3 192.43 182.72C192.59 182.14 192.84 181.59 193.16 181.09C193.43 180.65 193.76 180.25 194.13 179.9C194.51 179.55 194.92 179.24 195.38 178.99C195.83 178.74 196.31 178.55 196.81 178.43C197.3 178.3 197.82 178.24 198.33 178.24C198.62 178.24 198.91 178.26 199.19 178.3C199.47 178.34 199.76 178.4 200.03 178.48C200.31 178.56 200.58 178.66 200.84 178.78C201.1 178.89 201.35 179.03 201.6 179.18C201.94 179.4 202.26 179.64 202.55 179.92C202.84 180.2 203.1 180.5 203.34 180.83C203.57 181.16 203.77 181.51 203.93 181.88C204.09 182.25 204.22 182.63 204.3 183.03ZM193.73 205.67C193.59 205.91 193.49 206.17 193.44 206.45C193.39 206.72 193.4 207 193.46 207.27C193.52 207.54 193.63 207.8 193.79 208.03C193.95 208.25 194.16 208.45 194.39 208.6C194.63 208.75 194.89 208.85 195.16 208.89C195.44 208.94 195.72 208.93 195.99 208.87C196.26 208.81 196.51 208.7 196.74 208.54C196.97 208.38 197.16 208.17 197.31 207.94C197.46 207.7 197.56 207.44 197.6 207.17C197.65 206.89 197.64 206.61 197.58 206.34C197.52 206.07 197.41 205.81 197.25 205.59C197.09 205.36 196.88 205.17 196.65 205.02C196.41 204.87 196.15 204.77 195.88 204.73C195.61 204.68 195.33 204.69 195.06 204.75C194.79 204.81 194.53 204.92 194.3 205.08C194.08 205.24 193.88 205.44 193.73 205.67ZM200.11 185.49C200.32 185.17 200.43 184.8 200.44 184.42C200.45 184.05 200.36 183.67 200.18 183.34C200 183.01 199.73 182.73 199.41 182.54C199.08 182.35 198.71 182.25 198.33 182.25C198.15 182.25 197.98 182.27 197.81 182.31C197.63 182.35 197.47 182.42 197.31 182.51C197.16 182.59 197.01 182.7 196.88 182.82C196.76 182.94 196.64 183.08 196.55 183.23C196.4 183.45 196.3 183.7 196.25 183.97C196.2 184.23 196.21 184.5 196.26 184.76C196.31 185.03 196.41 185.28 196.55 185.5C196.7 185.73 196.89 185.92 197.11 186.08L197.2 186.14C198.18 186.76 199.51 186.44 200.11 185.49Z" /> + <path id="Layer" fill-rule="evenodd" class="shp4" d="M213 224.01C210.76 224.39 209.57 225.15 208.14 226.08C206.98 226.83 205.74 227.64 203.93 228.27C208.47 231.6 214.56 237.87 217.12 242.06C217.52 241.95 217.94 241.89 218.35 241.87C218.77 241.85 219.19 241.87 219.6 241.93C220.01 242 220.42 242.11 220.81 242.25C221.2 242.4 221.58 242.58 221.93 242.81C222.27 243.02 222.59 243.27 222.88 243.55C223.17 243.82 223.44 244.13 223.67 244.46C223.9 244.79 224.1 245.14 224.26 245.51C224.43 245.88 224.55 246.26 224.64 246.65C224.73 247.04 224.78 247.44 224.79 247.84C224.8 248.24 224.77 248.64 224.7 249.03C224.63 249.43 224.52 249.81 224.38 250.19C224.23 250.56 224.05 250.92 223.83 251.25C223.56 251.69 223.23 252.09 222.86 252.44C222.48 252.8 222.07 253.1 221.62 253.35C221.16 253.6 220.68 253.79 220.19 253.92C219.69 254.04 219.17 254.11 218.66 254.1C217.5 254.1 216.37 253.78 215.39 253.16C215.26 253.07 215.18 253.02 215.11 252.97C212.46 251.1 211.75 247.47 213.49 244.71C213.59 244.56 213.69 244.42 213.8 244.28C211.13 239.63 201.64 230.6 199.03 229.96C195.63 229.11 193.67 229.18 191.18 229.27C189.73 229.32 188.08 229.39 185.97 229.27C182.89 229.11 181.2 227.19 179.71 225.49C178.15 223.71 176.65 222.12 173.36 221.82L173.36 217.82C178.5 218.2 180.92 220.79 182.72 222.85C184.08 224.39 184.85 225.2 186.18 225.27C188.12 225.37 189.6 225.32 191.03 225.27C192.77 225.2 194.36 225.15 196.32 225.38C202 225.29 203.92 224.04 205.96 222.72C207.58 221.66 209.41 220.48 212.78 219.99C212.82 219.85 212.87 219.71 212.92 219.58C212.97 219.44 213.02 219.31 213.08 219.17C213.14 219.04 213.2 218.91 213.27 218.78C213.34 218.66 213.41 218.53 213.49 218.41C213.54 218.34 213.59 218.27 213.64 218.19C213.69 218.12 213.74 218.05 213.79 217.98C213.84 217.92 213.9 217.85 213.95 217.78C214.01 217.71 214.07 217.65 214.12 217.58C214.26 217.44 214.41 217.32 214.55 217.19C213.44 213.09 213.24 207.29 214.59 202.77C214.56 202.75 214.54 202.73 214.51 202.71C213.99 202.23 213.56 201.66 213.23 201.03C212.9 200.4 212.69 199.72 212.59 199.01C212.5 198.31 212.53 197.59 212.68 196.9C212.84 196.21 213.11 195.55 213.49 194.95C213.76 194.51 214.09 194.11 214.46 193.76C214.84 193.4 215.26 193.1 215.71 192.85C216.16 192.6 216.64 192.41 217.14 192.28C217.64 192.16 218.15 192.09 218.66 192.1C218.95 192.1 219.24 192.12 219.52 192.16C219.81 192.2 220.09 192.26 220.36 192.34C220.64 192.42 220.91 192.52 221.17 192.63C221.43 192.75 221.69 192.89 221.93 193.04C222.27 193.25 222.59 193.5 222.88 193.78C223.17 194.06 223.44 194.36 223.67 194.69C223.9 195.02 224.1 195.37 224.26 195.74C224.42 196.11 224.55 196.49 224.64 196.88C224.73 197.27 224.78 197.67 224.79 198.07C224.8 198.47 224.77 198.87 224.7 199.27C224.63 199.66 224.52 200.05 224.38 200.42C224.23 200.79 224.05 201.15 223.83 201.49C223.56 201.92 223.23 202.32 222.86 202.68C222.48 203.03 222.07 203.33 221.62 203.58C221.16 203.83 220.68 204.02 220.19 204.15C219.69 204.27 219.17 204.34 218.66 204.34C218.55 204.34 218.44 204.32 218.32 204.31C217.37 207.81 217.58 212.42 218.29 215.58C218.41 215.57 218.54 215.56 218.66 215.56C218.95 215.56 219.24 215.58 219.52 215.62C219.81 215.66 220.09 215.72 220.36 215.8C220.64 215.88 220.91 215.98 221.17 216.1C221.43 216.21 221.69 216.35 221.93 216.5C222.27 216.72 222.59 216.96 222.88 217.24C223.17 217.52 223.44 217.82 223.67 218.15C223.9 218.48 224.1 218.83 224.26 219.2C224.42 219.57 224.55 219.95 224.64 220.35C224.73 220.74 224.78 221.14 224.79 221.54C224.8 221.94 224.77 222.34 224.7 222.73C224.63 223.12 224.52 223.51 224.38 223.88C224.23 224.26 224.05 224.61 223.83 224.95C223.56 225.39 223.23 225.79 222.86 226.14C222.48 226.49 222.07 226.8 221.62 227.05C221.16 227.29 220.68 227.48 220.19 227.61C219.69 227.74 219.17 227.8 218.66 227.8C218.34 227.8 218.03 227.78 217.72 227.73C217.4 227.68 217.1 227.6 216.8 227.51C216.49 227.41 216.2 227.29 215.92 227.15C215.64 227.01 215.37 226.84 215.11 226.66C214.88 226.5 214.65 226.31 214.45 226.12C214.24 225.92 214.04 225.71 213.87 225.49C213.69 225.26 213.52 225.03 213.38 224.78C213.24 224.53 213.11 224.28 213 224.01ZM216.88 197.09C216.74 197.31 216.64 197.56 216.59 197.83C216.54 198.09 216.54 198.36 216.59 198.62C216.64 198.88 216.74 199.14 216.89 199.36C217.03 199.59 217.22 199.78 217.44 199.94L217.53 200C218.51 200.62 219.85 200.3 220.44 199.34C220.52 199.23 220.58 199.11 220.63 198.98C220.68 198.85 220.72 198.72 220.74 198.58C220.77 198.44 220.78 198.31 220.78 198.17C220.77 198.03 220.76 197.89 220.73 197.76C220.6 197.2 220.27 196.73 219.79 196.43C219.45 196.22 219.06 196.1 218.66 196.1C218.49 196.1 218.31 196.13 218.14 196.17C217.97 196.21 217.8 196.28 217.64 196.36C217.49 196.45 217.35 196.55 217.22 196.68C217.09 196.8 216.97 196.93 216.88 197.09ZM216.88 220.55C216.74 220.77 216.64 221.03 216.59 221.29C216.54 221.55 216.54 221.82 216.59 222.09C216.64 222.35 216.74 222.6 216.89 222.82C217.03 223.05 217.22 223.24 217.44 223.4L217.53 223.46C218.51 224.08 219.85 223.76 220.44 222.81C220.52 222.69 220.58 222.57 220.63 222.44C220.68 222.31 220.72 222.18 220.74 222.04C220.77 221.91 220.78 221.77 220.78 221.63C220.77 221.49 220.76 221.36 220.72 221.22C220.6 220.67 220.27 220.2 219.79 219.89C219.45 219.68 219.06 219.57 218.66 219.57C218.49 219.57 218.31 219.59 218.14 219.63C217.97 219.68 217.8 219.74 217.64 219.83C217.49 219.91 217.35 220.02 217.22 220.14C217.09 220.26 216.97 220.4 216.88 220.55ZM216.88 246.85C216.74 247.08 216.64 247.33 216.59 247.59C216.54 247.86 216.54 248.13 216.59 248.39C216.64 248.65 216.74 248.9 216.89 249.13C217.03 249.35 217.22 249.55 217.44 249.7L217.53 249.76C218.51 250.38 219.85 250.06 220.44 249.11C220.52 249 220.58 248.87 220.63 248.74C220.68 248.62 220.72 248.48 220.74 248.35C220.77 248.21 220.78 248.07 220.78 247.94C220.77 247.8 220.76 247.66 220.72 247.53C220.6 246.97 220.27 246.5 219.79 246.2C219.45 245.98 219.06 245.87 218.66 245.87C218.49 245.87 218.31 245.89 218.14 245.94C217.97 245.98 217.8 246.05 217.64 246.13C217.49 246.22 217.35 246.32 217.22 246.44C217.09 246.56 216.97 246.7 216.88 246.85Z" /> + <g id="Layer" style="opacity: 0.251"> + <path id="Layer" class="shp2" d="M151.78 249.54C151.54 249.54 151.3 249.47 151.09 249.32C150.96 249.23 150.85 249.11 150.77 248.98C150.68 248.84 150.63 248.69 150.6 248.54C150.57 248.38 150.58 248.22 150.61 248.06C150.65 247.91 150.71 247.76 150.8 247.63C151.07 247.25 151.32 246.84 151.53 246.41C151.88 245.71 152.14 244.96 152.3 244.2C152.46 243.43 152.52 242.64 152.47 241.86C152.43 241.08 152.28 240.3 152.04 239.56C151.8 238.81 151.46 238.1 151.03 237.45C150.86 237.18 150.8 236.85 150.87 236.54C150.93 236.22 151.12 235.95 151.39 235.78C151.66 235.6 151.99 235.54 152.3 235.61C152.62 235.68 152.89 235.87 153.06 236.14C155.24 239.53 155.48 243.88 153.7 247.48C153.63 247.61 153.56 247.75 153.49 247.88C153.42 248.01 153.34 248.14 153.27 248.27C153.19 248.4 153.11 248.53 153.03 248.65C152.95 248.78 152.86 248.9 152.78 249.03C152.72 249.11 152.66 249.18 152.58 249.24C152.51 249.3 152.43 249.36 152.34 249.4C152.26 249.45 152.16 249.48 152.07 249.5C151.98 249.53 151.88 249.54 151.78 249.54M157.34 252.41C157.12 252.41 156.9 252.35 156.71 252.23C156.52 252.12 156.37 251.95 156.27 251.75C156.17 251.55 156.12 251.33 156.14 251.11C156.16 250.88 156.24 250.67 156.37 250.49C156.82 249.88 157.22 249.21 157.57 248.52C158.11 247.43 158.51 246.26 158.73 245.06C158.96 243.86 159.02 242.64 158.91 241.42C158.8 240.2 158.52 239.01 158.08 237.87C157.64 236.73 157.05 235.65 156.32 234.67C156.13 234.42 156.05 234.09 156.09 233.78C156.14 233.46 156.31 233.18 156.57 232.98C156.82 232.79 157.15 232.71 157.46 232.76C157.78 232.8 158.06 232.98 158.25 233.23C159.12 234.39 159.82 235.66 160.34 237C160.86 238.35 161.18 239.76 161.31 241.2C161.44 242.64 161.37 244.09 161.11 245.5C160.84 246.92 160.38 248.3 159.73 249.59C159.33 250.41 158.85 251.19 158.32 251.92C158.26 252 158.2 252.07 158.13 252.13C158.05 252.19 157.97 252.24 157.89 252.28C157.8 252.33 157.71 252.36 157.62 252.38C157.53 252.4 157.43 252.41 157.34 252.41ZM146.34 246.49C146.13 246.48 145.93 246.43 145.75 246.32C145.57 246.22 145.41 246.06 145.31 245.88C145.2 245.7 145.15 245.49 145.15 245.28C145.14 245.07 145.2 244.87 145.3 244.68C145.49 244.35 145.63 244 145.74 243.64C145.84 243.27 145.9 242.9 145.92 242.52C145.93 242.14 145.9 241.76 145.83 241.39C145.76 241.02 145.64 240.66 145.48 240.31C145.36 240.03 145.36 239.7 145.47 239.41C145.58 239.12 145.81 238.88 146.09 238.75C146.37 238.62 146.7 238.61 146.99 238.71C147.29 238.81 147.53 239.03 147.67 239.31C147.91 239.82 148.09 240.37 148.2 240.92C148.31 241.48 148.35 242.05 148.33 242.62C148.31 243.19 148.21 243.76 148.06 244.3C147.9 244.85 147.68 245.38 147.4 245.88C147.35 245.97 147.28 246.05 147.21 246.13C147.13 246.2 147.05 246.27 146.96 246.32C146.86 246.38 146.76 246.42 146.66 246.45C146.56 246.47 146.45 246.49 146.34 246.49" /> + </g> + <g id="Layer"> + <path id="Layer" class="shp0" d="M151.78 246.54C151.54 246.54 151.3 246.47 151.09 246.32C150.96 246.23 150.85 246.11 150.77 245.98C150.68 245.84 150.63 245.69 150.6 245.54C150.57 245.38 150.58 245.22 150.61 245.06C150.65 244.91 150.71 244.76 150.8 244.63C151.07 244.25 151.32 243.84 151.53 243.41C151.88 242.71 152.14 241.96 152.3 241.2C152.46 240.43 152.52 239.64 152.47 238.86C152.43 238.08 152.28 237.3 152.04 236.56C151.8 235.81 151.46 235.1 151.03 234.45C150.86 234.18 150.8 233.85 150.87 233.54C150.93 233.22 151.12 232.95 151.39 232.78C151.66 232.6 151.99 232.54 152.3 232.61C152.62 232.68 152.89 232.87 153.06 233.14C155.24 236.53 155.48 240.88 153.7 244.48C153.63 244.61 153.56 244.75 153.49 244.88C153.42 245.01 153.34 245.14 153.27 245.27C153.19 245.4 153.11 245.53 153.03 245.65C152.95 245.78 152.86 245.9 152.78 246.03C152.72 246.11 152.66 246.18 152.58 246.24C152.51 246.3 152.43 246.36 152.34 246.4C152.26 246.45 152.16 246.48 152.07 246.5C151.98 246.53 151.88 246.54 151.78 246.54M157.34 249.41C157.12 249.41 156.9 249.35 156.71 249.23C156.52 249.12 156.37 248.95 156.27 248.75C156.17 248.55 156.12 248.33 156.14 248.11C156.16 247.88 156.24 247.67 156.37 247.49C156.82 246.88 157.22 246.21 157.57 245.52C158.11 244.43 158.51 243.26 158.73 242.06C158.96 240.86 159.02 239.64 158.91 238.42C158.8 237.2 158.52 236.01 158.08 234.87C157.64 233.73 157.05 232.65 156.32 231.67C156.13 231.42 156.05 231.09 156.09 230.78C156.14 230.46 156.31 230.18 156.57 229.98C156.82 229.79 157.15 229.71 157.46 229.76C157.78 229.8 158.06 229.98 158.25 230.23C159.12 231.39 159.82 232.66 160.34 234C160.86 235.35 161.18 236.76 161.31 238.2C161.44 239.64 161.37 241.09 161.11 242.5C160.84 243.92 160.38 245.3 159.73 246.59C159.33 247.41 158.85 248.19 158.32 248.92C158.26 249 158.2 249.07 158.13 249.13C158.05 249.19 157.97 249.24 157.89 249.28C157.8 249.33 157.71 249.36 157.62 249.38C157.53 249.4 157.43 249.41 157.34 249.41ZM146.34 243.49C146.13 243.48 145.93 243.43 145.75 243.32C145.57 243.22 145.41 243.06 145.31 242.88C145.2 242.7 145.15 242.49 145.15 242.28C145.14 242.07 145.2 241.87 145.3 241.68C145.49 241.35 145.63 241 145.74 240.64C145.84 240.27 145.9 239.9 145.92 239.52C145.93 239.14 145.9 238.76 145.83 238.39C145.76 238.02 145.64 237.66 145.48 237.31C145.36 237.03 145.36 236.7 145.47 236.41C145.58 236.12 145.81 235.88 146.09 235.75C146.37 235.62 146.7 235.61 146.99 235.71C147.29 235.81 147.53 236.03 147.67 236.31C147.91 236.82 148.09 237.37 148.2 237.92C148.31 238.48 148.35 239.05 148.33 239.62C148.31 240.19 148.21 240.76 148.06 241.3C147.9 241.85 147.68 242.38 147.4 242.88C147.35 242.97 147.28 243.05 147.21 243.13C147.13 243.2 147.05 243.27 146.96 243.32C146.86 243.38 146.76 243.42 146.66 243.45C146.56 243.47 146.45 243.49 146.34 243.49" /> + </g> + <g id="Layer"> + <path id="Layer" fill-rule="evenodd" class="shp5" d="M164.08 192.24C165.2 192.24 167.16 192.28 168.36 192.39L168.36 195.88C167.32 195.78 165.12 195.74 164.08 195.74C154.95 195.74 145.38 199.12 139.72 204.35C135.15 208.57 132.19 214.61 131.03 221.84C130.8 223.29 130.66 225.08 130.6 226.94C130.89 225.07 132.31 223.65 134.03 223.65L135.93 223.65C137.86 223.65 139.42 225.44 139.42 227.65L139.42 257.32C139.42 259.53 137.86 261.32 135.93 261.32L134.03 261.32C132.1 261.32 130.53 259.53 130.53 257.32L130.53 255.72C130.24 255.79 129.94 255.83 129.62 255.83L127.59 255.83C127.06 255.83 126.54 255.73 126.06 255.53C125.57 255.33 125.13 255.03 124.76 254.66C124.39 254.29 124.09 253.85 123.89 253.36C123.69 252.88 123.59 252.36 123.59 251.83L123.59 233.71C123.59 233.24 123.67 232.77 123.84 232.32C124.01 231.88 124.25 231.47 124.56 231.11C124.87 230.75 125.24 230.45 125.66 230.22C126.08 229.99 126.53 229.84 127 229.77C126.82 218.03 130.46 208.14 137.35 201.78C143.72 195.9 153.96 192.24 164.08 192.24ZM129.62 251.43L130.54 251.43L130.54 234.12L129.62 234.12C129.27 234.12 128.93 234.18 128.61 234.32C128.29 234.45 127.99 234.65 127.75 234.89C127.5 235.14 127.31 235.43 127.17 235.75C127.04 236.07 126.97 236.42 126.97 236.77L126.97 248.78C126.97 249.13 127.04 249.47 127.17 249.79C127.31 250.11 127.5 250.41 127.75 250.65C128 250.9 128.29 251.09 128.61 251.23C128.93 251.36 129.27 251.43 129.62 251.43Z" /> + <path id="Layer" fill-rule="evenodd" class="shp4" d="M164.08 189.24C165.2 189.24 167.16 189.28 168.36 189.39L168.36 192.88C167.32 192.78 165.12 192.74 164.08 192.74C154.95 192.74 145.38 196.12 139.72 201.35C135.15 205.57 132.19 211.61 131.03 218.84C130.8 220.29 130.66 222.08 130.6 223.94C130.89 222.07 132.31 220.65 134.03 220.65L135.93 220.65C137.86 220.65 139.42 222.44 139.42 224.65L139.42 254.32C139.42 256.53 137.86 258.32 135.93 258.32L134.03 258.32C132.1 258.32 130.53 256.53 130.53 254.32L130.53 252.72C130.24 252.79 129.94 252.83 129.62 252.83L127.59 252.83C127.06 252.83 126.54 252.73 126.06 252.53C125.57 252.33 125.13 252.03 124.76 251.66C124.39 251.29 124.09 250.85 123.89 250.36C123.69 249.88 123.59 249.36 123.59 248.83L123.59 230.71C123.59 230.24 123.67 229.77 123.84 229.32C124.01 228.88 124.25 228.47 124.56 228.11C124.87 227.75 125.24 227.45 125.66 227.22C126.08 226.99 126.53 226.84 127 226.77C126.82 215.03 130.46 205.14 137.35 198.78C143.72 192.9 153.96 189.24 164.08 189.24ZM129.62 248.43L130.54 248.43L130.54 231.12L129.62 231.12C129.27 231.12 128.93 231.18 128.61 231.32C128.29 231.45 127.99 231.65 127.75 231.89C127.5 232.14 127.31 232.43 127.17 232.75C127.04 233.07 126.97 233.42 126.97 233.77L126.97 245.78C126.97 246.13 127.04 246.47 127.17 246.79C127.31 247.11 127.5 247.41 127.75 247.65C128 247.9 128.29 248.09 128.61 248.23C128.93 248.36 129.27 248.43 129.62 248.43Z" /> + </g> + <g id="Layer"> + <path id="Layer" fill-rule="evenodd" class="shp1" d="M295.75 242.67L295.75 252.82L259.58 252.82L259.58 198.08L272.78 198.08L272.78 242.67L295.75 242.67ZM300.63 201.65C300.63 199.85 301.29 198.37 302.59 197.22C303.89 196.06 305.59 195.49 307.67 195.49C309.75 195.49 311.44 196.06 312.74 197.22C314.04 198.37 314.7 199.85 314.7 201.65C314.7 203.46 314.05 204.94 312.74 206.09C311.44 207.24 309.75 207.82 307.67 207.82C305.59 207.82 303.89 207.24 302.59 206.09C301.29 204.94 300.63 203.46 300.63 201.65ZM314.1 252.82L301.39 252.82L301.39 212.14L314.1 212.14L314.1 252.82ZM341.5 238.8C340.38 238.15 338.23 237.47 335.07 236.77C331.92 236.07 329.31 235.15 327.25 234.01C325.2 232.87 323.63 231.48 322.55 229.85C321.48 228.22 320.94 226.36 320.94 224.25C320.94 220.51 322.48 217.44 325.56 215.02C328.64 212.6 332.68 211.39 337.67 211.39C343.03 211.39 347.34 212.61 350.6 215.04C353.86 217.47 355.49 220.66 355.49 224.63L342.78 224.63C342.78 221.37 341.06 219.74 337.63 219.74C336.3 219.74 335.19 220.11 334.28 220.85C333.38 221.58 332.93 222.51 332.93 223.61C332.93 224.74 333.48 225.65 334.58 226.35C335.69 227.06 337.45 227.63 339.87 228.08C342.29 228.53 344.41 229.07 346.24 229.7C352.36 231.8 355.41 235.58 355.41 241.02C355.41 244.73 353.77 247.75 350.47 250.08C347.17 252.41 342.91 253.57 337.67 253.57C334.18 253.57 331.08 252.95 328.34 251.69C325.61 250.44 323.48 248.74 321.95 246.58C320.42 244.43 319.66 242.16 319.66 239.77L331.5 239.77C331.55 241.66 332.18 243.03 333.38 243.89C334.58 244.76 336.13 245.19 338.01 245.19C339.74 245.19 341.03 244.84 341.9 244.14C342.76 243.44 343.19 242.52 343.19 241.39C343.19 240.32 342.63 239.45 341.5 238.8ZM376.09 212.14L382.79 212.14L382.79 220.94L376.09 220.94L376.09 239.55C376.09 241.08 376.37 242.15 376.92 242.75C377.47 243.35 378.56 243.65 380.19 243.65C381.44 243.65 382.5 243.57 383.35 243.42L383.35 252.49C382.77 252.67 382.19 252.83 381.6 252.96C381.01 253.1 380.42 253.22 379.82 253.31C379.22 253.4 378.62 253.47 378.02 253.51C377.41 253.56 376.81 253.58 376.21 253.57C371.84 253.57 368.62 252.55 366.54 250.49C364.46 248.44 363.42 245.32 363.42 241.13L363.42 220.94L358.23 220.94L358.23 212.14L363.42 212.14L363.42 202.03L376.09 202.03L376.09 212.14ZM392.3 247.99C388.44 244.27 386.51 239.43 386.51 233.46L386.51 232.41C386.51 228.25 387.28 224.58 388.82 221.39C390.36 218.21 392.61 215.75 395.57 214C398.53 212.26 402.04 211.39 406.1 211.39C411.81 211.39 416.32 213.16 419.63 216.71C422.94 220.26 424.59 225.2 424.59 231.54L424.59 236.47L399.41 236.47C399.86 238.75 400.85 240.54 402.38 241.84C403.9 243.15 405.88 243.8 408.32 243.8C412.33 243.8 415.46 242.4 417.72 239.59L423.5 246.43C421.93 248.61 419.69 250.35 416.79 251.64C413.9 252.93 410.78 253.57 407.45 253.57C401.21 253.57 396.16 251.71 392.3 247.99ZM399.4 228.53L412.19 228.53L412.19 227.56C412.24 225.53 411.72 223.95 410.65 222.84C409.57 221.72 408.03 221.17 406.02 221.17C402.31 221.17 400.11 223.62 399.4 228.53ZM441.74 216.92C444.55 213.23 448.42 211.39 453.36 211.39C457.59 211.39 460.76 212.66 462.85 215.19C464.94 217.72 466.03 221.53 466.1 226.62L466.1 252.82L453.39 252.82L453.39 227.14C453.39 225.09 452.98 223.58 452.15 222.61C451.33 221.65 449.82 221.17 447.64 221.17C445.16 221.17 443.32 222.14 442.11 224.1L442.11 252.82L429.44 252.82L429.44 212.14L441.32 212.14L441.74 216.92Z" /> + </g> + <g id="Layer"> + <path id="Layer" fill-rule="evenodd" class="shp0" d="M473.64 198.08L493.3 198.08C500.34 198.08 505.7 199.37 509.37 201.95C513.05 204.54 514.88 208.28 514.88 213.2C514.88 216.03 514.23 218.45 512.93 220.45C511.62 222.46 509.71 223.94 507.18 224.89C510.03 225.64 512.23 227.05 513.75 229.1C515.28 231.15 516.05 233.66 516.05 236.62C516.05 241.98 514.35 246.01 510.95 248.71C507.56 251.4 502.52 252.77 495.86 252.82L473.64 252.82L473.64 198.08ZM486.83 220.9L493.72 220.9C496.6 220.88 498.65 220.35 499.88 219.32C501.11 218.29 501.72 216.77 501.72 214.76C501.72 212.44 501.06 210.78 499.73 209.76C498.4 208.74 496.26 208.23 493.3 208.23L486.83 208.23L486.83 220.9ZM486.83 229.55L486.83 242.67L495.48 242.67C497.86 242.67 499.69 242.13 500.97 241.04C502.25 239.95 502.89 238.41 502.89 236.43C502.89 231.87 500.62 229.58 496.08 229.55L486.83 229.55ZM547.33 223.61L543.15 223.31C539.17 223.31 536.61 224.56 535.48 227.07L535.48 252.82L522.81 252.82L522.81 212.14L534.69 212.14L535.11 217.37C537.24 213.38 540.21 211.39 544.02 211.39C545.37 211.39 546.55 211.54 547.55 211.84L547.33 223.61ZM573.73 252.82C573.28 252 572.88 250.78 572.53 249.18C570.2 252.11 566.94 253.57 562.76 253.57C558.92 253.57 555.66 252.42 552.98 250.1C550.3 247.78 548.96 244.87 548.96 241.36C548.96 236.94 550.59 233.61 553.85 231.35C557.11 229.1 561.84 227.97 568.06 227.97L571.97 227.97L571.97 225.82C571.97 222.06 570.35 220.19 567.12 220.19C564.11 220.19 562.61 221.67 562.61 224.65L549.94 224.65C549.94 220.72 551.61 217.52 554.96 215.07C558.3 212.62 562.57 211.39 567.76 211.39C572.95 211.39 577.04 212.66 580.05 215.19C583.06 217.72 584.6 221.19 584.68 225.6L584.68 243.61C584.73 247.34 585.3 250.2 586.41 252.18L586.41 252.82L573.73 252.82L573.73 252.82ZM569.73 243.54C570.77 242.86 571.52 242.1 571.97 241.24L571.97 234.74L568.28 234.74C563.87 234.74 561.67 236.72 561.67 240.68C561.67 241.83 562.05 242.76 562.83 243.48C563.61 244.19 564.6 244.55 565.8 244.55C567.38 244.55 568.69 244.21 569.73 243.54ZM591.59 201.65C591.59 199.85 592.25 198.37 593.55 197.22C594.85 196.06 596.54 195.49 598.63 195.49C600.7 195.49 602.4 196.06 603.7 197.22C605 198.37 605.65 199.85 605.65 201.65C605.65 203.46 605 204.94 603.7 206.09C602.4 207.24 600.7 207.82 598.63 207.82C596.54 207.82 594.85 207.24 593.55 206.09C592.25 204.94 591.59 203.46 591.59 201.65ZM605.05 252.82L592.35 252.82L592.35 212.14L605.05 212.14L605.05 252.82ZM624.49 212.14L624.91 216.92C627.71 213.23 631.58 211.39 636.52 211.39C640.76 211.39 643.92 212.66 646.02 215.19C648.11 217.72 649.19 221.53 649.27 226.62L649.27 252.82L636.56 252.82L636.56 227.14C636.56 225.09 636.15 223.58 635.32 222.61C634.49 221.65 632.99 221.17 630.81 221.17C628.33 221.17 626.48 222.14 625.28 224.1L625.28 252.82L612.61 252.82L612.61 212.14L624.49 212.14ZM689.76 243.05L689.76 252.82L655.21 252.82L655.21 245.75L673.26 221.92L656 221.92L656 212.14L689.35 212.14L689.35 218.99L671.23 243.05L689.76 243.05Z" /> + </g> +</svg>
\ No newline at end of file diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/NOTICE.md b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/NOTICE.md new file mode 100644 index 0000000000..774e383f8b --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/NOTICE.md @@ -0,0 +1,23 @@ +# ListenBrainz logo attribution + +The file `ListenBrainz_logo.svg` shipped alongside this plugin is a derivative +work used here under the terms of the Creative Commons Attribution-ShareAlike +4.0 International license (CC BY-SA 4.0). + +## Attribution chain + +1. Original work: [ListenBrainz logo](https://github.com/metabrainz/metabrainz-logos/commit/10127d3e84e5bb7e1c8509f1da12223d19581e18) + by [MonkeyDo](https://github.com/metabrainz/metabrainz-logos/commits?author=MonkeyDo) + at the [MetaBrainz Foundation](https://github.com/metabrainz), licensed under + CC BY-SA 4.0. +2. "ListenBrainz logo for Jellyfin plugin" โ derivative by + [lyarenei](https://github.com/lyarenei), distributed in + [jellyfin-plugin-listenbrainz](https://github.com/lyarenei/jellyfin-plugin-listenbrainz/tree/main/res/listenbrainz) + under CC BY-SA 4.0. +3. This redistribution within Jellyfin retains the work unmodified and remains + licensed under CC BY-SA 4.0 per the license's ShareAlike requirement. + +## License + +A full copy of the CC BY-SA 4.0 license is available at +<https://creativecommons.org/licenses/by-sa/4.0/legalcode>. diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/PluginConfiguration.cs new file mode 100644 index 0000000000..6f60d18c33 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/PluginConfiguration.cs @@ -0,0 +1,65 @@ +using MediaBrowser.Model.Plugins; + +namespace MediaBrowser.Providers.Plugins.ListenBrainz.Configuration; + +/// <summary> +/// ListenBrainz plugin configuration. +/// </summary> +public class PluginConfiguration : BasePluginConfiguration +{ + /// <summary> + /// The default Labs API server URL. + /// </summary> + public const string DefaultLabsServer = "https://labs.api.listenbrainz.org"; + + /// <summary> + /// The default rate limit in seconds. + /// </summary> + public const double DefaultRateLimit = 1.0; + + private string _labsServer = DefaultLabsServer; + private double _rateLimit = DefaultRateLimit; + + /// <summary> + /// Gets or sets the Labs API server URL. + /// </summary> + public string LabsServer + { + get => _labsServer; + set => _labsServer = string.IsNullOrWhiteSpace(value) ? DefaultLabsServer : value.TrimEnd('/'); + } + + /// <summary> + /// Gets or sets the similarity algorithm. + /// </summary> + public SimilarityAlgorithm Algorithm { get; set; } = SimilarityAlgorithm.SessionBased1825Days; + + /// <summary> + /// Gets or sets the rate limit in seconds. + /// </summary> + public double RateLimit + { + get => _rateLimit; + set + { + if (value < DefaultRateLimit && _labsServer == DefaultLabsServer) + { + _rateLimit = DefaultRateLimit; + } + else + { + _rateLimit = value; + } + } + } + + /// <summary> + /// Gets or sets the cache duration in days for similar item results. A value of 0 disables caching. + /// </summary> + public int SimilarItemsCacheDays { get; set; } = 14; + + /// <summary> + /// Gets the algorithm string for the API call. + /// </summary> + public string AlgorithmString => Algorithm.ToApiString(); +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/SimilarityAlgorithm.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/SimilarityAlgorithm.cs new file mode 100644 index 0000000000..f297d99f6d --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/SimilarityAlgorithm.cs @@ -0,0 +1,37 @@ +namespace MediaBrowser.Providers.Plugins.ListenBrainz.Configuration; + +/// <summary> +/// Available similarity algorithms for ListenBrainz Labs API. +/// </summary> +public enum SimilarityAlgorithm +{ + /// <summary> + /// Session-based algorithm analyzing ~5 years of listening data. + /// </summary> + SessionBased1825Days = 0, + + /// <summary> + /// Session-based algorithm analyzing ~5 years of listening data (alternate). + /// </summary> + SessionBased1800Days = 1, + + /// <summary> + /// Session-based algorithm analyzing ~20 years of listening data. + /// </summary> + SessionBased7500Days = 2, + + /// <summary> + /// Session-based algorithm analyzing ~20 years with higher contribution threshold. + /// </summary> + SessionBased7500DaysHighContribution = 3, + + /// <summary> + /// Session-based algorithm analyzing ~25 years of listening data. + /// </summary> + SessionBased9000Days = 4, + + /// <summary> + /// Session-based algorithm analyzing ~75 days of recent listening data. + /// </summary> + SessionBased75Days = 5 +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/SimilarityAlgorithmExtensions.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/SimilarityAlgorithmExtensions.cs new file mode 100644 index 0000000000..f7874dbae8 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/SimilarityAlgorithmExtensions.cs @@ -0,0 +1,23 @@ +namespace MediaBrowser.Providers.Plugins.ListenBrainz.Configuration; + +/// <summary> +/// Extension methods for <see cref="SimilarityAlgorithm"/>. +/// </summary> +public static class SimilarityAlgorithmExtensions +{ + /// <summary> + /// Gets the API string value for the algorithm. + /// </summary> + /// <param name="algorithm">The algorithm.</param> + /// <returns>The API string value.</returns> + public static string ToApiString(this SimilarityAlgorithm algorithm) => algorithm switch + { + SimilarityAlgorithm.SessionBased1825Days => "session_based_days_1825_session_300_contribution_3_threshold_10_limit_100_filter_True_skip_30", + SimilarityAlgorithm.SessionBased1800Days => "session_based_days_1800_session_300_contribution_3_threshold_10_limit_100_filter_True_skip_30", + SimilarityAlgorithm.SessionBased7500Days => "session_based_days_7500_session_300_contribution_3_threshold_10_limit_100_filter_True_skip_30", + SimilarityAlgorithm.SessionBased7500DaysHighContribution => "session_based_days_7500_session_300_contribution_5_threshold_10_limit_100_filter_True_skip_30", + SimilarityAlgorithm.SessionBased9000Days => "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30", + SimilarityAlgorithm.SessionBased75Days => "session_based_days_75_session_300_contribution_5_threshold_10_limit_100_filter_True_skip_30", + _ => "session_based_days_1825_session_300_contribution_3_threshold_10_limit_100_filter_True_skip_30" + }; +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/config.html new file mode 100644 index 0000000000..dec21d1b42 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/Configuration/config.html @@ -0,0 +1,109 @@ +<!DOCTYPE html> +<html> +<head> + <title>ListenBrainz</title> +</head> +<body> + <div id="configPage" data-role="page" class="page type-interior pluginConfigurationPage configPage" data-require="emby-input,emby-button,emby-select"> + <div data-role="content"> + <div class="content-primary"> + <img id="listenBrainzLogo" alt="ListenBrainz" style="max-width:240px;display:block;margin:0 auto 1em;" /> + <h1>ListenBrainz</h1> + <p>Get similar artist recommendations from ListenBrainz Labs.</p> + <form class="configForm"> + <div class="inputContainer"> + <input is="emby-input" type="text" id="labsServer" required label="Labs API Server" /> + <div class="fieldDescription">The ListenBrainz Labs API server URL. Default: https://labs.api.listenbrainz.org</div> + </div> + <div class="selectContainer"> + <label class="selectLabel" for="algorithm">Similarity Algorithm</label> + <select is="emby-select" id="algorithm" class="emby-select-withcolor"> + <option value="0" selected>~5 years / 1825 days (Recommended)</option> + <option value="1">~5 years / 1800 days</option> + <option value="2">~20 years / 7500 days</option> + <option value="3">~20 years / 7500 days (high contribution)</option> + <option value="4">~25 years / 9000 days</option> + <option value="5">~75 days (recent)</option> + </select> + <div class="fieldDescription">The algorithm used for artist similarity calculation.</div> + </div> + <div class="inputContainer"> + <input is="emby-input" type="number" id="rateLimit" required pattern="[0-9]*" min="0" max="10" step=".01" label="Rate Limit (seconds)" /> + <div class="fieldDescription">Span of time between requests in seconds. The official server is rate limited to one request per second.</div> + </div> + <div class="inputContainer"> + <input is="emby-input" type="number" id="similarItemsCacheDays" required pattern="[0-9]*" min="0" max="365" label="Cache duration (days)" /> + <div class="fieldDescription">Number of days to cache similar artist results from ListenBrainz. Set to 0 to disable caching.</div> + </div> + <br /> + <div> + <button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button> + </div> + </form> + <div class="verticalSection" style="margin-top:2em;font-size:0.85em;opacity:0.8;"> + <p>The ListenBrainz logo is © the MetaBrainz Foundation (by MonkeyDo), + adapted for Jellyfin plugin use by + <a href="https://github.com/lyarenei" target="_blank" rel="noopener">lyarenei</a>, + and redistributed here under + <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener">CC BY-SA 4.0</a>. + Full attribution notice is shipped alongside the plugin in <code>NOTICE.md</code>.</p> + </div> + </div> + </div> + <script type="text/javascript"> + var ListenBrainzPluginConfig = { + uniquePluginId: "a5b2e8c1-9d4f-4a3b-8c7e-6f1a2b3c4d5e" + }; + + document.querySelector('.configPage') + .addEventListener('pageshow', function () { + Dashboard.showLoadingMsg(); + document.querySelector('#listenBrainzLogo').src = ApiClient.getUrl('web/ConfigurationPage', { name: 'ListenBrainzLogo' }); + ApiClient.getPluginConfiguration(ListenBrainzPluginConfig.uniquePluginId).then(function (config) { + var labsServer = document.querySelector('#labsServer'); + labsServer.value = config.LabsServer; + labsServer.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + + document.querySelector('#algorithm').value = config.Algorithm; + + var rateLimit = document.querySelector('#rateLimit'); + rateLimit.value = config.RateLimit; + rateLimit.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + + var similarItemsCacheDays = document.querySelector('#similarItemsCacheDays'); + similarItemsCacheDays.value = config.SimilarItemsCacheDays; + similarItemsCacheDays.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + + Dashboard.hideLoadingMsg(); + }); + }); + + document.querySelector('.configForm') + .addEventListener('submit', function (e) { + Dashboard.showLoadingMsg(); + + ApiClient.getPluginConfiguration(ListenBrainzPluginConfig.uniquePluginId).then(function (config) { + config.LabsServer = document.querySelector('#labsServer').value; + config.Algorithm = parseInt(document.querySelector('#algorithm').value, 10); + config.RateLimit = document.querySelector('#rateLimit').value; + config.SimilarItemsCacheDays = parseInt(document.querySelector('#similarItemsCacheDays').value, 10); + + ApiClient.updatePluginConfiguration(ListenBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); + }); + + e.preventDefault(); + return false; + }); + </script> + </div> +</body> +</html> diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/ListenBrainzPlugin.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/ListenBrainzPlugin.cs new file mode 100644 index 0000000000..1681f0334d --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/ListenBrainzPlugin.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Plugins.ListenBrainz.Configuration; + +namespace MediaBrowser.Providers.Plugins.ListenBrainz; + +/// <summary> +/// ListenBrainz plugin instance. +/// </summary> +public class ListenBrainzPlugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage +{ + /// <summary> + /// Initializes a new instance of the <see cref="ListenBrainzPlugin"/> class. + /// </summary> + /// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> + /// <param name="xmlSerializer">Instance of the <see cref="IXmlSerializer"/> interface.</param> + public ListenBrainzPlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + + /// <summary> + /// Gets the current plugin instance. + /// </summary> + public static ListenBrainzPlugin? Instance { get; private set; } + + /// <inheritdoc /> + public override Guid Id => new("a5b2e8c1-9d4f-4a3b-8c7e-6f1a2b3c4d5e"); + + /// <inheritdoc /> + public override string Name => "ListenBrainz Similarity Provider"; + + /// <inheritdoc /> + public override string Description => "Get similar artist recommendations from ListenBrainz Labs."; + + /// <inheritdoc /> + public override string ConfigurationFileName => "Jellyfin.Plugin.ListenBrainz.xml"; + + /// <inheritdoc /> + public string ImageResourceName => GetType().Namespace + ".Configuration.ListenBrainz_logo.svg"; + + /// <inheritdoc /> + public IEnumerable<PluginPageInfo> GetPages() + { + var resourcePrefix = GetType().Namespace + ".Configuration."; + yield return new PluginPageInfo + { + Name = Name, + EmbeddedResourcePath = resourcePrefix + "config.html" + }; + yield return new PluginPageInfo + { + Name = Name + "Notice", + EmbeddedResourcePath = resourcePrefix + "NOTICE.md" + }; + } +} diff --git a/MediaBrowser.Providers/Plugins/ListenBrainz/ListenBrainzSimilarArtistProvider.cs b/MediaBrowser.Providers/Plugins/ListenBrainz/ListenBrainzSimilarArtistProvider.cs new file mode 100644 index 0000000000..3dca748d06 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/ListenBrainz/ListenBrainzSimilarArtistProvider.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Plugins.ListenBrainz.Api; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Plugins.ListenBrainz; + +/// <summary> +/// ListenBrainz-based similar items provider for music artists. +/// </summary> +public class ListenBrainzSimilarArtistProvider : IRemoteSimilarItemsProvider<MusicArtist> +{ + private readonly ListenBrainzLabsClient _labsClient; + private readonly ILogger<ListenBrainzSimilarArtistProvider> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="ListenBrainzSimilarArtistProvider"/> class. + /// </summary> + /// <param name="labsClient">The ListenBrainz Labs API client.</param> + /// <param name="logger">The logger.</param> + public ListenBrainzSimilarArtistProvider( + ListenBrainzLabsClient labsClient, + ILogger<ListenBrainzSimilarArtistProvider> logger) + { + _labsClient = labsClient; + _logger = logger; + } + + /// <inheritdoc/> + public string Name => "ListenBrainz"; + + /// <inheritdoc/> + public MetadataPluginType Type => MetadataPluginType.SimilarityProvider; + + /// <inheritdoc/> + public TimeSpan? CacheDuration + { + get + { + var days = ListenBrainzPlugin.Instance?.Configuration.SimilarItemsCacheDays ?? 0; + return days > 0 ? TimeSpan.FromDays(days) : null; + } + } + + /// <inheritdoc/> + public async IAsyncEnumerable<SimilarItemReference> GetSimilarItemsAsync( + MusicArtist item, + SimilarItemsQuery query, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(query); + + if (!item.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out var mbidStr) || !Guid.TryParse(mbidStr, out var mbid)) + { + _logger.LogDebug("No MusicBrainz Artist ID found for {ArtistName}", item.Name); + yield break; + } + + IReadOnlyList<Guid> similarMbids; + try + { + similarMbids = await _labsClient.GetSimilarArtistsAsync(mbid, cancellationToken).ConfigureAwait(false); + } + catch (HttpRequestException ex) + { + _logger.LogWarning(ex, "Failed to fetch similar artists from ListenBrainz for {ArtistMbid}", mbid); + yield break; + } + + var providerName = MetadataProvider.MusicBrainzArtist.ToString(); + + foreach (var similarMbid in similarMbids) + { + yield return new SimilarItemReference + { + ProviderName = providerName, + ProviderId = similarMbid.ToString() + }; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 715bdd9da4..7dee5fd31d 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -14,6 +14,7 @@ using MediaBrowser.Providers.Music; using MetaBrainz.MusicBrainz; using MetaBrainz.MusicBrainz.Interfaces.Entities; using MetaBrainz.MusicBrainz.Interfaces.Searches; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.MusicBrainz; @@ -22,6 +23,17 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz; /// </summary> public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder { + private readonly ILogger<MusicBrainzAlbumProvider> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="MusicBrainzAlbumProvider"/> class. + /// </summary> + /// <param name="logger">The logger.</param> + public MusicBrainzAlbumProvider(ILogger<MusicBrainzAlbumProvider> logger) + { + _logger = logger; + } + /// <inheritdoc /> public string Name => "MusicBrainz"; @@ -32,21 +44,26 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) { var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery; - var releaseId = searchInfo.GetReleaseId(); - var releaseGroupId = searchInfo.GetReleaseGroupId(); + var releaseId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetReleaseId(), "release", _logger); + var releaseGroupId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetReleaseGroupId(), "release group", _logger); - if (!string.IsNullOrEmpty(releaseId)) + if (releaseId is not null) { - var releaseResult = await query.LookupReleaseAsync(new Guid(releaseId), Include.Artists | Include.ReleaseGroups, cancellationToken).ConfigureAwait(false); - return GetReleaseResult(releaseResult).SingleItemAsEnumerable(); + var releaseResult = await query.LookupReleaseOrNullAsync(releaseId.Value, Include.Artists | Include.ReleaseGroups, _logger, cancellationToken).ConfigureAwait(false); + if (releaseResult is not null) + { + return GetReleaseResult(releaseResult).SingleItemAsEnumerable(); + } } - if (!string.IsNullOrEmpty(releaseGroupId)) + if (releaseGroupId is not null) { - var releaseGroupResult = await query.LookupReleaseGroupAsync(new Guid(releaseGroupId), Include.Releases, null, cancellationToken).ConfigureAwait(false); - - // No need to pass the cancellation token to GetReleaseGroupResultAsync as we're already passing it to ToBlockingEnumerable - return GetReleaseGroupResultAsync(releaseGroupResult.Releases, CancellationToken.None).ToBlockingEnumerable(cancellationToken); + var releaseGroupResult = await query.LookupReleaseGroupOrNullAsync(releaseGroupId.Value, Include.Releases, _logger, cancellationToken).ConfigureAwait(false); + if (releaseGroupResult is not null) + { + // No need to pass the cancellation token to GetReleaseGroupResultAsync as we're already passing it to ToBlockingEnumerable + return GetReleaseGroupResultAsync(releaseGroupResult.Releases, CancellationToken.None).ToBlockingEnumerable(cancellationToken); + } } var artistMusicBrainzId = searchInfo.GetMusicBrainzArtistId(); @@ -102,8 +119,11 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu foreach (var result in releaseSearchResults) { // Fetch full release info, otherwise artists are missing - var fullResult = await query.LookupReleaseAsync(result.Id, Include.Artists | Include.ReleaseGroups, cancellationToken).ConfigureAwait(false); - yield return GetReleaseResult(fullResult); + var fullResult = await query.LookupReleaseOrNullAsync(result.Id, Include.Artists | Include.ReleaseGroups, _logger, cancellationToken).ConfigureAwait(false); + if (fullResult is not null) + { + yield return GetReleaseResult(fullResult); + } } } @@ -155,10 +175,9 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu /// <inheritdoc /> public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken) { - // TODO: This sets essentially nothing. As-is, it's mostly useless. Make it actually pull metadata and use it. var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery; - var releaseId = info.GetReleaseId(); - var releaseGroupId = info.GetReleaseGroupId(); + var releaseId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetReleaseId(), "release", _logger); + var releaseGroupId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetReleaseGroupId(), "release group", _logger); var result = new MetadataResult<MusicAlbum> { @@ -166,20 +185,15 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu }; // If there is a release group, but no release ID, try to match the release - if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId)) + if (releaseId is null && releaseGroupId is not null) { // TODO: Actually try to match the release. Simply taking the first result is stupid. - var releaseGroup = await query.LookupReleaseGroupAsync(new Guid(releaseGroupId), Include.None, null, cancellationToken).ConfigureAwait(false); - var release = releaseGroup.Releases?.Count > 0 ? releaseGroup.Releases[0] : null; - if (release is not null) - { - releaseId = release.Id.ToString(); - result.HasMetadata = true; - } + var releaseGroupLookup = await query.LookupReleaseGroupOrNullAsync(releaseGroupId.Value, Include.None, _logger, cancellationToken).ConfigureAwait(false); + releaseId = releaseGroupLookup?.Releases?.Count > 0 ? releaseGroupLookup.Releases[0].Id : null; } // If there is no release ID, lookup a release with the info we have - if (string.IsNullOrWhiteSpace(releaseId)) + if (releaseId is null) { var artistMusicBrainzId = info.GetMusicBrainzArtistId(); IRelease? releaseResult = null; @@ -199,49 +213,118 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu if (releaseResult is not null) { - releaseId = releaseResult.Id.ToString(); + releaseId = releaseResult.Id; if (releaseResult.ReleaseGroup?.Id is not null) { - releaseGroupId = releaseResult.ReleaseGroup.Id.ToString(); + releaseGroupId = releaseResult.ReleaseGroup.Id; } + } + } + + if (releaseId is null && releaseGroupId is null) + { + return result; + } + + // Fetch the full release (and its release group) so we can populate everything MusicBrainz returns. + IRelease? release = null; + if (releaseId is not null) + { + release = await query.LookupReleaseOrNullAsync( + releaseId.Value, + Include.Artists | Include.ReleaseGroups | Include.Labels | Include.Genres | Include.Tags, + _logger, + cancellationToken).ConfigureAwait(false); - result.HasMetadata = true; - result.Item.ProductionYear = releaseResult.Date?.Year; - result.Item.Overview = releaseResult.Annotation; + if (releaseGroupId is null && release?.ReleaseGroup?.Id is not null) + { + releaseGroupId = release.ReleaseGroup.Id; } } - // If we have a release ID but not a release group ID, lookup the release group - if (!string.IsNullOrWhiteSpace(releaseId) && string.IsNullOrWhiteSpace(releaseGroupId)) + IReleaseGroup? releaseGroup = null; + if (releaseGroupId is not null) { - var release = await query.LookupReleaseAsync(new Guid(releaseId), Include.ReleaseGroups, cancellationToken).ConfigureAwait(false); - releaseGroupId = release.ReleaseGroup?.Id.ToString(); - result.HasMetadata = true; + releaseGroup = await query.LookupReleaseGroupOrNullAsync( + releaseGroupId.Value, + Include.Artists | Include.Genres | Include.Tags, + _logger, + cancellationToken).ConfigureAwait(false); } - // If we have a release ID and a release group ID - if (!string.IsNullOrWhiteSpace(releaseId) || !string.IsNullOrWhiteSpace(releaseGroupId)) + if (release is null && releaseGroup is null) { - result.HasMetadata = true; + return result; } - if (result.HasMetadata) + result.HasMetadata = true; + + if (releaseId is not null) { - if (!string.IsNullOrEmpty(releaseId)) - { - result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId); - } + result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId.Value.ToString()); + } - if (!string.IsNullOrEmpty(releaseGroupId)) - { - result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId); - } + if (releaseGroupId is not null) + { + result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId.Value.ToString()); } + Populate(result.Item, release, releaseGroup); + return result; } + private static void Populate(MusicAlbum item, IRelease? release, IReleaseGroup? releaseGroup) + { + // Prefer the release group (album-level) data, falling back to the specific release. + // The release group's first release date is the original album date. + var date = releaseGroup?.FirstReleaseDate ?? release?.Date; + if (date is not null) + { + item.PremiereDate = date.NearestDate; + item.ProductionYear = date.Year; + } + + var artistCredit = release?.ArtistCredit ?? releaseGroup?.ArtistCredit; + if (artistCredit is not null && artistCredit.Count > 0) + { + item.AlbumArtists = artistCredit + .Select(credit => credit.Name) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .ToArray(); + } + + var genres = releaseGroup?.Genres ?? release?.Genres; + if (genres is not null && genres.Count > 0) + { + item.Genres = genres + .OrderByDescending(genre => genre.VoteCount) + .Select(genre => genre.Name) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .ToArray(); + } + + var tags = releaseGroup?.Tags ?? release?.Tags; + if (tags is not null && tags.Count > 0) + { + item.Tags = tags + .OrderByDescending(tag => tag.VoteCount) + .Select(tag => tag.Name) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .ToArray(); + } + + if (release?.LabelInfo is not null && release.LabelInfo.Count > 0) + { + item.Studios = release.LabelInfo + .Where(labelInfo => !string.IsNullOrWhiteSpace(labelInfo.Label?.Name)) + .Select(labelInfo => labelInfo.Label!.Name!) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToArray(); + } + } + /// <inheritdoc /> public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs index 0fe4e6bb16..c3d13ed42c 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs @@ -13,6 +13,7 @@ using MediaBrowser.Providers.Music; using MetaBrainz.MusicBrainz; using MetaBrainz.MusicBrainz.Interfaces.Entities; using MetaBrainz.MusicBrainz.Interfaces.Searches; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.MusicBrainz; @@ -21,6 +22,17 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz; /// </summary> public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder { + private readonly ILogger<MusicBrainzArtistProvider> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class. + /// </summary> + /// <param name="logger">The logger.</param> + public MusicBrainzArtistProvider(ILogger<MusicBrainzArtistProvider> logger) + { + _logger = logger; + } + /// <inheritdoc /> public string Name => "MusicBrainz"; @@ -32,12 +44,20 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) { var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery; - var artistId = searchInfo.GetMusicBrainzArtistId(); + var artistId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetMusicBrainzArtistId(), "artist", _logger); + + if (artistId is not null) + { + var artistResult = await query.LookupArtistOrNullAsync(artistId.Value, Include.Aliases, _logger, cancellationToken).ConfigureAwait(false); + if (artistResult is not null) + { + return GetResultFromResponse(artistResult).SingleItemAsEnumerable(); + } + } - if (!string.IsNullOrWhiteSpace(artistId)) + if (string.IsNullOrWhiteSpace(searchInfo.Name)) { - var artistResult = await query.LookupArtistAsync(new Guid(artistId), Include.Aliases, null, null, cancellationToken).ConfigureAwait(false); - return GetResultFromResponse(artistResult).SingleItemAsEnumerable(); + return []; } var artistSearchResults = await query.FindArtistsAsync($"\"{searchInfo.Name}\"", null, null, false, cancellationToken) @@ -58,7 +78,7 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar } } - return Enumerable.Empty<RemoteSearchResult>(); + return []; } private IEnumerable<RemoteSearchResult> GetResultsFromResponse(IEnumerable<ISearchResult<IArtist>>? releaseSearchResults) @@ -94,30 +114,69 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar { var result = new MetadataResult<MusicArtist> { Item = new MusicArtist() }; - var musicBrainzId = info.GetMusicBrainzArtistId(); + var musicBrainzId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetMusicBrainzArtistId(), "artist", _logger); - if (string.IsNullOrWhiteSpace(musicBrainzId)) + // If we don't have an id yet, resolve one by name so we can look the artist up. + if (musicBrainzId is null) { var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false); + musicBrainzId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchResults.FirstOrDefault()?.GetProviderId(MetadataProvider.MusicBrainzArtist), "artist", _logger); + } - var singleResult = searchResults.FirstOrDefault(); + if (musicBrainzId is null) + { + return result; + } - if (singleResult is not null) - { - musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist); - result.Item.Overview = singleResult.Overview; + var query = Plugin.Instance!.MusicBrainzQuery; + var artist = await query.LookupArtistOrNullAsync(musicBrainzId.Value, Include.Genres | Include.Tags, _logger, cancellationToken).ConfigureAwait(false); - if (Plugin.Instance!.Configuration.ReplaceArtistName) - { - result.Item.Name = singleResult.Name; - } - } + if (artist is null) + { + return result; + } + + result.HasMetadata = true; + result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString()); + + if (Plugin.Instance!.Configuration.ReplaceArtistName && !string.IsNullOrWhiteSpace(artist.Name)) + { + result.Item.Name = artist.Name; + } + + if (artist.LifeSpan?.Begin is not null) + { + result.Item.PremiereDate = artist.LifeSpan.Begin.NearestDate; + result.Item.ProductionYear = artist.LifeSpan.Begin.Year; + } + + if (artist.LifeSpan?.End is not null) + { + result.Item.EndDate = artist.LifeSpan.End.NearestDate; + } + + var location = string.IsNullOrWhiteSpace(artist.Area?.Name) ? artist.Country : artist.Area!.Name; + if (!string.IsNullOrWhiteSpace(location)) + { + result.Item.ProductionLocations = [location]; + } + + if (artist.Genres is not null && artist.Genres.Count > 0) + { + result.Item.Genres = artist.Genres + .OrderByDescending(genre => genre.VoteCount) + .Select(genre => genre.Name) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .ToArray(); } - if (!string.IsNullOrWhiteSpace(musicBrainzId)) + if (artist.Tags is not null && artist.Tags.Count > 0) { - result.HasMetadata = true; - result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId); + result.Item.Tags = artist.Tags + .OrderByDescending(tag => tag.VoteCount) + .Select(tag => tag.Name) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .ToArray(); } return result; diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs new file mode 100644 index 0000000000..f3df41e942 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs @@ -0,0 +1,112 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using MetaBrainz.Common; +using MetaBrainz.MusicBrainz; +using MetaBrainz.MusicBrainz.Interfaces.Entities; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Plugins.MusicBrainz; + +/// <summary> +/// Helpers for talking to MusicBrainz with identifiers that are not guaranteed to be valid. +/// </summary> +internal static class MusicBrainzQueryExtensions +{ + /// <summary> + /// Parses a MusicBrainz identifier, which may come from user-supplied tags or NFO files and is therefore not + /// guaranteed to be a valid GUID. + /// </summary> + /// <param name="id">The identifier to parse.</param> + /// <param name="entityType">The type of entity the identifier refers to, used for logging.</param> + /// <param name="logger">The logger.</param> + /// <returns>The parsed identifier, or <see langword="null"/> if it is missing or malformed.</returns> + public static Guid? ParseMusicBrainzId(string? id, string entityType, ILogger logger) + { + if (string.IsNullOrWhiteSpace(id)) + { + return null; + } + + if (!Guid.TryParse(id, out var parsedId)) + { + logger.LogDebug("Ignoring malformed MusicBrainz {EntityType} id {Id}", entityType, id); + return null; + } + + return parsedId; + } + + /// <summary> + /// Looks up a release, treating an unknown identifier as missing data rather than an error. + /// </summary> + /// <param name="query">The MusicBrainz query client.</param> + /// <param name="releaseId">The release identifier.</param> + /// <param name="include">The additional data to include in the lookup.</param> + /// <param name="logger">The logger.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The release, or <see langword="null"/> if MusicBrainz does not have it.</returns> + public static Task<IRelease?> LookupReleaseOrNullAsync(this Query query, Guid releaseId, Include include, ILogger logger, CancellationToken cancellationToken) + => NotFoundAsNullAsync( + () => query.LookupReleaseAsync(releaseId, include, cancellationToken), + "release", + releaseId, + logger); + + /// <summary> + /// Looks up a release group, treating an unknown identifier as missing data rather than an error. + /// </summary> + /// <param name="query">The MusicBrainz query client.</param> + /// <param name="releaseGroupId">The release group identifier.</param> + /// <param name="include">The additional data to include in the lookup.</param> + /// <param name="logger">The logger.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The release group, or <see langword="null"/> if MusicBrainz does not have it.</returns> + public static Task<IReleaseGroup?> LookupReleaseGroupOrNullAsync(this Query query, Guid releaseGroupId, Include include, ILogger logger, CancellationToken cancellationToken) + => NotFoundAsNullAsync( + () => query.LookupReleaseGroupAsync(releaseGroupId, include, null, cancellationToken), + "release group", + releaseGroupId, + logger); + + /// <summary> + /// Looks up an artist, treating an unknown identifier as missing data rather than an error. + /// </summary> + /// <param name="query">The MusicBrainz query client.</param> + /// <param name="artistId">The artist identifier.</param> + /// <param name="include">The additional data to include in the lookup.</param> + /// <param name="logger">The logger.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The artist, or <see langword="null"/> if MusicBrainz does not have it.</returns> + public static Task<IArtist?> LookupArtistOrNullAsync(this Query query, Guid artistId, Include include, ILogger logger, CancellationToken cancellationToken) + => NotFoundAsNullAsync( + () => query.LookupArtistAsync(artistId, include, null, null, cancellationToken), + "artist", + artistId, + logger); + + /// <summary> + /// Runs a lookup, mapping a "not found" response to <see langword="null"/>. Identifiers stored on a library item + /// can refer to entities that no longer exist in MusicBrainz, which is not an error worth failing a refresh over. + /// </summary> + /// <typeparam name="T">The type of entity being looked up.</typeparam> + /// <param name="lookup">The lookup to run.</param> + /// <param name="entityType">The type of entity being looked up, used for logging.</param> + /// <param name="id">The identifier being looked up, used for logging.</param> + /// <param name="logger">The logger.</param> + /// <returns>The entity, or <see langword="null"/> if MusicBrainz does not have it.</returns> + private static async Task<T?> NotFoundAsNullAsync<T>(Func<Task<T>> lookup, string entityType, Guid id, ILogger logger) + where T : class + { + try + { + return await lookup().ConfigureAwait(false); + } + catch (HttpError ex) when (ex.Status == HttpStatusCode.NotFound) + { + logger.LogDebug("MusicBrainz has no {EntityType} with id {Id}", entityType, id); + return null; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzReleaseGroupExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzReleaseGroupExternalUrlProvider.cs index dd0a939f72..f7c570692d 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzReleaseGroupExternalUrlProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzReleaseGroupExternalUrlProvider.cs @@ -19,7 +19,7 @@ public class MusicBrainzReleaseGroupExternalUrlProvider : IExternalUrlProvider { if (item is MusicAlbum) { - if (item.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out var externalId)) + if (item.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out var externalId)) { yield return Plugin.Instance!.Configuration.Server + $"/release-group/{externalId}"; } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzTrackExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzTrackExternalUrlProvider.cs index 59e6f42b19..c2bbd8ba86 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzTrackExternalUrlProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzTrackExternalUrlProvider.cs @@ -19,7 +19,7 @@ public class MusicBrainzTrackExternalUrlProvider : IExternalUrlProvider { if (item is Audio) { - if (item.TryGetProviderId(MetadataProvider.MusicBrainzTrack, out var externalId)) + if (item.TryGetProviderId(MetadataProvider.MusicBrainzTrack, out var externalId)) { yield return Plugin.Instance!.Configuration.Server + $"/track/{externalId}"; } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs index 69225d0b95..f448e6b20c 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs @@ -6,6 +6,7 @@ using System.Threading; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration; @@ -17,7 +18,7 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz; /// <summary> /// Plugin instance. /// </summary> -public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IDisposable +public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage, IDisposable { private readonly ILogger<Plugin> _logger; private readonly Lock _queryLock = new(); @@ -66,6 +67,9 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IDisposable // TODO remove when plugin removed from server. public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml"; + /// <inheritdoc /> + public string ImageResourceName => GetType().Namespace + ".jellyfin-plugin-musicbrainz.svg"; + /// <summary> /// Gets the current MusicBrainz query client. /// </summary> diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/jellyfin-plugin-musicbrainz.svg b/MediaBrowser.Providers/Plugins/MusicBrainz/jellyfin-plugin-musicbrainz.svg new file mode 100644 index 0000000000..8074d59d7b --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/jellyfin-plugin-musicbrainz.svg @@ -0,0 +1,36 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 508 285.75" version="1.1" id="svg8"> + <defs id="defs2"> + <linearGradient id="linearGradient851"> + <stop style="stop-color:#fffedb;stop-opacity:1" offset="0" id="stop847"/> + <stop style="stop-color:#ffeec2;stop-opacity:1" offset="1" id="stop849"/> + </linearGradient> + <linearGradient id="linear-gradient" y1="40.76" x2="190.24" y2="40.76" gradientUnits="userSpaceOnUse" gradientTransform="translate(95.25 74.882) scale(1.66894)"> + <stop offset="0" stop-color="#90cea1" id="stop916"/> + <stop offset=".56" stop-color="#3cbec9" id="stop918"/> + <stop offset="1" stop-color="#00b3e5" id="stop920"/> + </linearGradient> + <style id="style914"/> + <style id="style1359"> + .a{fill:#ba478f}.b{fill:#eb743b} + </style> + <radialGradient xlink:href="#linearGradient851" id="radialGradient853" cx="125.255" cy="16.735" fx="125.255" fy="16.735" r="254" gradientTransform="matrix(0 1.06475 -1.74952 0 154.532 -61.444)" gradientUnits="userSpaceOnUse"/> + </defs> + <g id="layer1"> + <path style="opacity:.98;fill:url(#radialGradient853);stroke-width:2.64583;fill-opacity:1" id="rect845" d="M0 0h508v285.75H0z"/> + <g id="g1409" transform="translate(95.25 119.153) scale(1.69442)"> + <path class="a" id="polygon1363" d="M0 7v14l12 7V0z"/> + <path class="b" id="polygon1365" d="M25 7v14l-12 7V0z"/> + <path class="a" d="m40.2 5.52 4.44 13.85 4.43-13.85h6.32v19.91h-4.82v-4.65l.43-9.51-4.77 14.16h-3.18l-4.82-14.18.46 9.53v4.65h-4.8V5.52h6.3z" transform="translate(-2 -1)" id="path1367"/> + <path class="a" d="M67 23.83a4.9 4.9 0 0 1-1.68 1.38 5 5 0 0 1-2.27.49 6.22 6.22 0 0 1-2-.31 3.89 3.89 0 0 1-1.56-1 4.51 4.51 0 0 1-1-1.71 7.7 7.7 0 0 1-.36-2.51v-9.53h4.61v9.6a2 2 0 0 0 .47 1.45 1.88 1.88 0 0 0 1.37.46 2.87 2.87 0 0 0 1.4-.3 2.24 2.24 0 0 0 .88-.85V10.64h4.63v14.79h-4.32z" transform="translate(-2 -1)" id="path1369"/> + <path class="a" d="M81.76 21.27a1 1 0 0 0-.13-.51 1.3 1.3 0 0 0-.46-.42 4.21 4.21 0 0 0-.89-.34q-.56-.18-1.42-.37a12.89 12.89 0 0 1-2-.61 6.57 6.57 0 0 1-1.65-.93 4.14 4.14 0 0 1-1.11-1.31 3.59 3.59 0 0 1-.4-1.72 4.12 4.12 0 0 1 .4-1.79 4.34 4.34 0 0 1 1.18-1.49 5.88 5.88 0 0 1 1.91-1 8.2 8.2 0 0 1 2.58-.38 9.48 9.48 0 0 1 2.69.36 6.34 6.34 0 0 1 2 1 4.41 4.41 0 0 1 1.29 1.52 4.21 4.21 0 0 1 .45 1.95h-4.59a1.88 1.88 0 0 0-.42-1.31 1.89 1.89 0 0 0-1.45-.46 2.08 2.08 0 0 0-.66.1 1.75 1.75 0 0 0-.54.29 1.38 1.38 0 0 0-.37.44 1.22 1.22 0 0 0-.14.57 1.14 1.14 0 0 0 .58 1 5.41 5.41 0 0 0 1.88.63 18.45 18.45 0 0 1 2.15.53 6.79 6.79 0 0 1 1.83.86 4.15 4.15 0 0 1 1.26 1.35 3.87 3.87 0 0 1 .47 2 3.76 3.76 0 0 1-.45 1.77 4.31 4.31 0 0 1-1.29 1.44 6.61 6.61 0 0 1-2 1 9.52 9.52 0 0 1-2.68.35 8.14 8.14 0 0 1-2.82-.45 6.56 6.56 0 0 1-2.05-1.17 4.92 4.92 0 0 1-1.25-1.61 4.14 4.14 0 0 1-.42-1.78h4.31a1.78 1.78 0 0 0 .68 1.5 2.81 2.81 0 0 0 1.68.47 2.21 2.21 0 0 0 1.42-.38 1.22 1.22 0 0 0 .43-1.1z" transform="translate(-2 -1)" id="path1371"/> + <path class="a" d="M88.32 6.82a2.17 2.17 0 0 1 .18-.9 2.07 2.07 0 0 1 .5-.71 2.44 2.44 0 0 1 .81-.46 3.37 3.37 0 0 1 2.08 0 2.44 2.44 0 0 1 .81.46 2.07 2.07 0 0 1 .53.71 2.3 2.3 0 0 1 0 1.8 2.07 2.07 0 0 1-.53.71 2.44 2.44 0 0 1-.81.46 3.37 3.37 0 0 1-2.08 0 2.44 2.44 0 0 1-.81-.45 2.07 2.07 0 0 1-.53-.71 2.17 2.17 0 0 1-.15-.91zm4.89 18.61H88.6V10.64h4.62v14.79z" transform="translate(-2 -1)" id="path1373"/> + <path class="a" d="M102.31 22.15a2.05 2.05 0 0 0 1.5-.53 1.93 1.93 0 0 0 .52-1.47h4.32a5.35 5.35 0 0 1-.47 2.28 5.22 5.22 0 0 1-1.31 1.75 5.88 5.88 0 0 1-2 1.13 8.13 8.13 0 0 1-5.51-.18 6 6 0 0 1-2.17-1.58 6.7 6.7 0 0 1-1.31-2.38 9.74 9.74 0 0 1-.44-3v-.29a9.81 9.81 0 0 1 .44-3 6.75 6.75 0 0 1 1.3-2.39 6 6 0 0 1 2.15-1.58 7.37 7.37 0 0 1 3-.57 7.81 7.81 0 0 1 2.55.4 5.57 5.57 0 0 1 2 1.16 5.18 5.18 0 0 1 1.29 1.87 6.53 6.53 0 0 1 .46 2.52h-4.32a3.62 3.62 0 0 0-.12-.93 2 2 0 0 0-.38-.76 1.83 1.83 0 0 0-.65-.51 2.14 2.14 0 0 0-.92-.18 1.87 1.87 0 0 0-1.14.32 2.07 2.07 0 0 0-.66.86 4.3 4.3 0 0 0-.31 1.26 14.81 14.81 0 0 0-.08 1.52v.33a14.89 14.89 0 0 0 .08 1.54 4.3 4.3 0 0 0 .31 1.26 1.93 1.93 0 0 0 .68.85 2 2 0 0 0 1.19.3z" transform="translate(-2 -1)" id="path1375"/> + <path class="b" d="M110.81 25.43V5.52H118a14.77 14.77 0 0 1 3.3.33 7.29 7.29 0 0 1 2.47 1 4.6 4.6 0 0 1 1.54 1.72 5.22 5.22 0 0 1 .53 2.43 5.69 5.69 0 0 1-.15 1.32 4.21 4.21 0 0 1-.49 1.2 3.92 3.92 0 0 1-.87 1 4.45 4.45 0 0 1-1.3.73 4.56 4.56 0 0 1 1.5.67 3.89 3.89 0 0 1 1 1 4 4 0 0 1 .55 1.24 5.36 5.36 0 0 1 .17 1.35 5.26 5.26 0 0 1-1.9 4.49 9 9 0 0 1-5.59 1.47h-7.94zm4.8-11.61h2.5a3.56 3.56 0 0 0 2.24-.57 2 2 0 0 0 .67-1.65 2.14 2.14 0 0 0-.72-1.81 3.89 3.89 0 0 0-2.3-.56h-2.35v4.59zm0 3.14v4.77h3.14a3.8 3.8 0 0 0 1.24-.18 2.25 2.25 0 0 0 .83-.49 1.88 1.88 0 0 0 .47-.72 2.55 2.55 0 0 0 .15-.89 3.69 3.69 0 0 0-.14-1 1.92 1.92 0 0 0-.44-.79 2 2 0 0 0-.78-.5 3.36 3.36 0 0 0-1.16-.18h-3.32z" transform="translate(-2 -1)" id="path1377"/> + <path class="b" d="M137.61 14.81h-1.52a4.09 4.09 0 0 0-1.79.33 2.06 2.06 0 0 0-1 1v9.37h-4.6V10.64h4.3l.15 1.9a4.51 4.51 0 0 1 1.36-1.6 3.18 3.18 0 0 1 1.88-.57 5.57 5.57 0 0 1 .68 0 3.68 3.68 0 0 1 .61.12z" transform="translate(-2 -1)" id="path1379"/> + <path class="b" d="M147.19 25.43a3.19 3.19 0 0 1-.25-.61q-.1-.34-.18-.72a4.24 4.24 0 0 1-3.55 1.6 5.66 5.66 0 0 1-1.93-.33 5.11 5.11 0 0 1-1.59-.91 4.31 4.31 0 0 1-1.09-1.4 4 4 0 0 1-.4-1.8 4.83 4.83 0 0 1 .42-2.05 3.89 3.89 0 0 1 1.27-1.52 6.3 6.3 0 0 1 2.16-1 12.56 12.56 0 0 1 3.1-.33h1.42v-.75a2.43 2.43 0 0 0-.41-1.49 1.58 1.58 0 0 0-1.35-.55 1.71 1.71 0 0 0-1.22.4 1.59 1.59 0 0 0-.42 1.22h-4.61a4.11 4.11 0 0 1 .46-1.91 4.49 4.49 0 0 1 1.31-1.53 6.54 6.54 0 0 1 2-1 9 9 0 0 1 2.67-.37 8.76 8.76 0 0 1 2.45.33 5.5 5.5 0 0 1 1.95 1 4.61 4.61 0 0 1 1.29 1.65 5.38 5.38 0 0 1 .46 2.3v7.3a7.71 7.71 0 0 0 .12.94 5 5 0 0 0 .2.72 4.26 4.26 0 0 0 .27.59v.23h-4.61zm-2.88-3a2.57 2.57 0 0 0 1.43-.37 2.32 2.32 0 0 0 .81-.83v-2.38h-1.45a2.94 2.94 0 0 0-1.1.15 1.85 1.85 0 0 0-.71.48 1.8 1.8 0 0 0-.38.69 2.84 2.84 0 0 0-.12.81 1.32 1.32 0 0 0 .42 1 1.53 1.53 0 0 0 1.1.44z" transform="translate(-2 -1)" id="path1381"/> + <path class="b" d="M153.69 6.82a2.17 2.17 0 0 1 .18-.9 2.07 2.07 0 0 1 .53-.71 2.44 2.44 0 0 1 .81-.46 3.37 3.37 0 0 1 2.08 0 2.44 2.44 0 0 1 .81.46 2.07 2.07 0 0 1 .53.71 2.3 2.3 0 0 1 0 1.8 2.07 2.07 0 0 1-.53.71 2.44 2.44 0 0 1-.81.46 3.37 3.37 0 0 1-2.08 0 2.44 2.44 0 0 1-.81-.46 2.07 2.07 0 0 1-.53-.71 2.17 2.17 0 0 1-.18-.9zm4.89 18.61H154V10.64h4.62v14.79z" transform="translate(-2 -1)" id="path1383"/> + <path class="b" d="m165.65 10.64.15 1.74a5 5 0 0 1 1.83-1.5 5.5 5.5 0 0 1 2.4-.51 5.74 5.74 0 0 1 1.88.29 3.5 3.5 0 0 1 1.47 1 4.59 4.59 0 0 1 1 1.78 9.43 9.43 0 0 1 .33 2.71v9.31H170v-9.35a3.48 3.48 0 0 0-.1-1.11 1.58 1.58 0 0 0-.41-.67 1.41 1.41 0 0 0-.66-.33 4 4 0 0 0-.88-.09 2.37 2.37 0 0 0-1.22.29 2.26 2.26 0 0 0-.79.78v10.45h-4.61V10.64z" transform="translate(-2 -1)" id="path1385"/> + <path class="b" d="M182.64 21.88h6.74v3.55h-12.56v-2.57l6.56-8.67h-6.28v-3.55h12.13v2.49z" transform="translate(-2 -1)" id="path1387"/> + </g> + </g> +</svg> diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs index ccff31ebaa..437a997c11 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs @@ -44,7 +44,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb var result = new MetadataResult<Episode> { Item = new Episode(), - QueriedById = true + QueriedById = true, + // OMDb is not localized, everything it returns is English + ResultLanguage = "en" }; // Allowing this will dramatically increase scan times diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index e84f1359b7..7b245ea5a7 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -218,7 +218,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb var result = new MetadataResult<T> { Item = new T(), - QueriedById = true + QueriedById = true, + // OMDb is not localized, everything it returns is English + ResultLanguage = "en" }; var imdbId = info.GetProviderId(MetadataProvider.Imdb); diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 4882822766..d51d913caa 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -27,6 +27,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb /// <summary>Provider for OMDB service.</summary> public class OmdbProvider { + /// <summary>Generational suffixes that OMDb separates from the name with a comma.</summary> + private static readonly string[] NameSuffixes = ["Jr", "Jnr", "Sr", "Snr", "II", "III", "IV", "V"]; + private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IHttpClientFactory _httpClientFactory; @@ -413,49 +416,103 @@ namespace MediaBrowser.Providers.Plugins.Omdb } item.Overview = result.Plot; - item.OriginalLanguage = result.Language; + item.OriginalLanguage = result.Language?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).FirstOrDefault(); if (!Plugin.Instance.Configuration.CastAndCrew) { return; } - if (!string.IsNullOrWhiteSpace(result.Director)) - { - var person = new PersonInfo - { - Name = result.Director.Trim(), - Type = PersonKind.Director - }; + AddPeople(itemResult, result.Director, PersonKind.Director); + AddPeople(itemResult, result.Writer, PersonKind.Writer); + AddPeople(itemResult, result.Actors, PersonKind.Actor); + } - itemResult.AddPerson(person); + /// <summary>Adds the people from a comma separated OMDb credit list.</summary> + /// <typeparam name="T">The item type.</typeparam> + /// <param name="itemResult">The metadata result to add the people to.</param> + /// <param name="credits">The comma separated OMDb credit list.</param> + /// <param name="type">The kind of person each credit describes.</param> + internal static void AddPeople<T>(MetadataResult<T> itemResult, string credits, PersonKind type) + where T : BaseItem + { + if (string.IsNullOrWhiteSpace(credits)) + { + return; } - if (!string.IsNullOrWhiteSpace(result.Writer)) + var names = new List<string>(); + + foreach (var credit in SplitCredits(credits)) { - var person = new PersonInfo + // OMDb annotates the credited role in parentheses, e.g. "Mari Okada (screenplay)". The same + // person can be credited more than once this way, so strip it and let AddPerson deduplicate. + var name = credit; + var annotation = name.IndexOf('(', StringComparison.Ordinal); + if (annotation >= 0) + { + name = name[..annotation]; + } + + name = name.Trim(); + if (name.Length == 0) { - Name = result.Writer.Trim(), - Type = PersonKind.Writer - }; + continue; + } + + // A generational suffix is separated from the name it belongs to by the same comma the list + // uses, e.g. "Jack Salvatore, Jr.", so it has to be joined back instead of becoming a credit. + if (names.Count > 0 && IsNameSuffix(name)) + { + names[^1] = names[^1] + ", " + name; + continue; + } - itemResult.AddPerson(person); + names.Add(name); } - if (!string.IsNullOrWhiteSpace(result.Actors)) + foreach (var name in names) { - var actorList = result.Actors.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - foreach (var actor in actorList) + itemResult.AddPerson(new PersonInfo { - var person = new PersonInfo - { - Name = actor, - Type = PersonKind.Actor - }; + Name = name, + Type = type + }); + } + } + + // Only the commas between credits, never one inside an annotation: "Jerry Siegel (created by: + // Superman, Superboy)" is one credit, and splitting it blindly invents a person called "Superboy)". + private static IEnumerable<string> SplitCredits(string credits) + { + var depth = 0; + var start = 0; - itemResult.AddPerson(person); + for (var i = 0; i < credits.Length; i++) + { + switch (credits[i]) + { + case '(': + depth++; + break; + case ')': + depth = Math.Max(0, depth - 1); + break; + case ',' when depth == 0: + yield return credits[start..i]; + start = i + 1; + break; } } + + yield return credits[start..]; + } + + private static bool IsNameSuffix(string value) + { + var suffix = value.EndsWith('.') ? value[..^1] : value; + + return NameSuffixes.Contains(suffix, StringComparer.OrdinalIgnoreCase); } private static bool IsConfiguredForEnglish(BaseItem item, string language) diff --git a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs index a0fba48f05..9066ff8523 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs @@ -5,12 +5,13 @@ using System; using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.Omdb { - public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages + public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage { public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer) @@ -29,6 +30,8 @@ namespace MediaBrowser.Providers.Plugins.Omdb // TODO remove when plugin removed from server. public override string ConfigurationFileName => "Jellyfin.Plugin.Omdb.xml"; + public string ImageResourceName => GetType().Namespace + ".jellyfin-plugin-omdb.png"; + public IEnumerable<PluginPageInfo> GetPages() { yield return new PluginPageInfo diff --git a/MediaBrowser.Providers/Plugins/Omdb/jellyfin-plugin-omdb.png b/MediaBrowser.Providers/Plugins/Omdb/jellyfin-plugin-omdb.png Binary files differnew file mode 100644 index 0000000000..a4bb7f7d5f --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Omdb/jellyfin-plugin-omdb.png diff --git a/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs b/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs index 28f8c0c617..167959967d 100644 --- a/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Plugins.StudioImages.Configuration; @@ -13,7 +14,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages /// <summary> /// Artwork Plugin class. /// </summary> - public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages + public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage { /// <summary> /// Artwork repository URL. @@ -51,6 +52,9 @@ namespace MediaBrowser.Providers.Plugins.StudioImages public override string ConfigurationFileName => "Jellyfin.Plugin.StudioImages.xml"; /// <inheritdoc/> + public string ImageResourceName => GetType().Namespace + ".jellyfin-plugin-studioimages.svg"; + + /// <inheritdoc/> public IEnumerable<PluginPageInfo> GetPages() { yield return new PluginPageInfo diff --git a/MediaBrowser.Providers/Plugins/StudioImages/jellyfin-plugin-studioimages.svg b/MediaBrowser.Providers/Plugins/StudioImages/jellyfin-plugin-studioimages.svg new file mode 100644 index 0000000000..e9da69c571 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/StudioImages/jellyfin-plugin-studioimages.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1920 1080" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect x="0" y="0" width="1920" height="1080" style="fill:url(#_Radial1);fill-rule:nonzero;"/><g><path d="M487.844,725.411c-10.779,0 -20.007,-3.838 -27.683,-11.514c-7.676,-7.676 -11.514,-16.904 -11.514,-27.683l0,-274.381c0,-10.779 3.838,-20.007 11.514,-27.683c7.676,-7.676 16.904,-11.514 27.683,-11.514l274.381,0c10.779,0 20.007,3.838 27.683,11.514c7.676,7.676 11.514,16.904 11.514,27.683l0,274.381c0,10.779 -3.838,20.007 -11.514,27.683c-7.676,7.676 -16.904,11.514 -27.683,11.514l-274.381,0Zm0,-39.197l274.381,0l0,-274.381l-274.381,0l0,274.381Zm19.599,-39.197l235.184,0l-73.495,-97.993l-58.796,78.395l-44.097,-58.796l-58.796,78.395Zm-19.599,39.197l0,-274.381l0,274.381Z" style="fill:#fff;fill-rule:nonzero;"/><path d="M934.841,493.37c0,6.419 -1.632,12.505 -4.897,18.26c-3.265,5.755 -8.3,10.43 -15.106,14.027c-6.806,3.597 -15.632,5.395 -26.477,5.395c-5.423,0 -10.154,-0.249 -14.193,-0.747c-4.039,-0.498 -7.83,-1.3 -11.371,-2.407c-3.541,-1.107 -7.193,-2.545 -10.956,-4.316l0,-28.552c6.419,3.209 12.893,5.672 19.422,7.387c6.529,1.715 12.45,2.573 17.762,2.573c4.759,0 8.245,-0.83 10.458,-2.49c2.213,-1.66 3.32,-3.763 3.32,-6.308c0,-3.099 -1.632,-5.616 -4.897,-7.553c-3.265,-1.937 -8.77,-4.62 -16.517,-8.051c-5.865,-2.767 -10.956,-5.672 -15.272,-8.715c-4.316,-3.043 -7.636,-6.778 -9.96,-11.205c-2.324,-4.427 -3.486,-10.015 -3.486,-16.766c0,-7.636 1.881,-14.027 5.644,-19.173c3.763,-5.146 8.992,-9.019 15.687,-11.62c6.695,-2.601 14.47,-3.901 23.323,-3.901c7.747,0 14.719,0.858 20.916,2.573c6.197,1.715 11.731,3.68 16.6,5.893l-9.794,24.734c-5.091,-2.324 -10.098,-4.178 -15.023,-5.561c-4.925,-1.383 -9.49,-2.075 -13.695,-2.075c-4.095,0 -7.11,0.719 -9.047,2.158c-1.937,1.439 -2.905,3.265 -2.905,5.478c0,1.881 0.719,3.541 2.158,4.98c1.439,1.439 3.846,3.016 7.221,4.731c3.375,1.715 7.996,3.901 13.861,6.557c5.755,2.545 10.652,5.34 14.691,8.383c4.039,3.043 7.138,6.64 9.296,10.79c2.158,4.15 3.237,9.324 3.237,15.521Zm60.59,12.616c2.877,0 5.506,-0.304 7.885,-0.913c2.379,-0.609 4.842,-1.356 7.387,-2.241l0,23.074c-3.431,1.439 -7.083,2.656 -10.956,3.652c-3.873,0.996 -8.798,1.494 -14.774,1.494c-5.976,0 -11.205,-0.941 -15.687,-2.822c-4.482,-1.881 -7.996,-5.118 -10.541,-9.711c-2.545,-4.593 -3.818,-11.039 -3.818,-19.339l0,-37.848l-11.122,0l0,-12.948l14.11,-9.96l8.134,-19.256l20.75,0l0,18.426l22.576,0l0,23.738l-22.576,0l0,35.69c0,5.976 2.877,8.964 8.632,8.964Zm119.852,-68.392l0,91.798l-23.904,0l-3.984,-11.454l-1.826,0c-2.877,4.648 -6.806,7.996 -11.786,10.043c-4.98,2.047 -10.292,3.071 -15.936,3.071c-5.976,0 -11.399,-1.162 -16.268,-3.486c-4.869,-2.324 -8.715,-5.976 -11.537,-10.956c-2.822,-4.98 -4.233,-11.399 -4.233,-19.256l0,-59.76l31.706,0l0,50.132c0,5.976 0.858,10.513 2.573,13.612c1.715,3.099 4.51,4.648 8.383,4.648c5.976,0 9.988,-2.435 12.035,-7.304c2.047,-4.869 3.071,-11.841 3.071,-20.916l0,-40.172l31.706,0Zm49.634,93.458c-9.407,0 -17.181,-4.039 -23.323,-12.118c-6.142,-8.079 -9.213,-19.865 -9.213,-35.358c0,-15.715 3.126,-27.584 9.379,-35.607c6.253,-8.023 14.359,-12.035 24.319,-12.035c6.197,0 11.15,1.273 14.857,3.818c3.707,2.545 6.778,5.755 9.213,9.628l0.664,0c-0.443,-2.324 -0.802,-5.533 -1.079,-9.628c-0.277,-4.095 -0.415,-8.079 -0.415,-11.952l0,-24.568l31.872,0l0,126.16l-23.904,0l-6.806,-11.62l-1.162,0c-2.213,3.652 -5.257,6.778 -9.13,9.379c-3.873,2.601 -8.964,3.901 -15.272,3.901Zm12.948,-25.066c5.091,0 8.687,-1.605 10.79,-4.814c2.103,-3.209 3.209,-8.134 3.32,-14.774l0,-2.49c0,-7.193 -1.024,-12.727 -3.071,-16.6c-2.047,-3.873 -5.838,-5.81 -11.371,-5.81c-3.763,0 -6.889,1.826 -9.379,5.478c-2.49,3.652 -3.735,9.351 -3.735,17.098c0,7.636 1.245,13.197 3.735,16.683c2.49,3.486 5.727,5.229 9.711,5.229Zm80.51,-105.41c4.537,0 8.494,0.941 11.869,2.822c3.375,1.881 5.063,5.644 5.063,11.288c0,5.423 -1.688,9.102 -5.063,11.039c-3.375,1.937 -7.332,2.905 -11.869,2.905c-4.648,0 -8.604,-0.968 -11.869,-2.905c-3.265,-1.937 -4.897,-5.616 -4.897,-11.039c0,-5.644 1.632,-9.407 4.897,-11.288c3.265,-1.881 7.221,-2.822 11.869,-2.822Zm15.77,37.018l0,91.798l-31.706,0l0,-91.798l31.706,0Zm108.896,45.65c0,15.383 -4.095,27.196 -12.284,35.441c-8.189,8.245 -19.422,12.367 -33.698,12.367c-8.853,0 -16.711,-1.854 -23.572,-5.561c-6.861,-3.707 -12.256,-9.13 -16.185,-16.268c-3.929,-7.138 -5.893,-15.798 -5.893,-25.979c0,-15.161 4.095,-26.837 12.284,-35.026c8.189,-8.189 19.477,-12.284 33.864,-12.284c8.853,0 16.683,1.826 23.489,5.478c6.806,3.652 12.173,8.992 16.102,16.019c3.929,7.027 5.893,15.632 5.893,25.813Zm-59.428,0c0,7.857 1.051,13.861 3.154,18.011c2.103,4.15 5.644,6.225 10.624,6.225c4.869,0 8.328,-2.075 10.375,-6.225c2.047,-4.15 3.071,-10.154 3.071,-18.011c0,-7.857 -1.024,-13.778 -3.071,-17.762c-2.047,-3.984 -5.561,-5.976 -10.541,-5.976c-4.759,0 -8.217,1.992 -10.375,5.976c-2.158,3.984 -3.237,9.905 -3.237,17.762Z" style="fill:#fff;"/><path d="M911.933,687.092l-61.752,0l0,-18.26l14.774,-5.644l0,-70.882l-14.774,-5.478l0,-18.26l61.752,0l0,18.26l-14.774,5.478l0,70.882l14.774,5.644l0,18.26Zm127.82,-93.458c10.513,0 18.537,2.656 24.07,7.968c5.533,5.312 8.3,13.889 8.3,25.73l0,59.76l-31.706,0l0,-49.966c0,-6.972 -0.941,-11.814 -2.822,-14.525c-1.881,-2.711 -4.648,-4.067 -8.3,-4.067c-5.091,0 -8.632,2.241 -10.624,6.723c-1.992,4.482 -2.988,10.818 -2.988,19.007l0,42.828l-31.706,0l0,-49.966c0,-6.64 -0.885,-11.399 -2.656,-14.276c-1.771,-2.877 -4.427,-4.316 -7.968,-4.316c-5.423,0 -9.102,2.435 -11.039,7.304c-1.937,4.869 -2.905,11.897 -2.905,21.082l0,40.172l-31.706,0l0,-91.798l23.904,0l4.648,11.288l0.83,0c2.435,-3.763 5.948,-6.861 10.541,-9.296c4.593,-2.435 10.264,-3.652 17.015,-3.652c6.751,0 12.312,1.134 16.683,3.403c4.371,2.269 7.94,5.395 10.707,9.379l0.996,0c2.877,-4.095 6.612,-7.249 11.205,-9.462c4.593,-2.213 9.766,-3.32 15.521,-3.32Zm97.94,0c11.62,0 20.695,2.877 27.224,8.632c6.529,5.755 9.794,13.944 9.794,24.568l0,60.258l-21.912,0l-6.142,-12.118l-0.664,0c-2.545,3.209 -5.174,5.838 -7.885,7.885c-2.711,2.047 -5.838,3.541 -9.379,4.482c-3.541,0.941 -7.857,1.411 -12.948,1.411c-7.968,0 -14.608,-2.435 -19.92,-7.304c-5.312,-4.869 -7.968,-12.339 -7.968,-22.41c0,-9.849 3.403,-17.153 10.209,-21.912c6.806,-4.759 16.683,-7.415 29.631,-7.968l15.272,-0.498l0,-1.328c0,-4.095 -0.996,-7 -2.988,-8.715c-1.992,-1.715 -4.703,-2.573 -8.134,-2.573c-3.652,0 -7.691,0.636 -12.118,1.909c-4.427,1.273 -8.909,2.905 -13.446,4.897l-9.13,-20.916c5.312,-2.767 11.316,-4.842 18.011,-6.225c6.695,-1.383 14.193,-2.075 22.493,-2.075Zm-1.826,52.788c-5.755,0.221 -9.822,1.245 -12.201,3.071c-2.379,1.826 -3.569,4.399 -3.569,7.719c0,3.099 0.83,5.395 2.49,6.889c1.66,1.494 3.873,2.241 6.64,2.241c3.873,0 7.166,-1.217 9.877,-3.652c2.711,-2.435 4.067,-5.589 4.067,-9.462l0,-7.138l-7.304,0.332Zm88.976,-52.788c6.419,0 11.592,1.245 15.521,3.735c3.929,2.49 7.11,5.561 9.545,9.213l0.664,0l2.324,-11.288l27.39,0l0,91.964c0,12.948 -4.012,22.797 -12.035,29.548c-8.023,6.751 -20.335,10.126 -36.935,10.126c-7.415,0 -13.944,-0.387 -19.588,-1.162c-5.644,-0.775 -11.067,-2.158 -16.268,-4.15l0,-26.394c5.533,2.324 10.79,4.067 15.77,5.229c4.98,1.162 11.067,1.743 18.26,1.743c12.727,0 19.09,-4.537 19.09,-13.612l0,-1.66c0,-3.209 0.332,-7.027 0.996,-11.454l-0.996,0c-2.103,3.652 -5.091,6.778 -8.964,9.379c-3.873,2.601 -8.964,3.901 -15.272,3.901c-9.739,0 -17.651,-4.039 -23.738,-12.118c-6.087,-8.079 -9.13,-19.865 -9.13,-35.358c0,-15.493 3.099,-27.307 9.296,-35.441c6.197,-8.134 14.221,-12.201 24.07,-12.201Zm11.952,24.568c-8.743,0 -13.114,7.857 -13.114,23.572c0,7.968 1.107,13.695 3.32,17.181c2.213,3.486 5.644,5.229 10.292,5.229c5.201,0 8.798,-1.632 10.79,-4.897c1.992,-3.265 2.988,-8.162 2.988,-14.691l0,-3.818c0,-7.193 -0.968,-12.754 -2.905,-16.683c-1.937,-3.929 -5.727,-5.893 -11.371,-5.893Zm105.908,-24.568c13.391,0 23.959,3.486 31.706,10.458c7.747,6.972 11.62,17.485 11.62,31.54l0,14.11l-56.606,0c0.221,4.759 2.02,8.687 5.395,11.786c3.375,3.099 8.272,4.648 14.691,4.648c5.755,0 11.011,-0.553 15.77,-1.66c4.759,-1.107 9.683,-2.877 14.774,-5.312l0,22.742c-4.427,2.324 -9.268,4.039 -14.525,5.146c-5.257,1.107 -11.869,1.66 -19.837,1.66c-9.296,0 -17.568,-1.632 -24.817,-4.897c-7.249,-3.265 -12.976,-8.383 -17.181,-15.355c-4.205,-6.972 -6.308,-15.881 -6.308,-26.726c0,-11.067 1.909,-20.141 5.727,-27.224c3.818,-7.083 9.13,-12.339 15.936,-15.77c6.806,-3.431 14.691,-5.146 23.655,-5.146Zm1.162,21.58c-3.763,0 -6.889,1.162 -9.379,3.486c-2.49,2.324 -3.956,6.031 -4.399,11.122l27.224,0c-0.111,-4.095 -1.3,-7.553 -3.569,-10.375c-2.269,-2.822 -5.561,-4.233 -9.877,-4.233Zm127.488,43.658c0,5.755 -1.3,10.901 -3.901,15.438c-2.601,4.537 -6.751,8.079 -12.45,10.624c-5.699,2.545 -13.197,3.818 -22.493,3.818c-6.529,0 -12.367,-0.36 -17.513,-1.079c-5.146,-0.719 -10.375,-2.075 -15.687,-4.067l0,-25.398c5.865,2.656 11.786,4.565 17.762,5.727c5.976,1.162 10.679,1.743 14.11,1.743c6.861,0 10.292,-1.549 10.292,-4.648c0,-1.328 -0.553,-2.49 -1.66,-3.486c-1.107,-0.996 -3.071,-2.103 -5.893,-3.32c-2.822,-1.217 -6.834,-2.877 -12.035,-4.98c-7.636,-3.209 -13.335,-6.889 -17.098,-11.039c-3.763,-4.15 -5.644,-9.988 -5.644,-17.513c0,-8.964 3.458,-15.715 10.375,-20.252c6.917,-4.537 16.074,-6.806 27.473,-6.806c6.087,0 11.786,0.664 17.098,1.992c5.312,1.328 10.845,3.265 16.6,5.81l-8.632,20.418c-4.537,-2.103 -9.13,-3.763 -13.778,-4.98c-4.648,-1.217 -8.355,-1.826 -11.122,-1.826c-5.201,0 -7.802,1.273 -7.802,3.818c0,1.107 0.47,2.103 1.411,2.988c0.941,0.885 2.739,1.881 5.395,2.988c2.656,1.107 6.529,2.711 11.62,4.814c5.312,2.103 9.711,4.399 13.197,6.889c3.486,2.49 6.087,5.478 7.802,8.964c1.715,3.486 2.573,7.94 2.573,13.363Z" style="fill:#fff;"/></g><defs><radialGradient id="_Radial1" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0,1022.208,-1679.52,0,473.39383,271.872181)"><stop offset="0" style="stop-color:#2f2f2f;stop-opacity:0.98"/><stop offset="1" style="stop-color:#111;stop-opacity:0.98"/></radialGradient></defs></svg>
\ No newline at end of file diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index 78be5804e3..23f8d89c67 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -56,7 +54,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets /// <inheritdoc /> public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - var tmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + item.TryGetTmdbId(out var tmdbId); if (tmdbId <= 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index a7bba2d539..0a75b71264 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -42,7 +41,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets /// <inheritdoc /> public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + searchInfo.TryGetTmdbId(out var tmdbId); var language = searchInfo.MetadataLanguage; if (tmdbId > 0) @@ -97,7 +96,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets /// <inheritdoc /> public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo info, CancellationToken cancellationToken) { - var tmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + info.TryGetTmdbId(out var tmdbId); var language = info.MetadataLanguage; // We don't already have an Id, need to fetch it @@ -115,7 +114,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets } } - var result = new MetadataResult<BoxSet>(); + var result = new MetadataResult<BoxSet> + { + ResultLanguage = language + }; if (tmdbId > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs index f11b1d95aa..b3a67189bb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1819 // Properties should not return arrays + using MediaBrowser.Model.Plugins; namespace MediaBrowser.Providers.Plugins.Tmdb @@ -34,6 +36,51 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public bool ImportSeasonName { get; set; } /// <summary> + /// Gets or sets a value indicating whether unaired (upcoming) episodes should be created as + /// virtual items from the episode list provided by TMDb. These populate the "Upcoming" view. + /// Enabling this will increase scan times. + /// </summary> + public bool ImportUnairedEpisodes { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether already aired episodes that are not present in the + /// library should be created as virtual items from the episode list provided by TMDb. These + /// surface as missing episodes. Enabling this will increase scan times. + /// </summary> + public bool ImportMissingEpisodes { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether specials (season 0) should be included when creating + /// virtual unaired or missing episodes. When disabled, specials are never added and any existing + /// virtual specials created by this provider are removed. + /// </summary> + public bool ImportSpecials { get; set; } + + /// <summary> + /// Gets or sets the ids (the "N" formatted GUIDs from <c>VirtualFolderInfo.ItemId</c>) of the + /// libraries for which the unaired/missing episode provider is enabled. Whether episodes are + /// imported at all, and how, is still controlled by the global toggles above; those toggles only + /// apply to the libraries listed here. Libraries not listed, including newly added ones, are + /// never processed, so an empty list disables the feature entirely. + /// </summary> + public string[] EnabledMissingEpisodeLibraries { get; set; } = []; + + /// <summary> + /// Gets or sets how often, in days, the scheduled task re-checks TMDb for newly announced + /// unaired or missing episodes. This is what keeps the "Upcoming" view current for series + /// whose local files have not changed. + /// </summary> + public int MissingEpisodeRefreshIntervalDays { get; set; } = 7; + + /// <summary> + /// Gets or sets the number of days a virtual episode is retained after it airs before it is + /// pruned (when missing episode import is disabled). This grace period leaves recently aired + /// episodes in place to allow for the delay between an episode airing and its file being added + /// to the library. + /// </summary> + public int UpcomingEpisodeGracePeriodDays { get; set; } = 7; + + /// <summary> /// Gets or sets a value indicating the maximum number of cast members to fetch for an item. /// </summary> public int MaxCastMembers { get; set; } = 15; @@ -77,5 +124,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// Gets or sets a value indicating the still image size to fetch. /// </summary> public string? StillSize { get; set; } + + /// <summary> + /// Gets or sets the cache duration in days for similar item results. A value of 0 disables caching. + /// </summary> + public int SimilarItemsCacheDays { get; set; } = 7; } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html index 89d380ec1f..582753759f 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html @@ -25,6 +25,40 @@ <input is="emby-checkbox" type="checkbox" id="importSeasonName" /> <span>Import season name from metadata fetched for series.</span> </label> + <div class="checkboxContainer checkboxContainer-withDescription"> + <label> + <input is="emby-checkbox" type="checkbox" id="importUnairedEpisodes" /> + <span>Create unaired (upcoming) episodes from metadata fetched for series.</span> + </label> + <div class="fieldDescription checkboxFieldDescription">Adds virtual entries for episodes listed on TMDb that have not aired yet. This populates the "Upcoming" view. Specials are never added to the "Upcoming" view.</div> + </div> + <div class="checkboxContainer checkboxContainer-withDescription"> + <label> + <input is="emby-checkbox" type="checkbox" id="importMissingEpisodes" /> + <span>Create missing episodes from metadata fetched for series.</span> + </label> + <div class="fieldDescription checkboxFieldDescription">Adds virtual entries for episodes listed on TMDb that have already aired but are not present in your library. Missing episodes are only shown when enabled in the user display preferences. Both options increase scan times.</div> + </div> + <div class="checkboxContainer checkboxContainer-withDescription"> + <label> + <input is="emby-checkbox" type="checkbox" id="importSpecials" /> + <span>Include specials when creating unaired and missing episodes.</span> + </label> + <div class="fieldDescription checkboxFieldDescription">When disabled, specials (season 0) are never added as virtual entries and any existing virtual specials are removed.</div> + </div> + <div class="inputContainer inputContainer-withDescription"> + <input is="emby-input" type="number" id="missingEpisodeRefreshIntervalDays" pattern="[0-9]*" required min="1" max="365" label="Episode refresh interval (days)" /> + <div class="fieldDescription">How often the scheduled task re-checks TMDb for newly announced unaired or missing episodes. Keeps the "Upcoming" view current for series whose local files have not changed.</div> + </div> + <div class="inputContainer inputContainer-withDescription"> + <input is="emby-input" type="number" id="upcomingEpisodeGracePeriodDays" pattern="[0-9]*" required min="0" max="365" label="Recently aired grace period (days)" /> + <div class="fieldDescription">When missing episodes are disabled, how many days a recently aired episode is kept in place before its placeholder is removed. This allows for the delay between an episode airing and its file being added to the library.</div> + </div> + <div class="verticalSection"> + <h2>Libraries</h2> + <div class="fieldDescription" style="margin-bottom:1em;">Choose which TV libraries the unaired/missing episode options above apply to. The settings above are global; this only controls which libraries they run for. Libraries are opted in individually, so newly added libraries are not processed until they are enabled here.</div> + <div id="missingEpisodeLibraries"></div> + </div> <div class="verticalSection"> <h2>Cast & Crew Settings</h2> <div class="inputContainer"> @@ -44,6 +78,13 @@ <span>Hide crew members without profile images.</span> </label> </div> + <div class="verticalSection"> + <h2>Similar Items</h2> + <div class="inputContainer"> + <input is="emby-input" type="number" id="similarItemsCacheDays" pattern="[0-9]*" required min="0" max="365" label="Cache duration (days)" /> + <div class="fieldDescription">Number of days to cache similar item results from TMDb. Set to 0 to disable caching.</div> + </div> + </div> <div class="verticalSection verticalSection-extrabottompadding"> <h2>Image Scaling</h2> <div class="selectContainer"> @@ -78,6 +119,29 @@ Dashboard.showLoadingMsg(); var clientConfig, pluginConfig; + var populateMissingEpisodeLibraries = function (enabledLibraries) { + var container = document.querySelector('#missingEpisodeLibraries'); + ApiClient.getVirtualFolders().then(function (folders) { + // Series only live in TV libraries (and mixed-content libraries, which report + // no collection type), so only those are worth listing here. + var tvLibraries = folders.filter(function (folder) { + return !folder.CollectionType || folder.CollectionType === 'tvshows'; + }); + + if (tvLibraries.length === 0) { + container.innerHTML = '<div class="fieldDescription">No TV libraries found.</div>'; + return; + } + + container.innerHTML = tvLibraries.map(function (folder) { + var checked = enabledLibraries.indexOf(folder.ItemId) === -1 ? '' : ' checked'; + return '<label class="checkboxContainer">' + + '<input is="emby-checkbox" type="checkbox" class="missingEpisodeLibrary" data-library-id="' + folder.ItemId + '"' + checked + ' />' + + '<span>' + folder.Name + '</span>' + + '</label>'; + }).join(''); + }); + } var configureImageScaling = function() { if (clientConfig === undefined || pluginConfig === undefined) { return; @@ -144,9 +208,16 @@ document.querySelector('#excludeTagsSeries').checked = config.ExcludeTagsSeries; document.querySelector('#excludeTagsMovies').checked = config.ExcludeTagsMovies; document.querySelector('#importSeasonName').checked = config.ImportSeasonName; + document.querySelector('#importUnairedEpisodes').checked = config.ImportUnairedEpisodes; + document.querySelector('#importMissingEpisodes').checked = config.ImportMissingEpisodes; + document.querySelector('#importSpecials').checked = config.ImportSpecials; + document.querySelector('#missingEpisodeRefreshIntervalDays').value = config.MissingEpisodeRefreshIntervalDays; + document.querySelector('#upcomingEpisodeGracePeriodDays').value = config.UpcomingEpisodeGracePeriodDays; document.querySelector('#hideMissingCastMembers').checked = config.HideMissingCastMembers; document.querySelector('#hideMissingCrewMembers').checked = config.HideMissingCrewMembers; + populateMissingEpisodeLibraries(config.EnabledMissingEpisodeLibraries || []); + var maxCastMembers = document.querySelector('#maxCastMembers'); maxCastMembers.value = config.MaxCastMembers; maxCastMembers.dispatchEvent(new Event('change', { @@ -161,6 +232,13 @@ cancelable: false })); + var similarItemsCacheDays = document.querySelector('#similarItemsCacheDays'); + similarItemsCacheDays.value = config.SimilarItemsCacheDays; + similarItemsCacheDays.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + pluginConfig = config; configureImageScaling(); }); @@ -175,10 +253,22 @@ config.ExcludeTagsSeries = document.querySelector('#excludeTagsSeries').checked; config.ExcludeTagsMovies = document.querySelector('#excludeTagsMovies').checked; config.ImportSeasonName = document.querySelector('#importSeasonName').checked; + config.ImportUnairedEpisodes = document.querySelector('#importUnairedEpisodes').checked; + config.ImportMissingEpisodes = document.querySelector('#importMissingEpisodes').checked; + config.ImportSpecials = document.querySelector('#importSpecials').checked; + config.MissingEpisodeRefreshIntervalDays = parseInt(document.querySelector('#missingEpisodeRefreshIntervalDays').value, 10); + config.UpcomingEpisodeGracePeriodDays = parseInt(document.querySelector('#upcomingEpisodeGracePeriodDays').value, 10); + var libraryCheckboxes = document.querySelectorAll('.missingEpisodeLibrary'); + if (libraryCheckboxes.length > 0) { + config.EnabledMissingEpisodeLibraries = Array.prototype.filter + .call(libraryCheckboxes, function (checkbox) { return checkbox.checked; }) + .map(function (checkbox) { return checkbox.getAttribute('data-library-id'); }); + } config.MaxCastMembers = document.querySelector('#maxCastMembers').value; config.MaxCrewMembers = document.querySelector('#maxCrewMembers').value; config.HideMissingCastMembers = document.querySelector('#hideMissingCastMembers').checked; config.HideMissingCrewMembers = document.querySelector('#hideMissingCrewMembers').checked; + config.SimilarItemsCacheDays = parseInt(document.querySelector('#similarItemsCacheDays').value, 10); config.PosterSize = document.querySelector('#selectPosterSize').value; config.BackdropSize = document.querySelector('#selectBackdropSize').value; config.LogoSize = document.querySelector('#selectLogoSize').value; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index 714c57d361..e686577311 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -61,7 +59,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var language = item.GetPreferredMetadataLanguage(); var countryCode = item.GetPreferredMetadataCountryCode(); - var movieTmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + item.TryGetTmdbId(out var movieTmdbId); if (movieTmdbId <= 0) { var movieImdbId = item.GetProviderId(MetadataProvider.Imdb); @@ -95,7 +93,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var posters = movie.Images.Posters; var backdrops = movie.Images.Backdrops; var logos = movie.Images.Logos; - var remoteImages = new List<RemoteImageInfo>(posters?.Count ?? 0 + backdrops?.Count ?? 0 + logos?.Count ?? 0); + var remoteImages = new List<RemoteImageInfo>((posters?.Count ?? 0) + (backdrops?.Count ?? 0) + (logos?.Count ?? 0)); if (posters is not null) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 8811a1787a..ef952082da 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -54,11 +54,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// <inheritdoc /> public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken) { - if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var id)) + if (searchInfo.TryGetTmdbId(out var tmdbId)) { var movie = await _tmdbClientManager .GetMovieAsync( - int.Parse(id, CultureInfo.InvariantCulture), + tmdbId, searchInfo.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode), searchInfo.MetadataCountryCode, @@ -90,7 +90,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies } IReadOnlyList<SearchMovie>? movieResults = null; - if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out id)) + if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out var id)) { var result = await _tmdbClientManager.FindByExternalIdAsync( id, @@ -151,11 +151,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// <inheritdoc /> public async Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken) { - var tmdbId = info.GetProviderId(MetadataProvider.Tmdb); + // A stored id that is not a TMDb id is treated as no id, so the search below can repair it + // rather than the lookup failing for as long as the bad id stays on the item. + info.TryGetTmdbId(out var tmdbId); var imdbId = info.GetProviderId(MetadataProvider.Imdb); var config = Plugin.Instance.Configuration; - if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId)) + if (tmdbId <= 0 && string.IsNullOrEmpty(imdbId)) { // ParseName is required here. // Caller provides the filename with extension stripped and NOT the parsed filename @@ -166,26 +168,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (searchResults?.Count > 0) { - tmdbId = searchResults[0].Id.ToString(CultureInfo.InvariantCulture); + tmdbId = searchResults[0].Id; } } - if (string.IsNullOrEmpty(tmdbId) && !string.IsNullOrEmpty(imdbId)) + if (tmdbId <= 0 && !string.IsNullOrEmpty(imdbId)) { var movieResultFromImdbId = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (movieResultFromImdbId?.MovieResults?.Count > 0) { - tmdbId = movieResultFromImdbId.MovieResults[0].Id.ToString(CultureInfo.InvariantCulture); + tmdbId = movieResultFromImdbId.MovieResults[0].Id; } } - if (string.IsNullOrEmpty(tmdbId)) + if (tmdbId <= 0) { return new MetadataResult<Movie>(); } var movieResult = await _tmdbClientManager - .GetMovieAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) + .GetMovieAsync(tmdbId, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (movieResult is null) @@ -208,7 +210,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies Item = movie }; - movie.SetProviderId(MetadataProvider.Tmdb, tmdbId); + movie.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString(CultureInfo.InvariantCulture)); movie.TrySetProviderId(MetadataProvider.Imdb, movieResult.ImdbId); if (movieResult.BelongsToCollection is not null) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieSimilarProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieSimilarProvider.cs new file mode 100644 index 0000000000..5206de78ce --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieSimilarProvider.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Threading; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using Microsoft.Extensions.Logging; +using Movie = MediaBrowser.Controller.Entities.Movies.Movie; + +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies; + +/// <summary> +/// TMDb-based similar items provider for movies. +/// </summary> +public class TmdbMovieSimilarProvider : IRemoteSimilarItemsProvider<Movie> +{ + private readonly TmdbClientManager _tmdbClientManager; + private readonly ILogger<TmdbMovieSimilarProvider> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="TmdbMovieSimilarProvider"/> class. + /// </summary> + /// <param name="tmdbClientManager">The TMDb client manager.</param> + /// <param name="logger">The logger.</param> + public TmdbMovieSimilarProvider(TmdbClientManager tmdbClientManager, ILogger<TmdbMovieSimilarProvider> logger) + { + _tmdbClientManager = tmdbClientManager; + _logger = logger; + } + + /// <inheritdoc/> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc/> + public MetadataPluginType Type => MetadataPluginType.SimilarityProvider; + + /// <inheritdoc/> + public TimeSpan? CacheDuration + { + get + { + var days = Plugin.Instance?.Configuration.SimilarItemsCacheDays ?? 0; + return days > 0 ? TimeSpan.FromDays(days) : null; + } + } + + /// <inheritdoc/> + public async IAsyncEnumerable<SimilarItemReference> GetSimilarItemsAsync( + Movie item, + SimilarItemsQuery query, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + if (!item.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbIdStr) || !int.TryParse(tmdbIdStr, CultureInfo.InvariantCulture, out var tmdbId)) + { + yield break; + } + + var providerName = MetadataProvider.Tmdb.ToString(); + var page = 0; + var totalPages = 1; + + while (page <= totalPages && !cancellationToken.IsCancellationRequested) + { + IReadOnlyList<TMDbLib.Objects.Search.SearchMovie> pageResults; + try + { + (pageResults, totalPages) = await _tmdbClientManager + .GetMovieSimilarPageAsync(tmdbId, page, TmdbUtils.GetImageLanguagesParam(string.Empty), cancellationToken) + .ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to get similar movies from TMDb for {TmdbId} page {Page}", tmdbId, page); + yield break; + } + + if (pageResults.Count == 0) + { + yield break; + } + + foreach (var similar in pageResults) + { + yield return new SimilarItemReference + { + ProviderName = providerName, + ProviderId = similar.Id.ToString(CultureInfo.InvariantCulture) + }; + } + + page++; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index 33888ddf4f..d38614811c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -54,14 +53,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People { var person = (Person)item; - if (!person.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) + if (!person.TryGetTmdbId(out var personTmdbId)) { return Enumerable.Empty<RemoteImageInfo>(); } var language = item.GetPreferredMetadataLanguage(); var countryCode = item.GetPreferredMetadataCountryCode(); - var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), language, countryCode, cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, language, countryCode, cancellationToken).ConfigureAwait(false); if (personResult?.Images?.Profiles is null) { return Enumerable.Empty<RemoteImageInfo>(); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 64ab98b262..695f347a9a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using System.Net.Http; @@ -37,9 +36,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People /// <inheritdoc /> public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken) { - if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) + if (searchInfo.TryGetTmdbId(out var personTmdbId)) { - var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (personResult is not null) { @@ -89,7 +88,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People /// <inheritdoc /> public async Task<MetadataResult<Person>> GetMetadata(PersonLookupInfo info, CancellationToken cancellationToken) { - var personTmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + // A person can carry another provider's id under the TMDb key, which is no more usable here + // than no id at all, so both take the search path and get the stored id repaired. + info.TryGetTmdbId(out var personTmdbId); // We don't already have an Id, need to fetch it if (personTmdbId <= 0) @@ -101,7 +102,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People } } - var result = new MetadataResult<Person>(); + var result = new MetadataResult<Person> + { + ResultLanguage = info.MetadataLanguage + }; if (personTmdbId > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Plugin.cs b/MediaBrowser.Providers/Plugins/Tmdb/Plugin.cs index 4adde8366a..04f1fd04a3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Plugin.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; @@ -12,7 +13,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <summary> /// Plugin class for the TMDb library. /// </summary> - public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages + public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage { /// <summary> /// Initializes a new instance of the <see cref="Plugin"/> class. @@ -44,6 +45,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <inheritdoc/> public override string ConfigurationFileName => "Jellyfin.Plugin.Tmdb.xml"; + /// <inheritdoc/> + public string ImageResourceName => GetType().Namespace + ".jellyfin-plugin-tmdb.svg"; + /// <summary> /// Return the plugin configuration page. /// </summary> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 7ae54cdcd3..1f8c87397d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -56,9 +54,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var episode = (Controller.Entities.TV.Episode)item; var series = episode.Series; - var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + var seriesTmdbId = 0; - if (series is null || seriesTmdbId <= 0) + if (series?.TryGetTmdbId(out seriesTmdbId) != true) { return Enumerable.Empty<RemoteImageInfo>(); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index f0e159f098..8172ab14df 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -91,8 +91,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? tmdbId); - var seriesTmdbId = Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture); - if (seriesTmdbId <= 0) + if (!TmdbUtils.TryParseTmdbId(tmdbId, out var seriesTmdbId)) { return metadataResult; } @@ -181,7 +180,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV ParentIndexNumber = seasonNumber, IndexNumberEnd = info.IndexNumberEnd, Name = episodeResult.Name, - PremiereDate = episodeResult.AirDate, + PremiereDate = episodeResult.AirDate.HasValue + ? DateTime.SpecifyKind(episodeResult.AirDate.Value, DateTimeKind.Local).ToUniversalTime() + : null, ProductionYear = episodeResult.AirDate?.Year, Overview = episodeResult.Overview, CommunityRating = Convert.ToSingle(episodeResult.VoteAverage) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbMissingEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbMissingEpisodeProvider.cs new file mode 100644 index 0000000000..b44361e4e7 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbMissingEpisodeProvider.cs @@ -0,0 +1,663 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using Microsoft.Extensions.Logging; +using TMDbLib.Objects.Search; + +namespace MediaBrowser.Providers.Plugins.Tmdb.TV +{ + /// <summary> + /// Creates virtual (metadata-only) entries for missing and unaired episodes. + /// </summary> + public class TmdbMissingEpisodeProvider : ICustomMetadataProvider<Series>, IHasItemChangeMonitor, IHasOrder + { + private readonly TmdbClientManager _tmdbClientManager; + private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; + private readonly IProviderManager _providerManager; + private readonly ILogger<TmdbMissingEpisodeProvider> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="TmdbMissingEpisodeProvider"/> class. + /// </summary> + /// <param name="tmdbClientManager">The <see cref="TmdbClientManager"/>.</param> + /// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param> + /// <param name="fileSystem">The <see cref="IFileSystem"/>.</param> + /// <param name="providerManager">The <see cref="IProviderManager"/>.</param> + /// <param name="logger">The <see cref="ILogger{TmdbMissingEpisodeProvider}"/>.</param> + public TmdbMissingEpisodeProvider( + TmdbClientManager tmdbClientManager, + ILibraryManager libraryManager, + IFileSystem fileSystem, + IProviderManager providerManager, + ILogger<TmdbMissingEpisodeProvider> logger) + { + _tmdbClientManager = tmdbClientManager; + _libraryManager = libraryManager; + _fileSystem = fileSystem; + _providerManager = providerManager; + _logger = logger; + } + + /// <inheritdoc /> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc /> + // Run after the remote series provider so the TMDb id and other metadata are available. + public int Order => 100; + + /// <inheritdoc /> + public bool HasChanged(BaseItem item, IDirectoryService directoryService) + { + // Reporting a change makes this provider (and only this provider) run during an otherwise incremental refresh. + if (Plugin.Instance?.Configuration is null) + { + return false; + } + + return item is Series series && series.HasProviderId(MetadataProvider.Tmdb); + } + + /// <inheritdoc /> + public async Task<ItemUpdateType> FetchAsync(Series item, MetadataRefreshOptions options, CancellationToken cancellationToken) + { + var configuration = Plugin.Instance?.Configuration; + var importUnaired = (configuration?.ImportUnairedEpisodes).GetValueOrDefault(); + var importMissing = (configuration?.ImportMissingEpisodes).GetValueOrDefault(); + + // The provider is inactive for this series when both global imports are off, or the series' + // library has not been opted in. In either case remove every virtual episode (unaired and + // missing alike) it previously created, so disabling the feature cleans up on the next scan. + if ((!importUnaired && !importMissing) || !IsEnabledForLibrary(item)) + { + if (!PruneAllVirtualEpisodes(item)) + { + return ItemUpdateType.None; + } + + item.Children = null; + return ItemUpdateType.MetadataImport; + } + + var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); + if (string.IsNullOrEmpty(tmdbId) + || !int.TryParse(tmdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var seriesTmdbId) + || seriesTmdbId <= 0) + { + return ItemUpdateType.None; + } + + var language = item.GetPreferredMetadataLanguage(); + var countryCode = item.GetPreferredMetadataCountryCode(); + var imageLanguages = TmdbUtils.GetImageLanguagesParam(language, countryCode); + + var tmdbSeries = await _tmdbClientManager + .GetSeriesAsync(seriesTmdbId, language, imageLanguages, countryCode, cancellationToken) + .ConfigureAwait(false); + + if (tmdbSeries?.Seasons is null) + { + return ItemUpdateType.None; + } + + var today = DateTime.UtcNow.Date; + + var importSpecials = (configuration?.ImportSpecials).GetValueOrDefault(); + var gracePeriodDays = Math.Max(0, (configuration?.UpcomingEpisodeGracePeriodDays).GetValueOrDefault()); + + // Track every (season, episode) number that already exists (physical or virtual) so we never + // create a duplicate. + // When missing episodes are disabled, this pass also prunes virtual episodes that aired more + // than the grace period ago, as well as any specials when specials are not wanted. + var (existingEpisodes, updatableEpisodes) = GetExistingEpisodes(item, !importMissing, today, gracePeriodDays, importSpecials, out var prunedEpisodes); + + var seasonsByNumber = item.GetRecursiveChildren(i => i is Season) + .OfType<Season>() + .Where(s => s.IndexNumber.HasValue) + .GroupBy(s => s.IndexNumber!.Value) + .ToDictionary(g => g.Key, g => g.First()); + + var addedEpisodes = false; + var updatedEpisodes = false; + + foreach (var seasonInfo in tmdbSeries.Seasons) + { + cancellationToken.ThrowIfCancellationRequested(); + + var seasonNumber = seasonInfo.SeasonNumber; + var tmdbSeason = await _tmdbClientManager + .GetSeasonAsync(seriesTmdbId, seasonNumber, language, imageLanguages, countryCode, cancellationToken) + .ConfigureAwait(false); + + if (tmdbSeason?.Episodes is null) + { + continue; + } + + foreach (var tmdbEpisode in tmdbSeason.Episodes) + { + var episodeNumber = (int)tmdbEpisode.EpisodeNumber; + var premiereDate = GetPremiereDate(tmdbEpisode); + + // Skips undated episodes, unaired (upcoming) ones unless upcoming import is enabled, + // already aired ones unless missing import is enabled, and unaired specials entirely. + if (!ShouldImportEpisode(premiereDate, today, importUnaired, importMissing, seasonNumber == 0, importSpecials)) + { + continue; + } + + var key = (seasonNumber, episodeNumber); + + // Already have a virtual episode this provider created, keep metadata in sync with TMDb. + if (updatableEpisodes.TryGetValue(key, out var existingEpisode)) + { + var season = await GetOrCreateSeasonAsync(item, seasonNumber, tmdbSeason.Name, seasonsByNumber, cancellationToken).ConfigureAwait(false); + var changed = UpdateVirtualEpisode(existingEpisode, tmdbEpisode, premiereDate); + + if (!existingEpisode.ParentId.Equals(season.Id)) + { + existingEpisode.SetParent(season); + existingEpisode.SeasonId = season.Id; + existingEpisode.SeasonName = season.Name; + changed = true; + } + + if (string.IsNullOrEmpty(existingEpisode.PresentationUniqueKey)) + { + existingEpisode.PresentationUniqueKey = existingEpisode.CreatePresentationUniqueKey(); + changed = true; + } + + if (changed) + { + await existingEpisode.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); + updatedEpisodes = true; + } + + // Backfill the still for placeholders created before images were fetched. + if (await EnsureEpisodeImageAsync(existingEpisode, tmdbEpisode, cancellationToken).ConfigureAwait(false)) + { + updatedEpisodes = true; + } + + continue; + } + + if (!existingEpisodes.Add(key)) + { + continue; + } + + var targetSeason = await GetOrCreateSeasonAsync(item, seasonNumber, tmdbSeason.Name, seasonsByNumber, cancellationToken).ConfigureAwait(false); + var newEpisode = AddVirtualEpisode(item, targetSeason, tmdbEpisode, premiereDate); + await EnsureEpisodeImageAsync(newEpisode, tmdbEpisode, cancellationToken).ConfigureAwait(false); + addedEpisodes = true; + } + } + + var alignedSeasons = await AlignVirtualSeasonSortNamesAsync(seasonsByNumber.Values, cancellationToken).ConfigureAwait(false); + + if (!addedEpisodes && !prunedEpisodes && !updatedEpisodes && !alignedSeasons) + { + return ItemUpdateType.None; + } + + // Invalidate the cached children so that the season creation / cleanup that runs later in + // SeriesMetadataService.AfterMetadataRefresh observes the newly created (and pruned) episodes. + item.Children = null; + + return ItemUpdateType.MetadataImport; + } + + /// <summary> + /// Returns the series' season with the given number, creating (and refreshing) a virtual season + /// when the whole season is missing from the library. + /// </summary> + private async Task<Season> GetOrCreateSeasonAsync(Series series, int seasonNumber, string? seasonName, Dictionary<int, Season> seasonsByNumber, CancellationToken cancellationToken) + { + if (seasonsByNumber.TryGetValue(seasonNumber, out var existingSeason)) + { + return existingSeason; + } + + _logger.LogInformation("Creating virtual season {SeasonNumber} for series {SeriesName}", seasonNumber, series.Name); + + var season = new Season + { + Name = seasonName, + IndexNumber = seasonNumber, + Id = _libraryManager.GetNewItemId( + series.Id.ToString("N", CultureInfo.InvariantCulture) + "Season" + seasonNumber.ToString(CultureInfo.InvariantCulture), + typeof(Season)), + IsVirtualItem = true, + SeriesId = series.Id, + SeriesName = series.Name, + SeriesPresentationUniqueKey = series.GetPresentationUniqueKey() + }; + + series.AddChild(season); + await season.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false); + + seasonsByNumber[seasonNumber] = season; + return season; + } + + /// <summary> + /// Mirrors physical seasons' name-based sort convention onto virtual seasons so they interleave by + /// number instead of jumping ahead. See <see cref="BuildSeasonSortNameTemplate"/> for the details. + /// </summary> + /// <param name="seasons">The series' seasons (physical and virtual).</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns><c>true</c> if any virtual season was updated; otherwise <c>false</c>.</returns> + private async Task<bool> AlignVirtualSeasonSortNamesAsync(IEnumerable<Season> seasons, CancellationToken cancellationToken) + { + var seasonList = seasons.ToList(); + var template = BuildSeasonSortNameTemplate(seasonList); + if (template is null) + { + // No physical season sorts by name: virtual seasons already share the bare-index key space. + return false; + } + + var updated = false; + foreach (var season in seasonList) + { + if (!season.IsVirtualItem || !season.IndexNumber.HasValue) + { + continue; + } + + var desired = template(season.IndexNumber.Value); + if (string.Equals(season.ForcedSortName, desired, StringComparison.Ordinal)) + { + continue; + } + + _logger.LogInformation( + "Aligning sort name of virtual season {SeasonNumber} in series {SeriesName} to {SortName}", + season.IndexNumber, + season.SeriesName, + desired); + + season.ForcedSortName = desired; + await season.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); + updated = true; + } + + return updated; + } + + /// <summary> + /// Builds a factory that maps a season number to a forced sort name mirroring a physical, + /// name-sorted sibling season, or <c>null</c> when no physical season sorts by name. + /// </summary> + /// <param name="seasons">The series' seasons (physical and virtual).</param> + /// <returns>A season-number-to-sort-name factory, or <c>null</c> if there is nothing to mirror.</returns> + internal static Func<int, string>? BuildSeasonSortNameTemplate(IEnumerable<Season> seasons) + { + // Season.CreateSortName sorts by the bare padded index ("0003"), but season NFOs give physical + // seasons a name-based forced sort ("Season 01" -> "season 0000000001"). The digit-leading key + // sorts ahead of the letter-leading one, so mirror the sibling's token with each season number. + var reference = seasons.FirstOrDefault(s => + !s.IsVirtualItem && s.IndexNumber.HasValue && !string.IsNullOrEmpty(s.ForcedSortName)); + if (reference is null) + { + return null; + } + + var forced = reference.ForcedSortName!; + + // Locate the last run of digits (the season number) in the sibling's forced sort name. + var end = -1; + var start = -1; + for (var i = forced.Length - 1; i >= 0; i--) + { + if (char.IsDigit(forced[i])) + { + end = end < 0 ? i : end; + start = i; + } + else if (end >= 0) + { + break; + } + } + + if (end < 0) + { + // Sibling has no numeric component to swap; leave virtual seasons on the bare-index key. + return null; + } + + var prefix = forced[..start]; + var suffix = forced[(end + 1)..]; + var width = end - start + 1; + + // The exact zero-padding is cosmetic: ModifySortChunks pads every digit run to 10 characters, + // so "Season 3" and "Season 03" collapse to the same sort key. Keeping the sibling's width just + // makes the stored value read naturally. + return number => prefix + + number.ToString(CultureInfo.InvariantCulture).PadLeft(width, '0') + + suffix; + } + + private bool IsEnabledForLibrary(BaseItem item) + { + var enabledLibraries = Plugin.Instance?.Configuration.EnabledMissingEpisodeLibraries; + if (enabledLibraries is null || enabledLibraries.Length == 0) + { + return false; + } + + // A series can live under more than one collection folder; opting in any one of them is + // enough. An item that belongs to no collection folder cannot be opted in at all. + return _libraryManager.GetCollectionFolders(item).Any(folder => + enabledLibraries.Contains(folder.Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)); + } + + private (HashSet<(int Season, int Episode)> Keys, Dictionary<(int Season, int Episode), Episode> Updatable) GetExistingEpisodes(Series series, bool pruneAgedOut, DateTime today, int gracePeriodDays, bool importSpecials, out bool pruned) + { + var keys = new HashSet<(int Season, int Episode)>(); + var updatable = new Dictionary<(int Season, int Episode), Episode>(); + var physicalKeys = new HashSet<(int Season, int Episode)>(); + var ourVirtuals = new List<((int Season, int Episode) Key, Episode Episode)>(); + pruned = false; + + // Enumerate by parent rather than via Series.GetEpisodes: on an initial scan the episodes' + // SeriesPresentationUniqueKey is not set yet, so the presentation-key based query would miss + // them. GetRecursiveChildren walks the actual child tree and sees them regardless. + foreach (var episode in series.GetRecursiveChildren(i => i is Episode).OfType<Episode>()) + { + // The series is refreshed before its episodes during an initial scan, so a freshly + // resolved physical episode may not have its numbers populated yet. Resolve them from + // the path (in memory, mirroring CreateSeasonsAsync) so we can dedupe against episodes + // the user actually has files for instead of creating virtual duplicates. + if (episode.IsFileProtocol && (!episode.ParentIndexNumber.HasValue || !episode.IndexNumber.HasValue)) + { + try + { + _libraryManager.FillMissingEpisodeNumbersFromPath(episode, false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error resolving episode number from path for {Path}", episode.Path); + } + } + + // Virtual episodes this provider created are candidates for metadata sync (and pruning). + var isOurs = episode.IsVirtualItem && episode.HasProviderId(MetadataProvider.Tmdb); + + if (ShouldPrune(episode, pruneAgedOut, today, gracePeriodDays, importSpecials)) + { + DeleteEpisode(episode, "no longer upcoming and missing episodes are disabled"); + pruned = true; + continue; + } + + if (episode.ParentIndexNumber.HasValue && episode.IndexNumber.HasValue) + { + var key = (episode.ParentIndexNumber.Value, episode.IndexNumber.Value); + keys.Add(key); + + // Defer the ours/physical reconciliation: an episode's virtual counterpart and its + // physical file can appear in either order while walking the tree, so we can only + // decide which of our virtual episodes are superseded once every episode is seen. + if (isOurs) + { + ourVirtuals.Add((key, episode)); + } + else if (!episode.IsVirtualItem) + { + physicalKeys.Add(key); + } + } + } + + // A physical file now exists for one of our placeholders: delete the placeholder here rather + // than updating it (and then leaving RemoveObsoleteEpisodes to delete it moments later). The + // physical key already blocks re-creation via the dedupe set above. + foreach (var (key, episode) in ourVirtuals) + { + if (physicalKeys.Contains(key)) + { + DeleteEpisode(episode, "a physical episode now exists for this slot"); + pruned = true; + } + else + { + // Virtual episodes this provider created are candidates for metadata sync. + updatable[key] = episode; + } + } + + return (keys, updatable); + } + + /// <summary> + /// Removes every virtual episode this provider previously created in the series. + /// </summary> + /// <param name="series">The series to clean up.</param> + /// <returns><c>true</c> if any episode was removed; otherwise <c>false</c>.</returns> + private bool PruneAllVirtualEpisodes(Series series) + { + var pruned = false; + foreach (var episode in series.GetRecursiveChildren(i => i is Episode).OfType<Episode>()) + { + if (episode.IsVirtualItem && episode.HasProviderId(MetadataProvider.Tmdb)) + { + DeleteEpisode(episode, "the TMDb missing episode provider is disabled for this library"); + pruned = true; + } + } + + return pruned; + } + + private void DeleteEpisode(Episode episode, string reason) + { + _logger.LogInformation( + "Removing virtual episode S{SeasonNumber}E{EpisodeNumber} in series {SeriesName}: {Reason}", + episode.ParentIndexNumber, + episode.IndexNumber, + episode.SeriesName, + reason); + + _libraryManager.DeleteItem( + episode, + new DeleteOptions { DeleteFileLocation = false }, + false); + } + + /// <summary> + /// Determines whether a TMDb episode should be imported as a virtual item, based on its air date + /// and the enabled options. Undated episodes are never imported; unaired (today or later) episodes + /// require <paramref name="importUnaired"/>; already aired episodes require <paramref name="importMissing"/>. + /// Specials (season 0) are only imported when <paramref name="importSpecials"/> is enabled. + /// </summary> + /// <param name="premiereDate">The episode air date (UTC), or null if unknown.</param> + /// <param name="today">The current UTC date.</param> + /// <param name="importUnaired">Whether unaired (upcoming) episodes should be imported.</param> + /// <param name="importMissing">Whether already aired missing episodes should be imported.</param> + /// <param name="isSpecial">Whether the episode belongs to the specials season (season 0).</param> + /// <param name="importSpecials">Whether specials should be included.</param> + /// <returns><c>true</c> if the episode should be imported; otherwise <c>false</c>.</returns> + internal static bool ShouldImportEpisode(DateTime? premiereDate, DateTime today, bool importUnaired, bool importMissing, bool isSpecial, bool importSpecials) + { + if (!premiereDate.HasValue) + { + return false; + } + + // Specials are only imported when the user opts in. + if (isSpecial && !importSpecials) + { + return false; + } + + var isUnaired = premiereDate.Value.Date >= today; + return isUnaired ? importUnaired : importMissing; + } + + /// <summary> + /// Determines whether an existing virtual episode created by this provider (carries a TMDb id) + /// should be pruned. Specials are removed entirely unless <paramref name="importSpecials"/> is + /// enabled. Otherwise, when missing episodes are not wanted, an entry is pruned once its air date + /// is more than <paramref name="gracePeriodDays"/> in the past; the grace period keeps recently + /// aired episodes in place to allow for the delay between an episode airing and its file being + /// added to the library. + /// </summary> + /// <param name="episode">The episode to evaluate.</param> + /// <param name="pruneAgedOut">Whether aged-out virtual episodes should be pruned (missing import disabled).</param> + /// <param name="today">The current UTC date.</param> + /// <param name="gracePeriodDays">The number of days an aired episode is retained before pruning.</param> + /// <param name="importSpecials">Whether specials should be kept.</param> + /// <returns><c>true</c> if the episode should be pruned; otherwise <c>false</c>.</returns> + internal static bool ShouldPrune(Episode episode, bool pruneAgedOut, DateTime today, int gracePeriodDays, bool importSpecials) + { + if (!episode.IsVirtualItem || !episode.HasProviderId(MetadataProvider.Tmdb)) + { + return false; + } + + // Specials are removed entirely unless the user opts in. + if (episode.ParentIndexNumber == 0 && !importSpecials) + { + return true; + } + + // When missing episodes are not wanted, prune placeholders for episodes that aired more than + // the grace period ago. + return pruneAgedOut + && episode.PremiereDate.HasValue + && episode.PremiereDate.Value.Date < today.AddDays(-gracePeriodDays); + } + + internal static DateTime? GetPremiereDate(TvSeasonEpisode tmdbEpisode) + { + return tmdbEpisode.AirDate.HasValue + ? DateTime.SpecifyKind(tmdbEpisode.AirDate.Value, DateTimeKind.Local).ToUniversalTime() + : null; + } + + internal static bool UpdateVirtualEpisode(Episode episode, TvSeasonEpisode tmdbEpisode, DateTime? premiereDate) + { + var changed = false; + + if (!string.IsNullOrEmpty(tmdbEpisode.Name) && !string.Equals(episode.Name, tmdbEpisode.Name, StringComparison.Ordinal)) + { + episode.Name = tmdbEpisode.Name; + changed = true; + } + + if (!string.IsNullOrEmpty(tmdbEpisode.Overview) && !string.Equals(episode.Overview, tmdbEpisode.Overview, StringComparison.Ordinal)) + { + episode.Overview = tmdbEpisode.Overview; + changed = true; + } + + if (premiereDate.HasValue && episode.PremiereDate != premiereDate) + { + episode.PremiereDate = premiereDate; + episode.ProductionYear = tmdbEpisode.AirDate?.Year; + changed = true; + } + + return changed; + } + + private Episode AddVirtualEpisode(Series series, Season season, TvSeasonEpisode tmdbEpisode, DateTime? premiereDate) + { + var seasonNumber = season.IndexNumber.GetValueOrDefault(); + var episodeNumber = (int)tmdbEpisode.EpisodeNumber; + + // Leaving Path unset makes the item a virtual (metadata-only) episode. + var episode = new Episode + { + Name = tmdbEpisode.Name, + IndexNumber = episodeNumber, + ParentIndexNumber = seasonNumber, + Id = _libraryManager.GetNewItemId( + series.Id.ToString("N", CultureInfo.InvariantCulture) + + "Season" + seasonNumber.ToString(CultureInfo.InvariantCulture) + + "Episode" + episodeNumber.ToString(CultureInfo.InvariantCulture), + typeof(Episode)), + IsVirtualItem = true, + PremiereDate = premiereDate, + ProductionYear = tmdbEpisode.AirDate?.Year, + Overview = tmdbEpisode.Overview, + SeasonId = season.Id, + SeasonName = season.Name, + SeriesId = series.Id, + SeriesName = series.Name, + SeriesPresentationUniqueKey = series.GetPresentationUniqueKey() + }; + + episode.PresentationUniqueKey = episode.CreatePresentationUniqueKey(); + + if (tmdbEpisode.Id > 0) + { + episode.SetProviderId(MetadataProvider.Tmdb, tmdbEpisode.Id.ToString(CultureInfo.InvariantCulture)); + } + + _logger.LogInformation( + "Creating virtual episode S{SeasonNumber}E{EpisodeNumber} for series {SeriesName}", + seasonNumber, + episodeNumber, + series.Name); + + season.AddChild(episode); + + return episode; + } + + /// <summary> + /// Downloads the TMDb still for a virtual episode that has no image yet, so it does not fall back + /// to the season/series image. + /// </summary> + /// <param name="episode">The virtual episode.</param> + /// <param name="tmdbEpisode">The matching TMDb episode.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns><c>true</c> if a still was downloaded and saved; otherwise <c>false</c>.</returns> + private async Task<bool> EnsureEpisodeImageAsync(Episode episode, TvSeasonEpisode tmdbEpisode, CancellationToken cancellationToken) + { + // The still ships with the season episode list, so use it directly instead of a per-episode lookup. + if (episode.HasImage(ImageType.Primary, 0) || string.IsNullOrEmpty(tmdbEpisode.StillPath)) + { + return false; + } + + var stillUrl = _tmdbClientManager.GetStillUrl(tmdbEpisode.StillPath); + if (string.IsNullOrEmpty(stillUrl)) + { + return false; + } + + try + { + // SaveImage sets the image path on the item but does not persist it, so save afterwards. + await _providerManager.SaveImage(episode, stillUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); + await episode.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, cancellationToken).ConfigureAwait(false); + return true; + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Error downloading still for virtual episode S{SeasonNumber}E{EpisodeNumber} of {SeriesName}", + episode.ParentIndexNumber, + episode.IndexNumber, + episode.SeriesName); + return false; + } + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index 5b2f0d26e4..bc44d0266d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -57,9 +55,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var season = (Season)item; var series = season?.Series; - var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + var seriesTmdbId = 0; - if (seriesTmdbId <= 0 || season?.IndexNumber is null) + if (season?.IndexNumber is null || series?.TryGetTmdbId(out seriesTmdbId) != true) { return Enumerable.Empty<RemoteImageInfo>(); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 1eb522137d..9b8803f171 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -41,20 +40,23 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// <inheritdoc /> public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, CancellationToken cancellationToken) { - var result = new MetadataResult<Season>(); + var result = new MetadataResult<Season> + { + ResultLanguage = info.MetadataLanguage + }; var config = Plugin.Instance.Configuration; info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? seriesTmdbId); var seasonNumber = info.IndexNumber; - if (string.IsNullOrWhiteSpace(seriesTmdbId) || !seasonNumber.HasValue) + if (!seasonNumber.HasValue || !TmdbUtils.TryParseTmdbId(seriesTmdbId, out var seriesId)) { return result; } var seasonResult = await _tmdbClientManager - .GetSeasonAsync(Convert.ToInt32(seriesTmdbId, CultureInfo.InvariantCulture), seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) + .GetSeasonAsync(seriesId, seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (seasonResult is null) @@ -76,11 +78,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV result.Item.Name = seasonResult.Name; } + result.Item.TrySetProviderId(MetadataProvider.Tmdb, seasonResult.Id?.ToString(CultureInfo.InvariantCulture)); result.Item.TrySetProviderId(MetadataProvider.Tvdb, seasonResult.ExternalIds?.TvdbId); - // TODO why was this disabled? var credits = seasonResult.Credits; - if (credits?.Cast is not null) { var castQuery = config.HideMissingCastMembers diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index f2e7d0c6e4..dc4f860604 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -57,9 +55,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// <inheritdoc /> public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); - - if (string.IsNullOrEmpty(tmdbId)) + if (!item.TryGetTmdbId(out var tmdbId)) { return Enumerable.Empty<RemoteImageInfo>(); } @@ -68,7 +64,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var series = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), null, null, null, cancellationToken) + .GetSeriesAsync(tmdbId, null, null, null, cancellationToken) .ConfigureAwait(false); if (series?.Images is null) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 1eb411f0f6..6163e20194 100644..100755 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -54,10 +54,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// <inheritdoc /> public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { - if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId)) + if (searchInfo.TryGetTmdbId(out var tmdbId)) { var series = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken) + .GetSeriesAsync(tmdbId, searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (series is not null) @@ -256,11 +256,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV series.Overview = seriesResult.Overview; + var studios = Enumerable.Empty<string>(); + if (seriesResult.Networks is not null) { - series.Studios = seriesResult.Networks.Select(i => i.Name).ToArray(); + studios = studios.Concat(seriesResult.Networks.Select(i => i.Name).OfType<string>()); + } + + if (seriesResult.ProductionCompanies is not null) + { + studios = studios.Concat(seriesResult.ProductionCompanies.Select(i => i.Name).OfType<string>()); } + series.SetStudios(studios); + if (seriesResult.Genres is not null) { series.Genres = seriesResult.Genres.Select(i => i.Name).ToArray(); @@ -320,13 +329,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (seriesResult.Videos?.Results is not null) { - foreach (var video in seriesResult.Videos.Results) + var trailers = new List<MediaUrl>(); + + var sortedVideos = seriesResult.Videos.Results + .OrderByDescending(video => string.Equals(video.Type, "trailer", StringComparison.OrdinalIgnoreCase)); + + foreach (var video in sortedVideos) { - if (TmdbUtils.IsTrailerType(video)) + if (!TmdbUtils.IsTrailerType(video)) { - series.AddTrailerUrl("https://www.youtube.com/watch?v=" + video.Key); + continue; } + + trailers.Add(new MediaUrl + { + Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", video.Key), + Name = video.Name + }); } + + series.RemoteTrailers = trailers; } if (!string.IsNullOrEmpty(seriesResult.OriginalLanguage)) @@ -341,39 +363,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { var config = Plugin.Instance.Configuration; - if (seriesResult.Credits?.Cast is not null) - { - IEnumerable<Cast> castQuery = seriesResult.Credits.Cast.OrderBy(a => a.Order); - - if (config.HideMissingCastMembers) - { - castQuery = castQuery.Where(a => !string.IsNullOrEmpty(a.ProfilePath)); - } - - foreach (var actor in castQuery.Take(config.MaxCastMembers)) - { - if (string.IsNullOrWhiteSpace(actor.Name)) - { - continue; - } - - var personInfo = new PersonInfo - { - Name = actor.Name.Trim(), - Role = actor.Character?.Trim() ?? string.Empty, - Type = PersonKind.Actor, - SortOrder = actor.Order, - // NOTE: Null values are filtered out above - ImageUrl = _tmdbClientManager.GetProfileUrl(actor.ProfilePath!) - }; - - if (actor.Id > 0) - { - personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); - } + // The aggregated credits are what hold an actor's several characters apart; the flat ones + // put them in a single string. Only the aggregated list carries the whole run, so prefer it + // and fall back for the rare show TMDb has no aggregation for. + var cast = seriesResult.AggregateCredits?.Cast is { Count: > 0 } aggregated + ? TmdbUtils.MapAggregateCast(aggregated, config, _tmdbClientManager.GetProfileUrl) + : TmdbUtils.MapCast(seriesResult.Credits?.Cast, config, _tmdbClientManager.GetProfileUrl); - yield return personInfo; - } + foreach (var actor in cast) + { + yield return actor; } if (seriesResult.Credits?.Crew is not null) @@ -417,6 +416,31 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV yield return personInfo; } } + + if (seriesResult.CreatedBy is not null) + { + foreach (var person in seriesResult.CreatedBy) + { + if (string.IsNullOrWhiteSpace(person.Name)) + { + continue; + } + + var personInfo = new PersonInfo + { + Name = person.Name.Trim(), + Type = PersonKind.Creator, + ImageUrl = _tmdbClientManager.GetProfileUrl(person.ProfilePath) + }; + + if (person.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); + } + + yield return personInfo; + } + } } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesSimilarProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesSimilarProvider.cs new file mode 100644 index 0000000000..c85718b993 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesSimilarProvider.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Threading; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Plugins.Tmdb.TV; + +/// <summary> +/// TMDb-based similar items provider for TV series. +/// </summary> +public class TmdbSeriesSimilarProvider : IRemoteSimilarItemsProvider<Series> +{ + private readonly TmdbClientManager _tmdbClientManager; + private readonly ILogger<TmdbSeriesSimilarProvider> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="TmdbSeriesSimilarProvider"/> class. + /// </summary> + /// <param name="tmdbClientManager">The TMDb client manager.</param> + /// <param name="logger">The logger.</param> + public TmdbSeriesSimilarProvider(TmdbClientManager tmdbClientManager, ILogger<TmdbSeriesSimilarProvider> logger) + { + _tmdbClientManager = tmdbClientManager; + _logger = logger; + } + + /// <inheritdoc/> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc/> + public MetadataPluginType Type => MetadataPluginType.SimilarityProvider; + + /// <inheritdoc/> + public TimeSpan? CacheDuration + { + get + { + var days = Plugin.Instance?.Configuration.SimilarItemsCacheDays ?? 0; + return days > 0 ? TimeSpan.FromDays(days) : null; + } + } + + /// <inheritdoc/> + public async IAsyncEnumerable<SimilarItemReference> GetSimilarItemsAsync( + Series item, + SimilarItemsQuery query, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + if (!item.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbIdStr) || !int.TryParse(tmdbIdStr, CultureInfo.InvariantCulture, out var tmdbId)) + { + yield break; + } + + var providerName = MetadataProvider.Tmdb.ToString(); + var page = 1; + var totalPages = 1; + + while (page <= totalPages && !cancellationToken.IsCancellationRequested) + { + IReadOnlyList<TMDbLib.Objects.Search.SearchTv> pageResults; + try + { + (pageResults, totalPages) = await _tmdbClientManager + .GetSeriesSimilarPageAsync(tmdbId, page, TmdbUtils.GetImageLanguagesParam(string.Empty), cancellationToken) + .ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to get similar TV shows from TMDb for {TmdbId} page {Page}", tmdbId, page); + yield break; + } + + if (pageResults.Count == 0) + { + yield break; + } + + foreach (var similar in pageResults) + { + yield return new SimilarItemReference + { + ProviderName = providerName, + ProviderId = similar.Id.ToString(CultureInfo.InvariantCulture) + }; + } + + page++; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbUpcomingEpisodesTask.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbUpcomingEpisodesTask.cs new file mode 100644 index 0000000000..e2846c74a3 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbUpcomingEpisodesTask.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Plugins.Tmdb.TV +{ + /// <summary> + /// Scheduled task that re-checks TMDb for newly announced unaired and missing episodes and creates + /// the corresponding virtual items. This keeps the "Upcoming" view current for series whose local + /// files have not changed, which an ordinary library scan would never re-examine. + /// </summary> + public class TmdbUpcomingEpisodesTask : IScheduledTask + { + private const int DefaultIntervalDays = 7; + + private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; + private readonly ILogger<TmdbUpcomingEpisodesTask> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="TmdbUpcomingEpisodesTask"/> class. + /// </summary> + /// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param> + /// <param name="fileSystem">The <see cref="IFileSystem"/>.</param> + /// <param name="logger">The <see cref="ILogger{TmdbUpcomingEpisodesTask}"/>.</param> + public TmdbUpcomingEpisodesTask( + ILibraryManager libraryManager, + IFileSystem fileSystem, + ILogger<TmdbUpcomingEpisodesTask> logger) + { + _libraryManager = libraryManager; + _fileSystem = fileSystem; + _logger = logger; + } + + /// <inheritdoc /> + public string Name => "Refresh upcoming and missing episodes (TheMovieDb)"; + + /// <inheritdoc /> + public string Description => "Checks TheMovieDb for newly announced episodes and creates virtual entries for unaired and missing episodes, according to the TMDb plugin settings. When both options are disabled, removes any virtual entries previously created."; + + /// <inheritdoc /> + public string Category => "Library"; + + /// <inheritdoc /> + public string Key => "TmdbRefreshUpcomingEpisodes"; + + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + var intervalDays = Plugin.Instance?.Configuration.MissingEpisodeRefreshIntervalDays ?? DefaultIntervalDays; + if (intervalDays <= 0) + { + intervalDays = DefaultIntervalDays; + } + + yield return new TaskTriggerInfo + { + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromDays(intervalDays).Ticks + }; + } + + /// <inheritdoc /> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + var configuration = Plugin.Instance?.Configuration; + if (configuration is null) + { + progress.Report(100); + return; + } + + // The feature is fully disabled: remove every virtual episode (and now-empty virtual season) + // this provider previously created, across all libraries, then stop. + if ((!configuration.ImportUnairedEpisodes && !configuration.ImportMissingEpisodes) + || configuration.EnabledMissingEpisodeLibraries.Length == 0) + { + RemoveAllVirtualItems(progress, cancellationToken); + return; + } + + // Process non-ended series (they may have gained episodes) plus any series in a library that + // is not opted in (regardless of status) so the provider can prune the virtual episodes it + // previously created there. Ended series in enabled libraries cannot change, so they're skipped. + var series = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Series], + Recursive = true + }) + .OfType<Series>() + .Where(s => s.HasProviderId(MetadataProvider.Tmdb) + && (s.Status != SeriesStatus.Ended || !IsEnabledForLibrary(s))) + .ToList(); + + if (series.Count == 0) + { + progress.Report(100); + return; + } + + // ValidateChildren (rather than a bare RefreshMetadata) is required so the created episodes + // are immediately visible. + var refreshOptions = new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + MetadataRefreshMode = MetadataRefreshMode.Default, + ImageRefreshMode = MetadataRefreshMode.ValidationOnly, + IsAutomated = true + }; + + for (var i = 0; i < series.Count; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + await series[i].ValidateChildren(new Progress<double>(), refreshOptions, cancellationToken: cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error refreshing upcoming episodes for series {SeriesName}", series[i].Name); + } + + progress.Report(100.0 * (i + 1) / series.Count); + } + } + + private bool IsEnabledForLibrary(BaseItem item) + { + var enabledLibraries = Plugin.Instance?.Configuration.EnabledMissingEpisodeLibraries; + if (enabledLibraries is null || enabledLibraries.Length == 0) + { + return false; + } + + // A series can live under more than one collection folder; opting in any one of them is + // enough. An item that belongs to no collection folder cannot be opted in at all. + return _libraryManager.GetCollectionFolders(item).Any(folder => + enabledLibraries.Contains(folder.Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)); + } + + /// <summary> + /// Removes every virtual episode this provider created (identified by being virtual and carrying + /// a TMDb id), plus any virtual season left without episodes as a result. Used when both import + /// options are disabled so turning the feature off cleans up its placeholders. + /// </summary> + private void RemoveAllVirtualItems(IProgress<double> progress, CancellationToken cancellationToken) + { + var deleteOptions = new DeleteOptions { DeleteFileLocation = false }; + + var virtualEpisodes = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Episode], + IsVirtualItem = true, + HasTmdbId = true, + Recursive = true + }); + + for (var i = 0; i < virtualEpisodes.Count; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + _logger.LogInformation("Removing virtual episode {Name}: the TMDb missing episode provider is disabled", virtualEpisodes[i].Name); + _libraryManager.DeleteItem(virtualEpisodes[i], deleteOptions, false); + + progress.Report(95.0 * (i + 1) / virtualEpisodes.Count); + } + + // Remove virtual seasons that are now empty (mirrors the cleanup an ordinary series refresh does). + var virtualSeasons = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Season], + IsVirtualItem = true, + HasTmdbId = true, + Recursive = true + }); + + foreach (var season in virtualSeasons.OfType<Season>()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (season.GetEpisodes().Count == 0) + { + _libraryManager.DeleteItem(season, deleteOptions, false); + } + } + + progress.Report(100); + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 274db347ba..5379796465 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -137,7 +137,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); - var extraMethods = TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups; + var extraMethods = TvShowMethods.Credits | TvShowMethods.CreditsAggregate | TvShowMethods.Images | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups; if (!(Plugin.Instance?.Configuration.ExcludeTagsSeries).GetValueOrDefault()) { extraMethods |= TvShowMethods.Keywords; @@ -505,6 +505,54 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } /// <summary> + /// Gets a single page of similar movies for a movie from the TMDb API. + /// </summary> + /// <param name="tmdbId">The TMDb id of the movie.</param> + /// <param name="page">The page number to fetch (1-based).</param> + /// <param name="language">The language for results.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>A tuple containing the list of similar movies and the total number of pages available.</returns> + public async Task<(IReadOnlyList<SearchMovie> Results, int TotalPages)> GetMovieSimilarPageAsync(int tmdbId, int page, string? language, CancellationToken cancellationToken) + { + await EnsureClientConfigAsync().ConfigureAwait(false); + + var searchResults = await _tmDbClient + .GetMovieSimilarAsync(tmdbId, language, page, cancellationToken) + .ConfigureAwait(false); + + if (searchResults?.Results is null || searchResults.Results.Count == 0) + { + return ([], 0); + } + + return (searchResults.Results, searchResults.TotalPages); + } + + /// <summary> + /// Gets a single page of similar TV shows for a series from the TMDb API. + /// </summary> + /// <param name="tmdbId">The TMDb id of the TV show.</param> + /// <param name="page">The page number to fetch (1-based).</param> + /// <param name="language">The language for results.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>A tuple containing the list of similar TV shows and the total number of pages available.</returns> + public async Task<(IReadOnlyList<SearchTv> Results, int TotalPages)> GetSeriesSimilarPageAsync(int tmdbId, int page, string? language, CancellationToken cancellationToken) + { + await EnsureClientConfigAsync().ConfigureAwait(false); + + var searchResults = await _tmDbClient + .GetTvShowSimilarAsync(tmdbId, language, page, cancellationToken) + .ConfigureAwait(false); + + if (searchResults?.Results is null || searchResults.Results.Count == 0) + { + return ([], 0); + } + + return (searchResults.Results, searchResults.TotalPages); + } + + /// <summary> /// Handles bad path checking and builds the absolute url. /// </summary> /// <param name="size">The image size to fetch.</param> @@ -544,6 +592,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } /// <summary> + /// Gets the absolute URL of an episode still. + /// </summary> + /// <param name="stillPath">The relative URL of the still.</param> + /// <returns>The absolute URL.</returns> + public string? GetStillUrl(string? stillPath) + { + return GetUrl(Plugin.Instance.Configuration.StillSize, stillPath); + } + + /// <summary> /// Converts poster <see cref="ImageData"/>s into <see cref="RemoteImageInfo"/>s. /// </summary> /// <param name="images">The input images.</param> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 39c0497bed..44a2f7291e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -1,10 +1,15 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; using System.Text.RegularExpressions; using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; using TMDbLib.Objects.General; +using TMDbLib.Objects.TvShows; +using PersonInfo = MediaBrowser.Controller.Entities.PersonInfo; namespace MediaBrowser.Providers.Plugins.Tmdb { @@ -48,10 +53,47 @@ namespace MediaBrowser.Providers.Plugins.Tmdb PersonKind.Producer }; + /// <summary> + /// Writing jobs to keep. + /// </summary> + private static readonly FrozenSet<string> _writerJobs = new[] + { + "writer", + "screenplay", + "novel" + }.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + [GeneratedRegex(@"[\W_-[ยท]]+")] private static partial Regex NonWordRegex(); /// <summary> + /// Gets the TMDb id of an item, if it has one TMDb can be queried with. + /// </summary> + /// <param name="instance">The item.</param> + /// <param name="tmdbId">The TMDb id.</param> + /// <returns><c>true</c> if the item has a usable TMDb id; otherwise, <c>false</c>.</returns> + public static bool TryGetTmdbId(this IHasProviderIds instance, out int tmdbId) + { + instance.TryGetProviderId(MetadataProvider.Tmdb, out var value); + + return TryParseTmdbId(value, out tmdbId); + } + + /// <summary> + /// Parses a TMDb id. + /// </summary> + /// <param name="value">The stored id.</param> + /// <param name="tmdbId">The TMDb id.</param> + /// <returns><c>true</c> if the value is a usable TMDb id; otherwise, <c>false</c>.</returns> + public static bool TryParseTmdbId(string? value, out int tmdbId) + { + // Another provider can have filed one of its own ids under the TMDb key, e.g. an IMDb person + // id. Reporting that as "no id" lets the caller fall back to a search and repair the id, + // instead of throwing on every refresh of the item. + return int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out tmdbId) && tmdbId > 0; + } + + /// <summary> /// Cleans the name according to TMDb requirements. /// </summary> /// <param name="name">The name of the entity.</param> @@ -82,7 +124,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } if (string.Equals(crew.Department, "writing", StringComparison.OrdinalIgnoreCase) - && (string.Equals(crew.Job, "writer", StringComparison.OrdinalIgnoreCase) || string.Equals(crew.Job, "screenplay", StringComparison.OrdinalIgnoreCase))) + && crew.Job is not null && _writerJobs.Contains(crew.Job)) { return PersonKind.Writer; } @@ -91,6 +133,100 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } /// <summary> + /// Maps an aggregated TMDb cast list, whose entries hold every role their member played. + /// </summary> + /// <param name="cast">The aggregated cast list, or <c>null</c>.</param> + /// <param name="config">The configuration deciding how much of the cast to keep.</param> + /// <param name="getProfileUrl">Resolves a profile path into an absolute image url.</param> + /// <returns>One credit per role played.</returns> + internal static IEnumerable<PersonInfo> MapAggregateCast( + IReadOnlyList<CastAggregate>? cast, + PluginConfiguration config, + Func<string?, string?> getProfileUrl) + { + if (cast is null) + { + yield break; + } + + var billed = cast + .Where(member => !string.IsNullOrWhiteSpace(member.Name)) + .Where(member => !config.HideMissingCastMembers || !string.IsNullOrEmpty(member.ProfilePath)) + .OrderBy(member => member.Order) + .Take(config.MaxCastMembers); + + foreach (var member in billed) + { + // An actor playing several characters over the run gets one aggregated entry holding + // every role, so each of them becomes a credit of its own here. Their own billing puts + // the character they played the longest first. + var characters = member.Roles? + .Where(role => !string.IsNullOrWhiteSpace(role.Character)) + .OrderByDescending(role => role.EpisodeCount) + .Select(role => role.Character!.Trim()) + .ToArray(); + + if (characters is null || characters.Length == 0) + { + characters = [string.Empty]; + } + + foreach (var character in characters) + { + yield return CreateCredit(member.Name!, member.Id, member.ProfilePath, member.Order, character, getProfileUrl); + } + } + } + + /// <summary> + /// Maps a TMDb cast list whose entries hold the one character their member is credited for. + /// </summary> + /// <param name="cast">The cast list, or <c>null</c>.</param> + /// <param name="config">The configuration deciding how much of the cast to keep.</param> + /// <param name="getProfileUrl">Resolves a profile path into an absolute image url.</param> + /// <returns>One credit per cast entry.</returns> + internal static IEnumerable<PersonInfo> MapCast( + IReadOnlyList<Cast>? cast, + PluginConfiguration config, + Func<string?, string?> getProfileUrl) + { + if (cast is null) + { + yield break; + } + + var billed = cast + .Where(member => !string.IsNullOrWhiteSpace(member.Name)) + .Where(member => !config.HideMissingCastMembers || !string.IsNullOrEmpty(member.ProfilePath)) + .OrderBy(member => member.Order) + .Take(config.MaxCastMembers); + + foreach (var member in billed) + { + yield return CreateCredit(member.Name!, member.Id, member.ProfilePath, member.Order, member.Character?.Trim() ?? string.Empty, getProfileUrl); + } + } + + private static PersonInfo CreateCredit(string name, int id, string? profilePath, int? order, string role, Func<string?, string?> getProfileUrl) + { + var personInfo = new PersonInfo + { + Name = name.Trim(), + Role = role, + Type = PersonKind.Actor, + SortOrder = order, + ImageUrl = getProfileUrl(profilePath) + }; + + if (id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, id.ToString(CultureInfo.InvariantCulture)); + } + + return personInfo; + } + + /// <summary> /// Determines whether a video is a trailer. /// </summary> /// <param name="video">The TMDb video.</param> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/jellyfin-plugin-tmdb.svg b/MediaBrowser.Providers/Plugins/Tmdb/jellyfin-plugin-tmdb.svg new file mode 100644 index 0000000000..fbebb32b60 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/jellyfin-plugin-tmdb.svg @@ -0,0 +1,16 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 508 285.75"> + <defs> + <linearGradient id="a"> + <stop style="stop-color:#20464c;stop-opacity:1" offset="0"/> + <stop style="stop-color:#0d253f;stop-opacity:.999722" offset="1"/> + </linearGradient> + <linearGradient id="c" y1="40.76" x2="190.24" y2="40.76" gradientUnits="userSpaceOnUse" gradientTransform="translate(95.25 74.882) scale(1.66894)"> + <stop offset="0" stop-color="#90cea1"/> + <stop offset=".56" stop-color="#3cbec9"/> + <stop offset="1" stop-color="#00b3e5"/> + </linearGradient> + <radialGradient xlink:href="#a" id="b" cx="125.255" cy="16.735" fx="125.255" fy="16.735" r="254" gradientTransform="matrix(0 1.06475 -1.74952 0 154.532 -61.444)" gradientUnits="userSpaceOnUse"/> + </defs> + <path style="opacity:.98;fill:url(#b);stroke-width:2.64583;fill-opacity:1" d="M0 0h508v285.75H0z"/> + <path d="M271.607 135.064H383.26a29.49 29.49 0 0 0 29.49-29.473 29.49 29.49 0 0 0-29.49-29.49H271.607a29.49 29.49 0 0 0-29.49 29.49 29.49 29.49 0 0 0 29.49 29.473zM124.74 210.167h128.342a29.49 29.49 0 0 0 29.49-29.474 29.49 29.49 0 0 0-29.49-29.49H124.74a29.49 29.49 0 0 0-29.49 29.49 29.49 29.49 0 0 0 29.49 29.474zm-12.116-76.17h13.017V86.43h16.857V74.882h-46.73v11.516h16.856zm46.897 0h13.018V88.65h.167l15.02 45.312h10.014l15.52-45.312h.167v45.312h13.018v-59.08h-19.777l-13.686 38.552h-.167l-13.601-38.553H159.52zm190.126 33.795a25.151 25.151 0 0 0-7.543-9.212 30.992 30.992 0 0 0-11.149-5.14 55.976 55.976 0 0 0-13.468-1.67H297.96v59.081h21.279a41.023 41.023 0 0 0 12.6-1.92 32.277 32.277 0 0 0 10.598-5.54 27.154 27.154 0 0 0 7.294-9.18 28.222 28.222 0 0 0 2.72-12.65 30.875 30.875 0 0 0-2.804-13.769zm-12.4 21.58a14.687 14.687 0 0 1-4.406 5.674 17.858 17.858 0 0 1-6.676 3.038 36 36 0 0 1-8.345.918h-6.759v-35.048h7.677a28.372 28.372 0 0 1 7.794 1.051 19.46 19.46 0 0 1 6.476 3.121 15.254 15.254 0 0 1 4.239 5.224 16.472 16.472 0 0 1 1.669 7.544 19.844 19.844 0 0 1-1.67 8.478zm74.485-.217a13.352 13.352 0 0 0-2.637-4.373 13.986 13.986 0 0 0-4.039-3.087 17.207 17.207 0 0 0-5.29-1.67v-.166a15.388 15.388 0 0 0 7.376-4.707 12.4 12.4 0 0 0 2.804-8.344 14.053 14.053 0 0 0-1.92-7.761 13.502 13.502 0 0 0-5.006-4.54 20.962 20.962 0 0 0-6.976-2.17 54.808 54.808 0 0 0-7.71-.55h-22.03v59.08h24.199a37.401 37.401 0 0 0 7.877-.834 22.58 22.58 0 0 0 7.143-2.754 15.721 15.721 0 0 0 5.174-5.006 14.22 14.22 0 0 0 2.003-7.811 15.671 15.671 0 0 0-.918-5.307zm-32.411-26.286h8.845a16.69 16.69 0 0 1 3.088.3 10.314 10.314 0 0 1 2.837.952 5.658 5.658 0 0 1 2.036 1.885 5.374 5.374 0 0 1 .801 3.038 6.058 6.058 0 0 1-.717 3.004 5.674 5.674 0 0 1-1.87 2.003 8.211 8.211 0 0 1-2.636 1.085 12.534 12.534 0 0 1-2.954.333h-9.43zm19.56 33.379a6.509 6.509 0 0 1-2.036 2.17 7.744 7.744 0 0 1-2.804 1.168 13.652 13.652 0 0 1-3.037.333H379.32v-13.351h9.847a25.618 25.618 0 0 1 3.338.25 14.136 14.136 0 0 1 3.421.918 6.676 6.676 0 0 1 2.62 1.97 5.19 5.19 0 0 1 1.052 3.338 6.192 6.192 0 0 1-.718 3.204z" style="fill:url(#c);stroke-width:1.66894"/> +</svg> |
