func (u *Unpackerr) checkExtractDone() {
	for name, data := range u.Map {
		switch elapsed := time.Since(data.Updated); {
		case data.Status == DELETED && elapsed >= data.DeleteDelay:
			u.Finished++
			delete(u.Map, name)
			u.Printf("[%s] Finished, Removed History: %v", data.App, name)
		case data.App == FolderString:
			continue  
		case data.Status == EXTRACTFAILED && elapsed >= u.RetryDelay.Duration &&
			(u.MaxRetries == 0 || data.Retries < u.MaxRetries):
			u.Retries++
			data.Retries++
			data.Status = WAITING
			data.Updated = time.Now()
			u.Printf("[%s] Extract failed %v ago, triggering restart (%d/%d): %v",
				data.App, elapsed.Round(time.Second), data.Retries, u.MaxRetries, name)
		case data.Status == IMPORTED && elapsed >= data.DeleteDelay:
			var webhook bool
			if data.DeleteOrig {
				go u.DeleteFiles(data.Path)
				webhook = true
			} else if len(data.Resp.NewFiles) > 0 && data.DeleteDelay >= 0 {
				go u.DeleteFiles(data.Resp.NewFiles...)
				webhook = true
			}
			u.updateQueueStatus(&newStatus{Name: name, Status: DELETED, Resp: data.Resp}, webhook)
		}
	}
}
checkExtractDone checks if an extracted item imported items needs to be deleted.
Or if an extraction failed and needs to be restarted.
This runs at a short interval to check for extraction state changes, and shuold return quickly.
nolint:cyclop,wsl
package unpackerr
import (
	"os"
	"path/filepath"
	"strings"
	"time"
	"golift.io/starr"
	"golift.io/xtractr"
)
type Extract struct {
	Retries     uint
	Path        string
	App         string
	Updated     time.Time
	DeleteDelay time.Duration
	DeleteOrig  bool
	Status      ExtractStatus
	IDs         map[string]interface{}
	Resp        *xtractr.Response
}
func (u *Unpackerr) checkQueueChanges() {
	for name, data := range u.Map {
		switch {
		case data.App == FolderString:
			continue  
		case !u.haveQitem(name, data.App):
			switch elapsed := time.Since(data.Updated); {
			case data.Status == WAITING:
				delete(u.Map, name)
				u.Printf("[%v] Imported: %v (not extracted, removing from history)", data.App, name)
			case data.Status > IMPORTED:
				u.Debugf("Already imported? %s", name)
			case data.Status == IMPORTED:
				u.Debugf("%v: Awaiting Delete Delay (%v remains): %v",
					data.App, data.DeleteDelay-elapsed.Round(time.Second), name)
			default:
				u.updateQueueStatus(&newStatus{Name: name, Status: IMPORTED, Resp: data.Resp}, true)
				u.Printf("[%v] Imported: %v (delete in %v)", data.App, name, data.DeleteDelay)
			}
		case data.Status == IMPORTED:
			u.Printf("%s: Extraction Not Imported: %s - De-queued and returned.", data.App, name)
			data.Status = EXTRACTED
		case data.Status > IMPORTED:
			u.Printf("%s: Extraction Restarting: %s - Deleted Item De-queued and returned.", data.App, name)
			data.Status = WAITING
			data.Updated = time.Now()
		}
		u.Debugf("%s: Status: %s (%v, elapsed: %v)", data.App, name, data.Status.Desc(),
			time.Since(data.Updated).Round(time.Second))
	}
}
func (u *Unpackerr) handleCompletedDownload(name string, x *Extract) {
	item, ok := u.Map[name]
	if !ok {
		x.Updated = time.Now()
		u.Map[name] = x
		item = u.Map[name]
	}
	if time.Since(item.Updated) < u.Config.StartDelay.Duration {
		u.Printf("[%s] Waiting for Start Delay: %v (%v remains)", item.App, name,
			u.Config.StartDelay.Duration-time.Since(item.Updated).Round(time.Second))
		return
	}
	files := xtractr.FindCompressedFiles(item.Path)
	if len(files) == 0 {
		_, err := os.Stat(item.Path)
		u.Printf("[%s] Completed item still waiting: %s, no extractable files found at: %s (stat err: %v)",
			item.App, name, item.Path, err)
		return
	}
	item.Status = QUEUED
	item.Updated = time.Now()
	queueSize, _ := u.Extract(&xtractr.Xtract{
		Password:   u.getPasswordFromPath(item.Path),
		Name:       name,
		SearchPath: item.Path,
		TempFolder: false,
		DeleteOrig: false,
		CBChannel:  u.updates,
	})
	u.Printf("[%s] Extraction Queued: %s, extractable files: %d, delete orig: %v, items in queue: %d",
		item.App, item.Path, len(files), item.DeleteOrig, queueSize)
	u.updateHistory(item.App + ": " + item.Path)
}
func (u *Unpackerr) getPasswordFromPath(s string) string {
	start, end := strings.Index(s, "{{"), strings.Index(s, "}}")
	if start == -1 || end == -1 || start > end {
		return ""
	}
	u.Debugf("Found password in Path: %s", s[start+2:end])
	return s[start+2 : end]
}