giovedì 16 gennaio 2014

Script MangaPdfConverter

Ciao a tutti!
Eh sì ... sto un po' trascurando il blog (alla faccia di un po' :P), logicamente gli impegni lavorativi, sentimentali e hobbistici ( :) ) annullano il tempo libero a disposizione.
Mi ritrovo a voler scrivere un nuovo post grazie ad una delle mie fonti di ispirazione preferite in ambito linux ... @bit3lux ed in particolare da un suo post sul suo blog che vi consiglio sentitamente (http://parliamodi-ubuntu.blogspot.it/2014/01/lo-script-radiosh-si-aggiorna.html).

Proprio nello script di cui parla l'articolo, mi si è aperto un piccolo mondo che per la mia limitata esperienza non avevo ancora esplorato, parlo dell'utility Zenity di cui senza super approfondire vi rimando al sito https://help.gnome.org/users/zenity/stable/index.html.en.

Per farla breve si tratta di un'applicazione che, da riga di comando, permette di utilizzare dialog in gtk, piuttosto utili.

La curiosità e la voglia di utilizzarla mi ha spinto a modificare un mio script di qualche settimana fa così da poter dar modo di impostare con un'interfaccia grafica, i parametri principali.

Come da titolo, lo script in questione, l'ho scritto per ottimizzare la mia collezione di manga giapponesi facendo sì che, grazie all'integrazione con un'altra applicazione, ogni volume, composto da tanti file .jpg/.png quante sono le pagine che lo compongono, venga convertito in pdf.
Questo può essere inoltre utile, anche quando si hanno a disposizione le scansioni di pagine di libri suddivisi in capitoli e le si vogliono unire in pdf...

Requisiti collezione (file system)

La struttura su file system della mia collezione è così disposta


  • Directory Manga1
    • Directory 001 (Cap 1)
    • Directory 002 (Cap 2)
    • ...
  • Directory Manga2
    • Directory 001 (Cap 1)
    • ...
  • Directory Manga3
    • ...
  • ...
In pratica ci sono tante directory quanti sono i manga che possiedo, ogni directory è composta da tante sottodirectory quanti sono i capitoli (ma volendo anche i volumi) del manga, ogni sottodirectory contiene tanti file .jpg/.png quante sono le pagine che compongono i capitoli.

Come test faccio l'esempio dell'ipotetico manga ArtigliDellaLince il manga è composto da 65 capitoli con un numero diverso di pagine.
Importante è precisare che, anche se, poi vedremo nello script, è parametrizzabile, in questa versione è necessario che all'interno di ogni directory-capitolo sia presente una pagina denominata 000 (che sia .jpg o .png è indifferente) che poi può fungere anche la cover del capitolo.
Di seguito grosso un idea della struttura

Lista directory contenute nel manga

Lista pagine

Il risultato della conversione saranno 65 file .pdf composti da tante pagine quanti sono i file all'interno delle directory "volumi".
I file saranno denominati
[NumeroVolume] [NomeManga].pdf
nel mio caso "001 ArtigliDellaLince.pdf", "002 ArtigliDellaLince.pdf" ecc...

Requisiti software

Cosa occorre per poter eseguire lo script ?
Innanzitutto, l'applicazione che fa il vero lavoro, ovvero ImageMagickhttp://www.imagemagick.org/).
E poi come detto prima zenity, ma questa dovrebbe essere già presente su Ubuntu (almeno nel mio caso ... acc... dimenticavo ... ora sto col pangolino ... Ubuntu 13.04 :P ).

Modus operandi

Come detto ImageMagick è il cuore dello script.
In pratica lo script, una volta impostata la cartella del manga, il nome ed il numero di capitoli, non farà altro che eseguire la riga di comando (per ogni directory) :

convert *.* "../$dir $manga.pdf" 

in parole spicce.. il comando convert, converte tutte le immagini contenute nella directory impostata su $dir nel pdf $dir $manga.pdf (dove $manga è appunto il nome del manga).


Piccola parentesi

Come detto precedentemente lo script è di qualche settimana fa, all'inizio non vi era implementazione con zenity pertanto i parametri [Manga], [Volumi] e [PercorsoDirectory] venivano impostati direttamente passandoli allo script da shell.


Richiamo script

Prima di descrivere il codice, vi stuzzico (:P) mostrandovi come lavora.
In ordine 

  • Salviamo lo script in una directory (io metto tutti gli script nella directory scripts nella mia home)
  • Rendiamolo eseguibile con il solito chmod +x MangaPdfConverter.sh
  • Eseguiamolo con ./MangaPdfConverter.sh 

 Per prima cosa ci verrà chiesto di impostare il nome del manga ed il numero di capitoli da cui è composto (trucco.. non è necessario che siano presenti tutte le directory da 1 a n l'importante è che n corrisponda al numero che impostiamo, verranno convertite solo le directory presenti).
Il dialog sarà il seguente :

Subito dopo aver premuto Ok, si aprirà il dialog che permetterà di selezionare, sfogliando il proprio file system, la directory del manga (quella appunto contenente le n directory dei capitoli)

Appena si conferma, avrà inizio la conversione

Al termine verrà mostrato (se tutto è andato ok) il seguente messaggio

Il risultato della conversione è la generazione di 65 file .pdf :

Code 

Descriviamo ora il codice.
La prima operazione svolta all'interno dello script è il settaggio di titolo e numero capitoli con il dialog mostrato sopra, questo è possibile tramite questo codice

datiManga=$(zenity --forms --title="Dati Manga" \
--text="Inserisci dettagli." \
--separator="|" \
--add-entry="Titolo" \
--add-entry="Numero Capitoli")

Nella variabile $datiManga verrà memorizzata una stringa tipo Titolo|NumeroCapitoli
che come si può capire sarà necessario splittare in due variabili ...

Ammetto, questa operazione che in Java risulta piuttosto semplice, su shell mi ci sono volute un pochino di ricerche soprattutto per capire cosa fosse la variabile IFS (rimando http://en.wikipedia.org/wiki/Internal_field_separator) e cosa servisse, pertanto dopo aver impostato il carattere pipe "|" come delimitatore scorro l'array generato dalla stringa e imposto la variabile $manga con Titolo e la variabile $numeri con i numeri dei capitoli.


#variabili iniziali
ix=0
OIFS=$IFS
IFS='|'
arr=$datiManga
numeri=0
manga=""

#imposto il titolo del manga e il numero dei capitoli ricavati 
#dal form (variabile $datiManga>$arr)
for cnt in $arr
do
case $ix in
0) manga="$cnt" ;;
1) numeri=$cnt ;;
esac
ix=$(( $ix + 1 ))
done

IFS=$OIFS

Dopo aver verificato con due banali if che la variabile $manga non è vuota e la variabile $numeri è diversa da zero imposto la variabile $path sempre grazie a zenity:

path=$(zenity --file-selection --directory)

Questo permetterà appunto l'apertura del dialog per sfogliare il file system e quindi selezionare la directory.

A questo punto non rimane altro che gestire il ciclo che per ogni directory eseguirà la conversione ...
se ... te pare facile :D ;)
Scherzo è facile ma proprio per interagire con zenity (per capirci con il dialog che mostra la progressbar con lo stato della conversione) ritengo necessario iniziare a descrivere il codice per gradi ...
( ... Codice ...) | zenity --progress \
 --title="Conversione Manga $manga" \
 --text="Cerco" \
 --percentage=0 \
 --width=500 \
 --auto-close
Per richiamare la progressbar di zenity, bisgogna seguire questa sintassi, ovvero,
mettere prima del suo richiamo, il codice che deve man mano impostare l'avanzamento di stato.

Chiaramente, il nostro codice imposterà di volta in volta il numero di capitolo e verificherà la presenza dei file 000.[estensione] (si lo so ... ma na verifica sulla presenza della directory non era meglio...? :/ :D )

Per aggiornare l'avanzamento  utilizzo una variabile $perc che per ogni item (inteso come numero capitolo) imposto, appunto la percentuale con un banale:
perc=`expr $x \* 100 / $numeri`

Praticamente richiamando
echo "$perc" 
si dirà a zenity quale la percentuale di avanzamento deve impostare sulla barra,
mentre richiamando
echo "# testo informativo" 
si dirà a zenity di mostrare il testo scelto come "stato" (la differenza è propio "#" all'inizio).

Il ciclo while che viene eseguito, parte dal valore iniziale 1 (valore di $x) al valore di $numeri e svolge le seguenti operazioni :
Imposta il valore della variabile $dir impostando qualora $x sia minore di 10 in "00$x" e qualora minore di 100 in "0$x"
if [ $x -lt 10 ]
then
     dir="00$x"
else
    if [ $x -lt 100 ]
    then
        dir="0$x"
    else
        dir="$x"
    fi
fi

In seguito, essendo ora a conoscenza del path e del nome della directory, verifico la presenza del file 000.[estensione].
Nel mio caso mi sono limitato a file con estensione .jpg, .png e .JPG, volendo se ne possono gestire anche altri tipo .gif ecc... l'unica cosa che ho verificato è che ImageMagick con alcuni tipi di .gif va in errore (immagino per codifiche diverse dal classico RGB, 8bit ecc), pertanto se possibile è bene verificare presenza di tali tipi di file.

Se la verifica ha effetto positivo imposto
la directory in cui mi devo spostare
toElab="$path/$dir"
e la variabile flag che mi consentirà di saltare la conversione se =  1
flg=0

Infine se $flg = 0
mi sposto nella directory
cd "$toElab"
aggiorno lo stato del dialog della progress di zenity
echo "# converto capitolo $dir"
unisco e converto tutte le immagini nel pdf
convert *.* "../$dir $manga.pdf"

Dall'istruzione
(.. codice ..) | zenity --progress ....
si ottiene un parametro di ritorno, che se  uguale -1 identifica la presenza di un errore durante l'esecuzione, se uguale a 0 significa che il codice è stato eseguito completamente e se uguale a 1 significa che il codice è stato interrotto anticipatamente (magari stoppato dall'utente)
pertanto il nostro dialog di conferma riporterà messaggi differenti in tutti e tre i casi :

if [ "$?" = -1 ] ; then
    zenity --error \
    --text="Conversione Fallita."
else
    if [ "$?" = 0 ] 
    then
        zenity --info --title="Success" \
        --text="Manga convertito correttamente!"
    else
        zenity --info --title="Success" \
        --text="Operazione terminata ! \n Non tutti i capitoli sono stati convertiti"
    fi
fi

Bè direi che con questo siamo arrivati alla fine... (e che ve pensavate fosse un poema?! :D) ...

Link Script completo (aimè dall'ufficio pastebin è bloccato :P ) : http://notepad.cc/share/w6GfeZvPjs

Critiche e autocritiche

Chi mi conosce sa che sviluppo in .net ma adoro il mondo linux, pertanto ammetto di aver scritto lo script utilizzando sintassi meno avanzata e non gestendo le 10.000 casistiche possibili nell'esecuzione,
ma spero da qui al prossimo futuro di implementare migliorie e nuove feature man mano che la mia conoscenza si consolida :) ... per ora posso solo dire che in casi come questo lo script da un bella mano, soprattutto se si è soliti leggere manga su dispositivi android come me, e quindi si preferisce invece di incasinare la propria galleria con migliaia di immagini, poter organizzarla in modo più ordinato utilizzando invece i pdf :) ...
Chiaramente le migliorie sono pronto ad implementarle grazie anche ai vostri consigli, sempre ben accetti ;)
ciao a tutti