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

181 lines
4.6 KiB
Go

package usenet
import (
"context"
"fmt"
"github.com/rs/zerolog"
"github.com/sirrobot01/decypharr/internal/config"
"github.com/sirrobot01/decypharr/internal/logger"
"github.com/sirrobot01/decypharr/internal/nntp"
"io"
"os"
)
// Usenet interface for usenet operations
type Usenet interface {
Start(ctx context.Context) error
IsReady() chan struct{}
ProcessNZB(ctx context.Context, req *ProcessRequest) (*NZB, error)
GetDownloadByteRange(nzoID string, filename string) (int64, int64, error)
Close()
Logger() zerolog.Logger
Stream(ctx context.Context, nzbID string, filename string, start, end int64, writer io.Writer) error
Store() Store
Client() *nntp.Client
}
// Client implements UsenetClient
type usenet struct {
client *nntp.Client
store Store
processor *Processor
parser *NZBParser
streamer *Streamer
cache *SegmentCache
logger zerolog.Logger
ready chan struct{}
}
// New creates a new usenet client
func New() Usenet {
cfg := config.Get()
usenetConfig := cfg.Usenet
if usenetConfig == nil || len(usenetConfig.Providers) == 0 {
// No usenet providers configured, return nil
return nil
}
_logger := logger.New("usenet")
client, err := nntp.NewClient(usenetConfig.Providers)
if err != nil {
_logger.Error().Err(err).Msg("Failed to create usenet client")
return nil
}
store := NewStore(cfg, _logger)
processor, err := NewProcessor(usenetConfig, _logger, store, client)
if err != nil {
_logger.Error().Err(err).Msg("Failed to create usenet processor")
return nil
}
// Create cache and components
cache := NewSegmentCache(_logger)
parser := NewNZBParser(client, cache, _logger)
streamer := NewStreamer(client, cache, store, usenetConfig.Chunks, _logger)
return &usenet{
store: store,
client: client,
processor: processor,
parser: parser,
streamer: streamer,
cache: cache,
logger: _logger,
ready: make(chan struct{}),
}
}
func (c *usenet) Start(ctx context.Context) error {
// Init the client
if err := c.client.InitPools(); err != nil {
c.logger.Error().Err(err).Msg("Failed to initialize usenet client pools")
return fmt.Errorf("failed to initialize usenet client pools: %w", err)
}
// Initialize the store
if err := c.store.Load(); err != nil {
c.logger.Error().Err(err).Msg("Failed to initialize usenet store")
return fmt.Errorf("failed to initialize usenet store: %w", err)
}
close(c.ready)
c.logger.Info().Msg("Usenet client initialized")
return nil
}
func (c *usenet) IsReady() chan struct{} {
return c.ready
}
func (c *usenet) Store() Store {
return c.store
}
func (c *usenet) Client() *nntp.Client {
return c.client
}
func (c *usenet) Logger() zerolog.Logger {
return c.logger
}
func (c *usenet) ProcessNZB(ctx context.Context, req *ProcessRequest) (*NZB, error) {
return c.processor.Process(ctx, req)
}
// GetNZB retrieves an NZB by ID
func (c *usenet) GetNZB(nzoID string) *NZB {
return c.store.Get(nzoID)
}
// DeleteNZB deletes an NZB
func (c *usenet) DeleteNZB(nzoID string) error {
return c.store.Delete(nzoID)
}
// PauseNZB pauses an NZB download
func (c *usenet) PauseNZB(nzoID string) error {
return c.store.UpdateStatus(nzoID, "paused")
}
// ResumeNZB resumes an NZB download
func (c *usenet) ResumeNZB(nzoID string) error {
return c.store.UpdateStatus(nzoID, "downloading")
}
func (c *usenet) Close() {
if c.store != nil {
if err := c.store.Close(); err != nil {
c.logger.Error().Err(err).Msg("Failed to close store")
}
}
c.logger.Info().Msg("Usenet client closed")
}
// GetListing returns the file listing of the NZB directory
func (c *usenet) GetListing(folder string) []os.FileInfo {
return c.store.GetListing(folder)
}
func (c *usenet) GetDownloadByteRange(nzoID string, filename string) (int64, int64, error) {
return int64(0), int64(0), nil
}
func (c *usenet) RemoveNZB(nzoID string) error {
if err := c.store.Delete(nzoID); err != nil {
return fmt.Errorf("failed to delete NZB %s: %w", nzoID, err)
}
c.logger.Info().Msgf("NZB %s deleted successfully", nzoID)
return nil
}
// Stream streams a file using the new simplified streaming system
func (c *usenet) Stream(ctx context.Context, nzbID string, filename string, start, end int64, writer io.Writer) error {
// Get NZB from store
nzb := c.GetNZB(nzbID)
if nzb == nil {
return fmt.Errorf("NZB %s not found", nzbID)
}
// Get file
file := nzb.GetFileByName(filename)
if file == nil {
return fmt.Errorf("file %s not found in NZB %s", filename, nzbID)
}
if file.NzbID == "" {
file.NzbID = nzbID // Ensure NZB ID is set for the file
}
// Stream using the new streamer
return c.streamer.Stream(ctx, file, start, end, writer)
}