Compare commits

16 Commits

Author SHA1 Message Date
5cfcecc5c3 Numerous fixes to improve functionality 2025-03-25 00:12:03 -04:00
6a50c94cf2 Minor tweaks to AiringList before trying to handle late-week airing 2025-02-17 01:03:06 -05:00
acac2a25e8 Added some guard rails around airing list to avoid announcing old shows 2025-02-05 00:24:32 -05:00
47b44fb59a Handled airing multi-post 2024-12-31 20:59:03 -05:00
965bd58799 Initial commit of Airing Soon list 2024-11-29 15:42:12 -05:00
039e3189eb Changed ShowAnnounced to save last announced season number 2024-11-19 19:52:44 -05:00
6a518b0c9b Added Levenshtein title matching; Added AiringSoonList 2024-11-19 19:36:35 -05:00
7b830659bc Added TMDbLib library reference until nuget gets the required package for episode groups 2024-11-12 16:32:24 -05:00
c423d8f3ed Added implementation of episode groups with overrides for finale 2024-11-11 21:44:36 -05:00
25b214c0d7 Fixed appSettings name to work on linux; Set newly added torrent's seed mode and ratio 2024-10-13 21:57:13 -04:00
310978054d Added anidb search link for the mastodon post 2024-10-09 20:37:36 -04:00
c1103564bb Merge branch 'MassTorrentAdd' 2024-10-09 19:54:23 -04:00
57c42bcc41 Changed season finale logic to only print when the episode was actually changed 2024-10-08 17:33:41 -04:00
7bc2ef89a2 Handled case where a 2-cour season is a single TMDB season with 2 "finales" 2024-10-07 22:23:47 -04:00
2e48daae3a Removed internal on setters for cache item so JsonSerializer could set them 2024-10-06 00:54:54 -04:00
5937ddcbd8 Get season detail to look for episodes marked as finale; multiple tweaks to avoid announcing at the incorrect time 2024-10-02 16:49:53 -04:00
6 changed files with 325 additions and 32 deletions

View File

@@ -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>

View 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; }
}
}

View File

@@ -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();

View File

@@ -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; }
} }
} }

View File

@@ -1,4 +1,5 @@
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;
@@ -7,6 +8,7 @@ using Mastonet;
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 +19,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,10 +55,16 @@ namespace AnimeAnnouncer
mastodonClient = new MastodonClient(mastodonInstance, mastodonToken); mastodonClient = new MastodonClient(mastodonInstance, mastodonToken);
} }
airingSoonList = tmdbCache.GetAiringList().Result;
if(airingSoonList == null)
airingSoonList = new List<AiringSoonItem>();
nyaaIndexer.NewPost += OnNewPost; nyaaIndexer.NewPost += OnNewPost;
nyaaIndexer.RssReadFinished += OnRssReadFinished; nyaaIndexer.RssReadFinished += OnRssReadFinished;
//nyaaIndexer.IterationSleepTime = 60;
nyaaIndexer.Start(true); nyaaIndexer.Start(true);
Console.WriteLine("Press enter to quit..."); Console.WriteLine("Press enter to quit...");
Console.ReadLine(); Console.ReadLine();
} }
@@ -73,6 +83,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 +110,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 +127,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 +151,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 +166,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 +215,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)
{ {
@@ -181,18 +237,70 @@ namespace AnimeAnnouncer
if(latestSeasonNumber != int.Parse(season)) if(latestSeasonNumber != int.Parse(season))
{ {
Console.WriteLine($"Failing release due to TMDB's season number {latestSeasonNumber} not matching title season {season}"); var titleSeason = int.Parse(season);
return; 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.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}");
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}")) if(await tmdbCache.KeyExists($"ShowAnnounced-{supposedShowId}"))
@@ -200,16 +308,19 @@ namespace AnimeAnnouncer
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 +331,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
@@ -235,17 +395,95 @@ namespace AnimeAnnouncer
//Prepare Status //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 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; 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;
statusText = statusText[..497] + "..";
}
_ = mastodonClient.PublishStatus(statusText,
Visibility.Public, mediaIds: attachment != null ? new String[] {attachment.Id} : null);
if (statusText.Length + linkLength + 3 >= 500)
{
statusText = statusText[..(500 - (linkLength + 3))];
}
statusText += $"..{anidbLink}";
_ = mastodonClient.PublishStatus(statusText,
Visibility.Public, mediaIds: attachment != null ? new String[] { attachment.Id } : null);
}
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];
} }
} }
} }

View File

@@ -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");
@@ -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 });
} }