~hristoast/mousikofidi

1e61fe35a82699320389a89d41dfc49005528403 — Hristos N. Triantafillou 8 months ago c70b006
Clean up style handling

Moved most if not all of the logic about what CSS is rendered into the
python backend.

Now, the only template-side logic is there to conditionally load the
dark theme when light is selected.

The "/css" part of the paths for each theme has been trimmed out and
is now explicit; the prefix is properly computed based on if debug is
True or not and "/css" is a part of that computation.
3 files changed, 209 insertions(+), 265 deletions(-)

M mousikofidi/mousikofidi.py
M mousikofidi/templates/base.html
M test_mousikofidi.py
M mousikofidi/mousikofidi.py => mousikofidi/mousikofidi.py +57 -24
@@ 52,12 52,12 @@ LOGOS = {
}

THEMES = {
    "dark": "/css/water/dark.standalone",
    "light": "/css/water/light.standalone",
    "nes": "/css/nes/nes",
    "terminal": "/css/terminal",
    "terminal-green": "/css/terminal-green",
    "terminal-solarized": "/css/terminal-solarized",
    "dark": "/water/dark.standalone",
    "light": "/water/light.standalone",
    "nes": "/nes/nes",
    "terminal": "/terminal",
    "terminal-green": "/terminal-green",
    "terminal-solarized": "/terminal-solarized",
}

try:


@@ 284,11 284,6 @@ def request_context(config_data: dict) -> dict:
        icons = config_data["config"]["icons"]

    try:
        session_theme = session["theme"]
    except KeyError:
        session_theme = None

    try:
        queue = session["queue"]
    except KeyError:
        queue = []


@@ 298,7 293,10 @@ def request_context(config_data: dict) -> dict:
    except KeyError:
        username = None

    css, theme = select_css()

    return {
        "css": css,
        "debug": debug,
        "favicon_path": favicon,
        "icons": icons,


@@ 313,7 311,7 @@ def request_context(config_data: dict) -> dict:
        "secret_key": config_data["config"]["secret_key"],
        "site_name": config_data["config"]["site_name"],
        "queue": queue,
        "theme": session_theme or get_theme_from_config(config_data),
        "theme": theme,
        "username": username,
    }



@@ 462,6 460,20 @@ def init(
        )
        c["config"]["preload_video"] = False

    try:
        theme = c["config"]["theme"].lower()
        if theme in THEMES.keys():
            dbg("Using the configured theme: " + theme)
        else:
            wrn("Unrecognized theme: " + theme)
            wrn("Using the default theme: light")
            c["config"]["theme"] = "light"
    except KeyError:
        wrn(
            "No 'theme' value was found in the configuration file!  Defaulting to light..."
        )
        c["config"]["theme"] = "light"

    return c




@@ 622,14 634,6 @@ def get_metadata_value(key_list: list, metadata: dict) -> T[str, None]:
    return None


def get_theme_from_config(config: dict) -> str:
    theme = config["config"]["theme"].lower()
    if theme in THEMES.keys():
        return THEMES[theme]
    elif theme:
        return theme


def dir_dict(path: str) -> dict:
    return {"name": path.split(os.path.sep)[-1], "path": path.strip("/")}



@@ 1135,6 1139,33 @@ def select_logo(config: dict, item: str, fakenow=None) -> str:
        return config["config"][item]


def select_css() -> tuple:
    if debug:
        ext = ".css"
        path = "/static/"
    else:
        ext = ".min.css"
        path = "/"

    try:
        theme = session["theme"]
    except KeyError:
        theme = app.fidiConfig["config"]["theme"]

    css = [
        path + "css/normalize" + ext,
        path + "fa/css/fontawesome" + ext,
        path + "fa/css/solid" + ext,
        path + "css/fidi" + ext,
        path + "css" + THEMES[theme] + ext,
    ]

    if theme == "nes":
        css.append(path + "css/fidi-nes" + ext)

    return css, theme


def title_slug(title: str, slug_limit=20) -> str:
    return "".join(thing for thing in title if thing.isalnum()).lower()[:slug_limit]



@@ 1434,11 1465,13 @@ def settings():
    c = request_context(app.fidiConfig)

    themes = []
    for theme in THEMES.keys():
    for theme, path in THEMES.items():
        d = {"name": theme}
        if theme == "nes":
            themes.append({"name": theme, "proper": theme.upper()})
            d.update({"proper": theme.upper()})
        else:
            themes.append({"name": theme, "proper": theme.capitalize()})
            d.update({"proper": theme.capitalize()})
        themes.append(d)

    c["themes"] = themes
    c["page_name"] = "Settings"


@@ 1451,7 1484,7 @@ def settings_edit():
    _theme = request.form.get("theme")

    if _theme:
        session["theme"] = THEMES[_theme]
        session["theme"] = _theme

    if _icons == "disabled":
        dbg("DISABLING")

M mousikofidi/templates/base.html => mousikofidi/templates/base.html +8 -53
@@ 7,61 7,16 @@
    <meta name="author" content="{{ meta_author }}">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    {% if debug %}
      <link rel="stylesheet" href="/static/css/normalize.css">

      {% if icons %}
        <link rel="stylesheet" href="/static/fa/css/fontawesome.css">
        <link rel="stylesheet" href="/static/fa/css/solid.css">
      {% endif %}

      {% if "http" in theme and "//" in theme %}
        <!-- Custom theme -->
        <link rel="stylesheet" href="{{ theme }}">

      {% else %}
        <link rel="stylesheet" href="/static{{ theme }}.css">

        {% if "light" in theme %}
          <link media="(prefers-color-scheme: dark)" rel="stylesheet" href="/static/css/water/dark.standalone.css" type="text/css">
    {% for c in css %}
      <link href="{{ c }}" rel="stylesheet" type="text/css">
      {% if "light" in c %}
        {% if debug %}
          <link media="(prefers-color-scheme: dark)" rel="stylesheet" type="text/css" href="/static/css/water/water/dark.standalone.css" />
        {% else %}
          <link media="(prefers-color-scheme: dark)" rel="stylesheet" type="text/css" href="/css/water/water/dark.standalone.min.css" />
        {% endif %}

        <link rel="stylesheet" href="/static/css/fidi.css">
      {% endif %}

      {% if "nes" in theme %}
        {# Style patch for the NES theme #}
        <link rel="stylesheet" href="/static/css/fidi-nes.css">
      {% endif %}

    {% else %}
      <link rel="stylesheet" href="/css/normalize.min.css">

      {% if icons %}
        <link rel="stylesheet" href="/fa/css/fontawesome.min.css">
        <link rel="stylesheet" href="/fa/css/solid.min.css">
      {% endif %}

      {% if "http" in theme and "//" in theme %}
        <!-- Custom theme -->
        <link rel="stylesheet" href="{{ theme }}">

      {% else %}
        <link rel="stylesheet" href="{{ theme }}.min.css">

        {% if "light" in theme %}
          <link media="(prefers-color-scheme: dark)" rel="stylesheet" href="/css/water/dark.standalone.min.css" type="text/css">
        {% endif %}

        <link rel="stylesheet" href="/css/fidi.min.css">
      {% endif %}

      {% if "nes" in theme %}
        {# Style patch for the NES theme #}
        <link rel="stylesheet" href="/css/fidi-nes.min.css">
      {% endif %}

    {% endif %}
    {% endfor %}

    <link rel="icon" href="{{ favicon_path }}">


M test_mousikofidi.py => test_mousikofidi.py +144 -188
@@ 23,7 23,6 @@ import yaml
from datetime import datetime
from flask import session
from mousikofidi import (
    THEMES,
    app,
    breadcrumb_links_from_path,
    browse_dir,


@@ 35,7 34,6 @@ from mousikofidi import (
    get_metadata_dict,
    get_metadata_value,
    get_playlists,
    get_theme_from_config,
    handle_playlist_cmd,
    init,
    is_audio_file,


@@ 45,6 43,7 @@ from mousikofidi import (
    quote,
    request_context,
    select_cover_art,
    select_css,
    select_logo,
    title_slug,
)


@@ 443,6 442,13 @@ def test_browse_dir():
        "cover_art": "/serve/{}/MousikoFidi%20Sample%20Cover%20Art.png".format(
            example_dir.strip("/")
        ),
        "css": [
            "/css/normalize.min.css",
            "/fa/css/fontawesome.min.css",
            "/fa/css/solid.min.css",
            "/css/fidi.min.css",
            "/css/water/light.standalone.min.css",
        ],
        "file_list": [
            "{}/fake.flac".format(example_dir),
            "{}/fake.mp3".format(example_dir),


@@ 476,7 482,7 @@ def test_browse_dir():
        "secret_key": True,
        "site_name": "MousikóFídi - Your Music Cloud",
        "queue": [],
        "theme": "/css/water/light.standalone",
        "theme": "light",
        "username": None,
        "video_player": True,
        "page_name": "{}/example".format(sourcedir),


@@ 518,6 524,13 @@ def test_browse_file():
        "icons": False,
        "logo_path": logo,
        "page_name": "example",
        "css": [
            "/css/normalize.min.css",
            "/fa/css/fontawesome.min.css",
            "/fa/css/solid.min.css",
            "/css/fidi.min.css",
            "/css/water/light.standalone.min.css",
        ],
        "music_dirs": [
            {
                "full_path": "/home/username/music/flac",


@@ 543,7 556,7 @@ def test_browse_file():
        "secret_key": True,
        "site_name": "MousikóFídi - Your Music Cloud",
        "queue": [],
        "theme": "/css/water/light.standalone",
        "theme": "light",
        "username": None,
        "file_name": "example",
        "page_path": "",


@@ 614,6 627,13 @@ def test_request_context():
        logo = select_logo(c, "logo_path")

        assert r == {
            "css": [
                "/css/normalize.min.css",
                "/fa/css/fontawesome.min.css",
                "/fa/css/solid.min.css",
                "/css/fidi.min.css",
                "/css/water/light.standalone.min.css",
            ],
            "debug": False,
            "favicon_path": favicon,
            "icons": False,


@@ 641,7 661,7 @@ def test_request_context():
            "secret_key": True,
            "site_name": "MousikóFídi - Your Music Cloud",
            "queue": [],
            "theme": "/css/water/light.standalone",
            "theme": "light",
            "username": None,
        }



@@ 757,6 777,56 @@ def test_select_logo_dec_holiday():
    assert logo == "/fidi-dec.png"


def test_select_css_light():
    with app.test_request_context("/"):
        css, theme = select_css()
    assert css[-1] == "/css/water/light.standalone.min.css"
    assert theme == "light"


def test_select_css_dark():
    app.fidiConfig["config"]["theme"] = "dark"
    print(app.fidiConfig)
    with app.test_request_context("/"):
        css, theme = select_css()

    assert css[-1] == "/css/water/dark.standalone.min.css"
    assert theme == "dark"


def test_select_css_terminal():
    app.fidiConfig["config"]["theme"] = "terminal"
    print(app.fidiConfig)
    with app.test_request_context("/"):
        css, theme = select_css()

    assert css[-1] == "/css/terminal.min.css"
    assert theme == "terminal"


def test_select_css_terminal_green():
    app.fidiConfig["config"]["theme"] = "terminal-green"
    print(app.fidiConfig)
    with app.test_request_context("/"):
        css, theme = select_css()

    assert css[-1] == "/css/terminal-green.min.css"
    assert theme == "terminal-green"


def test_select_css_terminal_solarized():
    app.fidiConfig["config"]["theme"] = "terminal-solarized"
    print(app.fidiConfig)
    with app.test_request_context("/"):
        css, theme = select_css()

    # Reset the config
    app.fidiConfig["config"]["theme"] = "light"

    assert css[-1] == "/css/terminal-solarized.min.css"
    assert theme == "terminal-solarized"


def test_handle_playlist_cmd_add():
    with app.test_request_context("/"):
        example_dir = os.path.join(THIS_DIR, "example")


@@ 767,6 837,13 @@ def test_handle_playlist_cmd_add():
        favicon = select_logo(c, "favicon_path")
        logo = select_logo(c, "logo_path")
        expected_dict = {
            "css": [
                "/css/normalize.min.css",
                "/fa/css/fontawesome.min.css",
                "/fa/css/solid.min.css",
                "/css/fidi.min.css",
                "/css/water/light.standalone.min.css",
            ],
            "debug": False,
            "favicon_path": favicon,
            "icons": False,


@@ 794,7 871,7 @@ def test_handle_playlist_cmd_add():
            "secret_key": True,
            "site_name": "MousikóFídi - Your Music Cloud",
            "queue": ["{}/real.flac".format(example_dir)],
            "theme": "/css/water/light.standalone",
            "theme": "light",
            "username": None,
        }
        assert d == expected_dict


@@ 829,6 906,13 @@ def test_handle_playlist_cmd_add_multi():
        logo = select_logo(c, "logo_path")

        expected_dict = {
            "css": [
                "/css/normalize.min.css",
                "/fa/css/fontawesome.min.css",
                "/fa/css/solid.min.css",
                "/css/fidi.min.css",
                "/css/water/light.standalone.min.css",
            ],
            "debug": False,
            "favicon_path": favicon,
            "icons": False,


@@ 862,7 946,7 @@ def test_handle_playlist_cmd_add_multi():
                "{}/fake.mp4".format(example_dir),
                "{}/fake.webm".format(example_dir),
            ],
            "theme": "/css/water/light.standalone",
            "theme": "light",
            "username": None,
        }



@@ 879,6 963,13 @@ def test_handle_playlist_cmd_clear():
        favicon = select_logo(c, "favicon_path")
        logo = select_logo(c, "logo_path")
        expected_dict = {
            "css": [
                "/css/normalize.min.css",
                "/fa/css/fontawesome.min.css",
                "/fa/css/solid.min.css",
                "/css/fidi.min.css",
                "/css/water/light.standalone.min.css",
            ],
            "debug": False,
            "favicon_path": favicon,
            "icons": False,


@@ 906,7 997,7 @@ def test_handle_playlist_cmd_clear():
            "secret_key": True,
            "site_name": "MousikóFídi - Your Music Cloud",
            "queue": [],
            "theme": "/css/water/light.standalone",
            "theme": "light",
            "username": None,
        }
        assert d == expected_dict


@@ 950,6 1041,13 @@ def test_handle_playlist_cmd_rm():
        logo = select_logo(c, "logo_path")

        expected_dict = {
            "css": [
                "/css/normalize.min.css",
                "/fa/css/fontawesome.min.css",
                "/fa/css/solid.min.css",
                "/css/fidi.min.css",
                "/css/water/light.standalone.min.css",
            ],
            "debug": False,
            "favicon_path": favicon,
            "icons": False,


@@ 982,7 1080,7 @@ def test_handle_playlist_cmd_rm():
                "{}/real.ogg".format(example_dir),
                "{}/fake.webm".format(example_dir),
            ],
            "theme": "/css/water/light.standalone",
            "theme": "light",
            "username": None,
        }



@@ 1171,174 1269,6 @@ def test_get_playlists():
    assert playlists[3]["name"] == "PLAYLIST FILE 4"


def test_get_theme_from_config_dark():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: dark
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == THEMES["dark"]


def test_get_theme_from_config_light():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: light
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == THEMES["light"]


def test_get_theme_from_config_nes():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: nes
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == THEMES["nes"]


def test_get_theme_from_config_terminal():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: terminal
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == THEMES["terminal"]


def test_get_theme_from_config_terminal_green():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: terminal-green
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == THEMES["terminal-green"]


def test_get_theme_from_config_terminal_solarized():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: terminal-solarized
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == THEMES["terminal-solarized"]


def test_get_theme_from_config_none():
    config_string = """config:
  favicon_path: /fidi.png
  logo_path: /fidi.png
  music_dirs:
    - /home/username/musics
  playlist:
    dir: /home/username/music/playlists
    save: true
  preload_audio: false
  preload_video: false
  site_name: TestSite
  theme: https://mycooldomain.tld/css/mycoolstyle.css
"""
    config_file = tempfile.mkstemp(suffix=".yml")[1]
    with open(config_file, "w") as f:
        for line in config_string:
            f.write(line)
    c = init(use_config=config_file)
    theme = get_theme_from_config(c)
    os.remove(config_file)
    assert theme == "https://mycooldomain.tld/css/mycoolstyle.css"


def test_is_audio_file_flac():
    testfile = os.path.join(THIS_DIR, "example", "real.flac")
    is_audio = is_audio_file(testfile)


@@ 1562,7 1492,8 @@ def test_index_dark_theme(client):
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert (
        b'<link rel="stylesheet" href="/css/water/dark.standalone.min.css">' in rv.data
        b'<link href="/css/water/dark.standalone.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert bytes(title, "utf8") in rv.data
    assert (


@@ 1597,7 1528,8 @@ def test_index_light_theme(client):
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert (
        b'<link rel="stylesheet" href="/css/water/light.standalone.min.css">' in rv.data
        b'<link href="/css/water/light.standalone.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert bytes(title, "utf8") in rv.data
    assert (


@@ 1631,7 1563,14 @@ def test_index_nes_theme(client):
    logo = select_logo(app.fidiConfig, "logo_path")
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert b'<link rel="stylesheet" href="/css/nes/nes.min.css">' in rv.data
    assert (
        b'<link href="/css/nes/nes.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert (
        b'<link href="/css/fidi-nes.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert bytes(title, "utf8") in rv.data
    assert (
        bytes('<h4 class="center">Welcome to {}!</h4>'.format(site_name), "utf8")


@@ 1665,7 1604,10 @@ def test_index_terminal_theme(client):
    logo = select_logo(app.fidiConfig, "logo_path")
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert b'<link rel="stylesheet" href="/css/terminal.min.css">' in rv.data
    assert (
        b'<link href="/css/terminal.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert bytes(title, "utf8") in rv.data
    assert (
        bytes('<h4 class="center">Welcome to {}!</h4>'.format(site_name), "utf8")


@@ 1698,7 1640,15 @@ def test_index_terminal_green_theme(client):
    logo = select_logo(app.fidiConfig, "logo_path")
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert b'<link rel="stylesheet" href="/css/terminal-green.min.css">' in rv.data

    for line in rv.data.decode().split("\n"):
        if "css" in line:
            print(line)

    assert (
        b'<link href="/css/terminal-green.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert bytes(title, "utf8") in rv.data
    assert (
        bytes('<h4 class="center">Welcome to {}!</h4>'.format(site_name), "utf8")


@@ 1731,7 1681,10 @@ def test_index_terminal_solarized_theme(client):
    logo = select_logo(app.fidiConfig, "logo_path")
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert b'<link rel="stylesheet" href="/css/terminal-solarized.min.css">' in rv.data
    assert (
        b'<link href="/css/terminal-solarized.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert bytes(title, "utf8") in rv.data
    assert (
        bytes('<h4 class="center">Welcome to {}!</h4>'.format(site_name), "utf8")


@@ 1938,9 1891,12 @@ def test_dir_detail_found(client):
        in rv.data
    )
    assert (
        b'<link rel="stylesheet" href="/css/water/dark.standalone.min.css">' in rv.data
        b'<link href="/css/water/dark.standalone.min.css" rel="stylesheet" type="text/css">'
        in rv.data
    )
    assert (
        b'<link href="/css/fidi.min.css" rel="stylesheet" type="text/css">' in rv.data
    )
    assert b'<link rel="stylesheet" href="/css/fidi.min.css">' in rv.data
    assert (
        bytes(
            '<h2 class="center"><a href="/" title="The site name, and a link to the main index page.">{}</a></h2>'.format(


@@ 4014,15 3970,15 @@ def test_settings_edit(client):

    with app.app_context():
        client.post(url, data={"theme": "light"})
    assert session["theme"] == THEMES["light"]
    assert session["theme"] == "light"

    with app.app_context():
        client.post(url, data={"theme": "dark"})
    assert session["theme"] == THEMES["dark"]
    assert session["theme"] == "dark"

    with app.app_context():
        client.post(url, data={"theme": "nes"})
    assert session["theme"] == THEMES["nes"]
    assert session["theme"] == "nes"

    with app.app_context():
        client.post(url, data={"icons": "enabled"})