Unverified Commit fa98572f authored by TheTechRobo's avatar TheTechRobo Committed by GitHub
Browse files

Merge pull request #74 from TheTechRobo/coerce-stream-version

parents b76fa65e 71e30f1d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -5,3 +5,4 @@ nohup.out
static/ign
config.py
config.yml
tmptest.py
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
FROM python:3.11-slim-bullseye
RUN pip install --no-cache-dir gunicorn flask[async]
RUN pip install --no-cache-dir hypercorn quart
RUN apt install -y openssl
RUN apt clean
# The following line does not necessarily have to be updated with the requirements.txt as this is just to speed up the requirements.txt part (to improve cachability)
@@ -9,4 +9,4 @@ EXPOSE 8000
COPY . /app
WORKDIR /app
RUN pip install --upgrade -r REQUIREMENTS.txt
ENTRYPOINT ["gunicorn", "-b", "0.0.0.0:8000", "--config", "gunicorn.conf", "app:app"]
ENTRYPOINT ["hypercorn", "-b", "0.0.0.0:8000", "--config", "file:hypercorn_conf.py", "app:app"]
+6 −3
Original line number Diff line number Diff line
@@ -12,9 +12,9 @@ There are docstrings included in the module (it is contained in `lostmediafinder

## Frontend
### Running in Docker (recommended):
The software is available on Docker Hub: <https://hub.docker.com/r/thetechrobo/findyoutubevideo> Let me know if it doesn't work or if you need help.
There is an included Dockerfile. I will figure out publishing to Docker Hub soon enough.

Instead of modiying the gunicorn config, use `GUNICORN_<VARIABLE_NAME>` environment variables; the config is setup to work with that. For example, `GUNICORN_WORKERS` is the number of threads that are spawned to handle requests.
Instead of modiying the Hypercorn config, use `HYPERCORN_<VARIABLE_NAME>` environment variables; the config is setup to work with that.

A command like this should work (runs on port 8000; change the `-p` flag to `<whatever port you want>:8000` to change that):

@@ -22,6 +22,9 @@ A command like this should work (runs on port 8000; change the `-p` flag to `<wh
docker run --restart=unless-stopped -p 8000:8000 -e GUNICORN_WORKERS=4 thetechrobo/findyoutubevideo
```

### Running outside of Docker (unsupported)
You should be able to check the Dockerfile for what it is doing during the build (it's a glorified shell script).

## Licence

Copyright (c) 2022-2024 TheTechRobo
+1 −0
Original line number Diff line number Diff line
snscrape==0.4.3.20220106
quart
aiohttp[speedups]
requests
switch
+35 −32
Original line number Diff line number Diff line
from flask import Flask, render_template, request, Response, redirect, send_from_directory
import re, urllib.parse, yaml, json
from quart import Quart, jsonify, render_template, request, Response, redirect, send_from_directory, jsonify
import re, yaml, json, typing
import lostmediafinder

app = Flask(__name__)
app = Quart(__name__)

with open('config.yml', 'r') as file:
    config_yml = yaml.safe_load(file)

@app.route("/robots.txt")
async def robots():
    return send_from_directory("static", "robots.txt")
    return await send_from_directory("static", "robots.txt")

@app.route("/find/<id>")
async def youtubev2(id):
@@ -20,16 +20,19 @@ async def youtubev2(id):

async def wrapperYT(id, includeRaw):
    """
    Wrapper for generateAsync
    Wrapper for generate
    """
    return await lostmediafinder.YouTubeResponse.generate(id, includeRaw)

@app.route("/api/v<int:v>/<site>/<id>")
@app.route("/api/v<int:v>/<id>")
async def youtube(v, id, site="youtube", json=True):
async def wrapperYTS(id, includeRaw):
    """
    Wrapper around lostmediafinder
    Wrapper for generateStream
    """
    return await lostmediafinder.YouTubeResponse.generateStream(id, includeRaw)

@app.route("/api/v<int:v>/<site>/<id>")
@app.route("/api/v<int:v>/<id>")
async def youtube(v, id, site="youtube", jsn=True):
    includeRaw = True
    if v == 1:
        return "This API version is no longer supported.", 410
@@ -37,11 +40,23 @@ async def youtube(v, id, site="youtube", json=True):
        return "Unrecognised API version", 404
    if site == "youtube":
        includeRaw = True
        stream = False
        if v >= 4:
            stream = "stream" in request.args
            # Versions 4 and higher only provide `rawraw` if you ask for it
            includeRaw = "includeRaw" in request.args
        if stream:
            async def run():
                r = (await wrapperYTS(id, includeRaw=includeRaw)).coerce_to_api_version(v)
                async for item in r:
                    if type(item) == dict or item is None:
                        yield json.dumps(item) + "\n"
                    else:
                        yield item.json() + "\n"
            return run()
        else:
            r = (await wrapperYT(id, includeRaw=includeRaw)).coerce_to_api_version(v)
        if json:
            if jsn:
                return r.json()
            return r
    return "Unrecognised site", 404
@@ -50,7 +65,7 @@ async def youtube(v, id, site="youtube", json=True):
async def noscript_init():
    if id := request.args.get("d"):
        return redirect("/noscript_load.html?d=" + id)
    return render_template("noscript/init.j2")
    return await render_template("noscript/init.j2")

ID_PATTERN = re.compile(r'^[A-Za-z0-9_-]{10}[AEIMQUYcgkosw048]$')
PATTERNS = [
@@ -86,9 +101,9 @@ async def noscript_load():
        return "No d param provided - It should be the video id or url", 400
    id = coerce_to_id(request.args['d'])
    if not id:
        return render_template("noscript/error.j2", inp=request.args['d']), 400
        return await render_template("noscript/error.j2", inp=request.args['d']), 400
    headers = (("FinUrl", f"/noscript_load_thing.j2?id={id}"),)
    response = Response(render_template("noscript/loading.j2", id=id), headers=headers)
    response = Response(await render_template("noscript/loading.j2", id=id), headers=headers)
    return response, 302

@app.route("/api/coerce_to_id")
@@ -104,20 +119,8 @@ async def coerce_to_id_endpoint():
async def load_thing():
    if not request.args.get("id"):
        return "Missing id parameter", 400
    t = await youtube(3, request.args['id'], "youtube", json=False)
    return render_template("noscript/fid.j2", resp=t)

@app.after_request
async def apply_json_contenttype(response):
    if not request.path.startswith("/api"):
        return response
    try:
        json.loads(response.get_data(True))
    except json.JSONDecodeError:
        # Not JSON
        return response
    response.content_type = "application/json"
    return response
    t = await youtube(3, request.args['id'], "youtube", jsn=False)
    return await render_template("noscript/fid.j2", resp=t)

@app.route("/")
async def index():
@@ -126,7 +129,7 @@ async def index():
    """
    default = request.args.get("q") or ""
    default_id = coerce_to_id(default) or ""
    return render_template("index.j2", default=default, default_id=default_id, methods=get_enabled_methods())
    return await render_template("index.j2", default=default, default_id=default_id, methods=get_enabled_methods())

# The following code should be taken out and shot
def parse_changelog(changelog):
@@ -187,4 +190,4 @@ async def api():
    # Parse the attributes list
    responseDocstring = await parse_lines(rChangelog[0].split("Attributes:\n")[1].strip().split("\n"))
    serviceDocstring  = await parse_lines(sChangelog[0].split("Attributes:\n")[1].strip().split("\n"))
    return render_template("api.j2", fields=responseDocstring, services=serviceDocstring, changelog=changelog)
    return await render_template("api.j2", fields=responseDocstring, services=serviceDocstring, changelog=changelog)
Loading