Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

160 lines (128 sloc) 4.881 kb
# -*- coding: utf-8 -*-
#Canto - ncurses RSS reader
# Copyright (C) 2008 Jack Miller <>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
# Story is a slick little class. It's main purpose is to hold all of the data
# for each story, both content and state. To save memory, 0.7.0 will attempt to
# fetch data from disk if it's not held by default. A good example is
# description information which can take up kilobytes or megabytes of memory but
# are rarely used.
# Story doesn't care which feed or tag it's associated with. If you really want
# to get the feed, story["feed"] contains the unique URL, but you'd have to use
# the config to get the Feed() object. The only thing that the Story() gets from
# the feed is the get_ufp function that gets the feedparser dict from disk.
from const import STORY_SAVED, STORY_UPDATED
import cPickle
import fcntl
class Story():
def __init__(self, d, ufp_path, updated):
self.updated = updated
self.ufp_path = ufp_path
self.ondisk = None
self.d = d
self.sel = 0
self.in_reader = 0
def __eq__(self, other):
if self["id"] != other["id"]:
return 0
# The reason that we have to check for membership is
# that sometimes (like when writing to disk) it's convenient
# to compare against a dict in the feedparser block than
# another Story object. In all of these cases, the feeds
# are guaranteed to be the same anyway.
if "feed" in other and (self["feed"] != other["feed"]):
return 0
return 1
def __str__(self):
return self.d["title"] + " " + str(id(self))
# Where get_ufp reads the ufp from disk, this narrows that down to a
# particular feed entry.
def get_ufp_entry(self):
if not self.ufp_path:
return {}
f = open(self.ufp_path, "r")
fcntl.flock(f.fileno(), fcntl.LOCK_SH)
ufp = cPickle.load(f)
return {}
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
return {}
for ondisk in ufp["entries"]:
if ondisk["id"] == self["id"]:
self.ondisk = None
self.ondisk = ondisk
def __getitem__(self, key):
if key in self.d:
return self.d[key]
# If the key isn't stored by default, get it from disk.
if not self.ondisk:
if not self.ondisk:
return ""
if key in self.ondisk:
return self.ondisk[key]
return ""
def __setitem__(self, key, item):
self.d[key] = item
def __contains__(self, key):
if key in self.d:
return True
if not self.ondisk:
if not self.ondisk:
return False
return key in self.ondisk
def was(self, tag):
return tag in self.d["canto_state"]
def set(self, tag):
if not tag in self.d["canto_state"]:
self.updated = STORY_UPDATED
def unset(self,tag):
if tag in self.d["canto_state"]:
self.updated = STORY_UPDATED
# These are separate from the was/set/unset since the selected status isn't
# stored in the ufp.
def selected(self):
return self.sel
def select(self):
self.sel = 1
def unselect(self):
self.sel = 0
def get_text(self):
if "content" in self:
for c in self["content"]:
if "type" in c and "text" in c["type"]:
return c["value"]
return self["description"]
def get_type(self):
if "content" in self:
for c in self["content"]:
if "type" in c and "text" in c["type"]:
return c["type"]
if "summary_detail" in self and "type" in self["summary_detail"]:
return self["summary_detail"]["type"]
return "text/html"
def get_title_type(self):
if "title_detail" in self and "type" in self["title_detail"]:
return self["title_detail"]["type"]
return "text/html"
# Free makes the Story() forget all of the uncommon items. Should be called
# after anything that could cause Story() to fetch items (ie. in the
# Renderer hooks).
def free(self):
if self.ondisk:
self.ondisk = None
Jump to Line
Something went wrong with that request. Please try again.