Merge pull request #36 from Aevann1/videos2

Videos2
remotes/1693045480750635534/spooky-22
Aevann1 2021-09-07 00:03:39 +02:00 committed by GitHub
commit 28695c459c
9 changed files with 203 additions and 21 deletions

View File

@ -76,6 +76,9 @@ app.config["SPAM_URL_SIMILARITY_THRESHOLD"] = float(environ.get("SPAM_URL_SIMILA
app.config["COMMENT_SPAM_SIMILAR_THRESHOLD"] = float(environ.get("COMMENT_SPAM_SIMILAR_THRESHOLD", 0.5))
app.config["COMMENT_SPAM_COUNT_THRESHOLD"] = int(environ.get("COMMENT_SPAM_COUNT_THRESHOLD", 0.5))
# how many coins are required to upload videos
app.config["VIDEO_COIN_REQUIREMENT"] = int(environ.get("VIDEO_COIN_REQUIREMENT", 0))
app.config["CACHE_REDIS_URL"] = environ.get("REDIS_URL").strip()
app.config["CACHE_DEFAULT_TIMEOUT"] = 60
app.config["CACHE_KEY_PREFIX"] = "flask_caching_"

View File

@ -45,6 +45,7 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
thumburl = Column(String)
is_banned = Column(Boolean, default=False)
bannedfor = Column(Boolean)
processing = Column(Boolean, default=False)
views = Column(Integer, default=0)
deleted_utc = Column(Integer, default=0)
distinguish_level = Column(Integer, default=0)
@ -400,6 +401,13 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
if self.url: return self.url.lower().endswith('.jpg') or self.url.lower().endswith('.png') or self.url.lower().endswith('.gif') or self.url.lower().endswith('.jpeg') or self.url.lower().endswith('?maxwidth=9999')
else: return False
@property
def is_video(self) -> bool:
if self.url:
return self.url.startswith("https://i.imgur.com") and self.url.lower().endswith('.mp4')
else:
return False
@property
@lazy
def active_flags(self): return self.flags.count()

View File

@ -7,7 +7,16 @@ from .sanitize import *
from .const import *
def send_notification(vid, user, text):
def send_notification(vid, user, text, db=None):
# for when working outside request context
if isinstance(user, int):
uid = user
else:
uid = user.id
if not db:
db = g.db
text = text.replace('r/', 'r\/').replace('u/', 'u\/')
text = text.replace("\n", "\n\n").replace("\n\n\n\n\n\n", "\n\n").replace("\n\n\n\n", "\n\n").replace("\n\n\n", "\n\n")
@ -20,19 +29,19 @@ def send_notification(vid, user, text):
parent_submission=None,
distinguish_level=6,
)
g.db.add(new_comment)
db.add(new_comment)
g.db.flush()
db.flush()
new_aux = CommentAux(id=new_comment.id,
body=text,
body_html=text_html,
)
g.db.add(new_aux)
db.add(new_aux)
notif = Notification(comment_id=new_comment.id,
user_id=user.id)
g.db.add(notif)
user_id=uid)
db.add(notif)
def send_pm(vid, user, text):

View File

@ -1,9 +1,10 @@
import requests
from os import environ
from os import environ, path, remove
from PIL import Image as IImage, ImageSequence
import base64
from files.classes.images import *
from flask import g
from werkzeug.utils import secure_filename
CF_KEY = environ.get("CLOUDFLARE_KEY", "").strip()
CF_ZONE = environ.get("CLOUDFLARE_ZONE", "").strip()
@ -86,4 +87,36 @@ def upload_imgur(file=None, resize=False, png=False):
new_image = Image(text=url, deletehash=resp["deletehash"])
g.db.add(new_image)
return(url)
return(url)
class UploadException(Exception):
"""Custom exception to raise if upload goes wrong"""
pass
def upload_video(file):
file_path = path.join("temp", secure_filename(file.filename))
file.save(file_path)
headers = {"Authorization": f"Client-ID {IMGUR_KEY}"}
with open(file_path, 'rb') as f:
try:
r = requests.post('https://api.imgur.com/3/upload', headers=headers, files={"video": f})
r.raise_for_status()
resp = r.json()['data']
except requests.HTTPError as e:
raise UploadException("Invalid video. Make sure it's 1 minute long or shorter.")
except Exception:
raise UploadException("Error, please try again later.")
finally:
remove(file_path)
link = resp['link']
img = Image(text=link, deletehash=resp['deletehash'])
g.db.add(img)
return link

View File

@ -121,6 +121,11 @@ def frontlist(v=None, sort="hot", page=1, t="all", ids_only=True, filter_words='
posts = posts.filter_by(is_banned=False,stickied=False,private=False).filter(Submission.deleted_utc == 0)
if v:
posts = posts.filter(or_(Submission.processing == False, Submission.author_id == v.id))
else:
posts = posts.filter_by(processing=False)
if v and v.admin_level == 0:
blocking = g.db.query(
UserBlock.target_id).filter_by(

View File

@ -1,3 +1,4 @@
import time
from urllib.parse import urlparse
import mistletoe
import urllib.parse
@ -527,6 +528,44 @@ def filter_title(title):
return title
IMGUR_KEY = environ.get("IMGUR_KEY", "").strip()
def check_processing_thread(v, post, link, db):
image_id = link.split('/')[-1].rstrip('.mp4')
headers = {"Authorization": f"Client-ID {IMGUR_KEY}"}
while True:
# break on error to prevent zombie threads
try:
time.sleep(15)
req = requests.get(f"https://api.imgur.com/3/image/{image_id}", headers=headers)
status = req.json()['data']['processing']['status']
if status == 'completed':
post.processing = False
db.add(post)
send_notification(
NOTIFICATIONS_ACCOUNT,
v,
f"Your video has finished processing and your [post](/post/{post.id}) is now live.",
db=db
)
db.commit()
break
# just in case
elif status == 'failed':
print(f"video upload for post {post.id} failed")
break
except Exception:
break
@app.post("/submit")
@limiter.limit("6/minute")
@is_not_banned
@ -827,13 +866,72 @@ def submit_post(v):
abort(413)
file = request.files['file']
if not file.content_type.startswith('image/'):
if request.headers.get("Authorization"): return {"error": f"Image files only"}, 400
else: return render_template("submit.html", v=v, error=f"Image files only.", title=title, body=request.form.get("body", "")), 400
#if not file.content_type.startswith('image/'):
# if request.headers.get("Authorization"): return {"error": f"Image files only"}, 400
# else: return render_template("submit.html", v=v, error=f"Image files only.", title=title, body=request.form.get("body", "")), 400
if not file.content_type.startswith(('image/', 'video/')):
if request.headers.get("Authorization"): return {"error": f"File type not allowed"}, 400
else: return render_template("submit.html", v=v, error=f"File type not allowed.", title=title, body=request.form.get("body", "")), 400
if 'pcm' in request.host: new_post.url = upload_ibb(file)
else: new_post.url = upload_imgur(file)
if file.content_type.startswith('video/') and v.coins < app.config["VIDEO_COIN_REQUIREMENT"] and v.admin_level < 1:
if request.headers.get("Authorization"):
return {
"error": f"You need at least {app.config['VIDEO_COIN_REQUIREMENT']} coins to upload videos"
}, 403
else:
return render_template(
"submit.html",
v=v,
error=f"You need at least {app.config['VIDEO_COIN_REQUIREMENT']} coins to upload videos.",
title=title,
body=request.form.get("body", "")
), 403
if 'pcm' in request.host:
if file.content_type.startswith('image/'):
new_post.url = upload_ibb(file)
else:
try:
post_url = upload_video(file)
new_post.url = post_url
new_post.processing = True
gevent.spawn(check_processing_thread, v.id, new_post, post_url, g.db)
except UploadException as e:
if request.headers.get("Authorization"):
return {
"error": str(e),
}, 400
else:
return render_template(
"submit.html",
v=v,
error=str(e),
title=title,
body=request.form.get("body", "")
), 400
else:
if file.content_type.startswith('image/'):
new_post.url = upload_imgur(file)
else:
try:
post_url = upload_video(file)
new_post.url = post_url
new_post.processing = True
gevent.spawn(check_processing_thread, v.id, new_post, post_url, g.db)
except UploadException as e:
if request.headers.get("Authorization"):
return {
"error": str(e),
}, 400
else:
return render_template(
"submit.html",
v=v,
error=str(e),
title=title,
body=request.form.get("body", "")
), 400
g.db.add(new_post)
g.db.add(new_post.submission_aux)

View File

@ -36,6 +36,9 @@
<meta property="og:author" name="author" content="{{'@'+comment_info.author.username}}" />
<meta property="og:title" content="{{'@'+comment_info.author.username}} comments on {{p.title}} - {{'SITE_NAME' | app_config}}" />
<meta property="og:image" content="{% if p.is_image %}{{p.realurl(v)}}{% elif p.has_thumb%}{{p.thumb_url}}{% else %}{{'SITE_NAME' | app_config}}/assets/images/{{'SITE_NAME' | app_config}}/preview.gif{% endif %}" />
{% if p.is_video %}
<meta property="og:video" content="{{ p.realurl(v) }}" />
{% endif %}
<meta property="og:url" content="{{comment_info.permalink | full_link}}" />
<meta property="og:site_name" content="{{request.host}}" />
@ -62,6 +65,9 @@
<meta property="og:author" name="author" content="{{'@'+p.author.username}}" />
<meta property="og:title" content="{{p.title}} - {{'SITE_NAME' | app_config}}" />
<meta property="og:image" content="{% if p.is_image %}{{p.realurl(v)}}{% elif p.has_thumb%}{{p.thumb_url}}{% else %}{{'SITE_NAME' | app_config}}/assets/images/{{'SITE_NAME' | app_config}}/preview.gif{% endif %}" />
{% if p.is_video %}
<meta property="og:video" content="{{ p.realurl(v) }}" />
{% endif %}
<meta property="og:url" content="{{p.permalink | full_link}}" />
<meta property="og:site_name" content="{{request.host}}" />
@ -256,6 +262,7 @@
{% if p.is_bot %} <i class="fad fa-robot text-info" data-toggle="tooltip" data-placement="bottom" data-original-title="Bot"></i>{% endif %}
{% if p.over_18 %}<span class="badge badge-danger text-small-extra mr-1">+18</span>{% endif %}
{% if p.private %}<span class="badge border-warning border-1 text-small-extra">Draft</span>{% endif %}
{% if p.processing %}<span class="badge border-warning border-1 text-small-extra">uploading...</span>{% endif %}
{% if p.active_flags %}<a class="btn btn-primary" href="javascript:void(0)" style="padding:1px 5px; font-size:10px;" onclick="document.getElementById('flaggers').classList.toggle('d-none')">{{p.active_flags}} Reports</a>{% endif %}
{% if p.author.verified %}<i class="fas fa-badge-check align-middle ml-1" style="color:#1DA1F2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="{{p.author.verified}}"></i>
{% endif %}
@ -301,7 +308,7 @@
<iframe src="{{streamurl}}" width="100%" height="80" frameBorder="0" allowtransparency="true" allow="encrypted-media"></iframe>
<pre></pre>
{% elif not p.embed_url and not p.is_image %}
{% elif not p.embed_url and not p.is_image and not p.is_video %}
<a rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}" {% if not v or v.newtabexternal %}target="_blank"{% endif %}>
<div class="d-flex d-md-none justify-content-between align-items-center border rounded p-2{% if p.realbody(v) %} mb-3{% endif %}">
<span>{{p.domain|truncate(30, True)}}</span>
@ -321,6 +328,15 @@
</div>
</div>
<pre></pre>
{% elif p.is_video %}
<div class="row no-gutters">
<div class="col">
<video controls preload="metadata" style="max-width: 100%">
<source src="{{ p.realurl(v) }}" type="video/mp4">
</video>
</div>
</div>
<pre></pre>
{% endif %}
{{p.realbody(v) | safe}}
</div>
@ -497,7 +513,7 @@
</div>
{% if not p.is_image %}
{% if not p.is_image and not p.is_video %}
<div class="row no-gutters d-block d-md-none">
<div class="col">
<a {% if not v or v.newtabexternal %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}"></a>

View File

@ -69,10 +69,10 @@
<img loading="lazy" src="{{p.thumb_url}}" class="post-img">
</a>
{% elif p.is_image %}
<a href="javascript:void(0)" class="expandable-image" data-toggle="modal" data-target="#expandImageModal" data-url="{{p.realurl(v)}}">
<a href="javascript:void(0)" class="expandable-image" data-toggle="modal" data-target="#expandImageModal" data-url="{{p.realurl(v)}}" onclick="expandDesktopImage('{{ p.realurl(v) }}')">
<img loading="lazy" src="{{p.thumb_url}}" class="post-img">
</a>
{% else %}
{% elif not p.is_video %}
<a {% if not v or v.newtabexternal %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}">
<img loading="lazy" src="{{p.thumb_url}}" class="post-img">
</a>
@ -104,6 +104,7 @@
{% if p.is_blocking %}<i class="fas fa-user-minus text-warning" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="You're blocking this user, but you can see this post because {{'it\'s an admin post' if p.distinguish_level else 'you\'re an admin'}}."></i>{% endif %}
{% if p.is_blocked %}<i class="fas fa-user-minus text-danger" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="This user is blocking you."></i>{% endif %}
{% if p.private %}<span class="badge border-warning border-1 text-small-extra">Draft</span>{% endif %}
{% if p.processing %}<span class="badge border-warning border-1 text-small-extra">uploading...</span>{% endif %}
{% if p.active_flags %}<a class="btn btn-primary" href="javascript:void(0)" style="padding:1px 5px; font-size:10px;" onclick="document.getElementById('flaggers-{{p.id}}').classList.toggle('d-none')">{{p.active_flags}} Reports</a>{% endif %}
{% if p.author.verified %}<i class="fas fa-badge-check align-middle ml-1" style="color:#1DA1F2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="{{p.author.verified}}"></i>
{% endif %}
@ -392,6 +393,14 @@
<img loading="lazy" src="{{p.url}}" class="img-fluid" style="max-height:500px;" alt="Unable to load image">
</a>
</div>
{% elif p.is_video %}
<a href="javascript:void(0)">
<div style="text-align: center" class="mt-4">
<video controls preload="metadata" style="max-width: 100%">
<source src="{{p.realurl(v)}}" type="video/mp4">
</video>
</div>
</a>
{% elif p.embed_url and "youtu" in p.domain or "streamable.com/" in p.url %}
<div style="text-align: center" class="mt-3 mb-4">
<iframe src="{{p.embed_url}}" frameborder="0" width="600" height="337" allow="fullscreen"></iframe>

View File

@ -330,16 +330,17 @@
</div>
<div id="image-upload-block">
<div><label class="mt-3">Image Upload</label></div>
<div><label class="mt-3">Attachment Upload</label></div>
<img loading="lazy" id="image-preview" class="w-100 d-block">
<label class="btn btn-secondary m-0" for="file-upload">
<div id="filename-show">Select Image</div>
<input id="file-upload" type="file" name="file" accept="image/*" hidden>
<div id="filename-show">Select File</div>
<input id="file-upload" type="file" name="file" accept="image/*, video/*" hidden>
</label>
<small class="form-text text-muted">Images uploaded will be public. Optional if you have text.</small>
<small class="form-text text-muted">Optional if you have text.</small>
<small class="form-text text-muted">You can upload videos up to 1 minute long if you have at least {{ 'VIDEO_COIN_REQUIREMENT' | app_config }} {{ 'COINS_NAME' | app_config }}{% if v.admin_level > 1 %} or are an admin{% endif %}.</small>
</div>
</div>