Implementing a streaming setup with Usenet
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user