Basic functionality commit
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
@@ -7,9 +6,9 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AnitomySharp.NET6" Version="0.5.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="TMDbLib" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,10 +1,84 @@
|
||||
namespace AnimeAnnouncer
|
||||
using AnimeAnnouncer.RSS;
|
||||
using TMDbLib.Client;
|
||||
|
||||
namespace AnimeAnnouncer
|
||||
{
|
||||
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)
|
||||
{
|
||||
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