Files
AnimeAnnouncer/AnimeAnnouncer/Program.cs

252 lines
10 KiB
C#

using System.ComponentModel;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using AnimeAnnouncer.Cache;
using AnimeAnnouncer.RSS;
using Mastonet;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using TMDbLib.Client;
namespace AnimeAnnouncer
{
internal class Program
{
private static NyaaIndexer nyaaIndexer= new NyaaIndexer();
private static TMDbClient tmdbClient;
private static TMDBCache tmdbCache;
private static MastodonClient mastodonClient;
static void Main(string[] args)
{
Console.WriteLine("Starting...");
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.SetBasePath(System.IO.Directory.GetCurrentDirectory()); // errors here
configurationBuilder.AddJsonFile(path: "appSettings.json", optional: false, reloadOnChange: true); // errors here
var config = configurationBuilder.Build();
String TMDBApiKey = config["TMDBApiKey"];
if(String.IsNullOrEmpty(TMDBApiKey))
{
Console.WriteLine("ERROR: API key not found in config.");
return;
}
tmdbClient = new TMDbClient(TMDBApiKey);
String redisCacheConnectionString = config["redisConnectionString"];
if(!String.IsNullOrEmpty(redisCacheConnectionString))
{
tmdbCache = new TMDBCache(redisCacheConnectionString);
Console.WriteLine("Using cache");
}
String mastodonInstance = config["MastodonInstance"];
if(!String.IsNullOrEmpty(mastodonInstance))
{
String mastodonToken = config["MastodonToken"];
mastodonClient = new MastodonClient(mastodonInstance, mastodonToken);
}
nyaaIndexer.NewPost += OnNewPost;
nyaaIndexer.RssReadFinished += OnRssReadFinished;
nyaaIndexer.Start(true);
Console.WriteLine("Press enter to quit...");
Console.ReadLine();
}
static async void OnNewPost(RSS.NyaaItem item)
{
try
{
ProcessNewItem(item).Wait();
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static void OnRssReadFinished()
{
}
static async Task ProcessNewItem(NyaaItem item)
{
Console.WriteLine($"Processing {item.Title}");
//Parse release name using Anitomy library
var items = AnitomySharp.AnitomySharp.Parse(item.Title);
var title = items.FirstOrDefault(p => p.Category == AnitomySharp.Element.ElementCategory.ElementAnimeTitle)?.Value;
var episodeTitle = items.FirstOrDefault(p => p.Category == AnitomySharp.Element.ElementCategory.ElementEpisodeTitle)?.Value;
var season = items.FirstOrDefault(p => p.Category == AnitomySharp.Element.ElementCategory.ElementAnimeSeason)?.Value;
var episode = items.FirstOrDefault(p => p.Category == AnitomySharp.Element.ElementCategory.ElementEpisodeNumber)?.Value;
if(title == null || season == null || episode == null)
{
Console.WriteLine("Skipping release due to inability to determine title, season, or episode number.");
return;
}
int latestSeasonNumber = 0, latestEpisodeNumber = 0, supposedShowId = 0;
int? latestOrdinalEpisodeNumber = null;
TMDBCacheItem? cachedShow = null;
try
{
cachedShow = await tmdbCache.GetCacheItem($"ShowCache-{title}");
if(cachedShow != null)
{
Console.WriteLine("Cache hit");
latestSeasonNumber = cachedShow.LatestSeasonNumber;
latestEpisodeNumber = cachedShow.LastEpisodeNumber;
latestOrdinalEpisodeNumber = cachedShow.LatestOrdinalEpisodeNumber;
supposedShowId = cachedShow.ShowID;
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
if(supposedShowId == 0)
{ //no cache hit
var searchResults = await tmdbClient.SearchTvShowAsync(title);
Console.WriteLine($"Searching {title} on TMDB");
if(!searchResults.Results.Any())
{
Console.WriteLine("TMDB did not return any results.");
return;
}
supposedShowId = searchResults.Results.First().Id;
var showResult = await tmdbClient.GetTvShowAsync(supposedShowId);
var latestSeason = showResult.Seasons.Where(s => s.AirDate.HasValue && s.AirDate.Value < DateTime.Now).OrderByDescending(s => s.SeasonNumber).FirstOrDefault();
if(latestSeason == null)
{
Console.WriteLine("Unable to find a viable season from TMDB.");
return;
}
latestSeasonNumber = latestSeason.SeasonNumber;
latestEpisodeNumber = latestSeason.EpisodeCount;
if(showResult.Seasons.Count > 1)
{
latestOrdinalEpisodeNumber = showResult.Seasons.Sum(s => s.EpisodeCount);
}
if(tmdbCache != null && searchResults != null)
{
try
{
cachedShow = new TMDBCacheItem()
{
Title = title,
PosterPath = showResult.PosterPath.Length > 0 ? $"https://image.tmdb.org/t/p/original{showResult.PosterPath}" : String.Empty,
BackdropPath = showResult.BackdropPath.Length > 0 ? $"https://image.tmdb.org/t/p/original{showResult.BackdropPath}" : String.Empty,
LatestSeasonPosterPath = latestSeason.PosterPath.Length > 0 ? $"https://image.tmdb.org/t/p/original{latestSeason.PosterPath}" : String.Empty,
LatestSeasonOverview = latestSeason.Overview,
LatestOrdinalEpisodeNumber = latestOrdinalEpisodeNumber,
Overview = showResult.Overview,
VoteAverage = showResult.VoteAverage,
ShowID = supposedShowId,
LatestSeasonNumber = latestSeason.SeasonNumber,
LastEpisodeNumber = latestSeason.EpisodeCount
};
_ = tmdbCache.SetCacheItem($"ShowCache-{title}", cachedShow);
Console.WriteLine($"{title} Added to cache");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
if(latestSeasonNumber != int.Parse(season))
{
Console.WriteLine($"Failing release due to TMDB's season number {latestSeasonNumber} not matching title season {season}");
return;
}
if(latestEpisodeNumber != int.Parse(episode))
{
if(!latestOrdinalEpisodeNumber.HasValue || (latestOrdinalEpisodeNumber.HasValue && latestOrdinalEpisodeNumber.Value != int.Parse(episode)))
{
Console.WriteLine($"Failing release due to TMDB's last episode number of {latestEpisodeNumber}|{latestOrdinalEpisodeNumber ?? 0} not matching title episode number {episode}");
return;
}
}
// Announce if unique
if(await tmdbCache.KeyExists($"ShowAnnounced-{supposedShowId}"))
{
Console.WriteLine($"{title} has been previously announced, so avoiding announcing it again.");
return;
}
else
{
_ = tmdbCache.SetPair($"ShowAnnounced-{supposedShowId}", "1");
}
if(mastodonClient != null && cachedShow != null)
{
try
{
PostStatus(cachedShow);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.WriteLine($"Choosing {title} as a finished season!");
}
private static async void PostStatus(TMDBCacheItem cachedShow)
{
//get poster
String? posterUrl = cachedShow.LatestSeasonPosterPath ?? cachedShow.PosterPath;
Mastonet.Entities.Attachment? attachment = null;
if(posterUrl != null)
{
HttpClient httpClient = new HttpClient();
byte[] data = await httpClient.GetByteArrayAsync(posterUrl);
MediaDefinition postMedia = new MediaDefinition(new MemoryStream(data), $"{cachedShow.Title.Replace(" ", "_")}.png");
attachment = await mastodonClient.UploadMedia(postMedia);
}
//Prepare Status
String statusText = $"{cachedShow.Title} ({cachedShow.VoteAverage}/10) DUBBED has finished airing season {cachedShow.LatestSeasonNumber}! Episode {cachedShow.LastEpisodeNumber} is now available for download. {Environment.NewLine + Environment.NewLine}";
String overview = !String.IsNullOrWhiteSpace(cachedShow.LatestSeasonOverview) ? cachedShow.LatestSeasonOverview : cachedShow.Overview ?? String.Empty;
if(!String.IsNullOrWhiteSpace(overview))
{
statusText += $"Overview: {overview}";
}
if(statusText.Length >= 500)
{
statusText = statusText[..497] + "..";
}
_ = mastodonClient.PublishStatus(statusText,
Visibility.Public, mediaIds: attachment != null ? new String[] {attachment.Id} : null);
}
}
}