File > Export To > Another Application > XML
The script below can convert:
- an .xml file exported from DL3
- to a .bex file (json content, importable to Bookends)
File > Import References > From File or Clipboard > Import references using: Bookends
You would need to edit the file paths at the top of the script to match your own
(or perhaps script the addition of fileChooser and fileSave dialogs)
This script uses the osascript -l JavaScript idiom ("JavaScript for Automation" rather than "AppleScript")
so if you are using Script Editor, then the language selector dropdown at top left needs to read JavaScript
Code: Select all
(() => {
"use strict";
// main :: IO ()
const main = () => {
const
fpDL3xml = "~/Desktop/Library Export 2021-09-24.xml",
fpBookendsJSON = "~/Desktop/Library Export 2021-09-24.bex";
return either(
alert("Bookends JSON from Delicious 3 XML")
)(
json => (
writeFile(fpBookendsJSON)(
showJSON(json)
),
json
)
)(
bindLR(
readPlistArrayFileLR(fpDL3xml)
)(
xs => Right(xs.map(x => ({
type: beTypeFromAzType(x.type),
title: x.title,
authors: x.creatorsCompositeString,
publisher: x.publishersCompositeString,
thedate: beDateFromISO8601(
x.publishDate
)
})))
)
);
};
// ---------- BOOKENDS JSON FROM DL3 PLIST -----------
// beTypeFromAzType :: String -> String
const beTypeFromAzType = s =>
({
"Book": "2"
})[s] || "2";
const beDateFromISO8601 = s =>
Boolean(s) ? (
"Date" !== s.constructor.name ? (
`${s}`
) : s.toJSON().slice(0, 4)
) : "";
// ---------------------- PLIST ----------------------
// readPlistArrayFileLR :: FilePath -> Either String Object
const readPlistArrayFileLR = fp =>
bindLR(
doesFileExist(fp) ? (
Right(filePath(fp))
) : Left(`No file found at path:\n\t${fp}`)
)(fpFull => {
const
e = $(),
maybeDict = (
$.NSArray
.arrayWithContentsOfURLError(
$.NSURL.fileURLWithPath(fpFull),
e
)
);
return maybeDict.isNil() ? (() => {
const
msg = ObjC.unwrap(
e.localizedDescription
);
return Left(`readPlistFileLR:\n\t${msg}`);
})() : Right(ObjC.deepUnwrap(maybeDict));
});
// ----------------------- JXA -----------------------
// alert :: String => String -> IO String
const alert = title =>
s => {
const sa = Object.assign(
Application("System Events"), {
includeStandardAdditions: true
});
return (
sa.activate(),
sa.displayDialog(s, {
withTitle: title,
buttons: ["OK"],
defaultButton: "OK"
}),
s
);
};
// --------------------- GENERIC ---------------------
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// doesFileExist :: FilePath -> IO Bool
const doesFileExist = fp => {
const ref = Ref();
return $.NSFileManager.defaultManager
.fileExistsAtPathIsDirectory(
$(fp)
.stringByStandardizingPath, ref
) && 1 !== ref[0];
};
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
// Application of the function fl to the
// contents of any Left value in e, or
// the application of fr to its Right value.
fr => e => "Left" in e ? (
fl(e.Left)
) : fr(e.Right);
// filePath :: String -> FilePath
const filePath = s =>
// The given file path with any tilde expanded
// to the full user directory path.
ObjC.unwrap(ObjC.wrap(s)
.stringByStandardizingPath);
// showJSON :: a -> String
const showJSON = x =>
// Indented JSON representation of the value x.
JSON.stringify(x, null, 2);
// writeFile :: FilePath -> String -> IO ()
const writeFile = fp => s =>
$.NSString.alloc.initWithUTF8String(s)
.writeToFileAtomicallyEncodingError(
$(fp)
.stringByStandardizingPath, false,
$.NSUTF8StringEncoding, null
);
// MAIN ---
return main();
})();