joe di castrohttp://joedicastro.com2012-10-19T22:40:00+02:00Productividad & Linux: Ncdu2012-10-19T22:40:00+02:00joe di castrohttp://joedicastro.com/productividad-linux-ncdu.html<p>Esta es una pequeña aplicación que sigue la máxima de <a href="http://es.wikipedia.org/wiki/Douglas_McIlroy">Doug Mcllroy</a> que
acabaría resumiendo la filosofía Unix <em>"Escribe programas que hagan una sola
cosa y que la hagan bien"</em>. Y siguiendo la línea de los programas que engloban
esta serie de productividad sobre Linux, es un programa de consola que se maneja
íntegramente con el teclado, y donde además los atajos están inspirados en los
de <a href="http://www.vim.org/">Vim</a>.</p>
<h2 id="ncdu">Ncdu</h2>
<p><a href="http://dev.yorhel.nl/ncdu">Ncdu</a> es un acrónimo para "NCurses Disk Usage", es decir, "Uso de disco
con interfaz ncurses". Lo que pretendía <a href="https://github.com/yorhel">Yoran Heling</a>, su autor, cuando
creo <strong>ncdu</strong>, era obtener un analizador de uso de disco en consola que fuera lo
suficientemente ligero para poder emplearlo en un servidor remoto a través de
<a href="https://es.wikipedia.org/wiki/Ssh">ssh</a>. El gran acierto fue emplear la interfaz <a href="https://es.wikipedia.org/wiki/Ncurses">ncurses</a> para
poder navegar por los directorios con extrema facilidad al mismo tiempo que se
presentaba la información en un formato legible y amigable. El segundo y no
menos importante acierto, es emplear los conocidos atajos de teclado de Vim para
navegar entre los directorios.</p>
<p>El resultado es un analizador de ocupación de disco que es realmente útil,
agradable de utilizar y que es tan rápido y ligero que se puede emplear sin
interfaz gráfica y en maquinas remotas. Es una gran ayuda para los
administradores de sistemas, que hasta ahora nos teníamos que pelear con otras
herramientas bastante menos agradables. Es la típica herramienta que cuando la
utilizas por primera vez piensas ¿Por qué demonios no la habría encontrado
antes?</p>
<p style="text-align:center;"><img src="pictures/ncdu.png" width="700"
height="559" alt="ncdu" /></p>
<p>Como se puede ver la interfaz es muy limpia y carece de elementos superfluos que
nos distraigan de lo importante, un pecado que cometen algunas de las
aplicaciones gráficas más famosas, que cuentan con unos gráficos muy bonitos,
pero realmente no demasiado útiles. Es sencillisima de emplear una vez que
conoces los atajos y cuenta con ayuda integrada a la que se accede pulsando
<strong><code>?</code></strong>. Incluso para aquellos que no están acostumbrados a navegar empleando
las típicas teclas de vim (<strong>h</strong>, <strong>j</strong>, <strong>k</strong>, <strong>l</strong>), se pueden utilizar las
teclas de dirección.</p>
<h3 id="caracter+sticas">Características</h3>
<p>Un programa de este tipo no necesita una funcionalidad muy elevada, pero
destacare aquí algunas de las posibilidades que nos ofrece:</p>
<ul>
<li>Podemos ordenar los directorios/ficheros en función de su nombre o de su
tamaño y en orden ascendente o descendente </li>
<li>Se pueden eliminar directamente ficheros o directorios desde la aplicación,
aunque también podemos iniciarla en modo solo lectura para prevenir borrados
accidentales</li>
<li>Se muestra o no, el porcentaje de espacio ocupado de forma gráfica y/o numérica</li>
<li>El espacio empleado en disco puede hacer referencia al real o al aparente</li>
<li>Los directorios y ficheros ocultos pueden ser o no mostrados</li>
<li>Nos permite recalcular el espacio ocupado de un directorio concreto sin
necesidad de volver a recalcular todo </li>
<li>Tenemos la posibilidad de volcar la información a un archivo, que puede
también ser leído por el mismo programa</li>
<li>Es posible mostrar la información de un elemento individual en un cuadro
emergente</li>
</ul>
<p>La verdad es que lo único que hecho en falta en el programa es quizás un poco de
color para distinguir entre ficheros y directorios de forma más visual. Por lo
demás, es muy rápido calculando el espacio, no he hecho una comparación directa,
pero me parece ligeramente más rápido que Baobab.</p>
<h3 id="alternativas">Alternativas</h3>
<p>Existen varias alternativas decentes como aplicaciones gráficas, como los
conocidos <a href="http://www.marzocca.net/linux/baobab/">Baobab</a> (que viene con las herramientas de Gnome) o
<a href="http://www.jgoodies.com/freeware/jdiskreport/">JDiskReport</a>. Después existen múltiples alternativas tanto en modo
gráfico como en modo consola que, sinceramente, no puedo recomendar a nadie,
bien porque son más vistosos que prácticos o bien porque son directamente
insufribles. Existe una alternativa en modo texto que algunos prefieren a ncdu
que es <a href="http://gt5.sourceforge.net/">gt5</a>.</p>Productividad & Linux: Ranger2012-10-18T21:50:00+02:00joe di castrohttp://joedicastro.com/productividad-linux-ranger.html<p>Algunos pensarán, de acuerdo, podemos entender que prefieras un <a href="http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html">gestor de
ventanas de mosaico</a>, o que emplees aplicaciones como <a href="http://joedicastro.com/productividad-linux-pentadactyl.html">Pentadactyl</a>,
<a href="http://joedicastro.com/productividad-linux-newsbeuter.html">Newsbeuter</a>, <a href="http://joedicastro.com/productividad-linux-zathura.html">Zathura</a> o <a href="http://joedicastro.com/productividad-linux-turses.html">Turses</a>, pero <strong>Ranger</strong>... ya es
demasiado. ¿A quien en su sano juicio se le ocurriría utilizar semejante
engendro? A mí, y no solo lo utilizo a diario, si no que además opino que es
quizás la mejor aplicación que he descubierto en el último año y medio. Y esto
último es mucho decir para alguien, que como yo, está continuamente buscando
formas de mejorar su modo de trabajo.</p>
<p><a href="http://ranger.nongnu.org/">Ranger</a> es un administrador de archivos en modo texto. Pero no es un
administrador de archivos en modo texto clásico a dos columnas, como el norton
commander y similares. No, Ranger es una pequeña joya con un planteamiento
distinto y tan potente, que puede conseguir que olvides que no podías vivir sin
un gestor de archivos gráfico como <a href="https://es.wikipedia.org/wiki/Nautilus_(software)">Nautilus</a>, <a href="https://es.wikipedia.org/wiki/Konqueror">Konqueror</a>,
<a href="https://es.wikipedia.org/wiki/Dolphin_(administrador_de_archivos)">Dolphin</a> o <a href="https://es.wikipedia.org/wiki/Thunar">Thunar</a>.</p>
<h2 id="ranger">Ranger</h2>
<p>Tengo que estar enormemente agradecido a su autor, <a href="https://github.com/hut">Roman Zimbelmann</a> no
solo por su trabajo, si no por mostrarme que incluso en modo texto, se puede
revolucionar el mundo de la usabilidad. Evidentemente, en una aplicación que
basa su control en el teclado (tiene un soporte muy básico del ratón), es
requisito imprescindible invertir algún tiempo en aprender a utilizarlo, ya que
no es tan intuitivo como una aplicación gráfica. A menos que seas un usuario
típico de <a href="https://es.wikipedia.org/wiki/Vim">Vim</a>, entonces te encontrarás como en casa. Una vez que dominas
esta aplicación, el moverse entre los directorios y el realizar operaciones se
hace a velocidad de vértigo y con control absoluto (siempre que no seas un
manazas con el teclado).</p>
<p><strong>Ranger</strong> está desarrollado en <strong>Python</strong> y emplea una interfaz ncurses igual que
<a href="http://joedicastro.com/productividad-linux-turses.html">Turses</a>. Al contrario de lo que algunos puedan pensar, el estar
desarrollado en Python no lo convierte ni en pesado ni en lento, si no que se
mueve a velocidad endiablada y consume una cantidad de memoria ridícula
comparada con cualquier gestor de archivos gráfico.</p>
<p style="text-align:center;"><img src="pictures/ranger.png" width="700"
height="437" alt="Ranger" /></p>
<p>Aquí puede verse una imagen típica de ranger, visualizando el contenido de un
directorio y previsualizando el contenido de un fichero, en este caso un fichero
de código python.</p>
<h3 id="caracter+sticas">Características</h3>
<p>¿Qué se puede hacer con ranger?, bueno, quizás es mejor preguntarse que no se
puede hacer con Ranger. Prácticamente todas las funciones a las que estés
acostumbrado en un administrador de archivos gráfico se pueden realizar con
Ranger. Vamos primero a ver un resumen de sus características:</p>
<ul>
<li>Soporte de UTF-8 por defecto</li>
<li>Visualización en múltiples columnas</li>
<li>Previsualización del directorio o fichero seleccionado</li>
<li>Operaciones comunes sobre los ficheros. Crear, copiar, borrar, cambiar
atributos (<code>chmod</code>), etc...</li>
<li>Combinaciones de teclado y consola inspiradas en Vim</li>
<li>Autodetección de tipos de fichero y apertura de los mismos con el programa
adecuado</li>
<li>Permite establecer etiquetas y marcadores sobre archivos y directorios</li>
<li>Empleo de pestañas. Nos permite navegar por varios directorios simultáneamente
sin necesidad de abrir otra instancia del programa. Además se pueden realizar
operaciones entre ellas</li>
<li>Muestra una barra de progreso para las operaciones que lo necesitan</li>
</ul>
<p>Conviene dejar claro que no es un programa para <em>impacientes</em>, hacerse con él y
sus innumerables opciones requiere tiempo. Hay que leerse la ayuda del programa
y practicar con él. A su vez, si queremos personalizar las opciones o ampliarlo,
debemos comprender como funcionan sus archivos de configuración, mejor aún si
tenemos conocimientos de Python. La documentación de su web, así como la de sus
páginas <em>man</em> está un poco desactualizada, por lo que sacarle todo su jugo
requiere algo de paciencia.</p>
<h3 id="ejemplos">Ejemplos</h3>
<p>Voy a intentar dar algo de visibilidad a algunas de las capacidades que nos
ofrece ranger empleando una par de ejemplos que lo ilustren.</p>
<p>En el siguiente ejemplo vemos la previsualización que hace ranger en función del
tipo de fichero que se trate.</p>
<div style="text-align:center">
<iframe src="http://player.vimeo.com/video/62061254?title=0&byline=0&portrait=0"
width="700" height="438">
</iframe>
<p>Previsualización de archivos en ranger con varios tipos de archivo.
Recomiendo ver en alta resolución y a pantalla completa.</p>
</div>
<p>La previsualización de imágenes en ASCII puede parecer una tontería, pero
resulta muy útil a la hora de diferenciar entre distintas imágenes con nombre
muy parecido. El fichero en HTML está renderizado en vez de mostrarnos el texto
plano, algo más amigable. También se agradece mucho el que los ficheros de
código se vean con resaltado de sintaxis.</p>
<blockquote>
<p>Conviene aclarar que para realizar esta previsualización es necesario tener
instalado una serie de pequeñas aplicaciones que se detallan en el sitio de
ranger como dependencias.</p>
</blockquote>
<p>Aquí vemos como se abren automáticamente los programas adecuados en función del
tipo de archivo (se puede configurar que aplicaciones emplear y cuales usar en
cada momento)</p>
<div style="text-align:center">
<iframe src="http://player.vimeo.com/video/62634892?title=0&byline=0&portrait=0"
width="700" height="438">
</iframe>
<p>Ejecución automática de aplicaciones en función del tipo de archivo.
Recomiendo ver en alta resolución y a pantalla completa.</p>
</div>
<p>En el caso del fichero Torrent, al que no tengo asociada ninguna aplicación en
ranger, me abre un comando en el que puedo especificar directamente la
aplicación que deseo emplear para gestionarlo. En este caso, ninguna. En el
archivo comprimido solo me muestra el contenido del mismo, podría asociarle
alguna aplicación, pero ya tengo comandos para manejarlos dentro del propio
ranger, algo que veremos a continuación.</p>
<h2 id="mi_configuraci+n">Mi configuración</h2>
<p>No he personalizado mucho mi configuración de momento, pero quien quiera echarle
un vistazo puede hacerlo en mi repositorio dotfiles en <a href="http://github.com/joedicastro/dotfiles">GitHub</a></p>
<h3 id="archivos_de_configuraci+n">Archivos de configuración</h3>
<p>Para configurar ranger necesitamos editar ciertos archivos, que crearemos por
primera vez con el siguiente comando:</p>
<div class="codehilite"><pre><span class="nv">$ </span>ranger --copy-config<span class="o">=</span>all
</pre></div>
<p>qué nos creara los siguientes archivos por defecto en la carpeta
<code>~/.config/ranger/</code>:</p>
<ul>
<li><code>commands.py</code> <em>(python)</em>, en este archivo se configuran los comandos a emplear
en la línea de comandos de ranger</li>
<li><code>options.py</code> <em>(python)</em>, el fichero de opciones principal de ranger</li>
<li><code>rc.conf</code> <em>(texto)</em>, aquí configuramos los atajos de teclado de ranger</li>
<li><code>rifle.conf</code> <em>(texto)</em>, para establecer los programas que ejecutaran o abrirán
un tipo de archivo en orden de preferencia</li>
<li><code>scope.sh</code> <em>(bash)</em>, los programas empleados para previsualizar un determinado
tipo de archivo</li>
</ul>
<p>Adicionalmente, con el uso se añadirán tres ficheros más: <code>bookmarks</code>, <code>history</code>
y <code>tagged</code> que guardarán los marcadores, la historia de comandos y las etiquetas
respectivamente.</p>
<h3 id="gestionar_la_papelera">Gestionar la papelera</h3>
<p>Por defecto ranger no contempla la gestión de la papelera de Linux que
implementan escritorios como Gnome. Aunque no empleo ningún entorno
de escritorio ni gestores de archivos gráficos, si me interesa gestionar la
papelera, pues cada vez más aplicaciones la emplean para eliminar los archivos.
De hecho en <em>bash</em> y <em>zsh</em> tengo un alias para <code>rm</code> que mueve los archivos a la
papelera en vez de eliminarlos.</p>
<div class="codehilite"><pre><span class="nb">alias </span><span class="nv">rm</span><span class="o">=</span><span class="s1">'mv --target-directory ~/.local/share/Trash/files'</span>
</pre></div>
<p>Para gestionar la papelera desde ranger, añado dos atajos de teclado y un
comando editando varios archivos de configuración de la siguiente manera:</p>
<p><strong>Mandar archivos a la papelera</strong></p>
<p>Para mandar a la papelera lo que tengamos seleccionado actualmente, en lugar de
eliminarlo directamente, añadimos esta línea al fichero <code>rc.conf</code></p>
<div class="codehilite"><pre><span class="c"># move to trash</span>
<span class="nb">map</span> <span class="n">DD</span> <span class="n">shell</span> <span class="n">mv</span> <span class="o">-</span><span class="n">t</span> <span class="o">~/.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">Trash</span><span class="o">/</span><span class="n">files</span> <span class="o">%</span><span class="n">s</span>
</pre></div>
<p>de este modo, cada vez que pulsemos <strong><code>DD</code></strong> el contenido seleccionado es movido
al directorio donde tenemos nuestra papelera.</p>
<p><strong>Movernos a la papelera</strong></p>
<p>Del mismo modo que ranger provee por defecto de atajos de teclado para movernos
al directorio <code>home</code>, al directorio raiz, etc, he añadido un atajo de teclado
siguiendo el mismo criterio para acceder a nuestra papelera. Añadimos lo
siguiente al fichero <code>rc.conf</code></p>
<div class="codehilite"><pre><span class="c"># go to trash</span>
<span class="nb">map</span> <span class="n">gp</span> <span class="n">cd</span> <span class="o">~/.</span><span class="n">local</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">Trash</span><span class="o">/</span><span class="n">files</span>
</pre></div>
<p>así, pulsando <strong><code>gp</code></strong> vamos directamente a la papelera, estemos donde estemos.</p>
<p><strong>Vaciar la papelera</strong></p>
<p>Para vaciar la papelera opto por emplear un comando, para ello tenemos que
editar otro fichero, <code>commands.py</code>, y añadir lo siguiente</p>
<div class="codehilite"><pre><span class="k">class</span> <span class="nc">empty</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> :empty</span>
<span class="sd"> Empties the trash directory ~/.local/share/Trash/files</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="s">"rm -rf ~/.local/share/Trash/files/{*,.[^.]*}"</span><span class="p">)</span>
</pre></div>
<p>por lo que vaciar la papelera se convierte en algo tan simple como teclear
<strong><code>:empty</code></strong> o para los más cómodos <strong><code>:em↹</code></strong></p>
<h3 id="desmontar_una_unidad_con_udiskie">Desmontar una unidad con udiskie</h3>
<p>Dada mi "particular" configuración, ya que no empleo ningún escritorio, empleo
la herramienta <a href="https://bitbucket.org/byronclark/udiskie">udiskie</a> para automontar unidades de almacenamiento
externas, como unidades USB o tarjetas de memoria. El montado se hace de forma
automática y solo el desmontado ha de hacerse de forma manual. Ya que no quiero
teclear el comando cada vez que desee desmontar una unidad, lo que hago es
hacerlo desde ranger. En el fichero <code>rc.conf</code> mapeamos este atajo</p>
<div class="codehilite"><pre><span class="c"># umount a drive with udiskie</span>
<span class="nb">map</span> <span class="n">un</span> <span class="n">shell</span> <span class="o">-</span><span class="n">d</span> <span class="n">udiskie</span><span class="o">-</span><span class="n">umount</span> <span class="o">%</span><span class="n">d</span><span class="o">/%</span><span class="n">f</span>
</pre></div>
<p>así para desmontar una unidad inicio ranger (en mi caso <strong><code>Win + F1</code></strong>), me
dirijo a las unidades externas montadas, <strong><code>gm</code></strong>, selecciono la unidad deseada
y la desmonto, <strong><code>un</code></strong>. Un proceso que me lleva apenas dos segundos, sin
abandonar las manos del teclado, compárese con hacerlo con un ratón y un entorno
gráfico.</p>
<h3 id="expulsar_un_cddvd">Expulsar un CD/DVD</h3>
<p>Del mismo modo, en lugar de abrir un terminal y escribir el comando <code>eject</code>, en
determinadas circunstancias, por ejemplo al acabar de examinar el contenido de
un disco, prefiero hacerlo directamente desde ranger. Para ello en el fichero
<code>rc.conf</code> creamos este atajo de teclado</p>
<div class="codehilite"><pre><span class="c"># eject a CD-ROM/DVD</span>
<span class="nb">map</span> <span class="n">ej</span> <span class="n">shell</span> <span class="o">-</span><span class="n">d</span> <span class="n">eject</span>
</pre></div>
<h3 id="trabajar_con_archivos_comprimidos">Trabajar con archivos comprimidos</h3>
<p>Estos comandos los he sacado de el <a href="https://wiki.archlinux.org/index.php/Ranger">Wiki de Arch Linux</a> y los he adaptado
a la versión de ranger que estoy empleando en este momento, la 1.5.5</p>
<p><strong>Extraer los ficheros de un archivo comprimido</strong></p>
<p>Con el comando <strong><code>:extracthere</code></strong> extraemos el contenido de un archivo/s
comprimido que previamente habremos seleccionado para copia (es decir empleando
el atajo <strong><code>yy</code></strong>). El contenido se extrae en el directorio en el que nos
encontremos actualmente. Es muy comodo para extraer múltiples archivos
comprimidos a la vez, si quisieramos descomprimir solo un archivo y en el mismo
directorio en el que este se encuentra, podríamos emplear el atajo <strong><code>1l</code></strong></p>
<p>Para añadir el comando a ranger, tenemos que insertar esta clase en el fichero
<code>commands.py</code></p>
<div class="codehilite"><pre><span class="k">class</span> <span class="nc">extracthere</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">""" Extract copied files to current directory """</span>
<span class="n">copied_files</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">copy</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">copied_files</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">def</span> <span class="nf">refresh</span><span class="p">(</span><span class="n">_</span><span class="p">):</span>
<span class="n">cwd</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">get_directory</span><span class="p">(</span><span class="n">original_path</span><span class="p">)</span>
<span class="n">cwd</span><span class="o">.</span><span class="n">load_content</span><span class="p">()</span>
<span class="n">one_file</span> <span class="o">=</span> <span class="n">copied_files</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">cwd</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">thisdir</span>
<span class="n">original_path</span> <span class="o">=</span> <span class="n">cwd</span><span class="o">.</span><span class="n">path</span>
<span class="n">au_flags</span> <span class="o">=</span> <span class="p">[</span><span class="s">'-X'</span><span class="p">,</span> <span class="n">cwd</span><span class="o">.</span><span class="n">path</span><span class="p">]</span>
<span class="n">au_flags</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">1</span><span class="p">:]</span>
<span class="n">au_flags</span> <span class="o">+=</span> <span class="p">[</span><span class="s">'-e'</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">copy</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">cut</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">copied_files</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">descr</span> <span class="o">=</span> <span class="s">"extracting: "</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">one_file</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">descr</span> <span class="o">=</span> <span class="s">"extracting files from: "</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">one_file</span><span class="o">.</span><span class="n">dirname</span><span class="p">)</span>
<span class="n">obj</span> <span class="o">=</span> <span class="n">CommandLoader</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'aunpack'</span><span class="p">]</span> <span class="o">+</span> <span class="n">au_flags</span> \
<span class="o">+</span> <span class="p">[</span><span class="n">f</span><span class="o">.</span><span class="n">path</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">copied_files</span><span class="p">],</span> <span class="n">descr</span><span class="o">=</span><span class="n">descr</span><span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">signal_bind</span><span class="p">(</span><span class="s">'after'</span><span class="p">,</span> <span class="n">refresh</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
</pre></div>
<p><strong>Crear un archivo comprimido</strong></p>
<p>Este comando te permite seleccionar uno o varios ficheros y crear un archivo
comprimido al llamar al comando <strong><code>:compress</code></strong>. Este comando te permite (a
través del autocompletado) darle un nombre automático a partir del directorio o
uno personalizado. Te permite además seleccionar el tipo de compresión en
función de la extension empleada.</p>
<p>En el fichero <code>commands.py</code> añadimos el siguiente código.</p>
<div class="codehilite"><pre><span class="k">class</span> <span class="nc">compress</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">""" Compress marked files to current directory """</span>
<span class="n">cwd</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">thisdir</span>
<span class="n">marked_files</span> <span class="o">=</span> <span class="n">cwd</span><span class="o">.</span><span class="n">get_selection</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">marked_files</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">def</span> <span class="nf">refresh</span><span class="p">(</span><span class="n">_</span><span class="p">):</span>
<span class="n">cwd</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">get_directory</span><span class="p">(</span><span class="n">original_path</span><span class="p">)</span>
<span class="n">cwd</span><span class="o">.</span><span class="n">load_content</span><span class="p">()</span>
<span class="n">original_path</span> <span class="o">=</span> <span class="n">cwd</span><span class="o">.</span><span class="n">path</span>
<span class="n">parts</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
<span class="n">au_flags</span> <span class="o">=</span> <span class="n">parts</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
<span class="n">descr</span> <span class="o">=</span> <span class="s">"compressing files in: "</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">parts</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">obj</span> <span class="o">=</span> <span class="n">CommandLoader</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'apack'</span><span class="p">]</span> <span class="o">+</span> <span class="n">au_flags</span> <span class="o">+</span> \
<span class="p">[</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">relpath</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">cwd</span><span class="o">.</span><span class="n">path</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">marked_files</span><span class="p">],</span> <span class="n">descr</span><span class="o">=</span><span class="n">descr</span><span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">signal_bind</span><span class="p">(</span><span class="s">'after'</span><span class="p">,</span> <span class="n">refresh</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">tab</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">""" Complete with current folder name """</span>
<span class="n">extension</span> <span class="o">=</span> <span class="p">[</span><span class="s">'.zip'</span><span class="p">,</span> <span class="s">'.tar.gz'</span><span class="p">,</span> <span class="s">'.rar'</span><span class="p">,</span> <span class="s">'.7z'</span><span class="p">]</span>
<span class="k">return</span> <span class="p">[</span><span class="s">'compress '</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fm</span><span class="o">.</span><span class="n">thisdir</span><span class="o">.</span><span class="n">path</span><span class="p">)</span> <span class="o">+</span> <span class="n">ext</span> <span class="k">for</span> <span class="n">ext</span> <span class="ow">in</span> <span class="n">extension</span><span class="p">]</span>
</pre></div>Productividad & Linux: Turses2012-06-21T22:50:00+02:00joe di castrohttp://joedicastro.com/productividad-linux-turses.html<p>Que medio mundo parece estar conectado a <a href="http://twitter.com">Twitter</a> no es
ninguna novedad. Y que si sigues a un buen número de personas, el intentar estar
al tanto de todo lo que ocurre es una temeridad, tampoco debería sorprender a
nadie. De hecho, dado su éxito y el enorme flujo de información que circula por
él, se han desarrollado cientos de herramientas para gestionarlo.</p>
<p>Desde que cree mi cuenta en twitter, he probado unas cuantas, unas veinte (y
solo en Linux, en el resto uso la web). La primera, y la elección más obvia ya
que por aquel entonces usaba Ubuntu, fue <strong>Gwibber</strong> . Luego cansado de sus
muchos problemas, probé un sinnúmero de aplicaciones, solo merece la pena
reseñar una: <a href="http://hotot.org/">Hotot</a>. Es la mejor aplicación gráfica para twitter en Linux
que conozco.</p>
<p>Pero guiado por el mismo objetivo de <a href="http://joedicastro.com/tag/productividad.html">mejorar la productividad</a> en mis
herramientas habituales de trabajo, me lancé a la búsqueda de un cliente de
twitter que encajara en la misma filosofía. No hay muchas alternativas, la
mayoría, hay que reconocerlo, son demasiado "crudas" incluso para mí, que soy un
amante de la consola. Pero entonces dí con una pequeña joya, <a href="http://tyrs.nicosphere.net/">Tyrs</a>,
desarrollada por <a href="https://github.com/Nic0">Nicolas Paris</a>. Era una herramienta sencilla, pero que
cumplía muy bien con todo lo que buscaba de ella. Pero un buen día, Nicolas, en
su afán por mejorarla, empezó a reescribir la herramienta empleando una nueva
librería para gestionar el interfaz. Las primeras versiones tenían varios fallos
y Nicolas pronto se vio desbordado por una tarea que no le apetecía continuar y
a la que no podía dedicar más tiempo. Y <a href="http://www.nicosphere.net/small-projects-life-depends-on-his-owner/">decidió abandonar el proyecto</a>,
con la esperanza de que alguien se atreviera a retomarlo. Cuando leí la entrada
de su blog no perdí la esperanza del todo, al fin y al cabo, estaba desarrollado
en Python, un lenguaje con el que me desenvuelvo. Mientras esperaba que hacer,
seguí usando la última versión estable a diario. Pero entonces, apareció el
milagro, <strong>Turses</strong></p>
<h2 id="turses">Turses</h2>
<p><a href="https://github.com/alejandrogomez/">Alejandro Gómez</a>, un usuario de <strong>Tyrs</strong> <a href="http://dialelo.com/Python/turses/2012/03/02/turses-un-cliente-de-twitter-con-interfaz-ncurses.html">se lanzó</a> a crear su
propia aplicación basándose en él. Y no solo garantizaba la continuidad del buen
trabajo empezado por Nicolas, si no que llegaba lleno de ideas frescas y muchas
ganas de hacerlo bien. El propio Nicolas <a href="http://www.nicosphere.net/turses-a-fork-from-tyrs-ncurses-twitter-client/">le felicitó</a> por el trabajo
y la iniciativa. A día de hoy, el proyecto se sigue desarrollando, y aunque aún
tiene algunas metas marcadas por delante, la aplicación es perfectamente usable
en el día a día, de hecho es mi cliente habitual.</p>
<p>Como ya se habrá podido deducir, <a href="https://github.com/alejandrogomez/Turses">Turses</a> es un cliente de twitter para la
consola con interfaz <a href="https://es.wikipedia.org/wiki/Ncurses">ncurses</a>. Está desarrollado en Python y emplea la
librería <a href="http://excess.org/urwid/">Urwid</a> para crear la interfaz en curses. Lo mejor de esta
aplicación es que emplea atajos de teclado inspirados en <strong>Vim</strong> y es
totalmente controlable desde el teclado. Esto unido a que emplea una interfaz
basada en texto, la convierten en la aplicación más ágil de todas las que haya
probado. <strong>Hotot</strong> también tiene algunas combinaciones de teclas muy útiles,
pero ni se acercan a lo que <strong>Turses</strong> te permite.</p>
<p>Aquí se puede ver el aspecto por defecto de Turses</p>
<p style="text-align:center;"><img src="pictures/turses.png" width="700"
height="290" alt="Turses" /></p>
<p>Pero no se acaban ahí las bondades de Turses, tiene algunas características
geniales como la gestión dinámica de bufferes (líneas temporales) y de columnas.
Demos un repaso a lo que nos permite la aplicación:</p>
<ul>
<li><strong>Múltiples líneas temporales</strong> (<em>bufferes</em>). Es decir, nos permite consultar
los tweets de la gente a la que seguimos, los nuestros, menciones, etc. Es
decir, los bufferes habituales, incluidos conversaciones, búsquedas y
hashtags. Y podemos tenerlas simultáneamente abiertas y navegar entre ellas
muy fácilmente.</li>
<li><strong>Múltiples columnas</strong>. En cada columna se sitúa un buffer, y podemos añadir
o quitar columnas a voluntad de forma muy sencilla. Es decir, que podemos
visualizar un solo buffer de forma predefinida, o podemos ver varios a la vez
distribuidos en múltiples columnas.</li>
<li><strong>Tweet, Reply, Retweet, Borrar</strong>. Vamos, que permite las operaciones
habituales con los tweets. Además se puede hacer un Retweet editando el texto,
algo que parece obvio, pero que en algunas aplicaciones no es tan sencillo.</li>
<li><strong>Seguir/dejar de seguir</strong> a un usuario. Podemos hacerlo bien a través de un
tweet o bien introduciendo el nombre del usuario.</li>
<li><strong>Des/Marcar como favorito</strong>.</li>
<li><strong>Enviar mensajes directos</strong>.</li>
<li><strong>Abrir URLs en un navegador</strong>. Nos permite abrir las direcciones que aparecen
en un tweet, así como abrir el propio tweet.</li>
<li><strong>Visualizar conversaciones</strong>. Podemos abrir un nuevo buffer con la
conversación relacionada con un tweet.</li>
<li><strong>Contador de los no leídos</strong> funciona para todos los bufferes y nos permite
ponerlo a cero manualmente cuando queremos ignorar algunos no leídos.</li>
<li><strong>Búsqueda</strong>. Se puede buscar tanto por usuario como por termino.</li>
<li><strong>Ver los tweets de cualquier usuario </strong>.</li>
<li><strong>Visualizar el perfil de un usuario</strong>.</li>
<li><strong>Totalmente personalizable</strong> y la configuración se guarda en un fichero de
texto plano.</li>
<li><strong>Múltiples cuentas</strong>, eso sí, una por ejecución.</li>
<li><strong>Ayuda en línea</strong> con todas las combinaciones de teclas posibles. Accesible a
través de la tecla <strong><code>?</code></strong></li>
</ul>
<p>Interfaz de Turses mostrando múltiples columnas</p>
<p style="text-align:center;"><img src="pictures/turses_2cols.png" width="700"
height="285" alt="Turses con multiples columnas" /></p>
<p>Y entre las metas que tiene marcadas su autor, nos encontramos con el soporte
para listas, streaming, notificaciones emergentes y múltiples sesiones. Estoy
seguro de que las acabará incorporando, le sobra capacidad. Aunque he de reseñar
que actualmente he contribuido con una porción de código minúscula al proyecto y
que tengo la intención de seguir colaborando en todo lo que pueda. Si eres
programador Python y te apetece echar una mano, <a href="https://github.com/alejandrogomez/Turses">anímate</a>, Alejandro es muy
receptivo y un tío muy majo que estará encantado con toda la ayuda que le
podamos dar.</p>
<h2 id="mi_configuraci+n">Mi configuración</h2>
<p>Si a alguien le puede servir como inspiración mi configuración, esta disponible
en <a href="http://github.com/joedicastro/dotfiles">GitHub</a></p>
<p>Turses mostrando la información del perfil del autor de un tweet</p>
<p style="text-align:center;"><img src="pictures/turses_uinfo.png" width="700"
height="429" alt="Turses mostrando la información de un usuario" /></p>
<h2 id="alternativas">Alternativas</h2>
<p>Solo conozco dos alternativas en la misma línea que merezca la pena reseñar, las
demás que he probado no estaban a la altura:</p>
<ul>
<li>
<p><a href="http://www.vim.org/scripts/script.php?script_id=2204">TwitVim</a>, es un plugin para Vim. Funciona fantásticamente bien, eso sí,
solo apropiado para usuarios de Vim. La probé un tiempo y me gusto, pero
personalmente no me gusta emplear Vim para esta tarea y Turses es bastante más
manejable.</p>
</li>
<li>
<p><a href="http://www.floodgap.com/software/ttytter/">TTYtter</a>, está escrito en Perl y no tiene interfaz. Trabaja en la línea
de comandos a modo de interprete. Funciona muy bien y también lo usé un
tiempo, pero su propio funcionamiento le reste eficiencia comparado con
Turses.</p>
</li>
</ul>Productividad & Linux: Newsbeuter2012-06-18T23:26:00+02:00joe di castrohttp://joedicastro.com/productividad-linux-newsbeuter.html<p>Uno de los pilares de la productividad es, como no, la gestión del tiempo. Otro
de los pilares fundamentales es, inexorablemente, el conocimiento. Si pierdes el
tiempo en tareas irrelevantes (o directamente procrastinando), tu productividad
se resiente irremediablemente. Si no tiene los conocimientos adecuados y
suficientes, consumes el tiempo aprendiendo a hacerlo o directamente lo pierdes
haciéndolo mal. Hoy en día, rara es la actividad donde la formación continua no
sea un requisito indispensable, no ya para mejorar o mantener tu rendimiento, si
no simplemente para poder seguir ejerciéndola.</p>
<p>Por lo tanto nos vemos condenados a intentar mantenernos al día (y ampliar
conocimientos), mientras que procuramos dedicarle el menor tiempo posible para
no menoscabar nuestro rendimiento. Tal delicado equilibrio no es poca hazaña en
nuestros días. Nos vemos inundados de tal cantidad de información, que el
filtrado es la única manera de intentar sobrevivir a esa enorme vorágine de
datos a la que nos enfrentamos. Afortunadamente tenemos herramientas. Desde hace
muchos años he confiado esta tarea a emplear fuentes <strong>RSS</strong> de calidad y una
buena herramienta para gestionarlas.</p>
<h2 id="mi_b+squeda_del_cliente_rss_ideal">Mi búsqueda del cliente RSS ideal</h2>
<p>He empleado muchas herramientas distintas para esta tarea, siempre intentando
tener la más idónea para filtrar muchas fuentes RSS en el menor tiempo posible,
sin pasar por alto lo que me interesa conocer. Algunas muy buenas, que usaba
cuando aún empleaba Windows como SO principal, ya no existen. En Linux he pasado
por las más conocidas (en orden cronológico):</p>
<ul>
<li>
<p><a href="http://liferea.sourceforge.net/">Liferea</a>, era muy buena cuando la deje, lo de mostrar los comentarios en
las entradas es algo que no he vuelto a ver en ninguna otra herramienta. Pero
después de varios años de uso, la abandoné cuando se había convertido en
insufriblemente lenta.</p>
</li>
<li>
<p><a href="http://userbase.kde.org/Akregator">Akregator</a>, no era para mí, nunca acabe encontrándome a gusto con ella.
Lo hacía todo medianamente bien, pero no destacaba en nada, pronto la
abandoné.</p>
</li>
<li>
<p><a href="http://www.blogbridge.com/">Blogbride</a>, su planteamiento es diferente al resto. Es una buena
aplicación y estuve con ella muchos meses. Pero siempre seguí buscando algo
más eficiente.</p>
</li>
<li>
<p><a href="http://www.rssowl.org/">RSSOwl</a>, la mejor aplicación para leer RSS para escritorio que he
conocido. La he usado durante años. Está construida sobre Eclipse. Ofrece
muchas posibilidades de personalización y filtrado. Es muy rápida, pero debido
a que depende de Eclipse y java, si manejas un número considerable de fuentes
(+1000 en aquella época), se puede volver un poco pesada. Además, si como yo,
dejabas muchos artículos para leer en otro momento, la base de datos crecía de
tal manera, que podía llegar a ser muy lenta. La abandoné buscando algo aún
más ágil y productivo.</p>
</li>
<li>
<p><a href="https://es.wikipedia.org/wiki/Google_Reader">Google Reader</a>, decidí darle una oportunidad. Por aquella época
había empezado a usar Read it Later (hoy <a href="http://getpocket.com">Pocket</a>) para guardar aquello
que quería leer en otro momento o con más calma. La integración con RIL me
obligaba a abandonar el teclado y usar el ratón. Además el rediseño que hizo
Google no me convencía. Decidí buscar algo aún más rápido y eficiente.</p>
</li>
</ul>
<p>Durante todo ese tiempo probé muchísimas otras alternativas (incluidos
complementos para navegadores web) y no encontraba nada que me valiese. Es
muy difícil encontrar una aplicación de este tipo que te permita manejar un gran
número de fuentes RSS de forma realmente eficiente. Al final ya estaba decidido
a regresar a RSSOwl. Pero acostumbrado a <a href="http://joedicastro.com/productividad-linux-pentadactyl.html">Pentadactyl</a> y Vim y las aplicaciones
<a href="https://es.wikipedia.org/wiki/Ncurses">ncurses</a>, decidí buscar algo en esa línea, y lo encontré.</p>
<h2 id="newsbeuter">Newsbeuter</h2>
<p><a href="http://www.newsbeuter.org/">Newsbeuter</a> es un juego de palabras con la palabra alemana "Wildbeuter"
que significa <a href="https://es.wikipedia.org/wiki/Cazador_recolector">cazador-recolector</a>, por lo que Newsbeuter vendría a ser
algo así como <em>Cazador/Recolector de Noticias</em>. Newsbeuter es una aplicación
para leer fuentes RSS y Atom que utiliza una interfaz tipo ncurses para consola.
Quién esté familiarizado con el cliente de correo <a href="https://es.wikipedia.org/wiki/Mutt">Mutt</a> se sentirá cómodo
enseguida, ya que se inspira en este. Está programado en C++ y dado que funciona
en modo texto, una de sus ventajas es la enorme agilidad que proporciona para
moverse entre fuentes y noticias.</p>
<p>Un resumen de sus características:</p>
<ul>
<li>Permite suscribirnos a fuentes RSS y Atom</li>
<li>Soporta <a href="https://es.wikipedia.org/wiki/OPML">OPML</a> tanto para importar como para exportar las subscripciones</li>
<li>Descarga de podcasts</li>
<li>Se pueden configurar todos los atajos de teclado libremente</li>
<li>Podemos realizar búsquedas entre todos los artículos descargados. Similar a
Vim</li>
<li>Es posible crear etiquetas para dividir nuestras subscripciones en categorías y
realizar filtrados y búsquedas en función a ellas</li>
<li>Se pueden sincronizar las fuentes con Google Reader y <a href="http://tt-rss.org/redmine/">Tiny Tiny RSS</a></li>
<li>Podemos configurar el color y las cadenas de texto para personalizar su
aspecto</li>
<li>Se pueden eliminar de forma automática artículos que no deseemos a través
de un <a href="https://en.wikipedia.org/wiki/Kill_file">"killfile"</a></li>
<li>Es posible integrar cualquier fuente de datos a través de un flexible sistema
de filtros y plugins: Fichero con urls, fichero OPML, fichero OPML online, Google
Reader, varios ficheros...</li>
<li>Se pueden crear "meta fuentes" empleando un potente lenguaje de consultas</li>
<li>Permite crear marcadores a partir de cualquier enlace del articulo empleando
una aplicación externa o un script</li>
<li>Permite guardar artículos en texto plano</li>
<li>Podemos otorgar etiquetas de un solo carácter que el autor denomina "flags"
por articulo y varias por articulo. Útiles para emplearlas conjuntamente con
los filtros</li>
<li>Se pueden definir macros</li>
<li>Linea de comandos para poder ejecutar comandos y cambiar opciones sobre la
marcha</li>
<li>Funciona en Linux, Mac OS y FreeBSD</li>
<li>Programado en C++ y guarda los artículos en una BDD <a href="http://sqlite.org/">SQLite</a></li>
<li><a href="http://newsbeuter.org/doc/newsbeuter.html">Documentación</a> bastante completa</li>
</ul>
<p>Pantalla con la configuración por defecto de Newsbeuter</p>
<p style="text-align:center;"><img src="pictures/newsbeuter_default.png" width="700"
height="410" alt="Newsbeuter" /></p>
<h2 id="lo_que_lo_distingue">Lo que lo distingue</h2>
<p>Muchas de estas características las comparte con otras aplicaciones de las
mencionadas antes, pero lo que realmente distingue a Newsbeuter de todas ellas
es lo siguiente:</p>
<ul>
<li>Totalmente controlable desde el teclado, lo que unido a su velocidad, le
proporciona una agilidad inigualable.</li>
<li>Basado en texto, lo que nos evita distraernos de lo importante, el contenido.</li>
<li>Consumo ridículo de memoria y recursos comparado con cualquiera aplicación
mencionada antes. En el peor de los casos, me ha consumido unos 30 MiB de RAM
(para la versión de 64 bits)</li>
<li>Se puede configurar el números de hilos de proceso para descargar noticias. En
mi caso, con 8 hilos, tarda unos 40s en leer unos 250 canales RSS. Solo Google
Reader por su funcionamiento, puede mejorar esto.</li>
<li>Configurable con un fichero de texto plano</li>
</ul>
<p>Como dijo <a href="http://www.zedshaw.com/essays/i_want_the_mutt_of_feed_readers.html">Zed Shaw</a> y refrenda el autor de la aplicación, <a href="http://synflood.at/">Andreas
Krennmair</a>,</p>
<blockquote>
<p>"Newsbeuter es el Mutt de los lectores de noticias RSS"</p>
</blockquote>
<p>Desde mi propia experiencia puedo decir que este programa ha cambiado mi forma
de leer las <em>noticias del día</em>. Antes, para mi, era muy importante que el lector
de noticias que empleara, utilizara un estilo homogéneo entre todas las fuentes y
artículos, con el fin de centrarme en el contenido y no perder el tiempo con
nimiedades. Es algo más importante de lo que pueda parecer a simple vista, cuando
quieres emplear el menor tiempo posible en adquirir la información y al mismo
tiempo quieres asimilar lo que lees. Si cuando vas a leer las noticias tienes 45
minutos y 300 artículos sin leer, el cambiar de un articulo con fondo negro y
letra Sans Serif mediana a uno con fondo blanco y letra Serif enorme, te supone
una distracción y una adaptación de la vista innecesarias e incomodas.</p>
<p>Vista de un articulo con mi configuración</p>
<p style="text-align:center;"><img src="pictures/newsbeuter_articulo.png"
width="700" height="843" alt="Articulo en Newsbeuter" /></p>
<p>Pero al comenzar a emplear Newsbeuter me di enseguida cuenta de algo, el carecer
por completo de imágenes y compartir cabecera entre todos los artículos, me ha
servido para pasar por alto o leer en diagonal aquello que menos me interesa. No
solo filtro más rápido, si no que lo hago más eficientemente. Evidentemente hay
artículos en los que las imágenes complementan necesariamente al articulo. En
estos casos o bien la abro directamente en el navegador (tan sencillo como
pulsar <strong><code>o</code></strong> ) o bien puedo abrir las imágenes de forma independiente en el
mismo si me interesa por ejemplo ver solo una. En este sentido, Newsbeuter
proporciona una lista de todas las Urls presentes en el articulo en forma de
lista al final del mismo, pudiendo abrir cualquiera de ellas introduciendo el
indice de la misma. De hecho, empleando el comando <strong><code>u</code></strong> podemos acceder a la
lista completa de las mimas en una nueva ventana.</p>
<h2 id="mi_configuraci+n">Mi configuración</h2>
<p>Mi configuración no tiene demasiado de especial, quizás que emplea una
combinación de colores distinta a la habitual y que empleo un par de scripts
para las notificaciones y para crear marcadores. Esta se puede encontrar en mi
repositorio de mis <em>dotfiles</em> en <a href="http://github.com/joedicastro/dotfiles">GitHub</a></p>
<h2 id="notificaciones">Notificaciones</h2>
<p>Para las notificaciones que emite Newsbeuter después de refrescar las noticias
empleo el script <code>notify.py</code> que comentaba en este <a href="http://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.html">articulo</a>
ligeramente modificado para trabajar con Newsbeuter. En la imagen se puede ver
una notificación del programa.</p>
<p style="text-align:center;"><img src="pictures/newsbeuter_notify.png"
width="487" height="68" alt="Notificación de Newsbeuter" /></p>
<div class="codehilite"><pre><span class="k">try</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">gtk</span>
<span class="kn">import</span> <span class="nn">pynotify</span>
<span class="kn">import</span> <span class="nn">textwrap</span>
<span class="n">NOT_NOTIFY</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="n">NOT_NOTIFY</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">icon</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">wrap</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="sd">"""Send notification icon messages through libnotify.</span>
<span class="sd"> Parameters:</span>
<span class="sd"> (str) title -- The notification title</span>
<span class="sd"> (str) msg -- The message to display into notification</span>
<span class="sd"> (str / uri) icon -- Type of icon (ok|info|error|warm|ask|sync) or icon file</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="n">NOT_NOTIFY</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">is_initted</span><span class="p">():</span>
<span class="n">pynotify</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">title</span><span class="p">)</span>
<span class="n">gtk_icon</span> <span class="o">=</span> <span class="p">{</span><span class="s">'ok'</span><span class="p">:</span> <span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_YES</span><span class="p">,</span>
<span class="s">'info'</span><span class="p">:</span> <span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_INFO</span><span class="p">,</span>
<span class="s">'error'</span><span class="p">:</span> <span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_ERROR</span><span class="p">,</span>
<span class="s">'warm'</span><span class="p">:</span> <span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_WARNING</span><span class="p">,</span>
<span class="s">'ask'</span><span class="p">:</span> <span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_QUESTION</span><span class="p">,</span>
<span class="s">'sync'</span><span class="p">:</span> <span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_JUMP_TO</span><span class="p">}</span>
<span class="k">if</span> <span class="n">wrap</span><span class="p">:</span>
<span class="n">msg</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">linesep</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">textwrap</span><span class="o">.</span><span class="n">wrap</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">wrap</span><span class="p">))</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">note</span> <span class="o">=</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">Notification</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">msg</span><span class="p">)</span>
<span class="n">helper</span> <span class="o">=</span> <span class="n">gtk</span><span class="o">.</span><span class="n">Button</span><span class="p">()</span>
<span class="n">gtk_icon</span> <span class="o">=</span> <span class="n">helper</span><span class="o">.</span><span class="n">render_icon</span><span class="p">(</span><span class="n">gtk_icon</span><span class="p">[</span><span class="n">icon</span><span class="p">],</span> <span class="n">gtk</span><span class="o">.</span><span class="n">ICON_SIZE_BUTTON</span><span class="p">)</span>
<span class="n">note</span><span class="o">.</span><span class="n">set_icon_from_pixbuf</span><span class="p">(</span><span class="n">gtk_icon</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">note</span> <span class="o">=</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">Notification</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">icon</span><span class="p">)</span>
<span class="n">note</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="sd">"""Main section"""</span>
<span class="n">notify</span><span class="p">(</span><span class="s">'Newsbeuter'</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s">'/home/joedicastro/.newsbeuter/icon.png'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
<h2 id="integraci+n_con_pocket">Integración con Pocket</h2>
<p>Utilizo <a href="http://getpocket.com">Pocket</a> como nexo de unión entre el navegador y el Newsbeuter para
archivar todos aquello artículos que me interesa leer, pero que quiero dejar
para otro momento más idóneo. Hubo un tiempo en que empleaba <a href="http://delicious.com/">Delicious</a>
para esta tarea, pero me parece más adecuado Pocket.</p>
<p>Esto lo consigo empleando el comando para crear marcadores de Newsbeuter y un
script en Python creado para ello. Esta es la parte del archivo de configuración
que relaciona el comando con el script:</p>
<div class="codehilite"><pre><span class="n">bookmark</span><span class="o">-</span><span class="n">cmd</span> "<span class="o">~/</span><span class="p">.</span><span class="n">newsbeuter</span><span class="o">/</span><span class="n">send2ril</span><span class="p">.</span><span class="n">py</span>"
</pre></div>
<p>El script hace uso de la <a href="https://bitbucket.org/Surgo/ril/src">API Python</a> para Read it Later (como se llamaba
anteriormente Pocket) para guardar la url del articulo en mi cuenta de Pocket.
Así pulsando <strong><code>Ctrl + b</code></strong> se guarda el marcador en Pocket.</p>
<div class="codehilite"><pre><span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf8 -*-</span>
<span class="sd">"""</span>
<span class="sd"> send2ril.py: Send a new url to Read it Later</span>
<span class="sd">"""</span>
<span class="n">__author__</span> <span class="o">=</span> <span class="s">"joe di castro <joe@joedicastro.com>"</span>
<span class="n">__license__</span> <span class="o">=</span> <span class="s">"GNU General Public License version 3"</span>
<span class="n">__date__</span> <span class="o">=</span> <span class="s">"18/06/2012"</span>
<span class="n">__version__</span> <span class="o">=</span> <span class="s">"0.1"</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">readitlater</span>
<span class="kn">import</span> <span class="nn">ril_config</span> <span class="kn">as</span> <span class="nn">config</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="sd">"""Main section"""</span>
<span class="n">api</span> <span class="o">=</span> <span class="n">readitlater</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">RIL_APIKEY</span><span class="p">,</span> <span class="n">config</span><span class="o">.</span><span class="n">RIL_USERNAME</span><span class="p">,</span>
<span class="n">config</span><span class="o">.</span><span class="n">RIL_PASSWORD</span><span class="p">)</span>
<span class="n">new</span> <span class="o">=</span> <span class="p">[{</span><span class="s">"url"</span><span class="p">:</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s">"title"</span><span class="p">:</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]}]</span>
<span class="n">api</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">new</span><span class="o">=</span><span class="n">new</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
<p>En el script se importa un modulo, <code>ril_config</code>, que es el que contiene las
claves de mi cuenta de Pocket, este modulo sería algo similar a esto
(evidentemente los valores son falsos):</p>
<div class="codehilite"><pre><span class="c"># ril credentials</span>
<span class="n">RIL_APIKEY</span> <span class="o">=</span> <span class="s">'987u1ksjsdfRk54kKLKL34jkjij9945k'</span>
<span class="n">RIL_USERNAME</span> <span class="o">=</span> <span class="s">'usuario'</span>
<span class="n">RIL_PASSWORD</span> <span class="o">=</span> <span class="s">'ADRKSD-Xk3kj5kjljFl'</span>
</pre></div>
<p>Por lo tanto para hacerlo funcionar necesitamos crear un fichero <code>ril_config.py</code>
con las credenciales de cada uno para Pocket. Los campos <code>RIL_USERNAME</code> y
<code>RIL_PASSWORD</code> se corresponden evidentemente con el usuario y la contraseña que
tengamos para el servicio. El otro campo, <code>RIL_APIKEY</code> es una clave que podemos
obtener en <a href="http://getpocket.com/api/signup/">esta página</a> para registrar nuestra aplicación (en este caso
nuestro script) y que pueda acceder de forma autorizada a la API de Pocket.</p>
<h3 id="copia_de_seguridad_de_las_urls_de_pocket">Copia de seguridad de las urls de Pocket</h3>
<p>Del mismo modo, aprovechando la misma API que empleo en el anterior script, he
creado otro script que ejecuto regularmente con cron, que me guarda una copia en
mi disco duro con todas las urls que tengo guardadas en Pocket. Vamos, una copia
de seguridad, uno nunca sabe cuando este tipo de servicios pueden dejar de
funcionar. Estas direcciones las guardo en un fichero con formato <a href="https://es.wikipedia.org/wiki/Org-mode">Org-mode</a></p>
<div class="codehilite"><pre><span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf8 -*-</span>
<span class="sd">"""</span>
<span class="sd"> get.py: Get the urls stored in Read it Later & save them in a Org-mode file</span>
<span class="sd">"""</span>
<span class="n">__author__</span> <span class="o">=</span> <span class="s">"joe di castro <joe@joedicastro.com>"</span>
<span class="n">__license__</span> <span class="o">=</span> <span class="s">"GNU General Public License version 3"</span>
<span class="n">__date__</span> <span class="o">=</span> <span class="s">"18/06/2012"</span>
<span class="n">__version__</span> <span class="o">=</span> <span class="s">"0.1"</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">readitlater</span>
<span class="kn">import</span> <span class="nn">ril_config</span> <span class="kn">as</span> <span class="nn">config</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="c"># Checks the installation of the necessary python modules</span>
<span class="k">print</span><span class="p">((</span><span class="n">os</span><span class="o">.</span><span class="n">linesep</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s">"An error found importing one module:"</span><span class="p">,</span>
<span class="nb">str</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()[</span><span class="mi">1</span><span class="p">]),</span> <span class="s">"You need to install it"</span><span class="p">,</span> <span class="s">"Stopping..."</span><span class="p">]))</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="sd">"""Main section"""</span>
<span class="n">api</span> <span class="o">=</span> <span class="n">readitlater</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">RIL_APIKEY</span><span class="p">,</span> <span class="n">config</span><span class="o">.</span><span class="n">RIL_USERNAME</span><span class="p">,</span>
<span class="n">config</span><span class="o">.</span><span class="n">RIL_PASSWORD</span><span class="p">)</span>
<span class="n">items</span> <span class="o">=</span> <span class="n">api</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">state</span><span class="o">=</span><span class="s">"unread"</span><span class="p">)</span>
<span class="n">lista</span> <span class="o">=</span> <span class="n">items</span><span class="p">[</span><span class="s">"list"</span><span class="p">]</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"ril_urls.org"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">)</span> <span class="k">as</span> <span class="n">output</span><span class="p">:</span>
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">"* Read It Later URLs"</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">linesep</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">lista</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">"** {0}{1}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">k</span><span class="p">[</span><span class="s">'title'</span><span class="p">]</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s">"utf8"</span><span class="p">),</span>
<span class="n">os</span><span class="o">.</span><span class="n">linesep</span><span class="p">))</span>
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">" [[{0}][Enlace]]{1}{1}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">k</span><span class="p">[</span><span class="s">'url'</span><span class="p">]</span><span class="o">.</span>
<span class="n">encode</span><span class="p">(</span><span class="s">"utf8"</span><span class="p">),</span> <span class="n">os</span><span class="o">.</span><span class="n">linesep</span><span class="p">))</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
<h2 id="conclusi+n">Conclusión</h2>
<p>Newsbeuter no es para todo el mundo, por supuesto, la gran mayoría considerarían
decimonónico el emplear un interfaz de texto en vez de uno gráfico. Muchos
incluso llamarían herejía a usar el teclado en vez de el ratón (aunque luego se
vuelvan locos con las pantallas táctiles). Lo respeto y lo entiendo, pero para
aquellos que aman su tiempo y no están dispuestos a desperdiciarlo, deberían
darle una oportunidad a esta aplicación.</p>