added partial content support with tagging
This commit is contained in:
parent
71b9b7f23f
commit
83c0b9a106
File diff suppressed because one or more lines are too long
145
index.js
145
index.js
|
@ -185,14 +185,13 @@ async function track(id, format, tagging, range_header) {
|
||||||
format = 'misc' // user-upped tracks always use 'misc' as format
|
format = 'misc' // user-upped tracks always use 'misc' as format
|
||||||
}
|
}
|
||||||
|
|
||||||
let filesize = json['FILESIZE_' + formats[format].gw]
|
if (json['FILESIZE_' + formats[format].gw] == false) {
|
||||||
if (filesize == false) {
|
|
||||||
return new Response('Format unavailable', { status: 403, headers: { 'content-type': 'text/plain' } })
|
return new Response('Format unavailable', { status: 403, headers: { 'content-type': 'text/plain' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
let range_start = null
|
let range_start = null
|
||||||
let range_end = null
|
let range_end = null
|
||||||
if (!tagging && range_header !== null) {
|
if (range_header !== null) {
|
||||||
const range_match = range_header.match(/^bytes=(\d+)-(\d*)/)
|
const range_match = range_header.match(/^bytes=(\d+)-(\d*)/)
|
||||||
if (range_match !== null) {
|
if (range_match !== null) {
|
||||||
range_start = parseInt(range_match[1])
|
range_start = parseInt(range_match[1])
|
||||||
|
@ -250,8 +249,6 @@ async function track(id, format, tagging, range_header) {
|
||||||
track_url = await legacy_track_url(json, format, wasm.legacy_stream_url)
|
track_url = await legacy_track_url(json, format, wasm.legacy_stream_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
filesize = parseInt(filesize)
|
|
||||||
|
|
||||||
let title = json.SNG_TITLE
|
let title = json.SNG_TITLE
|
||||||
if (json.VERSION) title += ` ${json.VERSION}`
|
if (json.VERSION) title += ` ${json.VERSION}`
|
||||||
|
|
||||||
|
@ -260,53 +257,27 @@ async function track(id, format, tagging, range_header) {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': formats[format].mime,
|
'content-type': formats[format].mime,
|
||||||
'content-disposition': `inline; filename="${title.replaceAll('"', '\\"')} [${id}].${formats[format].ext}"`
|
'content-disposition': `inline; filename="${title.replaceAll('"', '\\"')} [${id}].${formats[format].ext}"`,
|
||||||
|
'accept-ranges': 'bytes'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tagging) {
|
|
||||||
resp_init.headers['accept-ranges'] = 'bytes'
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp_size = filesize
|
// filesize from the API isn't always the actual filesize,
|
||||||
let fixed_range_start
|
// so we get it with a HEAD request instead
|
||||||
let fixed_range_end = filesize
|
// example: https://www.deezer.com/track/7703895
|
||||||
let bytes_remove_start = 0
|
let track_size
|
||||||
let bytes_remove_end = 0
|
let track_head = await fetch(track_url, {method: 'HEAD'})
|
||||||
if (range_start !== null) {
|
if (track_head.status !== 200) {
|
||||||
if (isNaN(range_end)) {
|
|
||||||
range_end = filesize
|
|
||||||
} else {
|
|
||||||
range_end++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (range_start < 0 || range_start > filesize || range_end < range_start || range_end > filesize) {
|
|
||||||
return new Response('Range invalid', { status: 416, headers: { 'content-type': 'text/plain' } })
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_remove_start = range_start % 2048
|
|
||||||
bytes_remove_end = 2048 - range_end % 2048
|
|
||||||
|
|
||||||
fixed_range_start = range_start - bytes_remove_start
|
|
||||||
fixed_range_end = range_end + bytes_remove_end
|
|
||||||
if (fixed_range_end >= filesize) {
|
|
||||||
fixed_range_end = filesize
|
|
||||||
bytes_remove_end = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
init = { headers: new Headers({ range: `bytes=${fixed_range_start}-${fixed_range_end - 1}` }) }
|
|
||||||
resp_init.status = 206
|
|
||||||
resp_init.headers['content-range'] = `bytes ${range_start}-${range_end - 1}/${filesize}`
|
|
||||||
resp_size = range_end - range_start
|
|
||||||
}
|
|
||||||
|
|
||||||
const track = await fetch(track_url, init)
|
|
||||||
if (![200, 206].includes(track.status)) {
|
|
||||||
return new Response("Couldn't get track stream", { status: 403, headers: { 'content-type': 'text/plain' } })
|
return new Response("Couldn't get track stream", { status: 403, headers: { 'content-type': 'text/plain' } })
|
||||||
|
} else {
|
||||||
|
track_size = parseInt(track_head.headers.get('content-length'))
|
||||||
}
|
}
|
||||||
|
|
||||||
let id3
|
let resp_size = track_size
|
||||||
|
|
||||||
|
let id3_buffer
|
||||||
if (tagging) {
|
if (tagging) {
|
||||||
id3 = new ID3Writer(Buffer.alloc(0));
|
let id3 = new ID3Writer(Buffer.alloc(0));
|
||||||
id3.padding = 0
|
id3.padding = 0
|
||||||
|
|
||||||
id3.setFrame('TIT2', title)
|
id3.setFrame('TIT2', title)
|
||||||
|
@ -354,16 +325,86 @@ async function track(id, format, tagging, range_header) {
|
||||||
|
|
||||||
id3.addTag();
|
id3.addTag();
|
||||||
|
|
||||||
resp_size += id3.arrayBuffer.byteLength
|
id3_buffer = id3.arrayBuffer
|
||||||
|
resp_size += id3_buffer.byteLength
|
||||||
|
}
|
||||||
|
|
||||||
|
let skip_id3 = false
|
||||||
|
let skip_stream = false
|
||||||
|
let fixed_range_start = 0
|
||||||
|
let fixed_range_end = track_size
|
||||||
|
let bytes_remove_start = 0
|
||||||
|
let bytes_remove_end = 0
|
||||||
|
if (range_start !== null) {
|
||||||
|
if (isNaN(range_end)) {
|
||||||
|
range_end = resp_size
|
||||||
|
} else {
|
||||||
|
range_end++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range_start < 0 || range_start > resp_size || range_end < range_start || range_end > resp_size) {
|
||||||
|
return new Response('Range invalid', { status: 416, headers: { 'content-type': 'text/plain' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
let orig_range_start = range_start
|
||||||
|
let orig_range_end = range_end
|
||||||
|
|
||||||
|
if (tagging) {
|
||||||
|
let id3_len = id3_buffer.byteLength
|
||||||
|
|
||||||
|
if (range_start < id3_len) {
|
||||||
|
let bytes_remove_id3_end = undefined
|
||||||
|
if (range_end < id3_len) {
|
||||||
|
bytes_remove_id3_end = id3_len - (id3_len - range_end)
|
||||||
|
skip_stream = true
|
||||||
|
} else {
|
||||||
|
range_end -= id3_len
|
||||||
|
range_start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
id3_buffer = id3_buffer.slice(range_start, bytes_remove_id3_end)
|
||||||
|
} else {
|
||||||
|
skip_id3 = true
|
||||||
|
range_start -= id3_len
|
||||||
|
range_end -= id3_len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skip_stream) {
|
||||||
|
bytes_remove_start = range_start % 2048
|
||||||
|
bytes_remove_end = 2048 - range_end % 2048
|
||||||
|
|
||||||
|
fixed_range_start = range_start - bytes_remove_start
|
||||||
|
fixed_range_end = range_end + bytes_remove_end
|
||||||
|
if (fixed_range_end >= track_size) {
|
||||||
|
fixed_range_end = track_size
|
||||||
|
bytes_remove_end = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
init = { headers: new Headers({ range: `bytes=${fixed_range_start}-${fixed_range_end - 1}` }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
resp_init.status = 206
|
||||||
|
resp_init.headers['content-range'] = `bytes ${orig_range_start}-${orig_range_end - 1}/${resp_size}`
|
||||||
|
resp_size = orig_range_end - orig_range_start
|
||||||
|
}
|
||||||
|
|
||||||
|
let track
|
||||||
|
if (!skip_stream) {
|
||||||
|
track = await fetch(track_url, init)
|
||||||
|
if (![200, 206].includes(track.status)) {
|
||||||
|
return new Response("Couldn't get track stream", { status: 403, headers: { 'content-type': 'text/plain' } })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let { readable, writable } = new FixedLengthStream(resp_size)
|
let { readable, writable } = new FixedLengthStream(resp_size)
|
||||||
const writer = writable.getWriter()
|
const writer = writable.getWriter()
|
||||||
|
|
||||||
if (tagging) {
|
if (tagging && !skip_id3) {
|
||||||
writer.write(id3.arrayBuffer)
|
writer.write(id3_buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skip_stream) {
|
||||||
// needed if track has fallback, like https://www.deezer.com/track/11835714
|
// needed if track has fallback, like https://www.deezer.com/track/11835714
|
||||||
if (json.FALLBACK) {
|
if (json.FALLBACK) {
|
||||||
id = json.FALLBACK.SNG_ID
|
id = json.FALLBACK.SNG_ID
|
||||||
|
@ -372,16 +413,16 @@ async function track(id, format, tagging, range_header) {
|
||||||
const cipher = new wasm.Cipher(String(id))
|
const cipher = new wasm.Cipher(String(id))
|
||||||
|
|
||||||
pipeDecryptedStream(writer, track.body, fixed_range_end, cipher, fixed_range_start, bytes_remove_start, bytes_remove_end)
|
pipeDecryptedStream(writer, track.body, fixed_range_end, cipher, fixed_range_start, bytes_remove_start, bytes_remove_end)
|
||||||
|
} else {
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
return new Response(readable, resp_init)
|
return new Response(readable, resp_init)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pipeDecryptedStream(writer, body, length, cipher, fixed_range_start, bytes_remove_start, bytes_remove_end) {
|
async function pipeDecryptedStream(writer, body, length, cipher, fixed_range_start, bytes_remove_start, bytes_remove_end) {
|
||||||
const reader = body.getReader({ mode: 'byob' })
|
const reader = body.getReader({ mode: 'byob' })
|
||||||
let byte_count = 0
|
let byte_count = fixed_range_start
|
||||||
if (fixed_range_start) {
|
|
||||||
byte_count = fixed_range_start
|
|
||||||
}
|
|
||||||
let end = false
|
let end = false
|
||||||
while (!end) {
|
while (!end) {
|
||||||
end = byte_count + 2048 >= length
|
end = byte_count + 2048 >= length
|
||||||
|
|
Loading…
Reference in New Issue