11 September 2006

Experimenting with Scripting Support

This weekend I added LUA scripting support to SVN trunk. I'm not sure wether this is a good and very useful idea but I just wanted to try and see how things work out.

The current code allows to open the "Script Manager" window (screenshot) from the "Program" menu. When opening for the first time there should be no scripts registered. Everything should be empty.

The idea is that you select a scripting hook (the option menu at the top) and add one or more scripts that are to be called when the hook is triggered. Currently supported hooks are:
  • Startup
  • Feed updated
  • Feed selected
  • Item selected
  • Feed unselect
  • Item unselect
A script registered for one of these hooks will probably always modify one or more of the currently selected item(s) or feed(s). The currently possible actions are limited by the generated LUA binding. Which is created using SWIG. Currently there are bindings for item.h, node.h, itemlist.h and feedlist.h which should allow to execute all item, feed and feedlist modifications from within LUA.

A simple and useful example is shown in the screenshot. It addresses a feature I was asked for several times, but which was never implemented as an seperate preference. The feature is to mark all items of a feed as read once it is unselected.

To do this a script for the "feed unselect" hook is added. It uses the "liferea" LUA module which maps all functions from the includes mentioned above. So it simply calls feedlist_get_selected() to determine the node that is to be unselected and calls node_mark_all_read() for this node.

What do you think about scripting support? Would you use it? Ideas for other use cases?


Allan said...

Looks way cool! Will it be possible to escape to the system as well? Perhaps liferea.system.call("/home/me/bin/foo.sh") or some such.

Scenario: I subscribe to a number of feeds just to receive announcements of new releases. It would be nice to download the new version automatically, but I would probably need an external script of some sort to do that.

Lars Lindner said...

LUA itself allows you to escape to the system using the "OS" module. And the current source loads all available LUA modules (which raises the security question). To use the OS-function just call something like 'os.execute("xeyes")'.

So no need for Liferea to implement it. This is a very good use case!

Lars Strojny said...

I would prefer Python over LUA, because Python seems to me like a more or less default for the purpose of building small scripts to implement custom functions into applications. Epiphany does it, GIMP does it so why should Liferea don't do it.

Lars Lindner said...

@Lars: There are two reasons. The simplest one: I despise Python and always use Perl, it is subjective feeling, mostly targeted at the formatting style, but it is an important reason.

And the other reason is I don't want to allow running external programs, I want to allow internal application scripting. And I'm pretty sure that standalone scripting languages like Python are definitively the wrong choice. First their runtime stability and memory usage behaviour is unpredictable. And second they are total overkill. I want simple language support to process some basic Liferea data structures and to call some simple set/get methods.

From the security standpoint LUA allows you to enable only the functionality modules you really need and does not import countless user installed libraries running other system dependent code.

Also for Python, Perl, Ruby whatever language the following important thing is true: half of the users will use the "other" language and will hate it once you install the "other" scripting language and its 120 MB of default modules. While liblua50 for example has 68kB package size and 220kB installed packages size (at least the Debian package). And even if you use it only for Liferea it won't hurt that much.

Ben said...

I read an aggregator feed that includes some other feeds that I already subscribe to.

The results in the slightly annoying case where I have to mark something as read twice.

If I'd be able to compare aggregate feed items to the other feeds to automatically mark duplicate items read, I'd definitely go to the trouble of learning Lua.

A suggestion: As Lua isn't as widely known as the 'P' scripting languages, having a spot on your website where some commonly useful scripts can be stored might be helpful to those less inclined to learn Lua.

Lars Lindner said...

In fact I already added such a scripting description to the homepage. It can be found in the documentation section. Currently it features two small examples. I plan to add more.

The problem with the detection of identical items that you propose is that this needs a string comparsion of either the full content or the title/link of the item that you mark as read with all currently unread items. Which is much too slow. On each item list selection click you would have a delay of some seconds for a match against maybe 100 items because they need to be loaded from disk each time.

Lars Strojny said...

I understand why you chose LUA. The issue I want to point to is, that in the way I'm seeing the development of Open Source software, cluttering sucks. This is true for the interface, for libraries (just think on ffmpeg) and also for other tasks which are common. Writing plugins in a high-level language is one of this things, which is common. It seems to me, that the GTK-world, from which liferea comes from without any doubt, is turning to Python while the KDE-guys seems to love Ruby (if you ask me, I like GTK in combination with Ruby, but I will not overrate myself). So I just want to annotate if it is not worth thinking about using Python for plugins, just to be on the mainstream in the part of the world (GTK) Liferea is located. The same reason why I really don't like the new folder-icon. It has nothing to do with my taste (nevertheless I dislike the style), it has something to do with that I find it more important to provide a non-cluttered UI-impression than the accurate UI-design. Changing the last thing for all is easier than working with a lot of different styles.

Aristotle Pagaltzis said...

May I suggest embedding Ruby instead? It looks nicer than Perl to many people, it doesn’t look as odd as Python to many others, and it doesn’t seem to polarize people so much into hate-it/love-it camps as Perl and Python do. Also, it has a very friendly API for interfacing with native code (Perl is a nightmare in comparison).

The dependency criterion would favour Perl – on most unixoid systems it is installed by default.

Anyway, another point:

I think it would be much nicer to work with the API if it exposed the underlying model as a bunch of objects, rather than the raw procedural(-though-it’s-really-OO) C API. (Or does LUA not have OO features?)

Ben said...

You're right about the string comparison being expensive. I wonder if some kind of md5 or something could be performed when an item is first pulled in, which might make the operation less expensive (or might now, I'm not sure.)

If the md5's were small and you threw them into a hash table, lookup would be constant.

Lars Lindner said...

Yes, this would be possible. Calculating a hash once at download time should not be a problem. Can I convince you to write a patch?

Ben said...

I'll take a swim through the code and see what I can make out.

Lars Lindner said...

@ben: If you need any help with it just ask!