Use json instead

This commit is contained in:
Ben Sarmiento
2023-12-02 05:00:37 +01:00
parent a8e8bab853
commit b140dfd4d0
3 changed files with 124 additions and 69 deletions

View File

@@ -1,9 +1,9 @@
package torrent package torrent
import ( import (
"bufio" "encoding/json"
"encoding/gob"
"fmt" "fmt"
"io"
"math" "math"
"net/url" "net/url"
"os" "os"
@@ -56,7 +56,7 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, p
ResponseCache: cache, ResponseCache: cache,
accessKeySet: set.NewStringSet(), accessKeySet: set.NewStringSet(),
latestState: &initialSate, latestState: &initialSate,
requiredVersion: "18.11.2023", requiredVersion: "02.12.2023",
workerPool: p, workerPool: p,
log: log, log: log,
} }
@@ -317,27 +317,18 @@ func (t *TorrentManager) startRefreshJob() {
// getMoreInfo gets original name, size and files for a torrent // getMoreInfo gets original name, size and files for a torrent
func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent { func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE) infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE)
if tor, exists := infoCache.Get(rdTorrent.ID); exists { if tor, exists := infoCache.Get(rdTorrent.ID); exists && tor.SelectedFiles.Count() == len(rdTorrent.Links) {
// in memory cache is always fresh
return tor return tor
} }
var info *realdebrid.TorrentInfo
var err error
torrentFromFile := t.readTorrentFromFile(rdTorrent.ID) torrentFromFile := t.readTorrentFromFile(rdTorrent.ID)
// check staleness of file cache if torrentFromFile != nil && torrentFromFile.SelectedFiles.Count() == len(rdTorrent.Links) {
if torrentFromFile != nil && len(torrentFromFile.ID) > 0 && len(torrentFromFile.Links) > 0 && len(torrentFromFile.Links) == len(rdTorrent.Links) && torrentFromFile.Links[0] == rdTorrent.Links[0] { return torrentFromFile
info = torrentFromFile
info.Progress = rdTorrent.Progress
} else {
torrentFromFile = nil
} }
if info == nil {
info, err = t.Api.GetTorrentInfo(rdTorrent.ID) info, err := t.Api.GetTorrentInfo(rdTorrent.ID)
if err != nil { if err != nil {
t.log.Warnf("Cannot get info for id=%s: %v\n", rdTorrent.ID, err) t.log.Warnf("Cannot get info for id=%s: %v\n", rdTorrent.ID, err)
return nil return nil
}
} }
// SelectedFiles is a subset of Files with only the selected ones // SelectedFiles is a subset of Files with only the selected ones
// it also has a Link field, which can be empty // it also has a Link field, which can be empty
@@ -373,18 +364,18 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
torrent := Torrent{ torrent := Torrent{
AccessKey: t.computeAccessKey(info.Name, info.OriginalName), AccessKey: t.computeAccessKey(info.Name, info.OriginalName),
LatestAdded: info.Added, LatestAdded: info.Added,
Instances: []realdebrid.TorrentInfo{*info}, Instances: []*realdebrid.TorrentInfo{info},
} }
torrent.SelectedFiles = cmap.New[*File]() torrent.SelectedFiles = cmap.New[*File]()
for _, file := range selectedFiles { for _, file := range selectedFiles {
torrent.SelectedFiles.Set(filepath.Base(file.Path), file) 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) 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 return &torrent
} }
@@ -403,28 +394,31 @@ func (t *TorrentManager) computeAccessKey(name, originalName string) string {
} }
} }
func (t *TorrentManager) writeTorrentToFile(torrent *realdebrid.TorrentInfo) error { func (t *TorrentManager) writeTorrentToFile(instanceID string, torrent *Torrent) error {
filePath := "data/" + torrent.ID + ".bin" filePath := "data/" + instanceID + ".json"
file, err := os.Create(filePath) file, err := os.Create(filePath)
if err != nil { if err != nil {
return fmt.Errorf("failed creating file: %w", err) return fmt.Errorf("failed creating file: %w", err)
} }
defer file.Close() defer file.Close()
w := bufio.NewWriter(file)
defer w.Flush()
torrent.Version = t.requiredVersion torrent.Version = t.requiredVersion
dataEncoder := gob.NewEncoder(w)
if err := dataEncoder.Encode(torrent); err != nil { jsonData, err := json.Marshal(torrent)
return fmt.Errorf("failed encoding torrent: %w", err) 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 return nil
} }
func (t *TorrentManager) readTorrentFromFile(torrentID string) *realdebrid.TorrentInfo { func (t *TorrentManager) readTorrentFromFile(torrentID string) *Torrent {
filePath := "data/" + torrentID + ".bin" filePath := "data/" + torrentID + ".json"
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@@ -435,23 +429,25 @@ func (t *TorrentManager) readTorrentFromFile(torrentID string) *realdebrid.Torre
return nil return nil
} }
defer file.Close() defer file.Close()
jsonData, err := io.ReadAll(file)
r := bufio.NewReader(file) if err != nil {
var torrent realdebrid.TorrentInfo t.log.Debugf("[file] error reading file: %v", err)
dataDecoder := gob.NewDecoder(r) return nil
if err := dataDecoder.Decode(&torrent); err != nil { }
t.log.Debugf("[file] error decoding torrent: %v", err) var torrent *Torrent
if err := json.Unmarshal(jsonData, &torrent); err != nil {
t.log.Debugf("[file] error unmarshaling torrent: %v", err)
return nil return nil
} }
if torrent.Version != t.requiredVersion { if torrent.Version != t.requiredVersion {
t.log.Debugf("[file] not the right version: %s", torrent.Version) t.log.Debugf("[file] not the right version: %s", torrent.Version)
return nil return nil
} }
return &torrent return torrent
} }
func (t *TorrentManager) deleteTorrentFile(torrentID string) { func (t *TorrentManager) deleteTorrentFile(torrentID string) {
filePath := "data/" + torrentID + ".bin" filePath := "data/" + torrentID + ".json"
err := os.Remove(filePath) err := os.Remove(filePath)
if err != nil { if err != nil {
t.log.Warnf("Cannot delete file %s: %v", filePath, err) 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 { func (t *TorrentManager) CheckDeletedState(torrent *Torrent) bool {
unselected := 0 var unselectedIDs []int
torrent.SelectedFiles.IterCb(func(_ string, file *File) { torrent.SelectedFiles.IterCb(func(_ string, file *File) {
if file.Link == "unselect" { 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 return true
} else if unselected > 0 { } else if len(unselectedIDs) > 0 {
// save to file
for i := range torrent.Instances { for i := range torrent.Instances {
t.writeTorrentToFile(&torrent.Instances[i]) t.writeTorrentToFile(torrent.Instances[i].ID, torrent)
} }
} }
return false return false

View File

@@ -1,16 +1,68 @@
package torrent package torrent
import ( import (
"encoding/json"
"github.com/debridmediamanager/zurg/pkg/realdebrid" "github.com/debridmediamanager/zurg/pkg/realdebrid"
cmap "github.com/orcaman/concurrent-map/v2" cmap "github.com/orcaman/concurrent-map/v2"
) )
type Torrent struct { type Torrent struct {
AccessKey string AccessKey string `json:"AccessKey"`
SelectedFiles cmap.ConcurrentMap[string, *File] SelectedFiles cmap.ConcurrentMap[string, *File] `json:"-"`
LatestAdded string 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 { func (t *Torrent) AnyInProgress() bool {
@@ -34,7 +86,7 @@ func (t *Torrent) AllInProgress() bool {
type File struct { type File struct {
realdebrid.File realdebrid.File
Added string Added string `json:"Added"`
Ended string Ended string `json:"Ended"`
Link string Link string `json:"Link"`
} }

View File

@@ -37,15 +37,11 @@ func (i *Torrent) UnmarshalJSON(data []byte) error {
}{ }{
Alias: (*Alias)(i), Alias: (*Alias)(i),
} }
if err := json.Unmarshal(data, &aux); err != nil { if err := json.Unmarshal(data, &aux); err != nil {
return err return err
} }
i.Progress = int(math.Floor(aux.Progress))
i.Progress = int(math.Round(aux.Progress))
i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1) i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1)
return nil return nil
} }
@@ -62,7 +58,24 @@ type TorrentInfo struct {
OriginalName string `json:"original_filename"` OriginalName string `json:"original_filename"`
OriginalBytes int64 `json:"original_bytes"` OriginalBytes int64 `json:"original_bytes"`
Files []File `json:"files"` 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 { func (i *TorrentInfo) UnmarshalJSON(data []byte) error {
@@ -75,19 +88,14 @@ func (i *TorrentInfo) UnmarshalJSON(data []byte) error {
}{ }{
Alias: (*Alias)(i), Alias: (*Alias)(i),
} }
if err := json.Unmarshal(data, &aux); err != nil { if err := json.Unmarshal(data, &aux); err != nil {
return err return err
} }
i.Progress = int(math.Floor(aux.Progress))
i.Progress = int(math.Round(aux.Progress))
i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1) i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1)
if aux.Ended != "" { if aux.Ended != "" {
i.Ended = strings.Replace(aux.Ended, "Z", "+01:00", 1) i.Ended = strings.Replace(aux.Ended, "Z", "+01:00", 1)
} }
return nil return nil
} }