Author: Michele Bologna

  • iTunes e il flag “Part of a compilation”

    Durante una riorganizzazione della mia libreria iTunes, mi sono chiesto che cosa significasse l’opzione “Part of a compilation” in iTunes per una determinata traccia.

    Selezionando questa opzione, impedite ad iTunes di creare una cartella personalizzata per l’autore in questione (aprite la directory iTunes Media sul vostro hard disk e capirete). E questa opzione è utile quando abbiamo un album che rappresenta una compilation (ogni traccia è realizzata da un artista diverso). Buono a sapersi!

  • Online JavaScript Deminifier

    Se vi trovate a dover debuggare del codice JavaScript che è minified (per ottimizzare il trasferimento viene applicato il minifying, ovvero vengono tolti tutti gli spazi inutili nel codice), dovete passare per un deminifier/unminified (o beautifier). In ogni caso, un tool che vi permetta di leggere il codice JavaScript senza “incrociare gli occhi“.

    A tal proposito ho provato JSBeautifier e ne sono rimasto soddisfatto. Segnalo anche Javascript Deminifier (per Firefox).

  • Python: scriviamo un generatore di Fibonacci ricorsivo (e poi iterativo) ed analizziamone la complessità asintotica

    Tralasciando la parte matematica su cui potete trovare un’esauriente (ed interessante) spiegazione su Wikipedia, la serie di Fibonacci si definisce:

    F(n) = F(n-1) + F(n-2)

    per n > 1, e F(1) = 1 e F(0) = 0

    Scriviamo un generatore di questa serie in Python:

    #!/usr/bin/python
    
    # fib(n) = fib(n-1)+fib(n-2)
    # fib(0) = 0
    # fib(1) = 1
    # fib(2) = 1
    # fib(3) = 2
    # fib(4) = 3
    # fib(5) = 5
    # fib(6) = 8
    # ...
    
    def fib(n):
        if n == 0:
            return 0;
        if n == 1:
            return 1;
        else:
            return fib(n-1) + fib(n-2)
    

    Abbiamo utilizzato un’implementazione ricorsiva; il vero problema di quest’implementazione è subito chiaro: la complessità O(n^2) data dalla ricorsione e dalle tail-call sullo stack (per n molto grande).
    Decidiamo allora di scriverne una versione equivalente ma iterativa, in modo da eliminare il problema della complessità:

    def fibnr(n):
        fn1 = 1;
        fn2 = 0;
        fib = 0;
        i = 2;
        if n == 0:
             return 0;
        if n == 1:
             return 1;
    
        while (i <= n):
            fib = fn1 + fn2
            tmp = fn1
            fn1 = fib
            fn2 = tmp
            i += 1
        return fib
    

    La vera complessità questa volta, sta nella testa del programmatore: è molto importante, infatti, scrivere nel modo più preciso possibile il corpo del while. Inoltre, è necessario implementare uno swap di variabile per tenere traccia della storicità della serie (per semplicità qui non ho usato lo swap di due variabili senza una variabile temporanea che ho presentato nei post precedenti). La complessità di questa soluzione iterativa è O(n).

    Implementando una sorta di controllo automatico (tramite assert), possiamo capire che la soluzione iterativa è equivalente alla soluzione ricorsiva, ma sappiamo anche che è più veloce e lineare (grazie all’analisi asintotica che abbiamo realizzato):

    #!/usr/bin/python
    
    # fib(n) = fib(n-1)+fib(n-2)
    # fib(0) = 0
    # fib(1) = 1
    # fib(2) = 1
    # fib(3) = 2
    # fib(4) = 3
    # fib(5) = 5
    # fib(6) = 8
    # ...
    
    def fib(n):
        if n == 0:
            return 0;
        if n == 1:
            return 1;
        else:
            return fib(n-1) + fib(n-2)
    
    def fibnr(n):
        fn1 = 1;
        fn2 = 0;
        fib = 0;
        i = 2;
        if n == 0:
             return 0;
        if n == 1:
             return 1;
    
        while (i <= n):
            fib = fn1 + fn2
            tmp = fn1
            fn1 = fib
            fn2 = tmp
            i += 1
        return fib
    
    if __name__ == '__main__':
        for i in xrange(21):
            print i, ',', fib(i), '|', fibnr(i)
            assert (fib(i) == fibnr(i))
    
  • 7zip da linea di comando

    Segnalo che esiste la possibilità di usare il popolare compressore/decompressore 7-zip (non sarete rimasti a WinRAR?) da linea di comando. Le operazioni che potete fare sono molteplici (aggiunta/rimozione/list, etc.).
    Ad esempio, in uno script bat di manutenzione ho utilizzato:

    7za a -ttar Backup%date%.tar *.txt Reg*
    7za a -tgzip Backup%date%.tar.gz Backup%date%.tar

    per creare un file tar.gz a fronte di tutti i files Reg* presenti in una directory. Ovviamente, consiglio di aggiungere il path di installazione di 7-zip al PATH di Windows.

  • Python: scriviamo un HTTPS downloader simile a wget (con urllib2, optparse e getpass)

    Per ragioni di semplicità di utilizzo e immediatezza (e anche per imparare qualcosa di nuovo), la settimana scorsa ho dovuto scrivere un downloader da linea di comando simile a GNU wget, ma con alcuni requisiti personalizzati:

    • l’accesso alla pagina di download è protetto da userid e password (autenticazione HTTPDigestAuth);
    • il protocollo di accesso è HTTPS;
    • una volta ottenuto l’accesso, è necessario fare il GET di alcuni files ben particolari e crearli su hard disk.

    Per realizzare questo wget-like in Python, usiamo il modulo urllib2 che ci permette di accedere a risorse esterne tramite i protocolli più svariati. Per aprire una connessione HTTPS e autenticarci correttamente usiamo i seguenti statement:

        passman.add_password(None, url, username, password)
        authhandler = urllib2.HTTPDigestAuthHandler(passman)
        opener = urllib2.build_opener(authhandler)
    

    Affinché la gestione delle varie opzioni sia più semplice possibile, usiamo anche il modulo optparse (che permette di passare le opzioni al programma in modo simile alle utility GNU):

    parser = optparse.OptionParser()
    parser.add_option("-u", "–username")
    parser.add_option("-d", "–date")
    parser.add_option("-t", "–target", default="logs")
    options, arguments = parser.parse_args()
    

    Visto che dovremo gestire anche l’autenticazione tramite username e password, la scelta di passare la password come argomento del programma è una scelta non ottimale: una semplice ricerca nella history della shell utilizzata potrebbe permettere ad un attaccante di risalire alla password. Per questo ci viene incontro il pacchetto getpass che, in modo simile ai programmi UNIX che si rispettino, chiede interattivamente la password (senza stamparla, ovviamente!). Per richiedere l’inserimento della password all’utente è sufficiente:

    password = getpass.getpass()
    

    Torniamo ora all’inizio: abbiamo ottenuto dall’utente username (passato come argomento) e password (richiesta interattivamente): ora possiamo aprire una connessione verso l’hostname di riferimento e richiedere il download del file (salvando su file lo stream del file in ricezione tramite i metodi standard di Python):

    passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
    passman.add_password(None, url, options.username, password)
    authhandler = urllib2.HTTPDigestAuthHandler(passman)
    opener = urllib2.build_opener(authhandler)
    download = opener.open(file)
    logging.debug('downloading %s ...' % file)
    fo = open(options.target + os.sep + targetFileName, 'w')
    fo.write(download.read())
    fo.close()
    

    Abbiamo quindi ottenuto un semplice downloader di file scritto in Python, molto più semplice di wget ma al tempo stesso molto più customizzabile.

  • Ubuntu: avviare X senza uno schermo

    Mi è capitato di dover gestire una macchina con Xubuntu (ma questa soluzione si applica a tutte le *Ubuntu) che doveva essere utilizzata da remoto (tramite TeamViewer) e senza uno schermo attaccato.

    Ubuntu, intelligentemente, se non trova uno schermo attaccato allo startup non esegue X. Se X non viene eseguito, tuttavia, Teamviewer non può partire. Come risolvere questo problema?

    Apriamo una shell di root e modifichiamo la configurazione di grub (nel file /etc/default/grub; una volta questo file era era /boot/menu/grub.lst). Modifichiamo l’opzione seguente aggiungendo ‘nomodeset’:

    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nomodeset"

    salviamo e riavviamo. Risultato raggiunto!

  • Creare un panorama “incollando” più foto tra di loro con Hugin

    Le foto ad alta risoluzione ed incollate fra loro mi hanno sempre affascinato ed incuriosito: come possono essere prodotte con una semplice fotocamera tascabile?
    Ebbene, dopo varie ricerche ed aver provato diversi software, sono arrivato allo zen della composizione di foto.
    Innanzitutto, partiamo dal prodotto finale. Quella che vedete qui sotto è una vista panoramica di quello che si può ammirare dalla torre Eiffel, a Parigi: ho ottenuto questa vista incollando tra loro 6 fotografie, ottenute con una fotocamera digitale comune, senza un treppiede e senza apparecchiatura specialistica.

    Il risultato è davvero spettacolare, per una risoluzione finale di 4122×1438. Veniamo ora alla teoria: la fotografia panoramica prevede di “incollare” tra loro più fotografie, scattate in sequenza e con angolature diverse (per esempio ruotando leggermente la posizione di scatto della fotografia). Esistono software (il migliore è Hugin) che permettono di incollare queste foto in modo semi-automatico: il punto chiave del processo, come potete immaginare, è trovare dei punti di connessione tra le foto, ovvero dei punti di controllo che rendano il processo di “incollatura” il più preciso possibile. Ovviamente, più sono i punti di controllo tra due foto, più preciso sarà il merge tra le foto scattate.

    Hugin automatizza e semplifica il processo: se non siete dei virtuosi della fotografia panoramica (come il sottoscritto), il procedimento per l’unione delle foto viene presentato con un comodo wizard che vi permette di inserire le immagini, validare i control point identificati automaticamente del programma e creare il vostro primo panorama.

    Segnalo anche alcuni utili tutorial di Hugin, che spiegano come scattare correttamente le fotografie in modo che siano pronte per creare un panorama perfetto; inoltre, i tutorial spiegano anche come fare il tuning delle varie opzioni che possiamo trovare nel programma, per creare delle foto panorama personalizzate.

    In definitiva, Hugin è davvero un ottimo programma, che permette di creare facilmente delle foto panorama sia ad utenti principianti che foto panorama personalizzate per utenti esperti.

  • La guida di Google al SEO

    Segnalo che Google ha rilasciato una guida ufficiale al SEO (Search Engine Optimization) contenente tutti i consigli per i webmaster. La guida non è particolarmente lunga (32 pagine) ed è molto semplice (starter guide).

    I consigli forniti da Google sono molto utili e conosciuti, ma non fa mai male leggere quello che Google consiglia per promuovere il proprio sito. In particolare, segnalo:

    • l’utilizzo corretto del tag <title>
    • utilizzo dei meta tag (più specificatamente il tag description)
    • URL structure “parlante” (es. michelebologna.net/2010/keywords)
    • avere un contenuto sempre aggiornato (pubblicare spesso)
    • usare testi corretti negli tag anchor (“clicca <a href=”..”>qui</a>” è un orrore semplice da non commettere)
    • inserire sempre l’alt nelle immagini! (serve per gli screen reader)
    • gestire correttamente i robots.txt
    • creare correttamente le sitemap per i motori di ricerca (sitemap.xml)
    • progettare un contenuto separato per dispotivi mobili

    In definitiva, una lettura consigliata per tutti i webmaster e per chi si occupa di SEO.

    Official Google Webmaster Central Blog: SEO Starter Guide updated.

  • Eliminare il pezzo cantato di una canzone con Audacity

    Oggi sto “giocando” con Audacity per creare una versione “Karaoke” di una canzone; dopo aver cercato un po’ di tutorial, ho trovato quest’articolo breve e chiaro su come rimuovere il pezzo cantato di una canzone: Can I remove the vocals from a recording to make a Karaoke track?.

    La teoria che sta dietro a questo “trucco” è molto semplice: ogni canzone stereo ha due tracce, left e right. Separando le due tracce, e invertendo di fase una delle due, si possono “cancellare” alcune frequenze (come ad esempio quella relativa alla voce, che se ben ricordo viaggia intorno ai 4 Khz).

    Ora, la teoria che sta dietro a questo procedimento non è precisa: con molte canzoni questo non funziona. Ho fatto alcune prove, per alcune canzoni riesce effettivamente ad eliminare la parte cantata (con un leggero degrado audio); per altre, invece, la “cancellazione” non funziona.

    Rimango in attesa di essere illuminato da un audiofilo!

  • Ubuntu: l’aggiornamento a 11.04 corrompe grub. Ecco come risolvere

    Il 28/04, incuriosito dalla nuova release di Ubuntu (11.04) ho subito aggiornato dalla versione 10.10 usando la funzionalità di aggiornamento integrata nel sistema.

    Tuttavia, con mia grande sorpresa, l’aggiornamento corrompe la configurazione di grub, lasciando il sistema in uno stato inavviabile (in particolare, il sistema si arresta sulla schermata di grub prima del boot); e sembra che sia un problema generalizzato! Non tutto è perduto: il problema deriva da un’errata configurazione di grub prodotta dalla procedura di aggiornamento, ma possiamo recuperare il sistema senza dover reinstallare. Per risolvere il problema, procediamo nel seguente modo:

    • Prendete un CD di Ubuntu che abbia la stessa architettura del sistema da recuperare (i386 o amd64; se non sapete quale scegliere, usate i386);
    • Avviate il sistema da LiveCD e aprite un terminale
    • Trovate la partizione contenente la /boot del sistema da recuperare (aiutatevi con fdisk -l) e montatela (ad es. sudo mount /dev/sda1 /media/linux)
    • Montiamo le directory su cui dobbiamo operare:

      sudo mount -B /dev /media/linux/dev
      sudo mount -B /dev/pts /media/linux/dev/pts
      sudo mount -B /proc /media/linux/proc
      sudo mount -B /sys /media/linux/sys

    • E rilocalizziamo il sistema sul sistema target da recuperare: sudo chroot /media/linux
    • A questo punto ripariamo la configurazione di grub:

      grub-install /dev/sda
      update-grub

    • Smontate il filesystem e riavviate: il vostro sistema si avvierà con la versione di Ubuntu appena aggiornata!