diff --git a/app/blueprints/thumbnails/__init__.py b/app/blueprints/thumbnails/__init__.py index 43e7e710..379320ce 100644 --- a/app/blueprints/thumbnails/__init__.py +++ b/app/blueprints/thumbnails/__init__.py @@ -14,7 +14,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from flask import abort, send_file, Blueprint, current_app +import re +import requests +from flask import abort, send_file, Blueprint, current_app, request import os from PIL import Image @@ -105,3 +107,26 @@ def make_thumbnail(img, level): res = send_file(cache_filepath) res.headers["Cache-Control"] = "max-age=604800" # 1 week return res + + +@bp.route("/thumbnails/youtube/.jpg") +def youtube(id_: str): + if not re.match(r"^[A-Za-z0-9\-_]+$", id_): + abort(400) + + cache_dir = os.path.join(current_app.config["THUMBNAIL_DIR"], "youtube") + os.makedirs(cache_dir, exist_ok=True) + cache_filepath = os.path.join(cache_dir, id_ + ".jpg") + + url = f"https://img.youtube.com/vi/{id_}/default.jpg" + + response = requests.get(url, stream=True) + if response.status_code != 200: + abort(response.status_code) + + with open(cache_filepath, "wb") as file: + file.write(response.content) + + res = send_file(cache_filepath) + res.headers["Cache-Control"] = "max-age=604800" # 1 week + return res diff --git a/app/models/packages.py b/app/models/packages.py index 60086cdf..b8002d38 100644 --- a/app/models/packages.py +++ b/app/models/packages.py @@ -392,6 +392,19 @@ class Package(db.Model): def donate_url_actual(self): return self.donate_url or self.author.donate_url + @property + def video_thumbnail_url(self): + from app.utils.url import get_youtube_id + + if self.video_url is None: + return None + + id_ = get_youtube_id(self.video_url) + if id_: + return url_for("thumbnails.youtube", id_=id_) + + return None + enable_game_support_detection = db.Column(db.Boolean, nullable=False, default=True) translations = db.relationship("PackageTranslation", back_populates="package", diff --git a/app/scss/gallery.scss b/app/scss/gallery.scss index 66e00119..189ec565 100644 --- a/app/scss/gallery.scss +++ b/app/scss/gallery.scss @@ -52,10 +52,21 @@ justify-content: center !important; cursor: pointer; + img { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + object-fit: cover; + z-index: 10; + } + .fa-play { display: block; font-size: 200%; color: #f44; + z-index: 20; } &:hover { @@ -72,6 +83,7 @@ right: 0.5rem; color: #555; font-size: 80%; + z-index: 30; } } diff --git a/app/templates/base.html b/app/templates/base.html index befa8e43..7a1bc0bf 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -16,7 +16,7 @@ {%- endif %} - + {% if noindex -%} diff --git a/app/templates/packages/view.html b/app/templates/packages/view.html index 3fb4ad65..ee905d9c 100644 --- a/app/templates/packages/view.html +++ b/app/templates/packages/view.html @@ -292,6 +292,10 @@ {% if package.video_url %}
  • + {% set thumbnail_url = package.video_thumbnail_url %} + {% if thumbnail_url %} + {{ _('Thumbnail for video') }} + {% endif %}
    diff --git a/app/utils/url.py b/app/utils/url.py index 1114332c..dab39cca 100644 --- a/app/utils/url.py +++ b/app/utils/url.py @@ -31,16 +31,22 @@ def url_get_query(parsed_url: urlparse.ParseResult) -> Dict[str, List[str]]: return urlparse.parse_qs(parsed_url.query) -def clean_youtube_url(url: str) -> Optional[str]: +def get_youtube_id(url: str) -> Optional[str]: parsed = urlparse.urlparse(url) - print(parsed) if (parsed.netloc == "www.youtube.com" or parsed.netloc == "youtube.com") and parsed.path == "/watch": - print(url_get_query(parsed)) video_id = url_get_query(parsed).get("v", [None])[0] if video_id: - return url_set_query("https://www.youtube.com/watch", {"v": video_id}) + return video_id elif parsed.netloc == "youtu.be": - return url_set_query("https://www.youtube.com/watch", {"v": parsed.path[1:]}) + return parsed.path[1:] + + return None + + +def clean_youtube_url(url: str) -> Optional[str]: + id_ = get_youtube_id(url) + if id_: + return url_set_query("https://www.youtube.com/watch", {"v": id_}) return None