2
0

Compare commits

..

No commits in common. "14ba71005f06592fc552366ef2fb179ae5765e3b" and "ed79ee5d0f320e38e0af82f81ebb817496fda1ad" have entirely different histories.

9 changed files with 81 additions and 146 deletions

View File

@ -6,13 +6,8 @@ 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, string? parentJobId = null) : base(clone, connector, parentJobId: parentJobId)
public DownloadChapter(GlobalBase clone, MangaConnector connector, Chapter chapter) : base(clone, connector)
{
this.chapter = chapter;
}

View File

@ -6,15 +6,8 @@ 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, string? parentJobId = null) : base (clone, connector, recurring, recurrence, parentJobId)
public DownloadNewChapters(GlobalBase clone, MangaConnector connector, Manga manga, bool recurring = false, TimeSpan? recurrence = null) : base (clone, connector, recurring, recurrence)
{
this.manga = manga;
}
@ -33,13 +26,13 @@ public class DownloadNewChapters : Job
{
Chapter[] chapters = mangaConnector.GetNewChapters(manga);
this.progressToken.increments = chapters.Length;
List<Job> jobs = new();
List<Job> subJobs = new();
foreach (Chapter chapter in chapters)
{
DownloadChapter downloadChapterJob = new(this, this.mangaConnector, chapter, parentJobId: this.id);
jobs.Add(downloadChapterJob);
DownloadChapter downloadChapterJob = new(this, this.mangaConnector, chapter);
subJobs.Add(downloadChapterJob);
}
progressToken.Complete();
return jobs;
return subJobs;
}
}

View File

@ -11,10 +11,8 @@ 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; }
internal Job(GlobalBase clone, MangaConnector connector, bool recurring = false, TimeSpan? recurrenceTime = null, string? parentJobId = null) : base(clone)
public Job(GlobalBase clone, MangaConnector connector, bool recurring = false, TimeSpan? recurrenceTime = null) : base(clone)
{
this.mangaConnector = connector;
this.progressToken = new ProgressToken(0);
@ -23,70 +21,57 @@ 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 ?? 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;
this.recurrenceTime = recurrenceTime;
}
protected abstract string GetId();
public void AddSubJob(Job job)
public Job(GlobalBase clone, MangaConnector connector, ProgressToken progressToken, bool recurring = false, TimeSpan? recurrenceTime = null) : base(clone)
{
subJobs ??= new List<Job>();
subJobs = subJobs.Append(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;
}
private DateTime NextExecution()
{
if(recurrenceTime.HasValue && lastExecution.HasValue)
if(recurring && recurrenceTime.HasValue && lastExecution.HasValue)
return lastExecution.Value.Add(recurrenceTime.Value);
if(recurrenceTime.HasValue && !lastExecution.HasValue)
if(recurring && recurrenceTime.HasValue && !lastExecution.HasValue)
return DateTime.Now;
return DateTime.MaxValue;
}
public void ResetProgress()
public void Reset()
{
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();
this.progressToken = new ProgressToken(this.progressToken.increments);
}
public void Cancel()
{
Log($"Cancelling {this}");
this.progressToken.cancellationRequested = true;
this.progressToken.Cancel();
this.lastExecution = DateTime.Now;
if(subJobs is not null)
foreach(Job subJob in subJobs)
subJob.Cancel();
this.progressToken.Complete();
}
public IEnumerable<Job> ExecuteReturnSubTasks()
{
progressToken.Start();
subJobs = ExecuteReturnSubTasksInternal();
IEnumerable<Job> ret = ExecuteReturnSubTasksInternal();
lastExecution = DateTime.Now;
return subJobs;
return ret;
}
protected abstract IEnumerable<Job> ExecuteReturnSubTasksInternal();

View File

@ -11,11 +11,7 @@ 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))
@ -33,16 +29,10 @@ public class JobBoss : GlobalBase
{
Log($"Added {job}");
this.jobs.Add(job);
ExportJobsList();
File.WriteAllText(settings.jobsFilePath, JsonConvert.SerializeObject(this.jobs));
}
}
public void AddJobs(IEnumerable<Job> jobsToAdd)
{
foreach (Job job in jobsToAdd)
AddJob(job);
}
public bool ContainsJobLike(Job job)
{
if (job is DownloadChapter dcJob)
@ -61,17 +51,13 @@ 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)
if(job is not null)
RemoveJob(job);
foreach (Job job in jobsToRemove)
RemoveJob(job);
}
public IEnumerable<Job> GetJobsLike(string? connectorName = null, string? internalId = null, string? chapterNumber = null)
@ -101,10 +87,7 @@ public class JobBoss : GlobalBase
public IEnumerable<Job> GetJobsLike(MangaConnector? mangaConnector = null, Manga? publication = null,
Chapter? chapter = null)
{
if (chapter is not null)
return GetJobsLike(mangaConnector?.name, chapter.Value.parentManga.internalId, chapter?.chapterNumber);
else
return GetJobsLike(mangaConnector?.name, publication?.internalId);
return GetJobsLike(mangaConnector?.name, publication?.internalId, chapter?.chapterNumber);
}
public Job? GetJobById(string jobId)
@ -139,7 +122,6 @@ 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)
@ -148,42 +130,20 @@ 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 or ProgressToken.State.Cancelled)
if (queueHead.progressToken.state is ProgressToken.State.Complete)
{
switch (queueHead)
{
case DownloadChapter:
RemoveJob(queueHead);
break;
case DownloadNewChapters:
if(queueHead.recurring)
queueHead.progressToken.Complete();
break;
}
if(queueHead.recurring)
queueHead.Reset();
jobQueue.Dequeue();
}else if (queueHead.progressToken.state is ProgressToken.State.Standby)
{
Job[] subJobs = jobQueue.Peek().ExecuteReturnSubTasks().ToArray();
AddJobs(subJobs);
AddJobsToQueue(subJobs);
}
}else if(queueHead.progressToken.state is ProgressToken.State.Standby)
AddJobsToQueue(jobQueue.Peek().ExecuteReturnSubTasks());
}
}
}

View File

@ -34,10 +34,8 @@ 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("parentJobId")!.Value<string?>());
jo.GetValue("recurrenceTime")!.ToObject<TimeSpan?>());
}
if (jo.ContainsKey("chapter"))//DownloadChapter
@ -50,9 +48,7 @@ public class JobJsonConverter : JsonConverter
this._mangaConnectorJsonConverter
}
}))!,
jo.GetValue("chapter")!.ToObject<Chapter>(),
DateTime.UnixEpoch,
jo.GetValue("parentJobId")!.Value<string?>());
jo.GetValue("chapter")!.ToObject<Chapter>());
}
throw new Exception();

View File

@ -7,7 +7,7 @@ public class ProgressToken
public int incrementsCompleted { get; set; }
public float progress => GetProgress();
public enum State { Running, Complete, Standby, Cancelled }
public enum State { Running, Complete, Standby }
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.Complete;
this.state = State.Standby;
}
private float GetProgress()
@ -32,11 +32,6 @@ public class ProgressToken
state = State.Complete;
}
public void Standby()
{
state = State.Standby;
}
public void Start()
{
state = State.Running;
@ -46,9 +41,4 @@ public class ProgressToken
{
state = State.Complete;
}
public void Cancel()
{
state = State.Cancelled;
}
}

View File

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

View File

@ -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,10 +211,9 @@ public class Server : GlobalBase
private void HandlePost(HttpListenerRequest request, HttpListenerResponse response)
{
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
string? connectorName, internalId, jobId;
string? connectorName, internalId;
MangaConnector connector;
Manga manga;
Job? job;
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
switch (path)
{
@ -249,8 +248,8 @@ public class Server : GlobalBase
SendResponse(HttpStatusCode.Accepted, response);
break;
case "Jobs/StartNow":
if (!requestVariables.TryGetValue("jobId", out jobId) ||
!_parent.jobBoss.TryGetJobById(jobId, out job))
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
!_parent.jobBoss.TryGetJobById(jobId, out Job? job))
{
SendResponse(HttpStatusCode.BadRequest, response);
break;
@ -258,16 +257,6 @@ 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) ||
@ -373,7 +362,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);
@ -382,6 +371,33 @@ 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) ||
@ -425,7 +441,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");

View File

@ -65,7 +65,7 @@ public partial class Tranga : GlobalBase
while (keepRunning)
{
jobBoss.CheckJobs();
Thread.Sleep(100);
Thread.Sleep(1000);
}
});
t.Start();