ShootNup: Upload su ImageShack dal tuo cellulare

Finestra principale

Ho finalmente terminato la mia ultima applicazione: ShootNup. Come suggerisce già il titolo (shoot and upload), quest’applicazione permette di caricare le tue foto online su ImageShack, direttamente dal cellulare.

Tra le varie opzioni, è possibile selezionare come oggetto dell’upload una foto salvata in memoria oppure uno screenshot dello schermo del cellulare.
Una volta che l’immagine è stata inserita online, verrà restituito il link diretto ad essa che sarà conservato per tutta la sessione di lavoro.

L’applicazione fa uso della connessione ad internet, e dato il grande numero di dati che viene trasferito (a causa del peso delle immagini) si consiglia l’utilizzo di una connessione wi-fi o di un abbonamento ad internet.

Questo programma è scritto in Python e richiede ovviamente che l’interprete del linguaggio sia installato sul vostro cellulare. Oltre a ciò utilizza due moduli esterni, che sono appswitch e dialog: il primo serve a passare l’applicazione dalla modalità background a foreground quando si salva uno screenshot, il secondo serve per mostrare le schermate di stato (caricamento in corso, upload in corso, ecc.). Non essendo strettamente necessari per l’upload, potete anche non installarli, ma li ho inclusi nel file .zip che segue e consiglio caldamente di installarli.

Ecco qualche screenshot (click per ingrandire):

Download link:

-Download ShootNup.zip (dialog.sis, appswitch.sis, ShootNup.sis)-

Segue il codice sorgente dell’applicazione.

import os
import e32
import urllib
import e32dbm
import httplib
import appuifw
import graphics
import globalui
from time import time, altzone

# These modules are not essential, but program looks better with them.
try:
    import dialog
except ImportError:
    dialog = None

try:
    import appswitch
except ImportError:
    appswitch = None

# Var
COL = (250, 0, 0) # File select color
FILE = None # File to upload
SYS_DIR = ur'C:\Data\ShootNup' # System dir
if not os.path.isdir(SYS_DIR): os.mkdir(SYS_DIR)
URL_FILE = os.path.join(SYS_DIR, u'url_list.txt') # Default URL file
CFG_FILE = os.path.join(SYS_DIR, u'config') # Config file
cfg = e32dbm.open(CFG_FILE, 'c')
FONT1 = ('normal', 20) # Font
FONT2 = ('normal', 14) # Small font
preview = None # Uploading image preview
PREVIEW = ur'%s\preview.jpg' % SYS_DIR # Uploading file preview
NW = 130 # Preview image width
NH = 100 # Preview image height
url_list = [] # Url list
SYS_TIME = time() # App start timestamp

def draw(r=None):
    """Draws background"""
    global preview
    c.clear((255, 255, 0))
    c.text((4, 20), text['author'], font=FONT1)
    c.text((4, 45), text['site'], font=FONT1)
    c.text((4, 70), text['description_1'], font=FONT2)
    c.text((4, 88), text['description_2'], font=FONT2)
    if FILE:
        path = FILE.split('\\')
        path = [p.decode('utf-8') for p in path] # I need an unicode path
        if len(path) > 3:
            file_name = u'%s\\%s\\...\\%s' % (path[0], path[1], path[-1])
        else:
            file_name = FILE.decode('utf-8')
    else:
        file_name = text['no_file']
    c.text((4, 110), u'File = %s' % file_name, font=FONT2, fill = COL)
    # Try to show FILE preview
    try:
        last_m = os.path.getmtime(PREVIEW)
        if last_m > SYS_TIME + altzone:
            preview = graphics.Image.open(PREVIEW)
            c.blit(preview, target=(4, 120))
    except SymbianError:
        pass

def photo():
    """Starts camera"""
    from camera import start_finder
    from key_codes import EKeySelect
    appuifw.note(text['ok_to_shoot'], 'info')
    start_finder(lambda img: c.blit(img))
    c.bind(EKeySelect, shoot)

def shoot():
    """Shoot a photo"""
    from camera import stop_finder, take_photo, image_sizes, release
    global FILE
    if dialog:
        d = dialog.Wait(text['taking_photo'], False)
        d.show()
    stop_finder()
    FILE = '%s\\photo.jpg' % SYS_DIR.encode('utf-8')
    # Shoot the photo
    img = take_photo(size=image_sizes()[0],
                     mode='RGB',
                     flash='auto')
    img.save(FILE)
    try:
        preview = graphics.Image.open(FILE.decode('utf-8'))
        w, h = preview.size
        if w > h: preview.resize((NW, NW*h/w)).save(PREVIEW)
        else: preview.resize((NH*w/h, NH)).save(PREVIEW)
    except SymbianError:
        appuifw.error(text['preview_error'], 'error')
    if dialog:
        d.close()
    appuifw.note(text['photo_taken'], 'conf')
    release()

def select_last():
    """Select last shot in E:\images\ """
    global FILE
    from time import strftime
    try:
        images_dir = os.listdir(r'E:\images\%s' % strftime('%Y%m'))
        photos = os.listdir(r'E:\images\%s\%s' % (strftime('%Y%m'), images_dir[-1]))
        if globalui.global_query(u'%s %s?' % (text['select_last'], photos[-1].decode('utf-8'))):
            FILE = r'E:\images\%s\%s\%s' % (strftime('%Y%m'), images_dir[-1], photos[-1])
            if globalui.global_query(text['preview_query']):
                if dialog:
                    d = dialog.Wait(text['preview'], False)
                    d.show()
                    e32.ao_sleep(1)
                try:
                    preview = graphics.Image.open(FILE.decode('utf-8'))
                    w, h = preview.size
                    if w > h: preview.resize((NW, NW*h/w)).save(PREVIEW)
                    else: preview.resize((NH*w/h, NH)).save(PREVIEW)
                except SymbianError:
                    appuifw.error(text['preview_error'], 'error')
                if dialog:
                    d.close()
            else:
                # Try to del previous preview file
                try:
                    os.unlink(PREVIEW)
                except OSError:
                    pass
    except OSError:
        appuifw.note(text['last_not_found'], 'error')

def screenshot():
    """Starts the screenshot capturer"""
    import keycapture
    global capturer, screen_lock
    capturer = keycapture.KeyCapturer(screenshot_key)
    capturer.keys = ([35]) # Hash code
    capturer.start()
    appuifw.note(text['screenshot_info'], 'info')
    if appswitch:
        appswitch.switch_to_bg(u'Python')
        appswitch.switch_to_bg(u'ShootNup')
    screen_lock = e32.Ao_lock()
    screen_lock.wait()

def screenshot_key(event):
    """Capture the screenshot"""
    from graphics import screenshot
    global FILE
    ss = screenshot()
    ss.save(ur'%s\screenshot.jpg' % SYS_DIR)
    FILE = r'%s\screenshot.jpg' % SYS_DIR.encode('utf-8')
    try:
        preview = graphics.Image.open(FILE.decode('utf-8'))
        w, h = preview.size
        if w > h: preview.resize((NW, NW*h/w)).save(PREVIEW)
        else: preview.resize((NH*w/h, NH)).save(PREVIEW)
    except SymbianError:
        appuifw.error(text['preview_error'], 'error')
    if appswitch:
        appswitch.switch_to_fg(u'Python')
        appswitch.switch_to_fg(u'ShootNup')
    globalui.global_note(text['screenshot_taken'])
    capturer.stop()
    screen_lock.signal()

def list(path):
    """List a directory and return the selected file"""
    # Previous directory
    if path and path.endswith(r'\..'):
        # Show memories
        if path in [r'C:\\..', r'E:\\..', r'C:\..', r'E:\..']:
            files = ['C:', 'E:']
            path = ''
        # Show previous directory
        else:
            path = os.path.split(os.path.split(path)[0])[0]
            # List the dir
            files = os.listdir(path)
            files.sort()
            files[0:0] = [r'..']
    # List the dir
    elif path:
        files = os.listdir(path)
        files.sort()
        files[0:0] = [r'..']
    ufiles = [i.decode('utf-8') for i in files] # appuifw require unicode
    try:
        selected = files[appuifw.selection_list(ufiles, 1)]
    except TypeError:
        # No file selected
        return None
    # If you select E: it mustn't return \E:!
    if path:
        return '\\'.join([path, selected])
    else:
        return selected

def mimetype_ext(filename):
    """Choose a mimetype according to the file extension"""
    supported_mimetypes = {
        '.jpe': 'image/pjpeg',
        '.jpeg': 'image/jpeg',
        '.jpg': 'image/jpeg',
        '.png': 'image/png',
        '.tif': 'image/tiff',
        '.tiff': 'image/tiff',
        '.bmp': 'image/bmp',
        '.gif': 'image/gif',
        '.ico': 'image/x-icon',
        '.pic': 'image/pict',
        '.pict': 'image/pict',
        '.svf': 'image/x-dwg'
    }
    ext = os.path.splitext(filename)[1]
    if ext in supported_mimetypes:
        return supported_mimetypes[ext]
    else:
        appuifw.note(text['not_supported'], 'error')
        return 'application/octet-stream'

def select_file():
    """Select a file to upload"""
    global FILE
    appuifw.note(text['select_file'])
    FILE = None # File not selected
    path = r'C:\\..' # Default dir (back to memories)
    temp = list(path)
    while not FILE:
        if temp and os.path.isfile(temp):
            FILE = temp
            if globalui.global_query(text['preview_query']):
                if dialog:
                    d = dialog.Wait(text['preview'], False)
                    d.show()
                    e32.ao_sleep(1)
                try:
                    preview = graphics.Image.open(FILE.decode('utf-8'))
                    w, h = preview.size
                    if w > h: preview.resize((NW, NW*h/w)).save(PREVIEW)
                    else: preview.resize((NH*w/h, NH)).save(PREVIEW)
                except SymbianError:
                    appuifw.error(text['preview_error'], 'error')
                if dialog:
                    d.close()
            else:
                # Try to del previous preview file
                try:
                    os.unlink(PREVIEW)
                except OSError:
                    pass
            appuifw.note(text['file_selected'], 'conf')
        elif temp and os.path.isdir(temp):
            temp = list(temp)
        else:
            return None

def upload():
    # Check you select a file to upload
    try:
        image = open(FILE).read()
    except IOError:
        appuifw.note(text['no_file'], 'error')
        return None
    # Sending parameters
    params = [('MAX_FILE_SIZE', '3145728'),
              ('refer', 'http://reg.imageshack.us/v_images.php')]
    files = [('fileupload', FILE, image)]
    url = post_multipart('imageshack.us', 80, '/index.php', params, files)
    if url:
        url_list.append(url.decode('utf-8'))
        appuifw.query(text['direct_url'], 'text', url.decode('utf-8'))

def post_multipart(host, port, selector, fields, files):
    """
    Post fields and files to an http host as multipart/form-data.
    fields is a sequence of (name, value) elements for regular form fields.
    files is a sequence of (name, filename, value)
    elements for data to be uploaded as files
    Return the server's response page.
    """
    content_type, body = encode_multipart_formdata(fields, files)
    h = httplib.HTTP(host, port)
    h.putrequest('POST', selector)
    h.putheader('content-type', content_type)
    h.putheader('content-length', str(len(body)))
    if dialog:
        d = dialog.Wait(text['uploading'], False)
    try:
        if dialog:
            d.show()
        h.endheaders()
        h.send(body)
        code, errmsg, headers = h.getreply()
    except Exception, errore:
        if dialog:
            d.close()
        appuifw.note(text['conn_error'], 'error')
        appuifw.note(str(errore).decode('utf-8'), 'error')
        return None
    if dialog:
        d.close()
    # If success, return direct url
    if code == 302:
        return headers.dict['location'].replace('content.php?page=done&l=', '')
    else:
        appuifw.note(u'%s %d!' % (text['error'], code), 'error')
        return None

def encode_multipart_formdata(fields, files):
    """
    fields is a sequence of (name, value) elements for regular form
    fields.
    files is a sequence of (name, filename, value) elements for data to
    be uploaded as files
    Return (content_type, body) ready for httplib.HTTP instance
    """
    BOUNDARY = '---------------------------13049614110900'
    L = []
    for (key, value) in fields:
        L.extend(['--' + BOUNDARY,
                  'Content-Disposition: form-data; name="%s"' % key,
                  '',
                  value])
    for (key, filename, value) in files:
        L.extend(['--' + BOUNDARY,
                  'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename),
                  'Content-type: %s' % mimetype_ext(FILE),
                  '',
                  value])
    L.append('--' + BOUNDARY + '--')
    L.append('')
    body = '\r\n'.join(L)
    content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
    return content_type, body

def show_url(item=None):
    """Show url list"""
    if not url_list:
        appuifw.note(text['no_url'], 'error')
        return
    if item:
        appuifw.query(text['direct_url'], 'text', item)
    else:
        names_list = [url.split('/')[-1] for url in url_list]
        select = appuifw.selection_list(names_list)
        try:
            show_url(url_list[select])
        except TypeError:
            # No link selected
            pass

def save_url():
    """Save url list"""
    file_name = appuifw.query(text['path'], 'text', URL_FILE)
    if not url_list:
        appuifw.note(text['no_url'], 'error')
        return
    try:
        f = open(file_name, 'a')
    except IOError:
        appuifw.note(text['name_error'], 'error')
        return
    for u in url_list:
        f.write(u+'\n')
    f.close()
    appuifw.note(u'%s %s' % (text['url_saved'], SYS_DIR), 'conf')

def quit():
    """Ask for saving url, then quit"""
    if url_list and appuifw.query(text['save_before_exit'], 'query'):
        save_url()
    try:
        os.unlink(PREVIEW)
    except OSError:
        pass
    lock.signal()
    appuifw.app.set_exit()

def lang(l='en'):
    """Choose application language"""
    global text
    if l == 'it':
        cfg[u'lang'] = 'it'
        text = {
            'author': u'Created by Ale152',
            'site': u'www.Wirgilio.it',
            'description_1': u'Scatta una foto o seleziona un file,',
            'description_2': u'poi clicca su Upload.',
            'no_file': u'Nessuno',
            'ok_to_shoot': u'Premere OK per scattare',
            'taking_photo': u'Acquisizione foto in corso...',
            'photo_taken': u'Foto acquisita.',
            'preview_query': u'Creare anteprima immagine?',
            'preview_error': u'Impossibile creare anteprima!',
            'preview': u'Creazione anteprima in corso...',
            'last_not_found': u'Foto non trovata!',
            'screenshot_info': u'Premere # per salvare screenshot',
            'screenshot_taken': u'Screenshot catturato!',
            'not_supported': 'File non supportato!',
            'select_file': u'Selezionare un file da uppare.',
            'file_selected': u'File selezionato.',
            'no_file': u'Nessun file selezionato!',
            'select_last': u'Selezionare',
            'direct_url': u'Url diretto',
            'uploading': u'Upload in corso...',
            'conn_error': u'Errore di connessione!',
            'error': u'Errore',
            'no_url': u'Nessun URL salvato!',
            'path': u'Indirizzo',
            'name_error': u'Nome file non valido!',
            'url_saved': u'URL salvati in',
            'save_before_exit': u'Salvare lista URL acquisiti?',
            # Menu
            'select': u'Seleziona',
            'file': u'File',
            'shoot': u'Scatta foto',
            'last': u'Ultima foto scattata',
            'screenshot': u'Screenshot',
            'URL': u'URL',
            'show_url': u'Mostra URL',
            'save_url': u'Salva lista',
            'language': u'Lingua',
            'italian': u'Italiano',
            'english': u'Inglese',
            'upload': u'Upload!',
            'quit': u'Esci'
        }
    elif l == 'en':
        cfg[u'lang'] = 'en'
        text = {
            'author': u'Created by Ale152',
            'site': u'www.Wirgilio.it',
            'description_1': u'Shoot a photo or select a file, then',
            'description_2': u'click on Upload!',
            'no_file': u'No file',
            'ok_to_shoot': u'Press OK to shoot',
            'taking_photo': u'Taking photo...',
            'photo_taken': u'Photo taken!',
            'preview_query': u'Create an image preview?',
            'preview_error': u'Unable to create preview!',
            'preview': u'Creating image preview...',
            'last_not_found': u'Photo not found!',
            'screenshot_info': u'Press # to save screenshot',
            'screenshot_taken': u'Screenshot taken!',
            'not_supported': 'File not supported!',
            'select_file': u'Select a file to upload.',
            'file_selected': u'File selected.',
            'no_file': u'No file selected!',
            'select_last': u'Select',
            'direct_url': u'Direct URL',
            'uploading': u'Upload...',
            'conn_error': u'Connection error!',
            'error': u'Error',
            'no_url': u'No URL Saved!',
            'path': u'File path',
            'name_error': u'Filename not valid!',
            'url_saved': u'URL saved in',
            'save_before_exit': u'Save URL acquired before exit?',
            # Menu
            'select': u'Select',
            'file': u'File',
            'shoot': u'Take foto',
            'last': u'Last taken photo',
            'screenshot': u'Screenshot',
            'URL': u'URL',
            'show_url': u'Show URL',
            'save_url': u'Save URL list',
            'language': u'Language',
            'italian': u'Italian',
            'english': u'English',
            'upload': u'Upload!',
            'quit': u'Quit'
        }
    # Upgrade menu
    appuifw.app.menu = [
        (text['select'], (
            (text['file'], select_file),
            (text['shoot'], photo),
            (text['last'], select_last),
            (text['screenshot'], screenshot))),
        (text['URL'], (
            (text['show_url'], show_url),
            (text['save_url'], save_url))),
        (text['language'], (
            (text['italian'], lambda: lang('it')),
            (text['english'], lambda: lang('en')))),
        (text['upload'], upload),
        (text['quit'], quit)
    ]

# Check for language in config file
if u'lang' in cfg:
    lang(cfg[u'lang'])
else:
    lang()

# Application body
c = appuifw.Canvas(redraw_callback=draw)
appuifw.app.body = c
appuifw.app.title = u'ShootNup'
appuifw.app.exit_key_handler = quit

# Prevent app closing
lock = e32.Ao_lock()
lock.wait()