Compare commits
18 Commits
MassTorren
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8dcc153806 | |||
| 41c75ee3f2 | |||
| 5cfcecc5c3 | |||
| 6a50c94cf2 | |||
| acac2a25e8 | |||
| 47b44fb59a | |||
| 965bd58799 | |||
| 039e3189eb | |||
| 6a518b0c9b | |||
| 7b830659bc | |||
| c423d8f3ed | |||
| 25b214c0d7 | |||
| 310978054d | |||
| c1103564bb | |||
| 57c42bcc41 | |||
| 7bc2ef89a2 | |||
| 2e48daae3a | |||
| 5937ddcbd8 |
@@ -13,12 +13,17 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||||
<PackageReference Include="TMDbLib" Version="2.2.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="appSettings.json">
|
<None Update="appSettings.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="TMDbLib">
|
||||||
|
<HintPath>..\Libraries\TMDbLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
11
AnimeAnnouncer/Cache/AiringSoonItem.cs
Normal file
11
AnimeAnnouncer/Cache/AiringSoonItem.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
namespace AnimeAnnouncer.Cache
|
||||||
|
{
|
||||||
|
public class AiringSoonItem
|
||||||
|
{
|
||||||
|
public required String Title { get; set; }
|
||||||
|
public int ShowID { get; set; }
|
||||||
|
public DateTime? LastAirDate { get; set; }
|
||||||
|
public int LatestEpisodeNumber { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ public class TMDBCache
|
|||||||
public async Task SetCacheItem(String key, TMDBCacheItem item)
|
public async Task SetCacheItem(String key, TMDBCacheItem item)
|
||||||
{
|
{
|
||||||
var db = redis.GetDatabase();
|
var db = redis.GetDatabase();
|
||||||
|
|
||||||
String jsonItem = JsonSerializer.Serialize(item);
|
String jsonItem = JsonSerializer.Serialize(item);
|
||||||
await db.StringSetAsync(key, jsonItem, CacheExpiration);
|
await db.StringSetAsync(key, jsonItem, CacheExpiration);
|
||||||
}
|
}
|
||||||
@@ -33,6 +34,32 @@ public class TMDBCache
|
|||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RemoveCacheItem(String key)
|
||||||
|
{
|
||||||
|
var db = redis.GetDatabase();
|
||||||
|
_ = await db.KeyDeleteAsync(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetAiringSoon(List<AiringSoonItem> list)
|
||||||
|
{
|
||||||
|
var db = redis.GetDatabase();
|
||||||
|
String jsonItem = JsonSerializer.Serialize(list);
|
||||||
|
await db.StringSetAsync("AiringSoon", jsonItem, TimeSpan.FromDays(60));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<AiringSoonItem>> GetAiringList()
|
||||||
|
{
|
||||||
|
var db = redis.GetDatabase();
|
||||||
|
|
||||||
|
String jsonItem = await db.StringGetAsync("AiringSoon");
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(jsonItem))
|
||||||
|
return JsonSerializer.Deserialize<List<AiringSoonItem>>(jsonItem);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public async Task<bool> KeyExists(String key)
|
public async Task<bool> KeyExists(String key)
|
||||||
{
|
{
|
||||||
var db = redis.GetDatabase();
|
var db = redis.GetDatabase();
|
||||||
@@ -43,4 +70,9 @@ public class TMDBCache
|
|||||||
var db = redis.GetDatabase();
|
var db = redis.GetDatabase();
|
||||||
await db.StringSetAsync(key, value);
|
await db.StringSetAsync(key, value);
|
||||||
}
|
}
|
||||||
|
public async Task<String?> GetStringValue(String key)
|
||||||
|
{
|
||||||
|
var db = redis.GetDatabase();
|
||||||
|
return await db.StringGetAsync(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ namespace AnimeAnnouncer.Cache
|
|||||||
|
|
||||||
public int LatestSeasonNumber { get; set; }
|
public int LatestSeasonNumber { get; set; }
|
||||||
public int LastEpisodeNumber { get; set; }
|
public int LastEpisodeNumber { get; set; }
|
||||||
public string? PosterPath { get; internal set; }
|
public string? PosterPath { get; set; }
|
||||||
public string? BackdropPath { get; internal set; }
|
public string? BackdropPath { get; set; }
|
||||||
public string? LatestSeasonPosterPath { get; internal set; }
|
public string? LatestSeasonPosterPath { get; set; }
|
||||||
public string? LatestSeasonOverview { get; internal set; }
|
public string? LatestSeasonOverview { get; set; }
|
||||||
public double VoteAverage { get; internal set; }
|
public double VoteAverage { get; set; }
|
||||||
public string? Overview { get; internal set; }
|
public string? Overview { get; set; }
|
||||||
public int? LatestOrdinalEpisodeNumber { get; internal set; }
|
public int? LatestOrdinalEpisodeNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LastAirDate { get; set; }
|
||||||
|
//This is the latest episode number to air, NOT the last episode in the series
|
||||||
|
public int? LatestEpisodeNumber { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using AnimeAnnouncer.Cache;
|
using AnimeAnnouncer.Cache;
|
||||||
using AnimeAnnouncer.RSS;
|
using AnimeAnnouncer.RSS;
|
||||||
using Mastonet;
|
using Mastonet;
|
||||||
|
using Mastonet.Entities;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TMDbLib.Client;
|
using TMDbLib.Client;
|
||||||
|
using TMDbLib.Objects.Authentication;
|
||||||
|
|
||||||
namespace AnimeAnnouncer
|
namespace AnimeAnnouncer
|
||||||
{
|
{
|
||||||
@@ -17,6 +20,8 @@ namespace AnimeAnnouncer
|
|||||||
private static TMDbClient tmdbClient;
|
private static TMDbClient tmdbClient;
|
||||||
private static TMDBCache tmdbCache;
|
private static TMDBCache tmdbCache;
|
||||||
private static MastodonClient mastodonClient;
|
private static MastodonClient mastodonClient;
|
||||||
|
|
||||||
|
private static List<AiringSoonItem> airingSoonList;
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Starting...");
|
Console.WriteLine("Starting...");
|
||||||
@@ -51,14 +56,37 @@ namespace AnimeAnnouncer
|
|||||||
mastodonClient = new MastodonClient(mastodonInstance, mastodonToken);
|
mastodonClient = new MastodonClient(mastodonInstance, mastodonToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
airingSoonList = tmdbCache.GetAiringList().Result;
|
||||||
|
|
||||||
|
if(airingSoonList == null)
|
||||||
|
airingSoonList = new List<AiringSoonItem>();
|
||||||
|
|
||||||
|
if (args.Any(p => p == "--show-airing-list"))
|
||||||
|
{
|
||||||
|
PrintAiringList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nyaaIndexer.NewPost += OnNewPost;
|
nyaaIndexer.NewPost += OnNewPost;
|
||||||
nyaaIndexer.RssReadFinished += OnRssReadFinished;
|
nyaaIndexer.RssReadFinished += OnRssReadFinished;
|
||||||
nyaaIndexer.Start(true);
|
nyaaIndexer.IterationSleepTime = 60;
|
||||||
|
nyaaIndexer.Start(false);
|
||||||
|
|
||||||
Console.WriteLine("Press enter to quit...");
|
Console.WriteLine("Press enter to quit...");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void PrintAiringList()
|
||||||
|
{
|
||||||
|
var airingThisHalf = airingSoonList.Where(a => a.LastAirDate > DateTime.Now && a.LastAirDate.Value.Subtract(DateTime.Now) < TimeSpan.FromDays(180)).ToList();
|
||||||
|
int cnt = 1;
|
||||||
|
for(int i = 0;i < airingThisHalf.Count;i++)
|
||||||
|
{
|
||||||
|
var airing = airingThisHalf[i];
|
||||||
|
Console.WriteLine($"{cnt++}. {airing.Title} on {airing.LastAirDate:MM/dd/yyyy}{Environment.NewLine}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async void OnNewPost(RSS.NyaaItem item)
|
static async void OnNewPost(RSS.NyaaItem item)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -73,6 +101,14 @@ namespace AnimeAnnouncer
|
|||||||
|
|
||||||
static void OnRssReadFinished()
|
static void OnRssReadFinished()
|
||||||
{
|
{
|
||||||
|
if(DateTime.Today.Day == 1)
|
||||||
|
{
|
||||||
|
if(!tmdbCache.KeyExists($"UpcomingAnnounced-{DateTime.Today.Year}-{DateTime.Today.Month}").Result)
|
||||||
|
{
|
||||||
|
CreateAiringMonthPost().Wait();
|
||||||
|
tmdbCache.SetPair($"UpcomingAnnounced-{DateTime.Today.Year}-{DateTime.Today.Month}", "1").Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +128,14 @@ namespace AnimeAnnouncer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int latestSeasonNumber = 0, latestEpisodeNumber = 0, supposedShowId = 0;
|
int latestSeasonNumber = 0, lastEpisodeNumber = 0, latestEpisodeNumber = 0, supposedShowId = 0;
|
||||||
|
|
||||||
int? latestOrdinalEpisodeNumber = null;
|
int? latestOrdinalEpisodeNumber = null;
|
||||||
|
|
||||||
|
bool finaleConfirmed = false;
|
||||||
|
|
||||||
|
DateTime? lastAirDate = null;
|
||||||
|
|
||||||
TMDBCacheItem? cachedShow = null;
|
TMDBCacheItem? cachedShow = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -105,8 +145,9 @@ namespace AnimeAnnouncer
|
|||||||
{
|
{
|
||||||
Console.WriteLine("Cache hit");
|
Console.WriteLine("Cache hit");
|
||||||
latestSeasonNumber = cachedShow.LatestSeasonNumber;
|
latestSeasonNumber = cachedShow.LatestSeasonNumber;
|
||||||
latestEpisodeNumber = cachedShow.LastEpisodeNumber;
|
lastEpisodeNumber = cachedShow.LastEpisodeNumber;
|
||||||
latestOrdinalEpisodeNumber = cachedShow.LatestOrdinalEpisodeNumber;
|
latestOrdinalEpisodeNumber = cachedShow.LatestOrdinalEpisodeNumber;
|
||||||
|
lastAirDate = cachedShow.LastAirDate;
|
||||||
supposedShowId = cachedShow.ShowID;
|
supposedShowId = cachedShow.ShowID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,7 +169,8 @@ namespace AnimeAnnouncer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
supposedShowId = searchResults.Results.First().Id;
|
//supposedShowId = searchResults.Results.First().Id;
|
||||||
|
supposedShowId = searchResults.Results.OrderBy(r => ComputeLevenshteinDistance(title, r.Name)).First().Id;
|
||||||
|
|
||||||
var showResult = await tmdbClient.GetTvShowAsync(supposedShowId);
|
var showResult = await tmdbClient.GetTvShowAsync(supposedShowId);
|
||||||
|
|
||||||
@@ -142,11 +184,38 @@ namespace AnimeAnnouncer
|
|||||||
|
|
||||||
latestSeasonNumber = latestSeason.SeasonNumber;
|
latestSeasonNumber = latestSeason.SeasonNumber;
|
||||||
|
|
||||||
latestEpisodeNumber = latestSeason.EpisodeCount;
|
lastEpisodeNumber = latestSeason.EpisodeCount;
|
||||||
|
|
||||||
|
latestEpisodeNumber = int.Parse(episode);
|
||||||
|
|
||||||
|
//if nearing the end of a season, confirm it's marked with season finale
|
||||||
|
|
||||||
|
if(latestEpisodeNumber >= (lastEpisodeNumber - 2) || !HasAiringEndDate(supposedShowId))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Querying episode information");
|
||||||
|
var latestSeasonDetail = await tmdbClient.GetTvSeasonAsync(supposedShowId, latestSeasonNumber);
|
||||||
|
|
||||||
|
var seasonFinale = latestSeasonDetail.Episodes.LastOrDefault(e => e.EpisodeType.Equals("finale", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
|
if(seasonFinale != null)
|
||||||
|
{
|
||||||
|
if(seasonFinale.EpisodeNumber != lastEpisodeNumber)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Overriding previous finale choice of {lastEpisodeNumber} due to season detail response where it's {seasonFinale.EpisodeNumber}");
|
||||||
|
lastEpisodeNumber = seasonFinale.EpisodeNumber;
|
||||||
|
}
|
||||||
|
finaleConfirmed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(showResult.Seasons.Count > 1)
|
if(showResult.Seasons.Count > 1)
|
||||||
{
|
{
|
||||||
latestOrdinalEpisodeNumber = showResult.Seasons.Sum(s => s.EpisodeCount);
|
latestOrdinalEpisodeNumber = showResult.Seasons.Where(s => s.SeasonNumber != 0).Sum(s => s.EpisodeCount);
|
||||||
|
}
|
||||||
|
//try to figure out Last Air Date based on this new airing episode
|
||||||
|
if(lastEpisodeNumber > latestEpisodeNumber)
|
||||||
|
{
|
||||||
|
lastAirDate = DateTime.Today.AddDays(7 * (lastEpisodeNumber - latestEpisodeNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tmdbCache != null && searchResults != null)
|
if(tmdbCache != null && searchResults != null)
|
||||||
@@ -164,11 +233,16 @@ namespace AnimeAnnouncer
|
|||||||
Overview = showResult.Overview,
|
Overview = showResult.Overview,
|
||||||
VoteAverage = showResult.VoteAverage,
|
VoteAverage = showResult.VoteAverage,
|
||||||
ShowID = supposedShowId,
|
ShowID = supposedShowId,
|
||||||
LatestSeasonNumber = latestSeason.SeasonNumber,
|
LatestSeasonNumber = latestSeasonNumber,
|
||||||
LastEpisodeNumber = latestSeason.EpisodeCount
|
LastEpisodeNumber = lastEpisodeNumber,
|
||||||
|
LastAirDate = lastAirDate,
|
||||||
|
LatestEpisodeNumber = latestEpisodeNumber
|
||||||
};
|
};
|
||||||
_ = tmdbCache.SetCacheItem($"ShowCache-{title}", cachedShow);
|
_ = tmdbCache.SetCacheItem($"ShowCache-{title}", cachedShow);
|
||||||
Console.WriteLine($"{title} Added to cache");
|
Console.WriteLine($"{title} Added to cache");
|
||||||
|
if(latestSeason.AirDate < DateTime.Now && (latestSeason.AirDate + TimeSpan.FromDays(180)) > DateTime.Now &&
|
||||||
|
((showResult.Status == "Ended" || showResult.NextEpisodeToAir == null ? showResult.LastAirDate : showResult.NextEpisodeToAir.AirDate) + TimeSpan.FromDays(30)) > DateTime.Now)
|
||||||
|
UpdateAiringShowList(cachedShow);
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
{
|
{
|
||||||
@@ -180,36 +254,95 @@ namespace AnimeAnnouncer
|
|||||||
|
|
||||||
|
|
||||||
if(latestSeasonNumber != int.Parse(season))
|
if(latestSeasonNumber != int.Parse(season))
|
||||||
|
{
|
||||||
|
var titleSeason = int.Parse(season);
|
||||||
|
bool seasonOverride = false;
|
||||||
|
if(latestSeasonNumber ==1 && titleSeason > 1 && lastEpisodeNumber > 3)
|
||||||
|
{ //Check episode groups to see if this is a single-season default show
|
||||||
|
var showResult = await tmdbClient.GetTvShowAsync(supposedShowId, TMDbLib.Objects.TvShows.TvShowMethods.EpisodeGroups);
|
||||||
|
var seasonEpisodeGroup = showResult.EpisodeGroups.Results.FirstOrDefault(eg => eg.Name == "Seasons");
|
||||||
|
if(seasonEpisodeGroup != null)
|
||||||
|
{ //Evaluate using new episode group
|
||||||
|
var targetEpisodeGroup = await tmdbClient.GetTvEpisodeGroupsAsync(seasonEpisodeGroup.Id);
|
||||||
|
if(targetEpisodeGroup != null)
|
||||||
|
{
|
||||||
|
var targetSeason = targetEpisodeGroup.Groups.FirstOrDefault(g => g.Order == titleSeason);
|
||||||
|
if(targetSeason != null)
|
||||||
|
{ //Episode_Type isn't making it with the current client, guess based on ordering
|
||||||
|
var targetFinale = targetSeason.Episodes.OrderByDescending(g => g.Order).FirstOrDefault(e => e.EpisodeType.Equals("finale", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
//Confirm it's aired within the last week
|
||||||
|
if(targetFinale != null && targetEpisodeGroup.Groups.Where(g => g.Episodes.Count != 0).OrderByDescending(g => g.Order).FirstOrDefault() == targetSeason)
|
||||||
|
{ //confirm the episode is marked as finale and that this season is the latest one in the episode group
|
||||||
|
Console.WriteLine($"!!Choosing S{titleSeason}E{targetFinale.Order + 1} airing on {targetFinale.AirDate} for finale using episode groups");
|
||||||
|
cachedShow.LatestSeasonNumber = latestSeasonNumber = titleSeason;
|
||||||
|
cachedShow.LastEpisodeNumber = lastEpisodeNumber = targetFinale.Order + 1;
|
||||||
|
if(cachedShow.LastEpisodeNumber > cachedShow.LatestEpisodeNumber)
|
||||||
|
{
|
||||||
|
cachedShow.LastAirDate = DateTime.Today.AddDays(7 * (lastEpisodeNumber - latestEpisodeNumber));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cachedShow.LastAirDate = lastAirDate = targetFinale.AirDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
seasonOverride = finaleConfirmed = true;
|
||||||
|
_ = tmdbCache.SetCacheItem($"ShowCache-{title}", cachedShow);
|
||||||
|
|
||||||
|
if(targetSeason != null && (targetSeason.Episodes.OrderBy(e => e.Order).First().AirDate + TimeSpan.FromDays(180)) > DateTime.Now &&
|
||||||
|
((showResult.NextEpisodeToAir ?? showResult.LastEpisodeToAir).AirDate + TimeSpan.FromDays(30)) > DateTime.Now)
|
||||||
|
UpdateAiringShowList(cachedShow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Console.WriteLine("Could not find a finale episode.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!seasonOverride)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Failing release due to TMDB's season number {latestSeasonNumber} not matching title season {season}");
|
Console.WriteLine($"Failing release due to TMDB's season number {latestSeasonNumber} not matching title season {season}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(latestEpisodeNumber != int.Parse(episode))
|
if(lastEpisodeNumber != int.Parse(episode))
|
||||||
{
|
{
|
||||||
if(!latestOrdinalEpisodeNumber.HasValue || (latestOrdinalEpisodeNumber.HasValue && latestOrdinalEpisodeNumber.Value != 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}");
|
Console.WriteLine($"Failing release due to TMDB's last episode number of {lastEpisodeNumber}|{latestOrdinalEpisodeNumber ?? 0} not matching title episode number {episode}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Sometimes TMDB metadata hasn't been filled in yet
|
||||||
|
if(lastEpisodeNumber < 3)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Skipping announcement due to a low episode number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Announce if unique
|
// Announce if unique
|
||||||
if(await tmdbCache.KeyExists($"ShowAnnounced-{supposedShowId}"))
|
var lastAnnouncedSeason = await tmdbCache.GetStringValue($"ShowAnnounced-{supposedShowId}");
|
||||||
|
Int32 lastSeason = 0;
|
||||||
|
if (lastAnnouncedSeason != null && Int32.TryParse(lastAnnouncedSeason, out lastSeason) &&
|
||||||
|
latestSeasonNumber <= lastSeason)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{title} has been previously announced, so avoiding announcing it again.");
|
Console.WriteLine($"{title} has been previously announced, so avoiding announcing it again.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else if (!finaleConfirmed)
|
||||||
{
|
{
|
||||||
_ = tmdbCache.SetPair($"ShowAnnounced-{supposedShowId}", "1");
|
Console.WriteLine($"Would have announced finale for {title}, on S{cachedShow.LatestSeasonNumber}E{cachedShow.LastEpisodeNumber} but the finale episode type was not found.");
|
||||||
|
//Remove from cache so subsequent releases cause a fresh lookup
|
||||||
|
await tmdbCache.RemoveCacheItem($"ShowCache-{title}");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if(mastodonClient != null && cachedShow != null)
|
if(mastodonClient != null && cachedShow != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_ = tmdbCache.SetPair($"ShowAnnounced-{supposedShowId}", latestSeasonNumber.ToString());
|
||||||
PostStatus(cachedShow);
|
PostStatus(cachedShow);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
{
|
{
|
||||||
@@ -220,6 +353,55 @@ namespace AnimeAnnouncer
|
|||||||
Console.WriteLine($"Choosing {title} as a finished season!");
|
Console.WriteLine($"Choosing {title} as a finished season!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static async Task CreateAiringMonthPost()
|
||||||
|
{
|
||||||
|
var airingSoon = await tmdbCache.GetAiringList();
|
||||||
|
DateTime beginningOfMonth = DateTime.Today;
|
||||||
|
DateTime endOfMonth = DateTime.Today.AddMonths(1);
|
||||||
|
var airingThisMonth = airingSoon.Where(p => p.LastAirDate >= beginningOfMonth && p.LastAirDate <= endOfMonth).ToList();
|
||||||
|
String statusText = $"DUBBED airing anime ending this month (probably){Environment.NewLine + Environment.NewLine}";
|
||||||
|
int cnt = 1;
|
||||||
|
for(int i = 0;i < airingThisMonth.Count;i++)
|
||||||
|
{
|
||||||
|
var airing = airingThisMonth[i];
|
||||||
|
if(!await tmdbCache.KeyExists($"ShowAnnounced-{airing.ShowID}"))
|
||||||
|
{ //TODO: fix for seasons
|
||||||
|
statusText += $"{cnt++}. {airing.Title} on {airing.LastAirDate:MM/dd/yyyy}{Environment.NewLine}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int neededPostCount = (int)Math.Ceiling((decimal)statusText.Length / 500);
|
||||||
|
if(neededPostCount >= 2)
|
||||||
|
{
|
||||||
|
//statusText = statusText[..497] + "...";
|
||||||
|
int lineBreakIdx = 500 - new string(statusText.Take(500).Reverse().ToArray()).IndexOf(Environment.NewLine);
|
||||||
|
String postOne = statusText.Substring(0, lineBreakIdx);
|
||||||
|
String postTwo = statusText.Substring(lineBreakIdx);
|
||||||
|
|
||||||
|
var statusOne = await mastodonClient.PublishStatus(postOne,
|
||||||
|
Visibility.Public, null);
|
||||||
|
var statusTwo = await mastodonClient.PublishStatus(postTwo, Visibility.Public, statusOne.Id);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
_ = mastodonClient.PublishStatus(statusText,
|
||||||
|
Visibility.Public, null);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(cnt = 1; cnt < neededPostCount; cnt++)
|
||||||
|
{
|
||||||
|
//go back and find a line break
|
||||||
|
String postPart = "";
|
||||||
|
//_ = mastodonClient.PublishStatus(${statusText},
|
||||||
|
// Visibility.Public, null);
|
||||||
|
System.Threading.Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
private static async void PostStatus(TMDBCacheItem cachedShow)
|
private static async void PostStatus(TMDBCacheItem cachedShow)
|
||||||
{
|
{
|
||||||
//get poster
|
//get poster
|
||||||
@@ -237,15 +419,97 @@ namespace AnimeAnnouncer
|
|||||||
String overview = !String.IsNullOrWhiteSpace(cachedShow.LatestSeasonOverview) ? cachedShow.LatestSeasonOverview : cachedShow.Overview ?? String.Empty;
|
String overview = !String.IsNullOrWhiteSpace(cachedShow.LatestSeasonOverview) ? cachedShow.LatestSeasonOverview : cachedShow.Overview ?? String.Empty;
|
||||||
if (!String.IsNullOrWhiteSpace(overview))
|
if (!String.IsNullOrWhiteSpace(overview))
|
||||||
{
|
{
|
||||||
statusText += $"Overview: {overview}";
|
statusText += $"{overview}";
|
||||||
}
|
}
|
||||||
if(statusText.Length >= 500)
|
String anidbLink = $"{Environment.NewLine}https://anilist.co/search/anime?search={cachedShow.Title.Replace(" ", "%20")}";
|
||||||
|
int linkLength = anidbLink.Length;
|
||||||
|
|
||||||
|
if (statusText.Length + linkLength + 3 >= 500)
|
||||||
{
|
{
|
||||||
statusText = statusText[..497] + "..";
|
statusText = statusText[..(500 - (linkLength + 3))];
|
||||||
}
|
}
|
||||||
_ = mastodonClient.PublishStatus(statusText,
|
statusText += $"..{anidbLink}";
|
||||||
|
|
||||||
|
var status = await mastodonClient.PublishStatus(statusText,
|
||||||
Visibility.Public, mediaIds: attachment != null ? new String[] { attachment.Id } : null);
|
Visibility.Public, mediaIds: attachment != null ? new String[] { attachment.Id } : null);
|
||||||
|
if(String.IsNullOrEmpty(status.Id))
|
||||||
|
{
|
||||||
|
Console.WriteLine("An error has occurred posting the status update");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
private static async void UpdateAiringShowList(TMDBCacheItem cachedShow)
|
||||||
|
{
|
||||||
|
var airingSoonItem = new AiringSoonItem()
|
||||||
|
{
|
||||||
|
Title = cachedShow.Title,
|
||||||
|
ShowID = cachedShow.ShowID,
|
||||||
|
LastAirDate = cachedShow.LastAirDate ?? DateTime.MaxValue,
|
||||||
|
LatestEpisodeNumber = cachedShow.LatestEpisodeNumber ?? 0
|
||||||
|
};
|
||||||
|
var existingAiringItem = airingSoonList.FirstOrDefault(s => s.ShowID == cachedShow.ShowID);
|
||||||
|
if(existingAiringItem == null)
|
||||||
|
{
|
||||||
|
airingSoonList.Add(airingSoonItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(existingAiringItem.LatestEpisodeNumber >= cachedShow.LatestEpisodeNumber)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Not updating {cachedShow.Title} airdate due to episode number not incrementing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
airingSoonList.RemoveAll(s => s.ShowID == cachedShow.ShowID);
|
||||||
|
airingSoonList.Add(airingSoonItem);
|
||||||
|
}
|
||||||
|
airingSoonList.Sort((showOne, showTwo) => showOne.LastAirDate.Value.CompareTo(showTwo.LastAirDate.Value));
|
||||||
|
_ = tmdbCache.SetAiringSoon(airingSoonList);
|
||||||
|
Console.WriteLine($"Updated airing soon list, setting {cachedShow.Title} Season {cachedShow.LatestSeasonNumber} to end on {cachedShow.LastAirDate ?? DateTime.MaxValue}");
|
||||||
|
}
|
||||||
|
private static bool HasAiringEndDate(int showID)
|
||||||
|
{
|
||||||
|
return airingSoonList.Any(s => s.ShowID == showID && s.LastAirDate.HasValue);
|
||||||
|
}
|
||||||
|
private static int ComputeLevenshteinDistance(string s, string t)
|
||||||
|
{
|
||||||
|
int n = s.Length;
|
||||||
|
int m = t.Length;
|
||||||
|
int[,] d = new int[n + 1, m + 1];
|
||||||
|
|
||||||
|
// Verify arguments.
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m == 0)
|
||||||
|
{
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize arrays.
|
||||||
|
for (int i = 0; i <= n; d[i, 0] = i++)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j <= m; d[0, j] = j++)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin looping.
|
||||||
|
for (int i = 1; i <= n; i++)
|
||||||
|
{
|
||||||
|
for (int j = 1; j <= m; j++)
|
||||||
|
{
|
||||||
|
// Compute cost.
|
||||||
|
int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
|
||||||
|
d[i, j] = Math.Min(
|
||||||
|
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
|
||||||
|
d[i - 1, j - 1] + cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return cost.
|
||||||
|
return d[n, m];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace MassTorrentAdd
|
|||||||
|
|
||||||
var builder = new ConfigurationBuilder()
|
var builder = new ConfigurationBuilder()
|
||||||
.SetBasePath(Directory.GetCurrentDirectory())
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
.AddJsonFile("appSettings.json", optional: false, reloadOnChange: true);
|
||||||
|
|
||||||
IConfigurationRoot configuration = builder.Build();
|
IConfigurationRoot configuration = builder.Build();
|
||||||
transmissionHost = configuration.GetValue<String>("transmissionURI");
|
transmissionHost = configuration.GetValue<String>("transmissionURI");
|
||||||
@@ -70,7 +70,7 @@ namespace MassTorrentAdd
|
|||||||
transmissionDirectoryName = System.Text.RegularExpressions.Regex.Replace(transmissionDirectoryName, @"E\d{2,3}.*?(\d{3,4}p)", " $1");
|
transmissionDirectoryName = System.Text.RegularExpressions.Regex.Replace(transmissionDirectoryName, @"E\d{2,3}.*?(\d{3,4}p)", " $1");
|
||||||
transmissionDirectoryName = System.Text.RegularExpressions.Regex.Replace(transmissionDirectoryName, @"\s\(.*?\)$", "");
|
transmissionDirectoryName = System.Text.RegularExpressions.Regex.Replace(transmissionDirectoryName, @"\s\(.*?\)$", "");
|
||||||
transmissionDirectoryName = destinationFolder + (destinationFolder.EndsWith("/") ? String.Empty : "/") + transmissionDirectoryName.Replace(" ", ".");
|
transmissionDirectoryName = destinationFolder + (destinationFolder.EndsWith("/") ? String.Empty : "/") + transmissionDirectoryName.Replace(" ", ".");
|
||||||
Console.WriteLine("to " + transmissionDirectoryName);
|
Console.WriteLine($"{items.Count()} item(s) to {transmissionDirectoryName}");
|
||||||
|
|
||||||
Console.WriteLine("Continue?");
|
Console.WriteLine("Continue?");
|
||||||
if(Console.ReadKey().KeyChar != 'y')
|
if(Console.ReadKey().KeyChar != 'y')
|
||||||
@@ -94,6 +94,14 @@ namespace MassTorrentAdd
|
|||||||
Metainfo = Convert.ToBase64String(torrentFileData)
|
Metainfo = Convert.ToBase64String(torrentFileData)
|
||||||
};
|
};
|
||||||
var torrentInfo = transmissionClient.TorrentAdd(newTorrent);
|
var torrentInfo = transmissionClient.TorrentAdd(newTorrent);
|
||||||
|
var seedSettings = new Transmission.API.RPC.Arguments.TorrentSettings()
|
||||||
|
{
|
||||||
|
SeedRatioLimit = 1.0,
|
||||||
|
SeedRatioMode = 1,
|
||||||
|
IDs = [torrentInfo.ID]
|
||||||
|
};
|
||||||
|
|
||||||
|
transmissionClient.TorrentSet(seedSettings);
|
||||||
//transmissionClient.TorrentVerify(new object[] { torrentInfo.ID });
|
//transmissionClient.TorrentVerify(new object[] { torrentInfo.ID });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user