Fixup saving and loading state files

* All state files have a backup created before saving
 * The backup will now be used if saving or loading fails
 * GTKUI state files stored in new gtkui_state dir and common load/save functions created
 * Detects bad shutdown and archives timestamped state files in separate config directory.
This commit is contained in:
Calum Lind 2013-05-21 23:45:26 +01:00
parent 2bbc1013be
commit 2c4ef9dbb3
10 changed files with 283 additions and 229 deletions

View File

@ -486,8 +486,8 @@ what is currently in the config and it could not convert the value
# Make a backup of the old config # Make a backup of the old config
try: try:
log.debug("Backing up old config file to %s~", filename) log.debug("Backing up old config file to %s.bak", filename)
shutil.move(filename, filename + "~") shutil.move(filename, filename + ".bak")
except Exception, e: except Exception, e:
log.warning("Unable to backup old config...") log.warning("Unable to backup old config...")

View File

@ -100,7 +100,7 @@ class AuthManager(component.Component):
def update(self): def update(self):
auth_file = configmanager.get_config_dir("auth") auth_file = configmanager.get_config_dir("auth")
# Check for auth file and create if necessary # Check for auth file and create if necessary
if not os.path.exists(auth_file): if not os.path.isfile(auth_file):
log.info("Authfile not found, recreating it.") log.info("Authfile not found, recreating it.")
self.__load_auth_file() self.__load_auth_file()
return return
@ -192,36 +192,40 @@ class AuthManager(component.Component):
return True return True
def write_auth_file(self): def write_auth_file(self):
old_auth_file = configmanager.get_config_dir("auth") filename = "auth"
new_auth_file = old_auth_file + '.new' filepath = os.path.join(configmanager,get_config_dir(), filename)
bak_auth_file = old_auth_file + '.bak' filepath_bak = filepath + ".bak"
# Let's first create a backup
if os.path.exists(old_auth_file):
shutil.copy2(old_auth_file, bak_auth_file)
try: try:
fd = open(new_auth_file, "w") if os.path.isfile(filepath):
for account in self.__auth.values(): log.info("Creating backup of %s at: %s", filename, filepath_bak)
fd.write( shutil.copy2(filepath, filepath_bak)
"%(username)s:%(password)s:%(authlevel_int)s\n" % except IOError as ex:
account.data() log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
) else:
fd.flush() log.info("Saving the %s at: %s", filename, filepath)
os.fsync(fd.fileno()) try:
fd.close() with open(filepath, "wb") as _file:
os.rename(new_auth_file, old_auth_file) for account in self.__auth.values():
except: _file.write("%(username)s:%(password)s:%(authlevel_int)s\n" % account.data())
# Something failed, let's restore the previous file _file.flush()
if os.path.exists(bak_auth_file): os.fsync(_file.fileno())
os.rename(bak_auth_file, old_auth_file) except (IOError) as ex:
log.error("Unable to save %s: %s", filename, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
shutil.move(filepath_bak, filepath)
self.__load_auth_file() self.__load_auth_file()
def __load_auth_file(self): def __load_auth_file(self):
save_and_reload = False save_and_reload = False
auth_file = configmanager.get_config_dir("auth") filename = "auth"
auth_file = configmanager.get_config_dir(filename)
auth_file_bak = auth_file + ".bak"
# Check for auth file and create if necessary # Check for auth file and create if necessary
if not os.path.exists(auth_file): if not os.path.isfile(auth_file):
create_localclient_account() create_localclient_account()
return self.__load_auth_file() return self.__load_auth_file()
@ -232,10 +236,20 @@ class AuthManager(component.Component):
# File didn't change, no need for re-parsing's # File didn't change, no need for re-parsing's
return return
# Load the auth file into a dictionary: {username: Account(...)} for _filepath in (auth_file, auth_file_bak):
f = open(auth_file, "r").readlines() log.info("Opening %s for load: %s", filename, _filepath)
try:
with open(_filepath, "rb") as _file:
file_data = _file.readlines()
except (IOError), ex:
log.warning("Unable to load %s: %s", _filepath, ex)
file_data = []
else:
log.info("Successfully loaded %s: %s", filename, _filepath)
break
for line in f: # Load the auth file into a dictionary: {username: Account(...)}
for line in file_data:
line = line.strip() line = line.strip()
if line.startswith("#") or not line: if line.startswith("#") or not line:
# This line is a comment or empty # This line is a comment or empty

View File

@ -38,6 +38,7 @@ from deluge._libtorrent import lt
import os import os
import glob import glob
import shutil
import base64 import base64
import logging import logging
import threading import threading
@ -50,7 +51,7 @@ import twisted.web.error
from deluge.httpdownloader import download_file from deluge.httpdownloader import download_file
from deluge import path_chooser_common from deluge import path_chooser_common
import deluge.configmanager from deluge.configmanager import ConfigManager, get_config_dir
import deluge.common import deluge.common
import deluge.component as component import deluge.component as component
from deluge.event import * from deluge.event import *
@ -120,7 +121,7 @@ class Core(component.Component):
self.new_release = None self.new_release = None
# Get the core config # Get the core config
self.config = deluge.configmanager.ConfigManager("core.conf") self.config = ConfigManager("core.conf")
self.config.save() self.config.save()
# If there was an interface value from the command line, use it, but # If there was an interface value from the command line, use it, but
@ -153,19 +154,46 @@ class Core(component.Component):
def __save_session_state(self): def __save_session_state(self):
"""Saves the libtorrent session state""" """Saves the libtorrent session state"""
filename = "session.state"
filepath = get_config_dir(filename)
filepath_bak = filepath + ".bak"
try: try:
session_state = deluge.configmanager.get_config_dir("session.state") if os.path.isfile(filepath):
open(session_state, "wb").write(lt.bencode(self.session.save_state())) log.info("Creating backup of %s at: %s", filename, filepath_bak)
except Exception, e: shutil.copy2(filepath, filepath_bak)
log.warning("Failed to save lt state: %s", e) except IOError as ex:
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
else:
log.info("Saving the %s at: %s", filename, filepath)
try:
with open(filepath, "wb") as _file:
_file.write(lt.bencode(self.session.save_state()))
_file.flush()
os.fsync(_file.fileno())
except (IOError, EOFError) as ex:
log.error("Unable to save %s: %s", filename, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
shutil.move(filepath_bak, filepath)
def __load_session_state(self): def __load_session_state(self):
"""Loads the libtorrent session state""" """Loads the libtorrent session state"""
try: filename = "session.state"
session_state = deluge.configmanager.get_config_dir("session.state") filepath = get_config_dir(filename)
self.session.load_state(lt.bdecode(open(session_state, "rb").read())) filepath_bak = filepath + ".bak"
except Exception, e:
log.warning("Failed to load lt state: %s", e) for _filepath in (filepath, filepath_bak):
log.info("Opening %s for load: %s", filename, _filepath)
try:
with open(_filepath, "rb") as _file:
state = lt.bdecode(_file.read())
except (IOError, EOFError, RuntimeError), ex:
log.warning("Unable to load %s: %s", _filepath, ex)
else:
log.info("Successfully loaded %s: %s", filename, _filepath)
self.session.load_state(state)
return
def get_new_release(self): def get_new_release(self):
log.debug("get_new_release") log.debug("get_new_release")
@ -679,7 +707,7 @@ class Core(component.Component):
log.exception(e) log.exception(e)
return return
f = open(os.path.join(deluge.configmanager.get_config_dir(), "plugins", filename), "wb") f = open(os.path.join(get_config_dir(), "plugins", filename), "wb")
f.write(filedump) f.write(filedump)
f.close() f.close()
component.get("CorePluginManager").scan_for_plugins() component.get("CorePluginManager").scan_for_plugins()

View File

@ -144,8 +144,9 @@ class TorrentManager(component.Component):
self.config = ConfigManager("core.conf") self.config = ConfigManager("core.conf")
# Make sure the state folder has been created # Make sure the state folder has been created
if not os.path.exists(os.path.join(get_config_dir(), "state")): self.state_dir = os.path.join(get_config_dir(), "state")
os.makedirs(os.path.join(get_config_dir(), "state")) if not os.path.exists(self.state_dir):
os.makedirs(self.state_dir)
# Create the torrents dict { torrent_id: Torrent } # Create the torrents dict { torrent_id: Torrent }
self.torrents = {} self.torrents = {}
@ -212,6 +213,34 @@ class TorrentManager(component.Component):
# Get the pluginmanager reference # Get the pluginmanager reference
self.plugins = component.get("CorePluginManager") self.plugins = component.get("CorePluginManager")
# Check for temp file
self.temp_file = os.path.join(self.state_dir, ".safe_state_check")
if os.path.isfile(self.temp_file):
def archive_file(filename):
import datetime
filepath = os.path.join(self.state_dir, filename)
filepath_bak = state_filepath + ".bak"
archive_dir = os.path.join(get_config_dir(), "archive")
if not os.path.exists(archive_dir):
os.makedirs(archive_dir)
for _filepath in (filepath, filepath_bak):
timestamp = datetime.datetime.now().replace(microsecond=0).isoformat().replace(':', '-')
archive_filepath = os.path.join(archive_dir, filename + "-" + timestamp)
try:
shutil.copy2(_filepath, archive_filepath)
except IOError:
log.error("Unable to archive: %s", filename)
else:
log.info("Archive of %s successful: %s", filename, archive_filepath)
log.warning("Potential bad shutdown of Deluge detected, archiving torrent state files...")
archive_file("torrents.state")
archive_file("torrents.fastresume")
else:
with file(self.temp_file, 'a'):
os.utime(self.temp_file, None)
# Run the old state upgrader before loading state # Run the old state upgrader before loading state
deluge.core.oldstateupgrader.OldStateUpgrader() deluge.core.oldstateupgrader.OldStateUpgrader()
@ -246,7 +275,13 @@ class TorrentManager(component.Component):
# Stop the status cleanup LoopingCall here # Stop the status cleanup LoopingCall here
self.torrents[key].prev_status_cleanup_loop.stop() self.torrents[key].prev_status_cleanup_loop.stop()
return self.save_resume_data(self.torrents.keys()) def remove_temp_file(result):
if result and os.path.isfile(self.temp_file):
os.remove(self.temp_file)
d = self.save_resume_data(self.torrents.keys())
d.addCallback(remove_temp_file)
return d
def update(self): def update(self):
for torrent_id, torrent in self.torrents.items(): for torrent_id, torrent in self.torrents.items():
@ -301,8 +336,7 @@ class TorrentManager(component.Component):
"""Returns an entry with the resume data or None""" """Returns an entry with the resume data or None"""
fastresume = "" fastresume = ""
try: try:
_file = open(os.path.join(get_config_dir(), "state", _file = open(os.path.join(self.state_dir, torrent_id + ".fastresume"), "rb")
torrent_id + ".fastresume"), "rb")
fastresume = _file.read() fastresume = _file.read()
_file.close() _file.close()
except IOError, e: except IOError, e:
@ -312,8 +346,7 @@ class TorrentManager(component.Component):
def legacy_delete_resume_data(self, torrent_id): def legacy_delete_resume_data(self, torrent_id):
"""Deletes the .fastresume file""" """Deletes the .fastresume file"""
path = os.path.join(get_config_dir(), "state", path = os.path.join(self.state_dir, torrent_id + ".fastresume")
torrent_id + ".fastresume")
log.debug("Deleting fastresume file: %s", path) log.debug("Deleting fastresume file: %s", path)
try: try:
os.remove(path) os.remove(path)
@ -501,9 +534,7 @@ class TorrentManager(component.Component):
# Write the .torrent file to the state directory # Write the .torrent file to the state directory
if filedump: if filedump:
try: try:
save_file = open(os.path.join(get_config_dir(), "state", save_file = open(os.path.join(self.state_dir, torrent.torrent_id + ".torrent"), "wb")
torrent.torrent_id + ".torrent"),
"wb")
save_file.write(filedump) save_file.write(filedump)
save_file.close() save_file.close()
except IOError, e: except IOError, e:
@ -546,10 +577,7 @@ class TorrentManager(component.Component):
# Get the torrent data from the torrent file # Get the torrent data from the torrent file
try: try:
log.debug("Attempting to open %s for add.", torrent_id) log.debug("Attempting to open %s for add.", torrent_id)
_file = open( _file = open(os.path.join(self.state_dir, torrent_id + ".torrent"), "rb")
os.path.join(
get_config_dir(), "state", torrent_id + ".torrent"),
"rb")
filedump = lt.bdecode(_file.read()) filedump = lt.bdecode(_file.read())
_file.close() _file.close()
except (IOError, RuntimeError), e: except (IOError, RuntimeError), e:
@ -636,16 +664,24 @@ class TorrentManager(component.Component):
def load_state(self): def load_state(self):
"""Load the state of the TorrentManager from the torrents.state file""" """Load the state of the TorrentManager from the torrents.state file"""
state = TorrentManagerState() filename = "torrents.state"
filepath = os.path.join(self.state_dir, filename)
filepath_bak = filepath + ".bak"
try: for _filepath in (filepath, filepath_bak):
log.debug("Opening torrent state file for load.") log.info("Opening %s for load: %s", filename, _filepath)
state_file = open( try:
os.path.join(get_config_dir(), "state", "torrents.state"), "rb") with open(_filepath, "rb") as _file:
state = cPickle.load(state_file) state = cPickle.load(_file)
state_file.close() except (IOError, EOFError, cPickle.UnpicklingError), ex:
except (EOFError, IOError, Exception, cPickle.UnpicklingError), e: log.warning("Unable to load %s: %s", _filepath, ex)
log.warning("Unable to load state file: %s", e) state = None
else:
log.info("Successfully loaded %s: %s", filename, _filepath)
break
if state is None:
state = TorrentManagerState()
# Try to use an old state # Try to use an old state
try: try:
@ -727,28 +763,29 @@ class TorrentManager(component.Component):
) )
state.torrents.append(torrent_state) state.torrents.append(torrent_state)
# Pickle the TorrentManagerState object filename = "torrents.state"
try: filepath = os.path.join(self.state_dir, filename)
log.debug("Saving torrent state file.") filepath_bak = filepath + ".bak"
state_file = open(os.path.join(get_config_dir(),
"state", "torrents.state.new"), "wb")
cPickle.dump(state, state_file)
state_file.flush()
os.fsync(state_file.fileno())
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
return True
# We have to move the 'torrents.state.new' file to 'torrents.state'
try: try:
shutil.move( if os.path.isfile(filepath):
os.path.join(get_config_dir(), "state", "torrents.state.new"), log.info("Creating backup of %s at: %s", filename, filepath_bak)
os.path.join(get_config_dir(), "state", "torrents.state")) shutil.copy2(filepath, filepath_bak)
except IOError: except IOError as ex:
log.warning("Unable to save state file.") log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
return True else:
log.info("Saving the %s at: %s", filename, filepath)
try:
with open(filepath, "wb") as _file:
# Pickle the TorrentManagerState object
cPickle.dump(state, _file)
_file.flush()
os.fsync(_file.fileno())
except (IOError, cPickle.PicklingError) as ex:
log.error("Unable to save %s: %s", filename, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
shutil.move(filepath_bak, filepath)
# We return True so that the timer thread will continue # We return True so that the timer thread will continue
return True return True
@ -760,7 +797,6 @@ class TorrentManager(component.Component):
:returns: A Deferred whose callback will be invoked when save is complete :returns: A Deferred whose callback will be invoked when save is complete
:rtype: twisted.internet.defer.Deferred :rtype: twisted.internet.defer.Deferred
""" """
if torrent_ids is None: if torrent_ids is None:
torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data()) torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data())
@ -779,43 +815,63 @@ class TorrentManager(component.Component):
def on_all_resume_data_finished(result): def on_all_resume_data_finished(result):
if result: if result:
self.save_resume_data_file() if self.save_resume_data_file():
return True
return DeferredList(deferreds).addBoth(on_all_resume_data_finished) return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
def load_resume_data_file(self): def load_resume_data_file(self):
resume_data = {} filename = "torrents.fastresume"
try: filepath = os.path.join(self.state_dir, filename)
log.debug("Opening torrents fastresume file for load.") filepath_bak = filepath + ".bak"
fastresume_file = open(os.path.join(get_config_dir(), "state", old_data_filepath = os.path.join(get_config_dir(), filename)
"torrents.fastresume"), "rb")
resume_data = lt.bdecode(fastresume_file.read())
fastresume_file.close()
except (EOFError, IOError, Exception), e:
log.warning("Unable to load fastresume file: %s", e)
for _filepath in (filepath, filepath_bak, old_data_filepath):
log.info("Opening %s for load: %s", filename, _filepath)
try:
with open(_filepath, "rb") as _file:
resume_data = lt.bdecode(_file.read())
except (IOError, EOFError, RuntimeError), ex:
log.warning("Unable to load %s: %s", _filepath, ex)
resume_data = None
else:
log.info("Successfully loaded %s: %s", filename, _filepath)
break
# If the libtorrent bdecode doesn't happen properly, it will return None # If the libtorrent bdecode doesn't happen properly, it will return None
# so we need to make sure we return a {} # so we need to make sure we return a {}
if resume_data is None: if resume_data is None:
return {} return {}
else:
return resume_data return resume_data
def save_resume_data_file(self): def save_resume_data_file(self):
""" """
Saves the resume data file with the contents of self.resume_data. Saves the resume data file with the contents of self.resume_data.
""" """
path = os.path.join(get_config_dir(), "state", "torrents.fastresume") filename = "torrents.fastresume"
filepath = os.path.join(self.state_dir, filename)
filepath_bak = filepath + ".bak"
try: try:
log.debug("Saving fastresume file: %s", path) if os.path.isfile(filepath):
fastresume_file = open(path, "wb") log.info("Creating backup of %s at: %s", filename, filepath_bak)
fastresume_file.write(lt.bencode(self.resume_data)) shutil.copy2(filepath, filepath_bak)
fastresume_file.flush() except IOError as ex:
os.fsync(fastresume_file.fileno()) log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
fastresume_file.close() else:
except IOError: log.info("Saving the %s at: %s", filename, filepath)
log.warning("Error trying to save fastresume file") try:
with open(filepath, "wb") as _file:
_file.write(lt.bencode(self.session.save_state()))
_file.flush()
os.fsync(_file.fileno())
except (IOError, EOFError) as ex:
log.error("Unable to save %s: %s", filename, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
shutil.move(filepath_bak, filepath)
else:
return True
def get_queue_position(self, torrent_id): def get_queue_position(self, torrent_id):
"""Get queue position of torrent""" """Get queue position of torrent"""

View File

@ -41,6 +41,8 @@ import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import logging import logging
import cPickle
import shutil
import deluge.component as component import deluge.component as component
import deluge.common import deluge.common
@ -264,3 +266,53 @@ def associate_magnet_links(overwrite=False):
log.error("Unable to register Deluge as default magnet uri handler.") log.error("Unable to register Deluge as default magnet uri handler.")
return False return False
return False return False
def save_pickled_state_file(filename, state):
"""Save a file in the config directory and creates a backup
filename: Filename to be saved to config
state: The data to be pickled and written to file
"""
from deluge.configmanager import get_config_dir
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
filepath_bak = filepath + ".bak"
try:
if os.path.isfile(filepath):
log.info("Creating backup of %s at: %s", filename, filepath_bak)
shutil.copy2(filepath, filepath_bak)
except IOError as ex:
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
else:
log.info("Saving the %s at: %s", filename, filepath)
try:
with open(filepath, "wb") as _file:
# Pickle the state object
cPickle.dump(state, _file)
_file.flush()
os.fsync(_file.fileno())
except (IOError, EOFError, cPickle.PicklingError) as ex:
log.error("Unable to save %s: %s", filename, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
shutil.move(filepath_bak, filepath)
def load_pickled_state_file(filename):
"""Loads a file from the config directory, attempting backup if original fails to load.
filename: Filename to be loaded from config
returns unpickled state
"""
from deluge.configmanager import get_config_dir
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
filepath_bak = filepath + ".bak"
old_data_filepath = os.path.join(get_config_dir(), filename)
for _filepath in (filepath, filepath_bak, old_data_filepath):
log.info("Opening %s for load: %s", filename, _filepath)
try:
with open(_filepath, "rb") as _file:
state = cPickle.load(_file)
except (IOError, cPickle.UnpicklingError), ex:
log.warning("Unable to load %s: %s", _filepath, ex)
else:
log.info("Successfully loaded %s: %s", filename, _filepath)
return state

View File

@ -38,15 +38,14 @@ import gtk
import gtk.gdk import gtk.gdk
import gobject import gobject
import os.path import os.path
import cPickle
import logging import logging
import cPickle
from deluge.ui.gtkui.torrentdetails import Tab from deluge.ui.gtkui.torrentdetails import Tab
from deluge.ui.client import client from deluge.ui.client import client
import deluge.configmanager
import deluge.component as component import deluge.component as component
import deluge.common import deluge.common
import common from deluge.ui.gtkui.common import reparent_iter, save_pickled_state_file, load_pickled_state_file
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -242,7 +241,6 @@ class FilesTab(Tab):
getattr(widget, attr)() getattr(widget, attr)()
def save_state(self): def save_state(self):
filename = "files_tab.state"
# Get the current sort order of the view # Get the current sort order of the view
column_id, sort_order = self.treestore.get_sort_column_id() column_id, sort_order = self.treestore.get_sort_column_id()
@ -259,30 +257,10 @@ class FilesTab(Tab):
"width": column.get_width() "width": column.get_width()
} }
# Get the config location for saving the state file save_pickled_state_file("files_tab.state", state)
config_location = deluge.configmanager.get_config_dir()
try:
log.debug("Saving FilesTab state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "wb")
cPickle.dump(state, state_file)
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
def load_state(self): def load_state(self):
filename = "files_tab.state" state = load_pickled_state_file("files_tabs.state")
# Get the config location for loading the state file
config_location = deluge.configmanager.get_config_dir()
state = None
try:
log.debug("Loading FilesTab state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "rb")
state = cPickle.load(state_file)
state_file.close()
except (EOFError, IOError, AttributeError, cPickle.UnpicklingError), e:
log.warning("Unable to load state file: %s", e)
if state == None: if state == None:
return return
@ -807,14 +785,14 @@ class FilesTab(Tab):
return return
if new_folder_iter: if new_folder_iter:
# This means that a folder by this name already exists # This means that a folder by this name already exists
common.reparent_iter(self.treestore, self.treestore.iter_children(old_folder_iter), new_folder_iter) reparent_iter(self.treestore, self.treestore.iter_children(old_folder_iter), new_folder_iter)
else: else:
parent = old_folder_iter_parent parent = old_folder_iter_parent
for ns in new_split[:-1]: for ns in new_split[:-1]:
parent = self.treestore.append(parent, [ns + "/", 0, "", 0, 0, -1, gtk.STOCK_DIRECTORY]) parent = self.treestore.append(parent, [ns + "/", 0, "", 0, 0, -1, gtk.STOCK_DIRECTORY])
self.treestore[old_folder_iter][0] = new_split[-1] + "/" self.treestore[old_folder_iter][0] = new_split[-1] + "/"
common.reparent_iter(self.treestore, old_folder_iter, parent) reparent_iter(self.treestore, old_folder_iter, parent)
# We need to check if the old_folder_iter_parent no longer has children # We need to check if the old_folder_iter_parent no longer has children
# and if so, we delete it # and if so, we delete it

View File

@ -40,6 +40,7 @@ gobject.set_prgname("deluge")
from twisted.internet import gtk2reactor from twisted.internet import gtk2reactor
reactor = gtk2reactor.install() reactor = gtk2reactor.install()
import os
import gtk import gtk
import sys import sys
import logging import logging
@ -71,14 +72,13 @@ from queuedtorrents import QueuedTorrents
from addtorrentdialog import AddTorrentDialog from addtorrentdialog import AddTorrentDialog
from deluge.ui.sessionproxy import SessionProxy from deluge.ui.sessionproxy import SessionProxy
import dialogs import dialogs
import common from deluge.ui.gtkui.common import associate_magnet_links
from deluge.configmanager import ConfigManager, get_config_dir
import deluge.configmanager
import deluge.common import deluge.common
import deluge.error import deluge.error
from deluge.ui.ui import _UI from deluge.ui.ui import _UI
class Gtk(_UI): class Gtk(_UI):
help = """Starts the Deluge GTK+ interface""" help = """Starts the Deluge GTK+ interface"""
@ -90,6 +90,7 @@ class Gtk(_UI):
super(Gtk, self).start() super(Gtk, self).start()
GtkUI(self.args) GtkUI(self.args)
def start(): def start():
Gtk().start() Gtk().start()
@ -152,6 +153,7 @@ DEFAULT_PREFS = {
"focus_main_window_on_add": True, "focus_main_window_on_add": True,
} }
class GtkUI(object): class GtkUI(object):
def __init__(self, args): def __init__(self, args):
self.daemon_bps = (0,0,0) self.daemon_bps = (0,0,0)
@ -192,10 +194,14 @@ class GtkUI(object):
# Attempt to register a magnet URI handler with gconf, but do not overwrite # Attempt to register a magnet URI handler with gconf, but do not overwrite
# if already set by another program. # if already set by another program.
common.associate_magnet_links(False) associate_magnet_links(False)
# Make sure gtkui.conf has at least the defaults set # Make sure gtkui.conf has at least the defaults set
self.config = deluge.configmanager.ConfigManager("gtkui.conf", DEFAULT_PREFS) self.config = ConfigManager("gtkui.conf", DEFAULT_PREFS)
# Make sure the gtkui state folder has been created
if not os.path.exists(os.path.join(get_config_dir(), "gtkui_state")):
os.makedirs(os.path.join(get_config_dir(), "gtkui_state"))
# We need to check on exit if it was started in classic mode to ensure we # We need to check on exit if it was started in classic mode to ensure we
# shutdown the daemon. # shutdown the daemon.
@ -370,7 +376,7 @@ Please see the details below for more information."), details=traceback.format_e
if self.config["autostart_localhost"] and host in ("localhost", "127.0.0.1"): if self.config["autostart_localhost"] and host in ("localhost", "127.0.0.1"):
log.debug("Autostarting localhost:%s", host) log.debug("Autostarting localhost:%s", host)
try_connect = client.start_daemon( try_connect = client.start_daemon(
port, deluge.configmanager.get_config_dir() port, get_config_dir()
) )
log.debug("Localhost started: %s", try_connect) log.debug("Localhost started: %s", try_connect)
if not try_connect: if not try_connect:

View File

@ -34,19 +34,13 @@
# #
# #
import cPickle
import os.path
import logging import logging
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import gettext
from deluge.configmanager import ConfigManager
import deluge.configmanager
import deluge.common import deluge.common
from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
from gobject import signal_new, SIGNAL_RUN_LAST, TYPE_NONE from gobject import signal_new, SIGNAL_RUN_LAST, TYPE_NONE
from gtk import gdk from gtk import gdk
@ -333,34 +327,11 @@ class ListView:
state.append(self.create_column_state(column, counter)) state.append(self.create_column_state(column, counter))
state += self.removed_columns_state state += self.removed_columns_state
save_pickled_state_file(filename, state)
# Get the config location for saving the state file
config_location = deluge.configmanager.get_config_dir()
try:
log.debug("Saving ListView state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "wb")
cPickle.dump(state, state_file)
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
def load_state(self, filename): def load_state(self, filename):
"""Load the listview state from filename.""" """Load the listview state from filename."""
# Get the config location for loading the state file self.state = load_pickled_state_file(filename)
config_location = deluge.configmanager.get_config_dir()
state = None
try:
log.debug("Loading ListView state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "rb")
state = cPickle.load(state_file)
state_file.close()
except (EOFError, IOError, cPickle.UnpicklingError), e:
log.warning("Unable to load state file: %s", e)
# Keep the state in self.state so we can access it as we add new columns
self.state = state
def set_treeview(self, treeview_widget): def set_treeview(self, treeview_widget):
"""Set the treeview widget that this listview uses.""" """Set the treeview widget that this listview uses."""

View File

@ -36,16 +36,15 @@
import gtk import gtk
import logging import logging
import os.path import os.path
import cPickle
from itertools import izip from itertools import izip
from deluge.ui.client import client from deluge.ui.client import client
import deluge.configmanager
import deluge.component as component import deluge.component as component
import deluge.common import deluge.common
from deluge.ui.gtkui.listview import cell_data_speed as cell_data_speed from deluge.ui.gtkui.listview import cell_data_speed as cell_data_speed
from deluge.ui.gtkui.torrentdetails import Tab from deluge.ui.gtkui.torrentdetails import Tab
from deluge.ui.countries import COUNTRIES from deluge.ui.countries import COUNTRIES
from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -171,7 +170,6 @@ class PeersTab(Tab):
self.torrent_id = None self.torrent_id = None
def save_state(self): def save_state(self):
filename = "peers_tab.state"
# Get the current sort order of the view # Get the current sort order of the view
column_id, sort_order = self.liststore.get_sort_column_id() column_id, sort_order = self.liststore.get_sort_column_id()
@ -187,31 +185,10 @@ class PeersTab(Tab):
"position": index, "position": index,
"width": column.get_width() "width": column.get_width()
} }
save_pickled_state_file("peers_tab.state", state)
# Get the config location for saving the state file
config_location = deluge.configmanager.get_config_dir()
try:
log.debug("Saving FilesTab state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "wb")
cPickle.dump(state, state_file)
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
def load_state(self): def load_state(self):
filename = "peers_tab.state" state = load_pickled_state_file("peers_tabs.state")
# Get the config location for loading the state file
config_location = deluge.configmanager.get_config_dir()
state = None
try:
log.debug("Loading PeersTab state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "rb")
state = cPickle.load(state_file)
state_file.close()
except (EOFError, IOError, AttributeError, cPickle.UnpicklingError), e:
log.warning("Unable to load state file: %s", e)
if state == None: if state == None:
return return

View File

@ -37,15 +37,11 @@
"""The torrent details component shows info about the selected torrent.""" """The torrent details component shows info about the selected torrent."""
import gtk import gtk
import os
import os.path
import cPickle
import logging import logging
import deluge.component as component import deluge.component as component
from deluge.ui.client import client from deluge.ui.client import client
from deluge.configmanager import ConfigManager from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
import deluge.configmanager
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -411,8 +407,6 @@ class TorrentDetails(component.Component):
def save_state(self): def save_state(self):
"""We save the state, which is basically the tab_index list""" """We save the state, which is basically the tab_index list"""
filename = "tabs.state"
#Update the visiblity status of all tabs #Update the visiblity status of all tabs
#Leave tabs we dont know anything about it the state as they #Leave tabs we dont know anything about it the state as they
#might come from a plugin #might come from a plugin
@ -423,29 +417,7 @@ class TorrentDetails(component.Component):
log.debug("Set to %s %d" % self.state[i]) log.debug("Set to %s %d" % self.state[i])
state = self.state state = self.state
# Get the config location for saving the state file save_pickled_state_file("tabs.state", state)
config_location = deluge.configmanager.get_config_dir()
try:
log.debug("Saving TorrentDetails state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "wb")
cPickle.dump(state, state_file)
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
def load_state(self): def load_state(self):
filename = "tabs.state" return load_pickled_state_file("tabs.state")
# Get the config location for loading the state file
config_location = deluge.configmanager.get_config_dir()
state = None
try:
log.debug("Loading TorrentDetails state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "rb")
state = cPickle.load(state_file)
state_file.close()
except (EOFError, IOError, cPickle.UnpicklingError), e:
log.warning("Unable to load state file: %s", e)
return state