Compare commits
15 Commits
ed79ee5d0f
...
14ba71005f
Author | SHA1 | Date | |
---|---|---|---|
14ba71005f | |||
22c4c0eb2c | |||
44f8d369c3 | |||
c0e6da144e | |||
51a1ae72ca | |||
79bbc92467 | |||
ae5be31c89 | |||
eebe25a378 | |||
0f3da4ec81 | |||
0b77dc1172 | |||
37cf47bc17 | |||
4cce2e04cb | |||
5465ac4e5c | |||
dd4d5a81ee | |||
a05e1914e3 |
@ -6,8 +6,13 @@ namespace Tranga.Jobs;
|
||||
public class DownloadChapter : Job
|
||||
{
|
||||
public Chapter chapter { get; init; }
|
||||
|
||||
public DownloadChapter(GlobalBase clone, MangaConnector connector, Chapter chapter, DateTime lastExecution, string? parentJobId = null) : base(clone, connector, lastExecution, parentJobId: parentJobId)
|
||||
{
|
||||
this.chapter = chapter;
|
||||
}
|
||||
|
||||
public DownloadChapter(GlobalBase clone, MangaConnector connector, Chapter chapter) : base(clone, connector)
|
||||
public DownloadChapter(GlobalBase clone, MangaConnector connector, Chapter chapter, string? parentJobId = null) : base(clone, connector, parentJobId: parentJobId)
|
||||
{
|
||||
this.chapter = chapter;
|
||||
}
|
||||
|
@ -6,8 +6,15 @@ namespace Tranga.Jobs;
|
||||
public class DownloadNewChapters : Job
|
||||
{
|
||||
public Manga manga { get; init; }
|
||||
|
||||
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, DateTime lastExecution,
|
||||
bool recurring = false, TimeSpan? recurrence = null, string? parentJobId = null) : base(clone, connector, lastExecution, recurring,
|
||||
recurrence, parentJobId)
|
||||
{
|
||||
this.manga = manga;
|
||||
}
|
||||
|
||||
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, bool recurring = false, TimeSpan? recurrence = null) : base (clone, connector, recurring, recurrence)
|
||||
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, bool recurring = false, TimeSpan? recurrence = null, string? parentJobId = null) : base (clone, connector, recurring, recurrence, parentJobId)
|
||||
{
|
||||
this.manga = manga;
|
||||
}
|
||||
@ -26,13 +33,13 @@ public class DownloadNewChapters : Job
|
||||
{
|
||||
Chapter[] chapters = mangaConnector.GetNewChapters(manga);
|
||||
this.progressToken.increments = chapters.Length;
|
||||
List<Job> subJobs = new();
|
||||
List<Job> jobs = new();
|
||||
foreach (Chapter chapter in chapters)
|
||||
{
|
||||
DownloadChapter downloadChapterJob = new(this, this.mangaConnector, chapter);
|
||||
subJobs.Add(downloadChapterJob);
|
||||
DownloadChapter downloadChapterJob = new(this, this.mangaConnector, chapter, parentJobId: this.id);
|
||||
jobs.Add(downloadChapterJob);
|
||||
}
|
||||
progressToken.Complete();
|
||||
return subJobs;
|
||||
return jobs;
|
||||
}
|
||||
}
|
@ -11,8 +11,10 @@ public abstract class Job : GlobalBase
|
||||
public DateTime? lastExecution { get; private set; }
|
||||
public DateTime nextExecution => NextExecution();
|
||||
public string id => GetId();
|
||||
internal IEnumerable<Job>? subJobs { get; private set; }
|
||||
public string? parentJobId { get; init; }
|
||||
|
||||
public Job(GlobalBase clone, MangaConnector connector, bool recurring = false, TimeSpan? recurrenceTime = null) : base(clone)
|
||||
internal Job(GlobalBase clone, MangaConnector connector, bool recurring = false, TimeSpan? recurrenceTime = null, string? parentJobId = null) : base(clone)
|
||||
{
|
||||
this.mangaConnector = connector;
|
||||
this.progressToken = new ProgressToken(0);
|
||||
@ -21,57 +23,70 @@ public abstract class Job : GlobalBase
|
||||
throw new ArgumentException("If recurrence is set to true, a recurrence time has to be provided.");
|
||||
else if(recurring && recurrenceTime is not null)
|
||||
this.lastExecution = DateTime.Now.Subtract((TimeSpan)recurrenceTime);
|
||||
this.recurrenceTime = recurrenceTime;
|
||||
this.recurrenceTime = recurrenceTime ?? TimeSpan.Zero;
|
||||
this.parentJobId = parentJobId;
|
||||
}
|
||||
|
||||
internal Job(GlobalBase clone, MangaConnector connector, DateTime lastExecution, bool recurring = false,
|
||||
TimeSpan? recurrenceTime = null, string? parentJobId = null) : base(clone)
|
||||
{
|
||||
this.mangaConnector = connector;
|
||||
this.progressToken = new ProgressToken(0);
|
||||
this.recurring = recurring;
|
||||
if (recurring && recurrenceTime is null)
|
||||
throw new ArgumentException("If recurrence is set to true, a recurrence time has to be provided.");
|
||||
this.lastExecution = lastExecution;
|
||||
this.recurrenceTime = recurrenceTime ?? TimeSpan.Zero;
|
||||
this.parentJobId = parentJobId;
|
||||
}
|
||||
|
||||
protected abstract string GetId();
|
||||
|
||||
public Job(GlobalBase clone, MangaConnector connector, ProgressToken progressToken, bool recurring = false, TimeSpan? recurrenceTime = null) : base(clone)
|
||||
public void AddSubJob(Job job)
|
||||
{
|
||||
this.mangaConnector = connector;
|
||||
this.progressToken = progressToken;
|
||||
this.recurring = recurring;
|
||||
if (recurring && recurrenceTime is null)
|
||||
throw new ArgumentException("If recurrence is set to true, a recurrence time has to be provided.");
|
||||
this.recurrenceTime = recurrenceTime;
|
||||
}
|
||||
|
||||
public Job(GlobalBase clone, MangaConnector connector, int taskIncrements, bool recurring = false, TimeSpan? recurrenceTime = null) : base(clone)
|
||||
{
|
||||
this.mangaConnector = connector;
|
||||
this.progressToken = new ProgressToken(taskIncrements);
|
||||
this.recurring = recurring;
|
||||
if (recurring && recurrenceTime is null)
|
||||
throw new ArgumentException("If recurrence is set to true, a recurrence time has to be provided.");
|
||||
this.recurrenceTime = recurrenceTime;
|
||||
subJobs ??= new List<Job>();
|
||||
subJobs = subJobs.Append(job);
|
||||
}
|
||||
|
||||
private DateTime NextExecution()
|
||||
{
|
||||
if(recurring && recurrenceTime.HasValue && lastExecution.HasValue)
|
||||
if(recurrenceTime.HasValue && lastExecution.HasValue)
|
||||
return lastExecution.Value.Add(recurrenceTime.Value);
|
||||
if(recurring && recurrenceTime.HasValue && !lastExecution.HasValue)
|
||||
if(recurrenceTime.HasValue && !lastExecution.HasValue)
|
||||
return DateTime.Now;
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
public void ResetProgress()
|
||||
{
|
||||
this.progressToken = new ProgressToken(this.progressToken.increments);
|
||||
this.progressToken.increments = this.progressToken.increments - this.progressToken.incrementsCompleted;
|
||||
this.lastExecution = DateTime.Now;
|
||||
}
|
||||
|
||||
public void ExecutionEnqueue()
|
||||
{
|
||||
this.progressToken.increments = this.progressToken.increments - this.progressToken.incrementsCompleted;
|
||||
this.lastExecution = recurrenceTime is not null ? DateTime.Now.Subtract((TimeSpan)recurrenceTime) : DateTime.UnixEpoch;
|
||||
this.progressToken.Standby();
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
Log($"Cancelling {this}");
|
||||
this.progressToken.cancellationRequested = true;
|
||||
this.progressToken.Complete();
|
||||
this.progressToken.Cancel();
|
||||
this.lastExecution = DateTime.Now;
|
||||
if(subJobs is not null)
|
||||
foreach(Job subJob in subJobs)
|
||||
subJob.Cancel();
|
||||
}
|
||||
|
||||
public IEnumerable<Job> ExecuteReturnSubTasks()
|
||||
{
|
||||
progressToken.Start();
|
||||
IEnumerable<Job> ret = ExecuteReturnSubTasksInternal();
|
||||
subJobs = ExecuteReturnSubTasksInternal();
|
||||
lastExecution = DateTime.Now;
|
||||
return ret;
|
||||
return subJobs;
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<Job> ExecuteReturnSubTasksInternal();
|
||||
|
@ -11,7 +11,11 @@ public class JobBoss : GlobalBase
|
||||
public JobBoss(GlobalBase clone, HashSet<MangaConnector> connectors) : base(clone)
|
||||
{
|
||||
if (File.Exists(settings.jobsFilePath))
|
||||
{
|
||||
this.jobs = JsonConvert.DeserializeObject<HashSet<Job>>(File.ReadAllText(settings.jobsFilePath), new JobJsonConverter(this, new MangaConnectorJsonConverter(this, connectors)))!;
|
||||
foreach (Job job in this.jobs)
|
||||
this.jobs.FirstOrDefault(jjob => jjob.id == job.parentJobId)?.AddSubJob(job);
|
||||
}
|
||||
else
|
||||
this.jobs = new();
|
||||
foreach (DownloadNewChapters ncJob in this.jobs.Where(job => job is DownloadNewChapters))
|
||||
@ -29,10 +33,16 @@ public class JobBoss : GlobalBase
|
||||
{
|
||||
Log($"Added {job}");
|
||||
this.jobs.Add(job);
|
||||
File.WriteAllText(settings.jobsFilePath, JsonConvert.SerializeObject(this.jobs));
|
||||
ExportJobsList();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddJobs(IEnumerable<Job> jobsToAdd)
|
||||
{
|
||||
foreach (Job job in jobsToAdd)
|
||||
AddJob(job);
|
||||
}
|
||||
|
||||
public bool ContainsJobLike(Job job)
|
||||
{
|
||||
if (job is DownloadChapter dcJob)
|
||||
@ -51,13 +61,17 @@ public class JobBoss : GlobalBase
|
||||
Log($"Removing {job}");
|
||||
job.Cancel();
|
||||
this.jobs.Remove(job);
|
||||
if(job.subJobs is not null)
|
||||
RemoveJobs(job.subJobs);
|
||||
ExportJobsList();
|
||||
}
|
||||
|
||||
public void RemoveJobs(IEnumerable<Job> jobsToRemove)
|
||||
public void RemoveJobs(IEnumerable<Job?> jobsToRemove)
|
||||
{
|
||||
Log($"Removing {jobsToRemove.Count()} jobs.");
|
||||
foreach (Job job in jobsToRemove)
|
||||
RemoveJob(job);
|
||||
foreach (Job? job in jobsToRemove)
|
||||
if(job is not null)
|
||||
RemoveJob(job);
|
||||
}
|
||||
|
||||
public IEnumerable<Job> GetJobsLike(string? connectorName = null, string? internalId = null, string? chapterNumber = null)
|
||||
@ -87,7 +101,10 @@ public class JobBoss : GlobalBase
|
||||
public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Manga? publication = null,
|
||||
Chapter? chapter = null)
|
||||
{
|
||||
return GetJobsLike(mangaConnector?.name, publication?.internalId, chapter?.chapterNumber);
|
||||
if (chapter is not null)
|
||||
return GetJobsLike(mangaConnector?.name, chapter.Value.parentManga.internalId, chapter?.chapterNumber);
|
||||
else
|
||||
return GetJobsLike(mangaConnector?.name, publication?.internalId);
|
||||
}
|
||||
|
||||
public Job? GetJobById(string jobId)
|
||||
@ -122,6 +139,7 @@ public class JobBoss : GlobalBase
|
||||
Queue<Job> connectorJobQueue = mangaConnectorJobQueue[job.mangaConnector];
|
||||
if(!connectorJobQueue.Contains(job))
|
||||
connectorJobQueue.Enqueue(job);
|
||||
job.ExecutionEnqueue();
|
||||
}
|
||||
|
||||
public void AddJobsToQueue(IEnumerable<Job> jobs)
|
||||
@ -130,20 +148,42 @@ public class JobBoss : GlobalBase
|
||||
AddJobToQueue(job);
|
||||
}
|
||||
|
||||
public void ExportJobsList()
|
||||
{
|
||||
Log($"Exporting {settings.jobsFilePath}");
|
||||
while(IsFileInUse(settings.jobsFilePath))
|
||||
Thread.Sleep(10);
|
||||
File.WriteAllText(settings.jobsFilePath, JsonConvert.SerializeObject(this.jobs));
|
||||
}
|
||||
|
||||
public void CheckJobs()
|
||||
{
|
||||
foreach (Job job in jobs.Where(job => job.nextExecution < DateTime.Now && !QueueContainsJob(job)).OrderBy(job => job.nextExecution))
|
||||
AddJobToQueue(job);
|
||||
foreach (Queue<Job> jobQueue in mangaConnectorJobQueue.Values)
|
||||
{
|
||||
if(jobQueue.Count < 1)
|
||||
continue;
|
||||
Job queueHead = jobQueue.Peek();
|
||||
if (queueHead.progressToken.state is ProgressToken.State.Complete)
|
||||
if (queueHead.progressToken.state is ProgressToken.State.Complete or ProgressToken.State.Cancelled)
|
||||
{
|
||||
if(queueHead.recurring)
|
||||
queueHead.Reset();
|
||||
switch (queueHead)
|
||||
{
|
||||
case DownloadChapter:
|
||||
RemoveJob(queueHead);
|
||||
break;
|
||||
case DownloadNewChapters:
|
||||
if(queueHead.recurring)
|
||||
queueHead.progressToken.Complete();
|
||||
break;
|
||||
}
|
||||
jobQueue.Dequeue();
|
||||
}else if(queueHead.progressToken.state is ProgressToken.State.Standby)
|
||||
AddJobsToQueue(jobQueue.Peek().ExecuteReturnSubTasks());
|
||||
}else if (queueHead.progressToken.state is ProgressToken.State.Standby)
|
||||
{
|
||||
Job[] subJobs = jobQueue.Peek().ExecuteReturnSubTasks().ToArray();
|
||||
AddJobs(subJobs);
|
||||
AddJobsToQueue(subJobs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -34,8 +34,10 @@ public class JobJsonConverter : JsonConverter
|
||||
}
|
||||
}))!,
|
||||
jo.GetValue("manga")!.ToObject<Manga>(),
|
||||
jo.GetValue("lastExecution")!.ToObject<DateTime>(),
|
||||
jo.GetValue("recurring")!.Value<bool>(),
|
||||
jo.GetValue("recurrenceTime")!.ToObject<TimeSpan?>());
|
||||
jo.GetValue("recurrenceTime")!.ToObject<TimeSpan?>(),
|
||||
jo.GetValue("parentJobId")!.Value<string?>());
|
||||
}
|
||||
|
||||
if (jo.ContainsKey("chapter"))//DownloadChapter
|
||||
@ -48,7 +50,9 @@ public class JobJsonConverter : JsonConverter
|
||||
this._mangaConnectorJsonConverter
|
||||
}
|
||||
}))!,
|
||||
jo.GetValue("chapter")!.ToObject<Chapter>());
|
||||
jo.GetValue("chapter")!.ToObject<Chapter>(),
|
||||
DateTime.UnixEpoch,
|
||||
jo.GetValue("parentJobId")!.Value<string?>());
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
|
@ -7,7 +7,7 @@ public class ProgressToken
|
||||
public int incrementsCompleted { get; set; }
|
||||
public float progress => GetProgress();
|
||||
|
||||
public enum State { Running, Complete, Standby }
|
||||
public enum State { Running, Complete, Standby, Cancelled }
|
||||
public State state { get; private set; }
|
||||
|
||||
public ProgressToken(int increments)
|
||||
@ -15,7 +15,7 @@ public class ProgressToken
|
||||
this.cancellationRequested = false;
|
||||
this.increments = increments;
|
||||
this.incrementsCompleted = 0;
|
||||
this.state = State.Standby;
|
||||
this.state = State.Complete;
|
||||
}
|
||||
|
||||
private float GetProgress()
|
||||
@ -32,6 +32,11 @@ public class ProgressToken
|
||||
state = State.Complete;
|
||||
}
|
||||
|
||||
public void Standby()
|
||||
{
|
||||
state = State.Standby;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
state = State.Running;
|
||||
@ -41,4 +46,9 @@ public class ProgressToken
|
||||
{
|
||||
state = State.Complete;
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
state = State.Cancelled;
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ internal class DownloadClient : GlobalBase
|
||||
if(referrer is not null)
|
||||
requestMessage.Headers.Referrer = new Uri(referrer);
|
||||
_lastExecutedRateLimit[requestType] = DateTime.Now;
|
||||
Log($"Requesting {requestType} {url}");
|
||||
//Log($"Requesting {requestType} {url}");
|
||||
response = Client.Send(requestMessage);
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
|
@ -45,7 +45,7 @@ public class Server : GlobalBase
|
||||
try
|
||||
{
|
||||
HttpListenerContext context = this._listener.GetContext();
|
||||
Log($"{context.Request.HttpMethod} {context.Request.Url} {context.Request.UserAgent}");
|
||||
//Log($"{context.Request.HttpMethod} {context.Request.Url} {context.Request.UserAgent}");
|
||||
Task t = new(() =>
|
||||
{
|
||||
HandleRequest(context);
|
||||
@ -211,9 +211,10 @@ public class Server : GlobalBase
|
||||
private void HandlePost(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
|
||||
string? connectorName, internalId;
|
||||
string? connectorName, internalId, jobId;
|
||||
MangaConnector connector;
|
||||
Manga manga;
|
||||
Job? job;
|
||||
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
|
||||
switch (path)
|
||||
{
|
||||
@ -248,8 +249,8 @@ public class Server : GlobalBase
|
||||
SendResponse(HttpStatusCode.Accepted, response);
|
||||
break;
|
||||
case "Jobs/StartNow":
|
||||
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
|
||||
!_parent.jobBoss.TryGetJobById(jobId, out Job? job))
|
||||
if (!requestVariables.TryGetValue("jobId", out jobId) ||
|
||||
!_parent.jobBoss.TryGetJobById(jobId, out job))
|
||||
{
|
||||
SendResponse(HttpStatusCode.BadRequest, response);
|
||||
break;
|
||||
@ -257,6 +258,16 @@ public class Server : GlobalBase
|
||||
_parent.jobBoss.AddJobToQueue(job!);
|
||||
SendResponse(HttpStatusCode.Accepted, response);
|
||||
break;
|
||||
case "Jobs/Cancel":
|
||||
if (!requestVariables.TryGetValue("jobId", out jobId) ||
|
||||
!_parent.jobBoss.TryGetJobById(jobId, out job))
|
||||
{
|
||||
SendResponse(HttpStatusCode.BadRequest, response);
|
||||
break;
|
||||
}
|
||||
job!.Cancel();
|
||||
SendResponse(HttpStatusCode.Accepted, response);
|
||||
break;
|
||||
case "Settings/UpdateDownloadLocation":
|
||||
if (!requestVariables.TryGetValue("downloadLocation", out string? downloadLocation) ||
|
||||
!requestVariables.TryGetValue("moveFiles", out string? moveFilesStr) ||
|
||||
@ -362,7 +373,7 @@ public class Server : GlobalBase
|
||||
switch (path)
|
||||
{
|
||||
case "Jobs":
|
||||
if (!requestVariables.TryGetValue("jobID", out string? jobId) ||
|
||||
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
|
||||
!_parent.jobBoss.TryGetJobById(jobId, out Job? job))
|
||||
{
|
||||
SendResponse(HttpStatusCode.BadRequest, response);
|
||||
@ -371,33 +382,6 @@ public class Server : GlobalBase
|
||||
_parent.jobBoss.RemoveJob(job!);
|
||||
SendResponse(HttpStatusCode.Accepted, response);
|
||||
break;
|
||||
case "Jobs/DownloadChapter":
|
||||
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
||||
!requestVariables.TryGetValue("internalId", out internalId) ||
|
||||
!requestVariables.TryGetValue("chapterNumber", out string? chapterNumber) ||
|
||||
_parent.GetConnector(connectorName) is null ||
|
||||
_parent.GetPublicationById(internalId) is null)
|
||||
{
|
||||
SendResponse(HttpStatusCode.BadRequest, response);
|
||||
break;
|
||||
}
|
||||
_parent.jobBoss.RemoveJobs(_parent.jobBoss.GetJobsLike(connectorName, internalId, chapterNumber));
|
||||
SendResponse(HttpStatusCode.Accepted, response);
|
||||
break;
|
||||
case "Jobs/MonitorManga":
|
||||
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
||||
!requestVariables.TryGetValue("internalId", out internalId) ||
|
||||
_parent.GetConnector(connectorName) is null ||
|
||||
_parent.GetPublicationById(internalId) is null)
|
||||
{
|
||||
SendResponse(HttpStatusCode.BadRequest, response);
|
||||
break;
|
||||
}
|
||||
connector = _parent.GetConnector(connectorName)!;
|
||||
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
||||
_parent.jobBoss.RemoveJobs(_parent.jobBoss.GetJobsLike(connector, manga));
|
||||
SendResponse(HttpStatusCode.Accepted, response);
|
||||
break;
|
||||
case "Jobs/DownloadNewChapters":
|
||||
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
||||
!requestVariables.TryGetValue("internalId", out internalId) ||
|
||||
@ -441,7 +425,7 @@ public class Server : GlobalBase
|
||||
|
||||
private void SendResponse(HttpStatusCode statusCode, HttpListenerResponse response, object? content = null)
|
||||
{
|
||||
Log($"Response: {statusCode} {content}");
|
||||
//Log($"Response: {statusCode} {content}");
|
||||
response.StatusCode = (int)statusCode;
|
||||
response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
|
||||
response.AddHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
||||
|
@ -65,7 +65,7 @@ public partial class Tranga : GlobalBase
|
||||
while (keepRunning)
|
||||
{
|
||||
jobBoss.CheckJobs();
|
||||
Thread.Sleep(1000);
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
});
|
||||
t.Start();
|
||||
|
Loading…
Reference in New Issue
Block a user