retry upload after it's stopped for 20 seconds #844

Massimo Melina committed Jan 2, 2025 at 15:15 UTC f153653291ca0fbc0551299b3bc9eed09003bab4
1 file changed +19 -6
frontend/src/uploadQueue.ts
+19 -6
@@ -51,6 +51,8 @@ window.onbeforeunload = ev => {
51 return ev.returnValue = t("Uploading") // modern browsers ignore this message
52 }
53
54 +const RETRY_UPLOAD = -1
55 +let stuckSince = Infinity
56 // keep track of speed
57 let bytesSentTimestamp = Date.now()
58 let bytesSent = 0
@@ -59,6 +61,10 @@ setInterval(() => {
61 const passed = (now - bytesSentTimestamp) / 1000
62 if (passed < 3 && uploadState.speed) return
63 uploadState.speed = bytesSent / passed
64 + if (now - stuckSince >= 10_000) { // this will normally cause the upload to be retried after 10+10 seconds of no progress
65 + overrideStatus = RETRY_UPLOAD // try again
66 + abortCurrentUpload()
67 + }
68 bytesSent = 0 // reset counter
69 bytesSentTimestamp = now
70
@@ -89,18 +95,25 @@ export async function startUpload(toUpload: ToUpload, to: string, resume=0) {
95 const splitSize = getHFS().splitUploads
96 const fullSize = toUpload.file.size
97 let offset = resume
98 + let stopLooping = false
99 do { // at least one iteration, even for empty files
100 req = new XMLHttpRequest()
101 const finished = pendingPromise()
102 req.onloadend = () => {
103 finished.resolve()
104 if (req?.readyState !== 4) return
105 + if (overrideStatus === RETRY_UPLOAD) {
106 + overrideStatus = 0
107 + stopLooping = true
108 + startUpload(toUpload, to, offset)
109 + return
110 + }
111 const status = overrideStatus || req.status
112 if (!partial) // if the upload ends here, the offer for resuming must stop
113 closeLast?.()
114 if (resuming) { // resuming requested
115 resuming = false // this behavior is only for once, for cancellation of the upload that is in the background while resume is confirmed
103 - stopLooping()
116 + stopLooping = true
117 return
118 }
119 if (!status || status === HTTP_CONFLICT) // 0 = user-aborted, HTTP_CONFLICT = skipped because existing
@@ -121,13 +134,15 @@ export async function startUpload(toUpload: ToUpload, to: string, resume=0) {
134 req.onerror = () => {
135 error(0)
136 finished.resolve()
124 - stopLooping()
137 + stopLooping = true
138 }
139 let lastProgress = 0
140 req.upload.onprogress = (e:any) => {
141 uploadState.partial = e.loaded + offset
142 uploadState.progress = uploadState.partial / fullSize
143 bytesSent += e.loaded - lastProgress
144 + if (e.loaded > lastProgress)
145 + stuckSince = Date.now()
146 lastProgress = e.loaded
147 }
148 let uploadPath = getFilePath(toUpload.file)
@@ -143,9 +158,7 @@ export async function startUpload(toUpload: ToUpload, to: string, resume=0) {
158 }), true)
159 req.send(toUpload.file.slice(offset, splitSize ? offset + splitSize : undefined))
160 await finished
146 - } while (offset < fullSize)
147 -
148 - function stopLooping() { offset = fullSize }
161 + } while (!stopLooping && offset < fullSize)
162
163 async function subscribeNotifications() {
164 if (notificationChannel) return
@@ -196,7 +209,7 @@ export async function startUpload(toUpload: ToUpload, to: string, resume=0) {
209 }
210
211 function next() {
199 - stopLooping()
212 + stopLooping = true
213 uploadState.uploading = undefined
214 uploadState.partial = 0
215 const { qs } = uploadState