Browse Source

Workaround for streams timeouts working awfully

main
RemixDev 4 weeks ago
parent
commit
82d975ba91
Signed by: RemixDev GPG Key ID: B33962B465BDB51C
3 changed files with 49 additions and 11 deletions
  1. +25
    -6
      deemix/decryption.js
  2. +23
    -5
      deemix/downloader.js
  3. +1
    -0
      deemix/utils/index.js

+ 25
- 6
deemix/decryption.js View File

@ -1,7 +1,7 @@
const got = require('got')
const fs = require('fs')
const {_md5, _ecbCrypt, _ecbDecrypt, generateBlowfishKey, decryptChunk} = require('./utils/crypto.js')
const { DownloadCanceled, DownloadEmpty} = require('./errors.js')
const { DownloadCanceled, DownloadEmpty } = require('./errors.js')
const { USER_AGENT_HEADER, pipeline } = require('./utils/index.js')
@ -43,6 +43,7 @@ async function streamTrack(writepath, track, downloadObject, listener){
let isCryptedStream = track.downloadURL.includes("/mobile/") || track.downloadURL.includes("/media/")
let blowfishKey
let outputStream = fs.createWriteStream(writepath)
let timeout = null
let itemData = {
id: track.id,
@ -102,9 +103,9 @@ async function streamTrack(writepath, track, downloadObject, listener){
let request = got.stream(track.downloadURL, {
headers: headers,
timeout: 7000,
https: {rejectUnauthorized: false}
}).on('response', (response)=>{
clearTimeout(timeout)
complete = parseInt(response.headers["content-length"])
if (complete == 0) {
error = "DownloadEmpty"
@ -128,18 +129,37 @@ async function streamTrack(writepath, track, downloadObject, listener){
downloadObject.progressNext += ((chunk.length / complete) / downloadObject.size) * 100
downloadObject.updateProgress(listener)
}
clearTimeout(timeout)
timeout = setTimeout(()=>{
error = "DownloadTimeout"
request.destroy()
}, 5000)
})
timeout = setTimeout(()=>{
error = "DownloadTimeout"
request.destroy()
}, 5000)
try {
await pipeline(request, decrypter, depadder, outputStream)
} catch (e){
outputStream.close()
if (e instanceof got.ReadError || e instanceof got.TimeoutError || ["ESOCKETTIMEDOUT", "ERR_STREAM_PREMATURE_CLOSE", "ETIMEDOUT"].includes(e.code)){
if (fs.existsSync(writepath)) fs.unlinkSync(writepath)
if (
e instanceof got.ReadError ||
e instanceof got.TimeoutError ||
["ESOCKETTIMEDOUT", "ERR_STREAM_PREMATURE_CLOSE", "ETIMEDOUT"].includes(e.code) ||
request.destroyed && error == "DownloadTimeout"
){
if (downloadObject && chunkLength != 0){
downloadObject.progressNext -= ((chunkLength / complete) / downloadObject.size) * 100
downloadObject.updateProgress(listener)
}
if (listener) listener.send('downloadInfo', {
uuid: downloadObject.uuid,
data: itemData,
state: "downloadTimeout"
})
return await streamTrack(writepath, track, downloadObject, listener)
} else if (request.destroyed) {
switch (error) {
@ -152,7 +172,6 @@ async function streamTrack(writepath, track, downloadObject, listener){
throw e
}
}
outputStream.close()
}
module.exports = {


+ 23
- 5
deemix/downloader.js View File

@ -46,10 +46,24 @@ async function downloadImage(url, path, overwrite = OverwriteOption.DONT_OVERWRI
if (file.length != 0) return path
fs.unlinkSync(path)
}
const downloadStream = got.stream(url, { headers: {'User-Agent': USER_AGENT_HEADER}, https: {rejectUnauthorized: false}, timeout: 7000})
let timeout = null
let error = ""
const downloadStream = got.stream(url, { headers: {'User-Agent': USER_AGENT_HEADER}, https: {rejectUnauthorized: false}})
.on('data', function(){
clearTimeout(timeout)
timeout = setTimeout(()=>{
error = "DownloadTimeout"
downloadStream.destroy()
}, 5000)
})
const fileWriterStream = fs.createWriteStream(path)
timeout = setTimeout(()=>{
error = "DownloadTimeout"
downloadStream.destroy()
}, 5000)
try {
await pipeline(downloadStream, fileWriterStream)
} catch (e){
@ -64,7 +78,12 @@ async function downloadImage(url, path, overwrite = OverwriteOption.DONT_OVERWRI
}
return null
}
if (e instanceof got.TimeoutError || ["ESOCKETTIMEDOUT", "ERR_STREAM_PREMATURE_CLOSE", "ETIMEDOUT"].includes(e.code)) {
if (
e instanceof got.ReadError ||
e instanceof got.TimeoutError ||
["ESOCKETTIMEDOUT", "ERR_STREAM_PREMATURE_CLOSE", "ETIMEDOUT"].includes(e.code) ||
downloadStream.destroyed && error == "DownloadTimeout"
) {
return downloadImage(url, path, overwrite)
}
console.trace(e)
@ -87,7 +106,7 @@ async function getPreferredBitrate(dz, track, preferredBitrate, shouldFallback,
try{
request = got.get(
url,
{ headers: {'User-Agent': USER_AGENT_HEADER}, timeout: 7000, https: {rejectUnauthorized: false} }
{ headers: {'User-Agent': USER_AGENT_HEADER}, https: {rejectUnauthorized: false}, timeout: 7000 }
).on("response", (response)=>{
track.filesizes[`${formatName.toLowerCase()}`] = response.statusCode == 403 ? 0 : response.headers["content-length"]
request.cancel()
@ -481,7 +500,6 @@ class Downloader {
try {
await streamTrack(writepath, track, this.downloadObject, this.listener)
} catch (e){
fs.unlinkSync(writepath)
if (e instanceof got.HTTPError) throw new DownloadFailed('notAvailable', track)
throw e
}


+ 1
- 0
deemix/utils/index.js View File

@ -135,6 +135,7 @@ function formatListener(key, data){
if (data.alreadyStarted) message += ` Recovering download from ${data.value}.`
else message += ` Downloading ${data.value} bytes.`
break;
case "downloadTimeout": message = "Deezer timedout when downloading track, retrying..."; break;
case "downloaded": message = "Track downloaded."; break;
case "alreadyDownloaded": message = "Track already downloaded."; break;
case "tagging": message = "Tagging track."; break;


Loading…
Cancel
Save