diff --git a/ChangeLog b/ChangeLog index b1062d393..cc00f123a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32,6 +32,7 @@ * Implemented sequential downloads UI handling. * #378: Allow showing a pieces bar instead of a regular progress bar in a torrent's status tab. + * #2093: Make torrent opening compatible with all unicode paths. ==== Blocklist Plugin ==== * #1382: Implemented whitelist support to both core and GTK UI. diff --git a/deluge/ui/client.py b/deluge/ui/client.py index cf9dee85f..342480e20 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -37,6 +37,7 @@ import logging from twisted.internet.protocol import ClientFactory from twisted.internet import reactor, ssl, defer +import sys import subprocess import deluge.common @@ -624,6 +625,8 @@ class Client(object): :raises OSError: received from subprocess.call() """ + # subprocess.popen does not work with unicode args (with non-ascii characters) on windows + config = config.encode(sys.getfilesystemencoding()) try: subprocess.Popen(["deluged", "--port=%s" % port, "--config=%s" % config]) except OSError, e: diff --git a/deluge/ui/gtkui/addtorrentdialog.py b/deluge/ui/gtkui/addtorrentdialog.py index e2df0c91d..976f95eaa 100644 --- a/deluge/ui/gtkui/addtorrentdialog.py +++ b/deluge/ui/gtkui/addtorrentdialog.py @@ -213,9 +213,6 @@ class AddTorrentDialog(component.Component): new_row = None for filename in filenames: - # Convert the path to unicode - filename = unicode(filename) - # Get the torrent data from the torrent file try: info = deluge.ui.common.TorrentInfo(filename) diff --git a/deluge/ui/gtkui/ipcinterface.py b/deluge/ui/gtkui/ipcinterface.py index 242e6b2d7..6600e4ec2 100644 --- a/deluge/ui/gtkui/ipcinterface.py +++ b/deluge/ui/gtkui/ipcinterface.py @@ -60,7 +60,7 @@ log = logging.getLogger(__name__) class IPCProtocolServer(Protocol): def dataReceived(self, data): config = ConfigManager("gtkui.conf") - data = rencode.loads(data) + data = rencode.loads(data, decode_utf8=True) if not data or config["focus_main_window_on_add"]: component.get("MainWindow").present() process_args(data) diff --git a/deluge/ui/gtkui/queuedtorrents.py b/deluge/ui/gtkui/queuedtorrents.py index 3b63934c5..7eeaaa78b 100644 --- a/deluge/ui/gtkui/queuedtorrents.py +++ b/deluge/ui/gtkui/queuedtorrents.py @@ -173,7 +173,7 @@ class QueuedTorrents(component.Component): def on_button_add_clicked(self, widget): # Add all the torrents in the liststore def add_torrent(model, path, iter, data): - torrent_path = model.get_value(iter, 1) + torrent_path = model.get_value(iter, 1).decode('utf-8') process_args([torrent_path]) self.liststore.foreach(add_torrent, None) diff --git a/deluge/ui/ui.py b/deluge/ui/ui.py index 792f416c2..c10a2c323 100644 --- a/deluge/ui/ui.py +++ b/deluge/ui/ui.py @@ -64,6 +64,35 @@ if 'dev' not in deluge.common.get_version(): import warnings warnings.filterwarnings('ignore', category=DeprecationWarning, module='twisted') +def win32_unicode_argv(): + """Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode + strings. + + Versions 2.x of Python don't support Unicode in sys.argv on + Windows, with the underlying Windows API instead replacing multi-byte + characters with '?'. + """ + + from ctypes import POINTER, byref, cdll, c_int, windll + from ctypes.wintypes import LPCWSTR, LPWSTR + + GetCommandLineW = cdll.kernel32.GetCommandLineW + GetCommandLineW.argtypes = [] + GetCommandLineW.restype = LPCWSTR + + CommandLineToArgvW = windll.shell32.CommandLineToArgvW + CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)] + CommandLineToArgvW.restype = POINTER(LPWSTR) + + cmd = GetCommandLineW() + argc = c_int(0) + argv = CommandLineToArgvW(cmd, byref(argc)) + if argc.value > 0: + # Remove Python executable and commands if present + start = argc.value - len(sys.argv) + return [argv[i] for i in + xrange(start, argc.value)] + class _UI(object): def __init__(self, name="gtk"): @@ -107,7 +136,12 @@ class _UI(object): return self.__args def start(self): - (self.__options, self.__args) = self.__parser.parse_args() + # Make sure all arguments are unicode + if deluge.common.windows_check(): + argv = win32_unicode_argv()[1:] + else: + argv = [arg.decode(sys.stdin.encoding) for arg in sys.argv[1:]] + (self.__options, self.__args) = self.__parser.parse_args(argv) if self.__options.quiet: self.__options.loglevel = "none"