1 Commits
Author SHA1 Message Date
TorMatzAndren 75129db86c Fix Spooty download recovery and cookie staging 2026-06-21 21:46:10 +02:00
2 changed files with 36 additions and 12 deletions
+16 -3
View File
@@ -9,7 +9,10 @@ TOKEN="${SPOOTY_AUTH_TOKEN:-test-token}"
DOWNLOADS="${DOWNLOADS:-$PROJECT/test-downloads}"
CONFIG_DIR="${CONFIG_DIR:-$PROJECT/spooty-config}"
ENV_FILE="${ENV_FILE:-/etc/tokens/spotify.env}"
YT_COOKIES_FILE_HOST="${YT_COOKIES_FILE_HOST:-/etc/tokens/youtube.cookies.txt}"
RUNTIME_SECRETS_DIR="${RUNTIME_SECRETS_DIR:-$PROJECT/spooty-runtime-secrets}"
YT_COOKIES_FILE_STAGED="${YT_COOKIES_FILE_STAGED:-$RUNTIME_SECRETS_DIR/youtube.cookies.txt}"
YT_COOKIES_FILE_CONTAINER="${YT_COOKIES_FILE_CONTAINER:-/spooty/config/youtube.cookies.txt}"
cd "$PROJECT"
@@ -28,6 +31,18 @@ if [[ "${1:-}" == "--build" ]]; then
$DOCKER build -t "$IMAGE" .
fi
echo "== Preparing runtime directories =="
mkdir -p "$DOWNLOADS" "$CONFIG_DIR" "$RUNTIME_SECRETS_DIR"
if [[ -f "$YT_COOKIES_FILE_HOST" ]]; then
echo "== Staging YouTube cookies =="
cp "$YT_COOKIES_FILE_HOST" "$YT_COOKIES_FILE_STAGED"
chmod 0666 "$YT_COOKIES_FILE_STAGED"
else
echo "ERROR: YouTube cookies file not found: $YT_COOKIES_FILE_HOST" >&2
exit 1
fi
echo "== Removing named container if present =="
$DOCKER rm -f "$CONTAINER" >/dev/null 2>&1 || true
@@ -37,8 +52,6 @@ if [[ -n "$PORT_CONTAINERS" ]]; then
$DOCKER rm -f $PORT_CONTAINERS
fi
mkdir -p "$DOWNLOADS" "$CONFIG_DIR"
echo "== Starting Jarri Spooty =="
$DOCKER run -d \
--name "$CONTAINER" \
@@ -52,7 +65,7 @@ $DOCKER run -d \
-e YT_DOWNLOAD_FALLBACK_ATTEMPTS=3 \
-e YT_COOKIES_FILE="$YT_COOKIES_FILE_CONTAINER" \
-v "$DOWNLOADS:/spooty/backend/downloads" \
-v "$YT_COOKIES_FILE_HOST:$YT_COOKIES_FILE_CONTAINER:ro" \
-v "$YT_COOKIES_FILE_STAGED:$YT_COOKIES_FILE_CONTAINER" \
-v "$CONFIG_DIR:/spooty/backend/config" \
"$IMAGE"
+20 -9
View File
@@ -200,7 +200,18 @@ export class TrackService {
};
}
private rejectCurrentYoutubeCandidate(
private isPermanentYoutubeCandidateRejection(
reason: RejectionReason,
): boolean {
return [
'YOUTUBE_VIDEO_UNAVAILABLE',
'YOUTUBE_AGE_GATED',
'YOUTUBE_NO_FORMATS',
'YOUTUBE_PRIVATE_VIDEO',
].includes(reason.rejectionClass);
}
private recordCurrentYoutubeCandidateFailure(
track: TrackEntity,
reason: RejectionReason,
): {
@@ -212,7 +223,10 @@ export class TrackService {
const rejectedCandidate = this.buildRejectedYoutubeCandidate(track, reason);
if (rejectedCandidate) {
if (!rejectedUrls.includes(rejectedCandidate.url)) {
if (
this.isPermanentYoutubeCandidateRejection(reason) &&
!rejectedUrls.includes(rejectedCandidate.url)
) {
rejectedUrls.push(rejectedCandidate.url);
}
@@ -299,11 +313,6 @@ export class TrackService {
return;
}
const rejectedCandidateFields = this.rejectCurrentYoutubeCandidate(track, {
rejectionClass: 'MANUAL_RETRY',
rejectionSummary: 'Rejected by manual retry',
});
await this.update(id, {
...track,
youtubeUrl: null,
@@ -311,7 +320,9 @@ export class TrackService {
selectedYoutubeAuthor: null,
selectedYoutubeScore: null,
selectedYoutubeReason: null,
...rejectedCandidateFields,
rejectedYoutubeUrls: track.rejectedYoutubeUrls || JSON.stringify([]),
rejectedYoutubeCandidatesJson:
track.rejectedYoutubeCandidatesJson || JSON.stringify([]),
downloadAttemptCount: 0,
error: null,
status: TrackStatusEnum.New,
@@ -476,7 +487,7 @@ export class TrackService {
const nextAttemptCount = (track.downloadAttemptCount || 0) + 1;
const maxAttempts = this.getMaxDownloadAttempts();
const rejectedCandidateFields = error
? this.rejectCurrentYoutubeCandidate(track, this.classifyRejection(error))
? this.recordCurrentYoutubeCandidateFailure(track, this.classifyRejection(error))
: {
rejectedYoutubeUrls: this.stringifyRejectedYoutubeUrls(
this.parseRejectedYoutubeUrls(track),