From b140dfd4d0c670ecf002268722e4854b173c3366 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Sat, 2 Dec 2023 05:00:37 +0100 Subject: [PATCH] Use json instead --- internal/torrent/manager.go | 95 ++++++++++++++++++------------------- internal/torrent/types.go | 66 +++++++++++++++++++++++--- pkg/realdebrid/types.go | 32 ++++++++----- 3 files changed, 124 insertions(+), 69 deletions(-) diff --git a/internal/torrent/manager.go b/internal/torrent/manager.go index 8996bf9..fb90f82 100644 --- a/internal/torrent/manager.go +++ b/internal/torrent/manager.go @@ -1,9 +1,9 @@ package torrent import ( - "bufio" - "encoding/gob" + "encoding/json" "fmt" + "io" "math" "net/url" "os" @@ -56,7 +56,7 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, p ResponseCache: cache, accessKeySet: set.NewStringSet(), latestState: &initialSate, - requiredVersion: "18.11.2023", + requiredVersion: "02.12.2023", workerPool: p, log: log, } @@ -317,27 +317,18 @@ func (t *TorrentManager) startRefreshJob() { // getMoreInfo gets original name, size and files for a torrent func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent { infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE) - if tor, exists := infoCache.Get(rdTorrent.ID); exists { - // in memory cache is always fresh + if tor, exists := infoCache.Get(rdTorrent.ID); exists && tor.SelectedFiles.Count() == len(rdTorrent.Links) { return tor } - - var info *realdebrid.TorrentInfo - var err error torrentFromFile := t.readTorrentFromFile(rdTorrent.ID) - // check staleness of file cache - if torrentFromFile != nil && len(torrentFromFile.ID) > 0 && len(torrentFromFile.Links) > 0 && len(torrentFromFile.Links) == len(rdTorrent.Links) && torrentFromFile.Links[0] == rdTorrent.Links[0] { - info = torrentFromFile - info.Progress = rdTorrent.Progress - } else { - torrentFromFile = nil + if torrentFromFile != nil && torrentFromFile.SelectedFiles.Count() == len(rdTorrent.Links) { + return torrentFromFile } - if info == nil { - info, err = t.Api.GetTorrentInfo(rdTorrent.ID) - if err != nil { - t.log.Warnf("Cannot get info for id=%s: %v\n", rdTorrent.ID, err) - return nil - } + + info, err := t.Api.GetTorrentInfo(rdTorrent.ID) + if err != nil { + t.log.Warnf("Cannot get info for id=%s: %v\n", rdTorrent.ID, err) + return nil } // SelectedFiles is a subset of Files with only the selected ones // it also has a Link field, which can be empty @@ -373,18 +364,18 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent { torrent := Torrent{ AccessKey: t.computeAccessKey(info.Name, info.OriginalName), LatestAdded: info.Added, - Instances: []realdebrid.TorrentInfo{*info}, + Instances: []*realdebrid.TorrentInfo{info}, } torrent.SelectedFiles = cmap.New[*File]() for _, file := range selectedFiles { torrent.SelectedFiles.Set(filepath.Base(file.Path), file) } - if len(selectedFiles) > 0 && torrentFromFile == nil { - t.writeTorrentToFile(info) // only when there are selected files, else it's useless - } - infoCache.Set(rdTorrent.ID, &torrent) + err = t.writeTorrentToFile(rdTorrent.ID, &torrent) + if err != nil { + t.log.Warnf("Cannot write torrent to file: %v", err) + } return &torrent } @@ -403,28 +394,31 @@ func (t *TorrentManager) computeAccessKey(name, originalName string) string { } } -func (t *TorrentManager) writeTorrentToFile(torrent *realdebrid.TorrentInfo) error { - filePath := "data/" + torrent.ID + ".bin" +func (t *TorrentManager) writeTorrentToFile(instanceID string, torrent *Torrent) error { + filePath := "data/" + instanceID + ".json" file, err := os.Create(filePath) if err != nil { return fmt.Errorf("failed creating file: %w", err) } defer file.Close() - w := bufio.NewWriter(file) - defer w.Flush() - torrent.Version = t.requiredVersion - dataEncoder := gob.NewEncoder(w) - if err := dataEncoder.Encode(torrent); err != nil { - return fmt.Errorf("failed encoding torrent: %w", err) + + jsonData, err := json.Marshal(torrent) + if err != nil { + return fmt.Errorf("failed marshaling torrent: %w", err) } - t.log.Debugf("Saved torrent %s to file", torrent.ID) + + if _, err := file.Write(jsonData); err != nil { + return fmt.Errorf("failed writing to file: %w", err) + } + + t.log.Debugf("Saved torrent %s to file", instanceID) return nil } -func (t *TorrentManager) readTorrentFromFile(torrentID string) *realdebrid.TorrentInfo { - filePath := "data/" + torrentID + ".bin" +func (t *TorrentManager) readTorrentFromFile(torrentID string) *Torrent { + filePath := "data/" + torrentID + ".json" file, err := os.Open(filePath) if err != nil { if os.IsNotExist(err) { @@ -435,23 +429,25 @@ func (t *TorrentManager) readTorrentFromFile(torrentID string) *realdebrid.Torre return nil } defer file.Close() - - r := bufio.NewReader(file) - var torrent realdebrid.TorrentInfo - dataDecoder := gob.NewDecoder(r) - if err := dataDecoder.Decode(&torrent); err != nil { - t.log.Debugf("[file] error decoding torrent: %v", err) + jsonData, err := io.ReadAll(file) + if err != nil { + t.log.Debugf("[file] error reading file: %v", err) + return nil + } + var torrent *Torrent + if err := json.Unmarshal(jsonData, &torrent); err != nil { + t.log.Debugf("[file] error unmarshaling torrent: %v", err) return nil } if torrent.Version != t.requiredVersion { t.log.Debugf("[file] not the right version: %s", torrent.Version) return nil } - return &torrent + return torrent } func (t *TorrentManager) deleteTorrentFile(torrentID string) { - filePath := "data/" + torrentID + ".bin" + filePath := "data/" + torrentID + ".json" err := os.Remove(filePath) if err != nil { t.log.Warnf("Cannot delete file %s: %v", filePath, err) @@ -567,18 +563,17 @@ func (t *TorrentManager) checkForOtherDeletedTorrents() { } func (t *TorrentManager) CheckDeletedState(torrent *Torrent) bool { - unselected := 0 + var unselectedIDs []int torrent.SelectedFiles.IterCb(func(_ string, file *File) { if file.Link == "unselect" { - unselected++ + unselectedIDs = append(unselectedIDs, file.ID) } }) - if unselected == torrent.SelectedFiles.Count() && unselected > 0 { + if len(unselectedIDs) == torrent.SelectedFiles.Count() && len(unselectedIDs) > 0 { return true - } else if unselected > 0 { - // save to file + } else if len(unselectedIDs) > 0 { for i := range torrent.Instances { - t.writeTorrentToFile(&torrent.Instances[i]) + t.writeTorrentToFile(torrent.Instances[i].ID, torrent) } } return false diff --git a/internal/torrent/types.go b/internal/torrent/types.go index fc66243..7c20741 100644 --- a/internal/torrent/types.go +++ b/internal/torrent/types.go @@ -1,16 +1,68 @@ package torrent import ( + "encoding/json" + "github.com/debridmediamanager/zurg/pkg/realdebrid" cmap "github.com/orcaman/concurrent-map/v2" ) type Torrent struct { - AccessKey string - SelectedFiles cmap.ConcurrentMap[string, *File] - LatestAdded string + AccessKey string `json:"AccessKey"` + SelectedFiles cmap.ConcurrentMap[string, *File] `json:"-"` + LatestAdded string `json:"LatestAdded"` + Version string `json:"Version"` - Instances []realdebrid.TorrentInfo + Instances []*realdebrid.TorrentInfo `json:"Instances"` +} + +func (t *Torrent) MarshalJSON() ([]byte, error) { + type Alias Torrent + temp := &struct { + SelectedFilesJson json.RawMessage `json:"SelectedFiles"` + // InstancesJson json.RawMessage `json:"Instances"` + *Alias + }{ + Alias: (*Alias)(t), + } + selectedFilesJson, err := t.SelectedFiles.MarshalJSON() + if err != nil { + return nil, err + } + temp.SelectedFilesJson = selectedFilesJson + // instancesJson, err := json.Marshal(t.Instances) + // if err != nil { + // return nil, err + // } + // temp.InstancesJson = instancesJson + return json.Marshal(temp) +} + +func (t *Torrent) UnmarshalJSON(data []byte) error { + type Alias Torrent + temp := &struct { + SelectedFilesJson json.RawMessage `json:"SelectedFiles"` + // InstancesJson json.RawMessage `json:"Instances"` + *Alias + }{ + Alias: (*Alias)(t), + } + if err := json.Unmarshal(data, temp); err != nil { + return err + } + if len(temp.SelectedFilesJson) > 0 { + t.SelectedFiles = cmap.New[*File]() + if err := t.SelectedFiles.UnmarshalJSON(temp.SelectedFilesJson); err != nil { + return err + } + } + // if len(temp.InstancesJson) > 0 { + // instances := make([]*realdebrid.TorrentInfo, 0) + // if err := json.Unmarshal(temp.SelectedFilesJson, &instances); err != nil { + // return nil + // } + // } + return nil } func (t *Torrent) AnyInProgress() bool { @@ -34,7 +86,7 @@ func (t *Torrent) AllInProgress() bool { type File struct { realdebrid.File - Added string - Ended string - Link string + Added string `json:"Added"` + Ended string `json:"Ended"` + Link string `json:"Link"` } diff --git a/pkg/realdebrid/types.go b/pkg/realdebrid/types.go index cda2509..c98ff5c 100644 --- a/pkg/realdebrid/types.go +++ b/pkg/realdebrid/types.go @@ -37,15 +37,11 @@ func (i *Torrent) UnmarshalJSON(data []byte) error { }{ Alias: (*Alias)(i), } - if err := json.Unmarshal(data, &aux); err != nil { return err } - - i.Progress = int(math.Round(aux.Progress)) - + i.Progress = int(math.Floor(aux.Progress)) i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1) - return nil } @@ -62,7 +58,24 @@ type TorrentInfo struct { OriginalName string `json:"original_filename"` OriginalBytes int64 `json:"original_bytes"` Files []File `json:"files"` - Version string `json:"-"` +} + +func (i *TorrentInfo) MarshalJSON() ([]byte, error) { + type Alias TorrentInfo + aux := &struct { + Progress float64 `json:"progress"` + Added string `json:"added"` + Ended string `json:"ended"` + *Alias + }{ + Alias: (*Alias)(i), + Progress: float64(i.Progress), // Convert int to float64 for JSON representation + Added: i.Added, + } + if i.Ended != "" { + aux.Ended = i.Ended + } + return json.Marshal(aux) } func (i *TorrentInfo) UnmarshalJSON(data []byte) error { @@ -75,19 +88,14 @@ func (i *TorrentInfo) UnmarshalJSON(data []byte) error { }{ Alias: (*Alias)(i), } - if err := json.Unmarshal(data, &aux); err != nil { return err } - - i.Progress = int(math.Round(aux.Progress)) - + i.Progress = int(math.Floor(aux.Progress)) i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1) - if aux.Ended != "" { i.Ended = strings.Replace(aux.Ended, "Z", "+01:00", 1) } - return nil }