Skip to content

Commit 120c86b

Browse files
authored
Fix: Compressed files with unsupported chars in Windows are not correct displayed
1 parent 536f645 commit 120c86b

5 files changed

Lines changed: 183 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4343
- Turn page forward in manga mode (Only on non-arrow keys) [`bdbc0dc`](https://github.com/ollm/OpenComic/commit/bdbc0dc31ab37bdfcef570bf8f2130e39c08861e)
4444
- Check if the file is written to disk when extracting using 7zip [`66d4897`](https://github.com/ollm/OpenComic/commit/66d48977b4ef33b320676d39656d48ea41aff653)
4545
- Wrong size detection for animated AVIF images [`b741e52`](https://github.com/ollm/OpenComic/commit/b741e52c1fffac842a782fc45536eada123edeba)
46+
- Compressed files with unsupported chars in Windows are not correct displayed
4647

4748
## [v1.4.1](https://github.com/ollm/OpenComic/releases/tag/v1.4.1) (08-02-2025)
4849

scripts/file-manager.js

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ var file = function(path, _config = false) {
221221
if(json.error && !this.config.fromThumbnailsGeneration && !this.config.subtask)
222222
dom.compressedError({message: json.error}, false, sha1(this.path));
223223

224-
setFileSizes(path, json.files);
224+
setFileData(path, json.files);
225225

226226
return json.files;
227227
}
@@ -246,7 +246,7 @@ var file = function(path, _config = false) {
246246
if(!json || json.mtime != mtime)
247247
cache.writeJson(compressed.cacheFile, {mtime: mtime, files: files, metadata: metadata});
248248

249-
setFileSizes(path, files);
249+
setFileData(path, files);
250250

251251
return files;
252252

@@ -1514,18 +1514,20 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
15141514

15151515
this._filesToMultidimension = function(files, dimensions, from = false) {
15161516

1517-
let _files = [];
1517+
const _files = [];
15181518

15191519
for(let key in dimensions)
15201520
{
1521-
let value = dimensions[key];
1521+
const value = dimensions[key];
15221522

15231523
if(typeof value === 'number')
15241524
{
1525-
let file = files[value];
1525+
const file = files[value];
15261526

1527-
let data = {
1527+
const data = {
15281528
name: key,
1529+
fixedName: file.fixedName || key,
1530+
originalName: file.originalName || file.fixedName || key,
15291531
path: file.path,
15301532
folder: file.folder ? true : false,
15311533
fileSize: file.fileSize || 0,
@@ -1539,7 +1541,7 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
15391541
}
15401542
else
15411543
{
1542-
let _name = from ? p.join(from, key) : key;
1544+
const _name = from ? p.join(from, key) : key;
15431545

15441546
_files.push({
15451547
name: key,
@@ -1700,6 +1702,25 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
17001702

17011703
}
17021704

1705+
this.fixUnsupportedCharsInWindows = function(path) {
1706+
1707+
if(process.platform !== 'win32')
1708+
return path;
1709+
1710+
// Replace dots and spaces at the end of the filename
1711+
path = path.replace(/([\. ]+)([\\\/]|$)/g, function(match, unsupported, separator) {
1712+
1713+
return '_'.repeat(unsupported.length)+separator;
1714+
1715+
});
1716+
1717+
// Replace unsupported characters
1718+
path = path.replace(/[<>:|?*"]/g, '_');
1719+
1720+
return path;
1721+
1722+
}
1723+
17031724
// 7z
17041725
this._7z = false;
17051726

@@ -1737,9 +1758,14 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
17371758

17381759
if(data.file)
17391760
{
1740-
let name = _this.removeTmp(p.normalize(data.file));
1761+
if(/^D/.test(data.attributes) && !data.size) // Ignore directories
1762+
return;
17411763

1742-
files.push({name: name, path: p.join(_this.path, name), fileSize: data.size});
1764+
const originalName = _this.removeTmp(p.normalize(data.file));
1765+
const name = _this.fixUnsupportedCharsInWindows(originalName);
1766+
const same = originalName === name ? true : false;
1767+
1768+
files.push({name: name, fixedName: (!same ? name : ''), originalName: (!same ? originalName : ''), path: p.join(_this.path, name), fileSize: data.size});
17431769
_this.setFileStatus(name, {extracted: false});
17441770

17451771
readSome = true;
@@ -1775,8 +1801,26 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
17751801

17761802
let _this = this;
17771803

1778-
const onlyLen = this.config._only ? this.config._only.length : false;
1779-
const tasks = this.stackOnlyInTasks(this.config._only || false, 100);
1804+
const only = [];
1805+
const extractName = {};
1806+
1807+
let onlyLen = this.config._only ? this.config._only.length : false;
1808+
1809+
// Use the original file name and not the extracted one, which may be different on Windows due to incompatibility with certain characters (fixUnsupportedCharsInWindows)
1810+
for(let i = 0; i < onlyLen; i++)
1811+
{
1812+
const file = this.config._only[i];
1813+
const originalName = fileOriginalName.get(file) || file;
1814+
1815+
if(!extractName[originalName])
1816+
{
1817+
extractName[originalName] = file;
1818+
only.push(originalName);
1819+
}
1820+
}
1821+
1822+
onlyLen = only.lenght;
1823+
const tasks = this.stackOnlyInTasks(only || false, 100);
17801824

17811825
let result = false;
17821826

@@ -1800,7 +1844,9 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
18001844
{
18011845
_this.setProgress(_this.progressIndex++ / onlyLen);
18021846

1803-
const name = _this.removeTmp(p.normalize(data.file));
1847+
let name = _this.removeTmp(p.normalize(data.file));
1848+
name = extractName[name] || name;
1849+
18041850
const path = p.join(_this.path, name);
18051851
const realPath = p.join(_this.tmp, name);
18061852

@@ -2305,29 +2351,38 @@ var fileCompressed = function(path, _realPath = false, forceType = false, prefix
23052351

23062352
}
23072353

2354+
var fileDataInMap = new Set();
23082355
var fileSizes = new Map();
2309-
var fileSizesInMap = new Set();
2356+
var fileOriginalName = new Map();
23102357

2311-
function _setFileSizes(files)
2358+
function _setFileData(files)
23122359
{
23132360
for(let i = 0, len = files.length; i < len; i++)
23142361
{
23152362
const file = files[i];
23162363

23172364
if(file.files)
2318-
_setFileSizes(file.files);
2319-
else if(file.fileSize)
2320-
fileSizes.set(file.path, file.fileSize);
2365+
{
2366+
_setFileData(file.files);
2367+
}
2368+
else
2369+
{
2370+
if(file.fileSize)
2371+
fileSizes.set(file.path, file.fileSize);
2372+
2373+
if(file.fixedName && file.originalName)
2374+
fileOriginalName.set(file.fixedName, file.originalName);
2375+
}
23212376
}
23222377
}
23232378

2324-
function setFileSizes(path, files)
2379+
function setFileData(path, files)
23252380
{
2326-
if(fileSizesInMap.has(path))
2381+
if(fileDataInMap.has(path))
23272382
return;
23282383

2329-
fileSizesInMap.add(path);
2330-
_setFileSizes(files);
2384+
fileDataInMap.add(path);
2385+
_setFileData(files);
23312386
}
23322387

23332388
var extractingPromises = {};

scripts/migration.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function clearPdfAndEpubCache()
6262
{
6363
const file = files[i];
6464

65-
if(/\.json$/.test(file) || /\.json\.zstd$/.test(file))
65+
if(/^compressed-files-/.test(file))
6666
{
6767
const json = cache.readJson(file.replace(/\.zstd$/, ''));
6868
const first = json.files[0] ?? false;
@@ -255,6 +255,57 @@ function migratePasswordsAndTokensToSafeStorage(data)
255255
return data;
256256
}
257257

258+
function hasUnsupportedCharsInWindows(files)
259+
{
260+
for(let i = 0, len = files.length; i < len; i++)
261+
{
262+
const file = files[i];
263+
const path = file.path.replace(/^[a-z0-9]+\:/i, '');
264+
265+
// Dots and spaces at the end of the filename
266+
if(/([\. ]+)([\\\/]|$)/.test(path))
267+
return true;
268+
269+
// Unsupported characters
270+
if(/[<>:|?*"]/.test(path))
271+
return true;
272+
273+
if(file.files && hasUnsupportedCharsInWindows(file.files))
274+
return true;
275+
}
276+
277+
return false;
278+
}
279+
280+
function migrateCompressedFilesWithUnsupportedCharsInWindows(data)
281+
{
282+
if(process.platform !== 'win32')
283+
return data;
284+
285+
console.time('Migration: compressedFilesWithUnsupportedCharsInWindows');
286+
287+
const files = fs.readdirSync(cache.folder);
288+
289+
for(let i = 0, len = files.length; i < len; i++)
290+
{
291+
const file = files[i];
292+
293+
if(/^compressed-files-/.test(file))
294+
{
295+
const json = cache.readJson(file.replace(/\.zstd$/, ''));
296+
297+
if(hasUnsupportedCharsInWindows(json.files))
298+
fs.unlinkSync(p.join(cache.folder, file));
299+
}
300+
}
301+
302+
cache.flushJsonMemory();
303+
304+
console.timeEnd('Migration: compressedFilesWithUnsupportedCharsInWindows');
305+
306+
return data;
307+
}
308+
258309
function start(data)
259310
{
260311
let changes = data.config.changes;
@@ -285,6 +336,9 @@ function start(data)
285336
if(changes < 110) // Use safeStorage for passwords and tokens
286337
data = migratePasswordsAndTokensToSafeStorage(data);
287338

339+
if(changes < 113) // Fix compressed files with unsupported characters in Windows
340+
data = migrateCompressedFilesWithUnsupportedCharsInWindows(data);
341+
288342
data = opds.addNewDefaultCatalogs(data, changes);
289343

290344
return data;

scripts/storage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const safe = require(p.join(appDir, 'scripts/storage/safe.js'));
22

3-
const changes = 112; // Update this if readingPagesConfig is updated
3+
const changes = 113; // Update this if readingPagesConfig is updated
44

55
const readingPagesConfig = {
66
readingConfigName: '',

scripts/threads.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var threads = false;
22
var threadsList = {};
33
var queue = {default: []};
44
var queueIsStop = {default: false};
5+
var stats = {};
56
var onEnd = {};
67

78
async function job(key = 'default', options = {}, callback = false)
@@ -40,6 +41,7 @@ async function job(key = 'default', options = {}, callback = false)
4041
function addToQueue(key, options, callback, _arguments, promise)
4142
{
4243
if(!queue[key]) queue[key] = [];
44+
if(!stats[key]) stats[key] = {done: 0};
4345

4446
if(options.priorize)
4547
{
@@ -124,6 +126,8 @@ async function startJob(thread)
124126
thread.currentJob = false;
125127
thread.busy = false;
126128

129+
stats[thread.key].done++;
130+
127131
processJob(thread.key, thread.thread);
128132
}
129133
catch(error)
@@ -183,10 +187,56 @@ function end(key, callback)
183187
onEnd[key] = callback;
184188
}
185189

190+
function getStats(key = false)
191+
{
192+
const _stats = {};
193+
194+
for(let key in queue)
195+
{
196+
const list = Object.values(threadsList[key] ?? {});
197+
198+
_stats[key] = {
199+
key: key,
200+
done: stats[key]?.done || 0,
201+
busy: list.filter(thread => thread.busy).length,
202+
queue: queue[key]?.length,
203+
threads: (!list.length || list.length === threads) ? list.length : list.length - 1,
204+
};
205+
}
206+
207+
return key ? _stats[key] : _stats;
208+
}
209+
210+
function sumStats()
211+
{
212+
const sum = {
213+
done: 0,
214+
busy: 0,
215+
queue: 0,
216+
threads: 0,
217+
};
218+
219+
const stats = getStats();
220+
221+
for(let key in stats)
222+
{
223+
const stat = stats[key];
224+
225+
sum.done += stat.done;
226+
sum.busy += stat.busy;
227+
sum.queue += stat.queue;
228+
sum.threads += stat.threads;
229+
}
230+
231+
return sum;
232+
}
233+
186234
module.exports = {
187235
job: job,
188236
stop: stop,
189237
resume: resume,
190238
clean: clean,
191239
end: end,
240+
stats: getStats,
241+
sumStats: sumStats,
192242
}

0 commit comments

Comments
 (0)