
'''
Usage:
    unzip-http <url.zip> <filenames..>

Extract <filenames> from a remote .zip at <url> to stdout.
If no filenames given, displays .zip contents (filenames and sizes).
Each filename can be a wildcard glob; all matching files are concatenated and sent to stdout in zipfile order.

HTTP server must send `Accept-Ranges: bytes` and `Content-Length` in headers.
'''

import sys
import io
import time
import fnmatch

import unzip_http


class StreamProgress:
    def __init__(self, fp, name='', total=0):
        self.name = name
        self.fp = fp
        self.total = total
        self.start_time = time.time()
        self.last_update = 0
        self.amtread = 0

    def read(self, n):
        r = self.fp.read(n)
        self.amtread += len(r)
        now = time.time()
        if now - self.last_update > 0.1:
            self.last_update = now

            elapsed_s = now - self.start_time
            sys.stderr.write(f'\r{elapsed_s:.0f}s  {self.amtread/10**6:.02f}/{self.total/10**6:.02f}MB  ({self.amtread/10**6/elapsed_s:.02f} MB/s)  {self.name}')

        if not r:
            sys.stderr.write('\n')

        return r


def main(url, *globs):
    rzf = unzip_http.RemoteZipFile(url)
    for f in rzf.infolist():
        if not globs:
            print(f'{f.compress_size/2**20:.02f}MB -> {f.file_size/2**20:.02f}MB  {f.filename}')
        elif any(fnmatch.fnmatch(f.filename, g) for g in globs):
            fp = StreamProgress(rzf.open(f), name=f.filename, total=f.compress_size)
            while r := fp.read(2**18):
                sys.stdout.buffer.write(r)


args = sys.argv[1:]
if not args:
    print(__doc__, file=sys.stderr)
else:
    main(*args)
