tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

long-wav.py (4222B)


      1 """
      2 This generates a 30 minute silent wav, and is capable of
      3 responding to Range requests.
      4 """
      5 import time
      6 import re
      7 import struct
      8 
      9 from wptserve.utils import isomorphic_decode
     10 
     11 def create_wav_header(sample_rate, bit_depth, channels, duration):
     12    bytes_per_sample = int(bit_depth / 8)
     13    block_align = bytes_per_sample * channels
     14    byte_rate = sample_rate * block_align
     15    sub_chunk_2_size = duration * byte_rate
     16 
     17    data = b''
     18    # ChunkID
     19    data += b'RIFF'
     20    # ChunkSize
     21    data += struct.pack('<L', 36 + sub_chunk_2_size)
     22    # Format
     23    data += b'WAVE'
     24    # Subchunk1ID
     25    data += b'fmt '
     26    # Subchunk1Size
     27    data += struct.pack('<L', 16)
     28    # AudioFormat
     29    data += struct.pack('<H', 1)
     30    # NumChannels
     31    data += struct.pack('<H', channels)
     32    # SampleRate
     33    data += struct.pack('<L', sample_rate)
     34    # ByteRate
     35    data += struct.pack('<L', byte_rate)
     36    # BlockAlign
     37    data += struct.pack('<H', block_align)
     38    # BitsPerSample
     39    data += struct.pack('<H', bit_depth)
     40    # Subchunk2ID
     41    data += b'data'
     42    # Subchunk2Size
     43    data += struct.pack('<L', sub_chunk_2_size)
     44 
     45    return data
     46 
     47 
     48 def main(request, response):
     49    if request.method == u"OPTIONS":
     50        response.status = (404, b"Not Found")
     51        response.headers.set(b"Content-Type", b"text/plain")
     52        return b"Preflight not accepted"
     53 
     54    response.headers.set(b"Content-Type", b"audio/wav")
     55    response.headers.set(b"Accept-Ranges", b"bytes")
     56    response.headers.set(b"Cache-Control", b"no-cache")
     57    response.headers.set(b"Access-Control-Allow-Origin", request.headers.get(b'Origin', b''))
     58 
     59    range_header = request.headers.get(b'Range', b'')
     60    range_header_match = range_header and re.search(r'^bytes=(\d*)-(\d*)$', isomorphic_decode(range_header))
     61    range_received_key = request.GET.first(b'range-received-key', b'')
     62    accept_encoding_key = request.GET.first(b'accept-encoding-key', b'')
     63 
     64    if range_received_key and range_header:
     65        # Remove any current value
     66        request.server.stash.take(range_received_key, b'/fetch/range/')
     67        # This is later collected using stash-take.py
     68        request.server.stash.put(range_received_key, u'range-header-received', b'/fetch/range/')
     69 
     70    if accept_encoding_key:
     71        # Remove any current value
     72        request.server.stash.take(
     73            accept_encoding_key,
     74            b'/fetch/range/'
     75        )
     76        # This is later collected using stash-take.py
     77        request.server.stash.put(
     78            accept_encoding_key,
     79            isomorphic_decode(request.headers.get(b'Accept-Encoding', b'')),
     80            b'/fetch/range/'
     81        )
     82 
     83    # Audio details
     84    sample_rate = 8000
     85    bit_depth = 8
     86    channels = 1
     87    duration = 60 * 5
     88 
     89    total_length = int((sample_rate * bit_depth * channels * duration) / 8)
     90    bytes_remaining_to_send = total_length
     91    initial_write = b''
     92 
     93    if range_header_match:
     94        response.status = 206
     95        start, end = range_header_match.groups()
     96 
     97        start = int(start)
     98        end = int(end) if end else 0
     99 
    100        if end:
    101            bytes_remaining_to_send = (end + 1) - start
    102        else:
    103            bytes_remaining_to_send = total_length - start
    104 
    105        wav_header = create_wav_header(sample_rate, bit_depth, channels, duration)
    106 
    107        if start < len(wav_header):
    108            initial_write = wav_header[start:]
    109 
    110            if bytes_remaining_to_send < len(initial_write):
    111                initial_write = initial_write[0:bytes_remaining_to_send]
    112 
    113        content_range = b"bytes %d-%d/%d" % (start, end or total_length - 1, total_length)
    114 
    115        response.headers.set(b"Content-Range", content_range)
    116    else:
    117        initial_write = create_wav_header(sample_rate, bit_depth, channels, duration)
    118 
    119    response.headers.set(b"Content-Length", bytes_remaining_to_send)
    120 
    121    response.write_status_headers()
    122    response.writer.write(initial_write)
    123 
    124    bytes_remaining_to_send -= len(initial_write)
    125 
    126    while bytes_remaining_to_send > 0:
    127        to_send = b'\x00' * min(bytes_remaining_to_send, sample_rate)
    128        bytes_remaining_to_send -= len(to_send)
    129 
    130        if not response.writer.write(to_send):
    131            break
    132 
    133        # Throttle the stream
    134        time.sleep(0.5)