Skip to content

Commit a86516d

Browse files
OmniTroidclaude
andcommitted
Match webAO URL lookup behavior: lowercase paths and encodeURI encoding
- Add normalizePathForWeb() to lowercase and percent-encode path components - Use JavaScript encodeURI-compatible encoding (preserves safe chars like ! ' ( ) *) - Check pending/failed downloads per normalized path with suffix - Return early if cached file exists for any suffix Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c4ef658 commit a86516d

1 file changed

Lines changed: 55 additions & 20 deletions

File tree

src/webcache.cpp

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,33 @@
1010
#include <QNetworkRequest>
1111
#include <QUrl>
1212

13+
namespace {
14+
// Normalize path like webAO: lowercase and URL-encode each component
15+
// Uses encodeURI-compatible encoding (preserves safe characters like ! ' ( ) *)
16+
QString normalizePathForWeb(const QString &path)
17+
{
18+
// Split path into components, lowercase and URL-encode each, then rejoin
19+
QStringList components = path.split('/');
20+
QStringList encoded;
21+
// Characters that encodeURI does NOT encode (excluding / which we handle via split)
22+
// These are: A-Za-z0-9 ; , ? : @ & = + $ - _ . ! ~ * ' ( ) #
23+
// We only pass the non-alphanumeric ones since alphanumerics are never encoded
24+
const QByteArray safeChars = ";,?:@&=+$-_.!~*'()#";
25+
for (const QString &component : components)
26+
{
27+
if (component.isEmpty())
28+
{
29+
continue;
30+
}
31+
// Lowercase and URL-encode (percent-encode) the component
32+
QString lowered = component.toLower();
33+
QString percentEncoded = QUrl::toPercentEncoding(lowered, safeChars);
34+
encoded.append(percentEncoded);
35+
}
36+
return encoded.join('/');
37+
}
38+
} // namespace
39+
1340
WebCache::WebCache(AOApplication *parent)
1441
: QObject(parent)
1542
, ao_app(parent)
@@ -61,7 +88,9 @@ QString WebCache::getCachedPath(const QString &relativePath) const
6188
return QString();
6289
}
6390

64-
QString localPath = cacheDir() + subdir + relativePath;
91+
// Use normalized (lowercase) path for cache lookup
92+
QString normalizedPath = normalizePathForWeb(relativePath);
93+
QString localPath = cacheDir() + subdir + normalizedPath;
6594

6695
if (!file_exists(localPath))
6796
{
@@ -123,42 +152,48 @@ void WebCache::resolveOrDownload(const QString &relativePath, const QStringList
123152
assetUrl += '/';
124153
}
125154

126-
// Check if already downloading this path
127-
if (m_pending_downloads.contains(relativePath))
128-
{
129-
return;
130-
}
131-
132-
// Check if this path previously failed (don't retry within this session)
133-
if (m_failed_downloads.contains(relativePath))
134-
{
135-
return;
136-
}
137-
138155
// Get cache subdirectory for this server's asset URL
139156
QString subdir = cacheSubdir();
140157
if (subdir.isEmpty())
141158
{
142159
return;
143160
}
144161

145-
// Try each suffix
146-
for (const QString &suffix : suffixes)
162+
// Try each suffix (like webAO tries multiple extensions)
163+
QStringList effectiveSuffixes = suffixes.isEmpty() ? QStringList{""} : suffixes;
164+
for (const QString &suffix : effectiveSuffixes)
147165
{
148166
QString fullPath = relativePath + suffix;
149-
QString localPath = cacheDir() + subdir + fullPath;
167+
168+
// Normalize path like webAO: lowercase and URL-encode
169+
QString normalizedPath = normalizePathForWeb(fullPath);
170+
171+
// Check if already downloading this path
172+
if (m_pending_downloads.contains(normalizedPath))
173+
{
174+
return;
175+
}
176+
177+
// Check if this path previously failed (don't retry within this session)
178+
if (m_failed_downloads.contains(normalizedPath))
179+
{
180+
continue; // Try next suffix
181+
}
182+
183+
QString localPath = cacheDir() + subdir + normalizedPath;
150184

151185
// Skip if already cached and not expired
152186
if (file_exists(localPath) && !isExpired(localPath))
153187
{
154-
continue;
188+
return; // Already have a valid cached file
155189
}
156190

157-
QString remoteUrl = assetUrl + fullPath;
191+
// Construct remote URL with normalized path
192+
QString remoteUrl = assetUrl + normalizedPath;
158193

159194
// Mark as pending and start download
160-
m_pending_downloads.insert(relativePath, true);
161-
startDownload(remoteUrl, localPath, relativePath);
195+
m_pending_downloads.insert(normalizedPath, true);
196+
startDownload(remoteUrl, localPath, normalizedPath);
162197
return; // Only try one suffix at a time
163198
}
164199
}

0 commit comments

Comments
 (0)