Use json instead
This commit is contained in:
@@ -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,28 +317,19 @@ 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
|
||||||
// if it is empty, it means the file is no longer available
|
// if it is empty, it means the file is no longer available
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user