ULR, url list reader ou mon viewer pour newsbeuter 2010-05-18

MAJ: j'ai déjà reçu mon premier patch (gloire) par taziden qui permet d'ajouter l'url courante dans un fichier "bookmarks" (touche B) donc le code qui suit n'est plus à jours.

Situation: j'utilise newsbeuter pour lire mes RSS. C'est véritablement le meilleur lecteur de RSS sur lequel j'ai pu mettre la main, en plus il est en console. Sauf que, pour une fois la console me fait chier. En effet je lis plein de blog bayday et autre webcomics idiot et tout ce joli petit tointoin est rempli d'images ce qui ne fonctionne pas très bien en console. Surtout que j'ai une queryfeed avec des centaines d'éléments.

Dans l'ordre, voici les solutions qui s'offraient à moi:

  • utiliser la fonction intégré à newsbeuter pour ouvrir l'url dans mon naviguateur oueb. Ça va un moment, mais c'est lent et vraiment pas efficace.
  • histoire d'automatiser un peu le tout je me suis fait des macro super bourrin qui ouvre 10 20 30 40 80 120 élément dans mon browser. ça marche pas mal, sauf que Ça fait souvent péter firefox, faut attendre que tout est chargé, c'est pénible.

Puis finalement j'en ai eu marre et j'ai décidé de coder ma propre solution et voilà l'arrivé de ULR (vous remarquerez le subtile jeux de mot), un naviguateur web ultra minimaliste.

Le principe est simple: vous rajoutez des urls dans un fichier (ou lui passé en argument, il les rajoutera lui même). Puis vous le lancez le browser qui naviguera dans cette liste d'urls.

C'est du python, ça utilise webkit (le machin pour utiliser gecko était vraiment pourri en comparaison), c'est minimaliste, sans interface, juste des raccourcis clavier. J'aurais pu faire ça avec UZBL mais je n'ai pas eu le courage de chercher à comprendre comment le faire même si cela ne doit pas être bien difficile et puis quand je vois que cela tien sur moins de 200 lignes de python.

Un jouli screenshot:

url screenshot preview

Et parce que c'est toujours pratique pour l'utilisation, les raccourcis:

  • esc/q: quitter sans modifier la liste
  • n/espace: passe à l'url suivante (espace fait baisser l'écran aussi, j'ai eu la flemme de corriger ce bug)
  • r: fait un refresh de la page courante
  • s: sauve l'avancement dans la liste des urls (en gros supprime du fichier urls les urls déjà vu)
  • S: pareil puis quitte
  • y: copie l'url courante dans le clipboard de X (il faut avoir xclip installé)
  • f: envoie l'url vers firefox (il faut bien entendu qu'il soit installé)

Pour choper le code, un petit git clone git://git.worlddomination.be/python/ulr.git

EDIT: Le code qui suit ne correspond plus au master.

Et parce qu'il n'est pas très long mais bien dégeux (je n'avais aucun intérêt à le faire propre), voici le code:

#!/usr/bin/python
# -*- coding:Utf-8 -*-

import gtk
import os
import sys

import webkit
import gobject

class Browser:
    def delete_event(self, widget, event, data=None):
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def open_list(self):
        self.urls = open("urls", "r").readlines()
        if len(self.urls) == 0:
            print "List empty, fill list before launching"
            sys.exit(0)
        print len(self.urls), self.urls

    def __init__(self):
        gobject.threads_init()
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_resizable(True)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.connect("key-press-event", self.keyboard_cb)

        self.open_list()

        self.position = 0

        #webkit.WebView allows us to embed a webkit browser
        #it takes care of going backwards/fowards/reloading
        #it even handles flash
        self.web_view = webkit.WebView()
        self.web_view.open(self.urls[0][:-1])

        #entry bar for typing in and display URLs, when they type in a site
        #and hit enter the on_active function is called
        self.url_bar = gtk.Entry()
        self.url_bar.connect("activate", self.on_active)

        self.bar = gtk.Label()
        self.bar.set_single_line_mode(True)
        self.bar.set_text(self.urls[0][:-1] + "               " + "%i/%i" % (self.position + 1, len(self.urls)))

        #anytime a site is loaded the update_buttons will be called
        self.web_view.connect("load_committed", self.update_buttons)

        scroll_window = gtk.ScrolledWindow(None, None)
        scroll_window.add(self.web_view)


        vbox = gtk.VBox(False, 0)
        vbox.pack_start(self.bar, False, True, 0)
        vbox.add(scroll_window)

        self.window.add(vbox)
        self.window.show_all()

    def on_active(self, widge, data=None):
        '''When the user enters an address in the bar, we check to make
           sure they added the http://, if not we add it for them.  Once
           the url is correct, we just ask webkit to open that site.'''
        url = self.url_bar.get_text()
        try:
            url.index("://")
        except:
            url = "http://"+url
        self.url_bar.set_text(url)
        self.web_view.open(url)

    def go_back(self, widget, data=None):
        '''Webkit will remember the links and this will allow us to go
           backwards.'''
        self.web_view.go_back()

    def go_forward(self, widget, data=None):
        '''Webkit will remember the links and this will allow us to go
           forwards.'''
        self.web_view.go_forward()

    def refresh(self, widget, data=None):
        '''Simple makes webkit reload the current back.'''
        self.web_view.reload()

    def update_buttons(self, widget, data=None):
        '''Gets the current url entry and puts that into the url bar.
           It then checks to see if we can go back, if we can it makes the
           back button clickable.  Then it does the same for the foward
           button.'''
        self.url_bar.set_text( widget.get_main_frame().get_uri() )

    def main(self):
        gtk.main()

    def keyboard_cb(self, widget, event, data=None):
        #print "event:", event
        keyname = gtk.gdk.keyval_name(event.keyval)
        print keyname
        if keyname == "Escape" or keyname == "q":
            print "call deleting"
            self.destroy(widget)
        elif keyname == "space" or keyname == "n":
            print "next"
            self.next()
        elif keyname == "r" or keyname == "R":
            print "reload"
            self.refresh("widget")
        elif keyname == "b" or keyname == "p" or keyname == "Backspace":
            print "previous"
            self.previous()
        elif keyname == "s":
            print "saving"
            self.save()
        elif keyname == "S":
            print "saving"
            self.save()
            print "destroying"
            self.destroy("widget")
        elif keyname == "y":
            print "copy to clipboard"
            if os.system('echo -n "%s" | xclip -i' % self.web_view.get_main_frame().get_uri()):
                print "#fail"
        elif keyname == "f":
            print "go to firefox"
            if os.system("firefox %s" % self.web_view.get_main_frame().get_uri()):
                print "#fail"

    def save(self):
        urls = open("urls", "w")
        for i in self.urls[self.position + 1:]:
            urls.write(i)

    def next(self):
        print "was:", self.position
        if self.position < len(self.urls) - 1:
            print "go forwarf", self.position + 1
            self.position += 1
            print "loading:", self.urls[self.position][:-1]
            self.web_view.open(self.urls[self.position][:-1])
            self.bar.set_text(self.urls[self.position][:-1] + "               " + "%i/%i" % (self.position + 1, len(self.urls)))
        elif self.position == len(self.urls) - 1:
            print "show finish", self.position + 1
            data = '<html><head><title>Hello</title></head><body><center><h1>Finish</h1><h3>One more step forward and the browser will quit and empty the list</h3></center></body></html>'
            self.web_view.load_string(data, 'text/html', "utf-8", "about")
            self.position += 1

        else:
      sp;    print "end ? saving ? destroy"
            self.save()
            self.destroy("widget")

    def previous(self):
        print "was:", self.position
        if self.position > 0:
            print "go back", self.position - 1
            self.position -= 1
            self.web_view.open(self.urls[self.position][:-1])
            self.bar.set_text(self.urls[self.position][:-1] + "               " + "%i/%i" % (self.position + 1, len(self.urls)))
        elif self.position == 0:
            self.position -= 1
            print "show begin"
            data = '<html><head><title>Hello</title></head><body><center><h1>Begin</h1></center></body></html>'
            self.web_view.load_string(data, 'text/html', "utf-8", "about")

if __name__ == "__main__":
    if len(sys.argv) > 1:
        __dir__ = os.path.dirname(os.path.abspath(__file__))
        open(os.path.join(__dir__, "urls"), "a").write(sys.argv[1] + "\ ")
    else:
        browser = Browser()
        browser.main()

Remarque: rien ne vous empêche de combiner ULR avec rsstail ou n'importe quel autre script ou programme.

Feel free to send patchs

Pour le configurer dans newsbeur, rajouter le simplement en browser, cela ne le lancera mais rajoutera l'url dans la liste des urls à lire, puis pour lire cette liste d'url il faudra lancer ulr manuellement.

Si vous trouvez l'ajout de fichiers lents via ulr.py, rien ne vous empêche de créer un script bash qui fait un echo $1 >> /path/vers/le/fichier/urls