all repos — FixYouTube-legacy @ f947f56687b7c6f4280a9e90832d1ff4874d1973

A better way to embed YouTube videos everywhere (inspired by FixTweet).

more polish, add tests
Marco Andronaco andronacomarco@gmail.com
Sat, 02 Sep 2023 01:32:14 +0200
commit

f947f56687b7c6f4280a9e90832d1ff4874d1973

parent

2ea1ba90fb06ebe7f2a993e27e9320f149ccc4b1

A .vscode/settings.json

@@ -0,0 +1,7 @@

+{ + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +}
M README.mdREADME.md

@@ -31,6 +31,12 @@ poetry install

poetry run flask --app fixyoutube run --port 1111 --debug ``` +### Tests +``` +poetry install --with test +poetry run pytest +``` + ### Production ``` poetry install --with prod
M fixyoutube/constants.pyfixyoutube/constants.py

@@ -9,3 +9,8 @@ PROXY_HEADERS = { "Content-Type": "video/mp4" }

YTDL_OPTS = { "format": f"best[ext=mp4][filesize<?{ MAX_SIZE_MB }M][filesize_approx<?{ MAX_SIZE_MB }M][protocol^=http][protocol!*=dash] / (bv*+ba/b)" } YTDL_KEYS = [ "id", "title", "description", "uploader", "duration", "height", "width", "url" ] URL_KEY = YTDL_KEYS[-1] + +# test stuff +TELEGRAM_USER_AGENT = "TelegramBot (like TwitterBot)" +SHORT_VIDEO_ID = "crF2AIDlo54" +LONG_VIDEO_ID = "OkdMvr19V0o"
M fixyoutube/views.pyfixyoutube/views.py

@@ -1,15 +1,11 @@

from fixyoutube import app from fixyoutube.db import get_video_from_cache, get_info, clear_cache import fixyoutube.constants as c - from flask import request, redirect, abort, render_template, Response from requests import get import re def main_handler(request, video_id): - if video_id == "": - return render_template("index.html", repo_url=c.REPO_URL) - user_agent = request.headers.get("User-Agent", "") result = re.findall(c.UA_REGEX, user_agent, flags=re.I) if len(result) == 0:

@@ -20,6 +16,10 @@ if info is None:

return abort(400) return render_template("base.html", info=info, base_url=c.BASE_URL) + +@app.route("/") +def index_route(): + return render_template("index.html", repo_url=c.REPO_URL) @app.route("/clear") def clear_route():

@@ -28,18 +28,21 @@ return { "done": True }

@app.route("/watch") def watch_route(): - video = request.args.get('v', '') - return main_handler(request, video) + try: + video_id = request.args["v"] + if video_id == "": + raise KeyError + except KeyError: + return redirect("/") + return main_handler(request, video_id) -@app.route('/', defaults={'video': ''}) -@app.route('/<path:video>') -def main_route(video): - return main_handler(request, video) +@app.route('/<video_id>') +def main_route(video_id): + return main_handler(request, video_id) -@app.route('/proxy/', defaults={'path': ''}) -@app.route('/proxy/<path:path>') -def proxy(path): - result = get_video_from_cache(path) +@app.route('/proxy/<video_id>') +def proxy(video_id): + result = get_video_from_cache(video_id) try: if result.url == "":
M poetry.lockpoetry.lock

@@ -1,4 +1,4 @@

-# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "blinker"

@@ -374,6 +374,17 @@ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},

] [[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] name = "itsdangerous" version = "2.1.2" description = "Safely pass data to untrusted environments and back."

@@ -472,6 +483,17 @@ {file = "mutagen-1.46.0.tar.gz", hash = "sha256:6e5f8ba84836b99fe60be5fb27f84be4ad919bbb6b49caa6ae81e70584b55e58"},

] [[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] name = "peewee" version = "3.16.3" description = "a little orm"

@@ -482,6 +504,21 @@ {file = "peewee-3.16.3.tar.gz", hash = "sha256:12b30e931193bc37b11f7c2ac646e3f67125a8b1a543ad6ab37ad124c8df7d16"},

] [[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] name = "pycparser" version = "2.21" description = "C parser in Python"

@@ -532,6 +569,26 @@ {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196"},

{file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470"}, {file = "pycryptodomex-3.18.0.tar.gz", hash = "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e"}, ] + +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "requests"

@@ -704,4 +761,4 @@

[metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "de50625467a5189bd8d766842d16b45c094c8ed2922522e8f813bb6fb1f1078d" +content-hash = "5392374e3390ea38675cfdda0fe5ac2d3b0b2ae8c290d27a7a3d92644c0fa9ac"
M pyproject.tomlpyproject.toml

@@ -19,6 +19,12 @@

[tool.poetry.group.prod.dependencies] waitress = "^2.1.2" +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +pytest = "^7.4.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api"
A tests/test_flask.py

@@ -0,0 +1,37 @@

+import pytest +import fixyoutube.constants as c +from fixyoutube import app as flask_app +from fixyoutube.db import clear_cache + +@pytest.fixture() +def app(): + flask_app.config.update({ "TESTING": True }) + clear_cache() + yield flask_app + +@pytest.fixture() +def client(app): + return app.test_client() + +def test_homepage(client): + response = client.get("/") + assert b"youtu.be" in response.data + +def test_redirect(client): + response = client.get("/" + c.SHORT_VIDEO_ID) + assert response.location == c.BASE_URL + c.SHORT_VIDEO_ID + +def test_working_video(client): + response = client.get("/" + c.SHORT_VIDEO_ID, headers={'User-Agent': c.TELEGRAM_USER_AGENT}) + print(response.data.decode("utf-8")) + assert b"/proxy/" + c.SHORT_VIDEO_ID.encode("utf-8") in response.data + + response = client.get("/proxy/" + c.SHORT_VIDEO_ID) + assert response.status_code == 200 + +def test_not_working_video(client): + response = client.get("/" + c.LONG_VIDEO_ID, headers={'User-Agent': c.TELEGRAM_USER_AGENT}) + assert b"/proxy/" + c.LONG_VIDEO_ID.encode("utf-8") not in response.data + + response = client.get("/proxy/" + c.LONG_VIDEO_ID) + assert response.status_code == 400