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;
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")]
// [Authorize(Policy = "RequiresElevation")]
public class JCoverSharedController : BaseJellyfinApiController
{
private readonly IProviderManager _providerManager;
private readonly ILibraryManager _libraryManager;
///
/// 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.
///
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
}));
}
private static Dictionary<(int, int), POJO.File> CreateCoverFileLUT(POJO.Set set)
{
Dictionary episodeIdToEpisodeNumber = new();
foreach (var showSeason in set.show.seasons)
{
episodeIdToEpisodeNumber[showSeason.id] = (showSeason.season_number, -10);
foreach (var showEpisode in showSeason.episodes)
{
episodeIdToEpisodeNumber[showEpisode.id] = (showSeason.season_number, showEpisode.episode_number);
}
}
Dictionary<(int, int), POJO.File> episodeNumberToFile = new();
foreach (var file in set.files)
{
string id = string.Empty;
if (file.episode_id != null)
{
id = file.episode_id.id;
}
if (file.season_id != null)
{
id = file.season_id.id;
}
var tup = episodeIdToEpisodeNumber.GetValueOrDefault(id);
episodeNumberToFile[tup] = file;
}
return episodeNumberToFile;
}
[HttpPost("DownloadSeries")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task DownloadEntireSeriesImages(
[FromBody, Required] JsonObject setMeta
)
{
// TODO: handle missing fields, local seasons missing, series missing, etc.
var setMetaObj = JsonSerializer.Deserialize(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(jsonMeta).set;
var files = CreateCoverFileLUT(set);
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}:");
await TryDownloadEpisode(season, files, (seasonNumber, -10))
.ConfigureAwait(false);
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}");
await TryDownloadEpisode(episode, files, (seasonNumber, episodeNumber))
.ConfigureAwait(false);
}
}
return Empty;
}
private async Task TryDownloadEpisode(
BaseItem item,
Dictionary<(int, int), POJO.File> files,
(int, int) episodeNumber)
{
POJO.File file;
if (files.TryGetValue(episodeNumber, out file))
{
Plugin.Logger.LogInformation($" Found cover: {file.downloadUrl}");
await SaveCoverFileForItem(item, file.downloadUrl)
.ConfigureAwait(false);
}
}
private async Task SaveCoverFileForItem(
BaseItem item,
string downloadUrl
)
{
await _providerManager.SaveImage(
item, downloadUrl,
// Note: this needs to be updated if SeriesImageProvider ever supports more image types
ImageType.Primary,
null,
CancellationToken.None
).ConfigureAwait(false);
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None)
.ConfigureAwait(false);
}
}