#!/usr/bin/env python
'''
NukeFS Ultimate (NFSU): twin-algorithm shortener-based file cloud storage for Python 2.6+
Full library and command-line implementation
by Multiversum
Usage as stand-alone application: see nukefs.py -h
Usage as library:
import nukefs
//for master-chunk algorithm:
key_of_file = nukefs.upload("file.zip")
download(key_of_file, "downoaded.zip")
//for streaming algorithm:
key_of_file = nukefs.upload("file.zip", True)
download(key_of_file, "downoaded.zip", True)

Note: it gets damn slow and somewhat unstable on large files so plz don't upload >1M files
'''
 
from requests import get
from json import dumps, loads
from urllib import quote_plus, unquote_plus

CHUNKLEN=22000 #current TinyURL effective chunk length limitation

def chunk_upload(localpath):
    filekeys = []
    baseurl = 'http://tinyurl.com/api-create.php?url='
    with open(localpath, 'rb') as fileobj:
        while 1:
            data = fileobj.read(CHUNKLEN)
            if not data: break
            encoded = quote_plus(''.join(data.encode('base64').splitlines()))
            key = get(baseurl+encoded).text[19:]
            filekeys.append(key)
        fileobj.close()
    return get(baseurl+quote_plus(dumps(filekeys))).text[19:]
     
def chunk_download(filekey, localpath):
    filekeys = loads(unquote_plus(get('http://tinyurl.com/'+filekey, allow_redirects=False).headers['location']))
    with open(localpath, 'wb') as fileobj:
        for key in filekeys:
            data = get('http://tinyurl.com/'+key, allow_redirects=False).headers['location']
            if not data: break
            fileobj.write(unquote_plus(data).decode('base64'))
        fileobj.close()

def stream_upload(localpath):
	chunks = []
	with open(localpath, 'rb') as fileobj:
		while 1:
			data = fileobj.read(CHUNKLEN)
			if not data: break
			chunks.append(''.join(data.encode('base64').splitlines()))
		fileobj.close()
	baseurl = 'http://tinyurl.com/api-create.php?url='
	key = None
	for i in xrange(len(chunks),0,-1):
		chunkobj = {'data' : chunks[i-1], 'next' : key}
		encoded = quote_plus(dumps(chunkobj))
		key = get(baseurl+encoded).text[19:]
	return key
    
def stream_download(key, localpath):
	with open(localpath, 'wb') as fileobj:
		while 1:
			data = get('http://tinyurl.com/'+key, allow_redirects=False).headers['location']
			if not data: break
			chunkobj = loads(unquote_plus(data))
			if not chunkobj['data']: break
			fileobj.write(chunkobj['data'].decode('base64'))
			if not chunkobj['next']: break
			key = chunkobj['next']
		fileobj.close()

#universal functions to be called outside
#algo: False - master-chunk algorithm (default), True - streaming algorithm

def upload(localpath, algo=None):
	'''
	Upload a file from localpath to cloud and get its access key
	algo: False or None - master-chunk algorithm (default), True - streaming algorithm
	'''
	if algo:
		return stream_upload(localpath)
	else:
		return chunk_upload(localpath)

def download(filekey, localpath, algo=None):
	'''
	Download a file to localpath by its access key
	algo: False or None - master-chunk algorithm (default), True - streaming algorithm
	'''
	if algo:
		stream_download(filekey, localpath)
	else:
		chunk_download(filekey, localpath)
		
#stand-alone app code
if __name__ == '__main__':
	from optparse import OptionParser
	parser = OptionParser()
	parser.add_option("-u", dest="upfilename",
                help="upload FILE to cloud", metavar="FILE")
	parser.add_option("-d", dest="downfilename",
                help="download FILE from cloud", metavar="FILE")
	parser.add_option("-k", dest="accesskey",
		help="Access key to download a file")
	parser.add_option("-s", "--stream", action="store_true", dest="algo",
				help="Use streaming algorithm instead of chunked")
	(options, args) = parser.parse_args()
	if options.upfilename and options.downfilename:
		parser.error("You can either download or upload at the same time")
	if options.downfilename and not options.accesskey:
		parser.error("You must specify the access key (with -k option) to download a file")
	if options.upfilename:
		print 'Uploading...'
		accesskey = upload(options.upfilename, options.algo)
		print 'Done. File access key is', accesskey
	elif options.downfilename:
		print 'Downloading...'
		download(options.accesskey, options.downfilename, options.algo)
		print 'Done.'
	else:
		print "Please specify either the file to upload or the file to download. See nukefs.py -h for help."
