Files
decypharr/pkg/usenet/processor.go
2025-08-01 15:27:24 +01:00

146 lines
3.8 KiB
Go

package usenet
import (
"context"
"fmt"
"github.com/rs/zerolog"
"github.com/sirrobot01/decypharr/internal/config"
"github.com/sirrobot01/decypharr/internal/nntp"
"github.com/sirrobot01/decypharr/pkg/arr"
"path/filepath"
"time"
)
// Processor handles NZB processing and download orchestration
type Processor struct {
store Store
parser *NZBParser
downloadWorker *DownloadWorker
logger zerolog.Logger
client *nntp.Client
}
// ProcessRequest represents a request to process an NZB
type ProcessRequest struct {
NZBContent []byte
Name string
Arr *arr.Arr
Action string // "download", "symlink", "none"
DownloadDir string
}
// NewProcessor creates a new usenet processor
func NewProcessor(config *config.Usenet, logger zerolog.Logger, store Store, client *nntp.Client) (*Processor, error) {
processor := &Processor{
store: store,
logger: logger.With().Str("component", "usenet-processor").Logger(),
client: client,
}
// Initialize download worker
processor.downloadWorker = NewDownloadWorker(config, client, processor)
processor.parser = NewNZBParser(client, nil, processor.logger)
return processor, nil
}
// Process processes an NZB for download/streaming
func (p *Processor) Process(ctx context.Context, req *ProcessRequest) (*NZB, error) {
if len(req.NZBContent) == 0 {
return nil, fmt.Errorf("NZB content is empty")
}
// Validate NZB content
if err := ValidateNZB(req.NZBContent); err != nil {
return nil, fmt.Errorf("invalid NZB content: %w", err)
}
nzb, err := p.process(ctx, req)
if err != nil {
p.logger.Error().
Err(err).
Msg("Failed to process NZB content")
return nil, fmt.Errorf("failed to process NZB content: %w", err)
}
return nzb, nil
}
func (p *Processor) process(ctx context.Context, req *ProcessRequest) (*NZB, error) {
nzb, err := p.parser.Parse(ctx, req.Name, req.Arr.Name, req.NZBContent)
if err != nil {
p.logger.Error().
Err(err).
Msg("Failed to parse NZB content")
return nil, fmt.Errorf("failed to parse NZB content: %w", err)
}
if nzb == nil {
p.logger.Error().
Msg("Parsed NZB is nil")
return nil, fmt.Errorf("parsed NZB is nil")
}
p.logger.Info().
Str("nzb_id", nzb.ID).
Msg("Successfully parsed NZB content")
if existing := p.store.Get(nzb.ID); existing != nil {
p.logger.Info().Str("nzb_id", nzb.ID).Msg("NZB already exists")
return existing, nil
}
p.logger.Info().
Str("nzb_id", nzb.ID).
Msg("Creating new NZB download job")
downloadDir := req.DownloadDir
if req.Arr != nil {
downloadDir = filepath.Join(downloadDir, req.Arr.Name)
}
job := &DownloadJob{
NZB: nzb,
Action: req.Action,
DownloadDir: downloadDir,
Callback: func(completedNZB *NZB, err error) {
if err != nil {
p.logger.Error().
Err(err).
Str("nzb_id", completedNZB.ID).
Msg("Download job failed")
return
}
p.logger.Info().
Str("nzb_id", completedNZB.ID).
Msg("Download job completed successfully")
},
}
// Check availability before submitting the job
//if err := p.downloadWorker.CheckAvailability(ctx, job); err != nil {
// p.logger.Error().
// Err(err).
// Str("nzb_id", nzb.ID).
// Msg("NZB availability check failed")
// return nil, fmt.Errorf("availability check failed for NZB %s: %w", nzb.ID, err)
//}
// Mark NZB as downloaded but not completed
nzb.Downloaded = true
nzb.AddedOn = time.Now()
p.store.AddToQueue(nzb)
if err := p.store.Add(nzb); err != nil {
return nil, err
} // Add the downloaded NZB to the store asynchronously
p.logger.Info().
Str("nzb_id", nzb.ID).
Msg("NZB added to queue")
go func() {
if err := p.downloadWorker.Process(ctx, job); err != nil {
p.logger.Error().
Err(err).
Str("nzb_id", nzb.ID).
Msg("Failed to submit download job")
}
}()
return nzb, nil
}