Importing from Delicious Library 3

Users asking other users for AppleScripts that work with Bookends.
Post Reply
ComplexPoint
Posts: 9
Joined: Sun Jun 26, 2016 7:19 pm

Importing from Delicious Library 3

Post by ComplexPoint »

From Delicious Library 3 we can export a default XML file

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)
At the Bookends end, the menu path is:

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
Screenshot 2021-09-24 at 14.45.03.png
Screenshot 2021-09-24 at 14.45.03.png (9.37 KiB) Viewed 36499 times

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();
})();
Post Reply