mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Handle movies with the same slug but not the same date
This commit is contained in:
parent
8a925b35dd
commit
f59f9a7ba0
@ -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,13 +105,49 @@ 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 : "???";
|
||||||
await _DownloadImage(item.Poster, _GetBaseImagePath(item, "poster"), $"The poster of {name}");
|
|
||||||
await _DownloadImage(item.Thumbnail, _GetBaseImagePath(item, "thumbnail"), $"The poster of {name}");
|
string posterPath = $"{_GetBaseImagePath(item, "poster")}.{ImageQuality.High.ToString().ToLowerInvariant()}.webp";
|
||||||
await _DownloadImage(item.Logo, _GetBaseImagePath(item, "logo"), $"The poster of {name}");
|
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.Thumbnail, _GetBaseImagePath(item, "thumbnail"), $"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)
|
||||||
|
@ -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()}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user