2022-05-07 06:04:14 +00:00
import os
2022-11-15 09:19:08 +00:00
import subprocess
2022-05-22 16:13:19 +00:00
import time
2023-01-23 09:58:38 +00:00
import requests
2022-11-15 09:19:08 +00:00
from shutil import copyfile
2023-09-26 16:58:51 +00:00
import ffmpeg
2022-06-19 14:49:44 +00:00
import gevent
2022-09-27 01:24:20 +00:00
import imagehash
2023-01-23 09:58:38 +00:00
from flask import abort , g , has_request_context , request
2023-08-04 16:44:37 +00:00
from mimetypes import guess_extension
2022-11-15 09:19:08 +00:00
from PIL import Image
from PIL import UnidentifiedImageError
from PIL . ImageSequence import Iterator
2022-10-06 05:54:04 +00:00
from files . classes . media import *
2023-09-15 00:07:17 +00:00
from files . helpers . cloudflare import purge_files_in_cloudflare_cache
2023-01-23 09:58:38 +00:00
from files . helpers . settings import get_setting
2022-05-22 22:15:29 +00:00
2022-12-11 23:44:34 +00:00
from . config . const import *
2022-11-15 09:19:08 +00:00
2023-03-17 10:25:31 +00:00
def remove_media_using_link ( path ) :
2023-03-25 15:07:12 +00:00
if SITE in path :
path = path . split ( SITE , 1 ) [ 1 ]
2023-03-06 19:32:08 +00:00
os . remove ( path )
2023-01-25 15:13:24 +00:00
def media_ratelimit ( v ) :
2023-09-17 23:03:38 +00:00
if v . id == 15014 : # Marseygen exception
2023-07-20 21:26:59 +00:00
return
2023-01-25 15:13:24 +00:00
t = time . time ( ) - 86400
2023-03-16 06:27:58 +00:00
count = g . db . query ( Media ) . filter ( Media . user_id == v . id , Media . created_utc > t ) . count ( )
2024-01-10 21:34:25 +00:00
if v . patron or ( SITE == ' rdrama.net ' and v . id == 2158 ) :
limit = 300
else :
limit = 100
2023-11-16 13:09:44 +00:00
if count > limit and v . admin_level < PERMS [ ' USE_ADMIGGER_THREADS ' ] :
print ( STARS , flush = True )
2023-11-16 13:10:10 +00:00
print ( f ' @ { v . username } hit the { limit } file daily limit! ' )
2023-11-16 13:09:44 +00:00
print ( STARS , flush = True )
2023-03-07 00:46:09 +00:00
abort ( 500 )
2023-01-25 15:13:24 +00:00
2023-05-04 21:52:37 +00:00
def process_files ( files , v , body , is_dm = False , dm_user = None ) :
2022-11-15 09:19:08 +00:00
if g . is_tor or not files . get ( " file " ) : return body
2023-02-27 16:05:28 +00:00
files = files . getlist ( ' file ' ) [ : 20 ]
2023-02-22 17:27:33 +00:00
2023-01-25 15:13:24 +00:00
if files :
media_ratelimit ( v )
2023-02-26 12:46:38 +00:00
2022-11-15 09:19:08 +00:00
for file in files :
2023-02-27 15:02:35 +00:00
if f ' [ { file . filename } ] ' not in body :
2023-07-22 16:29:19 +00:00
continue
2023-02-27 15:02:35 +00:00
2022-11-15 09:19:08 +00:00
if file . content_type . startswith ( ' image/ ' ) :
name = f ' /images/ { time . time ( ) } ' . replace ( ' . ' , ' ' ) + ' .webp '
file . save ( name )
url = process_image ( name , v )
elif file . content_type . startswith ( ' video/ ' ) :
2023-05-05 01:32:57 +00:00
url = process_video ( file , v )
2022-11-15 09:19:08 +00:00
elif file . content_type . startswith ( ' audio/ ' ) :
2023-05-05 01:32:57 +00:00
url = f ' { SITE_FULL } { process_audio ( file , v ) } '
2023-08-10 13:12:43 +00:00
elif has_request_context ( ) :
2022-11-15 09:19:08 +00:00
abort ( 415 )
2023-08-10 13:12:43 +00:00
else :
return None
2023-05-05 21:45:25 +00:00
2023-05-04 21:52:37 +00:00
body = body . replace ( f ' [ { file . filename } ] ' , f ' { url } ' , 1 )
if is_dm :
2023-09-14 23:19:44 +00:00
with open ( f " { LOG_DIRECTORY } /dm_media.log " , " a+ " ) as f :
2023-05-04 21:52:37 +00:00
if dm_user :
f . write ( f ' { url } , { v . username } , { v . id } , { dm_user . username } , { dm_user . id } , { int ( time . time ( ) ) } \n ' )
else :
f . write ( f ' { url } , { v . username } , { v . id } , Modmail, Modmail, { int ( time . time ( ) ) } \n ' )
2024-01-12 14:05:30 +00:00
return body . replace ( ' \n ' , ' \n ' ) . strip ( )
2022-06-18 15:53:34 +00:00
2023-09-18 11:40:29 +00:00
def process_audio ( file , v , old = None ) :
2023-09-17 20:14:41 +00:00
if not old :
old = f ' /audio/ { time . time ( ) } ' . replace ( ' . ' , ' ' )
2023-03-17 10:50:13 +00:00
file . save ( old )
size = os . stat ( old ) . st_size
if size > MAX_IMAGE_AUDIO_SIZE_MB_PATRON * 1024 * 1024 or not v . patron and size > MAX_IMAGE_AUDIO_SIZE_MB * 1024 * 1024 :
os . remove ( old )
2023-08-12 19:52:23 +00:00
abort ( 413 , f " Max image/audio size is { MAX_IMAGE_AUDIO_SIZE_MB } MB ( { MAX_IMAGE_AUDIO_SIZE_MB_PATRON } MB for { patron } s) " )
2022-06-22 17:44:43 +00:00
2023-08-04 16:44:37 +00:00
extension = guess_extension ( file . content_type )
2023-08-06 01:20:14 +00:00
if not extension :
os . remove ( old )
abort ( 400 )
2023-08-04 16:44:37 +00:00
new = old + extension
2022-05-23 18:03:59 +00:00
2023-03-17 10:50:13 +00:00
try :
2023-09-26 16:58:51 +00:00
ffmpeg . input ( old ) . output ( new , loglevel = " quiet " , map_metadata = - 1 , acodec = " copy " ) . run ( )
2023-03-17 10:50:13 +00:00
except :
os . remove ( old )
if os . path . isfile ( new ) :
os . remove ( new )
abort ( 400 )
2022-05-23 18:03:59 +00:00
2023-03-17 10:50:13 +00:00
os . remove ( old )
2023-02-22 17:27:33 +00:00
2022-10-06 05:54:04 +00:00
media = Media (
kind = ' audio ' ,
2023-03-17 10:50:13 +00:00
filename = new ,
2022-11-15 09:19:08 +00:00
user_id = v . id ,
2022-10-06 05:54:04 +00:00
size = size
)
2023-03-16 06:27:58 +00:00
g . db . add ( media )
2022-10-06 05:54:04 +00:00
2023-03-17 10:50:13 +00:00
return new
2022-05-22 22:15:29 +00:00
2023-07-26 12:27:27 +00:00
def convert_to_mp4 ( old , new ) :
2022-10-06 05:16:24 +00:00
tmp = new . replace ( ' .mp4 ' , ' -t.mp4 ' )
2023-03-17 10:23:06 +00:00
try :
2023-09-26 16:58:51 +00:00
ffmpeg . input ( old ) . output ( tmp , loglevel = " quiet " , map_metadata = - 1 ) . run ( )
2023-03-17 10:23:06 +00:00
except :
2023-03-17 10:25:31 +00:00
os . remove ( old )
2023-03-17 10:50:19 +00:00
if os . path . isfile ( tmp ) :
os . remove ( tmp )
2023-07-23 15:16:00 +00:00
return
2023-05-05 21:45:25 +00:00
2022-10-06 05:07:24 +00:00
os . replace ( tmp , new )
2023-03-17 10:25:31 +00:00
os . remove ( old )
2022-10-11 17:26:38 +00:00
2023-07-27 23:04:58 +00:00
if SITE == ' watchpeopledie.tv ' :
url = f ' https://videos. { SITE } ' + new . split ( ' /videos ' ) [ 1 ]
else :
url = f " { SITE_FULL } { new } "
2023-09-15 00:07:17 +00:00
purge_files_in_cloudflare_cache ( url )
2022-11-22 15:49:15 +00:00
2022-10-06 06:00:41 +00:00
2022-11-15 09:19:08 +00:00
def process_video ( file , v ) :
2022-05-24 20:07:04 +00:00
old = f ' /videos/ { time . time ( ) } ' . replace ( ' . ' , ' ' )
2022-06-19 13:03:14 +00:00
file . save ( old )
2022-06-22 17:44:43 +00:00
2023-08-12 18:28:21 +00:00
size = os . stat ( old ) . st_size
if size > MAX_VIDEO_SIZE_MB_PATRON * 1024 * 1024 or ( not v . patron and size > MAX_VIDEO_SIZE_MB * 1024 * 1024 ) :
os . remove ( old )
abort ( 413 , f " Max video size is { MAX_VIDEO_SIZE_MB } MB ( { MAX_VIDEO_SIZE_MB_PATRON } MB for { patron } s) " )
2022-10-06 04:31:08 +00:00
2023-09-26 17:50:31 +00:00
new = f ' { old } .mp4 '
2022-05-24 20:07:04 +00:00
2023-10-15 15:04:58 +00:00
try :
codec = ffmpeg . probe ( old ) [ ' streams ' ] [ 0 ] [ ' codec_name ' ]
except :
os . remove ( old )
abort ( 400 , " Something went wrong processing your video and it might be on our end. Please try uploading it to https://pomf2.lain.la and post the link instead. " )
2023-10-06 13:56:48 +00:00
if codec != ' h264 ' :
2022-10-06 05:23:05 +00:00
copyfile ( old , new )
2023-08-20 16:24:26 +00:00
gevent . spawn ( convert_to_mp4 , old , new )
2022-10-06 04:54:05 +00:00
else :
2023-03-17 10:23:06 +00:00
try :
2023-09-26 16:58:51 +00:00
ffmpeg . input ( old ) . output ( new , loglevel = " quiet " , map_metadata = - 1 , acodec = " copy " , vcodec = " copy " ) . run ( )
2023-03-17 10:23:06 +00:00
except :
2023-03-17 10:25:31 +00:00
os . remove ( old )
2023-03-17 10:50:19 +00:00
if os . path . isfile ( new ) :
os . remove ( new )
2023-03-17 10:23:06 +00:00
abort ( 400 )
2023-03-17 10:25:31 +00:00
os . remove ( old )
2022-10-06 04:54:05 +00:00
2023-07-11 02:24:17 +00:00
media = Media (
kind = ' video ' ,
filename = new ,
user_id = v . id ,
size = os . stat ( new ) . st_size
)
g . db . add ( media )
2022-10-06 05:54:04 +00:00
2023-10-17 16:37:03 +00:00
if SITE == ' watchpeopledie.tv ' and v and v . username . lower ( ) . startswith ( " icosaka " ) :
gevent . spawn ( delete_file , new , f ' https://videos. { SITE } ' + new . split ( ' /videos ' ) [ 1 ] )
2023-09-07 15:26:31 +00:00
if SITE == ' watchpeopledie.tv ' :
2023-03-07 20:38:22 +00:00
return f ' https://videos. { SITE } ' + new . split ( ' /videos ' ) [ 1 ]
2023-03-07 20:33:12 +00:00
else :
return f " { SITE_FULL } { new } "
2022-05-23 19:00:14 +00:00
2023-10-06 19:10:06 +00:00
def process_image ( filename , v , resize = 0 , trim = False , uploader_id = None ) :
2022-11-15 09:19:08 +00:00
# thumbnails are processed in a thread and not in the request context
# if an image is too large or webp conversion fails, it'll crash
# to avoid this, we'll simply return None instead
2023-08-12 20:00:11 +00:00
original_resize = resize
2022-11-15 09:19:08 +00:00
has_request = has_request_context ( )
2022-05-07 06:04:14 +00:00
size = os . stat ( filename ) . st_size
2023-08-12 20:00:11 +00:00
if v and v . patron :
max_size = MAX_IMAGE_AUDIO_SIZE_MB_PATRON * 1024 * 1024
else :
max_size = MAX_IMAGE_AUDIO_SIZE_MB * 1024 * 1024
2022-05-04 23:09:46 +00:00
2022-11-15 09:19:08 +00:00
try :
with Image . open ( filename ) as i :
2023-08-25 03:38:15 +00:00
if not resize and size > max_size :
2023-08-12 20:00:11 +00:00
ratio = max_size / size
resize = i . width * ratio
2023-03-06 20:49:34 +00:00
oldformat = i . format
2023-07-06 23:30:46 +00:00
params = [ " magick " ]
2022-12-26 02:16:46 +00:00
if resize == 99 : params . append ( f " { filename } [0] " )
else : params . append ( filename )
params . extend ( [ " -coalesce " , " -quality " , " 88 " , " -strip " , " -auto-orient " ] )
2022-11-15 09:19:08 +00:00
if trim and len ( list ( Iterator ( i ) ) ) == 1 :
params . append ( " -trim " )
if resize and i . width > resize :
params . extend ( [ " -resize " , f " { resize } > " ] )
2023-03-17 11:00:10 +00:00
except :
2023-03-17 10:25:31 +00:00
os . remove ( filename )
2023-08-21 18:26:22 +00:00
if has_request and not filename . startswith ( ' /chat_images/ ' ) :
2022-11-15 09:19:08 +00:00
abort ( 415 )
return None
2022-10-25 15:41:18 +00:00
2022-12-21 17:05:45 +00:00
params . append ( filename )
2022-12-21 16:59:16 +00:00
try :
2023-09-26 16:58:51 +00:00
subprocess . run ( params , check = True , timeout = 30 )
2023-03-17 10:23:06 +00:00
except :
2023-03-17 10:25:31 +00:00
os . remove ( filename )
2022-12-21 16:59:16 +00:00
if has_request :
2023-10-15 15:05:35 +00:00
abort ( 400 , " An uploaded image couldn ' t be converted to WEBP. Please convert it to WEBP elsewhere then upload it again. " )
2022-12-21 16:59:16 +00:00
return None
2022-09-27 01:24:20 +00:00
2023-03-06 20:49:34 +00:00
size_after_conversion = os . stat ( filename ) . st_size
2023-08-12 20:00:11 +00:00
if original_resize :
2023-03-06 20:49:34 +00:00
if size_after_conversion > MAX_IMAGE_SIZE_BANNER_RESIZED_MB * 1024 * 1024 :
2023-03-17 10:25:31 +00:00
os . remove ( filename )
2022-11-15 09:19:08 +00:00
if has_request :
2022-12-07 20:53:52 +00:00
abort ( 413 , f " Max size for site assets is { MAX_IMAGE_SIZE_BANNER_RESIZED_MB } MB " )
2022-11-15 09:19:08 +00:00
return None
2022-09-27 01:24:20 +00:00
2022-10-22 15:24:07 +00:00
if filename . startswith ( ' files/assets/images/ ' ) :
path = filename . rsplit ( ' / ' , 1 ) [ 0 ]
2022-10-23 09:27:40 +00:00
kind = path . split ( ' / ' ) [ - 1 ]
2022-12-23 22:22:41 +00:00
if kind in { ' banners ' , ' sidebar ' } :
2022-10-23 09:27:40 +00:00
hashes = { }
for img in os . listdir ( path ) :
img_path = f ' { path } / { img } '
if img_path == filename : continue
2022-10-25 15:39:57 +00:00
with Image . open ( img_path ) as i :
i_hash = str ( imagehash . phash ( i ) )
2023-08-04 14:07:51 +00:00
if i_hash not in hashes . keys ( ) :
hashes [ i_hash ] = img_path
2022-10-23 09:27:40 +00:00
2022-10-25 15:39:57 +00:00
with Image . open ( filename ) as i :
i_hash = str ( imagehash . phash ( i ) )
2022-10-22 15:24:07 +00:00
if i_hash in hashes . keys ( ) :
2023-03-17 10:25:31 +00:00
os . remove ( filename )
2022-11-15 09:19:08 +00:00
return None
2022-09-26 01:27:31 +00:00
2023-10-06 19:10:06 +00:00
media = g . db . query ( Media ) . filter_by ( filename = filename , kind = ' image ' ) . one_or_none ( )
if media : g . db . delete ( media )
2023-03-18 05:42:34 +00:00
2022-10-06 05:54:04 +00:00
media = Media (
kind = ' image ' ,
2022-10-11 17:20:15 +00:00
filename = filename ,
2022-11-15 09:19:08 +00:00
user_id = uploader_id or v . id ,
2022-10-06 05:54:04 +00:00
size = os . stat ( filename ) . st_size
)
2023-10-06 19:10:06 +00:00
g . db . add ( media )
2022-10-06 05:54:04 +00:00
2023-09-15 00:10:11 +00:00
if SITE == ' watchpeopledie.tv ' and v and " dylan " in v . username . lower ( ) and " hewitt " in v . username . lower ( ) :
2023-10-17 16:37:03 +00:00
gevent . spawn ( delete_file , filename , f ' { SITE_FULL_IMAGES } { filename } ' )
2023-09-15 00:10:11 +00:00
2023-03-17 15:57:31 +00:00
return f ' { SITE_FULL_IMAGES } { filename } '
2023-09-15 00:10:11 +00:00
2023-10-17 16:37:03 +00:00
def delete_file ( filename , url ) :
2023-09-15 00:10:11 +00:00
time . sleep ( 60 )
os . remove ( filename )
2023-10-17 16:37:03 +00:00
purge_files_in_cloudflare_cache ( url )