~hristoast/mousikofidi

37df8ece829980318c24ae659ad432d5c5541b83 — Hristos N. Triantafillou 1 year, 1 month ago 7b1d2b8
Let flask handle encoding and decoding of paths in URLs

This change allows for deleting a bit of code, and changing any place
that had anything to do with encoded/unencoded paths.

Now, url_for() is used throughout templates and all url encoding is
done by Flask.  This allows for totally "clean"-looking URLs in more
recent releases of Firefox.
M mousikofidi/mousikofidi.py => mousikofidi/mousikofidi.py +73 -80
@@ 72,17 72,6 @@ dbg = app.logger.debug
err = app.logger.error
wrn = app.logger.warning

if debug:
    # The local dev server wants the double backslash
    dir_detail_route = "/browse//<path:path>"
    playlistctl_route = "/playlist/<cmd>//<path:path>"
    serve_file_route = "/serve//<path:path>"
else:
    # uwsgi does not!
    dir_detail_route = "/browse/<path:path>"
    playlistctl_route = "/playlist/<cmd>/<path:path>"
    serve_file_route = "/serve/<path:path>"


def quote(string: str) -> str:
    return urllib.parse.quote(string, safe="")


@@ 163,39 152,43 @@ def file_metadata(file_path: str) -> dict:
    return data_dict


def browse_dir(context: dict, unescaped_path: str) -> dict:
    dbg("Reading Dir: " + unescaped_path)
def browse_dir(context: dict, path: str) -> dict:
    dbg("Reading Dir: " + path)

    try:
        dir_items = sorted(os.listdir(unescaped_path))
        dir_items = sorted(os.listdir(path))
    except PermissionError:
        dbg("Got a PermissionError on '{}'!".format(unescaped_path))
        dbg("Got a PermissionError on '{}'!".format(path))
        dir_items = list()
        flash(
            """<p class="bold center red">The directory '{}' could not be read due to a permissions error!</p>""".format(
                unescaped_path
                path
            )
        )

    dir_list = []
    item_list = []
    file_list = []
    _item_list = []
    _audio_list = []
    _video_list = []

    for i in dir_items:
        item_path = os.path.join(unescaped_path, i)
        item_path = os.path.join(path, i)
        metadata = get_metadata_dict(item_path)

        if is_audio_file(item_path):
            dbg("Audio found: " + i)
            file_list.append(item_path)
            _item_list.append(file_dict(item_path, metadata, "audio"))
            _audio_list.append(file_dict(item_path, metadata, "audio"))
            item_list.append(file_dict(item_path, metadata, "audio"))
            context["add_all_button"] = True
            context["audio_player"] = True

        elif is_video_file(item_path):
            dbg("Video found: " + i)
            file_list.append(item_path)
            _item_list.append(file_dict(item_path, metadata, "video"))
            _video_list.append(file_dict(item_path, metadata, "video"))
            item_list.append(file_dict(item_path, metadata, "video"))
            context["add_all_button"] = True
            context["video_player"] = True



@@ 203,39 196,42 @@ def browse_dir(context: dict, unescaped_path: str) -> dict:
            dbg("Dir found: " + i)
            dir_list.append(dir_dict(item_path))

    item_list = make_unique_slugs(_item_list)
    audio_list = make_unique_slugs(_audio_list)
    video_list = make_unique_slugs(_video_list)

    music_dirs = []
    for d in context["music_dirs"]:
        music_dirs.append(d["raw"])
        music_dirs.append(d["full_path"])

    context["cover_art"] = select_cover_art(unescaped_path)
    context["cover_art"] = select_cover_art(path)
    context["file_list"] = file_list
    context["page_name"] = unescaped_path
    context["page_path"] = breadcrumb_links_from_path(unescaped_path, music_dirs)
    context["item_list"] = item_list
    context["audio_list"] = audio_list
    context["video_list"] = video_list
    context["page_name"] = path
    context["page_path"] = breadcrumb_links_from_path(path, music_dirs)
    context["playlist_add"] = True
    context["playlist_rm"] = False
    context["item_type"] = "dir"
    context["dir_list"] = dir_list
    context["item_list"] = item_list
    return context


def browse_file(context: dict, unescaped_path: str) -> dict:
    dbg("Reading File: " + unescaped_path)
def browse_file(context: dict, path: str) -> dict:
    dbg("Reading File: " + path)

    file_name = unescaped_path.split("/")[-1]
    metadata = get_metadata_dict(unescaped_path)
    file_name = path.split("/")[-1]
    metadata = get_metadata_dict(path)

    if is_audio_file(unescaped_path):
    if is_audio_file(path):
        context["item_type"] = "audio"

    elif is_video_file(unescaped_path):
    elif is_video_file(path):
        context["item_type"] = "video"

    music_dirs = []
    for d in context["music_dirs"]:
        music_dirs.append(d["raw"])
        music_dirs.append(d["full_path"])

    for tag in (
        "album",


@@ 256,12 252,12 @@ def browse_file(context: dict, unescaped_path: str) -> dict:
            # A given track may have any or none of the above tags.
            pass

    context["cover_art"] = select_cover_art(unescaped_path)
    context["cover_art"] = select_cover_art(path)
    context["page_name"] = metadata["title"] or file_name
    context["escaped_path"] = quote(unescaped_path)
    context["unescaped_path"] = unescaped_path
    context["full_path"] = path
    context["path"] = path.strip("/")
    context["file_name"] = file_name
    context["page_path"] = breadcrumb_links_from_path(unescaped_path, music_dirs)
    context["page_path"] = breadcrumb_links_from_path(path, music_dirs)

    return context



@@ 635,11 631,7 @@ def get_theme_from_config(config: dict) -> str:


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


def file_dict(path: str, metadata: dict, ftype: str, title_limit=17) -> dict:


@@ 667,8 659,7 @@ def file_dict(path: str, metadata: dict, ftype: str, title_limit=17) -> dict:
        "tracktotal": metadata["tracktotal"],
        "file_name": file_name,
        "file_name_mobile": file_name_mobile,
        "escaped_path": quote(path),
        "unescaped_path": path,
        "file_path": path.strip("/"),
        "slug": title_slug(title or file_name),
    }



@@ 679,8 670,9 @@ def breadcrumb_links_from_path(path: str, music_dirs: list) -> str:

    for d in music_dirs:
        if path.startswith(d):
            link_string += '<a href="/browse/{escaped}">{unescaped}</a>'.format(
                escaped=quote(d), unescaped=d
            _path = d.strip("/")
            link_string += '<a href="{url}">{path}</a>'.format(
                url=url_for(".dir_detail", path=_path), path=d
            )
            path_string += d
            new_path = path.replace(d, "").strip("/")


@@ 690,8 682,9 @@ def breadcrumb_links_from_path(path: str, music_dirs: list) -> str:
                if dd:
                    path_string = os.path.join(path_string, dd)
                    if os.path.isdir(path_string):
                        link_string += ' / <a href="/browse/{escaped}">{dir_name}</a>'.format(
                            escaped=quote(path_string), dir_name=dd
                        link_string += ' / <a href="{url}">{name}</a>'.format(
                            name=dd,
                            url=url_for(".dir_detail", path=path_string.strip("/")),
                        )
                    elif os.path.isfile(path_string):
                        link_string += " / {}".format(dd)


@@ 763,9 756,9 @@ 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:
                flash(
                flash(  # TODO: Use url_for here
                    """<p class="bold center green">Your playlist was cleared!</p>
<form id="playlistctl" action="/playlist/clear/%2Fundo" class="center" method="post">
<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(


@@ 944,8 937,8 @@ def is_valid_path(request_context: dict, path: str) -> bool:
    _path = path.strip("/").rstrip("/")
    abs_path = os.path.abspath(os.path.sep + _path)
    for d in request_context["music_dirs"]:
        raw_path = d["raw"]
        if abs_path.startswith(raw_path):
        full_path = d["full_path"]
        if abs_path.startswith(full_path):
            return True
    return False



@@ 985,8 978,8 @@ def make_unique_slugs(item_list: list) -> list:
def paths_list(music_dirs: list) -> list:
    dl = []
    for md in music_dirs:
        enc = quote(md)
        dl.append({"raw": md.rstrip("/"), "encoded": enc})
        path = md.strip("/")
        dl.append({"full_path": os.path.join(os.path.sep, path), "path": path})
    return dl




@@ 1065,10 1058,10 @@ def select_cover_art(path: str) -> str:
            break

    if cover_art:
        return "/serve/" + quote(cover_art)
        return url_for(".serve_file", path=cover_art.strip("/"))

    elif images:
        return "/serve/" + quote(os.path.join(_dir, images[0]))
        return url_for(".serve_file", path=os.path.join(_dir, images[0]).strip("/"))

    wrn("No cover art found for dir: " + path)



@@ 1196,19 1189,19 @@ def browse():
    return render_template("dirs.html", **c)


@app.route(dir_detail_route)
@app.route("/browse/<path:path>")
def dir_detail(path):
    _c = request_context(app.fidiConfig)
    unescaped = os.path.sep + urllib.parse.unquote(path)
    full_path = os.path.join(os.path.sep, path)

    if not is_valid_path(_c, unescaped):
    if not is_valid_path(_c, full_path):
        return abort(404)

    if os.path.isfile(unescaped):
        c = browse_file(_c, unescaped)
    if os.path.isfile(full_path):
        c = browse_file(_c, full_path)

    elif os.path.isdir(unescaped):
        c = browse_dir(_c, unescaped)
    elif os.path.isdir(full_path):
        c = browse_dir(_c, full_path)
        c["top_link"] = True

    else:


@@ 1221,10 1214,10 @@ def dir_detail(path):
@app.route("/playlist")
def playlist():
    c = request_context(app.fidiConfig)
    _item_list = []
    _audio_list = []
    _video_list = []
    file_list = []
    item_list = None
    audio_list = None
    video_list = None
    playlist_items = c["user_playlist"]
    playlist_names = []


@@ 1239,19 1232,19 @@ def playlist():
            metadata = get_metadata_dict(i)
            if is_audio_file(i):
                file_list.append(i)
                _item_list.append(file_dict(i, metadata, "audio"))
                _audio_list.append(file_dict(i, metadata, "audio"))
            elif is_video_file(i):
                _video_list.append(file_dict(i, metadata, "video"))
                file_list.append(i)

    if _item_list:
        item_list = make_unique_slugs(_item_list)
    if _audio_list:
        audio_list = make_unique_slugs(_audio_list)

    if _video_list:
        video_list = make_unique_slugs(_video_list)

    c["file_list"] = file_list
    c["item_list"] = item_list
    c["audio_list"] = audio_list
    c["video_list"] = video_list
    c["playlist_add"] = False
    c["playlist_names"] = playlist_names


@@ 1277,9 1270,9 @@ def playlists():
def playlist_detail(name):
    c = request_context(app.fidiConfig)
    file_list = []
    item_list = []
    audio_list = []
    video_list = []
    _item_list = []
    _audio_list = []
    _video_list = []
    bunk_tracks = []



@@ 1295,7 1288,7 @@ def playlist_detail(name):
            if is_audio_file(i):
                metadata = get_metadata_dict(i)
                file_list.append(i)
                _item_list.append(file_dict(i, metadata, "audio"))
                _audio_list.append(file_dict(i, metadata, "audio"))
            elif is_video_file(i):
                metadata = get_metadata_dict(i)
                _video_list.append(file_dict(i, metadata, "video"))


@@ 1312,15 1305,15 @@ def playlist_detail(name):
        for t in bunk_tracks:
            err(t)

    if _item_list:
        item_list = make_unique_slugs(_item_list)
    if _audio_list:
        audio_list = make_unique_slugs(_audio_list)

    if _video_list:
        video_list = make_unique_slugs(_video_list)

    c["link_button"] = True
    c["file_list"] = file_list
    c["item_list"] = item_list
    c["audio_list"] = audio_list
    c["video_list"] = video_list
    c["page_name"] = "Playlist: " + name
    c["playlist_add"] = True


@@ 1331,7 1324,7 @@ def playlist_detail(name):
    return render_template("playlist.html", **c)


@app.route(playlistctl_route, methods=("POST",))
@app.route("/playlist/<cmd>/<path:path>", methods=("POST",))
def playlistctl(cmd, path):
    c = request_context(app.fidiConfig)
    p = os.path.join(os.path.sep, path)


@@ 1394,7 1387,7 @@ def search():
        video_list = make_unique_slugs(_video_list)

    c["hits"] = hits
    c["item_list"] = audio_list
    c["audio_list"] = audio_list
    c["video_list"] = video_list
    c["only_audio"] = only_audio
    c["only_video"] = only_video


@@ 1407,7 1400,7 @@ def search():
    return render_template("search.html", **c)


@app.route(serve_file_route)
@app.route("/serve/<path:path>")
def serve_file(path):
    _c = request_context(app.fidiConfig)
    if not is_valid_path(_c, path):


@@ 1506,7 1499,7 @@ def test_js():

    if _audio_list:
        audio_list = make_unique_slugs(_audio_list)
        single_file_e = audio_list[0]["escaped_path"]
        single_file_e = audio_list[0]["file_path"]

    if _video_list:
        video_list = make_unique_slugs(_video_list)


@@ 1515,7 1508,7 @@ def test_js():
    c.update(
        {
            "page_name": "Javascript Tests Page!",
            "item_list": audio_list,
            "audio_list": audio_list,
            "video_list": video_list,
            "single_file_e": single_file_e,
        }

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

  <td class="center"> {# PLAY #}
    {% if page_name != "Playlist" %}
      <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>
  </td>

  <td class="center mobile-hide"> {# TRACK #}
    {% if audio.track and not audio.tracktotal %}{{ audio.track }}{% endif %}
    {% if audio.track and audio.tracktotal and audio.tracktotal != "0" %}{{ audio.track }}/{{ audio.tracktotal }}{% endif %}
    {% if audio.track and audio.tracktotal and audio.tracktotal == "0" %}{{ audio.track }}{% endif %}
  </td>

  {% if playlist_add %}
    <td> {# ADD #}
      <form action="{{ url_for('.playlistctl', cmd='add', path=audio.file_path) }}" class="center" method="post">
        <input class="plus" type="submit" value="&plus;" title="Add this track to the playlist.">
        <input id="{{ audio.slug }}" name="slug" type="hidden" value="{{ audio.slug }}">
      </form>
    </td>
  {% endif %}

  <td class="title title-regular"> {# TITLE #}
    <a class="mobile-big" href="{{ url_for('.dir_detail', path=audio.file_path) }}" title="This track's title.">
      {% if audio.title %}
        {{ audio.title }}
      {% else %}
        {{ audio.file_name }}
      {% endif %}
    </a>
  </td>

  <td class="title-mobile"> {# MOBILE TITLE #}
    <a class="mobile-big" href="{{ url_for('.dir_detail', path=audio.file_path) }}" title="This track's title.">
      {% if audio.title %}
        {{ audio.title_mobile }}
      {% else %}
        {{ audio.file_name_mobile }}
      {% endif %}
    </a>
  </td>

  <td class="mobile-hide"> {# ALBUM #}
    {% if audio.album %}{{ audio.album }}{% endif %}
  </td>

  <td class="mobile-hide">
    {% if audio.artist %}{{ audio.artist }}{% endif %}
  </td>

  <td class="center mobile-hide">
    {% if audio.length %}{{ audio.length }}{% endif %}
  </td>

  {% if secret_key and playlist_rm %}
    <td>
      <form action="{{ url_for('.playlistctl', cmd='rm', path=audio.file_path) }}" class="center" method="post">
        <input class="bold red X" type="submit" value="X">
      </form>
    </td>
  {% endif %}
</tr>

M mousikofidi/templates/dir_table.html => mousikofidi/templates/dir_table.html +2 -2
@@ 10,8 10,8 @@
      {% for dir in dir_list %}
        <tr>
          <td>
            <a class="bold mobile-big" href="{{ dir.escaped_path }}">
              {% if icons %}<i class="fas fa-folder"></i> {% endif %}{{ dir.dir_name }}
            <a class="bold mobile-big" href="{{ url_for('.dir_detail', path=dir.path) }}">
              {% if icons %}<i class="fas fa-folder"></i> {% endif %}{{ dir.name }}
            </a>
          </td>
        </tr>

M mousikofidi/templates/dirs.html => mousikofidi/templates/dirs.html +2 -2
@@ 14,8 14,8 @@
        <tr>
          <td></td>
          <td>
            <a class="bold mobile-big" href="/browse/{{ dir.encoded }}">
              {% if icons %}<i class="fas fa-folder"></i> {% endif %}{{ dir.raw }}
            <a class="bold mobile-big" href="{{ url_for('.dir_detail', path=dir.path) }}">
              {% if icons %}<i class="fas fa-folder"></i> {% endif %}{{ dir.full_path }}
            </a>
          </td>
          <td></td>

M mousikofidi/templates/file_detail.html => mousikofidi/templates/file_detail.html +14 -18
@@ 1,9 1,9 @@
<form class="center" action="/playlist/add/{{ escaped_path }}" method="post">
<form class="center" action="{{ url_for('.playlistctl', cmd='add', path=path) }}" method="post">
  <input class="plus-btn" type="submit" value="&plus;" title="Add this track to the playlist.">
  <input id="track-detail-no-slug" name="slug" type="hidden" value="track-detail-no-slug">
</form>

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



@@ 24,23 24,19 @@
</p>

{% if item_type == "audio" %}
<div class="center">
  <audio id="single"{% if autoplay %} autoplay{% endif %} controls src="/serve/{{ escaped_path }}">
    Your browser does not support the <code>audio</code> element.
  </audio>
</div>
  <div class="center">
    <audio id="single"{% if autoplay %} autoplay{% endif %} controls src="{{ url_for('.serve_file', path=path) }}">
      Your browser does not support the <code>audio</code> element.
    </audio>
  </div>
{% elif item_type == "video" %}
<div class="center" style="margin: 0 auto; width: 100%;">
  <video id="single"{% if autoplay %} autoplay{% endif %} controls>
    {% if "mp4" in escaped_path %}
      <source src="/serve/{{ escaped_path }}" type="video/mp4">
    {% elif "webm" in escaped_path %}
      <source src="/serve/{{ escaped_path }}" type="video/webm">
    {% endif %}
    <p>Your browser doesn't support HTML5 video. Here is
      a <a href="/serve/{{ escaped_path }}">link to the video</a> instead.</p>
  </video>
</div>
  <div class="center" style="margin: 0 auto; width: 100%;">
    <video id="single"{% if autoplay %} autoplay{% endif %} controls>
      <source src="{{ url_for('.serve_file', path=path) }}" type="{% if "mp4" in path %}video/mp4{% elif "webm" in path %}video/webm{% endif %}">
      <p>Your browser doesn't support HTML5 video. Here is
        a <a href="{{ url_for('.serve_file', path=path) }}">link to the video</a> instead.</p>
    </video>
  </div>
{% endif %}

<div class="center">

M mousikofidi/templates/item_table.html => mousikofidi/templates/item_table.html +8 -206
@@ 1,14 1,14 @@
{% if item_list %}

  {% if add_all_button %}
    <form class="center" action="/playlist/bulk/%2Fbulk" method="post">
    <form class="center" action="/playlist/bulk/bulk" method="post">
      <input id="bulk-add" type="submit" value="Add All Files To Playlist" title="Add all files in this directory to the playlist.">
      <input id="item-list" name="bulk-list" type="hidden" value="{{ file_list }}">
    </form>
  {% endif %}
{% endif %}

  <h4 class="center">{% if video_list %}Audio Tracks{% else %}Files:{% endif %}</h4>

{% if audio_list %}
  <h4 class="center">Audio Tracks</h4>
  <table style="margin: 0 auto;">

    <thead>


@@ 29,140 29,8 @@
    </thead>

    <tbody id="playlist">
      {% for item in item_list %}
        {% if item.type == "audio" %}
          <tr style="position: relative;">

            <td class="center"> {# PLAY #}
              {% if page_name != "Playlist" %}
                <div class="anchor" id="{{ item.slug }}-target"></div>
              {% endif %}
              <div class="play-arrow" data-browse="/browse/{{ item.escaped_path }}" data-num="0" data-path="{{ item.unescaped_path }}" data-title="{% if item.title %}{{ item.title }}{% else %}{{ item.file_name }}{% endif %}" id="{{ item.slug }}" title="Click to play this track."></div>
            </td>

            <td class="center mobile-hide"> {# TRACK #}
              {% if item.track and not item.tracktotal %}{{ item.track }}{% endif %}
              {% if item.track and item.tracktotal and item.tracktotal != "0" %}{{ item.track }}/{{ item.tracktotal }}{% endif %}
              {% if item.track and item.tracktotal and item.tracktotal == "0" %}{{ item.track }}{% endif %}
            </td>

            {% if playlist_add %}
              <td> {# ADD #}
                <form action="/playlist/add/{{ item.escaped_path }}" class="center" method="post">
                  <input class="plus" type="submit" value="&plus;" title="Add this track to the playlist.">
                  <input id="{{ item.slug }}" name="slug" type="hidden" value="{{ item.slug }}">
                </form>
              </td>
            {% endif %}

            <td class="title title-regular"> {# TITLE #}
              <a class="mobile-big" href="/browse/{{ item.escaped_path }}" title="This track's title.">
                {% if item.title %}
                  {{ item.title }}
                {% else %}
                  {{ item.file_name }}
                {% endif %}
              </a>
            </td>

            <td class="title-mobile"> {# MOBILE TITLE #}
              <a class="mobile-big" href="/browse/{{ item.escaped_path }}" title="This track's title.">
                {% if item.title %}
                  {{ item.title_mobile }}
                {% else %}
                  {{ item.file_name_mobile }}
                {% endif %}
              </a>
            </td>

            <td class="mobile-hide"> {# ALBUM #}
              {% if item.album %}{{ item.album }}{% endif %}
            </td>

            <td class="mobile-hide">
              {% if item.artist %}{{ item.artist }}{% endif %}
            </td>

            <td class="center mobile-hide">
              {% if item.length %}{{ item.length }}{% endif %}
            </td>

            {% if secret_key and playlist_rm %}
              <td>
                <form action="/playlist/rm/{{ item.escaped_path }}" class="center" method="post">
                  <input class="bold red X" type="submit" value="X">
                </form>
              </td>
            {% endif %}
          </tr>

        {% elif item.type == "video" %}
          <tr>

            <td class="center"> {# PLAY #}
              {% if page_name != "Playlist" %}
                <div class="anchor" id="{{ item.slug }}-target"></div>
              {% endif %}
              <div class="video-arrow" data-browse="/browse/{{ item.escaped_path }}" data-num="0" data-path="{{ item.unescaped_path }}" data-title="{% if item.title %}{{ item.title }}{% else %}{{ item.file_name }}{% endif %}" id="{{ item.slug }}" title="Click to play this track."></div>
            </td>

            <td class="center mobile-hide"> {# TRACK #}
              {% if item.track and not item.tracktotal %}{{ item.track }}{% endif %}
              {% if item.track and item.tracktotal and item.tracktotal != "0" %}{{ item.track }}/{{ item.tracktotal }}{% endif %}
              {% if item.track and item.tracktotal and item.tracktotal == "0" %}{{ item.track }}{% endif %}
            </td>

            {% if playlist_add %}
              <td> {# ADD #}
                <form action="/playlist/add/{{ item.escaped_path }}" class="center" method="post">
                  <input class="plus" type="submit" value="&plus;">
                  <input id="{{ item.slug }}" name="slug" type="hidden" value="{{ item.slug }}">
                </form>
              </td>
            {% endif %}

            <td class="title-regular"> {# TITLE #}
              <a class="mobile-big video-title" href="/browse/{{ item.escaped_path }}" title="This track's title.">
                {% if item.title %}
                  {{ item.title }}
                {% else %}
                  {{ item.file_name }}
                {% endif %}
              </a>
            </td>

            <td class="title-mobile"> {# TITLE #}
              <a class="mobile-big video-title" href="/browse/{{ item.escaped_path }}" title="This track's title.">
                {% if item.title %}
                  {{ item.title_mobile }}
                {% else %}
                  {{ item.file_name_mobile }}
                {% endif %}
              </a>
            </td>

            <td class="mobile-hide"> {# ALBUM #}
              {% if item.album %}{{ item.album }}{% endif %}
            </td>

            <td class="mobile-hide"> {# ARTIST #}
              {% if item.artist %}{{ item.artist }}{% endif %}
            </td>

            <td class="center mobile-hide">
              {% if item.length %}{{ item.length }}{% endif %}
            </td>

            {% if secret_key and playlist_rm %}
              <td>
                <form action="/playlist/rm/{{ item.escaped_path }}" class="center" method="post">
                  <input class="bold red X" type="submit" value="X">
                </form>
              </td>
            {% endif %}

          </tr>
        {% endif %}
      {% for audio in audio_list %}
        {% include "audio_table_rows.html" with context %}
      {% endfor %}
    </tbody>
  </table>


@@ 190,74 58,8 @@
    </thead>

    <tbody id="video-playlist">
      {% for item in video_list %}
        {% if item.type == "video" %}
          <tr>

            <td class="center"> {# PLAY #}
              {% if page_name != "Playlist" %}
                <div class="anchor" id="{{ item.slug }}-target"></div>
              {% endif %}
              <div class="video-arrow" data-browse="/browse/{{ item.escaped_path }}" data-num="0" data-path="{{ item.unescaped_path }}" data-title="{% if item.title %}{{ item.title }}{% else %}{{ item.file_name }}{% endif %}" id="{{ item.slug }}" title="Click to play this track."></div>
            </td>

            <td class="center mobile-hide">
              {% if item.track and not item.tracktotal %}{{ item.track }}{% endif %}
              {% if item.track and item.tracktotal and item.tracktotal != "0" %}{{ item.track }}/{{ item.tracktotal }}{% endif %}
              {% if item.track and item.tracktotal and item.tracktotal == "0" %}{{ item.track }}{% endif %}
            </td>

            {% if playlist_add %}
              <td> {# ADD #}
                <form action="/playlist/add/{{ item.escaped_path }}" class="center" method="post">
                  <input class="plus" type="submit" value="&plus;">
                  <input id="{{ item.slug }}" name="slug" type="hidden" value="{{ item.slug }}">
                </form>
              </td>
            {% endif %}

            <td class="title-regular"> {# TITLE #}
              <a class="mobile-big video-title" href="/browse/{{ item.escaped_path }}" title="This track's title.">
                {% if item.title %}
                  {{ item.title }}
                {% else %}
                  {{ item.file_name }}
                {% endif %}
              </a>
            </td>

            <td class="title-mobile"> {# TITLE #}
              <a class="mobile-big video-title" href="/browse/{{ item.escaped_path }}" title="This track's title.">
                {% if item.title %}
                  {{ item.title_mobile }}
                {% else %}
                  {{ item.file_name_mobile }}
                {% endif %}
              </a>
            </td>

            <td class="mobile-hide"> {# ALBUM #}
              {% if item.album %}{{ item.album }}{% endif %}
            </td>

            <td class="mobile-hide"> {# ARTIST #}
              {% if item.artist %}{{ item.artist }}{% endif %}
            </td>

            <td class="center mobile-hide">
              {% if item.length %}{{ item.length }}{% endif %}
            </td>

            {% if secret_key and playlist_rm %}
              <td class="mobile">
                <form action="/playlist/rm/{{ item.escaped_path }}" class="center" method="post">
                  <input class="bold red X" type="submit" value="X">
                </form>
              </td>
            {% endif %}

          </tr>
        {% endif %}
      {% for video in video_list %}
        {% include "video_table_rows.html" with context %}
      {% endfor %}
    </tbody>
  </table>

M mousikofidi/templates/playlist.html => mousikofidi/templates/playlist.html +12 -12
@@ 1,7 1,7 @@
{% extends 'base.html' %}
{% block content %}

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


@@ 13,11 13,11 @@

  {% if page_name == "Playlist" %}
    {% if playlists %}
      <form action="/playlist/load/%2Fload" class="center" id="playlistctl" method="post">
      <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" /> 
          <input class="center" id="delete" name="delete" title="Delete the selected playlist file." value="Delete Playlist" type="submit" />
        {% endif %}
        <input class="center" id="load" name="load" title="Load a playlist from a .m3u file." value="Load Playlist" type="submit" /> 
        <input class="center" id="load" name="load" title="Load a playlist from a .m3u file." value="Load Playlist" type="submit" />
        <select id="to-load" name="to-load" name="playlist" title="The name of the playlist to load.">
          {% for name in playlist_names %}
            <option value="{{ name }}">{{ name }}</option>


@@ 27,15 27,15 @@
    {% endif %}
  {% endif %}

  {% if item_list or video_list %}
  {% if audio_list or video_list %}
    {% if page_name == "Playlist" %}
      {% if playlistctl %}
        <form  action="/playlist/clear/%2Fall" class="center" id="playlistctl" method="post">
        <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" />
        </form>
        {% if playlist_save %}
          <form action="/playlist/save/%2Fsave" 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" /> 
          <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 id="item-list" name="bulk-list" type="hidden" value="{{ file_list }}">
          </form>


@@ 46,8 46,8 @@

    {% if playlist_detail %}
      <h2 class="bold center">Playlist Name: {{ playlist_name }}</h2>
      <form action="/playlist/load/%2Fload-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" /> 
      <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>


@@ 55,7 55,7 @@

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

    {% if item_list %}
    {% if audio_list %}
      {% include "audio_player.html" with context %}
    {% endif %}



@@ 64,7 64,7 @@
    {% endif %}
  {% endif %}

  {% if not item_list and not video_list %}
  {% if not audio_list and not video_list %}
    {% if page_name == "Playlist" %}
      <div class="center">
        <img src="{{ logo_path }}">

A mousikofidi/templates/video_table_rows.html => mousikofidi/templates/video_table_rows.html +65 -0
@@ 0,0 1,65 @@
<tr>

  <td class="center"> {# PLAY #}
    {% if page_name != "Playlist" %}
      <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>
  </td>

  <td class="center mobile-hide"> {# TRACK #}
    {% if video.track and not video.tracktotal %}{{ video.track }}{% endif %}
    {% if video.track and video.tracktotal and video.tracktotal != "0" %}{{ video.track }}/{{ video.tracktotal }}{% endif %}
    {% if video.track and video.tracktotal and video.tracktotal == "0" %}{{ video.track }}{% endif %}
  </td>

  {% if playlist_add %}
    <td> {# ADD #}
      <form action="{{ url_for('.playlistctl', cmd='add', path=video.file_path) }}" class="center" method="post">
        <input class="plus" type="submit" value="&plus;">
        <input id="{{ video.slug }}" name="slug" type="hidden" value="{{ video.slug }}">
      </form>
    </td>
  {% endif %}

  <td class="title-regular"> {# TITLE #}
    <a class="mobile-big video-title" href="{{ url_for('.dir_detail', path=video.file_path) }}" title="This track's title.">
      {% if video.title %}
        {{ video.title }}
      {% else %}
        {{ video.file_name }}
      {% endif %}
    </a>
  </td>

  <td class="title-mobile"> {# TITLE #}
    <a class="mobile-big video-title" href="{{ url_for('.dir_detail', path=video.file_path) }}" title="This track's title.">
      {% if video.title %}
        {{ video.title_mobile }}
      {% else %}
        {{ video.file_name_mobile }}
      {% endif %}
    </a>
  </td>

  <td class="mobile-hide"> {# ALBUM #}
    {% if video.album %}{{ video.album }}{% endif %}
  </td>

  <td class="mobile-hide"> {# ARTIST #}
    {% if video.artist %}{{ video.artist }}{% endif %}
  </td>

  <td class="center mobile-hide">
    {% if video.length %}{{ video.length }}{% endif %}
  </td>

  {% if secret_key and playlist_rm %}
    <td>
      <form action="{{ url_for('.playlistctl', cmd='rm', path=video.file_path) }}" class="center" method="post">
        <input class="bold red X" type="submit" value="X">
      </form>
    </td>
  {% endif %}

</tr>

M test_mousikofidi.py => test_mousikofidi.py +525 -539
@@ 62,8 62,7 @@ def client():


def test_audio_dict_flac():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example").strip("/")
    f = os.path.join(THIS_DIR, "example", "real.flac")
    m = get_metadata_dict(f)
    d = file_dict(f, m, "audio")


@@ 80,15 79,13 @@ def test_audio_dict_flac():
        "tracktotal": "0",
        "file_name": "real.flac",
        "file_name_mobile": "real.flac",
        "escaped_path": "{}%2Freal.flac".format(example_dir_escaped),
        "unescaped_path": "{}/real.flac".format(example_dir_unescaped),
        "file_path": "{}/real.flac".format(example_dir),
        "slug": "mousikófíditestflac",
    }


def test_audio_dict_mp3():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example").strip("/")
    f = os.path.join(THIS_DIR, "example", "real.mp3")
    m = get_metadata_dict(f)
    d = file_dict(f, m, "audio")


@@ 105,15 102,13 @@ def test_audio_dict_mp3():
        "tracktotal": "1",
        "file_name": "real.mp3",
        "file_name_mobile": "real.mp3",
        "escaped_path": "{}%2Freal.mp3".format(example_dir_escaped),
        "unescaped_path": "{}/real.mp3".format(example_dir_unescaped),
        "file_path": "{}/real.mp3".format(example_dir),
        "slug": "mousikófíditestmp3",
    }


def test_audio_dict_ogg():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example").strip("/")
    f = os.path.join(THIS_DIR, "example", "real.ogg")
    m = get_metadata_dict(f)
    d = file_dict(f, m, "audio")


@@ 130,8 125,7 @@ def test_audio_dict_ogg():
        "tracktotal": None,
        "file_name": "real.ogg",
        "file_name_mobile": "real.ogg",
        "escaped_path": "{}%2Freal.ogg".format(example_dir_escaped),
        "unescaped_path": "{}/real.ogg".format(example_dir_unescaped),
        "file_path": "{}/real.ogg".format(example_dir),
        "slug": "mousikófíditestogg",
    }



@@ 202,32 196,31 @@ def test_audio_metadata_real_ogg():
    }


# TODO: Another test with deeper directories is needed
def test_breadcrumb_links_from_path_dir():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    expected_string = '<a href="/browse/{escaped}">{unescaped}</a>'.format(
        escaped=example_dir_escaped, unescaped=example_dir_unescaped
    example_dir = os.path.join(THIS_DIR, "example")
    # TODO: somehow use url_for() here;
    # RuntimeError: Attempted to generate a URL without the application context being pushed. This has to be executed when application context is available.
    dir_detail_url = "/browse/{}".format(example_dir.strip("/"))
    expected_string = '<a href="{url}">{full_path}</a>'.format(
        full_path=example_dir, url=dir_detail_url
    )

    with app.test_request_context("/browse/" + example_dir_escaped):
        link_string = breadcrumb_links_from_path(
            example_dir_unescaped, [example_dir_unescaped]
        )
    with app.test_request_context(dir_detail_url):
        link_string = breadcrumb_links_from_path(example_dir, [example_dir])

    assert link_string == expected_string


def test_breadcrumb_links_from_path_file():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    expected_string = '<a href="/browse/{escaped}">{unescaped}</a> / real.flac'.format(
        escaped=example_dir_escaped, unescaped=example_dir_unescaped
    example_dir = os.path.join(THIS_DIR, "example")
    file_path = os.path.join(example_dir, "real.flac")
    url = "/browse/{}".format(example_dir.strip("/"))
    expected_string = '<a href="{url}">{path}</a> / real.flac'.format(
        path=example_dir, url=url
    )
    file_path = os.path.join(example_dir_unescaped, "real.flac")

    with app.test_request_context("/browse/" + example_dir_escaped):
        link_string = breadcrumb_links_from_path(file_path, [example_dir_unescaped])
    with app.test_request_context(url):
        link_string = breadcrumb_links_from_path(file_path, [example_dir])

    assert link_string == expected_string



@@ 237,10 230,22 @@ def test_breadcrumb_links_from_path_deep_dir():
    base = os.path.join(tmpdir, "flac")
    tmpdeep = os.path.join(base, "SomeArtist", "SomeAlbum", "SomeDisc")
    os.makedirs(tmpdeep)
    expected_string = '<a href="/browse/{etmp}%2Fflac">{tmp}/flac</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist">SomeArtist</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist%2FSomeAlbum">SomeAlbum</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist%2FSomeAlbum%2FSomeDisc">SomeDisc</a>'.format(
        etmp=quote(tmpdir), tmp=tmpdir
    )
    link_string = breadcrumb_links_from_path(tmpdeep, [base])
    expected_string = '<a href="{url1}">{dir1}</a> / <a href="{url2}">{dir2}</a> / <a href="{url3}">{dir3}</a> / <a href="{url4}">{dir4}</a>'.format(
        dir1=os.path.join(tmpdir, "flac"),
        dir2="SomeArtist",
        dir3="SomeAlbum",
        dir4="SomeDisc",
        url1="/browse/{}/flac".format(tmpdir.strip("/")),
        url2="/browse/{}/flac/SomeArtist".format(tmpdir.strip("/")),
        url3="/browse/{}/flac/SomeArtist/SomeAlbum".format(tmpdir.strip("/")),
        url4="/browse/{}/flac/SomeArtist/SomeAlbum/SomeDisc".format(tmpdir.strip("/")),
    )

    with app.test_request_context(
        "/browse/{}/flac/SomeArtist/SomeAlbum/SomeDisc".format(tmpdir.strip("/"))
    ):
        link_string = breadcrumb_links_from_path(tmpdeep, [base])

    shutil.rmtree(tmpdir)
    assert link_string == expected_string



@@ 250,30 255,184 @@ def test_breadcrumb_links_from_path_deep_file():
    base = os.path.join(tmpdir, "flac")
    tmpdeep = os.path.join(base, "SomeArtist", "SomeAlbum", "SomeDisc")
    os.makedirs(tmpdeep)
    expected_string = '<a href="/browse/{etmp}%2Fflac">{tmp}/flac</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist">SomeArtist</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist%2FSomeAlbum">SomeAlbum</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist%2FSomeAlbum%2FSomeDisc">SomeDisc</a> / real.flac'.format(
        etmp=quote(tmpdir), tmp=tmpdir
    # expected_string = '<a href="/browse/{etmp}%2Fflac">{tmp}/flac</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist">SomeArtist</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist%2FSomeAlbum">SomeAlbum</a> / <a href="/browse/{etmp}%2Fflac%2FSomeArtist%2FSomeAlbum%2FSomeDisc">SomeDisc</a> / real.flac'.format(
    #     etmp=quote(tmpdir), tmp=tmpdir
    # )

    expected_string = '<a href="{url1}">{dir1}</a> / <a href="{url2}">{dir2}</a> / <a href="{url3}">{dir3}</a> / <a href="{url4}">{dir4}</a> / real.flac'.format(
        dir1=os.path.join(tmpdir, "flac"),
        dir2="SomeArtist",
        dir3="SomeAlbum",
        dir4="SomeDisc",
        url1="/browse/{}/flac".format(tmpdir.strip("/")),
        url2="/browse/{}/flac/SomeArtist".format(tmpdir.strip("/")),
        url3="/browse/{}/flac/SomeArtist/SomeAlbum".format(tmpdir.strip("/")),
        url4="/browse/{}/flac/SomeArtist/SomeAlbum/SomeDisc".format(tmpdir.strip("/")),
    )

    deepfile = os.path.join(tmpdeep, "real.flac")
    shutil.copy(os.path.join(THIS_DIR, "example", "real.flac"), tmpdeep)
    link_string = breadcrumb_links_from_path(deepfile, [base])

    with app.test_request_context(
        "/browse/{}/flac/SomeArtist/SomeAlbum/SomeDisc/real.flac".format(
            tmpdir.strip("/")
        )
    ):
        link_string = breadcrumb_links_from_path(deepfile, [base])

    shutil.rmtree(tmpdir)
    assert link_string == expected_string


def test_browse_dir():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    with app.test_request_context("/browse/" + example_dir_escaped):
        c = request_context(
            init(use_config=os.path.join(example_dir_unescaped, "fidi.yml"))
        )
        dir_dict = browse_dir(c, example_dir_unescaped)
    unescaped_sourcedir = sys.argv[-1]
    escaped_sourcedir = quote(unescaped_sourcedir)
    example_dir = os.path.join(THIS_DIR, "example")
    with app.test_request_context("/browse/" + example_dir.strip("/")):
        c = request_context(init(use_config=os.path.join(example_dir, "fidi.yml")))
        actual_dict = browse_dir(c, example_dir)

    sourcedir = sys.argv[-1]

    favicon = select_logo(app.fidiConfig, "favicon_path")
    logo = select_logo(app.fidiConfig, "logo_path")

    audio_list = [
        {
            "album": None,
            "artist": None,
            "genre": None,
            "length": None,
            "title": None,
            "title_mobile": None,
            "track": None,
            "type": "audio",
            "tracktotal": None,
            "file_name": "fake.flac",
            "file_name_mobile": "fake.flac",
            "file_path": "{}/example/fake.flac".format(sourcedir).strip("/"),
            "slug": "fakeflac",
        },
        {
            "album": None,
            "artist": None,
            "genre": None,
            "length": None,
            "title": None,
            "title_mobile": None,
            "track": None,
            "type": "audio",
            "tracktotal": None,
            "file_name": "fake.mp3",
            "file_name_mobile": "fake.mp3",
            "file_path": "{}/example/fake.mp3".format(sourcedir).strip("/"),
            "slug": "fakemp3",
        },
        {
            "album": None,
            "artist": None,
            "genre": None,
            "length": None,
            "title": None,
            "title_mobile": None,
            "track": None,
            "type": "audio",
            "tracktotal": None,
            "file_name": "fake.ogg",
            "file_name_mobile": "fake.ogg",
            "file_path": "{}/example/fake.ogg".format(sourcedir).strip("/"),
            "slug": "fakeogg",
        },
        {
            "type": "audio",
            "album": "MousikóFídi Test Album",
            "artist": "MousikóFídi Test Artist",
            "genre": "MousikóFídi Test",
            "length": "0:04",
            "title": "MousikóFídi Test FLAC",
            "title_mobile": "MousikóFídi Test FLAC",
            "track": "34",
            "tracktotal": "0",
            "file_name": "real.flac",
            "file_name_mobile": "real.flac",
            "file_path": "{}/example/real.flac".format(sourcedir).strip("/"),
            "slug": "mousikófíditestflac",
        },
        {
            "type": "audio",
            "album": "MousikóFídi Test Album",
            "artist": "MousikóFídi Test Artist",
            "genre": "MousikóFídi Test",
            "length": "0:04",
            "title": "MousikóFídi Test MP3",
            "title_mobile": "MousikóFídi Test MP3",
            "track": "34",
            "tracktotal": "1",
            "file_name": "real.mp3",
            "file_name_mobile": "real.mp3",
            "file_path": "{}/example/real.mp3".format(sourcedir).strip("/"),
            "slug": "mousikófíditestmp3",
        },
        {
            "type": "audio",
            "album": "MousikóFídi Test Album",
            "artist": "MousikóFídi Test Artist",
            "genre": "MousikóFídi Test",
            "length": "0:00",
            "title": "MousikóFídi Test OGG",
            "title_mobile": "MousikóFídi Test OGG",
            "track": "34",
            "tracktotal": None,
            "file_name": "real.ogg",
            "file_name_mobile": "real.ogg",
            "file_path": "{}/example/real.ogg".format(sourcedir).strip("/"),
            "slug": "mousikófíditestogg",
        },
    ]
    video_list = [
        {
            "album": None,
            "artist": None,
            "genre": None,
            "length": None,
            "title": None,
            "title_mobile": None,
            "track": None,
            "tracktotal": None,
            "type": "video",
            "file_name": "fake.mp4",
            "file_name_mobile": "fake.mp4",
            "file_path": "{}/example/fake.mp4".format(sourcedir).strip("/"),
            "slug": "fakemp4",
        },
        {
            "album": None,
            "artist": None,
            "genre": None,
            "length": None,
            "title": None,
            "title_mobile": None,
            "track": None,
            "tracktotal": None,
            "type": "video",
            "file_name": "fake.webm",
            "file_name_mobile": "fake.webm",
            "file_path": "{}/example/fake.webm".format(sourcedir).strip("/"),
            "slug": "fakewebm",
        },
        {
            "type": "video",
            "album": "MousikóFídi Test Album",
            "artist": "MousikóFídi Test Artist",
            "genre": "MousikóFídi Test Genre",
            "length": "0:06",
            "title": "MousikóFídi Test MP4",
            "title_mobile": "MousikóFídi Test MP4",
            "track": "34",
            "tracktotal": "100",
            "file_name": "real.mp4",
            "file_name_mobile": "real.mp4",
            "file_path": "{}/example/real.mp4".format(sourcedir).strip("/"),
            "slug": "mousikófíditestmp4",
        },
    ]
    expected_dict = {
        "audio_player": True,
        "add_all_button": True,


@@ 281,21 440,32 @@ def test_browse_dir():
        "favicon_path": favicon,
        "icons": False,
        "logo_path": logo,
        "cover_art": "/serve/{}%2Fexample%2FMousikoFidi%20Sample%20Cover%20Art.png".format(
            escaped_sourcedir
        "cover_art": "/serve/{}/MousikoFidi%20Sample%20Cover%20Art.png".format(
            example_dir.strip("/")
        ),
        "file_list": [
            "{}/fake.flac".format(example_dir),
            "{}/fake.mp3".format(example_dir),
            "{}/fake.mp4".format(example_dir),
            "{}/fake.ogg".format(example_dir),
            "{}/fake.webm".format(example_dir),
            "{}/real.flac".format(example_dir),
            "{}/real.mp3".format(example_dir),
            "{}/real.mp4".format(example_dir),
            "{}/real.ogg".format(example_dir),
        ],
        "music_dirs": [
            {
                "raw": "/home/username/music/flac",
                "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                "full_path": "/home/username/music/flac",
                "path": "home/username/music/flac",
            },
            {
                "raw": "/home/username/music/ogg",
                "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                "full_path": "/home/username/music/ogg",
                "path": "home/username/music/ogg",
            },
            {
                "raw": "/home/username/video/mp4",
                "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                "full_path": "/home/username/video/mp4",
                "path": "home/username/video/mp4",
            },
        ],
        "playlist_dir": "/home/username/music/playlists",


@@ 309,191 479,29 @@ def test_browse_dir():
        "theme": "/css/water/light.standalone",
        "username": None,
        "video_player": True,
        "file_list": [
            "{}/example/fake.flac".format(unescaped_sourcedir),
            "{}/example/fake.mp3".format(unescaped_sourcedir),
            "{}/example/fake.mp4".format(unescaped_sourcedir),
            "{}/example/fake.ogg".format(unescaped_sourcedir),
            "{}/example/fake.webm".format(unescaped_sourcedir),
            "{}/example/real.flac".format(unescaped_sourcedir),
            "{}/example/real.mp3".format(unescaped_sourcedir),
            "{}/example/real.mp4".format(unescaped_sourcedir),
            "{}/example/real.ogg".format(unescaped_sourcedir),
        ],
        "page_name": "{}/example".format(unescaped_sourcedir),
        "page_name": "{}/example".format(sourcedir),
        "page_path": "",
        "search": False,
        "playlist_add": True,
        "playlist_rm": False,
        "item_type": "dir",
        "dir_list": [
            {
                "dir_name": "runit",
                "escaped_path": "{}%2Fexample%2Frunit".format(escaped_sourcedir),
                "unescaped_path": "{}/example/runit".format(unescaped_sourcedir),
            }
        ],
        "item_list": [
            {
                "album": None,
                "artist": None,
                "genre": None,
                "length": None,
                "title": None,
                "title_mobile": None,
                "track": None,
                "type": "audio",
                "tracktotal": None,
                "file_name": "fake.flac",
                "file_name_mobile": "fake.flac",
                "escaped_path": "{}%2Fexample%2Ffake.flac".format(escaped_sourcedir),
                "unescaped_path": "{}/example/fake.flac".format(unescaped_sourcedir),
                "slug": "fakeflac",
            },
            {
                "album": None,
                "artist": None,
                "genre": None,
                "length": None,
                "title": None,
                "title_mobile": None,
                "track": None,
                "type": "audio",
                "tracktotal": None,
                "file_name": "fake.mp3",
                "file_name_mobile": "fake.mp3",
                "escaped_path": "{}%2Fexample%2Ffake.mp3".format(escaped_sourcedir),
                "unescaped_path": "{}/example/fake.mp3".format(unescaped_sourcedir),
                "slug": "fakemp3",
            },
            {
                "album": None,
                "artist": None,
                "genre": None,
                "length": None,
                "title": None,
                "title_mobile": None,
                "track": None,
                "tracktotal": None,
                "type": "video",
                "file_name": "fake.mp4",
                "file_name_mobile": "fake.mp4",
                "escaped_path": "{}%2Fexample%2Ffake.mp4".format(escaped_sourcedir),
                "unescaped_path": "{}/example/fake.mp4".format(unescaped_sourcedir),
                "slug": "fakemp4",
            },
            {
                "album": None,
                "artist": None,
                "genre": None,
                "length": None,
                "title": None,
                "title_mobile": None,
                "track": None,
                "type": "audio",
                "tracktotal": None,
                "file_name": "fake.ogg",
                "file_name_mobile": "fake.ogg",
                "escaped_path": "{}%2Fexample%2Ffake.ogg".format(escaped_sourcedir),
                "unescaped_path": "{}/example/fake.ogg".format(unescaped_sourcedir),
                "slug": "fakeogg",
            },
            {
                "album": None,
                "artist": None,
                "genre": None,
                "length": None,
                "title": None,
                "title_mobile": None,
                "track": None,
                "tracktotal": None,
                "type": "video",
                "file_name": "fake.webm",
                "file_name_mobile": "fake.webm",
                "escaped_path": "{}%2Fexample%2Ffake.webm".format(escaped_sourcedir),
                "unescaped_path": "{}/example/fake.webm".format(unescaped_sourcedir),
                "slug": "fakewebm",
            },
            {
                "type": "audio",
                "album": "MousikóFídi Test Album",
                "artist": "MousikóFídi Test Artist",
                "genre": "MousikóFídi Test",
                "length": "0:04",
                "title": "MousikóFídi Test FLAC",
                "title_mobile": "MousikóFídi Test FLAC",
                "track": "34",
                "tracktotal": "0",
                "file_name": "real.flac",
                "file_name_mobile": "real.flac",
                "escaped_path": "{}%2Fexample%2Freal.flac".format(escaped_sourcedir),
                "unescaped_path": "{}/example/real.flac".format(unescaped_sourcedir),
                "slug": "mousikófíditestflac",
            },
            {
                "type": "audio",
                "album": "MousikóFídi Test Album",
                "artist": "MousikóFídi Test Artist",
                "genre": "MousikóFídi Test",
                "length": "0:04",
                "title": "MousikóFídi Test MP3",
                "title_mobile": "MousikóFídi Test MP3",
                "track": "34",
                "tracktotal": "1",
                "file_name": "real.mp3",
                "file_name_mobile": "real.mp3",
                "escaped_path": "{}%2Fexample%2Freal.mp3".format(escaped_sourcedir),
                "unescaped_path": "{}/example/real.mp3".format(unescaped_sourcedir),
                "slug": "mousikófíditestmp3",
            },
            {
                "type": "video",
                "album": "MousikóFídi Test Album",
                "artist": "MousikóFídi Test Artist",
                "genre": "MousikóFídi Test Genre",
                "length": "0:06",
                "title": "MousikóFídi Test MP4",
                "title_mobile": "MousikóFídi Test MP4",
                "track": "34",
                "tracktotal": "100",
                "file_name": "real.mp4",
                "file_name_mobile": "real.mp4",
                "escaped_path": "{}%2Fexample%2Freal.mp4".format(escaped_sourcedir),
                "unescaped_path": "{}/example/real.mp4".format(unescaped_sourcedir),
                "slug": "mousikófíditestmp4",
            },
            {
                "type": "audio",
                "album": "MousikóFídi Test Album",
                "artist": "MousikóFídi Test Artist",
                "genre": "MousikóFídi Test",
                "length": "0:00",
                "title": "MousikóFídi Test OGG",
                "title_mobile": "MousikóFídi Test OGG",
                "track": "34",
                "tracktotal": None,
                "file_name": "real.ogg",
                "file_name_mobile": "real.ogg",
                "escaped_path": "{}%2Fexample%2Freal.ogg".format(escaped_sourcedir),
                "unescaped_path": "{}/example/real.ogg".format(unescaped_sourcedir),
                "slug": "mousikófíditestogg",
            },
            {"name": "runit", "path": "{}/example/runit".format(sourcedir).strip("/")}
        ],
        "audio_list": audio_list,
        "video_list": video_list,
        "item_list": sorted(audio_list + video_list, key=lambda n: n["file_name"]),
    }

    assert dir_dict == expected_dict
    assert actual_dict == expected_dict


def test_browse_file():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    with app.test_request_context("/browse/" + example_dir_escaped):
        c = request_context(
            init(use_config=os.path.join(example_dir_unescaped, "fidi.yml"))
        )
        file_dict = browse_file(c, example_dir_unescaped)
    unescaped_sourcedir = sys.argv[-1]
    escaped_sourcedir = quote(unescaped_sourcedir)
    example_dir = os.path.join(THIS_DIR, "example")
    with app.test_request_context("/browse/" + example_dir.strip("/")):
        c = request_context(init(use_config=os.path.join(example_dir, "fidi.yml")))
        actual_dict = browse_file(c, example_dir)
    sourcedir = sys.argv[-1]
    favicon = select_logo(app.fidiConfig, "favicon_path")
    logo = select_logo(app.fidiConfig, "logo_path")
    expected_dict = {


@@ 512,18 520,20 @@ def test_browse_file():
        "page_name": "example",
        "music_dirs": [
            {
                "raw": "/home/username/music/flac",
                "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                "full_path": "/home/username/music/flac",
                "path": "home/username/music/flac",
            },
            {
                "raw": "/home/username/music/ogg",
                "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                "full_path": "/home/username/music/ogg",
                "path": "home/username/music/ogg",
            },
            {
                "raw": "/home/username/video/mp4",
                "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                "full_path": "/home/username/video/mp4",
                "path": "home/username/video/mp4",
            },
        ],
        "full_path": sourcedir + "/example",
        "path": sourcedir.strip("/") + "/example",
        "playlist_dir": "/home/username/music/playlists",
        "playlist_save": True,
        "playlists": [],


@@ 535,15 545,14 @@ def test_browse_file():
        "user_playlist": [],
        "theme": "/css/water/light.standalone",
        "username": None,
        "escaped_path": "{}%2Fexample".format(escaped_sourcedir),
        "unescaped_path": "{}/example".format(unescaped_sourcedir),
        "file_name": "example",
        "page_path": "",
        "cover_art": "/serve/{}%2Fexample%2FMousikoFidi%20Sample%20Cover%20Art.png".format(
            escaped_sourcedir
        "cover_art": "/serve/{}/example/MousikoFidi%20Sample%20Cover%20Art.png".format(
            sourcedir.strip("/")
        ),
    }
    assert file_dict == expected_dict

    assert actual_dict == expected_dict


def test_config_to_string():


@@ 574,25 583,20 @@ def test_config_to_string():


def test_dir_dict():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example")
    d = os.path.join(THIS_DIR, "example")
    dd = dir_dict(d)
    assert dd == {
        "dir_name": "example",
        "escaped_path": "{}".format(example_dir_escaped),
        "unescaped_path": "{}".format(example_dir_unescaped),
    }
    print(dd)
    assert dd == {"name": "example", "path": "{}".format(example_dir).strip("/")}


def test_make_unique_slugs():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    with app.test_request_context("/browse/" + example_dir_escaped):
    example_dir = os.path.join(THIS_DIR, "example")
    with app.test_request_context("/browse/" + example_dir):
        c = request_context(
            init(use_config=os.path.join(THIS_DIR, "example", "fidi.yml"))
        )
    browse_dir(c, example_dir_unescaped)
        browse_dir(c, example_dir)
    new_dict = make_unique_slugs(c["item_list"])

    used_slugs = []


@@ 608,6 612,7 @@ def test_request_context():
        r = request_context(c)
        favicon = select_logo(c, "favicon_path")
        logo = select_logo(c, "logo_path")

        assert r == {
            "debug": False,
            "favicon_path": favicon,


@@ 615,16 620,16 @@ def test_request_context():
            "logo_path": logo,
            "music_dirs": [
                {
                    "raw": "/home/username/music/flac",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                    "full_path": "/home/username/music/flac",
                    "path": "home/username/music/flac",
                },
                {
                    "raw": "/home/username/music/ogg",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                    "full_path": "/home/username/music/ogg",
                    "path": "home/username/music/ogg",
                },
                {
                    "raw": "/home/username/video/mp4",
                    "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                    "full_path": "/home/username/video/mp4",
                    "path": "home/username/video/mp4",
                },
            ],
            "playlist_dir": "/home/username/music/playlists",


@@ 652,12 657,13 @@ def test_select_cover_art_false():
def test_select_cover_art_true():
    app.fidiConfig["config"]["cover_art"] = True
    example_dir = os.path.join(THIS_DIR, "example")
    escaped_cover_art = quote(
        os.path.join(example_dir, "MousikoFidi Sample Cover Art.png")
    )
    cover_art_path = os.path.join(
        example_dir, quote("MousikoFidi Sample Cover Art.png")
    ).strip("/")
    real_flac = os.path.join(example_dir, "real.flac")
    cover_art = select_cover_art(real_flac)
    assert cover_art == "/serve/{}".format(escaped_cover_art)
    with app.test_request_context("/"):
        cover_art = select_cover_art(real_flac)
    assert cover_art == "/serve/{}".format(cover_art_path)


def test_select_logo_no_holiday():


@@ 767,16 773,16 @@ def test_handle_playlist_cmd_add():
            "logo_path": logo,
            "music_dirs": [
                {
                    "raw": "/home/username/music/flac",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                    "full_path": "/home/username/music/flac",
                    "path": "home/username/music/flac",
                },
                {
                    "raw": "/home/username/music/ogg",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                    "full_path": "/home/username/music/ogg",
                    "path": "home/username/music/ogg",
                },
                {
                    "raw": "/home/username/video/mp4",
                    "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                    "full_path": "/home/username/video/mp4",
                    "path": "home/username/video/mp4",
                },
            ],
            "playlist_dir": "/home/username/music/playlists",


@@ 794,11 800,11 @@ def test_handle_playlist_cmd_add():
        assert d == expected_dict


# TODO: A test is needed that posts to the bulk endpoint and is able to
# subsequently check the user playlist, either by viewing the page or by
# inspecting the context value.
# def test_handle_playlist_cmd_bulk(client):
#     pass
# # TODO: A test is needed that posts to the bulk endpoint and is able to
# # subsequently check the user playlist, either by viewing the page or by
# # inspecting the context value.
# # def test_handle_playlist_cmd_bulk(client):
# #     pass


def test_handle_playlist_cmd_add_multi():


@@ 829,16 835,16 @@ def test_handle_playlist_cmd_add_multi():
            "logo_path": logo,
            "music_dirs": [
                {
                    "raw": "/home/username/music/flac",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                    "full_path": "/home/username/music/flac",
                    "path": "home/username/music/flac",
                },
                {
                    "raw": "/home/username/music/ogg",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                    "full_path": "/home/username/music/ogg",
                    "path": "home/username/music/ogg",
                },
                {
                    "raw": "/home/username/video/mp4",
                    "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                    "full_path": "/home/username/video/mp4",
                    "path": "home/username/video/mp4",
                },
            ],
            "playlist_dir": "/home/username/music/playlists",


@@ 879,16 885,16 @@ def test_handle_playlist_cmd_clear():
            "logo_path": logo,
            "music_dirs": [
                {
                    "raw": "/home/username/music/flac",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                    "full_path": "/home/username/music/flac",
                    "path": "home/username/music/flac",
                },
                {
                    "raw": "/home/username/music/ogg",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                    "full_path": "/home/username/music/ogg",
                    "path": "home/username/music/ogg",
                },
                {
                    "raw": "/home/username/video/mp4",
                    "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                    "full_path": "/home/username/video/mp4",
                    "path": "home/username/video/mp4",
                },
            ],
            "playlist_dir": "/home/username/music/playlists",


@@ 906,18 912,18 @@ def test_handle_playlist_cmd_clear():
        assert d == expected_dict


# TODO: This
# def test_handle_playlist_cmd_save():
#     tmpdir = tempfile.mkdtemp()
#     context = {"playlist_dir": tmpdir}
#     handle_playlist_cmd("save", "%2Fsave", context)
# # TODO: This
# # def test_handle_playlist_cmd_save():
# #     tmpdir = tempfile.mkdtemp()
# #     context = {"playlist_dir": tmpdir}
# #     handle_playlist_cmd("save", "%2Fsave", context)


# TODO: And this
# def test_handle_playlist_cmd_load():
#     tmpdir = tempfile.mkdtemp()
#     context = {"playlist_dir": tmpdir}
#     handle_playlist_cmd("save", "%2Fsave", context)
# # TODO: And this
# # def test_handle_playlist_cmd_load():
# #     tmpdir = tempfile.mkdtemp()
# #     context = {"playlist_dir": tmpdir}
# #     handle_playlist_cmd("save", "%2Fsave", context)


def test_handle_playlist_cmd_rm():


@@ 950,16 956,16 @@ def test_handle_playlist_cmd_rm():
            "logo_path": logo,
            "music_dirs": [
                {
                    "raw": "/home/username/music/flac",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
                    "full_path": "/home/username/music/flac",
                    "path": "home/username/music/flac",
                },
                {
                    "raw": "/home/username/music/ogg",
                    "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
                    "full_path": "/home/username/music/ogg",
                    "path": "home/username/music/ogg",
                },
                {
                    "raw": "/home/username/video/mp4",
                    "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
                    "full_path": "/home/username/video/mp4",
                    "path": "home/username/video/mp4",
                },
            ],
            "playlist_dir": "/home/username/music/playlists",


@@ 1008,6 1014,9 @@ def test_init():
    }


# TODO: test init() with no cfg file


def test_get_metadata_dict_real_audio():
    f = os.path.join(THIS_DIR, "example", "real.flac")
    d = get_metadata_dict(f)


@@ 1397,18 1406,9 @@ def test_paths_list():
    md = d["config"]["music_dirs"]
    pl = paths_list(md)
    expected_pl = [
        {
            "raw": "/home/username/music/flac",
            "encoded": "%2Fhome%2Fusername%2Fmusic%2Fflac",
        },
        {
            "raw": "/home/username/music/ogg",
            "encoded": "%2Fhome%2Fusername%2Fmusic%2Fogg",
        },
        {
            "raw": "/home/username/video/mp4",
            "encoded": "%2Fhome%2Fusername%2Fvideo%2Fmp4",
        },
        {"full_path": "/home/username/music/flac", "path": "home/username/music/flac"},
        {"full_path": "/home/username/music/ogg", "path": "home/username/music/ogg"},
        {"full_path": "/home/username/video/mp4", "path": "home/username/video/mp4"},
    ]
    assert pl == expected_pl



@@ 1419,9 1419,8 @@ def test_title_slug():


def test_video_dict():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    f = os.path.join(example_dir_unescaped, "fake.mp4")
    example_dir = os.path.join(THIS_DIR, "example")
    f = os.path.join(example_dir, "fake.mp4")
    m = get_metadata_dict(f)
    d = file_dict(f, m, "video")
    assert d == {


@@ 1436,9 1435,8 @@ def test_video_dict():
        "type": "video",
        "file_name": "fake.mp4",
        "file_name_mobile": "fake.mp4",
        "escaped_path": "{}%2Ffake.mp4".format(example_dir_escaped),
        "file_path": "{}/fake.mp4".format(example_dir).strip("/"),
        "slug": "fakemp4",
        "unescaped_path": "{}/fake.mp4".format(example_dir_unescaped),
    }




@@ 1855,9 1853,9 @@ def test_index_terminal_solarized_theme(client):


def test_browse_http_code(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir = os.path.join(THIS_DIR, "example")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    site_name = "COOL TEST SITE"
    url = "/browse"
    app.fidiConfig["config"]["music_dirs"] = dir_list


@@ 1869,7 1867,7 @@ def test_browse_http_code(client):
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert rv.status == "200 OK"
    assert bytes(example_dir_unescaped, "utf8") in rv.data
    assert bytes(example_dir, "utf8") in rv.data
    assert (
        bytes(
            '<title id="title" data-sitename="{n}">Media Dirs | {n}</title>'.format(


@@ 1883,7 1881,7 @@ def test_browse_http_code(client):
    assert (
        bytes(
            '<a class="bold mobile-big" href="/browse/{}">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2094,11 2092,11 @@ def test_browse_with_playlists(client):


def test_dir_detail_found(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir = os.path.join(THIS_DIR, "example")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    site_name = "COOL TEST SITE"
    url = "/browse" + example_dir_unescaped
    url = "/browse" + example_dir

    app.fidiConfig["config"]["music_dirs"] = dir_list
    app.fidiConfig["config"]["site_name"] = site_name


@@ 2114,8 2112,8 @@ def test_dir_detail_found(client):
    app.fidiConfig = app._fidiConfig.copy()

    assert rv.status == "200 OK"
    assert bytes(example_dir_unescaped, "utf8") in rv.data
    assert bytes(quote(example_dir_unescaped), "utf8") in rv.data
    assert bytes(example_dir, "utf8") in rv.data
    assert bytes(example_dir, "utf8") in rv.data
    assert b"fake.flac" in rv.data
    assert b"fake.mp3" in rv.data
    assert b"fake.ogg" in rv.data


@@ 2134,7 2132,7 @@ def test_dir_detail_found(client):
    assert (
        bytes(
            '<title id="title" data-sitename="{n}">{d} | {n}</title>'.format(
                d=example_dir_unescaped, n=site_name
                d=example_dir, n=site_name
            ),
            "utf8",
        )


@@ 2142,8 2140,8 @@ def test_dir_detail_found(client):
    )
    assert (
        bytes(
            '<h4 class="center mobile-big"><a href="/browse/{escaped}">{unescaped}</a></h4>'.format(
                escaped=quote(example_dir_unescaped), unescaped=example_dir_unescaped
            '<h4 class="center mobile-big"><a href="/browse/{0}">{1}</a></h4>'.format(
                example_dir.strip("/"), example_dir
            ),
            "utf8",
        )


@@ 2272,18 2270,17 @@ def test_dir_detail_found(client):


def test_dir_detail_not_found():
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir_escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "zexample")
    with app.test_client() as c:
        rv = c.get("/browse/" + example_dir_escaped)
        rv = c.get("/browse/" + example_dir.strip("/"))
    assert rv.status == "404 NOT FOUND"


def test_dir_detail_not_found_real_with_extra(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir = os.path.join(THIS_DIR, "example")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    url = "/browse" + example_dir_unescaped + "extragarbage"
    dir_list.append(example_dir)
    url = "/browse" + example_dir.strip("/") + "extragarbage"
    app.fidiConfig["config"]["music_dirs"] = dir_list
    with app.app_context():
        rv = client.get(url)


@@ 2293,14 2290,14 @@ def test_dir_detail_not_found_real_with_extra(client):


def test_dir_detail_not_found_real_with_plus_sign(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir = os.path.join(THIS_DIR, "example")
    tempdir = "/tmp/valid + dir + ( with ) + [ pluses ]"
    os.mkdir(tempdir)
    shutil.copy(os.path.join(example_dir_unescaped, "real.mp3"), tempdir)
    shutil.copy(os.path.join(example_dir, "real.mp3"), tempdir)
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    dir_list.append(tempdir)
    url = "/browse" + quote(tempdir)
    url = "/browse/" + tempdir.strip("/")

    with app.app_context():
        app.fidiConfig["config"]["music_dirs"] = dir_list


@@ 2312,12 2309,11 @@ def test_dir_detail_not_found_real_with_plus_sign(client):


def test_file_detail_real_flac(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example", "real.flac")
    escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example", "real.flac")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    site_name = "COOL TEST SITE"
    url = "/browse" + example_dir_unescaped
    url = "/browse/" + example_dir.strip("/")
    app.fidiConfig["config"]["favicon_path"] = "/cool-favicon.png"
    app.fidiConfig["config"]["music_dirs"] = dir_list
    app.fidiConfig["config"]["site_name"] = site_name


@@ 2327,8 2323,7 @@ def test_file_detail_real_flac(client):
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert rv.status == "200 OK"
    assert bytes(escaped, "utf8") in rv.data
    assert bytes(example_dir_unescaped, "utf8") in rv.data
    assert bytes(example_dir, "utf8") in rv.data
    assert b"Track: 34" in rv.data
    assert b"Released: 2019-08-08" in rv.data
    assert bytes("Genre: MousikóFídi Test", "utf8") in rv.data


@@ 2336,15 2331,19 @@ def test_file_detail_real_flac(client):
    assert bytes("MousikóFídi Test Album", "utf8") in rv.data
    assert bytes("MousikóFídi Test FLAC", "utf8") in rv.data
    assert bytes(
        '<img data-stat="off" id="cover-art" src="/serve/{}%2Fexample%2FMousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            escaped
        '<img data-stat="off" id="cover-art" src="/serve/{}/example/MousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            example_dir.strip("/")
        ),
        "utf8",
    )

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

    assert (
        bytes(
            '<audio id="single" controls src="/serve/{}">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2353,7 2352,7 @@ def test_file_detail_real_flac(client):
    assert (
        bytes(
            '<form class="center" action="/playlist/add/{}" method="post">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2397,12 2396,11 @@ def test_file_detail_real_flac(client):


def test_file_detail_real_mp3(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example", "real.mp3")
    escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example", "real.mp3")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    site_name = "COOL TEST SITE"
    url = "/browse" + example_dir_unescaped
    url = "/browse/" + example_dir.strip("/")
    app.fidiConfig["config"]["favicon_path"] = "/cool-favicon.png"
    app.fidiConfig["config"]["music_dirs"] = dir_list
    app.fidiConfig["config"]["site_name"] = site_name


@@ 2412,8 2410,7 @@ def test_file_detail_real_mp3(client):
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert rv.status == "200 OK"
    assert bytes(escaped, "utf8") in rv.data
    assert bytes(example_dir_unescaped, "utf8") in rv.data
    assert bytes(example_dir.strip("/"), "utf8") in rv.data
    assert bytes("Genre: MousikóFídi Test", "utf8") in rv.data
    assert bytes('"MousikóFídi Test Album"', "utf8") in rv.data
    assert bytes("MousikóFídi Test Artist", "utf8") in rv.data


@@ 2421,15 2418,15 @@ def test_file_detail_real_mp3(client):
    assert bytes("MousikóFídi Test Artist", "utf8") in rv.data
    assert bytes("MousikóFídi Test MP3", "utf8") in rv.data
    assert bytes(
        '<img data-stat="off" id="cover-art" src="/serve/{}%2Fexample%2FMousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            escaped
        '<img data-stat="off" id="cover-art" src="/serve/{}/example/MousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            example_dir.strip("/")
        ),
        "utf8",
    )
    assert (
        bytes(
            '<audio id="single" controls src="/serve/{}">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2438,7 2435,7 @@ def test_file_detail_real_mp3(client):
    assert (
        bytes(
            '<form class="center" action="/playlist/add/{}" method="post">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2489,12 2486,11 @@ def test_file_detail_real_mp3(client):


def test_file_detail_real_mp4(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example", "real.mp4")
    escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example", "real.mp4")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    site_name = "COOL TEST SITE"
    url = "/browse" + example_dir_unescaped
    url = "/browse/" + example_dir.strip("/")

    app.fidiConfig["config"]["favicon_path"] = "/cool-favicon.png"
    app.fidiConfig["config"]["music_dirs"] = dir_list


@@ 2508,8 2504,7 @@ def test_file_detail_real_mp4(client):
    app.fidiConfig = app._fidiConfig.copy()

    assert rv.status == "200 OK"
    assert bytes(escaped, "utf8") in rv.data
    assert bytes(example_dir_unescaped, "utf8") in rv.data
    assert bytes(example_dir, "utf8") in rv.data
    assert bytes("Genre: MousikóFídi Test Genre", "utf8") in rv.data
    assert bytes('"MousikóFídi Test MP4"', "utf8") in rv.data
    assert bytes('"MousikóFídi Test Album"', "utf8") in rv.data


@@ 2518,8 2513,8 @@ def test_file_detail_real_mp4(client):
    assert bytes("MousikóFídi Test Artist", "utf8") in rv.data
    assert bytes("MousikóFídi Test MP4", "utf8") in rv.data
    assert bytes(
        '<img data-stat="off" id="cover-art" src="/serve/{}%2Fexample%2FMousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            escaped
        '<img data-stat="off" id="cover-art" src="/serve/{}/example/MousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            example_dir.strip("/")
        ),
        "utf8",
    )


@@ 2527,9 2522,7 @@ def test_file_detail_real_mp4(client):
    assert b"Track: 34/100" in rv.data
    assert (
        bytes(
            '<source src="/serve/{}" type="video/mp4">'.format(
                quote(example_dir_unescaped)
            ),
            '<source src="/serve/{}" type="video/mp4">'.format(example_dir.strip("/")),
            "utf8",
        )
        in rv.data


@@ 2537,7 2530,7 @@ def test_file_detail_real_mp4(client):
    assert (
        bytes(
            '<form class="center" action="/playlist/add/{}" method="post">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2588,12 2581,11 @@ def test_file_detail_real_mp4(client):


def test_file_detail_real_ogg(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example", "real.ogg")
    escaped = quote(example_dir_unescaped)
    example_dir = os.path.join(THIS_DIR, "example", "real.ogg")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    dir_list.append(example_dir)
    site_name = "COOL TEST SITE"
    url = "/browse" + example_dir_unescaped
    url = "/browse" + example_dir
    app.fidiConfig["config"]["favicon_path"] = "/cool-favicon.png"
    app.fidiConfig["config"]["music_dirs"] = dir_list
    app.fidiConfig["config"]["site_name"] = site_name


@@ 2603,8 2595,7 @@ def test_file_detail_real_ogg(client):
    # Reset the config
    app.fidiConfig = app._fidiConfig.copy()
    assert rv.status == "200 OK"
    assert bytes(escaped, "utf8") in rv.data
    assert bytes(example_dir_unescaped, "utf8") in rv.data
    assert bytes(example_dir, "utf8") in rv.data
    assert b"Track: 34" in rv.data
    assert b"Released: 2019-08-08" in rv.data
    assert bytes("Genre: MousikóFídi Test", "utf8") in rv.data


@@ 2612,15 2603,15 @@ def test_file_detail_real_ogg(client):
    assert bytes("MousikóFídi Test Album", "utf8") in rv.data
    assert bytes("MousikóFídi Test OGG", "utf8") in rv.data
    assert bytes(
        '<img data-stat="off" id="cover-art" src="/serve/{}%2Fexample%2FMousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            escaped
        '<img data-stat="off" id="cover-art" src="/serve/{}/example/MousikoFidi%20Sample%20Cover%20Art.png" title="The cover art for files in this directory.  Click to adjust size (up to three sizes).">'.format(
            example_dir.strip("/")
        ),
        "utf8",
    )
    assert (
        bytes(
            '<audio id="single" controls src="/serve/{}">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2629,7 2620,7 @@ def test_file_detail_real_ogg(client):
    assert (
        bytes(
            '<form class="center" action="/playlist/add/{}" method="post">'.format(
                quote(example_dir_unescaped)
                example_dir.strip("/")
            ),
            "utf8",
        )


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


@@ 2722,7 2713,7 @@ def test_playlist_with_audio_and_video(client):

    assert (
        bytes(
            '<form  action="/playlist/clear/%2Fall" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/clear/all" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 2736,7 2727,7 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/save/%2Fsave" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/save/save" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 2768,8 2759,8 @@ def test_playlist_with_audio_and_video(client):
    assert bytes('<h4 class="center">Video Tracks</h4>', "utf8") in rv.data
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.flac" data-num="0" data-path="{1}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.flac" data-num="0" data-path="{0}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2777,8 2768,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.flac" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.flac" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2790,8 2781,8 @@ def test_playlist_with_audio_and_video(client):
    assert bytes("MousikóFídi Test Album", "utf8") in rv.data
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.flac" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.flac" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2802,8 2793,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.mp3" data-num="0" data-path="{1}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.mp3" data-num="0" data-path="{0}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2811,8 2802,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.mp3" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.mp3" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2820,8 2811,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.mp3" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.mp3" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2829,8 2820,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Freal.mp4" data-num="0" data-path="{1}/real.mp4" data-title="MousikóFídi Test MP4" id="mousikófíditestmp4" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/real.mp4" data-num="0" data-path="{0}/real.mp4" data-title="MousikóFídi Test MP4" id="mousikófíditestmp4" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2838,8 2829,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Freal.mp4" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/real.mp4" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2847,8 2838,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.mp4" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.mp4" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2856,8 2847,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.ogg" data-num="0" data-path="{1}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.ogg" data-num="0" data-path="{0}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2865,8 2856,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.ogg" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.ogg" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2874,8 2865,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.ogg" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.ogg" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2883,8 2874,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Ffake.mp4" data-num="0" data-path="{1}/fake.mp4" data-title="fake.mp4" id="fakemp4" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/fake.mp4" data-num="0" data-path="{0}/fake.mp4" data-title="fake.mp4" id="fakemp4" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2892,8 2883,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Ffake.mp4" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/fake.mp4" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2901,8 2892,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Ffake.mp4" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/fake.mp4" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2910,8 2901,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Ffake.webm" data-num="0" data-path="{1}/fake.webm" data-title="fake.webm" id="fakewebm" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/fake.webm" data-num="0" data-path="{0}/fake.webm" data-title="fake.webm" id="fakewebm" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2919,8 2910,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Ffake.webm" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/fake.webm" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 2928,8 2919,8 @@ def test_playlist_with_audio_and_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Ffake.webm" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/fake.webm" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3044,7 3035,7 @@ def test_playlist_with_audio_no_video(client):

    assert (
        bytes(
            '<form  action="/playlist/clear/%2Fall" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/clear/all" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 3058,7 3049,7 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/save/%2Fsave" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/save/save" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 3086,11 3077,11 @@ def test_playlist_with_audio_no_video(client):
        )
        in rv.data
    )
    assert bytes('<h4 class="center">Files:</h4>', "utf8") in rv.data
    assert bytes('<h4 class="center">Audio Tracks</h4>', "utf8") in rv.data
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.flac" data-num="0" data-path="{1}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.flac" data-num="0" data-path="{0}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3098,8 3089,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.flac" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.flac" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3111,8 3102,8 @@ def test_playlist_with_audio_no_video(client):
    assert bytes("MousikóFídi Test Album", "utf8") in rv.data
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.flac" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.flac" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3123,8 3114,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.mp3" data-num="0" data-path="{1}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.mp3" data-num="0" data-path="{0}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3132,8 3123,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.mp3" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.mp3" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3141,8 3132,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.mp3" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.mp3" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3150,8 3141,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Freal.mp4" data-num="0" data-path="{1}/real.mp4" data-title="MousikóFídi Test MP4" id="mousikófíditestmp4" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/real.mp4" data-num="0" data-path="{0}/real.mp4" data-title="MousikóFídi Test MP4" id="mousikófíditestmp4" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3159,8 3150,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Freal.mp4">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/real.mp4">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3168,8 3159,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.mp4" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.mp4" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3177,8 3168,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.ogg" data-num="0" data-path="{1}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.ogg" data-num="0" data-path="{0}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3186,8 3177,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.ogg" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.ogg" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3195,8 3186,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.ogg" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.ogg" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3204,8 3195,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Ffake.mp4" data-num="0" data-path="{1}/fake.mp4" data-title="fake.mp4" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/fake.mp4" data-num="0" data-path="{0}/fake.mp4" data-title="fake.mp4" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3213,8 3204,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Ffake.mp4">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/fake.mp4">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3222,8 3213,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Ffake.mp4" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/fake.mp4" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3231,8 3222,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Ffake.webm" data-num="0" data-path="{1}/fake.webm" data-title="fake.webm" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/fake.webm" data-num="0" data-path="{0}/fake.webm" data-title="fake.webm" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3240,8 3231,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Ffake.webm">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/fake.webm">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3249,8 3240,8 @@ def test_playlist_with_audio_no_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Ffake.webm" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/fake.webm" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3366,7 3357,7 @@ def test_playlist_no_audio_with_video(client):

    assert (
        bytes(
            '<form  action="/playlist/clear/%2Fall" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/clear/all" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 3380,7 3371,7 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/save/%2Fsave" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/save/save" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 3413,8 3404,8 @@ def test_playlist_no_audio_with_video(client):
    assert bytes('<h4 class="center">Video Tracks</h4>', "utf8") in rv.data
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.flac" data-num="0" data-path="{1}/real.flac" data-title="MousikóFídi Test Title" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.flac" data-num="0" data-path="{0}/real.flac" data-title="MousikóFídi Test Title" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3422,8 3413,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.flac" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.flac" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3433,8 3424,8 @@ def test_playlist_no_audio_with_video(client):
    assert bytes("MousikóFídi Test Album", "utf8") in rv.data
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.flac" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.flac" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3445,8 3436,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.mp3" data-num="0" data-path="{1}/real.mp3" data-title="MousikóFídi Test Title" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.mp3" data-num="0" data-path="{0}/real.mp3" data-title="MousikóFídi Test Title" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3454,8 3445,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.mp3" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.mp3" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3463,8 3454,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.mp3" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.mp3" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3472,8 3463,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Freal.mp4" data-num="0" data-path="{1}/real.mp4" data-title="MousikóFídi Test MP4" id="mousikófíditestmp4" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/real.mp4" data-num="0" data-path="{0}/real.mp4" data-title="MousikóFídi Test MP4" id="mousikófíditestmp4" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3481,8 3472,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Freal.mp4" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/real.mp4" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3490,8 3481,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.mp4" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.mp4" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3499,8 3490,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{0}%2Freal.ogg" data-num="0" data-path="{1}/real.ogg" data-title="MousikóFídi Test Title" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="play-arrow" data-browse="/browse/{0}/real.ogg" data-num="0" data-path="{0}/real.ogg" data-title="MousikóFídi Test Title" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3508,8 3499,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.ogg" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big" href="/browse/{}/real.ogg" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3517,8 3508,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Freal.ogg" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/real.ogg" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3526,8 3517,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Ffake.mp4" data-num="0" data-path="{1}/fake.mp4" data-title="fake.mp4" id="fakemp4" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/fake.mp4" data-num="0" data-path="{0}/fake.mp4" data-title="fake.mp4" id="fakemp4" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3535,8 3526,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Ffake.mp4" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/fake.mp4" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3544,8 3535,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Ffake.mp4" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/fake.mp4" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3553,8 3544,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<div class="video-arrow" data-browse="/browse/{0}%2Ffake.webm" data-num="0" data-path="{1}/fake.webm" data-title="fake.webm" id="fakewebm" title="Click to play this track."></div>'.format(
                quote(example_dir), example_dir
            '<div class="video-arrow" data-browse="/browse/{0}/fake.webm" data-num="0" data-path="{0}/fake.webm" data-title="fake.webm" id="fakewebm" title="Click to play this track."></div>'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3562,8 3553,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<a class="mobile-big video-title" href="/browse/{}%2Ffake.webm" title="This track\'s title.">'.format(
                quote(example_dir)
            '<a class="mobile-big video-title" href="/browse/{}/fake.webm" title="This track\'s title.">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3571,8 3562,8 @@ def test_playlist_no_audio_with_video(client):
    )
    assert (
        bytes(
            '<form action="/playlist/rm/{}%2Ffake.webm" class="center" method="post">'.format(
                quote(example_dir)
            '<form action="/playlist/rm/{}/fake.webm" class="center" method="post">'.format(
                example_dir.strip("/")
            ),
            "utf8",
        )


@@ 3694,7 3685,6 @@ def test_playlist_detail_real_with_bad(client):
    edir = os.path.join(THIS_DIR, "example")
    mdir = tempfile.mkdtemp()
    pdir = tempfile.mkdtemp()
    qmdir = quote(mdir)

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


@@ 3746,7 3736,7 @@ def test_playlist_detail_real_with_bad(client):
    )
    assert (
        bytes(
            '<form action="/playlist/load/%2Fload-with-redirect" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/load/load-with-redirect" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 3770,8 3760,8 @@ def test_playlist_detail_real_with_bad(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{q}%2Freal.flac" data-num="0" data-path="{u}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                q=qmdir, u=mdir
            '<div class="play-arrow" data-browse="/browse/{0}/real.flac" data-num="0" data-path="{0}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 3779,24 3769,24 @@ def test_playlist_detail_real_with_bad(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.flac" title="This track\'s title.">'.format(
                qmdir
            '<a class="mobile-big" href="/browse/{}/real.flac" title="This track\'s title.">'.format(
                mdir.strip("/")
            ),
            "utf8",
        )
        in rv.data
    )
    assert bytes("\n                  MousikóFídi Test FLAC\n", "utf8") in rv.data
    assert bytes("\n                  MousikóFídi Test MP3\n", "utf8") in rv.data
    assert bytes("\n                  MousikóFídi Test OGG\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test FLAC\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test MP3\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test OGG\n", "utf8") in rv.data
    assert bytes("0:04", "utf8") in rv.data
    assert (
        bytes('<div class="anchor" id="mousikófíditestmp3-target">', "utf8") in rv.data
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{q}%2Freal.mp3" data-num="0" data-path="{u}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                q=qmdir, u=mdir
            '<div class="play-arrow" data-browse="/browse/{0}/real.mp3" data-num="0" data-path="{0}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 3804,22 3794,22 @@ def test_playlist_detail_real_with_bad(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.mp3" title="This track\'s title.">'.format(
                qmdir
            '<a class="mobile-big" href="/browse/{}/real.mp3" title="This track\'s title.">'.format(
                mdir.strip("/")
            ),
            "utf8",
        )
        in rv.data
    )
    assert bytes("\n              MousikóFídi Test Album\n", "utf8") in rv.data
    assert bytes("\n              MousikóFídi Test Artist\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test Album\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test Artist\n", "utf8") in rv.data
    assert (
        bytes('<div class="anchor" id="mousikófíditestogg-target">', "utf8") in rv.data
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{q}%2Freal.ogg" data-num="0" data-path="{u}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                q=qmdir, u=mdir
            '<div class="play-arrow" data-browse="/browse/{0}/real.ogg" data-num="0" data-path="{0}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 3827,8 3817,8 @@ def test_playlist_detail_real_with_bad(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.ogg" title="This track\'s title.">'.format(
                qmdir
            '<a class="mobile-big" href="/browse/{}/real.ogg" title="This track\'s title.">'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 3891,7 3881,6 @@ def test_playlist_detail_real(client):
    edir = os.path.join(THIS_DIR, "example")
    mdir = tempfile.mkdtemp()
    pdir = tempfile.mkdtemp()
    qmdir = quote(mdir)

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


@@ 3939,7 3928,7 @@ def test_playlist_detail_real(client):
    )
    assert (
        bytes(
            '<form action="/playlist/load/%2Fload-with-redirect" class="center" id="playlistctl" method="post">',
            '<form action="/playlist/load/load-with-redirect" class="center" id="playlistctl" method="post">',
            "utf8",
        )
        in rv.data


@@ 3956,8 3945,8 @@ def test_playlist_detail_real(client):
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{q}%2Freal.flac" data-num="0" data-path="{u}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                q=qmdir, u=mdir
            '<div class="play-arrow" data-browse="/browse/{0}/real.flac" data-num="0" data-path="{0}/real.flac" data-title="MousikóFídi Test FLAC" id="mousikófíditestflac" title="Click to play this track."></div>'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 3965,22 3954,22 @@ def test_playlist_detail_real(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.flac" title="This track\'s title.">'.format(
                qmdir
            '<a class="mobile-big" href="/browse/{}/real.flac" title="This track\'s title.">'.format(
                mdir.strip("/")
            ),
            "utf8",
        )
        in rv.data
    )
    assert bytes("\n                  MousikóFídi Test FLAC\n", "utf8") in rv.data
    assert bytes("    MousikóFídi Test FLAC\n", "utf8") in rv.data
    assert bytes("0:04", "utf8") in rv.data
    assert (
        bytes('<div class="anchor" id="mousikófíditestmp3-target">', "utf8") in rv.data
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{q}%2Freal.mp3" data-num="0" data-path="{u}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                q=qmdir, u=mdir
            '<div class="play-arrow" data-browse="/browse/{0}/real.mp3" data-num="0" data-path="{0}/real.mp3" data-title="MousikóFídi Test MP3" id="mousikófíditestmp3" title="Click to play this track."></div>'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 3988,24 3977,24 @@ def test_playlist_detail_real(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.mp3" title="This track\'s title.">'.format(
                qmdir
            '<a class="mobile-big" href="/browse/{}/real.mp3" title="This track\'s title.">'.format(
                mdir.strip("/")
            ),
            "utf8",
        )
        in rv.data
    )
    assert bytes("\n                  MousikóFídi Test MP3\n", "utf8") in rv.data
    assert bytes("\n                  MousikóFídi Test OGG\n", "utf8") in rv.data
    assert bytes("\n              MousikóFídi Test Album\n", "utf8") in rv.data
    assert bytes("\n              MousikóFídi Test Artist\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test MP3\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test OGG\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test Album\n", "utf8") in rv.data
    assert bytes("MousikóFídi Test Artist\n", "utf8") in rv.data
    assert (
        bytes('<div class="anchor" id="mousikófíditestogg-target">', "utf8") in rv.data
    )
    assert (
        bytes(
            '<div class="play-arrow" data-browse="/browse/{q}%2Freal.ogg" data-num="0" data-path="{u}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                q=qmdir, u=mdir
            '<div class="play-arrow" data-browse="/browse/{0}/real.ogg" data-num="0" data-path="{0}/real.ogg" data-title="MousikóFídi Test OGG" id="mousikófíditestogg" title="Click to play this track."></div>'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 4013,8 4002,8 @@ def test_playlist_detail_real(client):
    )
    assert (
        bytes(
            '<a class="mobile-big" href="/browse/{}%2Freal.ogg" title="This track\'s title.">'.format(
                qmdir
            '<a class="mobile-big" href="/browse/{}/real.ogg" title="This track\'s title.">'.format(
                mdir.strip("/")
            ),
            "utf8",
        )


@@ 4203,10 4192,10 @@ def test_playlists_yes_playlists(client):


def test_serve_file(client):
    example_dir_unescaped = os.path.join(THIS_DIR, "example")
    example_dir = os.path.join(THIS_DIR, "example")
    dir_list = []
    dir_list.append(example_dir_unescaped)
    file_path = os.path.join(example_dir_unescaped, "real.flac")
    dir_list.append(example_dir)
    file_path = os.path.join(example_dir, "real.flac")
    url = "/browse" + file_path

    app.fidiConfig["config"]["music_dirs"] = dir_list


@@ 4225,9 4214,6 @@ def test_settings(client):
    with app.app_context():
        rv = client.get(url)

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

    assert rv.status == "200 OK"
    assert (
        bytes(