diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..fde1e57
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,61 @@
+default_language_version:
+ python: python3.12
+repos:
+ # Apply a consistent format to pyproject.toml files.
+ # https://pyproject-fmt.readthedocs.io/en/latest/
+ - repo: https://github.com/tox-dev/pyproject-fmt
+ rev: "1.4.0"
+ hooks:
+ - id: pyproject-fmt
+
+ - repo: https://github.com/asottile/add-trailing-comma
+ rev: v3.1.0
+ hooks:
+ - id: add-trailing-comma
+
+ # Some out-of-the-box hooks for pre-commit.
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.5.0
+ hooks:
+ - id: check-ast
+ - id: check-builtin-literals
+ - id: check-docstring-first
+ - id: check-executables-have-shebangs
+ - id: check-merge-conflict
+ - id: check-toml
+ - id: check-vcs-permalinks
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ - id: name-tests-test
+ args: [--pytest-test-first]
+ - id: trailing-whitespace
+ args: [--markdown-linebreak-ext=md]
+ exclude_types:
+ - "html"
+
+ # Run Pyupgrade on all Python files. This will upgrade the code to Python 3.12.
+ - repo: https://github.com/asottile/pyupgrade
+ rev: v3.15.0
+ hooks:
+ - id: pyupgrade
+ args: ["--py312-plus"]
+
+ # An extremely fast Python linter and formatter.
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.1.3
+ hooks:
+ - id: ruff-format
+ - id: ruff
+ args: ["--fix", "--exit-non-zero-on-fix"]
+
+ # Static checker for GitHub Actions workflow files.
+ - repo: https://github.com/rhysd/actionlint
+ rev: v1.6.26
+ hooks:
+ - id: actionlint
+
+ # Optimize .png files.
+ - repo: https://github.com/shssoichiro/oxipng
+ rev: v9.0.0
+ hooks:
+ - id: oxipng
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 0d6942b..80704f8 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,7 +1,4 @@
{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
@@ -17,4 +14,4 @@
"justMyCode": true
}
]
-}
\ No newline at end of file
+}
diff --git a/Dockerfile b/Dockerfile
index f1cb019..bf68648 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,7 +16,7 @@ ENV PYTHONUNBUFFERED 1
# Update the system and install curl, it is needed for downloading Poetry.
RUN apt-get update && apt-get install curl ffmpeg -y --no-install-recommends
-# 1. Create user so we don't run as root
+# 1. Create user so we don't run as root
# 2. Create directories that the bot needs that are owned by the user.
# /Uploads is used to store the uploaded files.
# /home/botuser/discord-embed is where the Python code is stored.
@@ -45,4 +45,4 @@ VOLUME ["/Uploads"]
# Run the server on all interfaces and on port 5000.
# You should run a reverse proxy like nginx infront of this.
EXPOSE 5000
-CMD ["poetry", "run", "uvicorn", "discord_embed.main:app", "--host", "0.0.0.0", "--port", "5000"]
\ No newline at end of file
+CMD ["poetry", "run", "uvicorn", "discord_embed.main:app", "--host", "0.0.0.0", "--port", "5000"]
diff --git a/discord_embed/__init__.py b/discord_embed/__init__.py
index 5becc17..e69de29 100644
--- a/discord_embed/__init__.py
+++ b/discord_embed/__init__.py
@@ -1 +0,0 @@
-__version__ = "1.0.0"
diff --git a/discord_embed/generate_html.py b/discord_embed/generate_html.py
index 836a7e1..0fafba2 100644
--- a/discord_embed/generate_html.py
+++ b/discord_embed/generate_html.py
@@ -1,11 +1,19 @@
-import os
-from datetime import datetime
+from __future__ import annotations
+
+import datetime
+from pathlib import Path
from urllib.parse import urljoin
from discord_embed import settings
-def generate_html_for_videos(url: str, width: int, height: int, screenshot: str, filename: str) -> str:
+def generate_html_for_videos(
+ url: str,
+ width: int,
+ height: int,
+ screenshot: str,
+ filename: str,
+) -> str:
"""Generate HTML for video files.
Args:
@@ -18,10 +26,13 @@ def generate_html_for_videos(url: str, width: int, height: int, screenshot: str,
Returns:
Returns HTML for video.
"""
+ time_now: datetime.datetime = datetime.datetime.now(tz=datetime.UTC)
+ time_now_str: str = time_now.strftime("%Y-%m-%d %H:%M:%S %Z")
+
video_html: str = f"""
-
+
@@ -39,8 +50,8 @@ def generate_html_for_videos(url: str, width: int, height: int, screenshot: str,
# Take the filename and append .html to it.
filename += ".html"
- file_path: str = os.path.join(settings.upload_folder, filename)
- with open(file_path, "w", encoding="utf-8") as f:
+ file_path = Path(settings.upload_folder, filename)
+ with Path.open(file_path, "w", encoding="utf-8") as f:
f.write(video_html)
return html_url
diff --git a/discord_embed/main.py b/discord_embed/main.py
index 46bf54b..6aa534a 100644
--- a/discord_embed/main.py
+++ b/discord_embed/main.py
@@ -1,8 +1,10 @@
+from __future__ import annotations
+
+from pathlib import Path
from urllib.parse import urljoin
from fastapi import FastAPI, File, Request, UploadFile
from fastapi.responses import HTMLResponse, JSONResponse
-from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from discord_embed import settings
@@ -17,12 +19,11 @@ app: FastAPI = FastAPI(
},
)
-app.mount("/static", StaticFiles(directory="static"), name="static")
templates: Jinja2Templates = Jinja2Templates(directory="templates")
@app.post("/uploadfiles/", description="Where to send a POST request to upload files.")
-async def upload_file(file: UploadFile = File()):
+async def upload_file(file: UploadFile = File()): # noqa: B008
"""Page for uploading files.
If it is a video, we need to make an HTML file, and a thumbnail
@@ -35,15 +36,22 @@ async def upload_file(file: UploadFile = File()):
Returns:
Returns a dict with the filename, or a link to the .html if it was a video.
"""
+ if file.filename is None:
+ send_webhook("Filename is None")
+ return JSONResponse(content={"error": "Filename is None"}, status_code=500)
+ if file.content_type is None:
+ send_webhook("Content type is None")
+ return JSONResponse(content={"error": "Content type is None"}, status_code=500)
+
if file.content_type.startswith("video/"):
html_url: str = await do_things(file)
else:
filename: str = await remove_illegal_chars(file.filename)
- with open(f"{settings.upload_folder}/{filename}", "wb+") as f:
+ with Path.open(Path(settings.upload_folder, filename), "wb+") as f:
f.write(file.file.read())
- html_url: str = urljoin(settings.serve_domain, filename) # type: ignore
+ html_url: str = urljoin(settings.serve_domain, filename)
send_webhook(f"{html_url} was uploaded.")
return JSONResponse(content={"html_url": html_url})
@@ -58,8 +66,7 @@ async def remove_illegal_chars(file_name: str) -> str:
Returns:
Returns a string with the filename without illegal characters.
"""
-
- filename: str = file_name.replace(" ", ".") # type: ignore
+ filename: str = file_name.replace(" ", ".")
illegal_characters: list[str] = [
"*",
'"',
@@ -84,13 +91,31 @@ async def remove_illegal_chars(file_name: str) -> str:
",",
]
for character in illegal_characters:
- filename: str = filename.replace(character, "") # type: ignore
+ filename: str = filename.replace(character, "")
return filename
+index_html: str = """
+
+
+
+ discord-nice-embed
+ Swagger UI - API documentation
+
+ ReDoc - Alternative API documentation
+
+
+
+
+"""
+
+
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
-async def main(request: Request):
+async def main(request: Request): # noqa: ARG001
"""Our index view.
You can upload files here.
@@ -99,7 +124,6 @@ async def main(request: Request):
request: Our request.
Returns:
- TemplateResponse: Returns HTML for site.
+ HTMLResponse: Our index.html page.
"""
-
- return templates.TemplateResponse("index.html", {"request": request})
+ return index_html
diff --git a/discord_embed/settings.py b/discord_embed/settings.py
index 8f603e7..105eda3 100644
--- a/discord_embed/settings.py
+++ b/discord_embed/settings.py
@@ -1,37 +1,14 @@
-import os
-import pathlib
-import sys
+from __future__ import annotations
-from dotenv import load_dotenv
+import os
+from pathlib import Path
+
+from dotenv import find_dotenv, load_dotenv
# Load environment variables
-load_dotenv()
+load_dotenv(find_dotenv(), verbose=True)
-# Check if user has added a domain to the environment.
-try:
- serve_domain: str = os.environ["SERVE_DOMAIN"]
-except KeyError:
- sys.exit("discord-embed: Environment variable 'SERVE_DOMAIN' is missing!")
-
-# Remove trailing slash from domain
-if serve_domain.endswith("/"):
- serve_domain = serve_domain[:-1]
-
-# Check if we have a folder for uploads.
-try:
- upload_folder: str = os.environ["UPLOAD_FOLDER"]
-except KeyError:
- sys.exit("Environment variable 'UPLOAD_FOLDER' is missing!")
-
-# Create upload_folder if it doesn't exist.
-pathlib.Path(upload_folder).mkdir(parents=True, exist_ok=True)
-
-# Remove trailing slash from upload_folder
-if upload_folder.endswith("/"):
- upload_folder = upload_folder[:-1]
-
-# Discord webhook URL
-try:
- webhook_url: str = os.environ["WEBHOOK_URL"]
-except KeyError:
- sys.exit("Environment variable 'WEBHOOK_URL' is missing!")
+webhook_url: str = os.environ["WEBHOOK_URL"]
+serve_domain: str = os.environ["SERVE_DOMAIN"].removesuffix("/")
+upload_folder: str = os.environ["UPLOAD_FOLDER"].removesuffix("/")
+Path(upload_folder).mkdir(parents=True, exist_ok=True)
diff --git a/discord_embed/video.py b/discord_embed/video.py
index 6386a00..35b32f4 100644
--- a/discord_embed/video.py
+++ b/discord_embed/video.py
@@ -1,3 +1,6 @@
+from __future__ import annotations
+
+import logging
import sys
from dataclasses import dataclass
@@ -5,6 +8,8 @@ import ffmpeg
from discord_embed import settings
+logger: logging.Logger = logging.getLogger(__name__)
+
@dataclass
class Resolution:
@@ -28,9 +33,12 @@ def video_resolution(path_to_video: str) -> Resolution:
Returns height and width.
"""
probe = ffmpeg.probe(path_to_video)
- video_stream = next((stream for stream in probe["streams"] if stream["codec_type"] == "video"), None)
+ video_stream = next(
+ (stream for stream in probe["streams"] if stream["codec_type"] == "video"),
+ None,
+ )
if video_stream is None:
- print("No video stream found", file=sys.stderr)
+ logger.critical("No video stream found")
sys.exit(1)
width: int = int(video_stream["width"])
diff --git a/discord_embed/video_file_upload.py b/discord_embed/video_file_upload.py
index c498822..b2bf8a2 100644
--- a/discord_embed/video_file_upload.py
+++ b/discord_embed/video_file_upload.py
@@ -1,13 +1,16 @@
-import os
+from __future__ import annotations
+
from dataclasses import dataclass
from pathlib import Path
-
-from fastapi import UploadFile
+from typing import TYPE_CHECKING
from discord_embed import settings
from discord_embed.generate_html import generate_html_for_videos
from discord_embed.video import Resolution, make_thumbnail, video_resolution
+if TYPE_CHECKING:
+ from fastapi import UploadFile
+
@dataclass
class VideoFile:
@@ -32,19 +35,23 @@ def save_to_disk(file: UploadFile) -> VideoFile:
Returns:
VideoFile object with the filename and location.
"""
+ if file.filename is None:
+ msg = "Filename is None"
+ raise ValueError(msg)
+
# Create the folder where we should save the files
- folder_video: str = os.path.join(settings.upload_folder, "video")
- Path(folder_video).mkdir(parents=True, exist_ok=True)
+ save_folder_video = Path(settings.upload_folder, "video")
+ Path(save_folder_video).mkdir(parents=True, exist_ok=True)
# Replace spaces with dots in the filename.
filename: str = file.filename.replace(" ", ".")
# Save the uploaded file to disk.
- file_location: str = os.path.join(folder_video, filename)
- with open(file_location, "wb+") as f:
+ file_location = Path(save_folder_video, filename)
+ with Path.open(file_location, "wb+") as f:
f.write(file.file.read())
- return VideoFile(filename, file_location)
+ return VideoFile(filename, str(file_location))
async def do_things(file: UploadFile) -> str:
@@ -56,7 +63,6 @@ async def do_things(file: UploadFile) -> str:
Returns:
Returns URL for video.
"""
-
video_file: VideoFile = save_to_disk(file)
file_url: str = f"{settings.serve_domain}/video/{video_file.filename}"
diff --git a/discord_embed/webhook.py b/discord_embed/webhook.py
index f586f08..1c85cd6 100644
--- a/discord_embed/webhook.py
+++ b/discord_embed/webhook.py
@@ -1,7 +1,17 @@
+from __future__ import annotations
+
+import logging
+from typing import TYPE_CHECKING
+
from discord_webhook import DiscordWebhook
from discord_embed import settings
+if TYPE_CHECKING:
+ from requests import Response
+
+logger: logging.Logger = logging.getLogger(__name__)
+
def send_webhook(message: str) -> None:
"""Send a webhook to Discord.
@@ -11,7 +21,9 @@ def send_webhook(message: str) -> None:
"""
webhook: DiscordWebhook = DiscordWebhook(
url=settings.webhook_url,
- content=message,
+ content=message or "discord-nice-embed: No message was provided.",
rate_limit_retry=True,
)
- webhook.execute()
+ response: Response = webhook.execute()
+ if not response.ok:
+ logger.critical("Webhook failed to send\n %s\n %s", response, message)
diff --git a/embed.subdomain.conf b/embed.subdomain.conf
index 5d67e56..04258ba 100644
--- a/embed.subdomain.conf
+++ b/embed.subdomain.conf
@@ -17,4 +17,4 @@ server {
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
-}
\ No newline at end of file
+}
diff --git a/poetry.lock b/poetry.lock
index 0eaf2ff..9ff4aca 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -42,6 +42,17 @@ files = [
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
]
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+description = "Validate configuration and produce human readable error messages."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
+]
+
[[package]]
name = "charset-normalizer"
version = "3.3.2"
@@ -183,6 +194,17 @@ requests = ">=2.28.1,<3.0.0"
[package.extras]
async = ["httpx (>=0.23.0,<0.24.0)"]
+[[package]]
+name = "distlib"
+version = "0.3.7"
+description = "Distribution utilities"
+optional = false
+python-versions = "*"
+files = [
+ {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"},
+ {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"},
+]
+
[[package]]
name = "fastapi"
version = "0.104.1"
@@ -220,6 +242,22 @@ future = "*"
[package.extras]
dev = ["Sphinx (==2.1.0)", "future (==0.17.1)", "numpy (==1.16.4)", "pytest (==4.6.1)", "pytest-mock (==1.10.4)", "tox (==3.12.1)"]
+[[package]]
+name = "filelock"
+version = "3.13.1"
+description = "A platform independent file lock."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
+ {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
+typing = ["typing-extensions (>=4.8)"]
+
[[package]]
name = "future"
version = "0.18.3"
@@ -333,6 +371,20 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
+[[package]]
+name = "identify"
+version = "2.5.31"
+description = "File identification library for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"},
+ {file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"},
+]
+
+[package.extras]
+license = ["ukkonen"]
+
[[package]]
name = "idna"
version = "3.4"
@@ -441,6 +493,20 @@ files = [
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
]
+[[package]]
+name = "nodeenv"
+version = "1.8.0"
+description = "Node.js virtual environment builder"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
+files = [
+ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
+ {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
+]
+
+[package.dependencies]
+setuptools = "*"
+
[[package]]
name = "packaging"
version = "23.2"
@@ -452,6 +518,21 @@ files = [
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
+[[package]]
+name = "platformdirs"
+version = "3.11.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"},
+ {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+
[[package]]
name = "pluggy"
version = "1.3.0"
@@ -467,6 +548,24 @@ files = [
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
+[[package]]
+name = "pre-commit"
+version = "3.5.0"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"},
+ {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"},
+]
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+virtualenv = ">=20.10.0"
+
[[package]]
name = "pydantic"
version = "2.4.2"
@@ -732,6 +831,22 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+[[package]]
+name = "setuptools"
+version = "68.2.2"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
+ {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
[[package]]
name = "sniffio"
version = "1.3.0"
@@ -857,6 +972,26 @@ files = [
docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"]
+[[package]]
+name = "virtualenv"
+version = "20.24.6"
+description = "Virtual Python Environment builder"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"},
+ {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"},
+]
+
+[package.dependencies]
+distlib = ">=0.3.7,<1"
+filelock = ">=3.12.2,<4"
+platformdirs = ">=3.9.1,<4"
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
+
[[package]]
name = "watchfiles"
version = "0.21.0"
@@ -1028,4 +1163,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "3eb51d6b10b9c56043cfeaf3cffbe0f30f526ff774b2c4bbd7672f5e3a0d6b03"
+content-hash = "939a371df3b2147c6a306b152b4f7011fc0663defba72c79085f705b0eaeef9c"
diff --git a/pyproject.toml b/pyproject.toml
index 23fa87d..9af91c3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -18,16 +18,20 @@ uvicorn = { extras = ["standard"], version = "^0.23.0" }
[tool.poetry.group.dev.dependencies]
httpx = "^0.25.0"
pytest = "^7.4.3"
+pre-commit = "^3.5.0"
[build-system]
-requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
+requires = [
+ "poetry-core>=1",
+]
[tool.ruff]
fix = true
+unsafe-fixes = true
preview = true
select = ["ALL"]
-ignore = ["D100", "CPY001"]
+ignore = ["D100", "D104", "CPY001", "ANN201"]
[tool.ruff.pydocstyle]
convention = "google"
diff --git a/static/style.css b/static/style.css
deleted file mode 100644
index 948a07f..0000000
--- a/static/style.css
+++ /dev/null
@@ -1,12 +0,0 @@
-body {
- background-color: #111111;
- color: #DCDDDE;
-}
-
-a:link {
- color: #00AFF4;
-}
-
-a:visited {
- color: #00AFF4;
-}
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 9869f1e..8b3b5e7 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,21 +1,14 @@
-
-
- discord-nice-embed
-
-
-
-
+
-discord-nice-embed
-Swagger UI - API documentation
-
-ReDoc - Alternative API documentation
-
-
-
+ discord-nice-embed
+ Swagger UI - API documentation
+
+ ReDoc - Alternative API documentation
+
+
diff --git a/tests/test_discord_embed.py b/tests/test_discord_embed.py
index d3749c3..14dc3a3 100644
--- a/tests/test_discord_embed.py
+++ b/tests/test_discord_embed.py
@@ -1,68 +1,44 @@
-import os
+from __future__ import annotations
+
+import os
+from pathlib import Path
+from typing import TYPE_CHECKING
-from fastapi import Response
from fastapi.testclient import TestClient
-from discord_embed import __version__, settings
+from discord_embed import settings
from discord_embed.main import app
+if TYPE_CHECKING:
+ import httpx
+
client: TestClient = TestClient(app)
TEST_FILE: str = "tests/test.mp4"
-def test_version() -> None:
- """Test version is correct."""
- assert __version__ == "1.0.0"
-
-
def test_domain_ends_with_slash() -> None:
"""Test domain ends with a slash."""
assert not settings.serve_domain.endswith("/")
-def test_save_to_disk() -> None:
- """Test save_to_disk() works."""
- # TODO: Implement this test. I need to mock the UploadFile object.
-
-
-def test_do_things() -> None:
- """Test do_things() works."""
- # TODO: Implement this test. I need to mock the UploadFile object.
-
-
def test_main() -> None:
"""Test main() works."""
- data_without_trailing_nl = ""
- response: Response = client.get("/")
-
- # Check if response is our HTML.
- with open("templates/index.html", encoding="utf8") as our_html:
- data: str = our_html.read()
-
- # index.html has a trailing newline that we need to remove.
- if data[-1:] == "\n":
- data_without_trailing_nl: str = data[:-1] # type: ignore
-
- assert response.status_code == 200
- assert response.text == data_without_trailing_nl
+ response: httpx.Response = client.get("/")
+ assert response.is_success
def test_upload_file() -> None:
"""Test if we can upload files."""
- domain = os.environ["SERVE_DOMAIN"]
-
- # Remove trailing slash from domain
- if domain.endswith("/"):
- domain: str = domain[:-1] # type: ignore
+ domain = os.environ["SERVE_DOMAIN"].removesuffix("/")
# Upload our video file and check if it returns the html_url.
- with open(TEST_FILE, "rb") as uploaded_file:
- response: Response = client.post(
+ with Path.open(Path(TEST_FILE), "rb") as uploaded_file:
+ response: httpx.Response = client.post(
url="/uploadfiles/",
files={"file": uploaded_file},
)
returned_json = response.json()
html_url: str = returned_json["html_url"]
- assert response.status_code == 200
+ assert response.is_success
assert html_url == f"{domain}/test.mp4"
diff --git a/tests/test_generate_html.py b/tests/test_generate_html.py
index 6e37a12..0ed2ecd 100644
--- a/tests/test_generate_html.py
+++ b/tests/test_generate_html.py
@@ -1,4 +1,7 @@
+from __future__ import annotations
+
import os
+from pathlib import Path
from discord_embed.generate_html import generate_html_for_videos
@@ -12,8 +15,8 @@ def test_generate_html_for_videos() -> None:
domain = domain[:-1]
# Delete the old HTML file if it exists
- if os.path.exists("Uploads/test_video.mp4.html"):
- os.remove("Uploads/test_video.mp4.html")
+ if Path.exists(Path("Uploads/test_video.mp4.html")):
+ Path.unlink(Path("Uploads/test_video.mp4.html"))
generated_html: str = generate_html_for_videos(
url="https://www.youtube.com/watch?v=dQw4w9WgXcQ",
@@ -23,58 +26,3 @@ def test_generate_html_for_videos() -> None:
filename="test_video.mp4",
)
assert generated_html == f"{domain}/test_video.mp4"
-
- # Open the generated HTML and check if it contains the correct URL, width, height, and screenshot.
-
- with open("Uploads/test_video.mp4.html", "r") as generated_html_file:
- generated_html_lines: list[str] = generated_html_file.readlines()
- """
-
-
-
-
-
-
-
-
-
-
-
-
-
- """
-
- for line, html in enumerate(generated_html_lines):
- # Strip spaces and newlines
- stripped_html: str = html.strip()
-
- rick: str = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
-
- # Check each line
- if line == 1:
- assert stripped_html == ""
- elif line == 2:
- assert stripped_html == ""
- elif line == 3:
- assert stripped_html.startswith("