mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-28 15:51:29 +08:00
fix(downloader): report aria2 per-peer progress; perf(ci): scope server COPY
Two follow-ups: - aria2 peers now report progress (0..1) derived from the peer's piece bitfield (seeder => 1.0), matching qBittorrent. Previously the Peers list showed '—' for aria2 because the field was never populated. - The server builder copied the whole repo via 'COPY . .', so a cmd/-only (downloader) change busted the layer and forced a full vite/tsup rebuild. Copy only the build inputs + final-stage sources (src, server, shared, public, index.html, vite.config.ts, tsconfig.json, migrations, scripts) so downloader-only pushes reuse the cached server image. Verified the builder stage still builds and produces dist/dist-server/migrations/entrypoint.
This commit is contained in:
+10
-1
@@ -11,7 +11,16 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
|
||||
corepack enable \
|
||||
&& pnpm install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
# Copy only what the JS build (vite + tsup) and the final server image consume,
|
||||
# so a cmd/-only change (the Go downloader) doesn't bust this layer and force a
|
||||
# full server rebuild. Keep in sync with the build inputs + the final-stage COPYs.
|
||||
COPY index.html vite.config.ts tsconfig.json ./
|
||||
COPY src ./src
|
||||
COPY server ./server
|
||||
COPY shared ./shared
|
||||
COPY public ./public
|
||||
COPY migrations ./migrations
|
||||
COPY scripts ./scripts
|
||||
# .git is excluded from the build context, so git describe cannot run here.
|
||||
# The release workflow passes the tag via APP_VERSION and the commit SHA via
|
||||
# APP_COMMIT; resolveAppVersion/resolveAppCommit read them.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/bits"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -1026,6 +1027,34 @@ func aria2Leechers(peers []arigo.Peer) *int64 {
|
||||
return &count
|
||||
}
|
||||
|
||||
// aria2PeerProgress derives a peer's 0..1 completion from its piece bitfield
|
||||
// (aria2 has no direct percentage like qBittorrent). A seeder has every piece.
|
||||
// Spare bits past the last piece are zero, so dividing by the bitfield's bit
|
||||
// length only differs from the true piece count by sub-1% — fine for a bar.
|
||||
func aria2PeerProgress(peer arigo.Peer) *float64 {
|
||||
if peer.Seeder {
|
||||
full := 1.0
|
||||
return &full
|
||||
}
|
||||
if peer.BitField == "" {
|
||||
return nil
|
||||
}
|
||||
set := 0
|
||||
for _, digit := range peer.BitField {
|
||||
nibble, err := strconv.ParseUint(string(digit), 16, 8)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
set += bits.OnesCount8(uint8(nibble))
|
||||
}
|
||||
total := len(peer.BitField) * 4
|
||||
if total == 0 {
|
||||
return nil
|
||||
}
|
||||
progress := float64(set) / float64(total)
|
||||
return &progress
|
||||
}
|
||||
|
||||
func aria2Peers(peers []arigo.Peer, geoIP PeerGeoIPResolver) []client.DownloadTaskPeer {
|
||||
out := make([]client.DownloadTaskPeer, 0, min(len(peers), 20))
|
||||
for _, peer := range peers {
|
||||
@@ -1036,6 +1065,7 @@ func aria2Peers(peers []arigo.Peer, geoIP PeerGeoIPResolver) []client.DownloadTa
|
||||
up := int64(peer.UploadSpeed)
|
||||
item := client.DownloadTaskPeer{
|
||||
Address: fmt.Sprintf("%s:%d", peer.IP, peer.Port),
|
||||
Progress: aria2PeerProgress(peer),
|
||||
DownloadBps: &down,
|
||||
UploadBps: &up,
|
||||
}
|
||||
|
||||
@@ -201,6 +201,21 @@ func TestAria2SeedTimeMinutes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAria2PeerProgress(t *testing.T) {
|
||||
if p := aria2PeerProgress(arigo.Peer{Seeder: true}); p == nil || *p != 1.0 {
|
||||
t.Fatalf("expected seeder progress 1.0, got %v", p)
|
||||
}
|
||||
if p := aria2PeerProgress(arigo.Peer{BitField: "ff"}); p == nil || *p != 1.0 {
|
||||
t.Fatalf("expected full bitfield 1.0, got %v", p)
|
||||
}
|
||||
if p := aria2PeerProgress(arigo.Peer{BitField: "f0"}); p == nil || *p != 0.5 {
|
||||
t.Fatalf("expected half bitfield 0.5, got %v", p)
|
||||
}
|
||||
if p := aria2PeerProgress(arigo.Peer{BitField: ""}); p != nil {
|
||||
t.Fatalf("expected nil progress for empty bitfield, got %v", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQBittorrentStartArgsWritesManagedListenPort(t *testing.T) {
|
||||
stateDir := t.TempDir()
|
||||
downloadDir := t.TempDir()
|
||||
|
||||
Reference in New Issue
Block a user