Reading LJ by RSS

Some background. At some point a year or so ago (I'm writing this in mid-2006), apparently bloglines started making some evil noises to the effect of "if we download your RSS content for our users to read, then we own it." This scared some fanfiction writers and commenters (or just pissed them off), and in response to the whole situation, someone on LJ (with whom I have been in communication, and he is very nice) implemented a new feature: LJ users can set their RSS feeds to "title only" (no content, just titles) or turn off RSS syndication.

That solved the problem from the perspective of the content providers (ie, the fanfiction authors and commenters and other people posting to LJ), but then I came along and wanted to read various LJ blogs in RSS. And I was made a little insane by having to click through for the content of every post in some of these blogs. (I don't like the friendslist interface and prefer not to use it.)

Anyways, I explored a lot (a lot) of options, and what finally turned out to work was getting a paid LJ account, then writing an S2 style which lets me read peoples' pages in RSS. It doesn't open them up to bloglines' evilness because you have to log in to use the style. (In fact, I believe you have to have your own paid account and upload the style yourself in order to use it.) So I am hoping that this is a happy answer for both myself and the rest of the world. I'm posting this explanation in case there are other people out there like me who prefer using an RSS reader to the friendslist client and have been similarly aggravated by titles-only blogs.

Code based on code lifted from http://hacks.atrus.org/rdf_atom_s2/rdf_atom_s2.s2.

  1. Pay LJ for your account
  2. Go to your layers and create a new top-level layer of type layout
  3. Click on edit
  4. Replace the default stuff with the source below
  5. Go to your styles
  6. Create a new style. The name and language don't matter
  7. Select the layer you just created from the Layout dropdown and click Change
  8. Ignore the Language, Theme, and User dropdowns. Just click Save Changes
  9. You can now view any journal page in this style by appending s2id=xxxxxxx to the query string (replace the x's with the number of the style you just created). Note that you'll need to specify auth=digest to make sure that you log in first. For example:
    http://www.livejournal.com/users/jphekman/?s2id=11095029
    or
    http://www.livejournal.com/users/jphekman/?auth=digest&s2id=11095029

# Original code copyright (C) 2005 Nikolas Coukouma
# Modifications copyright (C) 2006 Jessica Perry Hekman

layerinfo type="layout";
layerinfo name="RSS";
layerinfo des="Displays an RSS feed of the requested view";
layerinfo author_name="Nikolas 'Atrus' Coukouma";
layerinfo author_email="atrus@atrus.org";
layerinfo source_viewable=1;
layerinfo is_public=1;

#inherited properties
property use page_recent_items;
property use page_friends_items;
property use IMGDIR;

# number of items to show on the page
set page_recent_items = 10;
set page_friends_items = 10;

# order in which to render entries and comments
property bool first_to_last;
set first_to_last = true;

# conform to W3C spec, as referenced by ATOM spec
# http://www.w3.org/TR/NOTE-datetime
property string dateformat;
set dateformat = "%%yyyy%%-%%mon%%-%%d%%T%%hh%%:%%min%%:%%sec%%+00:00";

function Page::print() {
# argh! still not commited
#	set_content_type( "text/xml" );
	var string strTitle;
	$strTitle = $this->title();

	"""<?xml version="1.0"?>
<rss version='2.0' xmlns:lj='http://www.livejournal.org/rss/lj/1.0/'>
<channel>
	<title>$this.global_title</title>
        
        <description>$this.global_subtitle</description>
	<generator>LiveJournal / S2 6513411</generator>""";

	$this->print_body();

"""</channel></rss>""";
}

function Page::print_body() {
	"""
		<entry>
			<title />
			<link rel="alternate" />
			<author />
			<modified />
			<issued />
		</entry>\n""";
}

function RecentPage::print_body() {
	if ($*first_to_last)
	{
		foreach var Entry e (reverse $this.entries) { $this->print_entry($e); }
	} else {
		foreach var Entry e ($this.entries) { $this->print_entry($e); }
	}
}


function EntryPage::print_body() {
	if ($*first_to_last)
	{
		$this->print_entry($.entry);
		"""
			<blogcomments:comments>""";
		foreach var Comment c (reverse $.comments) { $this->print_comment($c); }
		"""
			</blogcomments:comments>""";
	} else {
		$this->print_entry($.entry);
		"""
			<blogcomments:comments>""";
		foreach var Comment c ($.comments) { $this->print_comment($c); }
		"""
			</blogcomments:comments>""";
	}
}

function FriendsPage::print_body() {
	if ($*first_to_last)
	{
		foreach var Entry e (reverse $this.entries) { $this->print_entry($e); }
	} else {
		foreach var Entry e ($this.entries) { $this->print_entry($e); }
	}
}

function DayPage::print_body() {
	if ($*first_to_last)
	{
		foreach var Entry e (reverse $this.entries) { $this->print_entry($e); }
	} else {
		foreach var Entry e ($this.entries) { $this->print_entry($e); }
	}
}

function Page::print_entry(Entry e) {
	var Link l;
	"""
        <item>
			<guid>$e.permalink_url</guid>
			<link href="$e.permalink_url" />
			<title>""";
	print ehtml($e.subject);
	"""</title>
			<description type="text/html" mode="escaped">""";
	
	print ehtml($e.text);
	"""</description>
				<modified>""";
# bogus data, I'm sorry
	print $e.time->date_format( $*dateformat );
	"""</modified>
	</item>""";
}

function EntryPage::print_comment(Comment c) {
}