1

I found this snippet of code:

from flask import Flask
import flask
from os import listdir
import os.path
import pathlib
import ntpath


app = Flask(__name__)
@app.route('/', defaults={'req_path': ''})
@app.route('/<path:req_path>')
def dir_listing(req_path):
    BASE_DIR = 'D:/Users/xxx/projects/project1/static'

    # Joining the base and the requested path
    abs_path = os.path.join(BASE_DIR, req_path)

    # Return 404 if path doesn't exist
    if not os.path.exists(abs_path):
        return flask.abort(404)

    # Check if path is a file and serve
    if os.path.isfile(abs_path):
        return flask.send_file(abs_path)

    # Show directory contents
    files = os.listdir(abs_path)
    for i, f in enumerate(files):
        files[i] = os.path.join(req_path, f).replace('\\', '/')
    return flask.render_template('files.html', files=files)


if __name__ == '__main__':
    app.run(host='10.13.0.33', port=80)

along with the corresponding index.html:

<ul>
    {% for file in files %}
        <li><a href="{{ file }}">{{ file }}</a></li>
    {% endfor %}
</ul>

It works as expected until you get to the SecondFolder!
the rendered html looks OK:

<ul>
    <li><a href="FirstFolder/SecondFolder/file3.mp4">FirstFolder/SecondFolder/file3.mp4</a></li>
    <li><a href="FirstFolder/SecondFolder/ThirdFolder">FirstFolder/SecondFolder/ThirdFolder</a></li>
</ul>

BUT, when I hover on a link, I get this (note the first folder is duplicated!): http://10.13.0.33/FirstFolder/FirstFolder/SecondFolder/ThirdFolder

Original Post: I am trying to display links to videos in nested folders. There are many files in each folder (as much as 30) so I don't want to download or play all of them at once. The idea is for the user to see the one they want to view and click the play button.

This is currently being developed/tested on Windows 10 in PyCharm.

The top-level (home) page and next folder deep act as expected. When I select the next depth folder, the video is labeled "Invalid Source"

Is there a limit to the depth?

Here is my code:

from flask import Flask
from os import listdir
import os.path
import pathlib
import ntpath


def remove_top_folder(path):
    path_to_file = pathlib.Path(path)
    path_to_file = path_to_file.relative_to(*path_to_file.parts[:1])
    return str(path_to_file).replace('\\', '/')


def get_folders(folder):
    if folder is not None and os.path.isdir(folder):
        folders = []
        for f in listdir(folder):
            path_to_folder = os.path.join(folder, f).replace('\\', '/')
            if os.path.isdir(path_to_folder) and not f.startswith('.'):
                folders.append(remove_top_folder(path_to_folder))
        return folders
    return None


def get_videos(folder):
    if folder is not None and os.path.isdir(folder):
        videos = []
        for f in listdir(folder):
            path_to_video = os.path.join(folder, f).replace('\\', '/')
            if os.path.isfile(os.path.join(folder, f)) and not f.startswith('.') and f.endswith('.mp4'):
                videos.append(path_to_video)
        return videos
    return None


def get_stuff(folder):
    folders = get_folders(folder)
    videos = get_videos(folder)
    if folder == 'static\\' or folder == 'static/':
        rtn = ''
    else:
        rtn = '<H1>{}</H1>'.format(remove_top_folder(folder))
    if videos is not None:
        for file_name in videos:
            file_name = file_name
            rtn += '''
            <div class="container">
                <video width="320" controls preload="metadata">
                  <source src="{0}" type="video/mp4">
                </video>
            <div class="top">{1}</div>
            </div>
            '''.format(file_name, ntpath.basename(file_name))

    for folder in folders:
        rtn += '''
        <div class="container">
          <a href="{0}">{1}
        </div>
        '''.format(folder, ntpath.basename(folder))
    return rtn


app = Flask(__name__)
@app.route('/', defaults={'u_path': ''})
@app.route('/<path:u_path>')
def catch_all(u_path):
    if u_path is None:
        folder = 'static//'
    elif u_path.endswith('mp4'):
        rtn = '''
        <div class="container">
            <video width="320" controls preload="metadata">
              <source src="{0}" type="video/mp4">
            </video>
        <div class="top">{1}</div>
        </div>
        '''.format(u_path, remove_top_folder(u_path))
        return rtn
    else:
        folder = os.path.join('static', u_path).replace('\\', '/')
    stuff = get_stuff(folder)
    html = '''
        <!doctype html>
        <html>
            <body>
                <h1>{0}</h1>
            </body>
        </html>
    '''.format(stuff)
    return html


if __name__ == '__main__':
    app.run(host='10.13.0.33', port=80)

and I have my folders looking something like this:

static
  file_1.mp4
  First_Folder
    file_2.mp4
    Second_folder
      file_3.mp4

When I visit the web page, I get this:

enter image description here

When I click the FirstFolder link, I get this:

enter image description here

When I click the SecondFolder link, I get this:

enter image description here

jordanthompson
  • 888
  • 1
  • 12
  • 29
  • I don't have an answer to your specific question, but it looks like you are "reinventing the wheel". Flask has convenient ways of [serving static files](https://stackoverflow.com/a/20648053/4032503) as well as using [templates](https://jinja.palletsprojects.com/en/2.11.x/templates/) for your html. – noslenkwah Feb 13 '20 at 22:52
  • I appreciate your help (and if I am missing something, I sincerely apologize) , but I am not trying to download the file, I am trying to display it in the browser. – jordanthompson Feb 14 '20 at 02:25
  • I think there are some misconceptions about how websites work that are beyond the scope of this question. I would peruse [this](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) tutorial. It has way more than what you are trying to do here, but it will help with understanding how a typical wep app functions. – noslenkwah Feb 14 '20 at 14:14
  • Nevertheless, for some reason I cannot serve videos in folders that are nested too deeply. My question still is: is this a flask limitation? – jordanthompson Feb 14 '20 at 16:09
  • To clarify, I actually do NOT want to download the videos when the web page is accessed: I have too many in each folder to start them all playing simultaneously. This is why I am using the preload="metadata" tag in the html. I want the user to look at the image and decide which video to play. – jordanthompson Feb 14 '20 at 16:16

1 Answers1

0

I got it working:

app.py:

import flask
import ntpath
import os
import socket

host_name = socket.gethostname()
if host_name == 'hostname1':
    static_folder = '/some/path/to/videos'
    ip_address = '192.xxx.xxx.xxx'
    port = 80
elif host_name == "hostname2":
    static_folder = '/another/path/to/videos'
    ip_address = '10.xxx.xxx.xxx'
    port = 8000
else:
    raise Exception('hostname is not defined!')


def remove_static_folder(path):
    path_to_file = path.replace(static_folder, '/')
    return path_to_file


def get_folders(folder):
    if folder is not None and os.path.isdir(folder):
        folders = []
        for f in os.listdir(folder):
            path_to_folder = os.path.join(folder, f).replace('\\', '/')
            if os.path.isdir(path_to_folder) and not f.startswith('.'):
                folders.append([remove_static_folder(path_to_folder), ntpath.basename(path_to_folder)])
        return folders
    return []


def get_videos(folder):
    if os.path.isdir(folder):
        print("is dir")
    if folder is not None and os.path.isdir(folder):
        videos = []
        for f in os.listdir(folder):
            if os.path.isfile(os.path.join(folder, f)) and not f.startswith('.') and f.endswith('.mp4'):
                path_to_video = os.path.join(folder, f).replace('\\', '/')
                videos.append([remove_static_folder(path_to_video), ntpath.basename(f)])
        return videos
    return []


app = flask.Flask(__name__, static_folder=static_folder,
                  static_url_path= os.path.join(os.path.sep, os.path.basename(os.path.normpath(static_folder))))


@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    if path is None or path == "":
        folder = static_folder
    else:
        folder = os.path.join(static_folder, path).replace('\\', '/')
    videos = get_videos(folder)
    folders = get_folders(folder)
    folder = remove_static_folder(folder)
    if folder.startswith(".") or folder.startswith('/'):
        folder = folder[1:]
    return flask.render_template("index.html", folder=folder, videos=videos, folders=folders)


if __name__ == '__main__':
    if ip_address is None:
        print("hostname cannot be determined")
        exit(-1)
    app.run(host=ip_address, port=port)

index.html:

<!DOCTYPE html>
<html>

<head>
    <title>{{ folder }}</title>
</head>

<body>
    <div>
        <h1>{{ folder}} </h1>
        <ul>
            {% for video in videos %}
                <div class="container">
                    <video width="640" controls preload="metadata">
                      <source src="{{ url_for('static', filename=video[0]) }}" type="video/mp4">
                    </video>
                    <div class="top">{{ video[1] }}</div>
                </div>
            {% endfor %}
        </ul>
        <ul>
            {% for folder in folders %}
                <a href="{{ folder[0] }}">{{ folder[1] }} <br>
            {% endfor %}
        </ul>
    </div>
</body>

</html>
jordanthompson
  • 888
  • 1
  • 12
  • 29