Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 20 additions & 21 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,23 @@ type ActivityLog struct {
}

type JobRun struct {
ID int64 `json:"id"`
StartedAt time.Time `json:"started_at"`
FinishedAt time.Time `json:"finished_at,omitempty"`
DurationMs int64 `json:"duration_ms"`
JobID string `json:"job_id,omitempty"`
JobName string `json:"job_name"`
MediaType string `json:"media_type"` // "movie" or "show"
Mode string `json:"mode"` // "direct" or "jellyseerr"
Status string `json:"status"` // "running", "completed", "failed"
TotalFound int `json:"total_found"`
PassedFilter int `json:"passed_filters"`
Added int `json:"added"`
Requested int `json:"requested"`
Skipped int `json:"skipped"`
Rejected int `json:"rejected"`
Failed int `json:"failed"`
ErrorMessage string `json:"error_message,omitempty"`
ID int64 `json:"id"`
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
DurationMs int64 `json:"duration_ms"`
JobID string `json:"job_id,omitempty"`
JobName string `json:"job_name"`
MediaType string `json:"media_type"` // "movie" or "show"
Mode string `json:"mode"` // "direct" or "jellyseerr"
Status string `json:"status"` // "running", "completed", "failed"
TotalFound int `json:"total_found"`
PassedFilter int `json:"passed_filters"`
Added int `json:"added"`
Requested int `json:"requested"`
Skipped int `json:"skipped"`
Rejected int `json:"rejected"`
Failed int `json:"failed"`
ErrorMessage string `json:"error_message,omitempty"`
}

type ActivityDailyCount struct {
Expand Down Expand Up @@ -905,9 +905,7 @@ func (d *Database) CompleteJobRun(
err := d.db.QueryRow("SELECT started_at FROM job_runs WHERE id = ?", runID).Scan(&startedAt)
if err == nil {
durationMs = finishedAt.Sub(startedAt).Milliseconds()
if durationMs < 0 {
durationMs = 0
}
durationMs = max(durationMs, 0)
}

_, err = d.db.Exec(
Expand Down Expand Up @@ -980,7 +978,8 @@ func (d *Database) GetRecentJobRuns(limit int, jobID string) ([]JobRun, error) {
}

if finishedAt.Valid {
run.FinishedAt = finishedAt.Time
t := finishedAt.Time
run.FinishedAt = &t
}
if jobIDVal.Valid {
run.JobID = jobIDVal.String
Expand Down
11 changes: 2 additions & 9 deletions internal/rest/v1/routes/activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,7 @@ func RegisterActivityRoutes(router fiber.Router, gctx global.Context) {
// Legacy support for limit param
if limitStr := c.Query("limit"); limitStr != "" {
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
if parsedLimit > maxLegacyLimit {
pageSize = maxLegacyLimit
} else {
pageSize = parsedLimit
}
pageSize = min(parsedLimit, maxLegacyLimit)
}
}

Expand Down Expand Up @@ -175,10 +171,7 @@ func RegisterActivityRoutes(router fiber.Router, gctx global.Context) {
}

// Fetch more logs than needed to apply filters and calculate total
fetchLimit := pageSize * 100 // Fetch enough for filtering
if fetchLimit > maxFetchLimit {
fetchLimit = maxFetchLimit
}
fetchLimit := min(pageSize*100, maxFetchLimit) // Fetch enough for filtering
logs, err := db.GetRecentActivityFiltered(fetchLimit, status, mediaType, jobType)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
Expand Down
12 changes: 3 additions & 9 deletions internal/rest/v1/routes/dynamic_jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package routes

import (
"fmt"
"slices"
"strings"

"github.com/gofiber/fiber/v2"
Expand Down Expand Up @@ -345,14 +346,7 @@ func validateDynamicJob(job config.DynamicJob) error {
// Validate period value if provided
if job.Period != "" {
validPeriods := []string{"weekly", "monthly", "yearly", "all"}
isValid := false
for _, p := range validPeriods {
if job.Period == p {
isValid = true
break
}
}
if !isValid {
if !slices.Contains(validPeriods, job.Period) {
return fmt.Errorf("invalid period: %s (must be weekly, monthly, yearly, or all)", job.Period)
}
}
Expand All @@ -362,7 +356,7 @@ func validateDynamicJob(job config.DynamicJob) error {

// previewDynamicJob returns a preview for a dynamic job
// This routes to the appropriate existing preview function based on job type
func previewDynamicJob(cfg *config.Config, db *database.Database, job config.DynamicJob) interface{} {
func previewDynamicJob(cfg *config.Config, db *database.Database, job config.DynamicJob) any {
typeDef, ok := jobs.GetJobTypeDefinition(job.Type)
if !ok {
return fiber.Map{
Expand Down
23 changes: 10 additions & 13 deletions internal/services/jobs/registry.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package jobs

import "slices"

// JobTypeDefinition describes a job type that can be instantiated as a DynamicJob
type JobTypeDefinition struct {
Type string `json:"type"` // Internal type identifier
Expand Down Expand Up @@ -120,13 +122,13 @@ var JobTypeRegistry = map[string]JobTypeDefinition{

// JobTemplate represents a pre-configured job template for quick setup
type JobTemplate struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
MediaType string `json:"media"`
Limit int `json:"limit"`
Period string `json:"period,omitempty"`
Category string `json:"category"` // "Movies" or "TV Shows"
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
MediaType string `json:"media"`
Limit int `json:"limit"`
Period string `json:"period,omitempty"`
Category string `json:"category"` // "Movies" or "TV Shows"
}

// JobTemplates contains pre-defined job configurations for common use cases
Expand Down Expand Up @@ -265,12 +267,7 @@ func SupportsMediaType(jobType, mediaType string) bool {
if !ok {
return false
}
for _, m := range def.SupportedMedia {
if m == mediaType {
return true
}
}
return false
return slices.Contains(def.SupportedMedia, mediaType)
}

// GetAllJobTypes returns all job type definitions
Expand Down