def main(data):
    html = pystache.render(
        data["templates"]["page"],
        {
            "title":
            "Page not found",
            "description":
            "Error 404: page not found",
            ## Since we don't know the depth of this page relative to the root,
            ## we have to assume the db directory is located in the root of this web resource
            "navigation":
            utils.generateTopBarNavigation(
                "/" + data["config"].get("Site", "DbPath")),
            "name":
            "error",
            "content":
            pystache.render(data["templates"]["not-found-page-contents"]),
            ## Since we don't know the depth of this page relative to the root,
            ## we have to assume the search page is located in the root of this web resource
            "search":
            "/" + data["definitions"]["filenames"]["search"],
        })
    notFoundFile = utils.mkfile(
        data["definitions"]["runtime"]["cwd"],
        data["config"].get("Filesystem", "DestinationDirPath"),
        data["definitions"]["filenames"]["notfound"],
    )
    notFoundFile.write(html)
    notFoundFile.close()
def main(data):
    homePathWebPageLink = utils.getWebPageLink(".", "Home")
    ## Render HTML
    listHtml = pystache.render(data["templates"]["list"], {
        "links": [
            utils.getWebPageLink(data["config"]["Site"]["DbPath"] + "/", "Database"),
            utils.getWebPageLink(data["config"]["Site"]["TranslationsPath"] + "/", "Translations"),
        ],
    })
    html = pystache.render(data["templates"]["page"], {
        "title":       data["config"]["Site"]["Name"],
        "description": "Web interface for Open Lyrics Database",
        "logo":        pystache.render(data["templates"]["link"], {
            "href": ".",
            "content": "Lyrics",
        }),
        "navigation":  utils.generateTopBarNavigation(data["config"]["Site"]["DbPath"] + "/"),
        "css":         data["definitions"]["filenames"]["css"],
        "search":      data["definitions"]["filenames"]["search"],
        "breadcrumbs": utils.getBreadcrumbs(data["templates"], homePathWebPageLink),
        "name":        "home",
        "content":     listHtml + pystache.render(data["templates"]["home-page-contents"], {
            "archiveLinkBranch": data["config"]["Source"]["DefaultBranch"],
        }),
    })
    homepageFile = utils.mkfile(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        data["definitions"]["filenames"]["index"]
    )
    homepageFile.write(html)
    homepageFile.close()
    ## Add home page link to sitemap
    if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
        data["sitemap"].append("/")
Ejemplo n.º 3
0
def main(data):
    fileHandle = utils.mkfile(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        ".nojekyll",
    )
    fileHandle.close()
Ejemplo n.º 4
0
def main(data):
    ## Create Google website verification file
    if data["config"].get("ThirdParty",
                          "GoogleSiteVerificationKey",
                          fallback=False):
        contents = "google-site-verification: google" + data["config"].get(
            "ThirdParty", "GoogleSiteVerificationKey") + ".html\n"
        fileHandle = utils.mkfile(
            data["definitions"]["runtime"]["cwd"],
            data["config"].get("Filesystem", "DestinationDirPath"),
            "google" +
            data["config"].get("ThirdParty", "GoogleSiteVerificationKey") +
            ".html",
        )
        fileHandle.write(contents)
        fileHandle.close()

    ## Create Yandex website verification file
    if data["config"].get("ThirdParty",
                          "YandexSiteVerificationKey",
                          fallback=False):
        contents = '<html>\n\
    <head>\n\
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n\
    </head>\n\
    <body>\n\
        Verification: ' + data["config"].get(
            "ThirdParty", "YandexSiteVerificationKey") + '\n\
    </body>\n\
</html>\n'

        fileHandle = utils.mkfile(
            data["definitions"]["runtime"]["cwd"],
            data["config"].get("Filesystem", "DestinationDirPath"),
            "yandex_" +
            data["config"].get("ThirdParty", "YandexSiteVerificationKey") +
            ".html",
        )
        fileHandle.write(contents)
        fileHandle.close()
Ejemplo n.º 5
0
def main(data):
    if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
        ## Write sitemap file
        sitemapFile = utils.mkfile(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["DestinationDirPath"],
            data["definitions"]["filenames"]["sitemap"],
        )
        xml = pystache.render(data["templates"]["sitemap"], {
            "links": map(lambda path: utils.resolveURL(data["config"]["Site"]["URL"], path), data["sitemap"]),
        })
        sitemapFile.write(xml)
        sitemapFile.close()
def main(data):
    url = urlparse(data["config"]["Site"]["Url"])
    contents = "Host: " + url.netloc + "\n"
    if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
        contents += "Sitemap: " + utils.resolveURL(
            url.geturl(), data["definitions"]["filenames"]["sitemap"]) + "\n"
    fileHandle = utils.mkfile(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        "robots.txt",
    )
    fileHandle.write(contents)
    fileHandle.close()
Ejemplo n.º 7
0
def main(data):
    homePathWebPageLink = utils.getWebPageLink("/", "Home")

    ## Output progress status
    print(utils.indent("Website translation HTML files"), file=sys.stderr)
    sys.stderr.flush()
    ## Generate link
    trPathWebPageLink = utils.getWebPageLink(
        data["config"]["Site"]["TranslationsPath"] + "/", "Translations")
    ## Create containing directory
    utils.mkdir(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        data["config"]["Site"]["TranslationsPath"],
    )
    ## Create root index file for translations directory
    translationsLinkList = []
    for groupKey in data["translations"]:
        link = pystache.render(data["templates"]["link"], {
            "href": groupKey + "/",
            "content": groupKey,
        })
        translationsLinkList.append(link)
    html = pystache.render(
        data["templates"]["page"], {
            "title":
            "List of available translations languages",
            "description":
            utils.getDescriptionList(list(data["translations"].keys())),
            "logo":
            pystache.render(
                data["templates"]["link"], {
                    "href":
                    ".." if data["config"].getboolean(
                        "Site", "UseRelativePaths", fallback=False) else "/",
                    "content":
                    "Lyrics",
                }),
            "navigation":
            utils.generateTopBarNavigation(
                utils.giveLinkDepth(data["config"]["Site"]["DbPath"] +
                                    "/", 1) if data["config"].
                getboolean("Site", "UseRelativePaths") else "/" +
                data["config"]["Site"]["DbPath"] + "/"),
            "css":
            utils.giveLinkDepth(data["definitions"]["filenames"]["css"], 1),
            "search":
            utils.giveLinkDepth(data["definitions"]["filenames"]["search"], 1),
            "breadcrumbs":
            utils.getBreadcrumbs(data["templates"], homePathWebPageLink,
                                 trPathWebPageLink),
            "name":
            "translations",
            "content":
            pystache.render(data["templates"]["db-page-contents"], {
                "links": translationsLinkList,
            }),
        })
    translationsFile = utils.mkfile(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        data["config"]["Site"]["TranslationsPath"],
        data["definitions"]["filenames"]["index"],
    )
    translationsFile.write(html)
    translationsFile.close()
    ## Add relative path to list of sitemap items
    if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
        data["sitemap"].append(data["config"]["Site"]["TranslationsPath"] +
                               "/")

    ## Loop through languages
    for languageKey in data["translations"]:
        ## Output progress status
        print(utils.indent(languageKey, 1), file=sys.stderr)
        sys.stderr.flush()
        ## Assign variables
        language = data["translations"][languageKey]
        ## Resolve paths
        languagePathSource = os.path.join(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["SourcePathTranslations"],
            languageKey,
        )
        languagePathDestination = os.path.join(
            data["config"]["Site"]["TranslationsPath"],
            languageKey,
        )
        ## Generate link
        languagePathWebPageLink = utils.getWebPageLink(
            languageKey + "/", languageKey,
            data["definitions"]["link_types"]["language"])
        ## Create containing directory
        utils.mkdir(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["DestinationDirPath"],
            languagePathDestination,
        )
        ## Render HTML
        pageLinks = []
        for groupKey in language:
            link = utils.getWebPageLink(
                groupKey + "/", groupKey,
                data["definitions"]["link_types"]["group"])
            pageLinks.append(link)
        listHtml = pystache.render(data["templates"]["list"],
                                   {"links": pageLinks})
        html = pystache.render(
            data["templates"]["page"], {
                "title":
                "Groups of artists containing " + languageKey +
                " translations",
                "description":
                utils.getDescriptionList(list(language.keys())),
                "logo":
                pystache.render(
                    data["templates"]["link"], {
                        "href":
                        "../.." if data["config"].getboolean(
                            "Site", "UseRelativePaths", fallback=False) else
                        "/",
                        "content":
                        "Lyrics",
                    }),
                "navigation":
                utils.generateTopBarNavigation(
                    utils.giveLinkDepth(data["config"]["Site"]["DbPath"] +
                                        "/", 2) if data["config"].
                    getboolean("Site", "UseRelativePaths") else "/" +
                    data["config"]["Site"]["DbPath"] + "/"),
                "css":
                utils.giveLinkDepth(data["definitions"]["filenames"]["css"],
                                    2),
                "search":
                utils.giveLinkDepth(data["definitions"]["filenames"]["search"],
                                    2),
                "breadcrumbs":
                utils.getBreadcrumbs(data["templates"], homePathWebPageLink,
                                     trPathWebPageLink,
                                     languagePathWebPageLink),
                "name":
                "language",
                "content":
                listHtml,
            })
        ## Create index HTML file
        htmlFile = utils.mkfile(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["DestinationDirPath"],
            languagePathDestination,
            data["definitions"]["filenames"]["index"],
        )
        ## Write rendered HTML into the index HTML file
        htmlFile.write(html)
        htmlFile.close()
        ## Add relative path to list of sitemap items
        if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
            data["sitemap"].append(languagePathDestination + "/")

        ## Loop through groups
        for groupKey in language:
            ## Output progress status
            print(utils.indent(groupKey, 2), file=sys.stderr)
            sys.stderr.flush()
            ## Assign variables
            group = data["translations"][languageKey][groupKey]
            ## Resolve paths
            groupPathSource = os.path.join(
                data["definitions"]["runtime"]["cwd"],
                data["config"]["Filesystem"]["SourcePathTranslations"],
                languageKey,
                groupKey,
            )
            groupPathDestination = os.path.join(
                data["config"]["Site"]["TranslationsPath"],
                languageKey,
                groupKey,
            )
            ## Generate link
            groupPathWebPageLink = utils.getWebPageLink(
                groupKey + "/", groupKey,
                data["definitions"]["link_types"]["group"])
            ## Create containing directory
            utils.mkdir(
                data["definitions"]["runtime"]["cwd"],
                data["config"]["Filesystem"]["DestinationDirPath"],
                groupPathDestination,
            )
            ## Render HTML
            pageLinks = []
            for artistKey in group:
                link = utils.getWebPageLink(
                    artistKey + "/", artistKey,
                    data["definitions"]["link_types"]["artist"])
                pageLinks.append(link)
            listHtml = pystache.render(data["templates"]["list"],
                                       {"links": pageLinks})
            html = pystache.render(
                data["templates"]["page"], {
                    "title":
                    "Artist group “" + groupKey + "” of " + languageKey +
                    " translations",
                    "description":
                    utils.getDescriptionList(list(group.keys())),
                    "logo":
                    pystache.render(
                        data["templates"]["link"], {
                            "href":
                            "../../.." if data["config"].getboolean(
                                "Site", "UseRelativePaths", fallback=False)
                            else "/",
                            "content":
                            "Lyrics",
                        }),
                    "navigation":
                    utils.generateTopBarNavigation(
                        utils.
                        giveLinkDepth(data["config"]["Site"]["DbPath"] +
                                      "/", 3) if data["config"].
                        getboolean("Site", "UseRelativePaths") else "/" +
                        data["config"]["Site"]["DbPath"] + "/"),
                    "css":
                    utils.giveLinkDepth(
                        data["definitions"]["filenames"]["css"], 3),
                    "search":
                    utils.giveLinkDepth(
                        data["definitions"]["filenames"]["search"], 3),
                    "breadcrumbs":
                    utils.getBreadcrumbs(
                        data["templates"], homePathWebPageLink,
                        trPathWebPageLink, languagePathWebPageLink,
                        groupPathWebPageLink),
                    "name":
                    "group",
                    "content":
                    listHtml,
                })
            ## Create index HTML file
            htmlFile = utils.mkfile(
                data["definitions"]["runtime"]["cwd"],
                data["config"]["Filesystem"]["DestinationDirPath"],
                groupPathDestination,
                data["definitions"]["filenames"]["index"],
            )
            ## Write rendered HTML into the index HTML file
            htmlFile.write(html)
            htmlFile.close()
            ## Add relative path to list of sitemap items
            if data["config"].getboolean("Site",
                                         "CreateSitemap",
                                         fallback=False):
                data["sitemap"].append(groupPathDestination + "/")

            ## Loop through artists
            for artistKey in group:
                ## Output progress status
                print(utils.indent(artistKey, 3), file=sys.stderr)
                sys.stderr.flush()
                ## Assign variables
                artist = group[artistKey]
                ## Resolve paths
                artistPathSource = os.path.join(
                    data["definitions"]["runtime"]["cwd"],
                    data["config"]["Filesystem"]["SourcePathTranslations"],
                    languageKey,
                    groupKey,
                    artistKey,
                )
                artistPathDestination = os.path.join(
                    data["config"]["Site"]["TranslationsPath"],
                    languageKey,
                    groupKey,
                    artistKey,
                )
                ## Generate link
                artistPathWebPageLink = utils.getWebPageLink(
                    artistKey + "/", artistKey,
                    data["definitions"]["link_types"]["artist"])
                ## Create containing directory
                utils.mkdir(
                    data["definitions"]["runtime"]["cwd"],
                    data["config"]["Filesystem"]["DestinationDirPath"],
                    artistPathDestination,
                )
                ## Render HTML
                pageLinks = []
                for release in artist["releases"]:
                    link = utils.getWebPageLink(
                        release["name"] + "/", release["printable_name"],
                        data["definitions"]["link_types"]["release"])
                    pageLinks.append(link)
                listHtml = pystache.render(data["templates"]["list"],
                                           {"links": pageLinks})
                html = pystache.render(
                    data["templates"]["page"], {
                        "title":
                        "Releases by " + artist["printable_name"] +
                        " containing " + languageKey + " translations",
                        "description":
                        utils.getDescriptionList(
                            list(map(lambda link: link["label"], pageLinks))),
                        "logo":
                        pystache.render(
                            data["templates"]["link"], {
                                "href":
                                "../../../.." if data["config"].getboolean(
                                    "Site", "UseRelativePaths", fallback=False)
                                else "/",
                                "content":
                                "Lyrics",
                            }),
                        "navigation":
                        utils.generateTopBarNavigation(
                            utils.giveLinkDepth(
                                data["config"]["Site"]["DbPath"] +
                                "/", 4) if data["config"].
                            getboolean("Site", "UseRelativePaths") else "/" +
                            data["config"]["Site"]["DbPath"] + "/"),
                        "css":
                        utils.giveLinkDepth(
                            data["definitions"]["filenames"]["css"], 4),
                        "search":
                        utils.giveLinkDepth(
                            data["definitions"]["filenames"]["search"], 4),
                        "breadcrumbs":
                        utils.getBreadcrumbs(
                            data["templates"], homePathWebPageLink,
                            trPathWebPageLink, languagePathWebPageLink,
                            groupPathWebPageLink, artistPathWebPageLink),
                        "name":
                        "artist",
                        "content":
                        listHtml,
                    })
                ## Create index HTML file
                htmlFile = utils.mkfile(
                    data["definitions"]["runtime"]["cwd"],
                    data["config"]["Filesystem"]["DestinationDirPath"],
                    artistPathDestination,
                    data["definitions"]["filenames"]["index"],
                )
                ## Write rendered HTML into the index HTML file
                htmlFile.write(html)
                htmlFile.close()
                ## Add relative path to list of sitemap items
                if data["config"].getboolean("Site",
                                             "CreateSitemap",
                                             fallback=False):
                    data["sitemap"].append(artistPathDestination + "/")

                ## Loop through releases
                for release in artist["releases"]:
                    ## Output progress status
                    print(utils.indent(release["name"], 4), file=sys.stderr)
                    sys.stderr.flush()
                    ## Resolve paths
                    releasePathSource = os.path.join(
                        data["definitions"]["runtime"]["cwd"],
                        data["config"]["Filesystem"]["SourcePathTranslations"],
                        languageKey,
                        groupKey,
                        artistKey,
                        release["name"],
                    )
                    releasePathDestination = os.path.join(
                        data["config"]["Site"]["TranslationsPath"],
                        languageKey,
                        groupKey,
                        artistKey,
                        release["name"],
                    )
                    ## Generate link
                    releasePathWebPageLink = utils.getWebPageLink(
                        release["name"] + "/", release["printable_name"],
                        data["definitions"]["link_types"]["release"])
                    ## Create containing directory
                    utils.mkdir(
                        data["definitions"]["runtime"]["cwd"],
                        data["config"]["Filesystem"]["DestinationDirPath"],
                        releasePathDestination,
                    )
                    ## Render HTML
                    pageLinks = []
                    for recording in release["recordings"]:
                        link = utils.getWebPageLink(
                            release["name"] + "/", release["printable_name"],
                            data["definitions"]["link_types"]["recording"])
                        pageLinks.append(link)
                    listHtml = pystache.render(data["templates"]["list"],
                                               {"links": pageLinks})
                    html = pystache.render(
                        data["templates"]["page"], {
                            "title":
                            artist["printable_name"] + " " + languageKey +
                            " translations from “" +
                            release["printable_name"] + "”",
                            "description":
                            utils.getDescriptionList(
                                list(map(lambda link: link["label"],
                                         pageLinks))),
                            "logo":
                            pystache.render(
                                data["templates"]["link"], {
                                    "href":
                                    "../../../../.."
                                    if data["config"].getboolean(
                                        "Site",
                                        "UseRelativePaths",
                                        fallback=False) else "/",
                                    "content":
                                    "Lyrics",
                                }),
                            "navigation":
                            utils.generateTopBarNavigation(
                                utils.giveLinkDepth(
                                    data["config"]["Site"]["DbPath"] +
                                    "/", 5) if data["config"].getboolean(
                                        "Site", "UseRelativePaths") else "/" +
                                data["config"]["Site"]["DbPath"] + "/"),
                            "css":
                            utils.giveLinkDepth(
                                data["definitions"]["filenames"]["css"], 5),
                            "search":
                            utils.giveLinkDepth(
                                data["definitions"]["filenames"]["search"], 5),
                            "breadcrumbs":
                            utils.getBreadcrumbs(
                                data["templates"], homePathWebPageLink,
                                trPathWebPageLink, languagePathWebPageLink,
                                groupPathWebPageLink, artistPathWebPageLink,
                                releasePathWebPageLink),
                            "name":
                            "release",
                            "content":
                            listHtml,
                        })
                    ## Create index HTML file
                    htmlFile = utils.mkfile(
                        data["definitions"]["runtime"]["cwd"],
                        data["config"]["Filesystem"]["DestinationDirPath"],
                        releasePathDestination,
                        data["definitions"]["filenames"]["index"],
                    )
                    ## Write rendered HTML into the index HTML file
                    htmlFile.write(html)
                    htmlFile.close()
                    ## Add relative path to list of sitemap items
                    if data["config"].getboolean("Site",
                                                 "CreateSitemap",
                                                 fallback=False):
                        data["sitemap"].append(releasePathDestination + "/")

                    ## Loop through recordings
                    for recordingGroup in release["recordings"]:
                        for recording in recordingGroup:
                            ## Output progress status
                            print(utils.indent(recording["name"], 5),
                                  file=sys.stderr)
                            sys.stderr.flush()
                            ## Resolve paths
                            recordingPathSource = os.path.join(
                                data["definitions"]["runtime"]["cwd"],
                                data["config"]["Filesystem"]
                                ["SourcePathTranslations"],
                                languageKey,
                                groupKey,
                                artistKey,
                                release["name"],
                                recording["name"],
                            )
                            recordingPathDestination = os.path.join(
                                data["config"]["Site"]["TranslationsPath"],
                                languageKey,
                                groupKey,
                                artistKey,
                                release["name"],
                                recording["name"],
                            )
                            ## Generate link
                            recordingPathWebPageLink = utils.getWebPageLink(
                                recording["name"] + "/",
                                recording["printable_name"],
                                data["definitions"]["link_types"]["recording"])
                            ## Create containing directory
                            utils.mkdir(
                                data["definitions"]["runtime"]["cwd"],
                                data["config"]["Filesystem"]
                                ["DestinationDirPath"],
                                recordingPathDestination,
                            )
                            ## Add action buttons
                            lyricsActionsList = []
                            if data["config"].getboolean(
                                    "Site", "HasEditTextButton"):
                                actionButton1 = pystache.render(data["templates"]["link"], {
                                    "href": data["config"]["Source"]["Repository"]  + "/edit/" + \
                                            data["config"]["Source"]["DefaultBranch"]  + "/translations/" + \
                                            languageKey + "/" + groupKey + "/" + artistKey + "/" + release["name"] + "/" + recording["name"],
                                    "content": "Suggest improvements for this translation",
                                })
                                lyricsActionsList.append(actionButton1)
                            ## Render HTML
                            html = pystache.render(
                                data["templates"]["page"], {
                                    "title":
                                    languageKey + " translation of “" +
                                    recording["printable_name"] + "” by " +
                                    artist["printable_name"],
                                    "description":
                                    utils.getDescriptionText(
                                        recording["text"]),
                                    "logo":
                                    pystache.render(
                                        data["templates"]["link"], {
                                            "href":
                                            "../../../../../.."
                                            if data["config"].getboolean(
                                                "Site",
                                                "UseRelativePaths",
                                                fallback=False) else "/",
                                            "content":
                                            "Lyrics",
                                        }),
                                    "navigation":
                                    utils.generateTopBarNavigation(
                                        utils.giveLinkDepth(
                                            data["config"]["Site"]["DbPath"] +
                                            "/", 6) if data["config"].
                                        getboolean("Site", "UseRelativePaths"
                                                   ) else "/" +
                                        data["config"]["Site"]["DbPath"] +
                                        "/"),
                                    "css":
                                    utils.giveLinkDepth(
                                        data["definitions"]["filenames"]
                                        ["css"], 6),
                                    "search":
                                    utils.giveLinkDepth(
                                        data["definitions"]["filenames"]
                                        ["search"], 6),
                                    "breadcrumbs":
                                    utils.getBreadcrumbs(
                                        data["templates"], homePathWebPageLink,
                                        trPathWebPageLink,
                                        languagePathWebPageLink,
                                        groupPathWebPageLink,
                                        artistPathWebPageLink,
                                        releasePathWebPageLink,
                                        recordingPathWebPageLink),
                                    "name":
                                    "recording",
                                    "content":
                                    utils.formatLyricsAndMetadata(
                                        data["templates"], recording["text"],
                                        recording["metadata"],
                                        lyricsActionsList),
                                })
                            ## Create index HTML file
                            htmlFile = utils.mkfile(
                                data["definitions"]["runtime"]["cwd"],
                                data["config"]["Filesystem"]
                                ["DestinationDirPath"],
                                recordingPathDestination,
                                data["definitions"]["filenames"]["index"],
                            )
                            ## Write rendered HTML into the index HTML file
                            htmlFile.write(html)
                            htmlFile.close()
                            ## Add relative path to list of sitemap items
                            if data["config"].getboolean("Site",
                                                         "CreateSitemap",
                                                         fallback=False):
                                data["sitemap"].append(
                                    recordingPathDestination + "/")
Ejemplo n.º 8
0
def main(data):
    homePathWebPageLink = utils.getWebPageLink("/", "Home")

    ## Output progress status
    print(utils.indent("Website database HTML files"), file=sys.stderr)
    sys.stderr.flush()
    ## Generate link
    dbPathWebPageLink = utils.getWebPageLink(data["config"]["Site"]["DbPath"] + "/", "Database")
    ## Create containing directory
    utils.mkdir(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        data["config"]["Site"]["DbPath"],
    )
    ## Render HTML
    databaseLinkList = []
    for groupKey in data["database"]:
        link = pystache.render(data["templates"]["link"], {
            "href": groupKey + "/",
            "content": groupKey,
        })
        databaseLinkList.append(link)
    html = pystache.render(data["templates"]["page"], {
        "title":       "Main database index page",
        "description": "List of database artist groups",
        "logo":        pystache.render(data["templates"]["link"], {
            "href": ".." if data["config"].getboolean("Site", "UseRelativePaths", fallback=False) else "/",
            "content": "Lyrics",
        }),
        "navigation":  utils.generateTopBarNavigation(),
        "css":         utils.giveLinkDepth(data["definitions"]["filenames"]["css"], 1),
        "search":      utils.giveLinkDepth(data["definitions"]["filenames"]["search"], 1),
        "breadcrumbs": utils.getBreadcrumbs(data["templates"], homePathWebPageLink, dbPathWebPageLink),
        "name":        "db",
        "content":     pystache.render(data["templates"]["db-page-contents"], {
            "links": databaseLinkList,
        }),
    })
    ## Create index HTML file
    htmlFile = utils.mkfile(
        data["definitions"]["runtime"]["cwd"],
        data["config"]["Filesystem"]["DestinationDirPath"],
        data["config"]["Site"]["DbPath"],
        data["definitions"]["filenames"]["index"],
    )
    ## Write rendered HTML into index HTML file
    htmlFile.write(html)
    htmlFile.close()
    ## Add relative path to list of sitemap items
    if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
        data["sitemap"].append(data["config"]["Site"]["DbPath"] + "/")

    ## Loop through groups
    for groupKey in data["database"]:
        ## Output progress status
        print(utils.indent(groupKey, 1), file=sys.stderr)
        sys.stderr.flush()
        ## Assign variables
        group = data["database"][groupKey]
        ## Resolve paths
        groupPathSource = os.path.join(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["SourcePath"],
            groupKey,
        )
        groupPathDestination = os.path.join(
            data["config"]["Site"]["DbPath"],
            groupKey,
        )
        ## Generate link
        groupPathWebPageLink = utils.getWebPageLink(groupKey + "/", groupKey)
        ## Create containing directory
        utils.mkdir(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["DestinationDirPath"],
            groupPathDestination,
        )
        ## Render HTML
        pageLinks = []
        for artistKey in group:
            link = utils.getWebPageLink(artistKey + "/", group[artistKey]["printable_name"], data["definitions"]["link_types"]["artist"])
            pageLinks.append(link)
        listHtml = pystache.render(data["templates"]["list"], { "links": pageLinks })
        html = pystache.render(data["templates"]["page"], {
            "title":       "Artists starting with “" + groupKey + "”",
            "description": utils.getDescriptionList(list(group.keys())),
            "logo":        pystache.render(data["templates"]["link"], {
                "href": "../.." if data["config"].getboolean("Site", "UseRelativePaths", fallback=False) else "/",
                "content": "Lyrics",
            }),
            "navigation":  utils.generateTopBarNavigation(utils.giveLinkDepth("", 1) if data["config"].getboolean("Site", "UseRelativePaths") else "/" + data["config"]["Site"]["DbPath"] + "/"),
            "css":         utils.giveLinkDepth(data["definitions"]["filenames"]["css"], 2),
            "search":      utils.giveLinkDepth(data["definitions"]["filenames"]["search"], 2),
            "breadcrumbs": utils.getBreadcrumbs(data["templates"], homePathWebPageLink, dbPathWebPageLink, groupPathWebPageLink),
            "name":        "group",
            "content":     listHtml,
        })
        ## Create index HTML file
        htmlFile = utils.mkfile(
            data["definitions"]["runtime"]["cwd"],
            data["config"]["Filesystem"]["DestinationDirPath"],
            groupPathDestination,
            data["definitions"]["filenames"]["index"],
        )
        ## Write rendered HTML into the index HTML file
        htmlFile.write(html)
        htmlFile.close()
        ## Add relative path to list of sitemap items
        if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
            data["sitemap"].append(groupPathDestination + "/")

        ## Loop through artists
        for artistKey in group:
            ## Output progress status
            print(utils.indent(artistKey, 2), file=sys.stderr)
            sys.stderr.flush()
            ## Assign variables
            artist = group[artistKey]
            ## Resolve paths
            groupArtistPathSource = os.path.join(
                groupPathSource,
                artistKey,
            )
            groupArtistPathDestination = os.path.join(
                groupPathDestination,
                artistKey,
            )
            ## Generate link
            groupArtistPathWebPageLink = utils.getWebPageLink(artistKey + "/", artist["printable_name"], data["definitions"]["link_types"]["artist"])
            ## Create containing directory
            utils.mkdir(
                data["definitions"]["runtime"]["cwd"],
                data["config"]["Filesystem"]["DestinationDirPath"],
                groupArtistPathDestination,
            )
            ## Render items list HTML
            pageLinks = []
            for release in artist["releases"]:
                link = utils.getWebPageLink(release["name"] + "/", release["printable_name"], data["definitions"]["link_types"]["release"])
                if "year" in release:
                    link["postfix"] = "(" + str(release["year"]) + ")"
                pageLinks.append(link)
            listHtml = pystache.render(data["templates"]["list"], { "links": pageLinks })
            ## Render page HTML
            html = pystache.render(data["templates"]["page"], {
                "title":       "Albums by " + artist["printable_name"],
                "description": utils.getDescriptionList(list(map(lambda link: link["label"], pageLinks))),
                "logo":        pystache.render(data["templates"]["link"], {
                    "href": "../../.." if data["config"].getboolean("Site", "UseRelativePaths", fallback=False) else "/",
                    "content": "Lyrics",
                }),
                "navigation":  utils.generateTopBarNavigation(utils.giveLinkDepth("", 2) if data["config"].getboolean("Site", "UseRelativePaths") else "/" + data["config"]["Site"]["DbPath"] + "/"),
                "css":         utils.giveLinkDepth(data["definitions"]["filenames"]["css"], 3),
                "search":      utils.giveLinkDepth(data["definitions"]["filenames"]["search"], 3),
                "breadcrumbs": utils.getBreadcrumbs(data["templates"], homePathWebPageLink, dbPathWebPageLink, groupPathWebPageLink, groupArtistPathWebPageLink),
                "name":        "artist",
                "content":     listHtml,
            })
            ## Create index HTML file
            htmlFile = utils.mkfile(
                data["definitions"]["runtime"]["cwd"],
                data["config"]["Filesystem"]["DestinationDirPath"],
                groupArtistPathDestination,
                data["definitions"]["filenames"]["index"],
            )
            ## Write rendered HTML into the index HTML file
            htmlFile.write(html)
            htmlFile.close()
            ## Add relative path to list of sitemap items
            if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
                data["sitemap"].append(groupArtistPathDestination + "/")

            ## Loop through releases
            for release in artist["releases"]:
                ## Output progress status
                print(utils.indent(release["name"], 3), file=sys.stderr)
                sys.stderr.flush()
                ## Resolve paths
                groupArtistReleasePathSource = os.path.join(
                    groupArtistPathSource,
                    release["name"],
                )
                groupArtistReleasePathDestination = os.path.join(
                    groupArtistPathDestination,
                    release["name"],
                )
                ## Generate link
                groupArtistReleasePathWebPageLink = utils.getWebPageLink(release["name"] + "/", release["printable_name"], data["definitions"]["link_types"]["release"])
                ## Create containing directory
                utils.mkdir(
                    data["definitions"]["runtime"]["cwd"],
                    data["config"]["Filesystem"]["DestinationDirPath"],
                    groupArtistReleasePathDestination,
                )
                ## Render items list HTML
                listHtml = ""
                metaDescriptionLinks = []
                for recordingGroup in release["recordings"]:
                    recordingGroupLinks = []
                    for recording in recordingGroup:
                        link = utils.getWebPageLink(
                            recording["name"] + "/" if len(recording["name"]) > 0 else "",
                            recording["printable_name"],
                            data["definitions"]["link_types"]["recording"]
                        )
                        if "prefix" in recording:
                            link["prefix"] = recording["prefix"]
                        if "postfix" in recording:
                            link["postfix"] = recording["postfix"]
                        recordingGroupLinks.append(link)
                        metaDescriptionLinks.append(link)
                    listHtml += pystache.render(data["templates"]["list"], { "links": recordingGroupLinks })
                ## Render page HTML
                html = pystache.render(data["templates"]["page"], {
                    "title":       "Release “" + release["printable_name"] + "” by " + artist["printable_name"],
                    "description": utils.getDescriptionList(list(map(lambda link: link["label"], metaDescriptionLinks))),
                    "logo":        pystache.render(data["templates"]["link"], {
                        "href": "../../../.." if data["config"].getboolean("Site", "UseRelativePaths", fallback=False) else "/",
                        "content": "Lyrics",
                    }),
                    "navigation":  utils.generateTopBarNavigation(utils.giveLinkDepth("", 3) if data["config"].getboolean("Site", "UseRelativePaths") else "/" + data["config"]["Site"]["DbPath"] + "/"),
                    "css":         utils.giveLinkDepth(data["definitions"]["filenames"]["css"], 4),
                    "search":      utils.giveLinkDepth(data["definitions"]["filenames"]["search"], 4),
                    "breadcrumbs": utils.getBreadcrumbs(data["templates"], homePathWebPageLink, dbPathWebPageLink, groupPathWebPageLink, groupArtistPathWebPageLink, groupArtistReleasePathWebPageLink),
                    "name":        "release",
                    "content":     listHtml,
                })
                ## Create index HTML file
                htmlFile = utils.mkfile(
                    data["definitions"]["runtime"]["cwd"],
                    data["config"]["Filesystem"]["DestinationDirPath"],
                    groupArtistReleasePathDestination,
                    data["definitions"]["filenames"]["index"],
                )
                ## Write rendered HTML into the index HTML file
                htmlFile.write(html)
                htmlFile.close()
                ## Add relative path to list of sitemap items
                if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
                    data["sitemap"].append(groupArtistReleasePathDestination + "/")

                ## Loop through recordings
                for recordingGroup in release["recordings"]:
                    for recording in recordingGroup:
                        ## Skip empty (gap) items
                        if len(recording["name"]) == 0: continue
                        ## Output progress status
                        print(utils.indent(recording["name"], 4), file=sys.stderr)
                        sys.stderr.flush()
                        ## Resolve paths
                        groupArtistReleaseRecordingPathSource = os.path.join(
                            groupArtistReleasePathSource,
                            recording["name"],
                        )
                        groupArtistReleaseRecordingPathDestination = os.path.join(
                            groupArtistReleasePathDestination,
                            recording["name"],
                        )
                        ## Generate link
                        groupArtistReleaseRecordingPathWebPageLink = utils.getWebPageLink(recording["name"] + "/", recording["printable_name"], data["definitions"]["link_types"]["recording"])
                        ## Create containing directory
                        utils.mkdir(
                            data["definitions"]["runtime"]["cwd"],
                            data["config"]["Filesystem"]["DestinationDirPath"],
                            groupArtistReleaseRecordingPathDestination,
                        )
                        ## Add action buttons
                        lyricsActionsList = []
                        if data["config"].getboolean("Site", "HasEditTextButton"):
                            actionButton1 = pystache.render(data["templates"]["link"], {
                                "href": data["config"]["Source"]["Repository"]  + "/edit/" + \
                                        data["config"]["Source"]["DefaultBranch"]  + "/database/" + \
                                        groupKey + "/" + artistKey + "/" + release["name"] + "/" + recording["name"],
                                "content": "Suggest improvements for this text",
                            })
                            lyricsActionsList.append(actionButton1)
                        ## Render HTML
                        html = pystache.render(data["templates"]["page"], {
                            "title":       "Text of “" + recording["printable_name"] + "” by " + artist["printable_name"],
                            "description": utils.getDescriptionText(recording["text"]),
                            "logo":        pystache.render(data["templates"]["link"], {
                                "href": "../../../../.." if data["config"].getboolean("Site", "UseRelativePaths", fallback=False) else "/",
                                "content": "Lyrics",
                            }),
                            "navigation":  utils.generateTopBarNavigation(utils.giveLinkDepth("", 4) if data["config"].getboolean("Site", "UseRelativePaths") else "/" + data["config"]["Site"]["DbPath"] + "/"),
                            "css":         utils.giveLinkDepth(data["definitions"]["filenames"]["css"], 5),
                            "search":      utils.giveLinkDepth(data["definitions"]["filenames"]["search"], 5),
                            "breadcrumbs": utils.getBreadcrumbs(data["templates"], homePathWebPageLink, dbPathWebPageLink, groupPathWebPageLink, groupArtistPathWebPageLink, groupArtistReleasePathWebPageLink, groupArtistReleaseRecordingPathWebPageLink),
                            "name":        "recording",
                            "content":     utils.formatLyricsAndMetadata(data["templates"], recording["text"], recording["metadata"], lyricsActionsList),
                        })
                        ## Create index HTML file
                        htmlFile = utils.mkfile(
                            data["definitions"]["runtime"]["cwd"],
                            data["config"]["Filesystem"]["DestinationDirPath"],
                            groupArtistReleaseRecordingPathDestination,
                            data["definitions"]["filenames"]["index"],
                        )
                        ## Write rendered HTML into the index HTML file
                        htmlFile.write(html)
                        htmlFile.close()
                        ## Add relative path to list of sitemap items
                        if data["config"].getboolean("Site", "CreateSitemap", fallback=False):
                            data["sitemap"].append(groupArtistReleaseRecordingPathDestination + "/")