a whole new method - work order mementos
This commit is contained in:
parent
9628a7b8b9
commit
826b181cf1
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
tmp/
|
working/
|
||||||
# ---> VisualStudio
|
# ---> VisualStudio
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
@ -9,6 +9,7 @@ namespace ttrss_co_client
|
|||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
public string PodcastTitlePrefix { get; set; }
|
public string PodcastTitlePrefix { get; set; }
|
||||||
public string OnDoneCopy { get; set; }
|
public string OnDoneCopy { get; set; }
|
||||||
|
public string WorkingDirectory { get; set; } = "./working/";
|
||||||
public IEnumerable<FeedAction> feedActions { get; set; }
|
public IEnumerable<FeedAction> feedActions { get; set; }
|
||||||
public class FeedAction
|
public class FeedAction
|
||||||
{
|
{
|
||||||
|
368
Program.cs
368
Program.cs
@ -2,149 +2,180 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using ttrss_co_client.tasks;
|
||||||
|
|
||||||
namespace ttrss_co_client
|
namespace ttrss_co_client
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
|
private static ttrss.ApiClient ttrssClient;
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// var tasktypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
// .SelectMany(domainAssembly => domainAssembly.GetTypes())
|
||||||
|
// .Where(type => type.IsSubclassOf(typeof(Phase1Task)) && !type.IsAbstract)
|
||||||
|
// .ToList();
|
||||||
|
// foreach (var subtype in subtypes)
|
||||||
|
// {
|
||||||
|
// var inst = (CoClientTask) Activator.CreateInstance(subtype);
|
||||||
|
// }
|
||||||
|
|
||||||
var conf = Configure();
|
var conf = Configure();
|
||||||
Console.WriteLine($"{DateTime.Now.ToLongTimeString()}");
|
Console.WriteLine($"{DateTime.Now.ToLongTimeString()}");
|
||||||
|
|
||||||
var ttrssClient = new ttrss.ApiClient(conf.BaseURI);
|
ttrssClient = new ttrss.ApiClient(conf.BaseURI);
|
||||||
await ttrssClient.Login(conf.Username, conf.Password);
|
await ttrssClient.Login(conf.Username, conf.Password);
|
||||||
|
|
||||||
var loggedin = await ttrssClient.IsLoggedIn();
|
var loggedin = await ttrssClient.IsLoggedIn();
|
||||||
Console.WriteLine($"logged in: {loggedin}");
|
Console.WriteLine($"logged in: {loggedin}");
|
||||||
|
|
||||||
|
|
||||||
|
#region phase 1
|
||||||
|
Console.WriteLine("===== phase 1 =====");
|
||||||
|
|
||||||
|
var Phase1Tasks = new List<Task<WorkOrder>>();
|
||||||
|
|
||||||
|
Phase1Task.TtrssClient = ttrssClient;
|
||||||
|
Phase1Task.Conf = conf;
|
||||||
|
var phase1TaskTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.SelectMany(domainAssembly => domainAssembly.GetTypes())
|
||||||
|
.Where(type => type.IsSubclassOf(typeof(Phase1Task)) && !type.IsAbstract)
|
||||||
|
.ToList();
|
||||||
|
var phase1TaskConcretions = new List<Phase1Task>();
|
||||||
|
foreach (var phase1TaskType in phase1TaskTypes)
|
||||||
|
{
|
||||||
|
var concretion = (Phase1Task) Activator.CreateInstance(phase1TaskType);
|
||||||
|
concretion.TriggerLabel = conf.feedActions.FirstOrDefault(fa => fa.command == concretion.TaskName).triggerlabelCaption;
|
||||||
|
phase1TaskConcretions.Add(concretion);
|
||||||
|
}
|
||||||
|
Console.WriteLine($"{phase1TaskConcretions.Count()} phase 1 task types understood");
|
||||||
|
|
||||||
var unreadFeeds = await ttrssClient.GetFeeds(cat_id: -3, unread_only: true);
|
var unreadFeeds = await ttrssClient.GetFeeds(cat_id: -3, unread_only: true);
|
||||||
|
Console.WriteLine($"{unreadFeeds.Count()} feeds unread");
|
||||||
foreach (var uf in unreadFeeds)
|
foreach (var uf in unreadFeeds)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"unread feed: {uf.title}");
|
Console.WriteLine($"unread feed: {uf.title}");
|
||||||
var headlines = await ttrssClient.GetHeadlines(uf.id, view_mode: ttrss.ApiClient.VIEWMODE.Unread, include_attachments: true);
|
var headlines = await ttrssClient.GetHeadlines(uf.id, view_mode: ttrss.ApiClient.VIEWMODE.Unread, include_attachments: true);
|
||||||
foreach (var hl in headlines)
|
foreach (var hl in headlines)
|
||||||
{
|
{
|
||||||
var labelsWRTFeed = (await ttrssClient.GetLabels(hl.id));
|
var labelsWRTArticle = (await ttrssClient.GetLabels(hl.id));
|
||||||
var actionsForFeed = conf.feedActions.Where(fa =>
|
var actionsForFeed = conf.feedActions.Where(fa =>
|
||||||
labelsWRTFeed.Where(l => l.@checked).Select(l => l.caption).Contains(fa.triggerlabelCaption))?.ToList();
|
labelsWRTArticle.Where(l => l.@checked).Select(l => l.caption).Contains(fa.triggerlabelCaption))?.ToList();
|
||||||
if (actionsForFeed != null && actionsForFeed.Any())
|
if (actionsForFeed != null && actionsForFeed.Any())
|
||||||
{
|
{
|
||||||
var sponsorCheck = await SponsorCheck(hl);
|
var action = actionsForFeed.First();
|
||||||
if (!sponsorCheck.Item1)
|
|
||||||
|
Console.WriteLine($" headline {hl.title} has label {action.triggerlabelCaption} / action {action.command}");
|
||||||
|
|
||||||
|
var appropriatePhase1Task = phase1TaskConcretions.FirstOrDefault(tt => tt.TaskName == action.command);
|
||||||
|
if(appropriatePhase1Task != null)
|
||||||
{
|
{
|
||||||
await ttrssClient.UpdateArticleNote($"{hl.note}\n[{DateTime.Now.ToLongTimeString()}] sponsorcheck: {sponsorCheck.Item2}", hl.id);
|
Phase1Tasks.Add(appropriatePhase1Task.ActOn(hl, labelsWRTArticle));
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach (var action in actionsForFeed)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {hl.title} -> action: {action.command}");
|
|
||||||
var noteString = $"{hl.note}\n[{DateTime.Now.ToLongTimeString()}] sponsorcheck: {sponsorCheck.Item2}";
|
|
||||||
ttrss.datastructures.Label nameLabel;
|
|
||||||
string podcastName;
|
|
||||||
if (!string.IsNullOrWhiteSpace(noteString))
|
|
||||||
{
|
|
||||||
noteString += $"{hl.note}\n";
|
|
||||||
}
|
|
||||||
switch (action.command)
|
|
||||||
{
|
|
||||||
case "dl":
|
|
||||||
var stdDLResult = await standardDL(hl.link.ToString());
|
|
||||||
await ttrssClient.UpdateArticleNote($"{noteString}[{DateTime.Now.ToLongTimeString()}] - {stdDLResult.Item2}", hl.id);
|
|
||||||
if (stdDLResult.Item1 == true)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {hl.title} -> dl success, removing label");
|
|
||||||
await ttrssClient.SetArticleLabel(
|
|
||||||
labelsWRTFeed.First(l => l.caption == action.triggerlabelCaption).id,
|
|
||||||
false,
|
|
||||||
hl.id);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($" {hl.title} -> dl failed");
|
Console.Error.WriteLine($"couldn't find phase 1 task {action.command} for workorder referring to article id {hl.id}!");
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case "podcastifyYT":
|
|
||||||
nameLabel = labelsWRTFeed.FirstOrDefault(l => l.caption?.StartsWith(conf.PodcastTitlePrefix) == true && l.@checked);
|
|
||||||
podcastName = nameLabel?.caption.Substring(conf.PodcastTitlePrefix.Length)
|
|
||||||
?? hl.feed_title;
|
|
||||||
var YTpodcastifyResult = await podcastifyYT(hl.link.ToString(), podcastName);
|
|
||||||
await ttrssClient.UpdateArticleNote($"{noteString}[{DateTime.Now.ToLongTimeString()}] - {YTpodcastifyResult.Item2}", hl.id);
|
|
||||||
if (YTpodcastifyResult.Item1 == true)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {hl.title} -> podcastify (YT) success, removing labels");
|
|
||||||
await ttrssClient.SetArticleLabel(
|
|
||||||
labelsWRTFeed.First(l => l.caption == action.triggerlabelCaption).id,
|
|
||||||
false,
|
|
||||||
hl.id);
|
|
||||||
if (nameLabel != null)
|
|
||||||
{
|
|
||||||
await ttrssClient.SetArticleLabel(nameLabel.id, false, hl.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"done processing feeds. phase 1 tasks launched. wait to complete.");
|
||||||
|
var remainingWork = new List<WorkOrder>();
|
||||||
|
foreach(var lingeringTask in Phase1Tasks)
|
||||||
|
{
|
||||||
|
var wo = await lingeringTask;
|
||||||
|
if(wo != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"articleId {wo.articleId} left a work order; it has {wo.Phase2TaskList.Count()} phase 2 task{(wo.Phase2TaskList.Count() == 1 ? "" : "s")}");
|
||||||
|
remainingWork.Add(wo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"phase 1 tasks complete, carrying {remainingWork.Count()} workorder{(remainingWork.Count() == 1 ? "" : "s")} to phase 2");
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region local tasks (stripping ads, converting.)
|
||||||
|
Console.WriteLine("===== phase 2 =====");
|
||||||
|
//loop through working directory looking for other work orders to add
|
||||||
|
var workOrderFiles = Directory.GetFiles($"{conf.WorkingDirectory}", "workorder.json", new EnumerationOptions(){RecurseSubdirectories = true});
|
||||||
|
Console.WriteLine($"{workOrderFiles.Count()} workorder file{(workOrderFiles.Count() == 1 ? "" : "s")} located");
|
||||||
|
foreach(var workorderFile in workOrderFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
remainingWork.Add(JsonConvert.DeserializeObject<WorkOrder>(workorderFile));
|
||||||
|
Console.WriteLine($"picked up workorder task; {workorderFile}");
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"error picking up work order file {workorderFile} - {e.Message}; {e.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"{remainingWork.Count} phase 2 workorders, between pulled from ttrss and read from files");
|
||||||
|
|
||||||
|
while(remainingWork.Count > 0)
|
||||||
|
{
|
||||||
|
//todo: solve the halting problem
|
||||||
|
//ok but seriously,
|
||||||
|
Phase2Task.TtrssClient = ttrssClient;
|
||||||
|
Phase2Task.Conf = conf;
|
||||||
|
var phase2TaskTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.SelectMany(domainAssembly => domainAssembly.GetTypes())
|
||||||
|
.Where(type => type.IsSubclassOf(typeof(Phase2Task)) && !type.IsAbstract)
|
||||||
|
.ToList();
|
||||||
|
var phase2TaskConcretions = new List<Phase2Task>();
|
||||||
|
foreach (var phase2TaskType in phase2TaskTypes)
|
||||||
|
{
|
||||||
|
var concretion = (Phase2Task) Activator.CreateInstance(phase2TaskType);
|
||||||
|
phase2TaskConcretions.Add(concretion);
|
||||||
|
}
|
||||||
|
Console.WriteLine($"{phase2TaskConcretions.Count()} phase 2 task types understood");
|
||||||
|
|
||||||
|
var Phase2Tasks = new List<Task<Tuple<Phase2Task.TaskStatus, WorkOrder>>>();
|
||||||
|
|
||||||
|
|
||||||
|
Console.WriteLine($"launching first pass over work orders in phase 2.");
|
||||||
|
foreach(var wo in remainingWork)
|
||||||
|
{
|
||||||
|
var taskName = wo.Phase2TaskList[wo.Phase2TaskList.Keys.Min()];
|
||||||
|
|
||||||
|
var appropriatePhase2Task = phase2TaskConcretions.FirstOrDefault(tt => tt.TaskName == taskName);
|
||||||
|
if(appropriatePhase2Task != null)
|
||||||
|
{
|
||||||
|
wo.Phase2TaskList.Remove(wo.Phase2TaskList.Keys.Min());
|
||||||
|
Phase2Tasks.Add(appropriatePhase2Task.ActOn(wo));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($" {hl.title} -> podcastify (YT) failed");
|
Console.Error.WriteLine($"couldn't find phase 2 task {taskName} for workorder referring to article id {wo.articleId}!");
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "podcastifyAttachment":
|
|
||||||
nameLabel = labelsWRTFeed.FirstOrDefault(l => l.caption?.StartsWith(conf.PodcastTitlePrefix) == true && l.@checked);
|
|
||||||
podcastName = nameLabel?.caption.Substring(conf.PodcastTitlePrefix.Length)
|
|
||||||
?? hl.feed_title;
|
|
||||||
var attachmentLink = hl.attachments.Select(a => a.content_url)?.FirstOrDefault();
|
|
||||||
var ATTpodcastifyResult = await podcastifyAttachment(attachmentLink.ToString(), podcastName, hl.title);
|
|
||||||
await ttrssClient.UpdateArticleNote($"{noteString}[{DateTime.Now.ToLongTimeString()}] - {ATTpodcastifyResult.Item2}", hl.id);
|
|
||||||
if (ATTpodcastifyResult.Item1 == true)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {hl.title} -> podcastify (att) success, removing labels");
|
|
||||||
await ttrssClient.SetArticleLabel(
|
|
||||||
labelsWRTFeed.First(l => l.caption == action.triggerlabelCaption).id,
|
|
||||||
false,
|
|
||||||
hl.id);
|
|
||||||
if (nameLabel != null)
|
|
||||||
{
|
|
||||||
await ttrssClient.SetArticleLabel(nameLabel.id, false, hl.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {hl.title} -> podcastify (att) failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
noteString += $"[{DateTime.Now.ToLongTimeString()}] - feed configured but action not recognized";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"phase 2 tasks launched. now the complex part.");
|
||||||
|
|
||||||
Console.WriteLine($"logging out");
|
remainingWork = new List<WorkOrder>();
|
||||||
|
foreach(var lingeringTask in Phase2Tasks)
|
||||||
|
{
|
||||||
|
var wo = await lingeringTask;
|
||||||
|
switch (wo.Item1)
|
||||||
|
{
|
||||||
|
case Phase2Task.TaskStatus.Done:
|
||||||
|
// :)
|
||||||
|
break;
|
||||||
|
case Phase2Task.TaskStatus.ContinueNow:
|
||||||
|
remainingWork.Add(wo.Item2);
|
||||||
|
break;
|
||||||
|
case Phase2Task.TaskStatus.TryLater:
|
||||||
|
File.WriteAllText(Path.Combine(conf.WorkingDirectory, wo.Item2.guid.ToString(), "workorder.json"), JsonConvert.SerializeObject(wo));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"{remainingWork.Count} phase 2 tasks not complete - should be picked up next run..");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
await ttrssClient.Logout();
|
await ttrssClient.Logout();
|
||||||
Console.WriteLine($"done, moving files from temporary location");
|
Console.WriteLine($"logged out of ttrss.");
|
||||||
|
|
||||||
var resulted = Directory.GetFiles("tmp", "*.*", SearchOption.AllDirectories);
|
|
||||||
if (resulted.Count() > 0)
|
|
||||||
{
|
|
||||||
foreach (var f in resulted)
|
|
||||||
{
|
|
||||||
var moveTarget = Path.Combine(conf.OnDoneCopy, f.Substring("tmp/".Length));
|
|
||||||
if (!Path.Exists(Path.GetDirectoryName(moveTarget)))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(moveTarget));
|
|
||||||
}
|
|
||||||
if (File.Exists(moveTarget))
|
|
||||||
{
|
|
||||||
Console.WriteLine("file already exists, abandoning");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.Move(f, moveTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Console.WriteLine($"done for real");
|
Console.WriteLine($"done for real");
|
||||||
}
|
}
|
||||||
static Configuration Configure(string configurationPath = "appsettings.json")
|
static Configuration Configure(string configurationPath = "appsettings.json")
|
||||||
@ -174,49 +205,6 @@ namespace ttrss_co_client
|
|||||||
|
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Tuple<bool, string>> standardDL(string articleLink)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" standard downloading {articleLink}");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var ytdl = new YoutubeDLSharp.YoutubeDL();
|
|
||||||
ytdl.YoutubeDLPath = "yt-dlp";
|
|
||||||
ytdl.FFmpegPath = "ffmpeg";
|
|
||||||
ytdl.OutputFolder = "./tmp/recent episodes";
|
|
||||||
ytdl.OutputFileTemplate = "%(upload_date)s - %(title)s - [%(id)s].%(ext)s";
|
|
||||||
var sw = new Stopwatch();
|
|
||||||
sw.Start();
|
|
||||||
var res = await ytdl.RunVideoDownload(articleLink);
|
|
||||||
sw.Stop();
|
|
||||||
var outputStr = $"{(res.Success ? "Success" : "fail")} in {sw.Elapsed}";
|
|
||||||
if (res.ErrorOutput != null && res.ErrorOutput.Length > 0)
|
|
||||||
{
|
|
||||||
outputStr += "\n" + string.Join('\n', res.ErrorOutput);
|
|
||||||
}
|
|
||||||
Console.WriteLine($" download {(res.Success ? "success" : "failed")}: {articleLink} -> {res.Data}");
|
|
||||||
if (!res.Data.EndsWith(".mp4"))
|
|
||||||
{
|
|
||||||
Console.WriteLine(" must convert video");
|
|
||||||
sw.Reset();
|
|
||||||
var outputFilename = res.Data.Substring(0, res.Data.LastIndexOf('.')) + ".mp4";
|
|
||||||
sw.Start();
|
|
||||||
var conversionProc = Process.Start("ffmpeg", $"-y -i \"{res.Data}\" \"{outputFilename}\"");
|
|
||||||
conversionProc.WaitForExit();
|
|
||||||
sw.Stop();
|
|
||||||
Console.WriteLine($" converted {res.Data} -> {outputFilename}");
|
|
||||||
File.Delete(res.Data);
|
|
||||||
outputStr += $"\nconverted in {sw.Elapsed}";
|
|
||||||
}
|
|
||||||
return new Tuple<bool, string>(res.Success, outputStr);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine($"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
|
||||||
return new Tuple<bool, string>(false, $"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static async Task<Tuple<bool, string>> podcastifyYT(string articleLink, string podcastName)
|
private static async Task<Tuple<bool, string>> podcastifyYT(string articleLink, string podcastName)
|
||||||
{
|
{
|
||||||
Console.WriteLine($" youtube-podcastifying {articleLink} ({podcastName})");
|
Console.WriteLine($" youtube-podcastifying {articleLink} ({podcastName})");
|
||||||
@ -296,50 +284,50 @@ namespace ttrss_co_client
|
|||||||
return new Tuple<bool, string>(false, $"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
return new Tuple<bool, string>(false, $"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static async Task<Tuple<bool, string>> SponsorCheck(ttrss.datastructures.Headline hl)
|
// private static async Task<Tuple<bool, string>> SponsorCheck(ttrss.datastructures.Headline hl)
|
||||||
{
|
// {
|
||||||
if (!hl.link.Host.EndsWith("youtube.com"))
|
// if (!hl.link.Host.EndsWith("youtube.com"))
|
||||||
{
|
// {
|
||||||
return new Tuple<bool, string>(true, "sponsorblock, sadly, only exists for youtube");
|
// return new Tuple<bool, string>(true, "sponsorblock, sadly, only exists for youtube");
|
||||||
}
|
// }
|
||||||
var match = Regex.Match(hl.link.Query, "v=([^&]+)(&|$)");
|
// var match = Regex.Match(hl.link.Query, "v=([^&]+)(&|$)");
|
||||||
var videoId = match.Groups?[1].Value;
|
// var videoId = match.Groups?[1].Value;
|
||||||
var c = new HttpClient();
|
// var c = new HttpClient();
|
||||||
|
|
||||||
var sponsorblockcheck = await c.GetAsync($"https://sponsor.ajay.app/api/skipSegments?videoID={videoId}&category=sponsor&category=selfpromo&category=interaciton&category=intro&category=outro&category=preview");
|
// var sponsorblockcheck = await c.GetAsync($"https://sponsor.ajay.app/api/skipSegments?videoID={videoId}&category=sponsor&category=selfpromo&category=interaciton&category=intro&category=outro&category=preview");
|
||||||
if (sponsorblockcheck.StatusCode == System.Net.HttpStatusCode.NotFound)
|
// if (sponsorblockcheck.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||||
{
|
// {
|
||||||
Console.WriteLine($"sponsorblock reports that {videoId} has no entries (yet)");
|
// Console.WriteLine($"sponsorblock reports that {videoId} has no entries (yet)");
|
||||||
var updateTimestamp = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
// var updateTimestamp = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||||
updateTimestamp = updateTimestamp.AddSeconds(hl.updated).ToLocalTime();
|
// updateTimestamp = updateTimestamp.AddSeconds(hl.updated).ToLocalTime();
|
||||||
if (DateTime.Now - updateTimestamp > TimeSpan.FromMinutes(45))
|
// if (DateTime.Now - updateTimestamp > TimeSpan.FromMinutes(45))
|
||||||
{
|
// {
|
||||||
return new Tuple<bool, string>(true, $"updated {updateTimestamp} (more than 45 minutes ago), going to give up waiting for sponsorblock");
|
// return new Tuple<bool, string>(true, $"updated {updateTimestamp} (more than 45 minutes ago), going to give up waiting for sponsorblock");
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
return new Tuple<bool, string>(false, "none found, waiting");
|
// return new Tuple<bool, string>(false, "none found, waiting");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
var segments = JsonConvert.DeserializeObject<IEnumerable<sponsorblock.Segment>>(await sponsorblockcheck.Content.ReadAsStringAsync());
|
// var segments = JsonConvert.DeserializeObject<IEnumerable<sponsorblock.Segment>>(await sponsorblockcheck.Content.ReadAsStringAsync());
|
||||||
if (segments.Count() > 1)
|
// if (segments.Count() > 1)
|
||||||
{
|
// {
|
||||||
return new Tuple<bool, string>(true, $"{segments.Count()} segments");
|
// return new Tuple<bool, string>(true, $"{segments.Count()} segments");
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
return new Tuple<bool, string>(false, $"no segments");
|
// return new Tuple<bool, string>(false, $"no segments");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
catch (Exception e)
|
// catch (Exception e)
|
||||||
{
|
// {
|
||||||
return new Tuple<bool, string>(false, $"{e.ToString()} - {e.Message}");
|
// return new Tuple<bool, string>(false, $"{e.ToString()} - {e.Message}");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@
|
|||||||
"password": "sordph1sh",
|
"password": "sordph1sh",
|
||||||
"podcastTitlePrefix": "[podcast title] - ",
|
"podcastTitlePrefix": "[podcast title] - ",
|
||||||
"onDoneCopy":"./",
|
"onDoneCopy":"./",
|
||||||
|
"workingDirectory":"working/",
|
||||||
"feedActions":
|
"feedActions":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
15
tasks/Convert.cs
Normal file
15
tasks/Convert.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// using System.Diagnostics;
|
||||||
|
// using ttrss_co_client.ttrss;
|
||||||
|
// using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
// namespace ttrss_co_client.tasks
|
||||||
|
// {
|
||||||
|
// ///<summary>ffmpegify</summary>
|
||||||
|
// public class Convert : Phase2Task
|
||||||
|
// {
|
||||||
|
// public override async Task<WorkOrder> ActOn(WorkOrder wo)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
15
tasks/Phase1Task.cs
Normal file
15
tasks/Phase1Task.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using ttrss_co_client.ttrss;
|
||||||
|
using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
namespace ttrss_co_client.tasks
|
||||||
|
{
|
||||||
|
///<summary>generally, download</summary>
|
||||||
|
public abstract class Phase1Task
|
||||||
|
{
|
||||||
|
public virtual string TaskName { get { return this.GetType().ToString(); } }
|
||||||
|
public static ApiClient TtrssClient { get; set; }
|
||||||
|
public static Configuration Conf { get; set; }
|
||||||
|
public virtual string TriggerLabel { get; set; }
|
||||||
|
public abstract Task<WorkOrder> ActOn(Headline headline, IEnumerable<Label> labels);
|
||||||
|
}
|
||||||
|
}
|
16
tasks/Phase2Task.cs
Normal file
16
tasks/Phase2Task.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using ttrss_co_client.ttrss;
|
||||||
|
using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
namespace ttrss_co_client.tasks
|
||||||
|
{
|
||||||
|
///<summary>convert, rip ads, etc</summary>
|
||||||
|
public abstract class Phase2Task
|
||||||
|
{
|
||||||
|
public string TaskName { get { return this.GetType().ToString(); } }
|
||||||
|
public static ApiClient TtrssClient { get; set; }
|
||||||
|
public static Configuration Conf { get; set; }
|
||||||
|
|
||||||
|
public enum TaskStatus {Done, ContinueNow, TryLater}
|
||||||
|
public abstract Task<Tuple<TaskStatus, WorkOrder>> ActOn(WorkOrder taskSpec);
|
||||||
|
}
|
||||||
|
}
|
102
tasks/PodcastifyAttachment.cs
Normal file
102
tasks/PodcastifyAttachment.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// using System.Diagnostics;
|
||||||
|
// using ttrss_co_client.ttrss;
|
||||||
|
// using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
// namespace ttrss_co_client.tasks
|
||||||
|
// {
|
||||||
|
// ///<summary>download directly from an RSS attachment</summary>
|
||||||
|
// public class PodcastifyAttachment : Phase1Task
|
||||||
|
// {
|
||||||
|
// public override async Task<WorkOrder> ActOn(Headline headline, IEnumerable<Label> labelsWRTArticle)
|
||||||
|
// {
|
||||||
|
|
||||||
|
|
||||||
|
// // case "podcastifyAttachment":
|
||||||
|
// // nameLabel = labelsWRTArticle.FirstOrDefault(l => l.caption?.StartsWith(conf.PodcastTitlePrefix) == true && l.@checked);
|
||||||
|
// // podcastName = nameLabel?.caption.Substring(conf.PodcastTitlePrefix.Length)
|
||||||
|
// // ?? hl.feed_title;
|
||||||
|
// // var attachmentLink = hl.attachments.Select(a => a.content_url)?.FirstOrDefault();
|
||||||
|
// // var ATTpodcastifyResult = await podcastifyAttachment(attachmentLink.ToString(), podcastName, hl.title);
|
||||||
|
// // await ttrssClient.UpdateArticleNote($"{noteString}[{DateTime.Now.ToLongTimeString()}] - {ATTpodcastifyResult.Item2}", hl.id);
|
||||||
|
// // if (ATTpodcastifyResult.Item1 == true)
|
||||||
|
// // {
|
||||||
|
// // Console.WriteLine($" {hl.title} -> podcastify (att) success, removing labels");
|
||||||
|
// // await ttrssClient.SetArticleLabel(
|
||||||
|
// // labelsWRTArticle.First(l => l.caption == action.triggerlabelCaption).id,
|
||||||
|
// // false,
|
||||||
|
// // hl.id);
|
||||||
|
// // if (nameLabel != null)
|
||||||
|
// // {
|
||||||
|
// // await ttrssClient.SetArticleLabel(nameLabel.id, false, hl.id);
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // Console.WriteLine($" {hl.title} -> podcastify (att) failed");
|
||||||
|
// // }
|
||||||
|
// // break;
|
||||||
|
|
||||||
|
|
||||||
|
// // Console.WriteLine($" youtube podcastify: {headline.link.ToString()}");
|
||||||
|
// // var myGuid = Guid.NewGuid();
|
||||||
|
// // var ytdl = new YoutubeDLSharp.YoutubeDL();
|
||||||
|
// // ytdl.YoutubeDLPath = "yt-dlp";
|
||||||
|
// // ytdl.FFmpegPath = "ffmpeg";
|
||||||
|
// // ytdl.OutputFolder = $"{Conf.WorkingDirectory}/{myGuid}";
|
||||||
|
// // ytdl.OutputFileTemplate = "%(upload_date)s - %(title)s - [%(id)s].%(ext)s";
|
||||||
|
// // var sw = new Stopwatch();
|
||||||
|
|
||||||
|
// // try
|
||||||
|
// // {
|
||||||
|
// // sw.Start();
|
||||||
|
// // var res = await ytdl.RunVideoDownload(headline.link.ToString(), overrideOptions: new YoutubeDLSharp.Options.OptionSet() { });
|
||||||
|
// // sw.Stop();
|
||||||
|
|
||||||
|
// // var outputStr = $"download {(res.Success ? "success" : "fail")} in {sw.Elapsed}";
|
||||||
|
// // if (res.ErrorOutput != null && res.ErrorOutput.Length > 0)
|
||||||
|
// // {
|
||||||
|
// // outputStr += "\n" + string.Join('\n', res.ErrorOutput);
|
||||||
|
// // }
|
||||||
|
// // Console.WriteLine($" {headline.link} -> {res.Data}\n{outputStr}");
|
||||||
|
// // await TtrssClient.SetArticleLabel(labelsWRTArticle.First(l => l.caption?.ToLower() == this.TriggerLabel.ToLower()).id, false, headline.id);
|
||||||
|
// // Console.WriteLine($" {headline.title}: label removed");
|
||||||
|
// // if (!res.Success)
|
||||||
|
// // {
|
||||||
|
// // await TtrssClient.UpdateArticleNote($"{headline.note}\n[{DateTime.Now.ToLongTimeString()}] - standard dl failed; {string.Join('\n', res.ErrorOutput)}", hl.id);
|
||||||
|
// // return null;
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // var outputFilename = res.Data;
|
||||||
|
// // Console.WriteLine($"{headline.title} downloaded, shipping off to conversion. That task can determine if there's no work.");
|
||||||
|
// // var toReturn = new WorkOrder()
|
||||||
|
// // {
|
||||||
|
// // articleId = headline.id, //<-- that way adblocker, if I should want to suppress it, can be labelled on ttrss
|
||||||
|
// // nextTask = "convert",
|
||||||
|
// // data = new Dictionary<string, string>()
|
||||||
|
// // };
|
||||||
|
// // toReturn.data["path"] = outputFilename;
|
||||||
|
// // toReturn.data["target"] = outputFilename.Substring(0, res.Data.LastIndexOf('.')) + ".mp4";
|
||||||
|
// // return toReturn;
|
||||||
|
// // // sw.Reset();
|
||||||
|
// // // outputFilename =
|
||||||
|
// // // sw.Start();
|
||||||
|
// // // var conversionProc = Process.Start("ffmpeg", $"-y -i \"{res.Data}\" \"{outputFilename}\"");
|
||||||
|
// // // conversionProc.WaitForExit();
|
||||||
|
// // // sw.Stop();
|
||||||
|
// // // Console.WriteLine($" converted {res.Data} -> {outputFilename}");
|
||||||
|
// // // File.Delete(res.Data);
|
||||||
|
// // //outputStr += $"\nconverted in {sw.Elapsed}";
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // catch (Exception e)
|
||||||
|
// // {
|
||||||
|
// // Console.Error.WriteLine($"fatal error in standard DL for {headline.link}");
|
||||||
|
// // Console.Error.WriteLine($"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
||||||
|
// // await TtrssClient.UpdateArticleNote($"{headline.note}\n[{DateTime.Now.ToLongTimeString()}] - fatal error {e.ToString()}: {e.Message}.\n{e.StackTrace}", hl.id);
|
||||||
|
// // return null;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
99
tasks/PodcastifyYT.cs
Normal file
99
tasks/PodcastifyYT.cs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// using System.Diagnostics;
|
||||||
|
// using ttrss_co_client.ttrss;
|
||||||
|
// using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
// namespace ttrss_co_client.tasks
|
||||||
|
// {
|
||||||
|
// ///<summary>download from YT, to be ripped into a podcast</summary>
|
||||||
|
// public class PodcastifyYT : Phase1Task
|
||||||
|
// {
|
||||||
|
// public override async Task<WorkOrder> ActOn(Headline headline, IEnumerable<Label> labelsWRTArticle)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// // case "podcastifyYT":
|
||||||
|
// // nameLabel = labelsWRTArticle.FirstOrDefault(l => l.caption?.StartsWith(conf.PodcastTitlePrefix) == true && l.@checked);
|
||||||
|
// // podcastName = nameLabel?.caption.Substring(conf.PodcastTitlePrefix.Length)
|
||||||
|
// // ?? hl.feed_title;
|
||||||
|
// // var YTpodcastifyResult = await podcastifyYT(hl.link.ToString(), podcastName);
|
||||||
|
// // await ttrssClient.UpdateArticleNote($"{noteString}[{DateTime.Now.ToLongTimeString()}] - {YTpodcastifyResult.Item2}", hl.id);
|
||||||
|
// // if (YTpodcastifyResult.Item1 == true)
|
||||||
|
// // {
|
||||||
|
// // Console.WriteLine($" {hl.title} -> podcastify (YT) success, removing labels");
|
||||||
|
// // await ttrssClient.SetArticleLabel(
|
||||||
|
// // labelsWRTArticle.First(l => l.caption == action.triggerlabelCaption).id,
|
||||||
|
// // false,
|
||||||
|
// // hl.id);
|
||||||
|
// // if (nameLabel != null)
|
||||||
|
// // {
|
||||||
|
// // await ttrssClient.SetArticleLabel(nameLabel.id, false, hl.id);
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // Console.WriteLine($" {hl.title} -> podcastify (YT) failed");
|
||||||
|
// // }
|
||||||
|
// // break;
|
||||||
|
|
||||||
|
// // Console.WriteLine($" youtube podcastify: {headline.link.ToString()}");
|
||||||
|
// // var myGuid = Guid.NewGuid();
|
||||||
|
// // var ytdl = new YoutubeDLSharp.YoutubeDL();
|
||||||
|
// // ytdl.YoutubeDLPath = "yt-dlp";
|
||||||
|
// // ytdl.FFmpegPath = "ffmpeg";
|
||||||
|
// // ytdl.OutputFolder = $"{Conf.WorkingDirectory}/{myGuid}";
|
||||||
|
// // ytdl.OutputFileTemplate = "%(upload_date)s - %(title)s - [%(id)s].%(ext)s";
|
||||||
|
// // var sw = new Stopwatch();
|
||||||
|
|
||||||
|
// // try
|
||||||
|
// // {
|
||||||
|
// // sw.Start();
|
||||||
|
// // var res = await ytdl.RunVideoDownload(headline.link.ToString(), overrideOptions: new YoutubeDLSharp.Options.OptionSet() { });
|
||||||
|
// // sw.Stop();
|
||||||
|
|
||||||
|
// // var outputStr = $"download {(res.Success ? "success" : "fail")} in {sw.Elapsed}";
|
||||||
|
// // if (res.ErrorOutput != null && res.ErrorOutput.Length > 0)
|
||||||
|
// // {
|
||||||
|
// // outputStr += "\n" + string.Join('\n', res.ErrorOutput);
|
||||||
|
// // }
|
||||||
|
// // Console.WriteLine($" {headline.link} -> {res.Data}\n{outputStr}");
|
||||||
|
// // await TtrssClient.SetArticleLabel(labelsWRTArticle.First(l => l.caption?.ToLower() == this.TriggerLabel.ToLower()).id, false, headline.id);
|
||||||
|
// // Console.WriteLine($" {headline.title}: label removed");
|
||||||
|
// // if (!res.Success)
|
||||||
|
// // {
|
||||||
|
// // await TtrssClient.UpdateArticleNote($"{headline.note}\n[{DateTime.Now.ToLongTimeString()}] - standard dl failed; {string.Join('\n', res.ErrorOutput)}", hl.id);
|
||||||
|
// // return null;
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // var outputFilename = res.Data;
|
||||||
|
// // Console.WriteLine($"{headline.title} downloaded, shipping off to conversion. That task can determine if there's no work.");
|
||||||
|
// // var toReturn = new WorkOrder()
|
||||||
|
// // {
|
||||||
|
// // articleId = headline.id, //<-- that way adblocker, if I should want to suppress it, can be labelled on ttrss
|
||||||
|
// // nextTask = "convert",
|
||||||
|
// // data = new Dictionary<string, string>()
|
||||||
|
// // };
|
||||||
|
// // toReturn.data["path"] = outputFilename;
|
||||||
|
// // toReturn.data["target"] = outputFilename.Substring(0, res.Data.LastIndexOf('.')) + ".mp4";
|
||||||
|
// // return toReturn;
|
||||||
|
// // // sw.Reset();
|
||||||
|
// // // outputFilename =
|
||||||
|
// // // sw.Start();
|
||||||
|
// // // var conversionProc = Process.Start("ffmpeg", $"-y -i \"{res.Data}\" \"{outputFilename}\"");
|
||||||
|
// // // conversionProc.WaitForExit();
|
||||||
|
// // // sw.Stop();
|
||||||
|
// // // Console.WriteLine($" converted {res.Data} -> {outputFilename}");
|
||||||
|
// // // File.Delete(res.Data);
|
||||||
|
// // //outputStr += $"\nconverted in {sw.Elapsed}";
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // catch (Exception e)
|
||||||
|
// // {
|
||||||
|
// // Console.Error.WriteLine($"fatal error in standard DL for {headline.link}");
|
||||||
|
// // Console.Error.WriteLine($"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
||||||
|
// // await TtrssClient.UpdateArticleNote($"{headline.note}\n[{DateTime.Now.ToLongTimeString()}] - fatal error {e.ToString()}: {e.Message}.\n{e.StackTrace}", hl.id);
|
||||||
|
// // return null;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
25
tasks/Publish.cs
Normal file
25
tasks/Publish.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using ttrss_co_client.ttrss;
|
||||||
|
using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
namespace ttrss_co_client.tasks
|
||||||
|
{
|
||||||
|
///<summary>Move to output</summary>
|
||||||
|
public class Publish : Phase2Task
|
||||||
|
{
|
||||||
|
public override async Task<Tuple<TaskStatus, WorkOrder>>ActOn(WorkOrder workOrder)
|
||||||
|
{
|
||||||
|
var wo = workOrder as PublishWorkOrder;
|
||||||
|
File.Move(wo.Path, wo.PublishTarget, true);
|
||||||
|
var article = (await TtrssClient.GetArticles(wo.articleId))?.FirstOrDefault();
|
||||||
|
await TtrssClient.UpdateArticleNote($"{article.note}\n[{DateTime.Now.ToShortDateString()}] - copied");
|
||||||
|
return new Tuple<TaskStatus, WorkOrder>(TaskStatus.Done, wo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class PublishWorkOrder : WorkOrder
|
||||||
|
{
|
||||||
|
public string Path => data["path"];
|
||||||
|
public string PublishTarget => data["publish-target"];
|
||||||
|
}
|
||||||
|
}
|
15
tasks/Sponsorblock.cs
Normal file
15
tasks/Sponsorblock.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// using System.Diagnostics;
|
||||||
|
// using ttrss_co_client.ttrss;
|
||||||
|
// using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
// namespace ttrss_co_client.tasks
|
||||||
|
// {
|
||||||
|
// ///<summary>Move to output</summary>
|
||||||
|
// public class Sponsorblock : Phase2Task
|
||||||
|
// {
|
||||||
|
// public override async Task<WorkOrder> ActOn(WorkOrder wo)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
88
tasks/StandardDL.cs
Normal file
88
tasks/StandardDL.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using ttrss_co_client.ttrss;
|
||||||
|
using ttrss_co_client.ttrss.datastructures;
|
||||||
|
|
||||||
|
namespace ttrss_co_client.tasks
|
||||||
|
{
|
||||||
|
///<summary>download from YT</summary>
|
||||||
|
public class StandardDL : Phase1Task
|
||||||
|
{
|
||||||
|
public override string TaskName => "dl";
|
||||||
|
public override async Task<WorkOrder> ActOn(Headline headline, IEnumerable<Label> labelsWRTArticle)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" standard download: {headline.link.ToString()}");
|
||||||
|
var myGuid = Guid.NewGuid();
|
||||||
|
var ytdl = new YoutubeDLSharp.YoutubeDL();
|
||||||
|
ytdl.YoutubeDLPath = "yt-dlp";
|
||||||
|
ytdl.FFmpegPath = "ffmpeg";
|
||||||
|
ytdl.OutputFolder = $"{Conf.WorkingDirectory}/{myGuid}";
|
||||||
|
ytdl.OutputFileTemplate = "%(upload_date)s - %(title)s - [%(id)s].%(ext)s";
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sw.Start();
|
||||||
|
var res = await ytdl.RunVideoDownload(headline.link.ToString());
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
var outputStr = $"download {(res.Success ? "success" : "fail")} in {sw.Elapsed}";
|
||||||
|
if (res.ErrorOutput != null && res.ErrorOutput.Length > 0)
|
||||||
|
{
|
||||||
|
outputStr += "\n" + string.Join('\n', res.ErrorOutput);
|
||||||
|
}
|
||||||
|
Console.WriteLine($" {headline.link} -> {res.Data}\n{outputStr}");
|
||||||
|
await TtrssClient.SetArticleLabel(labelsWRTArticle.First(l => l.caption?.ToLower() == this.TriggerLabel.ToLower()).id, false, headline.id);
|
||||||
|
Console.WriteLine($" {headline.title}: label removed");
|
||||||
|
if (!res.Success)
|
||||||
|
{
|
||||||
|
await TtrssClient.UpdateArticleNote($"{headline.note}\n[{DateTime.Now.ToLongTimeString()}] - standard dl failed; {string.Join('\n', res.ErrorOutput)}", headline.id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var outputFilename = res.Data;
|
||||||
|
Console.WriteLine($"{headline.title} downloaded.");
|
||||||
|
|
||||||
|
var toReturn = new WorkOrder()
|
||||||
|
{
|
||||||
|
articleId = headline.id,//<-- that way later tasks can update the note
|
||||||
|
Phase2TaskList = new Dictionary<int, string>(),
|
||||||
|
data = new Dictionary<string, string>()
|
||||||
|
};
|
||||||
|
toReturn.data["path"] = outputFilename;
|
||||||
|
|
||||||
|
if(!outputFilename.EndsWith(".mp4"))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{headline.title} needs conversion task.");
|
||||||
|
toReturn.Phase2TaskList[0] = "convert";
|
||||||
|
toReturn.data["conversion-target"] = outputFilename.Substring(0, res.Data.LastIndexOf('.')) + ".mp4";
|
||||||
|
}
|
||||||
|
if(headline.link.Host.EndsWith("youtube.com"))
|
||||||
|
{
|
||||||
|
toReturn.Phase2TaskList[1] = "sponsorblock";
|
||||||
|
}
|
||||||
|
toReturn.Phase2TaskList[2] = "filemovePublish";
|
||||||
|
toReturn.data["publish-target"] = $"{Conf.OnDoneCopy}/recent episodes/{toReturn.data["conversion-target"]}";
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
// sw.Reset();
|
||||||
|
// outputFilename =
|
||||||
|
// sw.Start();
|
||||||
|
// var conversionProc = Process.Start("ffmpeg", $"-y -i \"{res.Data}\" \"{outputFilename}\"");
|
||||||
|
// conversionProc.WaitForExit();
|
||||||
|
// sw.Stop();
|
||||||
|
// Console.WriteLine($" converted {res.Data} -> {outputFilename}");
|
||||||
|
// File.Delete(res.Data);
|
||||||
|
//outputStr += $"\nconverted in {sw.Elapsed}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"fatal error in standard DL for {headline.link}");
|
||||||
|
Console.Error.WriteLine($"{e.ToString()}: {e.Message}.\n{e.StackTrace}");
|
||||||
|
await TtrssClient.UpdateArticleNote($"{headline.note}\n[{DateTime.Now.ToLongTimeString()}] - fatal error {e.ToString()}: {e.Message}.\n{e.StackTrace}", headline.id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
tasks/WorkOrder.cs
Normal file
12
tasks/WorkOrder.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ttrss_co_client.tasks
|
||||||
|
{
|
||||||
|
public class WorkOrder
|
||||||
|
{
|
||||||
|
public int articleId { get; set; }
|
||||||
|
public Guid guid { get; set; }
|
||||||
|
public Dictionary<int, string> Phase2TaskList { get; set; } //so youtube downloads will download, then add a conversion to this, then add an adblock, then publish
|
||||||
|
public Dictionary<string, string> data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user