1
0

FeedManager.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # The contents of this file are subject to the BitTorrent Open Source License
  2. # Version 1.1 (the License). You may not copy or use this file, in either
  3. # source code or executable form, except in compliance with the License. You
  4. # may obtain a copy of the License at http://www.bittorrent.com/license/.
  5. #
  6. # Software distributed under the License is distributed on an AS IS basis,
  7. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. # for the specific language governing rights and limitations under the
  9. # License.
  10. #
  11. # Written by Matt Chisholm
  12. import libxml2
  13. import BTL.stackthreading as threading
  14. #import gobject
  15. from PluginSupport import PluginManager, BasePlugin
  16. # BUG should be in config
  17. UPDATE_FEED_TIMEOUT = 5 * 60
  18. def get_content(xpath_result):
  19. if len(xpath_result) == 1:
  20. return xpath_result[0].content
  21. return None
  22. class FeedPlugin(BasePlugin):
  23. mimetype = 'text/xml'
  24. subtype = None
  25. def __init__(self, main, url, title, description, doc=None):
  26. BasePlugin.__init__(self, main)
  27. self.url = url
  28. self.title = title
  29. self.description = description
  30. self.doc = None
  31. self.thread = None
  32. self.update(doc)
  33. timeout_id = gobject.timeout_add(UPDATE_FEED_TIMEOUT * 1000, self.update)
  34. def _matches_type(mimetype, subtype):
  35. raise NotImplementedError
  36. matches_type = staticmethod(_matches_type)
  37. def update(self, doc=None):
  38. if self.thread is not None:
  39. if self.thread.isAlive():
  40. return True
  41. self.thread = threading.Thread(target=self._update,
  42. args=(doc,))
  43. self.thread.setDaemon(True)
  44. self.main.show_status("Downloading %s" % self.url)
  45. self.main.ui_wrap_func(self.thread.start)
  46. return True
  47. def _update(self, doc=None):
  48. self.main.show_status('_update running')
  49. if self.doc is not None:
  50. self.main.show_status('_update freeing doc')
  51. self.doc.freeDoc()
  52. if doc is None:
  53. self.main.show_status('_update parsingFile')
  54. doc = libxml2.parseFile(self.url)
  55. self.doc = doc
  56. self.main.feed_was_updated(self.url)
  57. self.main.show_status(self.get_items())
  58. self.main.show_status('_update done, thread should die')
  59. def get_items(self):
  60. raise NotImplementedError
  61. class FeedManager(PluginManager):
  62. kind = 'Feed'
  63. def __init__(self, config, ui_wrap_func):
  64. PluginManager.__init__(self, config, ui_wrap_func)
  65. self.ui_wrap_func = ui_wrap_func
  66. self.channels = {}
  67. self.update_all()
  68. def _check_plugin(self, plugin):
  69. if not PluginManager._check_plugin(self, plugin):
  70. return False
  71. if issubclass(plugin, FeedPlugin):
  72. if hasattr(plugin, 'get_items'):
  73. return True
  74. return False
  75. def update_all(self):
  76. for k in self.config.keys():
  77. self.update_feed(k)
  78. def update_feed(self, feed_url):
  79. if self.channels.has_key(feed_url):
  80. self.show_status("Updating %s" % feed_url)
  81. self.channels[feed_url].update()
  82. else:
  83. self.new_channel(feed_url)
  84. return
  85. def new_channel(self, url):
  86. thread = threading.Thread(target=self._new_channel,
  87. args=(url,))
  88. thread.setDaemon(True)
  89. self.ui_wrap_func(thread.start)
  90. def _new_channel(self, url):
  91. self.show_status("Adding %s" % url)
  92. doc = None
  93. channel = None
  94. try:
  95. doc = libxml2.parseFile(url)
  96. except libxml2.parserError:
  97. self.show_status("Could not parse \"%s\" as XML, searching for torrents" % url)
  98. else:
  99. context = doc.xpathNewContext()
  100. res = context.xpathEval('/rss/@version')
  101. if get_content(res) in ('0.91', '0.92', '2.0'):
  102. self.show_status("Found RSS 0.9x/2.0")
  103. plugin = self._find_plugin('text/xml', 'rss2')
  104. if plugin is not None:
  105. channel = plugin(self, url, doc)
  106. root = doc.children
  107. if (root.ns() is not None and
  108. root.ns().get_content() in ("http://www.w3.org/2005/Atom",)):
  109. self.show_status("Found Atom")
  110. plugin = self._find_plugin('text/xml', 'atom')
  111. if plugin is not None:
  112. channel = plugin(self, url, doc)
  113. context.xpathFreeContext()
  114. if channel is None:
  115. if doc is not None:
  116. doc.freeDoc()
  117. self.show_status("Unknown feed type, using raw")
  118. plugin = self._find_plugin(None, 'raw')
  119. if plugin is not None:
  120. channel = plugin(self, url, url)
  121. self.channels[url] = channel
  122. def feed_was_updated(self, feed_url):
  123. self.run_ui_task('feed_was_updated', feed_url)
  124. def get_feed(self, feed_url):
  125. if not self.channels.has_key(feed_url):
  126. self.update_feed(feed_url)
  127. feed = self.channels[feed_url]
  128. return feed
  129. def get_items(self, feed_url):
  130. channel = self.channels[feed_url]
  131. items = channel.get_items()
  132. return items