Convert Thunderbird Subscriptions to OPML

by Kevin Hemenway

With the official Thunderbird 1.0 out, and the wonderfulness of its RSS capabilities, I've re-examined the quickie RSS subscription parser I wrote back in September. My qualms back then was there was no way to alphabetically sort your subscription list under the "Manage Subscriptions" screen. The same code is still applicable - point the following bit of Perl at your feeds.rdf (located a few levels down in your Thunderbird profile folder), and replace the original RDF bag with the newly alphabetized output (the script is also available here):




#!/usr/bin/perl
use warnings; use strict;
use XML::Simple; my $xml = XMLin(shift, KeyAttr=>"dc:title");
my @titles = sort{ lc($a) cmp lc($b) } keys %{$xml->{"fz:feed"}};

foreach (@titles) {
print " <RDF:li RDF:resource=\"";
$xml->{"fz:feed"}{$_}{"dc:identifier"} =~ s/&/&amp;/g;
print $xml->{"fz:feed"}{$_}{"dc:identifier"}."\"/>\n"; }


But, to the lament of many, Thunderbird still can't read or output an OPML file of your subscriptions for use in other aggregators or simply to publish for visitors that want to read the same things you do. With a quick modification of the above script, we can generate an OPML file based on your Thunderbird subscriptions pretty easily (the script is also available here):




#!/usr/bin/perl
use warnings; use strict;
use XML::Simple; my $xml = XMLin(shift, KeyAttr=>'dc:title');
my @titles = sort{ lc($a) cmp lc($b) } keys %{$xml->{'fz:feed'}};
print "<opml version=\"1.1\"><head></head><body>\n";

foreach (@titles) {
my $url = $xml->{"fz:feed"}{$_}{"dc:identifier"};
$url =~ s/&/&amp;/g; # sbp is immortalized here.
print " <outline text=\"$_\" xmlUrl=\"$url\" />\n";
} print "</body></opml>\n";


Unfortunately, this is still export only - I've not been able to determine how to properly import subscriptions from outside Thunderbird (ie., without using the "Manage Subscriptions" interface). It's easy as pie to modify the feeds.rdf file (as per the alphabetical sorting, above), but Thunderbird doesn't seem to listen to newly added data; it'll allow the changes internally, but won't actually do anything with them - no downloading, no appearing under "Manage Subscriptions", nothin'. I suspect there's another file somewhere that contains the "master" information for feeds.rdf, and if you've got any tips or hints on where that is, leave me a comment below.



For the truly lazy, the above script can even be modified into a service for those without Perl installed. If a user uploads their feeds.rdf file to a website, for example, someone hosting the following script could automatically translate the RDF into OPML by passing "url" as a GET parameter (the script is also available here):




#!/usr/bin/perl
use warnings; use strict;

# did we receive a URL in GET?
use LWP::Simple 'get'; use CGI qw(:standard);
unless (param('url')) { print "Content-type: text/plain\n\n";
print "No URL parameter was passed. Try ?url=, bub."; exit; }

# can we download the URL?
my $data = get(param('url'));
unless ($data) { print "Content-type: text/plain\n\n";
print "Unable to access this URL. Sorry, bub."; exit; }

use XML::Simple; # can we parse the XML?
my $xml = eval { XMLin($data, KeyAttr=>'dc:title') };
if ($@) { print "Content-type: text/plain\n\n";
print "Couldn't parse. Sorry, bub."; exit; }

# continue on as with our other scripts.
my @titles = sort{ lc($a) cmp lc($b) } keys %{$xml->{'fz:feed'}};
print "Content-type: application/xml\n\n";
print "<opml version=\"1.1\"><head></head><body>\n";

foreach (@titles) {
my $url = $xml->{"fz:feed"}{$_}{"dc:identifier"};
$url =~ s/&/&amp;/g; # sbp is immortalized here.
print " <outline text=\"$_\" xmlUrl=\"$url\" />\n";
} print "</body></opml>\n";


For an example of its use, check out the URL click here - it's an export of my current Thunderbird subscriptions based on the feeds.rdf file. (I have seen some oddities with user submitted files containing multiple dc:title elements - these will not be properly parsed by the above code).



Have more information about feeds.rdf? Let us know!