#!/usr/bin/python

#
# Upload a file to S3 and return a web URI to clipboard, indicating progress via desktop notifications
#
# Usage: $0 [file-path]
# If no file-path argument is given, URI is read from clipboard
#
# TODO:
# - add use of option parser to optionally support s3 key prefix
#

import notify2
import pyperclip
import mimetypes
import os
import sys
import urllib
import boto3
from botocore.exceptions import ClientError

min_size = (100 * 1000 * 1000) # minimum filesize to notify upload milestones for
thresholds = [25, 50, 75] # percentages of upload to notify on
aws_profile = ''
s3_bucket = ''
detect_type = True # store at s3 key prefix depending on mimetype or file extension
domain = '' # http(s)://domain.tld

notify2.init('S3 Uploader')
def notification(title, body):
    n = notify2.Notification(title, body)
    n.show()

class S3Upload:
    def __init__(self):
        self.total = 0
        self.uploaded = 0
        self.progress = 0
        self.thresholds = thresholds

    def upload_callback(self, size):
        if self.total == 0:
            return
        
        self.uploaded += size
        self.progress = int(self.uploaded / self.total * 100)

        # if over 100MB and progress advanced to past a threshold percentage
        if self.total >= min_size and [self.progress for t in self.thresholds if self.progress >= t]:
            notification('S3 Upload', str(self.progress) + '% transferred')
            self.thresholds.pop(0)
        elif self.progress >= 100:
            notification('S3 Upload', 'Completed successfully!')

    def upload(self, bucket, key, file):
        self.total = os.stat(file).st_size

        with open(file, 'rb') as data:
            try:
                notification('S3 Upload', 'Starting!')

                try:
                    aws_profile != ''
                    session = boto3.Session(profile_name = aws_profile)
                    s3 = session.client('s3')
                except:
                    s3 = boto3.client('s3')

                s3.upload_fileobj(data, bucket, key,
                    ExtraArgs={'ContentType': mimetype},
                    Callback=self.upload_callback)
            except ClientError as e:
                notify2.Notification('S3 Upload', e)
                print(e)

try:
    # if an argument is passed, use that as file to upload
    sys.argv[1] and isinstance(sys.argv[1], str)
    filepath = sys.argv[1]
except:
    # otherwise, grab path from clipboard
    filepath = pyperclip.paste()

# if the file doesnt exist, error out
try:
    os.lstat(filepath)
except OSError as e:
    notification('S3 Upload', e)
    print(e)

filename = os.path.basename(filepath)

s3_key = ''
if detect_type:
    mimetype = mimetypes.guess_type(filepath)[0]
    mimens = mimetype.split('/')[0]
    fileext = filename.split('.')[1]

    # map file classifications to s3 key paths
    if mimens == 'image':
        s3_key = 'img'
    elif mimens == 'audio' or [e for e in ['mp3','flac'] if fileext == e]:
        s3_key = 'aud'
    elif mimens == 'text' or mimetype == 'application/pdf':
        s3_key = 'txt'
    else:
        notification('S3 Upload', 'Unknown file type')
        print('Unknown file type')

u = S3Upload()
u.upload(s3_bucket, s3_key + '/' + filename, filepath)

# copy resulting web accessible uri to clipboard
access_uri = domain + '/' + s3_key + '/' + urllib.parse.quote_plus(filename)
print(access_uri)
pyperclip.copy(access_uri)