Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5abf93824d |
No files matched your search
@@ -801,12 +801,11 @@ become `IMG_0001 (12345).JPG` and `IMG_0001 (12346).JPG`. A collection whose
|
|||||||
directory would have the name of another collection's JSON, again ignoring case,
|
directory would have the name of another collection's JSON, again ignoring case,
|
||||||
gets its ID added as well: beside an album `x`, whose JSON is `x.json`, an album
|
gets its ID added as well: beside an album `x`, whose JSON is `x.json`, an album
|
||||||
named `x.json` gets the directory `x.json (12)/`. A name too long for a file
|
named `x.json` gets the directory `x.json (12)/`. A name too long for a file
|
||||||
name, which most file systems limit to 255 bytes, gets its ID added too. A name
|
name, which most file systems limit to 255 bytes, is cut short between two
|
||||||
with its ID added is cut short between two characters, just far enough that it
|
characters, just far enough that it fits with its ID added, and gets its ID; a
|
||||||
fits: first the part before the ID, and once nothing is left of that, a
|
collection's directory name also leaves room for the `.json` its JSON adds. IDs
|
||||||
symlink's extension. A collection's directory name also leaves room for the
|
never change, so a name stays the same from run to run until such a clash
|
||||||
`.json` its JSON adds. IDs never change, so a name stays the same from run to
|
appears or goes away.
|
||||||
run until such a clash appears or goes away.
|
|
||||||
|
|
||||||
A collection whose directory or JSON cannot be written, such as one whose
|
A collection whose directory or JSON cannot be written, such as one whose
|
||||||
directory name is taken by a file you put in `collections/`, does not stop the
|
directory name is taken by a file you put in `collections/`, does not stop the
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ pre-1.0
|
|||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Five bugs are open: https://git.eeqj.de/sneak/quak/issues/196,
|
Three bugs are open: https://git.eeqj.de/sneak/quak/issues/196,
|
||||||
https://git.eeqj.de/sneak/quak/issues/198,
|
https://git.eeqj.de/sneak/quak/issues/198 and
|
||||||
https://git.eeqj.de/sneak/quak/issues/199,
|
https://git.eeqj.de/sneak/quak/issues/199. The check of quak against a live Ente
|
||||||
https://git.eeqj.de/sneak/quak/issues/205 and
|
|
||||||
https://git.eeqj.de/sneak/quak/issues/206. The check of quak against a live Ente
|
|
||||||
account, https://git.eeqj.de/sneak/quak/issues/187, needs an account only sneak
|
account, https://git.eeqj.de/sneak/quak/issues/187, needs an account only sneak
|
||||||
has, so it waits on him.
|
has, so it waits on him.
|
||||||
|
|
||||||
|
|||||||
+12
-11
@@ -274,14 +274,13 @@ const rebuildSymlink = (linkPath: string, target: string): void => {
|
|||||||
// name is used as is unless another entry would get the same name, ignoring
|
// name is used as is unless another entry would get the same name, ignoring
|
||||||
// case (two names that differ only in case are one entry on a case-insensitive
|
// case (two names that differ only in case are one entry on a case-insensitive
|
||||||
// file system), or it is too long for a file name; then it gets ` (<id>)`,
|
// file system), or it is too long for a file name; then it gets ` (<id>)`,
|
||||||
// right before the extension when `beforeExtension` is set. To fit, the part
|
// before the extension when `beforeExtension` is set, with the name before it
|
||||||
// before the ID is cut short as far as it must be, and once nothing is left of
|
// cut short as far as it must be to fit. With `withJSON`, each entry also has
|
||||||
// it, so is the extension. With `withJSON`, each entry also has the JSON file
|
// the JSON file `<name>.json` beside it, as an album does: a name then leaves
|
||||||
// `<name>.json` beside it, as an album does: a name then leaves room for
|
// room for `.json`, and one that is another entry's JSON name gets its ID
|
||||||
// `.json`, and one that is another entry's JSON name gets its ID added too.
|
// added too. A name with an ID added can match another entry's own name
|
||||||
// Two names with different IDs added never match, but one can match another
|
// (`IMG (6).JPG`), so this repeats until no name is shared. IDs are stable, so
|
||||||
// entry's own name (`IMG (6).JPG`), so this repeats until no name is shared.
|
// the names are too.
|
||||||
// IDs are stable, so the names are too.
|
|
||||||
const uniqueNames = (
|
const uniqueNames = (
|
||||||
entries: { id: number; name: string }[],
|
entries: { id: number; name: string }[],
|
||||||
opts: { beforeExtension?: boolean; withJSON?: boolean },
|
opts: { beforeExtension?: boolean; withJSON?: boolean },
|
||||||
@@ -289,9 +288,11 @@ const uniqueNames = (
|
|||||||
const maxBytes = MAX_NAME_BYTES - (opts.withJSON ? ".json".length : 0);
|
const maxBytes = MAX_NAME_BYTES - (opts.withJSON ? ".json".length : 0);
|
||||||
const withID = (id: number, name: string): string => {
|
const withID = (id: number, name: string): string => {
|
||||||
const tag = ` (${id})`;
|
const tag = ` (${id})`;
|
||||||
const fullExt = opts.beforeExtension ? extname(name) : "";
|
let ext = opts.beforeExtension ? extname(name) : "";
|
||||||
const stem = name.slice(0, name.length - fullExt.length);
|
// An extension too long to leave room for the ID is cut short with
|
||||||
const ext = shortenToBytes(fullExt, maxBytes - Buffer.byteLength(tag));
|
// the rest of the name.
|
||||||
|
if (Buffer.byteLength(tag + ext) > maxBytes) ext = "";
|
||||||
|
const stem = name.slice(0, name.length - ext.length);
|
||||||
const room = maxBytes - Buffer.byteLength(tag + ext);
|
const room = maxBytes - Buffer.byteLength(tag + ext);
|
||||||
return shortenToBytes(stem, room) + tag + ext;
|
return shortenToBytes(stem, room) + tag + ext;
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-56
@@ -1703,6 +1703,31 @@ describe("backup album folders", () => {
|
|||||||
expect(readLedger(outDir).files["5"]).toMatchObject({ attempts: 2 });
|
expect(readLedger(outDir).files["5"]).toMatchObject({ attempts: 2 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The extension of this title is 303 bytes, so the ID cannot go before
|
||||||
|
// it, and the whole title is cut short with the ID at its end. The
|
||||||
|
// extension holds a "-", so the original is saved with `.bin`, a name
|
||||||
|
// that fits.
|
||||||
|
it("cuts a title short, extension and all, when its extension leaves no room for the ID", async () => {
|
||||||
|
const outDir = join(root, "backup");
|
||||||
|
const lib = libraryOf([
|
||||||
|
{
|
||||||
|
collection: collection(10, "Trip"),
|
||||||
|
files: [file(1, 10, `a.${"L".repeat(300)}-x`)],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await runBackup(lib, { downloadDirectory: outDir });
|
||||||
|
|
||||||
|
const link = `a.${"L".repeat(249)} (1)`;
|
||||||
|
expect(Buffer.byteLength(link)).toBe(255);
|
||||||
|
expect(result.failed).toBe(0);
|
||||||
|
expect(tree(outDir)).toEqual([
|
||||||
|
"Trip/",
|
||||||
|
`Trip/${link} -> ${linkTo("1.bin")}`,
|
||||||
|
"Trip.json",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// The user has put a file in collections/ with the name of the album
|
// The user has put a file in collections/ with the name of the album
|
||||||
// Trip's folder, so the folder cannot be made.
|
// Trip's folder, so the folder cannot be made.
|
||||||
it("goes on to the next album when an album's folder cannot be made, and lists it as failed", async () => {
|
it("goes on to the next album when an album's folder cannot be made, and lists it as failed", async () => {
|
||||||
@@ -1737,62 +1762,6 @@ describe("backup album folders", () => {
|
|||||||
expect(readLedger(outDir).files["5"]).toMatchObject({ attempts: 1 });
|
expect(readLedger(outDir).files["5"]).toMatchObject({ attempts: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
// The extension of this title is 303 bytes, so it leaves no room for the
|
|
||||||
// ID: nothing is left of the name before the ID, and the extension is cut
|
|
||||||
// short after it. The extension holds a "-", so the original is saved
|
|
||||||
// with `.bin`, a name that fits.
|
|
||||||
it("cuts the extension of a title short when it leaves no room for the ID", async () => {
|
|
||||||
const outDir = join(root, "backup");
|
|
||||||
const lib = libraryOf([
|
|
||||||
{
|
|
||||||
collection: collection(10, "Trip"),
|
|
||||||
files: [file(1, 10, `a.${"L".repeat(300)}-x`)],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const result = await runBackup(lib, { downloadDirectory: outDir });
|
|
||||||
|
|
||||||
const link = ` (1).${"L".repeat(250)}`;
|
|
||||||
expect(Buffer.byteLength(link)).toBe(255);
|
|
||||||
expect(result.failed).toBe(0);
|
|
||||||
expect(tree(outDir)).toEqual([
|
|
||||||
"Trip/",
|
|
||||||
`Trip/${link} -> ${linkTo("1.bin")}`,
|
|
||||||
"Trip.json",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Files 1 and 3 share a title, so each gets its ID before the
|
|
||||||
// extension. File 2's title is too long, and its extension leaves no
|
|
||||||
// room for the ID. Were the ID put at the end of file 2's title cut
|
|
||||||
// short, its link would have file 1's name, and the link made second
|
|
||||||
// would replace the first.
|
|
||||||
it("keeps names unique when a title's extension leaves no room for the ID", async () => {
|
|
||||||
const outDir = join(root, "backup");
|
|
||||||
const shared = `x.${"Q".repeat(245)} (2)`;
|
|
||||||
const lib = libraryOf([
|
|
||||||
{
|
|
||||||
collection: collection(10, "Trip"),
|
|
||||||
files: [
|
|
||||||
file(1, 10, shared),
|
|
||||||
file(2, 10, `x (1).${"Q".repeat(250)}-`),
|
|
||||||
file(3, 10, shared),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const result = await runBackup(lib, { downloadDirectory: outDir });
|
|
||||||
|
|
||||||
expect(result.failed).toBe(0);
|
|
||||||
expect(tree(outDir)).toEqual([
|
|
||||||
"Trip/",
|
|
||||||
`Trip/ (2).${"Q".repeat(250)} -> ${linkTo("2.bin")}`,
|
|
||||||
`Trip/x (1).${"Q".repeat(245)} (2) -> ${linkTo("1.bin")}`,
|
|
||||||
`Trip/x (3).${"Q".repeat(245)} (2) -> ${linkTo("3.bin")}`,
|
|
||||||
"Trip.json",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes nothing on a second run over an unchanged account", async () => {
|
it("changes nothing on a second run over an unchanged account", async () => {
|
||||||
const outDir = join(root, "backup");
|
const outDir = join(root, "backup");
|
||||||
const lib = libraryOf([
|
const lib = libraryOf([
|
||||||
|
|||||||
Reference in new issue
Block a user