kuuko pushed a commit to branch master.

http://git.enlightenment.org/apps/epour.git/commit/?id=0b1b72fcacd532d47eb0b47b1bb66e6bcad7c20a

commit 0b1b72fcacd532d47eb0b47b1bb66e6bcad7c20a
Author: Kai Huuhko <[email protected]>
Date:   Thu Aug 4 18:23:02 2016 +0300

    Change internals to work with newer libtorrent versions
    
    This will once again change the save format of the torrent list, hopefully 
for the last
    time.
---
 README                       |  23 +-
 TODO                         |   4 +-
 bin/epour                    |  12 +-
 epour/Epour.py               |  86 ++----
 epour/gui/TorrentSelector.py |  63 ++--
 epour/gui/Widgets.py         |  41 ++-
 epour/gui/__init__.py        | 351 +++++++++++-----------
 epour/session.py             | 683 +++++++++++++++++++++++++++++++------------
 po/epour.pot                 | 264 ++++++++---------
 9 files changed, 890 insertions(+), 637 deletions(-)

diff --git a/README b/README
index 007012d..dd4a30a 100644
--- a/README
+++ b/README
@@ -2,22 +2,18 @@
 REQUIREMENTS
 ============
 
-* libtorrent-rasterbar 1.0 or later, currently tested and developed with
-  version 1.0.5
+* libtorrent-rasterbar 1.2.0 or later, currently tested and developed with
+  version 1.2.0
 
-    http://www.libtorrent.org/
+    https://libtorrent.org/
 
-* Enlightenment Foundation Libraries 1.8 or later
+* Python-EFL 1.15 or later
 
     https://www.enlightenment.org/download
 
-* Python-EFL 1.8 or later
+* Python 3.2 or later
 
-    https://www.enlightenment.org/download
-
-* Python 2.6 or later, 3.2 or later
-
-    http://www.python.org/
+    https://www.python.org/
 
 * python-distutils-extra
 
@@ -25,11 +21,11 @@ REQUIREMENTS
 
 * dbus-python
 
-    http://www.freedesktop.org/wiki/Software/DBusBindings/#dbus-python
+    https://www.freedesktop.org/wiki/Software/DBusBindings/#dbus-python
 
 * python-xdg / pyxdg
 
-    http://www.freedesktop.org/wiki/Software/pyxdg/
+    https://www.freedesktop.org/wiki/Software/pyxdg/
 
 =======
 INSTALL
@@ -43,5 +39,4 @@ CONTACT/BUGS
 
 Email:          [email protected]
 Mailing list:   [email protected]
-Forums:         http://forums.bodhilinux.com/index.php?/topic/7722-epour/
-Launchpad:      https://launchpad.net/epour
+Bugs:           https://phab.enlightenment.org/maniphest/ (set Tags-field to 
Epour)
diff --git a/TODO b/TODO
index 7179ed0..463bb03 100644
--- a/TODO
+++ b/TODO
@@ -39,7 +39,7 @@ Misc:
  ☐ Moving finished torrents to a different folder
    ☐ Individual torrent move when finished
      Can this setting be saved to torrent dicts user data?
- ☐ Save torrent & resume data periodically
+ ✔ Save torrent & resume data periodically @done (16-07-30 08:05)
    Will this accomplish anything?
    http://libtorrent.org/reference-Core.html#save_resume_data()
    http://libtorrent.org/reference-Core.html#need_save_resume_data()
@@ -47,7 +47,7 @@ Misc:
  ☐ Total count of torrents in session status
  ☐ Local search function for torrents, files?
  ☐ Web search
- ☐ Handle switching between py2 <--> py3, messes up with pickled list of 
torrents
+ ✘ Handle switching between py2 <--> py3, messes up with pickled list of 
torrents @cancelled (16-07-30 08:15)
     File "/usr/bin/epour", line 7, in <module>
     app = Epour()
     File "/usr/lib/python2.7/dist-packages/epour/Epour.py", line 89, in 
__init__
diff --git a/bin/epour b/bin/epour
index 72519f8..1890371 100755
--- a/bin/epour
+++ b/bin/epour
@@ -1,10 +1,14 @@
-#!/usr/bin/python2
+#!/usr/bin/python
 
 import logging
 
 from epour.Epour import Epour
 
-app = Epour()
-app.gui.run()
-app.quit()
+epour = Epour()
+epour.gui.run_mainloop()
+try:
+    epour.save_conf()
+except Exception:
+    epour.log.exception("Saving conf failed")
+
 logging.shutdown()
diff --git a/epour/Epour.py b/epour/Epour.py
index 17cb8a4..9d28418 100644
--- a/epour/Epour.py
+++ b/epour/Epour.py
@@ -2,7 +2,7 @@
 #
 #  Epour - A bittorrent client using EFL and libtorrent
 #
-#  Copyright 2012-2014 Kai Huuhko <[email protected]>
+#  Copyright 2012-2016 Kai Huuhko <[email protected]>
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -27,8 +27,7 @@ parser.add_argument(
     '-v', '--verbose', action="count", help="max is -vvv")
 parser.add_argument(
     '--disable-add-dialog', action="store_true",
-    help="Torrents to be added from arguments don't open a dialog"
-    )
+    help="Torrents to be added from arguments don't open a dialog")
 parser.add_argument(
     'torrents', nargs="*", help="file path, magnet uri, or info hash",
     metavar="TORRENT")
@@ -72,11 +71,13 @@ from .gui import MainInterface
 
 
 class Epour(object):
+
     def __init__(self):
         self.log = self.setup_log()
+        self.log.debug("Logging started")
         self.conf = self.setup_conf()
 
-        session = self.session = Session(self.conf)
+        session = self.session = Session(self.conf, self._quit_cb)
         session.load_state()
 
         self.gui = MainInterface(self, session)
@@ -93,16 +94,7 @@ class Epour(object):
                     self.conf.getboolean("Settings", "add_dialog_enabled"):
                 self.gui.add_torrent(t)
             else:
-                add_dict = {
-                    "save_path": self.conf.get("Settings", "storage_path"),
-                    "flags": 592
-                    }
-                if os.path.isfile(t):
-                    self.session.add_torrent_from_file(add_dict, t)
-                elif t.startswith("magnet:"):
-                    self.session.add_torrent_from_magnet(add_dict, t)
-                else:
-                    self.session.add_torrent_from_hash(add_dict, t)
+                self.session.add_torrent_with_uri(t)
 
     def setup_log(self):
         log_level = logging.ERROR
@@ -115,18 +107,15 @@ class Epour(object):
 
         ch = logging.StreamHandler()
         ch_formatter = logging.Formatter(
-            '%(name)s: [%(levelname)s] %(message)s'
-            )
+            '%(name)s: [%(levelname)s] %(message)s (%(filename)s: %(lineno)d)')
         ch.setFormatter(ch_formatter)
         ch.setLevel(log_level)
         log.addHandler(ch)
 
         fh = logging.FileHandler(
-            os.path.join(save_data_path("epour"), "epour.log")
-            )
+            os.path.join(save_data_path("epour"), "epour.log"))
         fh_formatter = logging.Formatter(
-            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
-            )
+            '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
         fh.setFormatter(fh_formatter)
         fh.setLevel(logging.ERROR)
         log.addHandler(fh)
@@ -167,25 +156,8 @@ class Epour(object):
             with open(conf_path, 'wb') as configfile:
                 self.conf.write(configfile)
 
-    def quit(self):
-        session = self.session
-
-        session.pause()
-
-        try:
-            session.save_torrents()
-        except:
-            self.log.exception("Saving torrents failed")
-
-        try:
-            session.save_state()
-        except:
-            self.log.exception("Saving session state failed")
-
-        try:
-            self.save_conf()
-        except:
-            self.log.exception("Saving conf failed")
+    def _quit_cb(self):
+        self.gui.stop_mainloop()
 
 
 class EpourDBus(dbus.service.Object):
@@ -198,8 +170,7 @@ class EpourDBus(dbus.service.Object):
         self.conf = conf
         dbus.service.Object.__init__(
             self, dbus.SessionBus(),
-            "/net/launchpad/epour", "net.launchpad.epour"
-            )
+            "/net/launchpad/epour", "net.launchpad.epour")
 
         self.props = {
         }
@@ -212,16 +183,7 @@ class EpourDBus(dbus.service.Object):
             if self.conf.getboolean("Settings", "add_dialog_enabled"):
                 self.gui.add_torrent(t)
             else:
-                add_dict = {
-                    "save_path": self.conf.get("Settings", "storage_path"),
-                    "flags": 592
-                    }
-                if t.startswith("magnet:"):
-                    self.session.add_torrent_from_magnet(add_dict, t)
-                elif os.path.isfile(t):
-                    self.session.add_torrent_from_file(add_dict, t)
-                else:
-                    self.session.add_torrent_from_hash(add_dict, t)
+                self.session.add_torrent_with_uri(t)
         except Exception:
             self.log.exception("Error while adding torrent from dbus")
 
@@ -230,16 +192,7 @@ class EpourDBus(dbus.service.Object):
     def AddTorrentWithoutDialog(self, t):
         self.log.info("Adding %s from dbus" % t)
         try:
-            add_dict = {
-                "save_path": self.conf.get("Settings", "storage_path"),
-                "flags": 592
-                }
-            if t.startswith("magnet:"):
-                self.session.add_torrent_from_magnet(add_dict, t)
-            elif os.path.isfile(t):
-                self.session.add_torrent_from_file(add_dict, t)
-            else:
-                self.session.add_torrent_from_hash(add_dict, t)
+            self.session.add_torrent_with_uri(t)
         except Exception:
             self.log.exception("Error while adding torrent from dbus")
 
@@ -247,13 +200,16 @@ if __name__ == "__main__":
     efllog = logging.getLogger("efl")
     efllog.setLevel(logging.INFO)
     efllog_formatter = logging.Formatter(
-        '%(name)s: [%(levelname)s] %(message)s'
-        )
+        '%(name)s: [%(levelname)s] %(message)s')
     efllog_handler = logging.StreamHandler()
     efllog_handler.setFormatter(efllog_formatter)
     efllog.addHandler(efllog_handler)
 
     epour = Epour()
-    epour.gui.run()
-    epour.quit()
+    epour.gui.run_mainloop()
+    try:
+        epour.save_conf()
+    except Exception:
+        epour.log.exception("Saving conf failed")
+
     logging.shutdown()
diff --git a/epour/gui/TorrentSelector.py b/epour/gui/TorrentSelector.py
index 01142d7..ab19d48 100644
--- a/epour/gui/TorrentSelector.py
+++ b/epour/gui/TorrentSelector.py
@@ -1,7 +1,7 @@
 #
 #  Epour - A bittorrent client using EFL and libtorrent
 #
-#  Copyright 2012-2014 Kai Huuhko <[email protected]>
+#  Copyright 2012-2016 Kai Huuhko <[email protected]>
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -26,18 +26,18 @@ from libtorrent import add_torrent_params_flags_t
 
 from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
 from efl import elementary as elm
-from efl.elementary.window import StandardWindow, Window, ELM_WIN_DIALOG_BASIC
-from efl.elementary.background import Background
-from efl.elementary.button import Button
-from efl.elementary.box import Box
-from efl.elementary.frame import Frame
-from efl.elementary.fileselector import Fileselector
-from efl.elementary.fileselector_entry import FileselectorEntry
-from efl.elementary.entry import Entry, markup_to_utf8, utf8_to_markup
-from efl.elementary.check import Check
-from efl.elementary.scroller import Scroller
-from efl.elementary.spinner import Spinner
-# from efl.elementary.object import ELM_SEL_TYPE_CLIPBOARD, \
+from efl.elementary import StandardWindow, Window, ELM_WIN_DIALOG_BASIC
+from efl.elementary import Background
+from efl.elementary import Button
+from efl.elementary import Box
+from efl.elementary import Frame
+from efl.elementary import Fileselector
+from efl.elementary import FileselectorEntry
+from efl.elementary import Entry, markup_to_utf8, utf8_to_markup
+from efl.elementary import Check
+from efl.elementary import Scroller
+from efl.elementary import Spinner
+# from efl.elementary import ELM_SEL_TYPE_CLIPBOARD, \
 #     ELM_SEL_FORMAT_TEXT
 
 from .Widgets import UnitSpinner
@@ -47,7 +47,7 @@ EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
 FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
 FILL_HORIZ = EVAS_HINT_FILL, 0.5
 
-from efl.elementary.configuration import Configuration
+from efl.elementary import Configuration
 
 elm_conf = Configuration()
 scale = elm_conf.scale
@@ -287,8 +287,8 @@ always used.'''
             opt_box.pack_end(e)
             e.show()
 
-        def fs_cb(fs, key):
-            self.add_dict[key] = fs.path
+        def fs_cb(fs):
+            self.add_dict["save_path"] = fs.path
 
         # Downloaded data is saved in this path
         title = _("Data Save Path")
@@ -296,8 +296,9 @@ always used.'''
             opt_box, size_hint_align=FILL_HORIZ, text=title,
             folder_only=True, expandable=False
             )
-        save_path.path = session.conf.get("Settings", "storage_path")
-        save_path.callback_changed_add(fs_cb, "save_path")
+        save_path.callback_changed_add(fs_cb)
+        path = session.conf.get("Settings", "storage_path").strip()
+        save_path.path = path
         opt_box.pack_end(save_path)
         save_path.show()
 
@@ -376,8 +377,7 @@ always used.'''
 
         bbox = Box(
             box, size_hint_weight=EXPAND_HORIZ,
-            size_hint_align=FILL_HORIZ, horizontal=True
-            )
+            size_hint_align=FILL_HORIZ, horizontal=True)
 
         ok_btn = Button(bbox, text=_("Ok"), size_hint_align=(1.0, 0.5))
         bbox.pack_end(ok_btn)
@@ -401,28 +401,13 @@ always used.'''
 
             uri = markup_to_utf8(uri)
 
-            if uri.startswith("magnet:"):
-                log.debug("Adding torrent from magnet link: %s", uri)
-                session.add_torrent_from_magnet(add_dict, uri)
-                self.delete()
-                return
-            elif uri.startswith("file://"):
-                uri = uri[7:]
+            session.fill_add_dict_based_on_uri(add_dict, uri)
+            session.add_torrent_with_dict(add_dict)
 
-            if os.path.isfile(uri):
-                log.debug("Adding file: %s", uri)
-                session.add_torrent_from_file(add_dict, uri)
-                self.delete()
-                return
-            else:
-                log.debug("Adding torrent from info hash: %s", uri)
-                session.add_torrent_from_hash(add_dict, uri)
-                self.delete()
-                return
+            self.delete()
 
         ok_btn.callback_clicked_add(
-            add_torrent_cb, uri_entry, session, self.add_dict
-            )
+            add_torrent_cb, uri_entry, session, self.add_dict)
         cancel_btn.callback_clicked_add(lambda x: self.delete())
 
         self.show()
diff --git a/epour/gui/Widgets.py b/epour/gui/Widgets.py
index 7aa0f06..9e1dab9 100644
--- a/epour/gui/Widgets.py
+++ b/epour/gui/Widgets.py
@@ -1,15 +1,36 @@
+#
+#  Epour - A bittorrent client using EFL and libtorrent
+#
+#  Copyright 2012-2016 Kai Huuhko <[email protected]>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 3 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+#
+
 from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, Rectangle
 from efl.ecore import Timer
-from efl.elementary.box import Box
-from efl.elementary.spinner import Spinner
-from efl.elementary.hoversel import Hoversel
-from efl.elementary.label import Label
-from efl.elementary.notify import Notify
-from efl.elementary.popup import Popup
-from efl.elementary.button import Button
-from efl.elementary.grid import Grid
-from efl.elementary.fileselector import Fileselector
-from efl.elementary.fileselector_button import FileselectorButton
+from efl.elementary import Box
+from efl.elementary import Spinner
+from efl.elementary import Hoversel
+from efl.elementary import Label
+from efl.elementary import Notify
+from efl.elementary import Popup
+from efl.elementary import Button
+from efl.elementary import Grid
+from efl.elementary import Fileselector
+from efl.elementary import FileselectorButton
 
 EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
 EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
diff --git a/epour/gui/__init__.py b/epour/gui/__init__.py
index e16c77c..cf31d1b 100644
--- a/epour/gui/__init__.py
+++ b/epour/gui/__init__.py
@@ -1,7 +1,7 @@
 #
 #  Epour - A bittorrent client using EFL and libtorrent
 #
-#  Copyright 2012-2014 Kai Huuhko <[email protected]>
+#  Copyright 2012-2016 Kai Huuhko <[email protected]>
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -34,20 +34,19 @@ from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, \
     EVAS_ASPECT_CONTROL_VERTICAL, Rectangle
 from efl.ecore import Timer
 from efl import elementary as elm
-elm.init()
-from efl.elementary.genlist import Genlist, GenlistItemClass, \
+from efl.elementary import Genlist, GenlistItemClass, \
     ELM_GENLIST_ITEM_FIELD_TEXT, ELM_GENLIST_ITEM_FIELD_CONTENT, \
     ELM_OBJECT_SELECT_MODE_NONE, ELM_OBJECT_SELECT_MODE_DEFAULT, \
     ELM_LIST_COMPRESS
-from efl.elementary.window import StandardWindow
-from efl.elementary.icon import Icon
-from efl.elementary.box import Box
-from efl.elementary.label import Label
-from efl.elementary.panel import Panel, ELM_PANEL_ORIENT_BOTTOM
-from efl.elementary.table import Table
-from efl.elementary.menu import Menu
-from efl.elementary.configuration import Configuration
-from efl.elementary.toolbar import Toolbar, ELM_TOOLBAR_SHRINK_NONE
+from efl.elementary import StandardWindow
+from efl.elementary import Icon
+from efl.elementary import Box
+from efl.elementary import Label
+from efl.elementary import Panel, ELM_PANEL_ORIENT_BOTTOM
+from efl.elementary import Table
+from efl.elementary import Menu
+from efl.elementary import Configuration
+from efl.elementary import Toolbar, ELM_TOOLBAR_SHRINK_NONE
 
 from .Widgets import ConfirmExit, Error, Information, BlockGraph
 
@@ -65,16 +64,16 @@ log = logging.getLogger("epour.gui")
 
 
 class MainInterface(object):
+
     def __init__(self, parent, session):
-        self.parent = parent
-        self.session = session
+        self._session = session
+        self.itc = TorrentClass(self._session, "double_label")
 
         self.torrentitems = {}
 
         win = self.win = StandardWindow(
             "epour", "Epour", size=(480 * scale, 400 * scale),
-            screen_constrain=True
-            )
+            screen_constrain=True)
         win.callback_delete_request_add(lambda x: self.quit())
 
         mbox = Box(win, size_hint_weight=EXPAND_BOTH)
@@ -85,17 +84,15 @@ class MainInterface(object):
         tb = Toolbar(
             win, size_hint_align=FILL_HORIZ, homogeneous=False,
             shrink_mode=ELM_TOOLBAR_SHRINK_NONE,
-            select_mode=ELM_OBJECT_SELECT_MODE_NONE
-            )
+            select_mode=ELM_OBJECT_SELECT_MODE_NONE)
         tb.menu_parent = win
 
         item = tb.item_append(
             "document-new", _("Add torrent"),
-            lambda x, y: self.add_torrent()
-            )
+            lambda x, y: self.add_torrent())
 
         def pause_session(it):
-            self.session.pause()
+            self._session.pause()
             it.state_set(it.state_next())
 
         def resume_session(it):
@@ -104,42 +101,37 @@ class MainInterface(object):
 
         item = tb.item_append(
             "media-playback-pause", _("Pause Session"),
-            lambda tb, it: pause_session(it)
-            )
+            lambda tb, it: pause_session(it))
         item.state_add(
             "media-playback-start", _("Resume Session"),
-            lambda tb, it: resume_session(it)
-            )
+            lambda tb, it: resume_session(it))
 
         def prefs_general_cb():
             from .Preferences import PreferencesGeneral
-            PreferencesGeneral(self, self.session).show()
+            PreferencesGeneral(self, self._session).show()
 
         def prefs_proxy_cb():
             from .Preferences import PreferencesProxy
-            PreferencesProxy(self, self.session).show()
+            PreferencesProxy(self, self._session).show()
 
         def prefs_session_cb():
             from .Preferences import PreferencesSession
-            PreferencesSession(self, self.session).show()
+            PreferencesSession(self, self._session).show()
 
         item = tb.item_append("preferences-system", _("Preferences"))
         item.menu = True
         item.menu.item_add(
             None, _("General"), "preferences-system",
-            lambda o, i: prefs_general_cb()
-            )
+            lambda o, i: prefs_general_cb())
         item.menu.item_add(
             None, _("Proxy"), "preferences-system",
-            lambda o, i: prefs_proxy_cb()
-            )
+            lambda o, i: prefs_proxy_cb())
         item.menu.item_add(
             None, _("Session"), "preferences-system",
-            lambda o, i: prefs_session_cb()
-            )
+            lambda o, i: prefs_session_cb())
 
         item = tb.item_append("application-exit", _("Exit"),
-                              lambda tb, it: elm.exit())
+                              lambda tb, it: self.quit())
 
         mbox.pack_start(tb)
         tb.show()
@@ -148,12 +140,12 @@ class MainInterface(object):
         self.tlist = tlist = Genlist(
             mbox, select_mode=ELM_OBJECT_SELECT_MODE_DEFAULT,
             mode=ELM_LIST_COMPRESS, size_hint_weight=EXPAND_BOTH,
-            size_hint_align=FILL_BOTH, homogeneous=True
-            )
+            size_hint_align=FILL_BOTH, homogeneous=True)
 
         def item_activated_cb(gl, item):
-            h = item.data
-            itm = ItemMenu(tlist, item, self.session, h)
+            torrent = item.data
+            handle = torrent.handle
+            itm = ItemMenu(tlist, item, self._session, handle)
             itm.focus = True
 
         tlist.callback_activated_add(item_activated_cb)
@@ -167,8 +159,8 @@ class MainInterface(object):
 
         p = Panel(
             topbox, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH,
-            color=(200, 200, 200, 200), orient=ELM_PANEL_ORIENT_BOTTOM
-            )
+            color=(200, 200, 200, 200), orient=ELM_PANEL_ORIENT_BOTTOM)
+
         p.content = SessionStatus(win, session)
         p.hidden = True
         p.show()
@@ -182,28 +174,31 @@ class MainInterface(object):
 
         def torrent_added_cb(a):
             h = a.handle
-            self.add_torrent_item(h)
+            info_hash = h.info_hash()
+            log.debug("Torrent added: %s" % str(info_hash))
+            torrent = session.torrents[str(info_hash)]
+            self.add_torrent_item(torrent)
 
         def torrent_removed_cb(a):
             self.remove_torrent_item(str(a.info_hash))
 
-        def torrent_update_cb(a):
-            self.remove_torrent_item(str(a.old_ih))
-            new_h = self.session.find_torrent(str(a.new_ih))
-            self.add_torrent_item(new_h)
+        # def torrent_update_cb(a):
+        #     self.remove_torrent_item(str(a.old_ih))
+        #     new_h = self._session.find_torrent(str(a.new_ih))
+        #     self.add_torrent_item(new_h)
 
         session.alert_manager.callback_add(
             "torrent_added_alert", torrent_added_cb)
         session.alert_manager.callback_add(
             "torrent_removed_alert", torrent_removed_cb)
-        session.alert_manager.callback_add(
-            "torrent_update_alert", torrent_update_cb)
+        # session.alert_manager.callback_add(
+        #     "torrent_update_alert", torrent_update_cb)
 
-        def torrent_paused_cb(a):
-            self.update_icon(a.handle)
+        # def torrent_paused_cb(a):
+        #     self.update_icon(a.handle)
 
-        for a_name in "torrent_paused_alert", "torrent_resumed_alert":
-            session.alert_manager.callback_add(a_name, torrent_paused_cb)
+        # for a_name in "torrent_paused_alert", "torrent_resumed_alert":
+        #     session.alert_manager.callback_add(a_name, torrent_paused_cb)
 
         def state_changed_cb(a):
             h = a.handle
@@ -211,16 +206,36 @@ class MainInterface(object):
                 log.debug("State changed for invalid handle.")
                 return
 
-            ihash = str(h.info_hash())
-            if not ihash in self.torrentitems:
-                log.debug("%s state changed but not in list", str(ihash))
+            info_hash = str(h.info_hash())
+            if info_hash not in self.torrentitems:
+                log.debug("%s state changed but not in list", str(info_hash))
                 return
 
-            self.update_icon(h)
+            torrent = self._session.torrents[info_hash]
+            torrent.state = a.state
+
+            self.torrentitems[info_hash].fields_update(
+                "elm.swallow.icon", ELM_GENLIST_ITEM_FIELD_CONTENT
+            )
 
         session.alert_manager.callback_add(
             "state_changed_alert", state_changed_cb)
 
+        # def state_update_alert_cb(a):
+        #     statuses = a.status
+        #     for status in statuses:
+        #         info_hash = str(status.info_hash)
+
+        #         if info_hash not in self.torrentitems:
+        #             return
+
+        #         self.torrentitems[info_hash].fields_update(
+        #             "*", ELM_GENLIST_ITEM_FIELD_TEXT
+        #         )
+
+        # session.alert_manager.callback_add(
+        #     "state_update_alert", state_update_alert_cb)
+
         def torrent_finished_cb(a):
             msg = _("Torrent {} has finished downloading.").format(
                 cgi.escape(a.handle.name())
@@ -234,9 +249,9 @@ class MainInterface(object):
 
     def add_torrent(self, t_uri=None):
         from .TorrentSelector import TorrentSelector
-        TorrentSelector(self.win, self.session, t_uri)
+        TorrentSelector(self.win, self._session, t_uri)
 
-    def run(self):
+    def run_mainloop(self):
         self.win.show()
 
         self.timer = Timer(1.0, self.update)
@@ -245,45 +260,27 @@ class MainInterface(object):
         self.win.callback_iconified_add(lambda x: self.timer.freeze())
         self.win.callback_normal_add(lambda x: self.timer.thaw())
         elm.run()
-        elm.shutdown()
+
+    def stop_mainloop(self):
+        elm.exit()
 
     def update(self):
         #log.debug("Torrent list TICK")
         for v in self.tlist.realized_items_get():
             v.fields_update("*", ELM_GENLIST_ITEM_FIELD_TEXT)
+        self._session.post_torrent_updates(64)
         return True
 
-    def update_icon(self, h):
-        if not h.is_valid():
-            return
-        ihash = str(h.info_hash())
-        if not ihash in self.torrentitems:
-            return
-        self.torrentitems[ihash].fields_update(
-            "elm.swallow.icon", ELM_GENLIST_ITEM_FIELD_CONTENT
-        )
-
-    def _torrent_item_tooltip_cb(self, gl, it, tooltip, h):
-        if not h.is_valid():
-            return
-
-        s = h.status(8)
-
-        if not s.has_metadata:
-            return
-
-        tt = TorrentTooltip(tooltip, h, s)
+    def _torrent_item_tooltip_cb(self, gl, it, tooltip, session, torrent):
+        return TorrentTooltip(tooltip, session, torrent)
 
-        return tt
+    def add_torrent_item(self, torrent):
+        info_hash = torrent.info_hash
 
-    def add_torrent_item(self, h):
-        ihash = str(h.info_hash())
-
-        itc = TorrentClass(self.session, "double_label")
-        item = self.tlist.item_append(itc, h)
-        item.tooltip_content_cb_set(self._torrent_item_tooltip_cb, h)
+        item = self.tlist.item_append(self.itc, torrent)
+        item.tooltip_content_cb_set(self._torrent_item_tooltip_cb, 
self._session, torrent)
         item.tooltip_window_mode_set(True)
-        self.torrentitems[ihash] = item
+        self.torrentitems[str(info_hash)] = item
 
     def remove_torrent_item(self, info_hash):
         it = self.torrentitems.pop(info_hash, None)
@@ -294,10 +291,14 @@ class MainInterface(object):
         Error(self.win, title, text)
 
     def quit(self, *args):
-        if self.session.conf.getboolean("Settings", "confirm_exit"):
-            ConfirmExit(self.win, elm.exit)
+        if self._session.conf.getboolean("Settings", "confirm_exit"):
+            ConfirmExit(self.win, self._quit_cb)
         else:
-            elm.exit()
+            self._quit_cb()
+
+    def _quit_cb(self):
+        self.win.hide()
+        self._session.shutdown()
 
 
 class SessionStatus(Table):
@@ -306,7 +307,7 @@ class SessionStatus(Table):
 
     def __init__(self, parent, session):
         Table.__init__(self, parent)
-        self.session = session
+        self._session = session
 
         s = session.status()
 
@@ -398,30 +399,29 @@ class SessionStatus(Table):
         self.update_timer = Timer(1.0, self.update)
         self.on_del_add(lambda x: self.update_timer.delete())
         self.top_widget.callback_withdrawn_add(
-            lambda x: self.update_timer.freeze()
-            )
+            lambda x: self.update_timer.freeze())
         self.top_widget.callback_iconified_add(
-            lambda x: self.update_timer.freeze()
-            )
+            lambda x: self.update_timer.freeze())
         self.top_widget.callback_normal_add(lambda x: self.update_timer.thaw())
 
     def update(self):
         #log.debug("Session status TICK")
-        s = self.session.status()
+        s = self._session.status()
+
         self.d_l.text = "{}/s".format(intrepr(s.payload_download_rate))
         self.u_l.text = "{}/s".format(intrepr(s.payload_upload_rate))
         self.peer_l.text = str(s.num_peers)
+
         t = "%s/%s" % (str(s.num_unchoked), str(s.allowed_upload_slots))
         self.uploads_l.text = t
-        self.listen_l.text = str(self.session.is_listening())
-        if self.session.is_paused():
-            icon = "media-playback-pause"
-        else:
-            icon = "media-playback-play"
+
+        self.listen_l.text = str(self._session.is_listening())
+
+        icon = "media-playback-pause" if self._session.is_paused() else 
"media-playback-play"
         try:
             self.ses_pause_ic.standard = icon
-        except Exception as e:
-            log.debug(e)
+        except Exception:
+            pass
 
         return True
 
@@ -431,54 +431,53 @@ class TorrentClass(GenlistItemClass):
     state_str = (
         _('Queued'), _('Checking'), _('Downloading metadata'),
         _('Downloading'), _('Finished'), _('Seeding'), _('Allocating'),
-        _('Checking resume data')
-        )
+        _('Checking resume data'))
 
     log = logging.getLogger("epour.gui.torrent_list")
 
     def __init__(self, session, *args, **kwargs):
         GenlistItemClass.__init__(self, *args, **kwargs)
 
-        self.session = session
+        self._session = session
 
     def text_get(self, obj, part, item_data):
-        h = item_data
+        torrent = item_data
+        handle = torrent.handle
+
+        if not handle.is_valid():
+            return _("Invalid torrent")
 
         if part == "elm.text":
-            name = h.name()
-            return '%s' % (
-                name
-            )
+            return '%s' % (torrent.status.name)
         elif part == "elm.text.sub":
-            if not h.is_valid():
-                return _("Invalid torrent")
-            s = h.status(0)
-            qp = h.queue_position()
+            status = torrent.status
+            qp = handle.queue_position()
             if qp == -1:
                 qp = "seeding"
 
-            return _("{0:.0%} complete, ETA: {1} "
+            return _(
+                "{0:.0%} complete, ETA: {1} "
                 "(Down: {2}/s Up: {3}/s Queue pos: {4})").format(
-                    s.progress,
-                    timedelta(seconds=self.get_eta(s)),
-                    intrepr(s.download_payload_rate, precision=0),
-                    intrepr(s.upload_payload_rate, precision=0),
-                    qp,
-                )
+                    status.progress,
+                    timedelta(seconds=self.get_eta(status)),
+                    intrepr(status.download_payload_rate, precision=0),
+                    intrepr(status.upload_payload_rate, precision=0),
+                    qp)
 
     def content_get(self, obj, part, item_data):
         if part != "elm.swallow.icon":
             return
 
-        h = item_data
+        torrent = item_data
+        handle = torrent.handle
 
-        if not h.is_valid():
+        if not handle.is_valid():
             return
 
-        s = h.status(0)
+        status = torrent.status
         ic = Icon(obj)
         try:
-            if h.is_paused():
+            if status.paused:
                 try:
                     ic.standard = "player_pause"
                 except Exception:
@@ -486,7 +485,7 @@ class TorrentClass(GenlistItemClass):
                         ic.standard = "media-playback-pause"
                     except Exception:
                         pass
-            elif h.is_seed():
+            elif status.is_seeding:
                 try:
                     ic.standard = "up"
                 except Exception:
@@ -504,16 +503,16 @@ class TorrentClass(GenlistItemClass):
                         pass
         except RuntimeError:
             log.debug("Setting torrent ic failed")
-        ic.tooltip_text_set(self.state_str[s.state])
+        ic.tooltip_text_set(self.state_str[torrent.state])
         ic.size_hint_aspect_set(EVAS_ASPECT_CONTROL_VERTICAL, 1, 1)
         return ic
 
     def get_eta(self, s):
-        # if self.is_finished and self.options["stop_at_ratio"]:
+        # if s.is_seeding and self.options["stop_at_ratio"]:
         #     # We're a seed, so calculate the time to the 'stop_share_ratio'
         #     if not s.upload_payload_rate:
         #         return 0
-        #     stop_ratio = self.session.settings().share_ratio_limit
+        #     stop_ratio = self._session.settings().share_ratio_limit
         #     return (
         #         (s.all_time_download * stop_ratio) -
         #         s.all_time_upload
@@ -530,6 +529,7 @@ class TorrentClass(GenlistItemClass):
 
 
 class ItemMenu(Menu):
+
     def __init__(self, parent, item, session, h):
         Menu.__init__(self, parent)
 
@@ -542,29 +542,22 @@ class ItemMenu(Menu):
         )
         q = self.item_add(None, _("Queue"), None, None)
         self.item_add(
-            q, _("Up"), None, lambda x, y: h.queue_position_up()
-            )
+            q, _("Up"), None, lambda x, y: h.queue_position_up())
         self.item_add(
-            q, _("Down"), None, lambda x, y: h.queue_position_down()
-            )
+            q, _("Down"), None, lambda x, y: h.queue_position_down())
         self.item_add(
-            q, _("Top"), None, lambda x, y: h.queue_position_top()
-            )
+            q, _("Top"), None, lambda x, y: h.queue_position_top())
         self.item_add(
-            q, _("Bottom"), None, lambda x, y: h.queue_position_bottom()
-            )
+            q, _("Bottom"), None, lambda x, y: h.queue_position_bottom())
         rem = self.item_add(
             None, _("Remove torrent"), None,
-            self.remove_torrent_cb, item, session, h, False
-            )
+            self.remove_torrent_cb, item, session, h, False)
         self.item_add(
             rem, _("and data files"), None,
-            self.remove_torrent_cb, item, session, h, True
-            )
+            self.remove_torrent_cb, item, session, h, True)
         self.item_separator_add(None)
         it = self.item_add(
-            None, _("Force reannounce"), None, lambda x, y: 
h.force_reannounce()
-            )
+            None, _("Force reannounce"), None, lambda x, y: 
h.force_reannounce())
         it.tooltip_text_set(
             "<b>Force reannounce</b> will force this torrent<br>"
             "to do another tracker request, to receive new<br>"
@@ -573,26 +566,21 @@ class ItemMenu(Menu):
             "since the last announce, the forced announce<br>"
             "will be scheduled to happen immediately as<br>"
             "the min_interval expires. This is to honor<br>"
-            "trackers minimum re-announce interval settings."
-            )
+            "trackers minimum re-announce interval settings.")
         self.item_add(
             None, _("Force DHT reannounce"), None,
-            lambda x, y: h.force_dht_announce()
-            )
+            lambda x, y: h.force_dht_announce())
         it = self.item_add(
             None, _("Scrape tracker"), None,
-            lambda x, y: h.scrape_tracker()
-            )
+            lambda x, y: h.scrape_tracker())
         it.tooltip_text_set(
             "<b>Scrape tracker</b> will send a scrape request to the<br>"
             "tracker. A scrape request queries the tracker for<br>"
             "statistics such as total number of incomplete peers,<br>"
-            "complete peers, number of downloads etc."
-            )
+            "complete peers, number of downloads etc.")
         it = self.item_add(
             None, _("Force re-check"), None,
-            lambda x, y: h.force_recheck()
-            )
+            lambda x, y: h.force_recheck())
         it.tooltip_text_set(
             "force_recheck puts the torrent back in a state<br>"
             "where it assumes to have no resume data.<br>"
@@ -602,30 +590,25 @@ class ItemMenu(Menu):
             "checked (all the files will be read and compared<br>"
             "to the piece hashes).<br>"
             "Once the check is complete, the torrent will start<br>"
-            "connecting to peers again, as normal."
-            )
+            "connecting to peers again, as normal.")
         self.item_separator_add(None)
         it = self.item_add(
             None, _("Torrent properties"), None,
-            self.torrent_props_cb, h
-            )
+            self.torrent_props_cb, h)
 
         def files_cb(menu, it, h):
             from .TorrentProps import TorrentFiles
             TorrentFiles(h)
         self.item_add(
             it, "Files", None,
-            files_cb, h
-            )
+            files_cb, h)
 
         self.move(*item.track_object.pos)
         del item.track_object
 
         self.show()
 
-    def remove_torrent_cb(
-        self, menu, item, glitem, session, h, with_data=False
-            ):
+    def remove_torrent_cb(self, menu, item, glitem, session, h, 
with_data=False):
         menu.close()
         session.remove_torrent(h, with_data)
 
@@ -657,12 +640,22 @@ class TorrentTooltip(Table):
         (_("Total uploaded this session"), intrepr, "total_payload_upload"),
         (_("Total failed"), intrepr, "total_failed_bytes"),
         (_("Number of seeds"), None, "num_seeds"),
-        (_("Number of peers"), None, "num_peers"),
-        )
+        (_("Number of peers"), None, "num_peers"))
 
     bold = "font_weight=Bold"
 
-    def __init__(self, parent, h, s):
+    def __init__(self, parent, session, torrent):
+
+        handle = torrent.handle
+
+        if not handle.is_valid():
+            raise ValueError("Invalid handle")
+
+        flags = lt.status_flags_t.query_pieces
+        status = handle.status(flags)
+
+        # if not s.has_metadata:
+        #     return
 
         Table.__init__(self, parent, size_hint_weight=EXPAND_BOTH)
 
@@ -673,7 +666,7 @@ class TorrentTooltip(Table):
             l1 = Label(self, text=desc)
             l1.show()
             self.pack(l1, 0, i, 1, 1)
-            v = getattr(s, attr_name)
+            v = getattr(status, attr_name)
             if conv:
                 if conv == datetime.fromtimestamp and v == 0:
                     v = _("N/A")
@@ -693,27 +686,27 @@ class TorrentTooltip(Table):
         WIDTH = 30
         HEIGHT = 4
 
-        g = BlockGraph(
-            self, s.pieces, s.num_pieces,
-            size=(WIDTH, HEIGHT), size_hint_align=FILL_BOTH
-            )
+        graph = BlockGraph(
+            self, status.pieces, status.num_pieces,
+            size=(WIDTH, HEIGHT), size_hint_align=FILL_BOTH)
 
         l.text = "".join((
             "<font %s>" % (self.bold),
-            _("Pieces (scaled 1:%d)") % (g.block_size),
-            "</>"
-            ))
+            _("Pieces (scaled 1:%d)") % (graph.block_size),
+            "</>"))
 
-        self.pack(g, 1, i, 1, 1)
-        g.show()
+        self.pack(graph, 1, i, 1, 1)
+        graph.show()
 
-        self.timer = Timer(1.0, self.update, h, self.items, value_labels, g)
+        self.timer = Timer(1.0, self.update, torrent, self.items, 
value_labels, graph)
         self.on_del_add(lambda x: self.timer.delete())
 
     @staticmethod
-    def update(h, items, value_labels, g):
+    def update(torrent, items, value_labels, g):
         #log.debug("Tooltip TICK")
-        s = h.status(8)
+        handle = torrent.handle
+        flags = lt.status_flags_t.query_pieces
+        s = handle.status(flags)
         for i, l in enumerate(value_labels):
             conv, attr_name = items[i][1:]
             v = getattr(s, attr_name)
diff --git a/epour/session.py b/epour/session.py
index e13d945..8acfedb 100644
--- a/epour/session.py
+++ b/epour/session.py
@@ -1,7 +1,7 @@
 #
 #  Epour - A bittorrent client using EFL and libtorrent
 #
-#  Copyright 2012-2015 Kai Huuhko <[email protected]>
+#  Copyright 2012-2016 Kai Huuhko <[email protected]>
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -19,14 +19,9 @@
 #  MA 02110-1301, USA.
 #
 
-import sys
 import os
 import mimetypes
-import urllib
-try:
-    import urlparse
-except ImportError:
-    from urllib import parse as urlparse
+from urllib.parse import urlparse, urlsplit
 import logging
 import shutil
 try:
@@ -42,10 +37,63 @@ from efl.ecore import Timer
 from xdg.BaseDirectory import save_data_path, load_data_paths
 
 
+log = logging.getLogger("epour.session")
+
+
+flags_t = lt.add_torrent_params_flags_t
+default_flags = (
+    flags_t.flag_apply_ip_filter +
+    flags_t.flag_update_subscribe +
+    flags_t.flag_duplicate_is_error +
+    flags_t.flag_auto_managed)
+
+
+def read_torrent_file(info_hash):
+    if not info_hash:
+        log.debug("Tried to read torrent with invalid info_hash.")
+        return
+
+    info_hash = str(info_hash)
+
+    log.debug("Reading torrent file %s.torrent", info_hash)
+
+    paths = load_data_paths("epour")
+    for p in paths:
+        t_path = os.path.join(p, "%s.torrent" % info_hash)
+        if os.path.exists(t_path):
+            ti = lt.torrent_info(t_path)
+            return ti
+
+
+def read_resume_data(info_hash):
+    log.debug("Reading resume data for %s", info_hash)
+
+    data = None
+
+    paths = load_data_paths("epour")
+    for p in paths:
+        t_path = os.path.join(p, "%s.fastresume" % info_hash)
+        if os.path.exists(t_path):
+            with open(t_path, "rb") as fp:
+                data = lt.read_resume_data(fp.read())
+            break
+
+    if data:
+        return data
+    else:
+        raise ValueError("Fast Resume data not found")
+
+
 class Session(lt.session):
-    def __init__(self, conf):
+
+    def __init__(self, conf, shutdown_cb):
         self.conf = conf
-        self.log = logging.getLogger("epour.session")
+        self._shutdown_cb = shutdown_cb
+        self._shutdown_timer = None
+        self._torrents_changed = False
+
+        self.torrents = OrderedDict()
+        self._outstanding_resume_data = 0
 
         from epour import __version__ as version
         ver_ints = []
@@ -54,7 +102,7 @@ class Session(lt.session):
         ver_ints.append(0)
 
         fp = lt.fingerprint("EP", *ver_ints)
-        self.log.debug("peer-id: {}".format(fp))
+        log.debug("peer-id: %s", fp)
 
         lt.session.__init__(
             self,
@@ -64,9 +112,7 @@ class Session(lt.session):
                 #lt.session_flags_t.start_default_features
         )
 
-        self.log.info("Session started")
-
-        self.torrents = OrderedDict()
+        log.info("Session started")
 
                 #rsdpipsdtsppe
                 #stheprtertoer
@@ -80,14 +126,32 @@ class Session(lt.session):
 
         self.listen_on(
             conf.getint("Settings", "listen_low"),
-            conf.getint("Settings", "listen_high")
-        )
+            conf.getint("Settings", "listen_high"))
 
         self.alert_manager = AlertManager(self)
-        self.alert_manager.callback_add(
-            "add_torrent_alert", self._add_torrent_cb)
-        self.alert_manager.callback_add(
-            "metadata_received_alert", self._metadata_received_cb)
+        self.alert_manager.callback_add("add_torrent_alert", 
self._add_torrent_cb)
+        self.alert_manager.callback_add("metadata_received_alert", 
self._metadata_received_cb)
+
+        def save_resume_alert_cb(a):
+            handle = a.handle
+            if handle is None:
+                log.error("Tried to write resume data with invalid handle.")
+                return
+
+            info_hash = str(handle.info_hash())
+            data = a.resume_data
+
+            self.torrents[info_hash].write_resume_data(data)
+
+            self._outstanding_resume_data -= 1
+            log.debug("Resume data written for %s", info_hash)
+
+        def save_resume_failed_alert_cb(a):
+            log.error(a.message)
+            self._outstanding_resume_data -= 1
+
+        self.alert_manager.callback_add("save_resume_data_alert", 
save_resume_alert_cb)
+        self.alert_manager.callback_add("save_resume_data_failed_alert", 
save_resume_failed_alert_cb)
 
         def torrent_finished_move_cb(a):
             h = a.handle
@@ -100,22 +164,51 @@ class Session(lt.session):
         self.alert_manager.callback_add(
             "torrent_finished_alert", torrent_finished_move_cb)
 
+        def periodic_save_func():
+            self.save_resume_data()
+            if self._torrents_changed:
+                self.save_torrents()
+                self._torrents_changed = False
+            return True
+
+        self.periodic_save_timer = Timer(15.0, periodic_save_func)
+
+        def state_update_alert_cb(a):
+            statuses = a.status
+            for status in statuses:
+                info_hash = status.info_hash
+                self.torrents[str(info_hash)].status = status
+
+        self.alert_manager.callback_add("state_update_alert", 
state_update_alert_cb)
+
     def _add_torrent_cb(self, a):
         e = a.error
         if e.value() > 0:
-            self.log.error("Adding torrent failed: %r" % (e.message()))
+            params = a.params
+            info_hash = str(params["info_hash"])
+            if info_hash in self.torrents:
+                del self.torrents[info_hash]
+            log.error("Adding torrent %s failed: %s", info_hash, e.message())
             return
-        h = a.handle
-        ihash = str(h.info_hash())
-        self.torrents[ihash] = a.params
-        self.log.debug("Torrent added.")
+        handle = a.handle
+        info_hash = handle.info_hash()
+
+        log.debug("Torrent %s added", info_hash)
+
+        if str(info_hash) in self.torrents:
+            log.debug("Torrent already in list, setting session")
+            self.torrents[str(info_hash)].set_session(self)
+        else:
+            torrent = Torrent(self, info_hash)
+            self.torrents[str(info_hash)] = torrent
+            self._torrents_changed = True
 
     def _metadata_received_cb(self, a):
-        h = a.handle
-        ihash = str(h.info_hash())
-        self.log.debug("Metadata received.")
-        t_info = h.get_torrent_info()
-        self.torrents[ihash]["ti"] = t_info
+        handle = a.handle
+        info_hash = handle.info_hash()
+        log.debug("Metadata received for %s", str(info_hash))
+        torrent = self.torrents[str(info_hash)]
+        torrent.add_metadata()
 
     def load_state(self):
         for p in load_data_paths("epour"):
@@ -126,18 +219,20 @@ class Session(lt.session):
                         state = lt.bdecode(f.read())
                     lt.session.load_state(self, state)
                 except Exception as e:
-                    self.log.debug("Could not load previous session state.")
-                    self.log.debug(e)
+                    log.debug("Could not load previous session state.")
+                    log.debug(e)
                 else:
-                    self.log.info("Session restored from disk.")
+                    log.info("Session restored from disk.")
                     break
 
         settings = self.settings()
+
         from epour import __version__ as version
         version += ".0"
         ver_s = "Epour/{} libtorrent/{}".format(version, lt.version)
         settings.user_agent = ver_s
-        self.log.debug("User agent: {}".format(ver_s))
+        log.debug("User agent: %s", ver_s)
+
         self.set_settings(settings)
 
     def save_state(self):
@@ -147,7 +242,7 @@ class Session(lt.session):
         with open(path, 'wb') as f:
             f.write(lt.bencode(state))
 
-        self.log.debug("Session state saved.")
+        log.debug("Session state saved.")
 
     def load_torrents(self):
         for p in load_data_paths("epour"):
@@ -156,204 +251,171 @@ class Session(lt.session):
                 break
 
         if not torrents_path:
-            self.info.debug("No previous list of torrents found.")
+            log.debug("Previous list of torrents not found.")
             return
 
         try:
             pkl_file = open(torrents_path, 'rb')
         except IOError:
-            self.log.warning("Could not open the list of torrents.")
+            log.warning("Could not open the list of torrents.")
         else:
             try:
                 torrents = cPickle.load(pkl_file)
             except Exception:
-                self.log.exception("Opening the list of torrents failed.")
+                log.exception("Opening the list of torrents failed.")
             else:
-                self.log.debug(
+                log.debug(
                     "List of torrents opened, "
-                    "restoring {} torrents.".format(len(torrents))
+                    "restoring %d torrents.", len(torrents)
                 )
-                for i, t in torrents.items():
+                for info_hash, torrent in torrents.items():
+                    log.debug("Restoring torrent %s", info_hash)
+                    self.torrents[info_hash] = torrent
+
+                    params_dict = torrent.get_params()
+
+                    params = None
+
+                    if "ti" in params_dict and params_dict["ti"]:
+                        try:
+                            ti = read_torrent_file(info_hash)
+                        except Exception:
+                            log.exception("Opening torrent %s failed", 
info_hash)
+                        else:
+                            params_dict["ti"] = ti
+                            try:
+                                params = read_resume_data(info_hash)
+                            except Exception:
+                                pass
+                            else:
+                                params.trackers = list(set(params.trackers))
+
+                    if params is None:
+                        params = lt.add_torrent_params()
+
+                    for k, v in params_dict.items():
+                        setattr(params, k, v)
+
                     try:
-                        for k, v in t.items():
-                            if v is None:
-                                continue
-                            elif k == "ti":
-                                # Epour <= 0.6 compat
-                                if isinstance(v, dict):
-                                    t[k] = lt.torrent_info(lt.bdecode(v))
-                                else:
-                                    t[k] = lt.bdecode(v)
-                            # elif k == "info_hash":
-                            #     torrents[i][k] = lt.big_number(v)
+                        self.async_add_torrent(params)
                     except Exception:
-                        self.log.exception("Opening torrent %s failed", i)
+                        log.exception("Opening torrent %s failed", info_hash)
                         continue
-
-                    self.async_add_torrent(t)
             finally:
                 pkl_file.close()
 
-    def save_torrents(self):
-        self.log.debug("Saving {} torrents.".format(len(self.torrents)))
-
-        for i, t in self.torrents.items():
-            for k, v in t.items():
-                if k == "info_hash":
-                    if v.is_all_zeros():
-                        del self.torrents[i][k]
-                    else:
-                        self.torrents[i][k] = v.to_bytes()
-
-        handles = self.get_torrents()
-        for h in handles:
-            if h.is_valid():
-                i = str(h.info_hash())
-                t_dict = self.torrents[i]
-                t_dict["save_path"] = h.save_path()
-                s = h.status(0)
-                if s.has_metadata:
-                    resume_data = lt.bencode(h.write_resume_data())
-                    t_dict["resume_data"] = resume_data
-                    t_info = h.get_torrent_info()
-                    t_dict["ti"] = lt.bencode(t_info)
+    def save_resume_data(self):
+        for handle in self.get_torrents():
+            if not handle.is_valid():
+                log.error("Invalid handle while trying to save resume data")
+                continue
+            status = handle.status(0)
+            if not status.has_metadata:
+                continue
+            if not status.need_save_resume:
+                continue
+
+            handle.save_resume_data()
+            self._outstanding_resume_data += 1
+
+    def shutdown(self):
+        self.pause()
+
+        self.save_resume_data()
+
+        def _check_outstanding():
+            if self._outstanding_resume_data == 0:
+                self.save_torrents()
+                self.save_state()
+                self._shutdown_cb()
+                return False
             else:
-                self.log.debug("Handle is invalid, skipping")
-
-        path = os.path.join(save_data_path("epour"), "torrents")
-        with open(path, 'wb') as f:
-            cPickle.dump(self.torrents, f, protocol=cPickle.HIGHEST_PROTOCOL)
-
-        self.log.debug("List of torrents saved.")
-
-    # def write_torrent(self, h):
-    #     if h is None:
-    #         self.log.debug("Tried to write torrent while handle was empty.")
-    #         return
-
-    #     t_info = h.get_torrent_info()
-    #     ihash = str(h.info_hash())
-
-    #     self.log.debug("Writing torrent file {}".format(ihash))
-
-    #     md = lt.bdecode(t_info.metadata())
-    #     t = {}
-    #     t["info"] = md
-
-    #     p = save_data_path("epour")
-    #     t_path = os.path.join(p, "{0}.torrent".format(ihash))
+                return True
 
-    #     if t_path:
-    #         with open(t_path, "wb") as f:
-    #             f.write(lt.bencode(t))
+        self._shutdown_timer = Timer(1.0, _check_outstanding)
 
-    #     return t_path
+    def save_torrents(self):
+        for info_hash, torrent in self.torrents.items():
+            info_hash2 = str(torrent.handle.info_hash())
+            assert info_hash == info_hash2, "%s is not %s" % (info_hash, 
info_hash2)
+        path = os.path.join(save_data_path("epour"), "torrents")
+        try:
+            data = cPickle.dumps(self.torrents, 
protocol=cPickle.HIGHEST_PROTOCOL)
+        except Exception:
+            log.exception("Failed to save torrents")
+        else:
+            with open(path, 'wb') as fp:
+                fp.write(data)
+            log.debug("List of torrents saved.")
 
     def remove_torrent(self, h, with_data=False):
-        ihash = str(h.info_hash())
+        info_hash = str(h.info_hash())
+
+        torrent = self.torrents[info_hash]
+        torrent.delete_torrent_file()
+        torrent.delete_resume_data()
 
-        del self.torrents[ihash]
+        del self.torrents[info_hash]
         lt.session.remove_torrent(self, h, option=with_data)
 
-        for p in load_data_paths("epour"):
-            fr_path = os.path.join(
-                p, "{0}.fastresume".format(ihash)
-            )
+        self._torrents_changed = True
 
-            try:
-                with open(fr_path):
-                    pass
-            except IOError:
-                self.log.debug("Could not remove %s", fr_path)
-            else:
-                os.remove(fr_path)
+    def add_torrent_with_uri(self, uri):
+        storage_path = self.conf.get("Settings", "storage_path")
+        #default_flags = self.conf.get("Settings", "default_flags")
 
-        t_path = None
-        for p in load_data_paths("epour"):
-            t_path = os.path.join(p, "{0}.torrent".format(ihash))
-            break
+        add_dict = {
+            "save_path": storage_path,
+            "flags": default_flags,
+        }
 
-        if t_path:
-            try:
-                with open(t_path):
-                    pass
-            except IOError:
-                self.log.debug("Could not remove torrent file.")
-            else:
-                os.remove(t_path)
+        self.fill_add_dict_based_on_uri(add_dict, uri)
 
-        if not hasattr(lt, "torrent_removed_alert"):
-            class torrent_removed_alert(object):
-                def __init__(self, h, info_hash):
-                    self.handle = h
-                    self.info_hash = info_hash
+        self.add_torrent_with_dict(add_dict)
 
-            a = torrent_removed_alert(h, ihash)
+    def fill_add_dict_based_on_uri(self, add_dict, uri):
+        parsed_uri = urlparse(uri)
 
-            self.alert_manager.signal(a)
+        if parsed_uri.scheme == "magnet":
+            add_dict["url"] = uri
+        elif parsed_uri.scheme == "file" or parsed_uri.scheme == "" and 
os.path.isfile(parsed_uri.path):
+            path = parsed_uri.path
 
-        return ihash
+            mimetype = mimetypes.guess_type(path)[0]
+            if not mimetype == "application/x-bittorrent":
+                log.warning("%s is not of a known torrent file type", path)
 
-    def add_torrent_from_file(self, add_dict, t_uri):
-        mimetype = mimetypes.guess_type(t_uri)[0]
-        if not mimetype == "application/x-bittorrent":
-            self.log.error("Invalid file")
-            return
+            with open(path, 'rb') as t:
+                t_raw = lt.bdecode(t.read())
 
-        if t_uri.startswith("file://"):
-            t_uri = urllib.unquote(urlparse.urlsplit(t_uri).path)
+            info = lt.torrent_info(t_raw)
 
-        with open(t_uri, 'rb') as t:
-            t_raw = lt.bdecode(t.read())
+            add_dict["ti"] = info
 
-        info = lt.torrent_info(t_raw)
-        add_dict["ti"] = info
+            ihash = str(info.info_hash())
+            path_dir = save_data_path("epour")
+            new_path = os.path.join(path_dir, "{0}.torrent".format(ihash))
 
-        rd = None
-        fr_file_name = "{}.fastresume".format(info.info_hash())
-        for p in load_data_paths("epour"):
-            path = os.path.join(p, fr_file_name)
-            if os.path.isfile(path):
-                try:
-                    with open(path, "rb") as f:
-                        rd = f.read()
-                except Exception:
-                    self.log.debug("Invalid resume data")
-                else:
-                    add_dict["resume_data"] = rd
-                    break
+            if path == new_path:
+                pass
+            else:
+                shutil.copy(path, new_path)
 
-        ihash = str(info.info_hash())
+                if self.conf.getboolean("Settings", "delete_original"):
+                    log.debug(
+                        "Deleting original torrent file %s", path)
+                    os.remove(path)
 
-        path = save_data_path("epour")
-        new_uri = os.path.join(path, "{0}.torrent".format(ihash))
+                path = new_path
 
-        if t_uri == new_uri:
-            pass
+        elif len(uri) == 40:  # looks like a sha1 string
+            add_dict["info_hash"] = uri
+        # elif uri.scheme == "http" or uri.scheme == "https":
+        #     pass
         else:
-            shutil.copy(t_uri, new_uri)
-
-            if self.conf.getboolean("Settings", "delete_original"):
-                self.log.debug(
-                    "Deleting original torrent file {}".format(t_uri))
-                os.remove(t_uri)
-
-            t_uri = new_uri
+            raise RuntimeError("Could not parse the torrent string.")
 
-        self.async_add_torrent(add_dict)
-
-    def add_torrent_from_magnet(self, add_dict, t_uri):
-        self.log.debug("Adding %r", t_uri)
-        t_uri = t_uri.encode("ascii")
-        tmp_dict = lt.parse_magnet_uri(t_uri)
-        tmp_dict.update(add_dict)
-        tmp_dict["info_hash"] = tmp_dict["info_hash"].to_bytes()
-        self.async_add_torrent(tmp_dict)
-
-    def add_torrent_from_hash(self, add_dict, t_uri):
-        t_uri = t_uri.encode("ascii")
-        add_dict["info_hash"] = t_uri
-        self.log.debug("Adding %s", t_uri)
+    def add_torrent_with_dict(self, add_dict):
         self.async_add_torrent(add_dict)
 
 
@@ -383,18 +445,255 @@ class AlertManager(object):
         a_name = type(a).__name__
 
         if a_name not in self.alerts:
-            self.log.debug("No handler: {} | {}".format(a_name, a))
+            log.debug("No handler: %s | %s", a_name, a)
             return
 
         for cb, args, kwargs in self.alerts[a_name]:
             try:
                 cb(a, *args, **kwargs)
             except:
-                self.log.exception("Exception while handling alerts")
+                log.exception("Exception while handling alerts")
 
     def update(self):
-        #self.log.debug("Alerts TICK")
         for a in self.session.pop_alerts():
             self.signal(a)
 
         return True
+
+
+def status_to_flags(status):
+    #flags_t = lt.add_torrent_params_flags_t
+    flags = 0
+    flags += 1 if status.is_seeding else 0
+    #flags += 2 deprecated
+    flags += 4 if status.upload_mode else 0
+    flags += 8 if status.share_mode else 0
+    flags += 16 if status.ip_filter_applies else 0
+    flags += 32 if status.paused else 0
+    flags += 64  # auto_managed
+    flags += 128  # duplicate_is_error
+    #flags += 256 deprecated
+    flags += 512  # update_subscribe
+    flags += 1024 if status.super_seeding else 0
+    flags += 2048 if status.sequential_download else 0
+    #flags += 4096 if pinned else 0
+    #flags += 8192 if stop_when_ready else 0
+    #flags += 16384 if override_trackers else 0
+    #flags += 32768 if override_web_seeds else 0
+    #flags += 65536 deprecated
+    #flags += 131072 if override_resume_data else 0
+    #flags += 262144 if merge_resume_trackers else 0
+    #flags += 524288 if use_resume_save_path else 0
+    #flags += 1048576 if merge_resume_http_seeds else 0
+
+    return flags
+
+
+class Torrent(object):
+
+    def __init__(self, session, info_hash, options={}):
+        assert isinstance(session, lt.session)
+        assert isinstance(info_hash, lt.sha1_hash)
+
+        self.session = session
+        self.info_hash = info_hash
+        self.options = options
+
+        self._status = None
+
+    # @property
+    # def info_hash(self):
+    #     return self.handle.info_hash()
+
+    @property
+    def handle(self):
+        return self.session.find_torrent(self.info_hash)
+
+    @property
+    def state(self):
+        if getattr(self, "_state", None):
+            return self._state
+        else:
+            status = self.handle.status(0)
+            state = status.state
+            self._state = state
+            return state
+
+    @state.setter
+    def state(self, value):
+        self._state = value
+
+    @property
+    def status(self):
+        if getattr(self, "_status", None):
+            return self._status
+        else:
+            status = self.handle.status(64)
+            self._status = status
+            return status
+
+    @status.setter
+    def status(self, value):
+        self._status = value
+
+    def get_params(self):
+        return self._params
+
+    def _get_params(self):
+        handle = self.handle
+        status = handle.status()
+        flags = status_to_flags(status)
+
+        # trackers = []
+
+        # for tracker in handle.trackers():
+        #     trackers.append(tracker["url"])
+
+        # trackers = ",".join(trackers)
+
+        params = {
+            #"trackers": trackers,
+            "url_seeds": handle.url_seeds(),
+            #"dht_nodes":
+            "name": status.name,
+            "save_path": status.save_path,
+            "storage_mode": status.storage_mode,
+            #"storage":
+            #"userdata":
+            "file_priorities": handle.file_priorities(),
+            #"trackerid":
+            #"url":
+            #"uuid":
+            #"source_feed_url":
+            "flags": flags,
+            #"info_hash": handle.info_hash().to_bytes(),
+            "info_hash": self.info_hash,
+            "max_uploads": handle.max_uploads(),
+            "max_connections": handle.max_connections(),
+            "upload_limit": handle.upload_limit(),
+            "download_limit": handle.download_limit(),
+        }
+        if self.has_metadata:
+            params["ti"] = self.torrent_file_path
+        return params
+
+    def __getstate__(self):
+        state = self.__dict__.copy()
+
+        params = self._get_params()
+        info_hash1 = params["info_hash"].to_bytes()
+        params["info_hash"] = info_hash1
+        state["_params"] = params
+
+        info_hash2 = state["info_hash"].to_bytes()
+        state["info_hash"] = info_hash2
+
+        del state["session"]
+        del state["_status"]
+        del state["_state"]
+        return state
+
+    def __setstate__(self, state):
+        params = state["_params"]
+        info_hash1 = params["info_hash"]
+        info_hash1 = lt.sha1_hash(info_hash1)
+        params["info_hash"] = info_hash1
+
+        info_hash2 = state["info_hash"]
+        info_hash2 = lt.sha1_hash(info_hash2)
+        state["info_hash"] = info_hash1
+
+        self.__dict__.update(state)
+
+    def set_session(self, session):
+        self.session = session
+
+    @property
+    def has_metadata(self):
+        return self.handle.status(0).has_metadata
+
+    @property
+    def torrent_file_path(self):
+        paths = load_data_paths("epour")
+        for p in paths:
+            t_path = os.path.join(p, "{0}.torrent".format(self.info_hash))
+            if os.path.exists(t_path):
+                return t_path
+
+        return None
+
+    def write_torrent_file(self):
+        assert self.handle is not None
+
+        handle = self.handle
+        info_hash = str(handle.info_hash())
+
+        log.debug("Writing torrent file {0}.torrent".format(info_hash))
+
+        p = save_data_path("epour")
+        t_path = os.path.join(p, "{0}.torrent".format(info_hash))
+
+        if t_path:
+            t_info = handle.torrent_file()
+            metadata = lt.bdecode(t_info.metadata())
+            torrent_file = {"info": metadata}
+            with open(t_path, "wb") as fp:
+                fp.write(lt.bencode(torrent_file))
+
+            log.debug("Torrent file was written to %s" % t_path)
+
+            return t_path
+
+    def write_resume_data(self, data):
+        assert self.handle is not None
+
+        info_hash = self.handle.info_hash()
+        info_hash = str(info_hash)
+
+        log.debug("Writing resume data for {}".format(info_hash))
+
+        t_path = os.path.join(
+            save_data_path("epour"),
+            info_hash + ".fastresume")
+
+        if t_path:
+            with open(t_path, "wb") as f:
+                f.write(lt.bencode(data))
+        else:
+            return
+
+        return t_path
+
+    def delete_torrent_file(self):
+        assert self.handle is not None
+
+        info_hash = str(self.handle.info_hash())
+
+        for p in load_data_paths("epour"):
+            t_path = os.path.join(p, "{0}.torrent".format(info_hash))
+            try:
+                with open(t_path):
+                    pass
+            except IOError:
+                continue
+            else:
+                os.remove(t_path)
+                break
+
+    def delete_resume_data(self):
+        assert self.handle is not None
+
+        info_hash = str(self.handle.info_hash())
+
+        for p in load_data_paths("epour"):
+            fr_path = os.path.join(p, "{0}.fastresume".format(info_hash))
+            try:
+                with open(fr_path):
+                    pass
+            except IOError:
+                continue
+            else:
+                os.remove(fr_path)
+
+    def add_metadata(self):
+        self.write_torrent_file()
diff --git a/po/epour.pot b/po/epour.pot
index ac0dff1..35a65c4 100644
--- a/po/epour.pot
+++ b/po/epour.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-01-20 22:04+0900\n"
+"POT-Creation-Date: 2016-08-04 18:18+0300\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <[email protected]>\n"
@@ -17,172 +17,115 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ../epour/gui/TorrentProps.py:90
-msgid "Enable/disable file download"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:124
-msgid "Invalid torrent handle."
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:149
-msgid "Torrent info"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:157
-msgid "Torrent settings"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:165
-msgid "Torrent status"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:174
-msgid "Magnet URI"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:182
-msgid "Copy"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:209
-#, python-format
-msgid "Epour - Files for torrent: %s"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:251
-msgid "Select all"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:255
-msgid "Select none"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:394
-msgid "Private"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:445
-msgid "Storage path"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:451
-msgid "Select"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:503
-msgid "disabled"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:539 ../epour/gui/__init__.py:432
-msgid "Queued"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:539 ../epour/gui/__init__.py:432
-msgid "Checking"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:539 ../epour/gui/__init__.py:432
-msgid "Downloading metadata"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:539 ../epour/gui/__init__.py:433
-msgid "Downloading"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:540 ../epour/gui/__init__.py:433
-msgid "Finished"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:540 ../epour/gui/__init__.py:433
-msgid "Seeding"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:540 ../epour/gui/__init__.py:433
-msgid "Allocating"
-msgstr ""
-
-#: ../epour/gui/TorrentProps.py:540 ../epour/gui/__init__.py:434
-msgid "Checking resume data"
-msgstr ""
-
-#: ../epour/gui/Widgets.py:109
+#: ../epour/gui/Widgets.py:130
 msgid "Close"
 msgstr ""
 
-#: ../epour/gui/Widgets.py:122
+#: ../epour/gui/Widgets.py:143
 msgid "OK"
 msgstr ""
 
-#: ../epour/gui/Widgets.py:131
+#: ../epour/gui/Widgets.py:152
 msgid "Confirm exit"
 msgstr ""
 
-#: ../epour/gui/Widgets.py:132
+#: ../epour/gui/Widgets.py:153
 msgid "Are you sure you wish to exit Epour?"
 msgstr ""
 
-#: ../epour/gui/Widgets.py:134
+#: ../epour/gui/Widgets.py:155
 msgid "Yes"
 msgstr ""
 
-#: ../epour/gui/Widgets.py:138
+#: ../epour/gui/Widgets.py:159
 msgid "No"
 msgstr ""
 
-#: ../epour/gui/__init__.py:93
+#: ../epour/gui/__init__.py:91
 msgid "Add torrent"
 msgstr ""
 
-#: ../epour/gui/__init__.py:106
+#: ../epour/gui/__init__.py:103
 msgid "Pause Session"
 msgstr ""
 
-#: ../epour/gui/__init__.py:110
+#: ../epour/gui/__init__.py:106
 msgid "Resume Session"
 msgstr ""
 
-#: ../epour/gui/__init__.py:126
+#: ../epour/gui/__init__.py:121
 msgid "Preferences"
 msgstr ""
 
-#: ../epour/gui/__init__.py:129
+#: ../epour/gui/__init__.py:124
 msgid "General"
 msgstr ""
 
-#: ../epour/gui/__init__.py:133
+#: ../epour/gui/__init__.py:127
 msgid "Proxy"
 msgstr ""
 
-#: ../epour/gui/__init__.py:137 ../epour/gui/__init__.py:339
+#: ../epour/gui/__init__.py:130 ../epour/gui/__init__.py:340
 msgid "Session"
 msgstr ""
 
-#: ../epour/gui/__init__.py:141
+#: ../epour/gui/__init__.py:133
 msgid "Exit"
 msgstr ""
 
-#: ../epour/gui/__init__.py:225
+#: ../epour/gui/__init__.py:240
 msgid "Torrent {} has finished downloading."
 msgstr ""
 
-#: ../epour/gui/__init__.py:370
+#: ../epour/gui/__init__.py:371
 msgid "Peer connections"
 msgstr ""
 
-#: ../epour/gui/__init__.py:378
+#: ../epour/gui/__init__.py:379
 msgid "Upload slots"
 msgstr ""
 
-#: ../epour/gui/__init__.py:387
+#: ../epour/gui/__init__.py:388
 msgid "Listening"
 msgstr ""
 
-#: ../epour/gui/__init__.py:454
+#: ../epour/gui/__init__.py:432 ../epour/gui/TorrentProps.py:539
+msgid "Queued"
+msgstr ""
+
+#: ../epour/gui/__init__.py:432 ../epour/gui/TorrentProps.py:539
+msgid "Checking"
+msgstr ""
+
+#: ../epour/gui/__init__.py:432 ../epour/gui/TorrentProps.py:539
+msgid "Downloading metadata"
+msgstr ""
+
+#: ../epour/gui/__init__.py:433 ../epour/gui/TorrentProps.py:539
+msgid "Downloading"
+msgstr ""
+
+#: ../epour/gui/__init__.py:433 ../epour/gui/TorrentProps.py:540
+msgid "Finished"
+msgstr ""
+
+#: ../epour/gui/__init__.py:433 ../epour/gui/TorrentProps.py:540
+msgid "Seeding"
+msgstr ""
+
+#: ../epour/gui/__init__.py:433 ../epour/gui/TorrentProps.py:540
+msgid "Allocating"
+msgstr ""
+
+#: ../epour/gui/__init__.py:434 ../epour/gui/TorrentProps.py:540
+msgid "Checking resume data"
+msgstr ""
+
+#: ../epour/gui/__init__.py:448
 msgid "Invalid torrent"
 msgstr ""
 
-#: ../epour/gui/__init__.py:460
+#: ../epour/gui/__init__.py:459
 #, python-brace-format
 msgid "{0:.0%} complete, ETA: {1} (Down: {2}/s Up: {3}/s Queue pos: {4})"
 msgstr ""
@@ -207,95 +150,152 @@ msgstr ""
 msgid "Down"
 msgstr ""
 
-#: ../epour/gui/__init__.py:551
+#: ../epour/gui/__init__.py:549
 msgid "Top"
 msgstr ""
 
-#: ../epour/gui/__init__.py:554
+#: ../epour/gui/__init__.py:551
 msgid "Bottom"
 msgstr ""
 
-#: ../epour/gui/__init__.py:557
+#: ../epour/gui/__init__.py:553
 msgid "Remove torrent"
 msgstr ""
 
-#: ../epour/gui/__init__.py:561
+#: ../epour/gui/__init__.py:556
 msgid "and data files"
 msgstr ""
 
-#: ../epour/gui/__init__.py:566
+#: ../epour/gui/__init__.py:560
 msgid "Force reannounce"
 msgstr ""
 
-#: ../epour/gui/__init__.py:579
+#: ../epour/gui/__init__.py:571
 msgid "Force DHT reannounce"
 msgstr ""
 
-#: ../epour/gui/__init__.py:583
+#: ../epour/gui/__init__.py:574
 msgid "Scrape tracker"
 msgstr ""
 
-#: ../epour/gui/__init__.py:593
+#: ../epour/gui/__init__.py:582
 msgid "Force re-check"
 msgstr ""
 
-#: ../epour/gui/__init__.py:609
+#: ../epour/gui/__init__.py:596
 msgid "Torrent properties"
 msgstr ""
 
-#: ../epour/gui/__init__.py:649
+#: ../epour/gui/__init__.py:632
 msgid "Time when added"
 msgstr ""
 
-#: ../epour/gui/__init__.py:650
+#: ../epour/gui/__init__.py:633
 msgid "Time when completed"
 msgstr ""
 
-#: ../epour/gui/__init__.py:651
+#: ../epour/gui/__init__.py:634
 msgid "All time downloaded"
 msgstr ""
 
-#: ../epour/gui/__init__.py:652
+#: ../epour/gui/__init__.py:635
 msgid "All time uploaded"
 msgstr ""
 
-#: ../epour/gui/__init__.py:653
+#: ../epour/gui/__init__.py:636
 msgid "Total wanted done"
 msgstr ""
 
-#: ../epour/gui/__init__.py:654
+#: ../epour/gui/__init__.py:637
 msgid "Total wanted"
 msgstr ""
 
-#: ../epour/gui/__init__.py:655
+#: ../epour/gui/__init__.py:638
 msgid "Total downloaded this session"
 msgstr ""
 
-#: ../epour/gui/__init__.py:657
+#: ../epour/gui/__init__.py:640
 msgid "Total uploaded this session"
 msgstr ""
 
-#: ../epour/gui/__init__.py:658
+#: ../epour/gui/__init__.py:641
 msgid "Total failed"
 msgstr ""
 
-#: ../epour/gui/__init__.py:659
+#: ../epour/gui/__init__.py:642
 msgid "Number of seeds"
 msgstr ""
 
-#: ../epour/gui/__init__.py:660
+#: ../epour/gui/__init__.py:643
 msgid "Number of peers"
 msgstr ""
 
-#: ../epour/gui/__init__.py:679 ../epour/gui/__init__.py:722
+#: ../epour/gui/__init__.py:672 ../epour/gui/__init__.py:715
 msgid "N/A"
 msgstr ""
 
-#: ../epour/gui/__init__.py:703
+#: ../epour/gui/__init__.py:695
 #, python-format
 msgid "Pieces (scaled 1:%d)"
 msgstr ""
 
+#: ../epour/gui/TorrentProps.py:90
+msgid "Enable/disable file download"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:124
+msgid "Invalid torrent handle."
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:149
+msgid "Torrent info"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:157
+msgid "Torrent settings"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:165
+msgid "Torrent status"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:174
+msgid "Magnet URI"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:182
+msgid "Copy"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:209
+#, python-format
+msgid "Epour - Files for torrent: %s"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:251
+msgid "Select all"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:255
+msgid "Select none"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:394
+msgid "Private"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:445
+msgid "Storage path"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:451
+msgid "Select"
+msgstr ""
+
+#: ../epour/gui/TorrentProps.py:503
+msgid "disabled"
+msgstr ""
+
 #: ../epour/gui/Preferences.py:97
 msgid "Epour General Preferences"
 msgstr ""

-- 


Reply via email to