diff --git a/juke-wizard/Sources/JukeWizard/JukeCloud.swift b/juke-wizard/Sources/JukeWizard/JukeCloud.swift
--- a/juke-wizard/Sources/JukeWizard/JukeCloud.swift
+++ b/juke-wizard/Sources/JukeWizard/JukeCloud.swift
@@ -87,10 +87,14 @@ var upload = URLRequest(url: prepared.uploadURL)
upload.httpMethod = "PUT"
for (name, value) in prepared.headers { upload.setValue(value, forHTTPHeaderField: name) }
upload.setValue(String(bytes), forHTTPHeaderField: "Content-Length")
- let (_, response) = try await session.upload(for: upload, fromFile: file)
+ let (uploadData, response) = try await session.upload(for: upload, fromFile: file)
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
guard (200..<300).contains(status) else {
- throw JukeCloudClientError.response("Upload failed (\(status)).")
+ let body = String(data: uploadData, encoding: .utf8) ?? ""
+ let detail = body.components(separatedBy: "").dropFirst().first?
+ .components(separatedBy: "").first
+ throw JukeCloudClientError.response(
+ "Upload failed (\(status))\(detail.map { ": \($0)" } ?? ".")")
}
return prepared.track
}
diff --git a/juke-wizard/bin/juke-cloud.mjs b/juke-wizard/bin/juke-cloud.mjs
--- a/juke-wizard/bin/juke-cloud.mjs
+++ b/juke-wizard/bin/juke-cloud.mjs
@@ -63,7 +63,10 @@ headers: { ...prepared.headers, "Content-Length": String(stat.size) },
body: Readable.toWeb(createReadStream(absolute)),
duplex: "half",
});
- if (!response.ok) throw new Error(`Upload failed (${response.status})`);
+ if (!response.ok) {
+ const detail = (await response.text()).match(/([^<]+)<\/Message>/)?.[1];
+ throw new Error(`Upload failed (${response.status})${detail ? `: ${detail}` : ""}`);
+ }
return prepared.track;
}
diff --git a/system/netlify/functions/juke-cloud.mjs b/system/netlify/functions/juke-cloud.mjs
--- a/system/netlify/functions/juke-cloud.mjs
+++ b/system/netlify/functions/juke-cloud.mjs
@@ -164,7 +164,11 @@ ContentDisposition: "inline",
ACL: "public-read",
}), { expiresIn: 15 * 60 });
const track = trackFromObject({ Key: key, Size: bytes, LastModified: new Date() });
- return respond(200, { uploadURL, headers: { "Content-Type": contentType }, track });
+ return respond(200, {
+ uploadURL,
+ headers: { "Content-Type": contentType, "Content-Disposition": "inline" },
+ track,
+ });
} catch (error) {
console.error("juke-cloud failed", error?.message || error);
return respond(503, { error: "Juke cloud is temporarily unavailable." });