~hristoast/mousikofidi

caf13195b86ce0aedf46a5615dcbd65ac8d0f176 — Hristos N. Triantafillou 9 months ago e123ecc
Rename the 'user playlist' to queue, and all the related changes

Additionally, the nav "Playlist" link is now two links; one for all
playlists (if there are any), and another for the user queue.
M mousikofidi/mousikofidi.py => mousikofidi/mousikofidi.py +17 -17
@@ 289,9 289,9 @@ def request_context(config_data: dict) -> dict:
        session_theme = None

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

    try:
        username = session["username"]


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


@@ 723,14 723,14 @@ completed because it would cause the playlist to exceed the max cookie size!</p>
<p class="bold center red">See <a href="https://man.sr.ht/~hristoast/mousikofidi/user_guide.md#adding-tracks-to-the-playlist">
this User's Guide entry</a> for more information.</p>"""

    p_size = len(bytes(str(context["user_playlist"]), "utf8"))
    p_size = len(bytes(str(context["queue"]), "utf8"))
    dbg("p_size: " + str(p_size))

    if cmd == "add":
        new_size = p_size + len(bytes(str(path), "utf8"))
        if new_size < MAX_COOKIE_SIZE:
            dbg("Adding item to playlist: " + path)
            context["user_playlist"].append(path)
            context["queue"].append(path)

        else:
            dbg("Cookie overflow detected, preventing add operation...")


@@ 744,7 744,7 @@ this User's Guide entry</a> for more information.</p>"""
            dbg("Bulk adding items to playlist:")
            for path in bulk:
                dbg("Adding: " + path)
                context["user_playlist"].append(path)
                context["queue"].append(path)
            flash(
                """<p class="bold center green">All files in this dir added to your playlist!</p>"""
            )


@@ 755,25 755,25 @@ this User's Guide entry</a> for more information.</p>"""

    elif cmd == "clear":
        if path == "/all":
            if len(bytes(str(context["user_playlist"]), "utf8")) < MAX_COOKIE_SIZE:
            if len(bytes(str(context["queue"]), "utf8")) < MAX_COOKIE_SIZE:
                flash(  # TODO: Use url_for here
                    """<p class="bold center green">Your playlist was cleared!</p>
<form id="playlistctl" action="/playlist/clear/undo" class="center" method="post">
  <input class="center" id="undo-clear" name="undo-clear" title="Undo clearing the playlist." value="Undo?" type="submit" />
  <input id="to-load" name="to-load" type="hidden" value="{}">
</form>""".format(
                        quote(str(context["user_playlist"]))
                        quote(str(context["queue"]))
                    )
                )
            else:
                flash(
                    """<p class="bold center green">Your playlist was cleared but it was too big for an undo!</p>"""
                )
            context["user_playlist"] = []
            context["queue"] = []

        elif path == "/undo":
            dbg("Undoing playlist clear!")
            context["user_playlist"] = ast.literal_eval(
            context["queue"] = ast.literal_eval(
                urllib.parse.unquote(request.form["to-load"])
            )
            flash('<p class="bold center green">The playlist clear was undone!</p>')


@@ 824,7 824,7 @@ this User's Guide entry</a> for more information.</p>"""

                        if os.path.isfile(_line) and is_valid_path(context, _line):
                            dbg("Loading track: " + _line)
                            context["user_playlist"].append(_line)
                            context["queue"].append(_line)
                            loaded.append(_line)

                        else:


@@ 859,7 859,7 @@ this User's Guide entry</a> for more information.</p>"""
                get_metadata_dict(path)["title"]
            )
        )
        context["user_playlist"].remove(path)
        context["queue"].remove(path)

    elif cmd == "save":
        allowed_chars = (" ", "_", "+")


@@ 893,7 893,7 @@ this User's Guide entry</a> for more information.</p>"""
  <input id="file-name" name="file-name" type="hidden" value="{0}">
  <input id="to-save" name="to-save" type="hidden" value='{1}'>
</form></p>""".format(
                        cleaned_file_name, quote(str(context["user_playlist"]))
                        cleaned_file_name, quote(str(context["queue"]))
                    )
                )
                return context


@@ 1211,7 1211,7 @@ def dir_detail(path):
    return render_template("dir_detail.html", **c)


@app.route("/playlist")
@app.route("/queue")
def playlist():
    c = request_context(app.fidiConfig)
    _audio_list = []


@@ 1219,7 1219,7 @@ def playlist():
    file_list = []
    audio_list = None
    video_list = None
    playlist_items = c["user_playlist"]
    playlist_items = c["queue"]
    playlist_names = []

    for p in c["playlists"]:


@@ 1250,7 1250,7 @@ def playlist():
    c["playlist_names"] = playlist_names
    c["playlist_rm"] = True
    c["playlistctl"] = True
    c["page_name"] = "Playlist"
    c["page_name"] = "Queue"
    c["top_link"] = True
    return render_template("playlist.html", **c)



@@ 1343,7 1343,7 @@ def playlistctl(cmd, path):
    ):
        u += "#{}".format(request.form["slug"] + "-target")

    session["user_playlist"] = c["user_playlist"]
    session["queue"] = c["queue"]

    return redirect(u)


M mousikofidi/templates/audio_table_rows.html => mousikofidi/templates/audio_table_rows.html +1 -1
@@ 1,7 1,7 @@
<tr style="position: relative;">

  <td class="center"> {# PLAY #}
    {% if page_name != "Playlist" %}
    {% if page_name != "Queue" %}
      <div class="anchor" id="{{ audio.slug }}-target"></div>
    {% endif %}
    <div class="play-arrow" data-browse="{{ url_for('.dir_detail', path=audio.file_path) }}" data-num="0" data-path="{{ audio.file_path }}" data-title="{% if audio.title %}{{ audio.title }}{% else %}{{ audio.file_name }}{% endif %}" id="{{ audio.slug }}" title="Click to play this track."></div>

M mousikofidi/templates/base.html => mousikofidi/templates/base.html +43 -5
@@ 80,18 80,56 @@

    <nav>
      <ul>
        <li><a href="/browse" title="A link to the browse page.">{% if icons %}<i class="fas fa-book-open"></i> {% endif %}Browse</a></li>

        <li>
          <a href="/browse" title="A link to the browse page.">
            {% if icons %}
              <i class="fas fa-book-open"></i>
            {% endif %}Browse
          </a>
        </li>

        {% if secret_key %}
          {% if playlists or user_playlist %}
            <li><a href="{{ url_for('.playlist') }}" title="A link to the playlist page.">{% if icons %}<i class="fas fa-list"></i> {% endif %}Playlist</a></li>

          {% if playlists %}
            <li>
              <a href="{{ url_for('.playlists') }}" title="A link to the playlist page.">
                {% if icons %}
                  <i class="fas fa-list"></i>
                {% endif %}Playlists
              </a>
            </li>
          {% endif %}

            <li><a href="{{ url_for('.playlist') }}" title="A link to the playlist page.">
              {% if icons %}
                <i class="fas fa-list"></i>
              {% endif %}Queue
            </a>
            </li>

        {% endif %}

        {% if search %}
          <li><a href="{{ url_for('.search') }}" title="A link to the search page.">{% if icons %}<i class="fas fa-search"></i> {% endif %}Search</a></li>
          <li>
            <a href="{{ url_for('.search') }}" title="A link to the search page.">
              {% if icons %}
                <i class="fas fa-search"></i>
              {% endif %}Search
            </a>
          </li>
        {% endif %}

        {% if secret_key %}
          <li><a href="{{ url_for('.settings') }}" title="A link to the settings page.">{% if icons %}<i class="fas fa-user-cog"></i> {% endif %}Settings</a></li>
          <li>
            <a href="{{ url_for('.settings') }}" title="A link to the settings page.">
              {% if icons %}
                <i class="fas fa-user-cog"></i>
              {% endif %}Settings
            </a>
          </li>
        {% endif %}

        <li id="logo">
          <a id="logo" href="{% if video_player or video_list %}#videoplayer{% elif audio_player or item_list %}#audio-time-link{% else %}/{% endif %}">
            <img alt="Fidi Logo and {% if video_player or video_list %}video player{% elif audio_player %}player{% else %}home{% endif %} link" src="{{ logo_path }}" title="The MousikóFídi logo, and a link to the {% if video_player or video_list %}video player{% elif audio_player %}player{% else %}main index page{% endif %}.">

M mousikofidi/templates/dirs.html => mousikofidi/templates/dirs.html +0 -5
@@ 24,9 24,4 @@
    </tbody>
  </table>

  {% if plists %}
    <h3 class="center">Playlists:</h3>
    {% include 'plist_table.html' with context %}
  {% endif %}

{% endblock %}

M mousikofidi/templates/file_detail.html => mousikofidi/templates/file_detail.html +1 -1
@@ 3,7 3,7 @@
  <input id="track-detail-no-slug" name="slug" type="hidden" value="track-detail-no-slug">
</form>

{% if path in user_playlist %}
{% if path in queue %}
  <p class="center">This track is currently in your playlist.</p>
{% endif %}


M mousikofidi/templates/playlist.html => mousikofidi/templates/playlist.html +10 -9
@@ 2,7 2,7 @@
{% block content %}

  {% if not audio_list and not video_list %}
    {% if page_name == "Playlist" %}
    {% if page_name == "Queue" %}
      {% if playlists %}
        <h2 class="center"><a href="{{ url_for('.playlists') }}">{% if icons %}<i class="fas fa-list"></i> {% endif %}View All Playlists</a></h2>
      {% else %}


@@ 11,8 11,9 @@
    {% endif %}
  {% endif %}

  {% if page_name == "Playlist" %}
  {% if page_name == "Queue" %}
    {% if playlists %}
      <h2 class="center"><a href="{{ url_for('.playlists') }}">{% if icons %}<i class="fas fa-list"></i> {% endif %}View All Playlists</a></h2>
      <form action="{{ url_for('.playlistctl', cmd='load', path='load') }}" class="center" id="playlistctl" method="post">
        {% if playlist_save %}
          <input class="center" id="delete" name="delete" title="Delete the selected playlist file." value="Delete Playlist" type="submit" />


@@ 28,29 29,28 @@
  {% endif %}

  {% if audio_list or video_list %}
    {% if page_name == "Playlist" %}
    {% if page_name == "Queue" %}
      {% if playlistctl %}
        <form action="{{ url_for('.playlistctl', cmd='clear', path='all') }}" class="center" id="playlistctl" method="post">
          <input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="New Playlist" type="submit" />
          <input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="Clear Queue" type="submit" />
        </form>
        {% if playlist_save %}
          <form action="{{ url_for('.playlistctl', cmd='save', path='save') }}" class="center" id="playlistctl" method="post">
            <input class="center" id="save" name="save" title="Save this playlist as a .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Playlist" type="submit" />
            <input id="file-name" name="file-name" type="text" placeholder="Playlist name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>
            <input class="center" id="save" name="save" title="Save this queue as an .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Queue" type="submit" />
            <input id="file-name" name="file-name" type="text" placeholder="Name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>
            <input id="item-list" name="bulk-list" type="hidden" value="{{ file_list }}">
          </form>
        {% endif %}
      {% endif %}
      <h2 class="center"><a href="{{ url_for('.playlists') }}">{% if icons %}<i class="fas fa-list"></i> {% endif %}View All Playlists</a></h2>
    {% endif %}

    {% if playlist_detail %}
      <h2 class="center"><a href="{{ url_for('.playlists') }}">{% if icons %}<i class="fas fa-list"></i> {% endif %}View All Playlists</a></h2>
      <h2 class="bold center">Playlist Name: {{ playlist_name }}</h2>
      <form action="{{ url_for('.playlistctl', cmd='load', path='load-with-redirect') }}" class="center" id="playlistctl" method="post">
        <input class="center" id="load" name="load" title="Load tracks from this playlist into your user session playlist.  This will redirect you to the playlist page." value="Load This Playlist" type="submit" />
        <input id="to-load" name="to-load" type="hidden" value="{{ playlist_name }}">
      </form>
      <h2 class="center"><a href="{{ url_for('.playlists') }}">{% if icons %}<i class="fas fa-list"></i> {% endif %}View All Playlists</a></h2>
    {% endif %}

    {% include "item_table.html" with context %}


@@ 65,7 65,8 @@
  {% endif %}

  {% if not audio_list and not video_list %}
    {% if page_name == "Playlist" %}
    <p class="center">Your queue is empty, click the "<span class="bold">+</span>" icon next to any track to add it.</p>
    {% if page_name == "Queue" %}
      <div class="center">
        <img src="{{ logo_path }}">
      </div>

M mousikofidi/templates/video_table_rows.html => mousikofidi/templates/video_table_rows.html +1 -1
@@ 1,7 1,7 @@
<tr>

  <td class="center"> {# PLAY #}
    {% if page_name != "Playlist" %}
    {% if page_name != "Queue" %}
      <div class="anchor" id="{{ video.slug }}-target"></div>
    {% endif %}
    <div class="video-arrow" data-browse="{{ url_for('.dir_detail', path=video.file_path) }}" data-num="0" data-path="{{ video.file_path }}" data-title="{% if video.title %}{{ video.title }}{% else %}{{ video.file_name }}{% endif %}" id="{{ video.slug }}" title="Click to play this track."></div>

M test_mousikofidi.py => test_mousikofidi.py +48 -268
@@ 475,7 475,7 @@ def test_browse_dir():
        "preload_video": False,
        "secret_key": True,
        "site_name": "MousikóFídi - Your Music Cloud",
        "user_playlist": [],
        "queue": [],
        "theme": "/css/water/light.standalone",
        "username": None,
        "video_player": True,


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


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


@@ 793,7 793,7 @@ def test_handle_playlist_cmd_add():
            "search": False,
            "secret_key": True,
            "site_name": "MousikóFídi - Your Music Cloud",
            "user_playlist": ["{}/real.flac".format(example_dir)],
            "queue": ["{}/real.flac".format(example_dir)],
            "theme": "/css/water/light.standalone",
            "username": None,
        }


@@ 855,7 855,7 @@ def test_handle_playlist_cmd_add_multi():
            "search": False,
            "secret_key": True,
            "site_name": "MousikóFídi - Your Music Cloud",
            "user_playlist": [
            "queue": [
                "{}/real.flac".format(example_dir),
                "{}/real.mp3".format(example_dir),
                "{}/real.ogg".format(example_dir),


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


@@ 976,7 976,7 @@ def test_handle_playlist_cmd_rm():
            "search": False,
            "secret_key": True,
            "site_name": "MousikóFídi - Your Music Cloud",
            "user_playlist": [
            "queue": [
                "{}/real.flac".format(example_dir),
                "{}/real.mp3".format(example_dir),
                "{}/real.ogg".format(example_dir),


@@ 1469,7 1469,9 @@ def test_index_http_code(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1477,124 1479,7 @@ def test_index_http_code(client):
    assert bytes('<link rel="icon" href="{}">'.format(favicon), "utf8") in rv.data


def test_index_with_playlists(client):
    edir = os.path.join(THIS_DIR, "example")
    mdir = tempfile.mkdtemp()
    pdir = tempfile.mkdtemp()

    shutil.copy(os.path.join(edir, "real.flac"), mdir)
    shutil.copy(os.path.join(edir, "real.mp3"), mdir)
    shutil.copy(os.path.join(edir, "real.ogg"), mdir)

    plist1 = os.path.join(pdir, "PLAYLIST FILE 1.m3u")
    plist2 = os.path.join(pdir, "PLAYLIST FILE 2.m3u")
    plist3 = os.path.join(pdir, "PLAYLIST FILE 3.m3u")
    plist4 = os.path.join(pdir, "PLAYLIST FILE 4.m3u")

    real_flac = os.path.join(mdir, "real.flac")
    real_mp3 = os.path.join(mdir, "real.mp3")
    real_ogg = os.path.join(mdir, "real.ogg")

    with open(plist1, "w") as f1:
        f1.write(real_flac)
        f1.write("\n")
        f1.write(real_mp3)
        f1.write("\n")
        f1.write(real_ogg)
        f1.write("\n")

    with open(plist2, "w") as f2:
        f2.write(real_flac)
        f2.write("\n")
        f2.write(real_flac)
        f2.write("\n")
        f2.write(real_mp3)
        f2.write("\n")
        f2.write(real_ogg)
        f2.write("\n")

    with open(plist3, "w") as f3:
        f3.write(real_flac)
        f3.write("\n")
        f3.write(real_flac)
        f3.write("\n")
        f3.write(real_mp3)
        f3.write("\n")
        f3.write(real_mp3)
        f3.write("\n")
        f3.write(real_ogg)
        f3.write("\n")

    with open(plist4, "w") as f4:
        f4.write(real_flac)
        f4.write("\n")
        f4.write(real_flac)
        f4.write("\n")
        f4.write(real_mp3)
        f4.write("\n")
        f4.write(real_mp3)
        f4.write("\n")
        f4.write(real_ogg)
        f4.write("\n")
        f4.write(real_ogg)
        f4.write("\n")

    app.fidiConfig["config"]["music_dirs"] = [mdir]
    app.fidiConfig["config"]["playlist"]["dir"] = pdir
    app.fidiConfig["config"]["site_name"] = "Pdir Test"

    with app.test_client() as tc:
        rv = tc.get("/")

    shutil.rmtree(mdir)
    shutil.rmtree(pdir)

    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()

    assert rv.status == "200 OK"
    assert (
        bytes(
            '<title id="title" data-sitename="Pdir Test">Welcome | Pdir Test</title>',
            "utf8",
        )
        in rv.data
    )
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%201">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 1", "utf8") in rv.data
    assert bytes("          <td>\n            3\n          </td>\n", "utf8") in rv.data
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%202">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 2", "utf8") in rv.data
    assert bytes("          <td>\n            4\n          </td>\n", "utf8") in rv.data
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%203">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 3", "utf8") in rv.data
    assert bytes("          <td>\n            5\n          </td>\n", "utf8") in rv.data
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%204">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 4", "utf8") in rv.data
    assert bytes("          <td>\n            6\n          </td>\n", "utf8") in rv.data


def test_index_no_playlists(client):
def test_index(client):
    mdir = tempfile.mkdtemp()
    pdir = tempfile.mkdtemp()



@@ 1686,7 1571,9 @@ def test_index_dark_theme(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1719,7 1606,9 @@ def test_index_light_theme(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1750,7 1639,9 @@ def test_index_nes_theme(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1782,7 1673,9 @@ def test_index_terminal_theme(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1813,7 1706,9 @@ def test_index_terminal_green_theme(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1844,7 1739,9 @@ def test_index_terminal_solarized_theme(client):
    )
    assert (
        bytes(
            '<img src="{}" title="The MousikóFídi logo, by Ogenfald.">'.format(logo),
            '<a href="https://mousikofidi.info/"><img src="{}" title="The MousikóFídi logo, by Ogenfald, and a link to the MousikóFídi home page (with online documentation)."></a>'.format(
                logo
            ),
            "utf8",
        )
        in rv.data


@@ 1907,7 1804,7 @@ def test_browse_http_code(client):
    assert bytes('<link rel="icon" href="{}">'.format(favicon), "utf8") in rv.data


def test_browse_no_playlists(client):
def test_browse(client):
    mdir = tempfile.mkdtemp()
    pdir = tempfile.mkdtemp()



@@ 1974,123 1871,6 @@ def test_browse_no_playlists(client):
    )


def test_browse_with_playlists(client):
    edir = os.path.join(THIS_DIR, "example")
    mdir = tempfile.mkdtemp()
    pdir = tempfile.mkdtemp()

    shutil.copy(os.path.join(edir, "real.flac"), mdir)
    shutil.copy(os.path.join(edir, "real.mp3"), mdir)
    shutil.copy(os.path.join(edir, "real.ogg"), mdir)

    plist1 = os.path.join(pdir, "PLAYLIST FILE 1.m3u")
    plist2 = os.path.join(pdir, "PLAYLIST FILE 2.m3u")
    plist3 = os.path.join(pdir, "PLAYLIST FILE 3.m3u")
    plist4 = os.path.join(pdir, "PLAYLIST FILE 4.m3u")

    real_flac = os.path.join(mdir, "real.flac")
    real_mp3 = os.path.join(mdir, "real.mp3")
    real_ogg = os.path.join(mdir, "real.ogg")

    with open(plist1, "w") as f1:
        f1.write(real_flac)
        f1.write("\n")
        f1.write(real_mp3)
        f1.write("\n")
        f1.write(real_ogg)
        f1.write("\n")

    with open(plist2, "w") as f2:
        f2.write(real_flac)
        f2.write("\n")
        f2.write(real_flac)
        f2.write("\n")
        f2.write(real_mp3)
        f2.write("\n")
        f2.write(real_ogg)
        f2.write("\n")

    with open(plist3, "w") as f3:
        f3.write(real_flac)
        f3.write("\n")
        f3.write(real_flac)
        f3.write("\n")
        f3.write(real_mp3)
        f3.write("\n")
        f3.write(real_mp3)
        f3.write("\n")
        f3.write(real_ogg)
        f3.write("\n")

    with open(plist4, "w") as f4:
        f4.write(real_flac)
        f4.write("\n")
        f4.write(real_flac)
        f4.write("\n")
        f4.write(real_mp3)
        f4.write("\n")
        f4.write(real_mp3)
        f4.write("\n")
        f4.write(real_ogg)
        f4.write("\n")
        f4.write(real_ogg)
        f4.write("\n")

    app.fidiConfig["config"]["music_dirs"] = [mdir]
    app.fidiConfig["config"]["playlist"]["dir"] = pdir
    app.fidiConfig["config"]["site_name"] = "Pdir Test"

    with app.test_client() as tc:
        rv = tc.get("/browse")

    shutil.rmtree(mdir)
    shutil.rmtree(pdir)

    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()

    assert rv.status == "200 OK"
    assert (
        bytes(
            '<title id="title" data-sitename="Pdir Test">Media Dirs | Pdir Test</title>',
            "utf8",
        )
        in rv.data
    )
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%201">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 1", "utf8") in rv.data
    assert bytes("          <td>\n            3\n          </td>\n", "utf8") in rv.data
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%202">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 2", "utf8") in rv.data
    assert bytes("          <td>\n            4\n          </td>\n", "utf8") in rv.data
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%203">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 3", "utf8") in rv.data
    assert bytes("          <td>\n            5\n          </td>\n", "utf8") in rv.data
    assert (
        bytes(
            '<a class="bold mobile-big" href="/playlist/PLAYLIST%20FILE%204">', "utf8"
        )
        in rv.data
    )
    assert bytes("PLAYLIST FILE 4", "utf8") in rv.data
    assert bytes("          <td>\n            6\n          </td>\n", "utf8") in rv.data


def test_dir_detail_found(client):
    example_dir = os.path.join(THIS_DIR, "example")
    dir_list = []


@@ 2664,7 2444,7 @@ def test_file_detail_real_ogg(client):


def test_playlist_empty(client):
    url = "/playlist"
    url = "/queue"
    with app.app_context():
        rv = client.get(url)
    assert rv.status == "200 OK"


@@ 2675,7 2455,7 @@ def test_playlist_empty(client):
        )
        in rv.data
    )
    assert bytes('<li><a href="/playlist">Playlist</a></li>', "utf8") not in rv.data
    assert bytes('<li><a href="/queue">Playlist</a></li>', "utf8") not in rv.data
    assert (
        bytes(
            '<form action="/playlist/clear/all" class="center" id="playlistctl" method="post">',


@@ 2685,7 2465,7 @@ def test_playlist_empty(client):
    )
    assert (
        bytes(
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="New Playlist" type="submit" />',
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="Clear Queue" type="submit" />',
            "utf8",
        )
        not in rv.data


@@ 2695,7 2475,7 @@ def test_playlist_empty(client):

def test_playlist_with_audio_and_video(client):
    example_dir = os.path.join(THIS_DIR, "example")
    url = "/playlist"
    url = "/queue"

    with app.test_client() as tc:



@@ 2707,7 2487,7 @@ def test_playlist_with_audio_and_video(client):
        f6 = os.path.join(example_dir, "fake.webm")

        with tc.session_transaction() as sess:
            sess["user_playlist"] = [f, f2, f3, f4, f5, f6]
            sess["queue"] = [f, f2, f3, f4, f5, f6]

        rv = tc.get(url)



@@ 2720,7 2500,7 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="New Playlist" type="submit" />',
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="Clear Queue" type="submit" />',
            "utf8",
        )
        in rv.data


@@ 2734,14 2514,14 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<input class="center" id="save" name="save" title="Save this playlist as a .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Playlist" type="submit" />',
            '<input class="center" id="save" name="save" title="Save this queue as an .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Queue" type="submit" />',
            "utf8",
        )
        in rv.data
    )
    assert (
        bytes(
            '<input id="file-name" name="file-name" type="text" placeholder="Playlist name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>',
            '<input id="file-name" name="file-name" type="text" placeholder="Name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>',
            "utf8",
        )
        in rv.data


@@ 3020,7 2800,7 @@ def test_playlist_with_audio_and_video(client):

def test_playlist_with_audio_no_video(client):
    example_dir = os.path.join(THIS_DIR, "example")
    url = "/playlist"
    url = "/queue"

    with app.test_client() as tc:



@@ 3029,7 2809,7 @@ def test_playlist_with_audio_no_video(client):
        f3 = os.path.join(example_dir, "real.ogg")

        with tc.session_transaction() as sess:
            sess["user_playlist"] = [f, f2, f3]
            sess["queue"] = [f, f2, f3]

        rv = tc.get(url)



@@ 3042,7 2822,7 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="New Playlist" type="submit" />',
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="Clear Queue" type="submit" />',
            "utf8",
        )
        in rv.data


@@ 3056,14 2836,14 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<input class="center" id="save" name="save" title="Save this playlist as a .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Playlist" type="submit" />',
            '<input class="center" id="save" name="save" title="Save this queue as an .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Queue" type="submit" />',
            "utf8",
        )
        in rv.data
    )
    assert (
        bytes(
            '<input id="file-name" name="file-name" type="text" placeholder="Playlist name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>',
            '<input id="file-name" name="file-name" type="text" placeholder="Name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>',
            "utf8",
        )
        in rv.data


@@ 3342,7 3122,7 @@ def test_playlist_with_audio_no_video(client):

def test_playlist_no_audio_with_video(client):
    example_dir = os.path.join(THIS_DIR, "example")
    url = "/playlist"
    url = "/queue"

    with app.test_client() as tc:



@@ 3351,7 3131,7 @@ def test_playlist_no_audio_with_video(client):
        f3 = os.path.join(example_dir, "fake.webm")

        with tc.session_transaction() as sess:
            sess["user_playlist"] = [f, f2, f3]
            sess["queue"] = [f, f2, f3]

        rv = tc.get(url)



@@ 3364,7 3144,7 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="New Playlist" type="submit" />',
            '<input class="center" id="clear" name="clear" title="Remove all tracks from your playlist." value="Clear Queue" type="submit" />',
            "utf8",
        )
        in rv.data


@@ 3378,14 3158,14 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<input class="center" id="save" name="save" title="Save this playlist as a .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Playlist" type="submit" />',
            '<input class="center" id="save" name="save" title="Save this queue as an .m3u file.  Note that doing so will reload the page, stopping any playback." value="Save Queue" type="submit" />',
            "utf8",
        )
        in rv.data
    )
    assert (
        bytes(
            '<input id="file-name" name="file-name" type="text" placeholder="Playlist name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>',
            '<input id="file-name" name="file-name" type="text" placeholder="Name" title="File name for the playlist to be saved.  Only alphanumeric charatcers, spaces, underscores, and plus signs are allowed." required/>',
            "utf8",
        )
        in rv.data