diff options
-rw-r--r-- | Api/JCoverSharedController.cs | 132 | ||||
-rw-r--r-- | Api/coverscript.js | 37 | ||||
-rw-r--r-- | Folder.DotSettings.user | 19 | ||||
-rw-r--r-- | ImageProvider.cs | 4 | ||||
-rw-r--r-- | POJO.cs | 42 | ||||
-rw-r--r-- | SeriesImageProvider.cs | 25 |
6 files changed, 247 insertions, 12 deletions
diff --git a/Api/JCoverSharedController.cs b/Api/JCoverSharedController.cs index a5eecc1..47a8c8a 100644 --- a/Api/JCoverSharedController.cs +++ b/Api/JCoverSharedController.cs @@ -1,9 +1,139 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using Jellyfin.Api; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.JCoverXtremePro.Api; [ApiController] [Route("JCoverXtreme")] -public class JCoverSharedController : ControllerBase +// [Authorize(Policy = "RequiresElevation")] +public class JCoverSharedController : BaseJellyfinApiController { + private readonly IProviderManager _providerManager; + private readonly ILibraryManager _libraryManager; + + /// <summary> + /// This key is appended to image URLs to inform the frontend about the presence of a potential mass-download. + /// Keep in sync with coverscript.js#URL_META_KEY. + /// </summary> + public static string URL_META_KEY = "JCoverXtremeProMeta"; + + public JCoverSharedController( + IProviderManager providerManager, + IServerApplicationPaths applicationPaths, + ILibraryManager libraryManager) + { + _providerManager = providerManager; + _libraryManager = libraryManager; + } + + public static string AppendUrlMeta(string baseUrl, string key, string value) + { + return baseUrl + (baseUrl.Contains('?', StringComparison.InvariantCulture) ? "&" : "?") + + HttpUtility.UrlEncode(key) + "=" + HttpUtility.UrlEncode(value); + } + + public class SetMeta + { + public Guid seriesId { get; set; } + public string setId { get; set; } + } + + public static string PackSetInfo(string baseUrl, Series series, POJO.Set set) + { + return AppendUrlMeta( + baseUrl, + URL_META_KEY, + JsonSerializer.Serialize(new SetMeta + { + setId = set.id, + seriesId = series.Id + })); + } + + [HttpPost("DownloadSeries")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task<ActionResult> DownloadEntireSeriesImages( + [FromBody, Required] JsonObject setMeta + ) + { + // TODO: handle missing fields, local seasons missing, series missing, etc. + var setMetaObj = JsonSerializer.Deserialize<SetMeta>(setMeta); + var series = _libraryManager.GetItemById(setMetaObj.seriesId) as Series; + var jsonMeta = await MediuxDownloader.instance.GetMediuxMetadata($"https://mediux.pro/sets/{setMetaObj.setId}") + .ConfigureAwait(false); + var set = JsonSerializer.Deserialize<POJO.SetData>(jsonMeta).set; + Dictionary<string, (int, int)> lut = new(); + foreach (var showSeason in set.show.seasons) + { + foreach (var showEpisode in showSeason.episodes) + { + lut[showEpisode.id] = (showSeason.season_number, showEpisode.episode_number); + } + } + + Dictionary<(int, int), POJO.File> files = new(); + foreach (var file in set.files) + { + var episode = file.episode_id; + if (episode == null) + { + continue; + } + + var tup = lut[episode.id]; + files[tup] = file; + } + + foreach (var item in series.GetSeasons(null, new DtoOptions(true))) + { + var season = item as Season; + var seasonNumber = season.GetLookupInfo().IndexNumber.Value; + Plugin.Logger.LogInformation($"Season id: {seasonNumber}:"); + foreach (var itemAgain in season.GetEpisodes()) + { + var episode = itemAgain as Episode; + var episodeNumber = episode.GetLookupInfo().IndexNumber.Value; + Plugin.Logger.LogInformation($" * Episode id: {episodeNumber} {episode.Name}"); + POJO.File file; + if (files.TryGetValue((seasonNumber, episodeNumber), out file)) + { + Plugin.Logger.LogInformation($" Found cover: {file.downloadUrl}"); + // await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None) + // .ConfigureAwait(false); + + // await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); + await _providerManager.SaveImage( + episode, file.downloadUrl, + // Note: this needs to be updated if SeriesImageProvider ever supports more image types + ImageType.Primary, + null, + CancellationToken.None + ).ConfigureAwait(false); + + await episode.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None) + .ConfigureAwait(false); + } + } + } + + return Empty; + } }
\ No newline at end of file diff --git a/Api/coverscript.js b/Api/coverscript.js index 24f3c34..9f3eb7f 100644 --- a/Api/coverscript.js +++ b/Api/coverscript.js @@ -19,9 +19,12 @@ /** * * @param {HTMLElement} cloneFrom + * @param {string} setMeta * @return {HTMLElement} */ - function createDownloadSeriesButton(cloneFrom) { + function createDownloadSeriesButton( + cloneFrom, + setMeta) { /*<button is="paper-icon-button-light" class="btnDownloadRemoteImage autoSize paper-icon-button-light" raised"="" title="Download"><span class="material-icons cloud_download" aria-hidden="true"></span></button>*/ //import LayersIcon from '@mui/icons-material/Layers'; //import CloudDownloadIcon from '@mui/icons-material/CloudDownload'; @@ -35,12 +38,35 @@ element.appendChild(icon) element.addEventListener("click", ev => { ev.preventDefault() - - alert("YOU HAVE JUST BEEN INTERDICTED BY THE JCOVERXTREMEPRO SERIES DOWNLOADIFICATOR") + console.log("Executing mass covering event! We will try to download the entirety of set " + setMeta) + fetch("/JCoverXtreme/DownloadSeries", + { + method: 'POST', + body: setMeta, + headers: { + "content-type": "application/json" + } + }).then(console.log) // TODO: check out the root somehow. for now just assume / }) return element } + /** + * Keep in sync with JCoverSharedController.URL_META_KEY + * @type {string} + */ + const URL_META_KEY = "JCoverXtremeProMeta" + + /** + * Extract the JCoverXtremePro metadata from an image url. + * + * @param {string} url + * @return {string} + */ + function extractSetMeta(url) { + return new URL(url).searchParams.get(URL_META_KEY) + } + const observer = new MutationObserver(() => { console.log("JCoverXtremePro observation was triggered!") console.log("Listing all download buttons") @@ -51,9 +77,12 @@ buttons.forEach(element => { const downloadRowContainer = findParent(element, ".cardText") + const cardContainer = findParent(element, ".cardBox") + const cardImage = cardContainer.querySelector("a.cardImageContainer[href]") + const setMeta = extractSetMeta(cardImage.href) if (downloadRowContainer.querySelector(`.${injectionMarker}`)) return // TODO: extract information about the series, and check if this is at all viable - downloadRowContainer.appendChild(createDownloadSeriesButton(element)) + downloadRowContainer.appendChild(createDownloadSeriesButton(element, setMeta)) }) }) diff --git a/Folder.DotSettings.user b/Folder.DotSettings.user index 87f4e76..8cd3da1 100644 --- a/Folder.DotSettings.user +++ b/Folder.DotSettings.user @@ -1,5 +1,22 @@ <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AActionResult_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcf607849fce840fcb02ecfccf47e7bce1dc400_003Fe0_003F073c6be1_003FActionResult_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAuthorizeAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c2ecf13f5ec4499a77a06d8880bd5c415800_003F77_003F5ce6003f_003FAuthorizeAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABaseItem_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Ff5158e2ef5269abc9726c67e850a8988a202b4911c07d9781389d71e7e227_003FBaseItem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcf607849fce840fcb02ecfccf47e7bce1dc400_003F86_003F5f98b882_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADtoOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fb4cecdec97dbab1be24731f1386a7fefabf47bec533415635c7ca430dc27_003FDtoOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEpisode_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1badb499b414e42f148749e92584cbf5d72618b72d771344ad7f7a053edf693_003FEpisode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> - <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMovie_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1e53cde0ae407da5e0d8b3675976ee156dbd2d3335bf707cb079be17fa471_003FMovie_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
\ No newline at end of file + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFromQueryAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcf607849fce840fcb02ecfccf47e7bce1dc400_003Fb4_003F8276602d_003FFromQueryAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIImageProvider_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F75d84f6ed6ffc68dfa26feab5f9bdd2c3f7f18b1dcb31ba7c17723a9153b49a8_003FIImageProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AILibraryManager_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8bee64d50df207991e33a95fceb50d15369caee29874fb919b40bd18b110_003FILibraryManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImageType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Faa567582df2a4613e0c695037928b7acde5365d9d183effaf9e4967c3afe7a_003FImageType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInternalItemsQuery_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fbac53285d850e394b99e61339968486d86d458dd56b6c8d1c97ecbb76f9d_003FInternalItemsQuery_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRemoteImageProvider_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F7c968f2245c52ff6a421bb307de4de67d52a456e767bd121d5d49850f546_003FIRemoteImageProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItemLookupInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa5624d02a21bf3271b26759b2f385a3b2499af4928c4a6e93a2de366d3ce_003FItemLookupInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMovie_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1e53cde0ae407da5e0d8b3675976ee156dbd2d3335bf707cb079be17fa471_003FMovie_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANullable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F14a2b70486934722acea07b38045413ab2d200_003F0f_003F279f6d62_003FNullable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASeasonInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F653f529ca112f3656e34d9b45946f33439b3e98cf773ffae38919541ff9c46_003FSeasonInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASeason_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fb5676b1ae3e5e98ca3e1d2fa9e7a9b5f9a9c220ece6ebde1a6bcb72c9b7f01f_003FSeason_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASeries_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff148e65fdb43480ab36e867827ff357283000_003F66_003F515d9cd6_003FSeries_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASeries_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9db9f2cea9e2b9d4e437349fabd6fc7b228d6fb2ff6f24d9367b75b06ae53_003FSeries_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F14a2b70486934722acea07b38045413ab2d200_003Fc9_003F5772db0a_003FSR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F14a2b70486934722acea07b38045413ab2d200_003F8f_003F6d1224a3_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
\ No newline at end of file diff --git a/ImageProvider.cs b/ImageProvider.cs index 8274a27..94f2598 100644 --- a/ImageProvider.cs +++ b/ImageProvider.cs @@ -68,8 +68,8 @@ public class ImageProvider var imageInfo = new RemoteImageInfo { Url = file.downloadUrl, - ProviderName = set.user_created.username + "(from Mediux)", - ThumbnailUrl = file.downloadUrl, + ProviderName = set.user_created.username + " (via Mediux)", + ThumbnailUrl = file.downloadUrl, // TODO: use generated thumbnails from /_next/image?url= Language = "en", RatingType = RatingType.Likes, Type = ft.Value @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; @@ -15,6 +16,17 @@ public class POJO [JsonIgnore] public IEnumerable<Set> allSets => sets.Concat(collectionSets); } + public class SetData + { + public Set set { get; set; } + } + + public class ShowData + { + // public Show show { get; set; } + public List<Set> sets { get; set; } + } + public class Set { public string id { get; set; } @@ -23,6 +35,29 @@ public class POJO public User user_created { get; set; } public List<File> files { get; set; } + public Show? show { get; set; } + } + + public class Show + { + public string id { get; set; } + public string name { get; set; } + public List<Season> seasons { get; set; } + } + + public class Season + { + public string id { get; set; } + public int season_number { get; set; } + public string name { get; set; } + public List<Episode> episodes { get; set; } + } + + public class Episode + { + public int episode_number { get; set; } + public string episode_name { get; set; } + public string id { get; set; } } public class User @@ -30,11 +65,17 @@ public class POJO public string username { get; set; } } + public class EpisodeId + { + public string id { get; set; } + } + public class File { public string fileType { get; set; } public string title { get; set; } public string id { get; set; } + public EpisodeId? episode_id { get; set; } public ImageType? JellyFinFileType() { @@ -43,6 +84,7 @@ public class POJO case "backdrop": return ImageType.Backdrop; case "poster": + case "title_card": return ImageType.Primary; } diff --git a/SeriesImageProvider.cs b/SeriesImageProvider.cs index 7f48223..dd0f3b3 100644 --- a/SeriesImageProvider.cs +++ b/SeriesImageProvider.cs @@ -1,14 +1,18 @@ -using Microsoft.Extensions.Logging; +using System.Linq; namespace Jellyfin.Plugin.JCoverXtremePro; using System.Collections.Generic; using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Plugin.JCoverXtremePro.Api; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -26,13 +30,14 @@ public class SeriesImageProvider { return [ + // Note: update JCoverSharedController if more image types are supported ImageType.Primary ]; } public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - // TODO: hadnle episodes + // TODO: handle specific episodes directly if (item is Series series) { return await HandleSeries(series, cancellationToken); @@ -51,8 +56,20 @@ public class SeriesImageProvider var metadata = await MediuxDownloader.instance.GetMediuxMetadata("https://mediux.pro/shows/" + tmdbId) .ConfigureAwait(false); - Plugin.Logger.LogInformation("JSON: " + metadata); - return []; + var show = JsonSerializer.Deserialize<POJO.ShowData>(metadata as JsonObject)!; + + return from set in show.sets + let representativeImage = set.files.Find(it => it.fileType is "poster" or "title_card")! + let enrichedUrl = JCoverSharedController.PackSetInfo(representativeImage.downloadUrl, series, set) + select new RemoteImageInfo + { + Url = enrichedUrl, + ProviderName = set.user_created.username + " (via Mediux)", + ThumbnailUrl = enrichedUrl, // TODO: use generated thumbnails from /_next/image?url= + Language = "en", + RatingType = RatingType.Likes, + Type = representativeImage.JellyFinFileType().Value + }; } public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) |