Use new torrent states instead of using libtorrent's.

This commit is contained in:
Andrew Resch 2008-02-02 01:30:18 +00:00
parent e2eaa9abb4
commit 9469610aca
7 changed files with 251 additions and 197 deletions

View File

@ -39,18 +39,26 @@ import pkg_resources
import xdg, xdg.BaseDirectory
TORRENT_STATE = [
"Queued",
"Checking",
"Connecting",
"Downloading Metadata",
"Downloading",
"Finished",
"Seeding",
"Allocating",
"Paused"
]
LT_TORRENT_STATE = {
"Queued": 0,
"Checking": 1,
"Connecting": 2,
"Downloading Metadata": 3,
"Downloading": 4,
"Finished": 5,
"Seeding": 6,
"Allocating": 7,
"Paused": 8
}
TORRENT_STATE = {
"Allocating": 0,
"Checking": 1,
"Downloading": 2,
"Seeding": 3,
"Paused": 4,
"Error": 5
}
def get_version():
"""Returns the program version from the egg metadata"""
return pkg_resources.require("Deluge")[0].version.split("r")[0]

View File

@ -344,17 +344,17 @@ class Core(
def export_force_reannounce(self, torrent_id):
log.debug("Forcing reannouncment to trackers of torrent %s", torrent_id)
self.torrents.force_reannounce(torrent_id)
self.torrents[torrent_id].force_reannounce()
def export_pause_torrent(self, torrent_id):
log.debug("Pausing torrent %s", torrent_id)
if not self.torrents.pause(torrent_id):
if not self.torrents[torrent_id].pause():
log.warning("Error pausing torrent %s", torrent_id)
def export_move_torrent(self, torrent_id, folder):
log.debug("Moving torrent %s to %s", torrent_id, folder)
if not self.torrents.move(torrent_id, folder):
log.warning("Error moving torrent %s to %s", torrent_id, folder)
def export_move_torrent(self, torrent_id, dest):
log.debug("Moving torrent %s to %s", torrent_id, dest)
if not self.torrents[torrent_id].move(dest):
log.warning("Error moving torrent %s to %s", torrent_id, dest)
def export_pause_all_torrents(self):
"""Pause all torrents in the session"""
@ -369,7 +369,7 @@ class Core(
def export_resume_torrent(self, torrent_id):
log.debug("Resuming torrent %s", torrent_id)
if self.torrents.resume(torrent_id):
if self.torrents[torrent_id].resume():
self.torrent_resumed(torrent_id)
def export_get_torrent_status(self, torrent_id, keys):
@ -477,7 +477,7 @@ class Core(
def export_set_torrent_trackers(self, torrent_id, trackers):
"""Sets a torrents tracker list. trackers will be [{"url", "tier"}]"""
return self.torrents.set_trackers(torrent_id, trackers)
return self.torrents[torrent_id].set_trackers(trackers)
# Signals
def torrent_added(self, torrent_id):

View File

@ -33,13 +33,23 @@
"""Internal Torrent class"""
import os
import deluge.libtorrent as lt
import deluge.common
from deluge.configmanager import ConfigManager
from deluge.log import LOG as log
TORRENT_STATE = deluge.common.TORRENT_STATE
class Torrent:
"""Torrent holds information about torrents added to the libtorrent session.
"""
def __init__(self, filename, handle, compact, save_path, total_uploaded=0,
trackers=None):
# Get the core config
self.config = ConfigManager("core.conf")
# Set the filename
self.filename = filename
# Set the libtorrent handle
@ -52,7 +62,22 @@ class Torrent:
self.compact = compact
# Where the torrent is being saved to
self.save_path = save_path
# The state of the torrent
self.state = None
# Holds status info so that we don't need to keep getting it from lt
self.status = self.handle.status()
self.torrent_info = self.handle.torrent_info()
# Set the initial state
if self.status.state == deluge.common.LT_TORRENT_STATE["Allocating"]:
self.set_state("Allocating")
elif self.status.state == deluge.common.LT_TORRENT_STATE["Checking"]:
self.set_state("Checking")
else:
self.set_state("Paused")
# Various torrent options
self.max_connections = -1
self.max_upload_slots = -1
self.max_upload_speed = -1
@ -73,10 +98,6 @@ class Torrent:
self.trackers.append(tracker)
else:
self.trackers = trackers
# Holds status info so that we don't need to keep getting it from lt
self.status = None
self.torrent_info = None
# Files dictionary
self.files = self.get_files()
@ -114,8 +135,21 @@ class Torrent:
def set_file_priorities(self, file_priorities):
self.file_priorities = file_priorities
self.handle.prioritize_files(file_priorities)
def get_state(self):
def set_state(self, state):
"""Accepts state strings, ie, "Paused", "Seeding", etc."""
# Only set 'Downloading' or 'Seeding' state if not paused
if state == "Downloading" or state == "Seeding":
if self.handle.is_paused():
state = "Paused"
try:
self.state = TORRENT_STATE[state]
except:
pass
def get_save_info(self):
"""Returns the state of this torrent for saving to the session state"""
status = self.handle.status()
return (self.torrent_id, self.filename, self.compact, status.paused,
@ -188,11 +222,6 @@ class Torrent:
# Adjust progress to be 0-100 value
progress = self.status.progress * 100
# Set the state to 'Paused' if the torrent is paused.
state = self.status.state
if self.status.paused:
state = deluge.common.TORRENT_STATE.index("Paused")
# Adjust status.distributed_copies to return a non-negative value
distributed_copies = self.status.distributed_copies
if distributed_copies < 0:
@ -207,7 +236,7 @@ class Torrent:
"distributed_copies": distributed_copies,
"total_done": self.status.total_done,
"total_uploaded": self.total_uploaded + self.status.total_payload_upload,
"state": int(state),
"state": self.state,
"paused": self.status.paused,
"progress": progress,
"next_announce": self.status.next_announce.seconds,
@ -242,3 +271,139 @@ class Torrent:
status_dict[key] = full_status[key]
return status_dict
def pause(self):
"""Pause this torrent"""
try:
self.handle.pause()
except Exception, e:
log.debug("Unable to pause torrent: %s", e)
return False
return True
def resume(self):
"""Resumes this torrent"""
if self.state != TORRENT_STATE["Paused"]:
return False
try:
self.handle.resume()
except:
return False
# Set the state
if self.handle.is_seed():
self.set_state("Seeding")
else:
self.set_state("Downloading")
status = self.get_status(["total_done", "total_wanted"])
# Only delete the .fastresume file if we're still downloading stuff
if status["total_done"] < status["total_wanted"]:
self.delete_fastresume()
return True
def move_storage(self, dest):
"""Move a torrent's storage location"""
try:
self.handle.move_storage(dest)
except:
return False
return True
def write_fastresume(self):
"""Writes the .fastresume file for the torrent"""
resume_data = lt.bencode(self.handle.write_resume_data())
path = "%s/%s.fastresume" % (
self.config["torrentfiles_location"],
self.filename)
log.debug("Saving fastresume file: %s", path)
try:
fastresume = open(path, "wb")
fastresume.write(resume_data)
fastresume.close()
except IOError:
log.warning("Error trying to save fastresume file")
def delete_fastresume(self):
"""Deletes the .fastresume file"""
path = "%s/%s.fastresume" % (
self.config["torrentfiles_location"],
self.filename)
log.debug("Deleting fastresume file: %s", path)
try:
os.remove(path)
except Exception, e:
log.warning("Unable to delete the fastresume file: %s", e)
def force_reannounce(self):
"""Force a tracker reannounce"""
try:
self.handle.force_reannounce()
except Exception, e:
log.debug("Unable to force reannounce: %s", e)
return False
return True
def scrape_tracker(self):
"""Scrape the tracker"""
try:
self.handle.scrape_tracker()
except Exception, e:
log.debug("Unable to scrape tracker: %s", e)
return False
return True
def set_trackers(self, trackers):
"""Sets trackers"""
if trackers == None:
trackers = []
log.debug("Setting trackers for %s: %s", self.torrent_id, trackers)
tracker_list = []
for tracker in trackers:
new_entry = lt.announce_entry(tracker["url"])
new_entry.tier = tracker["tier"]
tracker_list.append(new_entry)
self.handle.replace_trackers(tracker_list)
# Print out the trackers
for t in self.handle.trackers():
log.debug("tier: %s tracker: %s", t.tier, t.url)
# Set the tracker list in the torrent object
self.trackers = trackers
if len(trackers) > 0:
# Force a reannounce if there is at least 1 tracker
self.force_reannounce()
def save_torrent_file(self, filedump=None):
"""Saves a torrent file"""
log.debug("Attempting to save torrent file: %s", self.filename)
# Test if the torrentfiles_location is accessible
if os.access(
os.path.join(self.config["torrentfiles_location"]), os.F_OK) \
is False:
# The directory probably doesn't exist, so lets create it
try:
os.makedirs(os.path.join(self.config["torrentfiles_location"]))
except IOError, e:
log.warning("Unable to create torrent files directory: %s", e)
# Write the .torrent file to the torrent directory
try:
save_file = open(os.path.join(self.config["torrentfiles_location"],
self.filename),
"wb")
if filedump == None:
filedump = self.handle.torrent_info().create_torrent()
save_file.write(lt.bencode(filedump))
save_file.close()
except IOError, e:
log.warning("Unable to save torrent file: %s", e)

View File

@ -93,6 +93,8 @@ class TorrentManager(component.Component):
self.on_alert_torrent_finished)
self.alerts.register_handler("torrent_paused_alert",
self.on_alert_torrent_paused)
self.alerts.register_handler("torrent_checked_alert",
self.on_alert_torrent_checked)
self.alerts.register_handler("tracker_reply_alert",
self.on_alert_tracker_reply)
self.alerts.register_handler("tracker_announce_alert",
@ -116,7 +118,7 @@ class TorrentManager(component.Component):
# Pause all torrents and save the .fastresume files
self.pause_all()
for key in self.torrents.keys():
self.write_fastresume(key)
self.torrents[key].write_fastresume()
def shutdown(self):
self.stop()
@ -228,7 +230,7 @@ class TorrentManager(component.Component):
# Set the trackers
if trackers != None:
self.set_trackers(str(handle.info_hash()), trackers)
torrent.set_trackers(trackers)
# Set per-torrent options
torrent.set_max_connections(options["max_connections_per_torrent"])
@ -250,34 +252,11 @@ class TorrentManager(component.Component):
handle.resume()
# Save the torrent file
self.save_torrent(filename, filedump)
torrent.save_torrent_file(filedump)
# Save the session state
self.save_state()
return torrent.torrent_id
def save_torrent(self, filename, filedump):
"""Saves a torrent file"""
log.debug("Attempting to save torrent file: %s", filename)
# Test if the torrentfiles_location is accessible
if os.access(
os.path.join(self.config["torrentfiles_location"]), os.F_OK) \
is False:
# The directory probably doesn't exist, so lets create it
try:
os.makedirs(os.path.join(self.config["torrentfiles_location"]))
except IOError, e:
log.warning("Unable to create torrent files directory: %s", e)
# Write the .torrent file to the torrent directory
try:
save_file = open(os.path.join(self.config["torrentfiles_location"],
filename),
"wb")
save_file.write(lt.bencode(filedump))
save_file.close()
except IOError, e:
log.warning("Unable to save torrent file: %s", e)
def load_torrent(self, filename):
"""Load a torrent file and return it's torrent info"""
@ -322,7 +301,7 @@ class TorrentManager(component.Component):
log.warning("Unable to remove .torrent file: %s", e)
# Remove the .fastresume if it exists
self.delete_fastresume(torrent_id)
self.torrents[torrent_id].delete_fastresume()
# Remove the torrent from deluge's session
try:
@ -333,24 +312,6 @@ class TorrentManager(component.Component):
# Save the session state
self.save_state()
return True
def pause(self, torrent_id):
"""Pause a torrent"""
try:
self.torrents[torrent_id].handle.pause()
except:
return False
return True
def move(self, torrent_id, folder):
"""Move a torrent"""
try:
self.torrents[torrent_id].handle.move_storage(folder)
except:
return False
return True
def pause_all(self):
"""Pauses all torrents.. Returns a list of torrents paused."""
@ -363,24 +324,9 @@ class TorrentManager(component.Component):
log.warning("Unable to pause torrent %s", key)
return torrent_was_paused
def resume(self, torrent_id):
"""Resume a torrent"""
try:
self.torrents[torrent_id].handle.resume()
except:
return False
status = self.torrents[torrent_id].get_status(
["total_done", "total_wanted"])
# Only delete the .fastresume file if we're still downloading stuff
if status["total_done"] < status["total_wanted"]:
self.delete_fastresume(torrent_id)
return True
def resume_all(self):
"""Resumes all torrents.. Returns a list of torrents resumed"""
"""Resumes all torrents.. Returns True if at least 1 torrent is resumed"""
torrent_was_resumed = False
for key in self.torrents.keys():
if self.resume(key):
@ -389,50 +335,7 @@ class TorrentManager(component.Component):
log.warning("Unable to resume torrent %s", key)
return torrent_was_resumed
def set_trackers(self, torrent_id, trackers):
"""Sets trackers"""
if trackers == None:
trackers = []
log.debug("Setting trackers for %s: %s", torrent_id, trackers)
tracker_list = []
for tracker in trackers:
new_entry = lt.announce_entry(tracker["url"])
new_entry.tier = tracker["tier"]
tracker_list.append(new_entry)
self.torrents[torrent_id].handle.replace_trackers(tracker_list)
# Print out the trackers
for t in self.torrents[torrent_id].handle.trackers():
log.debug("tier: %s tracker: %s", t.tier, t.url)
# Set the tracker list in the torrent object
self.torrents[torrent_id].trackers = trackers
if len(trackers) > 0:
# Force a reannounce if there is at least 1 tracker
self.force_reannounce(torrent_id)
def force_reannounce(self, torrent_id):
"""Force a tracker reannounce"""
try:
self.torrents[torrent_id].handle.force_reannounce()
except Exception, e:
log.debug("Unable to force reannounce: %s", e)
return False
return True
def scrape_tracker(self, torrent_id):
"""Scrape the tracker"""
try:
self.torrents[torrent_id].handle.scrape_tracker()
except Exception, e:
log.debug("Unable to scrape tracker: %s", e)
return False
return True
def force_recheck(self, torrent_id):
"""Forces a re-check of the torrent's data"""
log.debug("Doing a forced recheck on %s", torrent_id)
@ -443,7 +346,7 @@ class TorrentManager(component.Component):
if os.access(os.path.join(self.config["torrentfiles_location"] +\
"/" + torrent.filename), os.F_OK) is False:
torrent_info = torrent.handle.get_torrent_info().create_torrent()
self.save_torrent(torrent.filename, torrent_info)
torrent.save_torrent_file()
# We start by removing it from the lt session
try:
@ -453,7 +356,7 @@ class TorrentManager(component.Component):
return False
# Remove the fastresume file if there
self.delete_fastresume(torrent_id)
torrent.delete_fastresume()
# Load the torrent info from file if needed
if torrent_info == None:
@ -481,6 +384,9 @@ class TorrentManager(component.Component):
# The torrent was not added to the session
return False
# Set the state to Checking
torrent.set_state("Checking")
return True
def load_state(self):
@ -508,7 +414,7 @@ class TorrentManager(component.Component):
state = TorrentManagerState()
# Create the state for each Torrent and append to the list
for torrent in self.torrents.values():
torrent_state = TorrentState(*torrent.get_state())
torrent_state = TorrentState(*torrent.get_save_info())
state.torrents.append(torrent_state)
# Pickle the TorrentManagerState object
@ -524,33 +430,6 @@ class TorrentManager(component.Component):
# We return True so that the timer thread will continue
return True
def delete_fastresume(self, torrent_id):
"""Deletes the .fastresume file"""
torrent = self.torrents[torrent_id]
path = "%s/%s.fastresume" % (
self.config["torrentfiles_location"],
torrent.filename)
log.debug("Deleting fastresume file: %s", path)
try:
os.remove(path)
except Exception, e:
log.warning("Unable to delete the fastresume file: %s", e)
def write_fastresume(self, torrent_id):
"""Writes the .fastresume file for the torrent"""
torrent = self.torrents[torrent_id]
resume_data = lt.bencode(torrent.handle.write_resume_data())
path = "%s/%s.fastresume" % (
self.config["torrentfiles_location"],
torrent.filename)
log.debug("Saving fastresume file: %s", path)
try:
fastresume = open(path,"wb")
fastresume.write(resume_data)
fastresume.close()
except IOError:
log.warning("Error trying to save fastresume file")
def on_set_max_connections_per_torrent(self, key, value):
"""Sets the per-torrent connection limit"""
log.debug("max_connections_per_torrent set to %s..", value)
@ -571,16 +450,27 @@ class TorrentManager(component.Component):
# Get the torrent_id
torrent_id = str(alert.handle.info_hash())
log.debug("%s is finished..", torrent_id)
# Set the torrent state
self.torrents[torrent_id].set_state("Seeding")
# Write the fastresume file
self.write_fastresume(torrent_id)
self.torrents[torrent_id].write_fastresume()
def on_alert_torrent_paused(self, alert):
log.debug("on_alert_torrent_paused")
# Get the torrent_id
torrent_id = str(alert.handle.info_hash())
# Set the torrent state
self.torrents[torrent_id].set_state("Paused")
# Write the fastresume file
self.write_fastresume(torrent_id)
self.torrents[torrent_id].write_fastresume()
def on_alert_torrent_checked(self, alert):
log.debug("on_alert_torrent_checked")
# Get the torrent_id
torrent_id = str(alert.handle.info_hash())
# Set the torrent state
self.torrents[torrent_id].set_state("Downloading")
def on_alert_tracker_reply(self, alert):
log.debug("on_alert_tracker_reply")
# Get the torrent_id

View File

@ -38,6 +38,8 @@ import deluge.component as component
import deluge.common
from deluge.log import LOG as log
TORRENT_STATE = deluge.common.TORRENT_STATE
class SideBar(component.Component):
def __init__(self):
component.Component.__init__(self, "SideBar")
@ -102,13 +104,11 @@ class SideBar(component.Component):
component.get("TorrentView").set_filter(None, None)
if value == "Downloading":
component.get("TorrentView").set_filter("state",
deluge.common.TORRENT_STATE.index("Downloading"))
TORRENT_STATE["Downloading"])
if value == "Seeding":
component.get("TorrentView").set_filter("state",
deluge.common.TORRENT_STATE.index("Seeding"))
TORRENT_STATE["Seeding"])
if value == "Paused":
component.get("TorrentView").set_filter("state",
deluge.common.TORRENT_STATE.index("Paused"))
TORRENT_STATE["Paused"])

View File

@ -42,9 +42,6 @@ from deluge.common import TORRENT_STATE
import deluge.ui.client as client
class ToolBar(component.Component):
STATE_FINISHED = TORRENT_STATE.index("Finished")
STATE_SEEDING = TORRENT_STATE.index("Seeding")
STATE_PAUSED = TORRENT_STATE.index("Paused")
def __init__(self):
component.Component.__init__(self, "ToolBar")
log.debug("ToolBar Init..")
@ -175,7 +172,7 @@ class ToolBar(component.Component):
except KeyError, e:
log.debug("Error getting torrent state: %s", e)
continue
if status == self.STATE_PAUSED:
if status == TORRENT_STATE["Paused"]:
resume = True
else:
pause = True

View File

@ -48,6 +48,8 @@ import deluge.ui.client as client
from deluge.log import LOG as log
import deluge.ui.gtkui.listview as listview
TORRENT_STATE = deluge.common.TORRENT_STATE
# Status icons.. Create them from file only once to avoid constantly
# re-creating them.
icon_downloading = gtk.gdk.pixbuf_new_from_file(
@ -56,18 +58,17 @@ icon_seeding = gtk.gdk.pixbuf_new_from_file(
deluge.common.get_pixmap("seeding16.png"))
icon_inactive = gtk.gdk.pixbuf_new_from_file(
deluge.common.get_pixmap("inactive16.png"))
icon_alert = gtk.gdk.pixbuf_new_from_file(
deluge.common.get_pixmap("alert16.png"))
# Holds the info for which status icon to display based on state
ICON_STATE = [
icon_inactive,
icon_downloading,
icon_downloading,
icon_downloading,
icon_inactive,
icon_downloading,
icon_seeding,
icon_seeding,
icon_downloading,
icon_inactive
icon_inactive,
icon_alert
]
def cell_data_statusicon(column, cell, model, row, data):
@ -78,23 +79,16 @@ def cell_data_statusicon(column, cell, model, row, data):
def cell_data_progress(column, cell, model, row, data):
"""Display progress bar with text"""
# Translated state strings
TORRENT_STATE = [
_("Queued"),
_("Checking"),
_("Connecting"),
_("Downloading Metadata"),
_("Downloading"),
_("Finished"),
_("Seeding"),
_("Allocating"),
_("Paused")
]
(value, text) = model.get(row, *data)
if cell.get_property("value") != value:
cell.set_property("value", value)
textstr = "%s" % TORRENT_STATE[text]
if TORRENT_STATE[text] != "Seeding" and TORRENT_STATE[text] != "Finished":
state_str = ""
for key in TORRENT_STATE.keys():
if TORRENT_STATE[key] == text:
state_str = key
break
textstr = "%s" % state_str
if state_str != "Seeding" and state_str != "Finished" and value < 100:
textstr = textstr + " %.2f%%" % value
if cell.get_property("text") != textstr:
cell.set_property("text", textstr)