Handle movies with the same slug but not the same date

This commit is contained in:
Zoe Roux 2023-11-28 01:17:10 +01:00
parent 8a925b35dd
commit f59f9a7ba0
2 changed files with 63 additions and 15 deletions

View File

@ -17,17 +17,17 @@
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Blurhash.SkiaSharp; using Blurhash.SkiaSharp;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SkiaSharp; using SkiaSharp;
#nullable enable
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers
{ {
/// <summary> /// <summary>
@ -35,9 +35,8 @@ namespace Kyoo.Core.Controllers
/// </summary> /// </summary>
public class ThumbnailsManager : IThumbnailsManager public class ThumbnailsManager : IThumbnailsManager
{ {
/// <summary> private static readonly Dictionary<string, TaskCompletionSource<object>> _downloading = new();
/// A logger to report errors.
/// </summary>
private readonly ILogger<ThumbnailsManager> _logger; private readonly ILogger<ThumbnailsManager> _logger;
private readonly IHttpClientFactory _clientFactory; private readonly IHttpClientFactory _clientFactory;
@ -106,14 +105,50 @@ namespace Kyoo.Core.Controllers
public async Task DownloadImages<T>(T item) public async Task DownloadImages<T>(T item)
where T : IThumbnails where T : IThumbnails
{ {
if (item == null)
throw new ArgumentNullException(nameof(item));
string name = item is IResource res ? res.Slug : "???"; string name = item is IResource res ? res.Slug : "???";
string posterPath = $"{_GetBaseImagePath(item, "poster")}.{ImageQuality.High.ToString().ToLowerInvariant()}.webp";
bool duplicated = false;
TaskCompletionSource<object>? sync = null;
try
{
lock (_downloading)
{
if (File.Exists(posterPath) || _downloading.ContainsKey(posterPath))
{
duplicated = true;
sync = _downloading.GetValueOrDefault(posterPath);
}
else
{
sync = new();
_downloading.Add(posterPath, sync);
}
}
if (duplicated)
{
object? dup = sync != null
? await sync.Task
: null;
throw new DuplicatedItemException(dup);
}
await _DownloadImage(item.Poster, _GetBaseImagePath(item, "poster"), $"The poster of {name}"); await _DownloadImage(item.Poster, _GetBaseImagePath(item, "poster"), $"The poster of {name}");
await _DownloadImage(item.Thumbnail, _GetBaseImagePath(item, "thumbnail"), $"The poster of {name}"); await _DownloadImage(item.Thumbnail, _GetBaseImagePath(item, "thumbnail"), $"The poster of {name}");
await _DownloadImage(item.Logo, _GetBaseImagePath(item, "logo"), $"The poster of {name}"); await _DownloadImage(item.Logo, _GetBaseImagePath(item, "logo"), $"The poster of {name}");
} }
finally
{
if (!duplicated)
{
lock (_downloading)
{
_downloading.Remove(posterPath);
sync!.SetResult(item);
}
}
}
}
private static string _GetBaseImagePath<T>(T item, string image) private static string _GetBaseImagePath<T>(T item, string image)
{ {

View File

@ -6,7 +6,7 @@ import re
from aiohttp import ClientSession from aiohttp import ClientSession
from pathlib import Path from pathlib import Path
from guessit import guessit from guessit import guessit
from typing import List, Literal from typing import List, Literal, Any
from providers.provider import Provider from providers.provider import Provider
from providers.types.collection import Collection from providers.types.collection import Collection
from providers.types.episode import Episode, PartialShow from providers.types.episode import Episode, PartialShow
@ -186,7 +186,7 @@ class Scanner:
) )
return await self.post("seasons", data=season.to_kyoo()) return await self.post("seasons", data=season.to_kyoo())
async def post(self, path: str, *, data: object) -> str: async def post(self, path: str, *, data: dict[str, Any]) -> str:
logging.debug( logging.debug(
"Sending %s: %s", "Sending %s: %s",
path, path,
@ -206,19 +206,32 @@ class Scanner:
logging.error(f"Request error: {await r.text()}") logging.error(f"Request error: {await r.text()}")
r.raise_for_status() r.raise_for_status()
ret = await r.json() ret = await r.json()
if r.status == 409 and (
(path == "shows" and ret["startAir"][:4] != str(data["start_air"].year))
or (
path == "movies" and ret["airDate"][:4] != str(data["air_date"].year)
)
):
logging.info(
f"Found a {path} with the same slug ({ret['slug']}) and a different date, using the date as part of the slug"
)
year = (data["start_air"] if path == "movie" else data["air_date"]).year
data["slug"] = f"{ret['slug']}-{year}"
return await self.post(path, data=data)
return ret["id"] return ret["id"]
async def delete(self, path: str): async def delete(self, path: str):
logging.info("Deleting %s", path) logging.info("Deleting %s", path)
async with self._client.delete( async with self._client.delete(
f"{self._url}/movies?path={path}", headers={"X-API-Key": self._api_key} f"{self._url}/movies?filter=path eq \"{path}\"", headers={"X-API-Key": self._api_key}
) as r: ) as r:
if not r.ok: if not r.ok:
logging.error(f"Request error: {await r.text()}") logging.error(f"Request error: {await r.text()}")
r.raise_for_status() r.raise_for_status()
async with self._client.delete( async with self._client.delete(
f"{self._url}/episodes?path={path}", headers={"X-API-Key": self._api_key} f"{self._url}/episodes?filter=path eq \"{path}\"", headers={"X-API-Key": self._api_key}
) as r: ) as r:
if not r.ok: if not r.ok:
logging.error(f"Request error: {await r.text()}") logging.error(f"Request error: {await r.text()}")