forked from rDrama/rDrama
1
0
Fork 0

Merge pull request #36 from Aevann1/videos2

Videos2
master
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_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)) 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_REDIS_URL"] = environ.get("REDIS_URL").strip()
app.config["CACHE_DEFAULT_TIMEOUT"] = 60 app.config["CACHE_DEFAULT_TIMEOUT"] = 60
app.config["CACHE_KEY_PREFIX"] = "flask_caching_" app.config["CACHE_KEY_PREFIX"] = "flask_caching_"

View File

@ -45,6 +45,7 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
thumburl = Column(String) thumburl = Column(String)
is_banned = Column(Boolean, default=False) is_banned = Column(Boolean, default=False)
bannedfor = Column(Boolean) bannedfor = Column(Boolean)
processing = Column(Boolean, default=False)
views = Column(Integer, default=0) views = Column(Integer, default=0)
deleted_utc = Column(Integer, default=0) deleted_utc = Column(Integer, default=0)
distinguish_level = 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') 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 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 @property
@lazy @lazy
def active_flags(self): return self.flags.count() def active_flags(self): return self.flags.count()

View File

@ -7,7 +7,16 @@ from .sanitize import *
from .const 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('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") 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, parent_submission=None,
distinguish_level=6, distinguish_level=6,
) )
g.db.add(new_comment) db.add(new_comment)
g.db.flush() db.flush()
new_aux = CommentAux(id=new_comment.id, new_aux = CommentAux(id=new_comment.id,
body=text, body=text,
body_html=text_html, body_html=text_html,
) )
g.db.add(new_aux) db.add(new_aux)
notif = Notification(comment_id=new_comment.id, notif = Notification(comment_id=new_comment.id,
user_id=user.id) user_id=uid)
g.db.add(notif) db.add(notif)
def send_pm(vid, user, text): def send_pm(vid, user, text):

View File

@ -1,9 +1,10 @@
import requests import requests
from os import environ from os import environ, path, remove
from PIL import Image as IImage, ImageSequence from PIL import Image as IImage, ImageSequence
import base64 import base64
from files.classes.images import * from files.classes.images import *
from flask import g from flask import g
from werkzeug.utils import secure_filename
CF_KEY = environ.get("CLOUDFLARE_KEY", "").strip() CF_KEY = environ.get("CLOUDFLARE_KEY", "").strip()
CF_ZONE = environ.get("CLOUDFLARE_ZONE", "").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"]) new_image = Image(text=url, deletehash=resp["deletehash"])
g.db.add(new_image) 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) 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: if v and v.admin_level == 0:
blocking = g.db.query( blocking = g.db.query(
UserBlock.target_id).filter_by( UserBlock.target_id).filter_by(

View File

@ -1,3 +1,4 @@
import time
from urllib.parse import urlparse from urllib.parse import urlparse
import mistletoe import mistletoe
import urllib.parse import urllib.parse
@ -527,6 +528,44 @@ def filter_title(title):
return 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") @app.post("/submit")
@limiter.limit("6/minute") @limiter.limit("6/minute")
@is_not_banned @is_not_banned
@ -827,13 +866,72 @@ def submit_post(v):
abort(413) abort(413)
file = request.files['file'] file = request.files['file']
if not file.content_type.startswith('image/'): #if not file.content_type.startswith('image/'):
if request.headers.get("Authorization"): return {"error": f"Image files only"}, 400 # 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 # 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) if file.content_type.startswith('video/') and v.coins < app.config["VIDEO_COIN_REQUIREMENT"] and v.admin_level < 1:
else: new_post.url = upload_imgur(file) 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)
g.db.add(new_post.submission_aux) 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: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: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 %}" /> <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:url" content="{{comment_info.permalink | full_link}}" />
<meta property="og:site_name" content="{{request.host}}" /> <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:author" name="author" content="{{'@'+p.author.username}}" />
<meta property="og:title" content="{{p.title}} - {{'SITE_NAME' | app_config}}" /> <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 %}" /> <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:url" content="{{p.permalink | full_link}}" />
<meta property="og:site_name" content="{{request.host}}" /> <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.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.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.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.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> {% 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 %} {% endif %}
@ -301,7 +308,7 @@
<iframe src="{{streamurl}}" width="100%" height="80" frameBorder="0" allowtransparency="true" allow="encrypted-media"></iframe> <iframe src="{{streamurl}}" width="100%" height="80" frameBorder="0" allowtransparency="true" allow="encrypted-media"></iframe>
<pre></pre> <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 %}> <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 %}"> <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> <span>{{p.domain|truncate(30, True)}}</span>
@ -321,6 +328,15 @@
</div> </div>
</div> </div>
<pre></pre> <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 %} {% endif %}
{{p.realbody(v) | safe}} {{p.realbody(v) | safe}}
</div> </div>
@ -497,7 +513,7 @@
</div> </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="row no-gutters d-block d-md-none">
<div class="col"> <div class="col">
<a {% if not v or v.newtabexternal %}target="_blank"{% endif %} rel="nofollow noopener noreferrer" href="{{p.realurl(v)}}"></a> <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"> <img loading="lazy" src="{{p.thumb_url}}" class="post-img">
</a> </a>
{% elif p.is_image %} {% 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"> <img loading="lazy" src="{{p.thumb_url}}" class="post-img">
</a> </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)}}"> <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"> <img loading="lazy" src="{{p.thumb_url}}" class="post-img">
</a> </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_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.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.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.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> {% 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 %} {% endif %}
@ -392,6 +393,14 @@
<img loading="lazy" src="{{p.url}}" class="img-fluid" style="max-height:500px;" alt="Unable to load image"> <img loading="lazy" src="{{p.url}}" class="img-fluid" style="max-height:500px;" alt="Unable to load image">
</a> </a>
</div> </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 %} {% 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"> <div style="text-align: center" class="mt-3 mb-4">
<iframe src="{{p.embed_url}}" frameborder="0" width="600" height="337" allow="fullscreen"></iframe> <iframe src="{{p.embed_url}}" frameborder="0" width="600" height="337" allow="fullscreen"></iframe>

View File

@ -330,16 +330,17 @@
</div> </div>
<div id="image-upload-block"> <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"> <img loading="lazy" id="image-preview" class="w-100 d-block">
<label class="btn btn-secondary m-0" for="file-upload"> <label class="btn btn-secondary m-0" for="file-upload">
<div id="filename-show">Select Image</div> <div id="filename-show">Select File</div>
<input id="file-upload" type="file" name="file" accept="image/*" hidden> <input id="file-upload" type="file" name="file" accept="image/*, video/*" hidden>
</label> </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>
</div> </div>