mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-03 03:37:28 +01:00
parent
e5a4161e76
commit
1018e1c29c
@ -250,6 +250,7 @@ class PackageForm(FlaskForm):
|
|||||||
website = StringField(lazy_gettext("Website URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
website = StringField(lazy_gettext("Website URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
issueTracker = StringField(lazy_gettext("Issue Tracker URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
issueTracker = StringField(lazy_gettext("Issue Tracker URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
forums = IntegerField(lazy_gettext("Forum Topic ID"), [Optional(), NumberRange(0,999999)])
|
forums = IntegerField(lazy_gettext("Forum Topic ID"), [Optional(), NumberRange(0,999999)])
|
||||||
|
video_url = StringField(lazy_gettext("Video URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
|
|
||||||
submit = SubmitField(lazy_gettext("Save"))
|
submit = SubmitField(lazy_gettext("Save"))
|
||||||
|
|
||||||
@ -333,6 +334,7 @@ def create_edit(author=None, name=None):
|
|||||||
"website": form.website.data,
|
"website": form.website.data,
|
||||||
"issueTracker": form.issueTracker.data,
|
"issueTracker": form.issueTracker.data,
|
||||||
"forums": form.forums.data,
|
"forums": form.forums.data,
|
||||||
|
"video_url": form.video_url.data,
|
||||||
})
|
})
|
||||||
|
|
||||||
if wasNew and package.repo is not None:
|
if wasNew and package.repo is not None:
|
||||||
|
@ -89,6 +89,7 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||||||
* `website`: Website URL.
|
* `website`: Website URL.
|
||||||
* `issue_tracker`: Issue tracker URL.
|
* `issue_tracker`: Issue tracker URL.
|
||||||
* `forums`: forum topic ID.
|
* `forums`: forum topic ID.
|
||||||
|
* `video_url`: URL to a video, YouTube only for now.
|
||||||
* GET `/api/packages/<username>/<name>/dependencies/`
|
* GET `/api/packages/<username>/<name>/dependencies/`
|
||||||
* Returns dependencies, with suggested candidates
|
* Returns dependencies, with suggested candidates
|
||||||
* If query argument `only_hard` is present, only hard deps will be returned.
|
* If query argument `only_hard` is present, only hard deps will be returned.
|
||||||
|
@ -61,6 +61,7 @@ It should be a JSON dictionary with one or more of the following optional keys:
|
|||||||
* `website`: Website URL.
|
* `website`: Website URL.
|
||||||
* `issue_tracker`: Issue tracker URL.
|
* `issue_tracker`: Issue tracker URL.
|
||||||
* `forums`: forum topic ID.
|
* `forums`: forum topic ID.
|
||||||
|
* `video_url`: URL to a video, YouTube only for now.
|
||||||
|
|
||||||
Use `null` to unset fields where relevant.
|
Use `null` to unset fields where relevant.
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ from app.logic.LogicError import LogicError
|
|||||||
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
||||||
License, UserRank, PackageDevState
|
License, UserRank, PackageDevState
|
||||||
from app.utils import addAuditLog
|
from app.utils import addAuditLog
|
||||||
|
from app.utils.url import clean_youtube_url
|
||||||
|
|
||||||
|
|
||||||
def check(cond: bool, msg: str):
|
def check(cond: bool, msg: str):
|
||||||
@ -61,6 +62,7 @@ ALLOWED_FIELDS = {
|
|||||||
"issue_tracker": str,
|
"issue_tracker": str,
|
||||||
"issueTracker": str,
|
"issueTracker": str,
|
||||||
"forums": int,
|
"forums": int,
|
||||||
|
"video_url": str,
|
||||||
}
|
}
|
||||||
|
|
||||||
ALIASES = {
|
ALIASES = {
|
||||||
@ -128,8 +130,13 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
|
|||||||
if "media_license" in data:
|
if "media_license" in data:
|
||||||
data["media_license"] = get_license(data["media_license"])
|
data["media_license"] = get_license(data["media_license"])
|
||||||
|
|
||||||
|
if "video_url" in data:
|
||||||
|
data["video_url"] = clean_youtube_url(data["video_url"])
|
||||||
|
if data["video_url"] is None:
|
||||||
|
raise LogicError(400, lazy_gettext("Video URL is not a YouTube video URL"))
|
||||||
|
|
||||||
for key in ["name", "title", "short_desc", "desc", "type", "dev_state", "license", "media_license",
|
for key in ["name", "title", "short_desc", "desc", "type", "dev_state", "license", "media_license",
|
||||||
"repo", "website", "issueTracker", "forums"]:
|
"repo", "website", "issueTracker", "forums", "video_url"]:
|
||||||
if key in data:
|
if key in data:
|
||||||
setattr(package, key, data[key])
|
setattr(package, key, data[key])
|
||||||
|
|
||||||
|
@ -389,6 +389,7 @@ class Package(db.Model):
|
|||||||
website = db.Column(db.String(200), nullable=True)
|
website = db.Column(db.String(200), nullable=True)
|
||||||
issueTracker = db.Column(db.String(200), nullable=True)
|
issueTracker = db.Column(db.String(200), nullable=True)
|
||||||
forums = db.Column(db.Integer, nullable=True)
|
forums = db.Column(db.Integer, nullable=True)
|
||||||
|
video_url = db.Column(db.String(200), nullable=True, default=None)
|
||||||
|
|
||||||
provides = db.relationship("MetaPackage", secondary=PackageProvides, order_by=db.asc("name"), back_populates="packages")
|
provides = db.relationship("MetaPackage", secondary=PackageProvides, order_by=db.asc("name"), back_populates="packages")
|
||||||
|
|
||||||
@ -527,6 +528,7 @@ class Package(db.Model):
|
|||||||
"website": self.website,
|
"website": self.website,
|
||||||
"issue_tracker": self.issueTracker,
|
"issue_tracker": self.issueTracker,
|
||||||
"forums": self.forums,
|
"forums": self.forums,
|
||||||
|
"video_url": self.video_url,
|
||||||
|
|
||||||
"tags": [x.name for x in self.tags],
|
"tags": [x.name for x in self.tags],
|
||||||
"content_warnings": [x.name for x in self.content_warnings],
|
"content_warnings": [x.name for x in self.content_warnings],
|
||||||
|
24
app/public/static/video_embed.js
Normal file
24
app/public/static/video_embed.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
document.querySelectorAll(".video-embed").forEach(ele => {
|
||||||
|
const url = new URL(ele.getAttribute("href"));
|
||||||
|
|
||||||
|
if (url.host == "www.youtube.com") {
|
||||||
|
ele.addEventListener("click", () => {
|
||||||
|
ele.parentNode.classList.add("d-block");
|
||||||
|
ele.classList.add("embed-responsive");
|
||||||
|
ele.classList.add("embed-responsive-16by9");
|
||||||
|
ele.innerHTML = `
|
||||||
|
<iframe title="YouTube video player" frameborder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowfullscreen>
|
||||||
|
</iframe>`;
|
||||||
|
|
||||||
|
const embedURL = new URL("https://www.youtube.com/");
|
||||||
|
embedURL.pathname = "/embed/" + url.searchParams.get("v");
|
||||||
|
embedURL.searchParams.set("autoplay", "1");
|
||||||
|
|
||||||
|
const iframe = ele.children[0];
|
||||||
|
iframe.setAttribute("src", embedURL);
|
||||||
|
});
|
||||||
|
ele.removeAttribute("href");
|
||||||
|
}
|
||||||
|
});
|
@ -1,5 +1,6 @@
|
|||||||
@import "components.scss";
|
@import "components.scss";
|
||||||
@import "packages.scss";
|
@import "packages.scss";
|
||||||
|
@import "gallery.scss";
|
||||||
@import "packagegrid.scss";
|
@import "packagegrid.scss";
|
||||||
@import "comments.scss";
|
@import "comments.scss";
|
||||||
|
|
||||||
|
84
app/scss/gallery.scss
Normal file
84
app/scss/gallery.scss
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
.gallery {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 2em;
|
||||||
|
overflow: auto hidden;
|
||||||
|
|
||||||
|
li, li a {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-image {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover img {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 200px;
|
||||||
|
height: 133px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-embed {
|
||||||
|
min-width: 200px;
|
||||||
|
min-height: 133px;
|
||||||
|
background: #111;
|
||||||
|
position: relative;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.fas {
|
||||||
|
display: block;
|
||||||
|
font-size: 200%;
|
||||||
|
color: #f44;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #191919;
|
||||||
|
|
||||||
|
.fas {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-add {
|
||||||
|
display: block !important;
|
||||||
|
width: 200px;
|
||||||
|
height: 133px;
|
||||||
|
background: #444;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 133px !important;
|
||||||
|
font-size: 80px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #555;
|
||||||
|
color: #999;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,3 @@
|
|||||||
.screenshot_list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 0 2em;
|
|
||||||
|
|
||||||
li, li a {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin: 5px;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 200px;
|
|
||||||
height: 133px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-tr {
|
.badge-tr {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
@ -34,23 +5,6 @@
|
|||||||
color: #ccc !important;;
|
color: #ccc !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshot-add {
|
|
||||||
display: block !important;
|
|
||||||
width: 200px;
|
|
||||||
height: 133px;
|
|
||||||
background: #444;
|
|
||||||
color: #666;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 133px !important;
|
|
||||||
font-size: 80px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #555;
|
|
||||||
color: #999;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-row {
|
.info-row {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
|
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/libs/bootstrap.min.css">
|
<link rel="stylesheet" type="text/css" href="/static/libs/bootstrap.min.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=32">
|
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=33">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" />
|
<link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" />
|
||||||
<link rel="shortcut icon" href="/favicon-16.png" sizes="16x16">
|
<link rel="shortcut icon" href="/favicon-16.png" sizes="16x16">
|
||||||
<link rel="icon" href="/favicon-128.png" sizes="128x128">
|
<link rel="icon" href="/favicon-128.png" sizes="128x128">
|
||||||
|
@ -117,6 +117,7 @@
|
|||||||
pattern="[0-9]+",
|
pattern="[0-9]+",
|
||||||
prefix="forum.minetest.net/viewtopic.php?t=",
|
prefix="forum.minetest.net/viewtopic.php?t=",
|
||||||
placeholder=_("Tip: paste in a forum topic URL")) }}
|
placeholder=_("Tip: paste in a forum topic URL")) }}
|
||||||
|
{{ render_field(form.video_url, class_="pkg_meta", hint=_("Only supports YouTube, for now")) }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="pkg_meta mt-5">{{ render_submit_field(form.submit) }}</div>
|
<div class="pkg_meta mt-5">{{ render_submit_field(form.submit) }}</div>
|
||||||
|
@ -78,6 +78,11 @@
|
|||||||
|
|
||||||
{{ render_submit_field(form.submit, tabindex=280) }}
|
{{ render_submit_field(form.submit, tabindex=280) }}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h2>{{ _("Videos") }}</h2>
|
||||||
|
<p>
|
||||||
|
{{ _("You can set a video on the Edit Details page") }}
|
||||||
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scriptextra %}
|
{% block scriptextra %}
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scriptextra %}
|
||||||
|
<script src="/static/video_embed.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% macro render_license(license) %}
|
{% macro render_license(license) %}
|
||||||
{% if license.url %}
|
{% if license.url %}
|
||||||
<a href="{{ license.url }}">{{ license.name }}</a>
|
<a href="{{ license.url }}">{{ license.name }}</a>
|
||||||
@ -89,19 +93,19 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="btn-group float-right mb-4">
|
<div class="btn-group float-right mb-4">
|
||||||
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
|
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
|
||||||
<a class="btn btn-primary" href="{{ package.getURL("packages.create_edit") }}">
|
<a class="btn btn-primary" href="{{ package.getURL('packages.create_edit') }}">
|
||||||
<i class="fas fa-pen mr-1"></i>
|
<i class="fas fa-pen mr-1"></i>
|
||||||
{{ _("Edit") }}
|
{{ _("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if package.checkPerm(current_user, "MAKE_RELEASE") %}
|
{% if package.checkPerm(current_user, "MAKE_RELEASE") %}
|
||||||
<a class="btn btn-primary" href="{{ package.getURL("packages.create_release") }}">
|
<a class="btn btn-primary" href="{{ package.getURL('packages.create_release') }}">
|
||||||
<i class="fas fa-plus mr-1"></i>
|
<i class="fas fa-plus mr-1"></i>
|
||||||
{{ _("Release") }}
|
{{ _("Release") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if package.checkPerm(current_user, "DELETE_PACKAGE") or package.checkPerm(current_user, "UNAPPROVE_PACKAGE") %}
|
{% if package.checkPerm(current_user, "DELETE_PACKAGE") or package.checkPerm(current_user, "UNAPPROVE_PACKAGE") %}
|
||||||
<a class="btn btn-danger" href="{{ package.getURL("packages.remove") }}">
|
<a class="btn btn-danger" href="{{ package.getURL('packages.remove') }}">
|
||||||
<i class="fas fa-trash mr-1"></i>
|
<i class="fas fa-trash mr-1"></i>
|
||||||
{{ _("Remove") }}
|
{{ _("Remove") }}
|
||||||
</a>
|
</a>
|
||||||
@ -236,33 +240,44 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-9" style="padding-right: 45px;">
|
<div class="col-md-9" style="padding-right: 45px;">
|
||||||
{% set screenshots = package.screenshots.all() %}
|
{% set screenshots = package.screenshots.all() %}
|
||||||
{% if screenshots or package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
|
||||||
{% if package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
|
||||||
<a href="{{ package.getURL("packages.screenshots") }}" class="btn btn-primary float-right">
|
|
||||||
<i class="fas fa-images mr-1"></i>
|
|
||||||
{{ _("Edit") }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<ul class="screenshot_list">
|
{% if package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
||||||
{% for ss in screenshots %}
|
<a href="{{ package.getURL('packages.screenshots') }}" class="btn btn-primary float-right">
|
||||||
{% if ss.approved or package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
<i class="fas fa-images mr-1"></i>
|
||||||
<li>
|
{{ _("Edit") }}
|
||||||
<a href="{{ ss.url }}" class="position-relative">
|
</a>
|
||||||
<img src="{{ ss.getThumbnailURL() }}" alt="{{ ss.title }}" />
|
{% endif %}
|
||||||
{% if not ss.approved %}
|
|
||||||
<span class="badge bg-dark badge-tr">{{ _("Awaiting review") }}</span>
|
{% if screenshots or package.checkPerm(current_user, "ADD_SCREENSHOTS") or package.video_url %}
|
||||||
{% endif %}
|
<ul class="gallery">
|
||||||
</a>
|
{% if package.video_url %}
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ package.getURL("packages.create_screenshot") }}">
|
<a href="{{ package.video_url }}" class="video-embed" title="{{ _('YouTube video embed') }}">
|
||||||
<i class="fas fa-plus screenshot-add"></i>
|
<i class="fas fa-play"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if screenshots or package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
||||||
|
{% for ss in screenshots %}
|
||||||
|
{% if ss.approved or package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ ss.url }}" class="gallery-image">
|
||||||
|
<img src="{{ ss.getThumbnailURL() }}" alt="{{ ss.title }}" />
|
||||||
|
{% if not ss.approved %}
|
||||||
|
<span class="badge bg-dark badge-tr">{{ _("Awaiting review") }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ package.getURL('packages.create_screenshot') }}">
|
||||||
|
<i class="fas fa-plus screenshot-add"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
13
app/tests/unit/test_url.py
Normal file
13
app/tests/unit/test_url.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from app.utils.url import clean_youtube_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_clean_youtube_url():
|
||||||
|
assert clean_youtube_url(
|
||||||
|
"https://www.youtube.com/watch?v=AABBCC") == "https://www.youtube.com/watch?v=AABBCC"
|
||||||
|
assert clean_youtube_url(
|
||||||
|
"https://www.youtube.com/watch?v=boGcB4H5-WA&other=1") == "https://www.youtube.com/watch?v=boGcB4H5-WA"
|
||||||
|
assert clean_youtube_url("https://www.youtube.com/watch?kk=boGcB4H5-WA&other=1") is None
|
||||||
|
assert clean_youtube_url("https://www.bob.com/watch?v=AABBCC") is None
|
||||||
|
|
||||||
|
assert clean_youtube_url("https://youtu.be/boGcB4H5-WA") == "https://www.youtube.com/watch?v=boGcB4H5-WA"
|
||||||
|
assert clean_youtube_url("https://youtu.be/boGcB4H5-WA?this=1") == "https://www.youtube.com/watch?v=boGcB4H5-WA"
|
@ -14,13 +14,12 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import re
|
||||||
import secrets
|
import secrets
|
||||||
|
|
||||||
from .flask import *
|
from .flask import *
|
||||||
from .models import *
|
from .models import *
|
||||||
from .user import *
|
from .user import *
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
YESES = ["yes", "true", "1", "on"]
|
YESES = ["yes", "true", "1", "on"]
|
||||||
|
|
||||||
|
46
app/utils/url.py
Normal file
46
app/utils/url.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# ContentDB
|
||||||
|
# Copyright (C) 2022 rubenwardy
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import urllib.parse as urlparse
|
||||||
|
from typing import Optional, Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
def url_set_query(url: str, params: Dict[str, str]) -> str:
|
||||||
|
url_parts = list(urlparse.urlparse(url))
|
||||||
|
query = dict(urlparse.parse_qsl(url_parts[4]))
|
||||||
|
query.update(params)
|
||||||
|
|
||||||
|
url_parts[4] = urlparse.urlencode(query)
|
||||||
|
return urlparse.urlunparse(url_parts)
|
||||||
|
|
||||||
|
|
||||||
|
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]:
|
||||||
|
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})
|
||||||
|
|
||||||
|
elif parsed.netloc == "youtu.be":
|
||||||
|
return url_set_query("https://www.youtube.com/watch", {"v": parsed.path[1:]})
|
||||||
|
|
||||||
|
return None
|
25
migrations/versions/011e42c52d21_.py
Normal file
25
migrations/versions/011e42c52d21_.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 011e42c52d21
|
||||||
|
Revises: 6e57b2b4dcdf
|
||||||
|
Create Date: 2022-01-25 18:48:46.367409
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '011e42c52d21'
|
||||||
|
down_revision = '6e57b2b4dcdf'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('package', sa.Column('video_url', sa.String(length=200), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('package', 'video_url')
|
Loading…
Reference in New Issue
Block a user