Rfetch.py

Материал из wiki.lissyara.su
Перейти к: навигация, поиск
#! /usr/bin/env python-shared3.0
# usage: rfetch.py url [url...]

import curses
import os
import re
import subprocess
import sys
import time
import urllib.request

def fetch(url):
    url, counter = suck(url)
    status(1, 'sleeping... {0:{1}} seconds remain', counter, 3)
    subprocess.call(['fetch', '-v', url])

def getcap(*caps):
    try:
        curses.setupterm(None, sys.__stderr__.fileno())
    except curses.error:
        curses.setupterm('dumb', sys.__stderr__.fileno())

    tc = []
    for cap in caps:
        try:
            if type(cap) is list:
                tc.append(curses.tparm(curses.tigetstr(cap[0])
                                       .decode(), *cap[1:]).decode())
            else:
                tc.append(curses.tparm(curses.tigetstr(cap)
                                       .decode()).decode())
        except AttributeError:
            tc.append('')

    return(tc)

def main():
    if sys.argv[1:] == [] or '' in sys.argv[1:]: # without or null argument(s)
        usage()

    for url in sys.argv[1:]:
        if url.find('://') < 0: # no http:// prefix
            url = 'http://' + url
        elif not url.startswith('http://'): # prefix is not http://
            usage('invalid scheme:// for rapidshare')

        fetch(url)

def status(update, line, counter, *rest):
    '''Show countdouwn for `sleep' each `update' seconds.'''

    def puts(*rest):
        print('\r', so, ''.join(rest), me, sep='', end='', file=sys.stderr)
        sys.stderr.flush()

    future = counter * update + int(time.time())
    so, me = getcap('smso', 'sgr0')

    while counter >= 0:
        # do not print anything unless we are in foreground
        # and connected to a controlling terminal
        if os.getpgrp() == os.tcgetpgrp(sys.__stderr__.fileno()):
            puts(line.format(counter, *rest))

        time.sleep(update)
        counter = (future - int(time.time())) // update

    puts()

def suck(url):
    '''Return url pointing to a real file and sleeping time.'''

    data = url2str(url)

    if url.find('rapidshare.de') >= 0:
        raise NotImplementedError('rapidshare.DE is unsupported')
    elif data.lower().find('file could not be found') >= 0:
        raise urllib.error.URLError('file not found')
    elif data.lower().find('contain illegal content') >= 0:
        raise urllib.error.URLError('file has been blocked')
    elif data.lower().find('this limit is reached') >= 0:
        raise urllib.error.URLError(getcap(['setaf', curses.COLOR_YELLOW])[0] +
                                    data.split('<div class="klappbox">')[1]
                                    .split('</div>')[0]
                                    .replace('<p>','')
                                    .replace('</p>','\n')
                                    .strip() +
                                    getcap('sgr0')[0])

    url = re.search('id="ff"\s*action="([^"]*)"', data).group(1)

    while True:
        data = url2str(url, 'dl.start=Free')

        if data.lower().find('already downloading a file') >= 0:
            raise urllib.error.URLError('already downloading one file')
        elif data.lower().find('reached the download limit') >= 0:
            # grab ticket and wait
            wait = int(re.search('about\s*(\d*)\s*minutes', data).group(1))
            status(60, 'waiting... {0:{1}} minutes remain', wait, 2)
        else:
            break

    url = re.search('name="dlf"\s*action="([^"]*)"', data).group(1)
    counter = int(re.search('var\s*c\s*=\s*(\d*)', data).group(1))

    return(url, counter)

def url2str(url, data=None):
    '''Fetch url and return its content as a string.'''

    f = urllib.request.urlopen(url, data)
    data = f.read().decode("utf-8", "replace")

    return(data)

def usage(msg=None):
    if msg is not None:
        print(msg, file=sys.stderr)
    else:
        print('usage: {0} url [url...]'.format(os.path.basename(sys.argv[0])),
              file=sys.stderr)

    sys.exit(1)

if __name__ == "__main__":
    main()