Implementing a streaming setup with Usenet
This commit is contained in:
239
pkg/usenet/types.go
Normal file
239
pkg/usenet/types.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package usenet
|
||||
|
||||
import "time"
|
||||
|
||||
// NZB represents a torrent-like structure for NZB files
|
||||
type NZB struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title,omitempty"`
|
||||
TotalSize int64 `json:"total_size"`
|
||||
DatePosted time.Time `json:"date_posted"`
|
||||
Category string `json:"category"`
|
||||
Groups []string `json:"groups"`
|
||||
Files []NZBFile `json:"files"`
|
||||
Downloaded bool `json:"downloaded"` // Whether the NZB has been downloaded
|
||||
StreamingInfo StreamingInfo `json:"streaming_info"`
|
||||
AddedOn time.Time `json:"added_on"` // When the NZB was added to the system
|
||||
LastActivity time.Time `json:"last_activity"` // Last activity timestamp
|
||||
Status string `json:"status"` // "queued", "downloading", "completed", "failed"
|
||||
Progress float64 `json:"progress"` // Percentage of download completion
|
||||
Percentage float64 `json:"percentage"` // Percentage of download completion
|
||||
SizeDownloaded int64 `json:"size_downloaded"` // Total size downloaded so far
|
||||
ETA int64 `json:"eta"` // Estimated time of arrival in seconds
|
||||
Speed int64 `json:"speed"` // Download speed in bytes per second
|
||||
CompletedOn time.Time `json:"completed_on"` // When the NZB was completed
|
||||
IsBad bool `json:"is_bad"`
|
||||
Storage string `json:"storage"`
|
||||
FailMessage string `json:"fail_message,omitempty"` // Error message if the download failed
|
||||
Password string `json:"-,omitempty"` // Password for encrypted RAR files
|
||||
}
|
||||
|
||||
// StreamingInfo contains metadata for streaming capabilities
|
||||
type StreamingInfo struct {
|
||||
IsStreamable bool `json:"is_streamable"`
|
||||
MainFileIndex int `json:"main_file_index"` // Index of the main media file
|
||||
HasParFiles bool `json:"has_par_files"`
|
||||
HasRarFiles bool `json:"has_rar_files"`
|
||||
TotalSegments int `json:"total_segments"`
|
||||
EstimatedTime int64 `json:"estimated_time"` // Estimated download time in seconds
|
||||
}
|
||||
|
||||
type SegmentValidationInfo struct {
|
||||
ExpectedSize int64
|
||||
ActualSize int64
|
||||
Validated bool
|
||||
}
|
||||
|
||||
// NZBFile represents a grouped file with its segments
|
||||
type NZBFile struct {
|
||||
NzbID string `json:"nzo_id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
StartOffset int64 `json:"start_offset"` // This is useful for removing rar headers
|
||||
Segments []NZBSegment `json:"segments"`
|
||||
Groups []string `json:"groups"`
|
||||
SegmentValidation map[string]*SegmentValidationInfo `json:"-"`
|
||||
IsRarArchive bool `json:"is_rar_archive"` // Whether this file is a RAR archive that needs extraction
|
||||
Password string `json:"password,omitempty"` // Password for encrypted RAR files
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
SegmentSize int64 `json:"segment_size,omitempty"` // Size of each segment in bytes, if applicable
|
||||
}
|
||||
|
||||
// NZBSegment represents a segment with all necessary download info
|
||||
type NZBSegment struct {
|
||||
Number int `json:"number"`
|
||||
MessageID string `json:"message_id"`
|
||||
Bytes int64 `json:"bytes"`
|
||||
StartOffset int64 `json:"start_offset"` // Byte offset within the file
|
||||
EndOffset int64 `json:"end_offset"` // End byte offset within the file
|
||||
Group string `json:"group"`
|
||||
}
|
||||
|
||||
// CompactNZB is a space-optimized version of NZB for storage
|
||||
type CompactNZB struct {
|
||||
ID string `json:"i"`
|
||||
Name string `json:"n"`
|
||||
Status string `json:"s"`
|
||||
Category string `json:"c"`
|
||||
Size int64 `json:"sz"`
|
||||
Progress float64 `json:"p"`
|
||||
Speed int64 `json:"sp,omitempty"`
|
||||
ETA int64 `json:"e,omitempty"`
|
||||
Added int64 `json:"a"` // Unix timestamp
|
||||
Modified int64 `json:"m"` // Unix timestamp
|
||||
Complete int64 `json:"co,omitempty"` // Unix timestamp
|
||||
Groups []string `json:"g,omitempty"`
|
||||
Files []CompactFile `json:"f,omitempty"`
|
||||
Storage string `json:"st,omitempty"` // Storage path
|
||||
FailMessage string `json:"fm,omitempty"` // Error message if the download failed
|
||||
Downloaded bool `json:"d,omitempty"`
|
||||
}
|
||||
|
||||
// CompactFile represents a file in compact format
|
||||
type CompactFile struct {
|
||||
Name string `json:"n"`
|
||||
Size int64 `json:"s"`
|
||||
Type string `json:"t"`
|
||||
Main bool `json:"m,omitempty"`
|
||||
Offset int64 `json:"o"`
|
||||
Segments []CompactSegment `json:"seg,omitempty"`
|
||||
IsRar bool `json:"r,omitempty"`
|
||||
Password string `json:"p,omitempty"`
|
||||
IsDeleted bool `json:"del,omitempty"` // Whether the file is marked as deleted
|
||||
ExtractedFileInfo *ExtractedFileInfo `json:"efi,omitempty"` // Pre-extracted RAR file info
|
||||
SegmentSize int64 `json:"ss,omitempty"` // Size of each segment in bytes, if applicable
|
||||
}
|
||||
|
||||
// CompactSegment represents a segment in compact format
|
||||
type CompactSegment struct {
|
||||
Number int `json:"n"` // Segment number
|
||||
MessageID string `json:"mid"` // Message-ID of the segment
|
||||
Bytes int64 `json:"b"` // Size in bytes
|
||||
StartOffset int64 `json:"so"` // Start byte offset within the file
|
||||
EndOffset int64 `json:"eo"` // End byte offset within the file
|
||||
Group string `json:"g,omitempty"` // Group associated with this segment
|
||||
}
|
||||
|
||||
type ExtractedFileInfo struct {
|
||||
FileName string `json:"fn,omitempty"`
|
||||
FileSize int64 `json:"fs,omitempty"`
|
||||
ArchiveSize int64 `json:"as,omitempty"` // Total size of the RAR archive
|
||||
EstimatedStartOffset int64 `json:"eso,omitempty"` // Estimated start offset in the archive
|
||||
SegmentSize int64 `json:"ss,omitempty"` // Size of each segment in the archive
|
||||
}
|
||||
|
||||
// toCompact converts NZB to compact format
|
||||
func (nzb *NZB) toCompact() *CompactNZB {
|
||||
compact := &CompactNZB{
|
||||
ID: nzb.ID,
|
||||
Name: nzb.Name,
|
||||
Status: nzb.Status,
|
||||
Category: nzb.Category,
|
||||
Size: nzb.TotalSize,
|
||||
Progress: nzb.Progress,
|
||||
Speed: nzb.Speed,
|
||||
ETA: nzb.ETA,
|
||||
Added: nzb.AddedOn.Unix(),
|
||||
Modified: nzb.LastActivity.Unix(),
|
||||
Storage: nzb.Storage,
|
||||
Downloaded: nzb.Downloaded,
|
||||
FailMessage: nzb.FailMessage,
|
||||
}
|
||||
|
||||
if !nzb.CompletedOn.IsZero() {
|
||||
compact.Complete = nzb.CompletedOn.Unix()
|
||||
}
|
||||
|
||||
// Only store essential groups (first 3)
|
||||
if len(nzb.Groups) > 0 {
|
||||
maxGroups := 3
|
||||
if len(nzb.Groups) < maxGroups {
|
||||
maxGroups = len(nzb.Groups)
|
||||
}
|
||||
compact.Groups = nzb.Groups[:maxGroups]
|
||||
}
|
||||
|
||||
// Store only essential file info
|
||||
if len(nzb.Files) > 0 {
|
||||
compact.Files = make([]CompactFile, len(nzb.Files))
|
||||
for i, file := range nzb.Files {
|
||||
compact.Files[i] = file.toCompact()
|
||||
}
|
||||
}
|
||||
|
||||
return compact
|
||||
}
|
||||
|
||||
// fromCompact converts compact format back to NZB
|
||||
func (compact *CompactNZB) toNZB() *NZB {
|
||||
nzb := &NZB{
|
||||
ID: compact.ID,
|
||||
Name: compact.Name,
|
||||
Status: compact.Status,
|
||||
Category: compact.Category,
|
||||
TotalSize: compact.Size,
|
||||
Progress: compact.Progress,
|
||||
Percentage: compact.Progress,
|
||||
Speed: compact.Speed,
|
||||
ETA: compact.ETA,
|
||||
Groups: compact.Groups,
|
||||
AddedOn: time.Unix(compact.Added, 0),
|
||||
LastActivity: time.Unix(compact.Modified, 0),
|
||||
Storage: compact.Storage,
|
||||
Downloaded: compact.Downloaded,
|
||||
FailMessage: compact.FailMessage,
|
||||
StreamingInfo: StreamingInfo{
|
||||
MainFileIndex: -1,
|
||||
},
|
||||
}
|
||||
|
||||
if compact.Complete > 0 {
|
||||
nzb.CompletedOn = time.Unix(compact.Complete, 0)
|
||||
}
|
||||
|
||||
// Reconstruct files
|
||||
if len(compact.Files) > 0 {
|
||||
nzb.Files = make([]NZBFile, len(compact.Files))
|
||||
for i, file := range compact.Files {
|
||||
nzb.Files[i] = file.toNZB()
|
||||
}
|
||||
|
||||
// Set streaming info
|
||||
nzb.StreamingInfo.TotalSegments = len(compact.Files)
|
||||
nzb.StreamingInfo.IsStreamable = nzb.StreamingInfo.MainFileIndex >= 0
|
||||
}
|
||||
|
||||
return nzb
|
||||
}
|
||||
|
||||
func (nf *NZBFile) toCompact() CompactFile {
|
||||
compact := CompactFile{
|
||||
Name: nf.Name,
|
||||
Size: nf.Size,
|
||||
Offset: nf.StartOffset,
|
||||
IsRar: nf.IsRarArchive,
|
||||
IsDeleted: nf.IsDeleted,
|
||||
Password: nf.Password,
|
||||
SegmentSize: nf.SegmentSize,
|
||||
}
|
||||
for _, seg := range nf.Segments {
|
||||
compact.Segments = append(compact.Segments, CompactSegment(seg))
|
||||
}
|
||||
return compact
|
||||
}
|
||||
func (compact *CompactFile) toNZB() NZBFile {
|
||||
f := NZBFile{
|
||||
Name: compact.Name,
|
||||
Size: compact.Size,
|
||||
StartOffset: compact.Offset,
|
||||
IsRarArchive: compact.IsRar,
|
||||
Password: compact.Password,
|
||||
IsDeleted: compact.IsDeleted,
|
||||
SegmentSize: compact.SegmentSize,
|
||||
}
|
||||
for _, seg := range compact.Segments {
|
||||
f.Segments = append(f.Segments, NZBSegment(seg))
|
||||
}
|
||||
return f
|
||||
}
|
||||
Reference in New Issue
Block a user