Rewrite state machines

This commit is contained in:
Ben Sarmiento
2024-05-21 17:07:40 +02:00
parent 6c24d74f61
commit 0743b01223
12 changed files with 69 additions and 55 deletions

View File

@@ -6,7 +6,7 @@ import cmap "github.com/orcaman/concurrent-map/v2"
func (t *TorrentManager) CheckDeletedStatus(torrent *Torrent) bool {
var deletedIDs []int
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
if file.State.Is("deleted") {
if file.State.Is("deleted_file") {
deletedIDs = append(deletedIDs, file.ID)
}
})

View File

@@ -104,7 +104,7 @@ func (t *TorrentManager) UnrestrictLinkUntilOk(link string) *realdebrid.Download
}
func (t *TorrentManager) UnrestrictFileUntilOk(file *File) *realdebrid.Download {
if !file.State.Is("ok") {
if !file.State.Is("ok_file") {
return nil
}
return t.UnrestrictLinkUntilOk(file.Link)

View File

@@ -1,6 +1,7 @@
package torrent
import (
"context"
"fmt"
"path/filepath"
"strings"
@@ -130,12 +131,10 @@ func (t *TorrentManager) refreshTorrents(isInitialRun bool) []string {
if t.Config.EnableRepair() {
if isInitialRun {
t.removeExpiredFixers(instances)
t.processFixers(instances)
} else {
t.workerPool.Submit(func() {
t.processFixers(instances)
})
}
t.workerPool.Submit(func() {
t.processFixers(instances)
})
}
return updatedPaths
@@ -198,7 +197,7 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
OriginalName: info.OriginalName,
Added: info.Added,
Hash: info.Hash,
State: NewTorrentState("ok"),
State: NewTorrentState("broken_torrent"),
}
// SelectedFiles is a subset of Files with only the selected ones
@@ -214,16 +213,23 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
File: file,
Ended: info.Ended,
Link: "", // no link yet, consider it broken
State: NewFileState("broken"),
State: NewFileState("broken_file"),
})
}
if len(selectedFiles) == len(info.Links) {
// all links are still intact! good!
for i, file := range selectedFiles {
file.Link = info.Links[i]
file.State.SetState("ok")
err := file.State.Event(context.Background(), "repair_file")
if err != nil {
t.log.Warnf("Cannot repair file %s: %v", file.Path, err)
}
}
torrent.UnassignedLinks = mapset.NewSet[string]()
err := torrent.State.Event(context.Background(), "repair_torrent")
if err != nil {
t.log.Warnf("Cannot repair torrent %s: %v", torrent.Hash, err)
}
} else {
torrent.UnassignedLinks = mapset.NewSet[string](info.Links...)
}
@@ -315,9 +321,12 @@ func (t *TorrentManager) mergeToMain(existing, toMerge *Torrent) *Torrent {
older.SelectedFiles.IterCb(func(key string, olderFile *File) {
if !mainTorrent.SelectedFiles.Has(key) {
mainTorrent.SelectedFiles.Set(key, olderFile)
} else if olderFile.State.Is("deleted") {
} else if olderFile.State.Is("deleted_file") {
newerFile, _ := mainTorrent.SelectedFiles.Get(key)
newerFile.State.SetState("deleted")
err := newerFile.State.Event(context.Background(), "delete_file")
if err != nil {
t.log.Warnf("Cannot delete file %s: %v", key, err)
}
}
})
t.CheckDeletedStatus(&mainTorrent)

View File

@@ -101,7 +101,7 @@ func (t *TorrentManager) repairAll(torrent *Torrent) {
// check 1: for broken files
brokenFileIDs := mapset.NewSet[int]()
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
if file.State.Is("broken") {
if file.State.Is("broken_file") {
brokenFileIDs.Add(file.ID)
}
})
@@ -257,7 +257,6 @@ func (t *TorrentManager) assignUnassignedLinks(torrent *Torrent) bool {
unrestrict := t.UnrestrictLinkUntilOk(link)
if unrestrict == nil {
expiredCount++
// newUnassignedLinks.Set(link, nil)
return false // next
}
@@ -265,9 +264,13 @@ func (t *TorrentManager) assignUnassignedLinks(torrent *Torrent) bool {
assigned := false
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
// base it on size because why not?
if (unrestrict.Filesize > 1_000_000 && file.Bytes == unrestrict.Filesize) || strings.HasSuffix(strings.ToLower(file.Path), strings.ToLower(unrestrict.Filename)) {
if !assigned && file.State.Is("broken_file") && ((unrestrict.Filesize > 1_000_000 && file.Bytes == unrestrict.Filesize) || strings.HasSuffix(strings.ToLower(file.Path), strings.ToLower(unrestrict.Filename))) {
file.Link = link
file.State.SetState("ok")
err := file.State.Event(context.Background(), "repair_done_file")
if err != nil {
t.log.Errorf("Failed to mark file %s as repaired: %v", file.Path, err)
return
}
assigned = true
assignedCount++
}
@@ -290,6 +293,10 @@ func (t *TorrentManager) assignUnassignedLinks(torrent *Torrent) bool {
return false
})
t.log.Debugf("Assigned %d links to the %d selected files of torrent %s", assignedCount, torrent.SelectedFiles.Count(), t.GetKey(torrent))
t.log.Debugf("Expired %d links to the %d selected files of torrent %s", expiredCount, torrent.SelectedFiles.Count(), t.GetKey(torrent))
t.log.Debugf("Rar'ed %d links to the %d selected files of torrent %s", rarCount, torrent.SelectedFiles.Count(), t.GetKey(torrent))
t.log.Debugf("Unassigned %d links to the %d selected files of torrent %s", newUnassignedLinks.Count(), torrent.SelectedFiles.Count(), t.GetKey(torrent))
if assignedCount == 0 && rarCount == 1 {
// this is a rar'ed torrent, nothing we can do
@@ -311,7 +318,7 @@ func (t *TorrentManager) assignUnassignedLinks(torrent *Torrent) bool {
},
Ended: torrent.Added,
Link: unassigned.Link,
State: NewFileState("ok"),
State: NewFileState("ok_file"),
}
torrent.SelectedFiles.Set(unassigned.Filename, newFile)
})
@@ -473,11 +480,11 @@ func (t *TorrentManager) canCapacityHandle() bool {
}
func (t *TorrentManager) markAsUnplayable(torrent *Torrent, reason string) {
if torrent.State.Is("unplayable") {
if torrent.State.Is("unplayable_torrent") {
return
}
t.log.Warnf("Marking torrent %s as unplayable - %s", t.GetKey(torrent), reason)
err := torrent.State.Event(context.Background(), "mark_as_unplayable")
err := torrent.State.Event(context.Background(), "mark_as_unplayable_torrent")
if err != nil {
t.log.Errorf("Failed to mark torrent %s as unplayable: %v", t.GetKey(torrent), err)
return
@@ -500,7 +507,7 @@ func getBrokenFiles(torrent *Torrent) ([]*File, bool) {
var brokenFiles []*File
allBroken := true
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
if file.State.Is("broken") {
if file.State.Is("broken_file") {
brokenFiles = append(brokenFiles, file)
} else {
allBroken = false
@@ -520,18 +527,22 @@ func (t *TorrentManager) isStillBroken(info *realdebrid.TorrentInfo, brokenFiles
selectedFiles = append(selectedFiles, &File{
File: file,
Ended: info.Ended,
Link: "", // no link yet
State: NewFileState("broken"),
Link: "",
State: NewFileState("broken_file"),
})
}
if len(selectedFiles) == len(info.Links) {
// all links are still intact! good!
for i, file := range selectedFiles {
file.Link = info.Links[i]
file.State.SetState("ok")
err := file.State.Event(context.Background(), "repair_file")
if err != nil {
t.log.Errorf("Failed to mark file %s as repaired: %v", file.Path, err)
return true
}
}
} else {
// if we can't assign links, it's still broken
// if we can't assign links then it's still broken
return true
}

View File

@@ -5,36 +5,30 @@ import (
)
func NewTorrentState(initial string) *fsm.FSM {
// ok
// broken
// under_repair
// deleted
// unplayable
// ok_torrent 1
// broken_torrent 1
// unplayable_torrent 1
return fsm.NewFSM(
initial,
fsm.Events{
{Name: "break", Src: []string{"ok", "unplayable"}, Dst: "broken"},
{Name: "repair", Src: []string{"broken"}, Dst: "under_repair"},
{Name: "repair_done", Src: []string{"under_repair"}, Dst: "ok"},
{Name: "delete", Src: []string{"ok", "broken", "under_repair", "unplayable"}, Dst: "deleted"},
{Name: "mark_as_unplayable", Src: []string{"ok", "under_repair"}, Dst: "unplayable"},
{Name: "break_torrent", Src: []string{"ok_torrent", "unplayable_torrent"}, Dst: "broken_torrent"},
{Name: "repair_torrent", Src: []string{"broken_torrent"}, Dst: "ok_torrent"},
{Name: "mark_as_unplayable_torrent", Src: []string{"ok_torrent"}, Dst: "unplayable_torrent"},
},
fsm.Callbacks{},
)
}
func NewFileState(initial string) *fsm.FSM {
// ok
// broken
// under_repair
// deleted
// ok_file 13
// broken_file 5
// deleted_file 3
return fsm.NewFSM(
initial,
fsm.Events{
{Name: "break", Src: []string{"ok"}, Dst: "broken"},
{Name: "repair", Src: []string{"broken"}, Dst: "under_repair"},
{Name: "repair_done", Src: []string{"under_repair"}, Dst: "ok"},
{Name: "delete", Src: []string{"ok", "broken", "under_repair"}, Dst: "deleted"},
{Name: "break_file", Src: []string{"ok_file"}, Dst: "broken_file"},
{Name: "repair_file", Src: []string{"broken_file"}, Dst: "ok_file"},
{Name: "delete_file", Src: []string{"ok_file", "broken_file"}, Dst: "deleted_file"},
},
fsm.Callbacks{},
)