Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5b1f100e2 | |||
| 4cf8246550 | |||
| 3f96382e76 | |||
| 43a94118f4 | |||
| 38d59cb01d | |||
| b306248db6 | |||
| 3a5289cb1d |
55
.gitea/workflows/ci.yml
Normal file
55
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
name: CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, main]
|
||||
pull_request:
|
||||
branches: [master, main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build & Push Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
outputs:
|
||||
image_tag: ${{ steps.meta.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Generate image metadata
|
||||
id: meta
|
||||
run: |
|
||||
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
|
||||
echo "tag=${SHORT_SHA}" >> $GITHUB_OUTPUT
|
||||
echo "Image will be tagged: ${SHORT_SHA}"
|
||||
|
||||
- name: Login to registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login registry.johnogle.info -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
registry.johnogle.info/johno/decypharr:${{ steps.meta.outputs.tag }}
|
||||
registry.johnogle.info/johno/decypharr:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.tag }}
|
||||
CHANNEL=dev
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -19,4 +19,11 @@ auth.json
|
||||
node_modules/
|
||||
.venv/
|
||||
.stignore
|
||||
.stfolder/**
|
||||
.stfolder/**
|
||||
|
||||
# Gas Town (added by gt)
|
||||
.runtime/
|
||||
.claude/
|
||||
.logs/
|
||||
.beads/
|
||||
state.json
|
||||
|
||||
@@ -171,13 +171,24 @@ func (tb *Torbox) SubmitMagnet(torrent *types.Torrent) (*types.Torrent, error) {
|
||||
}
|
||||
|
||||
func (tb *Torbox) getTorboxStatus(status string, finished bool) string {
|
||||
if finished {
|
||||
// Log raw values for debugging
|
||||
tb.logger.Debug().
|
||||
Str("download_state", status).
|
||||
Bool("download_finished", finished).
|
||||
Msg("getTorboxStatus called")
|
||||
|
||||
// For cached/completed torrents, content is immediately available even if
|
||||
// DownloadFinished=false (no download actually happened - it was already cached)
|
||||
// Use case-insensitive comparison for safety
|
||||
statusLower := strings.ToLower(status)
|
||||
if finished || statusLower == "cached" || statusLower == "completed" {
|
||||
return "downloaded"
|
||||
}
|
||||
downloading := []string{"completed", "cached", "paused", "downloading", "uploading",
|
||||
downloading := []string{"paused", "downloading", "uploading",
|
||||
"checkingResumeData", "metaDL", "pausedUP", "queuedUP", "checkingUP",
|
||||
"forcedUP", "allocating", "downloading", "metaDL", "pausedDL",
|
||||
"queuedDL", "checkingDL", "forcedDL", "checkingResumeData", "moving"}
|
||||
"queuedDL", "checkingDL", "forcedDL", "checkingResumeData", "moving",
|
||||
"checking"}
|
||||
|
||||
var determinedStatus string
|
||||
switch {
|
||||
|
||||
@@ -53,6 +53,12 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", debridTorrent.Name).
|
||||
Str("debrid_status", debridTorrent.Status).
|
||||
Str("torrent_state", torrent.State).
|
||||
Msg("processFiles started")
|
||||
|
||||
deb := s.debrid.Debrid(debridTorrent.Debrid)
|
||||
client := deb.Client()
|
||||
downloadingStatuses := client.GetDownloadingStatus()
|
||||
@@ -96,6 +102,12 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
||||
nextInterval := min(s.refreshInterval*2, 30*time.Second)
|
||||
backoff.Reset(nextInterval)
|
||||
}
|
||||
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", debridTorrent.Name).
|
||||
Str("debrid_status", debridTorrent.Status).
|
||||
Msg("Download loop exited, proceeding to post-processing")
|
||||
|
||||
var torrentSymlinkPath, torrentRclonePath string
|
||||
debridTorrent.Arr = _arr
|
||||
|
||||
@@ -114,8 +126,28 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
||||
}
|
||||
|
||||
onSuccess := func(torrentSymlinkPath string) {
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", debridTorrent.Name).
|
||||
Str("symlink_path", torrentSymlinkPath).
|
||||
Str("debrid_status", debridTorrent.Status).
|
||||
Msg("onSuccess called")
|
||||
torrent.TorrentPath = torrentSymlinkPath
|
||||
s.updateTorrent(torrent, debridTorrent)
|
||||
|
||||
// Safety check: ensure state is set correctly after updateTorrent
|
||||
// This catches any edge cases where updateTorrent doesn't set the state
|
||||
if torrent.State != "pausedUP" && torrentSymlinkPath != "" {
|
||||
s.logger.Warn().
|
||||
Str("torrent_name", debridTorrent.Name).
|
||||
Str("current_state", torrent.State).
|
||||
Str("debrid_status", debridTorrent.Status).
|
||||
Msg("State not pausedUP after updateTorrent, forcing state update")
|
||||
torrent.State = "pausedUP"
|
||||
torrent.Progress = 1.0
|
||||
torrent.AmountLeft = 0
|
||||
s.torrents.Update(torrent)
|
||||
}
|
||||
|
||||
s.logger.Info().Msgf("Adding %s took %s", debridTorrent.Name, time.Since(timer))
|
||||
|
||||
go importReq.markAsCompleted(torrent, debridTorrent) // Mark the import request as completed, send callback if needed
|
||||
@@ -201,7 +233,12 @@ func (s *Store) processFiles(torrent *Torrent, debridTorrent *types.Torrent, imp
|
||||
if torrentSymlinkPath == "" {
|
||||
err = fmt.Errorf("symlink path is empty for %s", debridTorrent.Name)
|
||||
onFailed(err)
|
||||
return
|
||||
}
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", debridTorrent.Name).
|
||||
Str("symlink_path", torrentSymlinkPath).
|
||||
Msg("Symlink processing complete, calling onSuccess")
|
||||
onSuccess(torrentSymlinkPath)
|
||||
return
|
||||
case "download":
|
||||
@@ -270,6 +307,12 @@ func (s *Store) partialTorrentUpdate(t *Torrent, debridTorrent *types.Torrent) *
|
||||
if math.IsNaN(progress) || math.IsInf(progress, 0) {
|
||||
progress = 0
|
||||
}
|
||||
// When debrid reports download complete, force progress to 100% to ensure
|
||||
// IsReady() returns true. This fixes a race condition where TorBox can report
|
||||
// DownloadFinished=true but Progress < 1.0, causing state to stay "downloading".
|
||||
if debridTorrent.Status == "downloaded" {
|
||||
progress = 1.0
|
||||
}
|
||||
sizeCompleted := int64(float64(totalSize) * progress)
|
||||
|
||||
var speed int64
|
||||
@@ -314,6 +357,13 @@ func (s *Store) updateTorrent(t *Torrent, debridTorrent *types.Torrent) *Torrent
|
||||
return t
|
||||
}
|
||||
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", t.Name).
|
||||
Str("debrid_status", debridTorrent.Status).
|
||||
Str("torrent_path", t.TorrentPath).
|
||||
Str("current_state", t.State).
|
||||
Msg("updateTorrent called")
|
||||
|
||||
if debridClient := s.debrid.Clients()[debridTorrent.Debrid]; debridClient != nil {
|
||||
if debridTorrent.Status != "downloaded" {
|
||||
_ = debridClient.UpdateTorrent(debridTorrent)
|
||||
@@ -322,7 +372,34 @@ func (s *Store) updateTorrent(t *Torrent, debridTorrent *types.Torrent) *Torrent
|
||||
t = s.partialTorrentUpdate(t, debridTorrent)
|
||||
t.ContentPath = t.TorrentPath
|
||||
|
||||
// When debrid reports download complete and we have a path, mark as ready.
|
||||
// This is a direct fix for TorBox where IsReady() might fail due to
|
||||
// progress/AmountLeft calculation issues.
|
||||
if debridTorrent.Status == "downloaded" && t.TorrentPath != "" {
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", t.Name).
|
||||
Msg("Setting state to pausedUP (downloaded + path)")
|
||||
t.State = "pausedUP"
|
||||
t.Progress = 1.0
|
||||
t.AmountLeft = 0
|
||||
s.torrents.Update(t)
|
||||
return t
|
||||
}
|
||||
|
||||
// Log why the primary condition failed
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", t.Name).
|
||||
Str("debrid_status", debridTorrent.Status).
|
||||
Str("torrent_path", t.TorrentPath).
|
||||
Bool("is_ready", t.IsReady()).
|
||||
Float64("progress", t.Progress).
|
||||
Int64("amount_left", t.AmountLeft).
|
||||
Msg("Primary pausedUP condition failed, checking IsReady")
|
||||
|
||||
if t.IsReady() {
|
||||
s.logger.Debug().
|
||||
Str("torrent_name", t.Name).
|
||||
Msg("Setting state to pausedUP (IsReady=true)")
|
||||
t.State = "pausedUP"
|
||||
s.torrents.Update(t)
|
||||
return t
|
||||
|
||||
Reference in New Issue
Block a user