From d2624dedb846b95eb0e63ff36906d786a94ea196 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 23 Oct 2020 20:25:06 +0530 Subject: [PATCH] Windows: Make adding files whose total path length is greater than 260 characters via the Add books button work. Fixes #1900761 [Private bug](https://bugs.launchpad.net/calibre/+bug/1900761) --- src/calibre/gui2/add.py | 29 ++++++++++++++++++---------- src/calibre/gui2/win_file_dialogs.py | 9 ++++++++- src/calibre/utils/filenames.py | 11 +++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index c28493ee59..592a60d6fa 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -5,29 +5,34 @@ __license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal ' -import shutil, os, weakref, traceback, tempfile, time -from threading import Thread +import os +import shutil +import tempfile +import time +import traceback +import weakref from collections import OrderedDict from io import BytesIO -from polyglot.builtins import iteritems, map, unicode_type, string_or_bytes - from PyQt5.Qt import QObject, Qt, pyqtSignal +from threading import Thread -from calibre import prints, as_unicode -from calibre.constants import DEBUG, iswindows, ismacos, filesystem_encoding -from calibre.customize.ui import run_plugins_on_postimport, run_plugins_on_postadd -from calibre.db.adding import find_books_in_directory, compile_rule +from calibre import as_unicode, prints +from calibre.constants import DEBUG, filesystem_encoding, ismacos, iswindows +from calibre.customize.ui import run_plugins_on_postadd, run_plugins_on_postimport +from calibre.db.adding import compile_rule, find_books_in_directory from calibre.db.utils import find_identical_books from calibre.ebooks.metadata import authors_to_sort_string from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.opf2 import OPF -from calibre.gui2 import error_dialog, warning_dialog, gprefs +from calibre.gui2 import error_dialog, gprefs, warning_dialog from calibre.gui2.dialogs.duplicates import DuplicatesQuestion from calibre.gui2.dialogs.progress import ProgressDialog from calibre.ptempfile import PersistentTemporaryDirectory from calibre.utils import join_with_timeout from calibre.utils.config import prefs -from calibre.utils.ipc.pool import Pool, Failure +from calibre.utils.filenames import make_long_path_useable +from calibre.utils.ipc.pool import Failure, Pool +from polyglot.builtins import iteritems, map, string_or_bytes, unicode_type from polyglot.queue import Empty @@ -61,6 +66,10 @@ class Adder(QObject): do_one_signal = pyqtSignal() def __init__(self, source, single_book_per_directory=True, db=None, parent=None, callback=None, pool=None, list_of_archives=False): + if isinstance(source, str): + source = make_long_path_useable(source) + else: + source = list(map(make_long_path_useable, source)) if not validate_source(source, parent): return QObject.__init__(self, parent) diff --git a/src/calibre/gui2/win_file_dialogs.py b/src/calibre/gui2/win_file_dialogs.py index 67ce8aabfe..3c4679dba0 100644 --- a/src/calibre/gui2/win_file_dialogs.py +++ b/src/calibre/gui2/win_file_dialogs.py @@ -222,7 +222,14 @@ def run_file_dialog( return () if parts[0] != secret: raise Exception('File dialog failed, incorrect secret received: ' + get_errors()) - ans = tuple((os.path.abspath(x.decode('utf-8')) for x in parts[1:])) + + from calibre_extensions.winutil import get_long_path_name + + def fix_path(x): + u = os.path.abspath(x.decode('utf-8')) + return get_long_path_name(u) + + ans = tuple(map(fix_path, parts[1:])) return ans diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 98f4d28ae7..8f340ac827 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -592,3 +592,14 @@ def copytree_using_links(path, dest, dest_is_parent=True, filecopyfunc=copyfile) rmtree = shutil.rmtree + + +if iswindows: + def make_long_path_useable(path): + if len(path) > 200: + from calibre_extensions.winutil import canonicalize_path + path = canonicalize_path(path) + return path +else: + def make_long_path_useable(path): + return path