feat: improve daemon resilience, streaming, and usenet downloads
- Add daemon state persistence and stale resume file cleanup - Add TriggerPoll for WebSocket resume actions - Improve stream server with graceful shutdown and connection tracking - Add desktop notifications for download completion - Add media file organization with Movies/TV Shows detection - Improve usenet downloader with progress tracking and resume support - Add self-update package with GitHub release verification - Downgrade tablewriter to v0.0.5 (v1.x API breaking change)
This commit is contained in:
parent
e332c0a6e4
commit
197e33956a
24 changed files with 2310 additions and 84 deletions
|
|
@ -3,9 +3,11 @@ package engine
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -15,7 +17,7 @@ import (
|
|||
|
||||
// fileProvider abstracts where to get a file reader for streaming.
|
||||
type fileProvider interface {
|
||||
NewFileReader(ctx context.Context) torrent.Reader
|
||||
NewFileReader(ctx context.Context) io.ReadSeekCloser
|
||||
FileName() string
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +51,7 @@ type torrentFileProvider struct {
|
|||
file *torrent.File
|
||||
}
|
||||
|
||||
func (p *torrentFileProvider) NewFileReader(ctx context.Context) torrent.Reader {
|
||||
func (p *torrentFileProvider) NewFileReader(ctx context.Context) io.ReadSeekCloser {
|
||||
reader := p.file.NewReader()
|
||||
reader.SetResponsive()
|
||||
reader.SetReadahead(5 * 1024 * 1024)
|
||||
|
|
@ -61,6 +63,33 @@ func (p *torrentFileProvider) FileName() string {
|
|||
return filepath.Base(p.file.DisplayPath())
|
||||
}
|
||||
|
||||
// diskFileProvider serves a file from disk.
|
||||
type diskFileProvider struct {
|
||||
path string
|
||||
name string
|
||||
}
|
||||
|
||||
func (p *diskFileProvider) NewFileReader(_ context.Context) io.ReadSeekCloser {
|
||||
f, err := os.Open(p.path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *diskFileProvider) FileName() string { return p.name }
|
||||
|
||||
// NewStreamServerFromDisk creates a server that streams a file from disk.
|
||||
func NewStreamServerFromDisk(filePath string, port int) *StreamServer {
|
||||
return &StreamServer{
|
||||
provider: &diskFileProvider{
|
||||
path: filePath,
|
||||
name: filepath.Base(filePath),
|
||||
},
|
||||
port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins serving the file on localhost. Returns the full URL.
|
||||
func (ss *StreamServer) Start(ctx context.Context) (string, error) {
|
||||
mux := http.NewServeMux()
|
||||
|
|
@ -106,6 +135,10 @@ func (ss *StreamServer) Shutdown(ctx context.Context) error {
|
|||
|
||||
func (ss *StreamServer) handler(w http.ResponseWriter, r *http.Request) {
|
||||
reader := ss.provider.NewFileReader(r.Context())
|
||||
if reader == nil {
|
||||
http.Error(w, "file not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
w.Header().Set("Content-Type", mimeTypeFromExt(ss.provider.FileName()))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue