Created
January 23, 2025 01:50
-
-
Save olchyk98/57855a911c151817535189689491d919 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import mime from 'mime' | |
| import { getSongAudioStream } from '../../../../../_db/song' | |
| import { headers } from 'next/headers' | |
| import { clamp } from '../../../../../../utils/clamp' | |
| export async function GET ( | |
| _req: Request, | |
| { params }: { params: Promise<{ songId: string }> }, | |
| ) { | |
| const id = (await params).songId | |
| if (!id || typeof id !== 'string') { | |
| return new Response('Invalid ID.', { status: 400 }) | |
| } | |
| const reqHeaders = await headers() | |
| const range = reqHeaders.get('range') ?? 'bytes=0-' | |
| const [ , from, to = Infinity ] = Array.from(range.matchAll(/bytes=(\d+)-(\d+)?/g))[0] | |
| /** | |
| * Note from when it was implemented. | |
| * This was very painful. All you have to remember | |
| * when you're reading this code is that the right boundary | |
| * in the Range value ("to" value) is NON-INCLUSIVE. | |
| * Meaning, if client is looking to get the entire | |
| * file, they'll specify #contentLength - 1# (or Infinity). | |
| * | |
| * The client expects us to do the same thing | |
| * for the response. Therefore Content-Length | |
| * would be returned as normal, but right | |
| * boundary for the Content-Range value | |
| * would be #sliceLength -1#. | |
| * */ | |
| const { stream, byteLength, filename } = await getSongAudioStream(id) | |
| const audioByteLength = byteLength | |
| const clampedFrom = clamp(Number(from), 0) | |
| const clampedTo = clamp(Number(to) + 1, 0, audioByteLength) | |
| const slicedAudioBuffer = await new Promise<Buffer>((res, rej) => { | |
| const slicedAudioBuffer = Buffer.alloc(clampedTo - clampedFrom) | |
| let cursor = 0 | |
| let writeOffset = 0 | |
| stream.on('data', (chunk: Buffer) => { | |
| if (cursor > clampedTo) { | |
| return res(slicedAudioBuffer) | |
| } | |
| for (let ma = 0; ma < chunk.byteLength; ++ma) { | |
| if (cursor >= clampedFrom) { | |
| slicedAudioBuffer[writeOffset] = chunk[ma] | |
| writeOffset += 1 | |
| } | |
| cursor += 1 | |
| } | |
| }) | |
| stream.on('end', () => res(slicedAudioBuffer)) | |
| stream.on('error', (e) => rej(e)) | |
| }) | |
| const contentType = mime.getType(filename) ?? 'audio/mpeg' | |
| return new Response( | |
| slicedAudioBuffer, | |
| { | |
| status: 206, | |
| headers: { | |
| 'Content-Type': contentType, | |
| 'Accept-Ranges': 'bytes', | |
| 'Content-Length': `${slicedAudioBuffer.byteLength}`, | |
| 'Content-Range': `bytes ${clampedFrom}-${clampedTo - 1}/${audioByteLength}`, | |
| }, | |
| }, | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment