Basic functionality commit
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
@@ -7,9 +6,9 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AnitomySharp.NET6" Version="0.5.1" />
|
||||||
<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="TMDbLib" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
</Project>
|
|
||||||
@@ -1,10 +1,84 @@
|
|||||||
namespace AnimeAnnouncer
|
using AnimeAnnouncer.RSS;
|
||||||
|
using TMDbLib.Client;
|
||||||
|
|
||||||
|
namespace AnimeAnnouncer
|
||||||
{
|
{
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
|
private static NyaaIndexer nyaaIndexer= new NyaaIndexer();
|
||||||
|
|
||||||
|
private const String TMDBApiKey = "";
|
||||||
|
|
||||||
|
private static TMDbClient tmdbClient = new TMDbClient(TMDBApiKey);
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Hello, World!");
|
Console.WriteLine("Starting...");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchResults = await tmdbClient.SearchTvShowAsync(title);
|
||||||
|
|
||||||
|
var supposedShowId = searchResults.Results.First().Id;
|
||||||
|
|
||||||
|
var showResult = await tmdbClient.GetTvShowAsync(supposedShowId);
|
||||||
|
|
||||||
|
var latestSeason = showResult.Seasons.OrderByDescending(s => s.SeasonNumber).First();
|
||||||
|
|
||||||
|
if(latestSeason.SeasonNumber != int.Parse(season))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failing release due to TMDB's season number {latestSeason.SeasonNumber} not matching title season {season}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(latestSeason.EpisodeCount != int.Parse(episode))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failing release due to TMDB's last episode number of {latestSeason.EpisodeCount} not matching title episode number {episode}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
//AnnounceFinishedSeason(title);
|
||||||
|
Console.WriteLine($"Choosing {title} as a finished season!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
101
AnimeAnnouncer/RSS/IndexerBase.cs
Normal file
101
AnimeAnnouncer/RSS/IndexerBase.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace AnimeAnnouncer.RSS
|
||||||
|
{
|
||||||
|
public abstract class IndexerBase<T>
|
||||||
|
where T : IndexerItem
|
||||||
|
{
|
||||||
|
public IndexerBase()
|
||||||
|
{
|
||||||
|
IterationSleepTime = 300;
|
||||||
|
PostEventSleepTime = 1;
|
||||||
|
}
|
||||||
|
public virtual void Start(Boolean populateSeenURLs = true)
|
||||||
|
{
|
||||||
|
Running = true;
|
||||||
|
//Populate SeenURLs
|
||||||
|
if(populateSeenURLs)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fillSeenURLs(GetRSSResponse());
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
ThreadPool.QueueUserWorkItem(p => rssLoop());
|
||||||
|
}
|
||||||
|
public virtual void Stop()
|
||||||
|
{
|
||||||
|
Running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual String GetRSSResponse()
|
||||||
|
{
|
||||||
|
HttpWebRequest req = WebRequest.Create(GetRSSURL()) as HttpWebRequest;
|
||||||
|
HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
|
||||||
|
return new StreamReader(resp.GetResponseStream()).ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String GetRSSURL();
|
||||||
|
|
||||||
|
protected abstract void HandleRSSResponse(String rssResponse);
|
||||||
|
|
||||||
|
protected virtual void OnNewPost(T post)
|
||||||
|
{
|
||||||
|
NewPost?.Invoke(post);
|
||||||
|
}
|
||||||
|
protected virtual void OnRssFinished()
|
||||||
|
{
|
||||||
|
RssReadFinished?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rssLoop()
|
||||||
|
{
|
||||||
|
while (Running)
|
||||||
|
{
|
||||||
|
Thread.Sleep(IterationSleepTime * 1000);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String xml = GetRSSResponse();
|
||||||
|
HandleRSSResponse(xml);
|
||||||
|
RssReadFinished?.Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void fillSeenURLs(String xml)
|
||||||
|
{
|
||||||
|
XDocument doc = XDocument.Parse(xml);
|
||||||
|
var items = doc.Descendants("channel").Elements("item");
|
||||||
|
foreach (var item in items)
|
||||||
|
SeenURLs.Add(item.Element("link").Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<T> NewPost;
|
||||||
|
public event Action RssReadFinished;
|
||||||
|
/// <summary>
|
||||||
|
/// Time in seconds to wait before raising the new post event. To avoid site hammering.
|
||||||
|
/// </summary>
|
||||||
|
public Int32 PostEventSleepTime { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Time in seconds to wait before refreshing RSS again
|
||||||
|
/// </summary>
|
||||||
|
public Int32 IterationSleepTime { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
protected readonly List<String> SeenURLs = new List<string>();
|
||||||
|
private Boolean Running;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
AnimeAnnouncer/RSS/IndexerItem.cs
Normal file
17
AnimeAnnouncer/RSS/IndexerItem.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AnimeAnnouncer.RSS
|
||||||
|
{
|
||||||
|
public class IndexerItem
|
||||||
|
{
|
||||||
|
public String Title { get; set; }
|
||||||
|
public String GUID { get; set; }
|
||||||
|
public String Link { get; set; }
|
||||||
|
public String Comments { get; set; }
|
||||||
|
public DateTime PublicationDate { get; set; }
|
||||||
|
public String Category { get; set; }
|
||||||
|
public String Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
79
AnimeAnnouncer/RSS/NyaaIndexer.cs
Normal file
79
AnimeAnnouncer/RSS/NyaaIndexer.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace AnimeAnnouncer.RSS
|
||||||
|
{
|
||||||
|
public class NyaaIndexer : IndexerBase<NyaaItem>
|
||||||
|
{
|
||||||
|
|
||||||
|
public NyaaIndexer()
|
||||||
|
{
|
||||||
|
// 30 minutes
|
||||||
|
IterationSleepTime = (30 * 60);
|
||||||
|
|
||||||
|
//Slow down for debugging to avoid API hammer
|
||||||
|
PostEventSleepTime = 5;
|
||||||
|
}
|
||||||
|
protected override string GetRSSURL()
|
||||||
|
{
|
||||||
|
return "https://nyaa.si/?page=rss&q=Dual+Audio&c=0_0&f=0";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleRSSResponse(string rssResponse)
|
||||||
|
{
|
||||||
|
XDocument doc = null;
|
||||||
|
XNamespace nyaaNS;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doc = XDocument.Parse(rssResponse);
|
||||||
|
nyaaNS = doc.Root.GetNamespaceOfPrefix("nyaa");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var items = from item in doc.Descendants("channel").Elements("item")
|
||||||
|
select new
|
||||||
|
{
|
||||||
|
Title = item.Element("title").Value,
|
||||||
|
Published = item.Element("pubDate").Value,
|
||||||
|
Category = item.Element(nyaaNS + "category").Value,
|
||||||
|
CategoryID = item.Element(nyaaNS + "categoryId").Value,
|
||||||
|
Size = item.Element(nyaaNS + "size").Value,
|
||||||
|
Link = item.Element("link").Value,
|
||||||
|
Description = item.Element("description").Value,
|
||||||
|
PostID = item.Element("guid").Value,
|
||||||
|
};
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
if (SeenURLs.Contains(item.Link))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
NyaaItem post = new NyaaItem()
|
||||||
|
{
|
||||||
|
Title = item.Title,
|
||||||
|
PublicationDate = DateTime.ParseExact(item.Published, "ddd, dd MMM yyyy HH:mm:ss K", new System.Globalization.CultureInfo("en-US")).ToUniversalTime(),
|
||||||
|
Category = item.Category,
|
||||||
|
Link = item.Link,
|
||||||
|
Description = item.Description,
|
||||||
|
GUID = item.PostID,
|
||||||
|
Size = Double.Parse(item.Size.Substring(0,item.Size.IndexOf(' ')))
|
||||||
|
};
|
||||||
|
|
||||||
|
SeenURLs.Add(item.Link);
|
||||||
|
OnNewPost(post);
|
||||||
|
Thread.Sleep(PostEventSleepTime * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
AnimeAnnouncer/RSS/NyaaItem.cs
Normal file
10
AnimeAnnouncer/RSS/NyaaItem.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace AnimeAnnouncer.RSS;
|
||||||
|
|
||||||
|
public class NyaaItem : IndexerItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the release in GB
|
||||||
|
/// </summary>
|
||||||
|
public Double Size { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user