diff --git a/l4d2web/l4d2web/static/js/files-overlay/uploads.js b/l4d2web/l4d2web/static/js/files-overlay/uploads.js index f0f78e6..ce03fac 100644 --- a/l4d2web/l4d2web/static/js/files-overlay/uploads.js +++ b/l4d2web/l4d2web/static/js/files-overlay/uploads.js @@ -37,11 +37,29 @@ const uploadsList = uploadsPanel?.querySelector(".files-uploads-list"); const uploadsClearBtn = uploadsPanel?.querySelector(".files-uploads-clear"); - // Attach a path-collision suffix: foo.txt → foo (1).txt + // Attach a path-collision suffix: foo.txt → foo (1).txt. + // Recognizes common compressed-tar double-extensions so + // foo.tar.gz → foo (1).tar.gz, not foo.tar (1).gz. + const DOUBLE_EXTS = [ + ".tar.gz", ".tar.bz2", ".tar.xz", ".tar.zst", ".tar.lz", ".tar.lzma", + ]; function withCollisionSuffix(path) { - const dot = path.lastIndexOf("."); const slash = path.lastIndexOf("/"); - if (dot > slash + 0 && dot > -1) { + const stemStart = slash + 1; + const basename = path.slice(stemStart); + const lower = basename.toLowerCase(); + for (const ext of DOUBLE_EXTS) { + if (lower.endsWith(ext) && basename.length > ext.length) { + const cut = stemStart + basename.length - ext.length; + return path.slice(0, cut) + " (1)" + path.slice(cut); + } + } + // Single extension (or none). A dot must appear after the last + // slash to count as an extension — preserves the legacy behavior + // where a leading-dot basename like ".hidden" gets the suffix at + // the end rather than at position 0. + const dot = path.lastIndexOf("."); + if (dot > slash && dot > -1) { return path.slice(0, dot) + " (1)" + path.slice(dot); } return path + " (1)";