diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po
index 01c63f6f..380936d4 100644
--- a/resources/language/resource.language.en_gb/strings.po
+++ b/resources/language/resource.language.en_gb/strings.po
@@ -880,6 +880,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
+# PKC Settings - Customize Paths
+msgctxt "#39090"
+msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
+msgstr ""
+
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"
diff --git a/resources/language/resource.language.es_ES/strings.po b/resources/language/resource.language.es_ES/strings.po
index aa332347..820ec736 100644
--- a/resources/language/resource.language.es_ES/strings.po
+++ b/resources/language/resource.language.es_ES/strings.po
@@ -965,7 +965,12 @@ msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
-msgstr "Escapar caracteres especiales en la ruta (i.e. espacio a %20)"
+msgstr "Escapar caracteres especiales en la ruta (p. ej. espacio a %20)"
+
+# PKC Settings - Customize Paths
+msgctxt "#39090"
+msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
+msgstr "Caracteres seguros para urls http(s), dav(s) y (s)ftp"
# PKC Settings - Customize Paths
msgctxt "#39037"
diff --git a/resources/lib/app/libsync.py b/resources/lib/app/libsync.py
index 3fb37887..c49db72b 100644
--- a/resources/lib/app/libsync.py
+++ b/resources/lib/app/libsync.py
@@ -42,6 +42,7 @@ class Sync(object):
self.remapSMBphotoNew = None
# Escape path?
self.escape_path = None
+ self.escape_path_safe_chars = None
# Shall we replace custom user ratings with the number of versions available?
self.indicate_media_versions = None
# Will sync movie trailer differently: either play trailer directly or show
@@ -109,6 +110,7 @@ class Sync(object):
self.remapSMBphotoOrg = remove_trailing_slash(utils.settings('remapSMBphotoOrg'))
self.remapSMBphotoNew = remove_trailing_slash(utils.settings('remapSMBphotoNew'))
self.escape_path = utils.settings('escapePath') == 'true'
+ self.escape_path_safe_chars = utils.settings('escapePathSafeChars').encode('utf-8')
self.indicate_media_versions = utils.settings('indicate_media_versions') == "true"
self.sync_specific_plex_playlists = utils.settings('syncSpecificPlexPlaylists') == 'true'
self.sync_specific_kodi_playlists = utils.settings('syncSpecificKodiPlaylists') == 'true'
diff --git a/resources/lib/plex_api/media.py b/resources/lib/plex_api/media.py
index 7d091fa4..60353d7c 100644
--- a/resources/lib/plex_api/media.py
+++ b/resources/lib/plex_api/media.py
@@ -319,7 +319,7 @@ class Media(object):
if path.startswith('\\\\'):
path = 'smb:' + path.replace('\\', '/')
if app.SYNC.escape_path:
- path = utils.escape_path(path)
+ path = utils.escape_path(path, app.SYNC.escape_path_safe_chars)
if (app.SYNC.path_verified and not force_check) or omit_check:
return path
diff --git a/resources/lib/utils.py b/resources/lib/utils.py
index 72127168..5f14e5ea 100644
--- a/resources/lib/utils.py
+++ b/resources/lib/utils.py
@@ -56,7 +56,7 @@ REGEX_MUSICPATH = re.compile(r'''^\^(.+)\$$''')
REGEX_PLEX_ID_FROM_URL = re.compile(r'''metadata%2F(\d+)''')
SAFE_URL_CHARACTERS = "%/:=&?~#+!$,;'@()*[]".encode('utf-8')
-
+HTTP_DAV_FTP = re.compile(r'(http(s)?|dav(s)?|(s)?ftp)://((.+):(.+)@)?([\w\.]+)(:([\d]+))?/')
def garbageCollect():
gc.collect(2)
@@ -385,7 +385,7 @@ def urlparse(url, scheme='', allow_fragments=True):
return _urlparse.urlparse(url, scheme, allow_fragments)
-def escape_path(path):
+def escape_path(path, safe_url_char=SAFE_URL_CHARACTERS):
"""
Uses urllib.quote to escape to escape path [unicode]. See here for the
reasoning whether a character is safe or not and whether or not it should
@@ -396,8 +396,34 @@ def escape_path(path):
you in trouble (e.g. '@')
Returns the escaped path as unicode
"""
- return urllib.quote(path.encode('utf-8'),
- safe=SAFE_URL_CHARACTERS).decode('utf-8')
+ is_http_dav_ftp = HTTP_DAV_FTP.match(path)
+ if is_http_dav_ftp:
+ # If path seems to be a http(s), dav(s) or (s)ftp url, the escape path will be constructed
+ # using RegExp and using safe_url_char as safe characters not to be escaped
+ protocol = is_http_dav_ftp.group(1)
+ user = is_http_dav_ftp.group(6)
+ psswd = is_http_dav_ftp.group(7)
+ if user and psswd:
+ user = urllib.quote(user.encode('utf-8'), safe=safe_url_char).decode('utf-8')
+ psswd = urllib.quote(psswd.encode('utf-8'), safe=safe_url_char).decode('utf-8')
+ host = is_http_dav_ftp.group(8)
+ port = is_http_dav_ftp.group(10)
+ url_path = path.replace(is_http_dav_ftp.group(), '', 1)
+ if url_path:
+ url_path = urllib.quote(path.replace(is_http_dav_ftp.group(), '', 1).encode('utf-8'),
+ safe=safe_url_char).decode('utf-8')
+ return protocol + \
+ u'://' + \
+ (user + u':' + psswd + u'@' if (user and psswd) else u'') + \
+ host + \
+ (u':' + port if port else u'') + \
+ u'/' + \
+ (url_path if url_path else u'')
+ else:
+ # If paths does not seem to be a http(s), dav(s) or (s)ftp url (e.g. plugin://)
+ # escape path as before
+ return urllib.quote(path.encode('utf-8'),
+ safe=SAFE_URL_CHARACTERS).decode('utf-8')
def quote(s, safe='/'):
diff --git a/resources/settings.xml b/resources/settings.xml
index 6e3de284..a8b0cb76 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -101,6 +101,7 @@
+