Skip to content
This repository has been archived by the owner on Apr 13, 2024. It is now read-only.

Commit

Permalink
Adds tvdb enrichment support
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcos Cordeiro committed Apr 16, 2023
1 parent bfcbf49 commit ffdd80b
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Wasari.Anime4k/AppExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static void AddAnime4KShader(this IServiceCollection serviceCollection)
{
serviceCollection.Configure<FFmpegShaderPresets>(c =>
{
c.ShadersFactory.Add("anime4k", s => new Anime4KShader());
c.ShadersFactory.Add("anime4k", _ => new Anime4KShader());
});
}
}
2 changes: 2 additions & 0 deletions Wasari.App/DownloadOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public record DownloadOptions

public bool CreateSeasonFolder { get; set; }

public bool TryEnrichEpisodes { get; set; } = true;

private Dictionary<string, Type> HostDownloadService { get; } = new();

public DownloadOptions AddHostDownloader<T>(string host) where T : IDownloadService
Expand Down
6 changes: 6 additions & 0 deletions Wasari.Cli/Commands/DownloadCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Wasari.Cli.Converters;
using Wasari.Crunchyroll;
using Wasari.FFmpeg;
using Wasari.Tvdb.Api.Client;
using Wasari.YoutubeDlp;
using WasariEnvironment;

Expand Down Expand Up @@ -90,6 +91,9 @@ public DownloadCommand(EnvironmentService environmentService, ILogger<DownloadCo
[CommandOption("ignore-tls")]
public bool IgnoreTls { get; init; }

[CommandOption("enrich-episodes", Description = "If true, will try to enrich episodes with metadata from TVDB (Fixes season and episode numbers)")]
public bool EnrichEpisodes { get; init; } = true;

private EnvironmentService EnvironmentService { get; }

private ILogger<DownloadCommand> Logger { get; }
Expand Down Expand Up @@ -172,6 +176,7 @@ public async ValueTask ExecuteAsync(IConsole console)
serviceCollection.AddDownloadServices();
serviceCollection.AddCrunchyrollServices();
serviceCollection.AddMemoryCache();
serviceCollection.AddWasariTvdbApi();
serviceCollection.Configure<DownloadOptions>(o =>
{
o.OutputDirectory = OutputDirectory;
Expand All @@ -182,6 +187,7 @@ public async ValueTask ExecuteAsync(IConsole console)
o.SeasonsRange = ParseRange(SeasonsRange);
o.CreateSeriesFolder = CreateSeriesFolder;
o.CreateSeasonFolder = CreateSeasonFolder;
o.TryEnrichEpisodes = EnrichEpisodes;
});
serviceCollection.Configure<FFmpegOptions>(o =>
{
Expand Down
6 changes: 3 additions & 3 deletions Wasari.Cli/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
FROM alpine:3.15 AS ffmpeg-base
FROM alpine:3.15 AS ffmpeg-base
RUN apk update && apk add wget
RUN wget https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz
RUN mkdir /ffmpeg
RUN tar -C /ffmpeg -xvf ffmpeg-master-latest-linux64-gpl.tar.xz --strip-components=1

FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
FROM mcr.microsoft.com/dotnet/runtime:7.0 AS base
RUN apt update && apt install curl python3 -y
RUN curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
RUN chmod a+rx /usr/local/bin/yt-dlp
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["Wasari.Cli/Wasari.Cli.csproj", "Wasari.Cli/"]
COPY ["Wasari.App/Wasari.App.csproj", "Wasari.App/"]
Expand Down
1 change: 1 addition & 0 deletions Wasari.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Wasari.Cli.Commands;
using Wasari.Cli.Converters;
using Wasari.FFmpeg;
using Wasari.Tvdb.Api.Client;
using WasariEnvironment;

namespace Wasari.Cli;
Expand Down
2 changes: 1 addition & 1 deletion Wasari.Cli/Wasari.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
Expand Down
12 changes: 6 additions & 6 deletions Wasari.Crunchyroll/CrunchyrollApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ private async Task<Url> BuildUrlFromSignature(string endpoint)
.SetQueryParam("Key-Pair-Id", signature.KeyPairId)
.SetQueryParam("locale", "en-US");
}

public IAsyncEnumerable<ApiEpisode> GetAllEpisodes(string seriesId)
{
return GetSeasons(seriesId)
.SelectMany(season => GetEpisodes(season.Id));
}

public async IAsyncEnumerable<ApiEpisode> GetEpisodes(string seasonId)
private async IAsyncEnumerable<ApiEpisode> GetEpisodes(string seasonId)
{
var url = await BuildUrlFromSignature("episodes");
url = url.SetQueryParam("season_id", seasonId);
Expand Down Expand Up @@ -94,24 +94,24 @@ public async IAsyncEnumerable<ApiSeason> GetSeasons(string seriesId)
url = url.SetQueryParam("series_id", seriesId);

var responseJson = await HttpClient.GetJsonAsync(url);

var seasons = responseJson.GetProperty("items").EnumerateArray()
.Select(i => i.Deserialize<ApiSeason>())
.Where(i => i != null)
.ToArray();

var lastNumber = seasons.Length > 0 ? seasons.Min(o => o.Number) : 1;

foreach (var apiSeason in seasons)
{
if (apiSeason != null)
{
apiSeason.Number = lastNumber;

if (apiSeason.Number > 0 && !apiSeason.IsDubbed)
lastNumber++;
}

yield return apiSeason;
}
}
Expand Down
95 changes: 92 additions & 3 deletions Wasari.Crunchyroll/CrunchyrollDownloadService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,108 @@
using Wasari.App;
using Wasari.App.Abstractions;
using Wasari.FFmpeg;
using Wasari.Tvdb.Api.Client;
using Wasari.YoutubeDlp;

namespace Wasari.Crunchyroll;

internal static class EpisodeExtensions
{
public static async IAsyncEnumerable<ApiEpisode> EnrichWithWasariApi(this IAsyncEnumerable<ApiEpisode> episodes, IServiceProvider serviceProvider, IOptions<DownloadOptions> downloadOptions)
{
var wasariTvdbApi = downloadOptions.Value.TryEnrichEpisodes ? serviceProvider.GetService<IWasariTvdbApi>() : null;

if (wasariTvdbApi != null)
{
var logger = serviceProvider.GetRequiredService<ILogger<IWasariTvdbApi>>();
logger.LogInformation("Trying to enrich episodes with Wasari.Tvdb");

var episodesArray = await episodes.ToArrayAsync();

var seriesName = episodesArray.Select(o => o.SeriesTitle)
.Distinct()
.ToArray();

if (seriesName.Length == 1)
{
var wasariApiEpisodes = await wasariTvdbApi.GetEpisodesAsync(seriesName.Single())
.ContinueWith(t =>
{
if (t.IsCompletedSuccessfully)
{
return t.Result;
}

logger.LogError(t.Exception, "Error while getting episodes from Wasari.Tvdb");
return null;
});

if (wasariApiEpisodes != null)
{
var episodesLookup = wasariApiEpisodes
.Where(i => !i.IsMovie)
.ToLookup(i => i.Name);

foreach (var episode in episodesArray)
{
var wasariEpisode = episodesLookup[episode.Title].SingleOrDefault();

if (wasariEpisode == null)
{
wasariEpisode = wasariApiEpisodes
.Where(i => !i.IsMovie)
.SingleOrDefault(o => o.Name.StartsWith(episode.Title, StringComparison.InvariantCultureIgnoreCase));

if (wasariEpisode == null)
{
logger.LogWarning("Skipping episode {EpisodeTitle} because it could not be found in Wasari.Tvdb", episode.Title);
}
}

if (wasariEpisode != null)
yield return episode with
{
SeasonNumber = wasariEpisode?.SeasonNumber ?? episode.SeasonNumber,
EpisodeNumber = wasariEpisode?.Number ?? episode.EpisodeNumber
};
}

yield break;
}
}

foreach (var episode in episodesArray)
{
yield return episode;
}

yield break;
}


await foreach (var episode in episodes)
{
yield return episode;
}
}
}

internal class CrunchyrollDownloadService : GenericDownloadService
{
private CrunchyrollApiService CrunchyrollApiService { get; }

private IOptions<DownloadOptions> DownloadOptions { get; }

public CrunchyrollDownloadService(ILogger<CrunchyrollDownloadService> logger, FFmpegService fFmpegService, IOptions<DownloadOptions> options, YoutubeDlpService youtubeDlpService, CrunchyrollApiService crunchyrollApiService, IOptions<DownloadOptions> downloadOptions) : base(logger, fFmpegService, options,
private IServiceProvider ServiceProvider { get; }

public CrunchyrollDownloadService(ILogger<CrunchyrollDownloadService> logger, FFmpegService fFmpegService, IOptions<DownloadOptions> options, YoutubeDlpService youtubeDlpService, CrunchyrollApiService crunchyrollApiService, IOptions<DownloadOptions> downloadOptions,
IServiceProvider serviceProvider) : base(logger, fFmpegService,
options,
youtubeDlpService)
{
CrunchyrollApiService = crunchyrollApiService;
DownloadOptions = downloadOptions;
ServiceProvider = serviceProvider;
}

public override async Task<DownloadedEpisode[]> DownloadEpisodes(string url, int levelOfParallelism)
Expand All @@ -34,8 +121,10 @@ public override async Task<DownloadedEpisode[]> DownloadEpisodes(string url, int
if (match.Groups["seriesId"].Success)
{
var seriesId = match.Groups["seriesId"].Value;

var episodes = CrunchyrollApiService.GetAllEpisodes(seriesId)
.Where(i => i.EpisodeNumber.HasValue && (DownloadOptions.Value.IncludeDubs || !i.IsDubbed))
.EnrichWithWasariApi(ServiceProvider, DownloadOptions)
.GroupBy(i => new { i.EpisodeNumber, i.SeasonNumber, i.SeriesTitle })
.SelectAwait(async groupedEpisodes =>
{
Expand Down Expand Up @@ -73,7 +162,7 @@ private async IAsyncEnumerable<IWasariEpisodeInput> ProcessEpisode(ApiEpisode ep
Logger.LogWarning("Episode found with no stream options: {@Episode}", episode);
yield break;
}

var stream = episode.ApiEpisodeStreams.Streams.Single(o => o.Type == "adaptive_hls" && string.IsNullOrEmpty(o.Locale));
var mediaInfo = await FFProbe.AnalyseAsync(new Uri(stream.Url));
var bestVideo = mediaInfo.VideoStreams.OrderBy(vStream => vStream.Height + vStream.Width).Last();
Expand Down
7 changes: 4 additions & 3 deletions Wasari.Crunchyroll/Wasari.Crunchyroll.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand All @@ -10,15 +10,16 @@
<PackageReference Include="JsonExtensions" Version="1.2.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.5" />
<PackageReference Include="MinimalHttpLogger" Version="1.0.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.29.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Wasari.App\Wasari.App.csproj" />
<ProjectReference Include="..\Wasari.Tvdb.Api.Client\Wasari.Tvdb.Api.Client.csproj" />
</ItemGroup>

</Project>

0 comments on commit ffdd80b

Please sign in to comment.