add web
This commit is contained in:
@@ -0,0 +1,652 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort, Response
|
||||
import hashlib
|
||||
import gzip
|
||||
import json
|
||||
from PIL import Image
|
||||
from app.models.groups import GroupIcon
|
||||
from app.models.user_thumbnail import UserThumbnail
|
||||
from app.models.asset_thumbnail import AssetThumbnail
|
||||
from app.models.place_icon import PlaceIcon
|
||||
from app.models.user import User
|
||||
from app.models.asset import Asset
|
||||
from app.routes.thumbnailer import TakeThumbnail
|
||||
from app.util import s3helper
|
||||
from app.extensions import redis_controller, csrf
|
||||
from config import Config
|
||||
from io import BytesIO
|
||||
|
||||
config = Config()
|
||||
ImageRoute = Blueprint('image', __name__)
|
||||
|
||||
def HandleResolutionCheck(
|
||||
WidthParametersName : list[ str ] = [ 'width', 'x' ],
|
||||
HeightParametersName : list[ str ] = [ 'height', 'y' ],
|
||||
AllowedWidths : list[ int ] = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
AllowedHeights : list[ int ] = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
MustBeSquare : bool = True,
|
||||
CanRoundToNearest : bool = True
|
||||
) -> ( int, int ):
|
||||
"""
|
||||
Handles resolution checking for images. Aborts the request if the resolution is invalid.
|
||||
Must be called in a flask request context.
|
||||
|
||||
:param WidthParametersName: The names of valid parameters for width.
|
||||
:param HeightParametersName: The names of valid parameters for height.
|
||||
:param AllowedWidths: The allowed widths.
|
||||
:param AllowedHeights: The allowed heights.
|
||||
:param MustBeSquare: Whether or not the image must be square.
|
||||
:param CanRoundToNearest: Whether or not the image can round to the nearest resolution.
|
||||
|
||||
:return: ( width, height )
|
||||
"""
|
||||
Width : int = None
|
||||
Height : int = None
|
||||
for WidthParameterName in WidthParametersName:
|
||||
if WidthParameterName in request.args:
|
||||
Width = request.args.get( key=WidthParameterName, default=None, type=int )
|
||||
break
|
||||
for HeightParameterName in HeightParametersName:
|
||||
if HeightParameterName in request.args:
|
||||
Height = request.args.get( key=HeightParameterName, default=None, type=int )
|
||||
break
|
||||
|
||||
|
||||
if Width is None or Height is None:
|
||||
abort( 400 )
|
||||
if ( Width not in AllowedWidths or Height not in AllowedHeights ) and not CanRoundToNearest:
|
||||
abort( 400 )
|
||||
if MustBeSquare and Width != Height:
|
||||
abort( 400 )
|
||||
|
||||
|
||||
if CanRoundToNearest:
|
||||
if Width not in AllowedWidths:
|
||||
Width = min( AllowedWidths, key=lambda x:abs(x-Width) )
|
||||
if Height not in AllowedHeights:
|
||||
Height = min( AllowedHeights, key=lambda x:abs(x-Height) )
|
||||
|
||||
return ( Width, Height )
|
||||
|
||||
def HandleImageResize(
|
||||
ImageContentHash : str,
|
||||
TargetWidth : int,
|
||||
TargetHeight : int,
|
||||
CroppedHash : str,
|
||||
CacheControl : str = "max-age=120",
|
||||
SkipCacheCroppedImage : bool = False,
|
||||
ReturnAsJSON : bool = False
|
||||
) -> Response:
|
||||
"""
|
||||
Handles image resizing
|
||||
|
||||
:param ImageContentHash: The content hash of the image.
|
||||
:param TargetWidth: The target width.
|
||||
:param TargetHeight: The target height.
|
||||
:param CroppedHash: The hash of the cropped image.
|
||||
|
||||
:return: Flask Response
|
||||
"""
|
||||
if s3helper.DoesKeyExist(CroppedHash) and not SkipCacheCroppedImage:
|
||||
if ReturnAsJSON:
|
||||
return jsonify({
|
||||
"Final": True,
|
||||
"Url": f"{config.CDN_URL}/{CroppedHash}"
|
||||
})
|
||||
ImageResponse = make_response(redirect(f"{config.CDN_URL}/{CroppedHash}"))
|
||||
ImageResponse.headers['Cache-Control'] = CacheControl
|
||||
return ImageResponse
|
||||
|
||||
if not s3helper.DoesKeyExist(ImageContentHash):
|
||||
if ReturnAsJSON:
|
||||
return jsonify({
|
||||
"Final": False,
|
||||
"Url": "/static/img/placeholder.png"
|
||||
})
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
ImageContent = BytesIO( s3helper.GetFileFromS3(ImageContentHash) )
|
||||
ImageObj = Image.open(ImageContent)
|
||||
ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
|
||||
|
||||
VirtualFile = BytesIO()
|
||||
ImageObj.save(VirtualFile, "PNG")
|
||||
VirtualFile.seek(0)
|
||||
s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
|
||||
|
||||
if ReturnAsJSON:
|
||||
return jsonify({
|
||||
"Final": True,
|
||||
"Url": f"{config.CDN_URL}/{CroppedHash}"
|
||||
})
|
||||
ImageResponse = make_response(redirect(f"{config.CDN_URL}/{CroppedHash}"))
|
||||
ImageResponse.headers['Cache-Control'] = CacheControl
|
||||
return ImageResponse
|
||||
|
||||
@ImageRoute.route("/avatar-thumbnail/image", methods=["GET"])
|
||||
@ImageRoute.route('/Thumbs/Avatar.ashx', methods=['GET'])
|
||||
@ImageRoute.route('/thumbs/avatar.ashx', methods=['GET'])
|
||||
def avatar():
|
||||
userId = request.args.get('userId', default = None, type = int)
|
||||
username = request.args.get('username', default = None, type = str)
|
||||
|
||||
if (userId is None and username is None):
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
TargetX, TargetY = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'x', 'width' ],
|
||||
HeightParametersName = [ 'y', 'height' ],
|
||||
AllowedWidths = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
AllowedHeights = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
MustBeSquare = True,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
if username is not None and userId is None:
|
||||
UserObj : User = User.query.filter_by(username=username).first()
|
||||
if UserObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
userId = UserObj.id
|
||||
|
||||
ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
|
||||
if ThumbnailObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
ContentHash = ThumbnailObj.full_contenthash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetX,
|
||||
TargetHeight = TargetY,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120"
|
||||
)
|
||||
|
||||
@ImageRoute.route('/avatar-thumbnail/json', methods=['GET'])
|
||||
def avatar_json():
|
||||
userId = request.args.get('userId', None, type=int)
|
||||
if userId is None:
|
||||
return jsonify({
|
||||
"Final": False,
|
||||
"Url": "/static/img/placeholder.png"
|
||||
})
|
||||
|
||||
TargetWidth, TargetHeight = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'width', 'x' ],
|
||||
HeightParametersName = [ 'height', 'y' ],
|
||||
AllowedWidths = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
AllowedHeights = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
MustBeSquare = True,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
thumbnail : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
|
||||
if thumbnail is None:
|
||||
return jsonify({
|
||||
"Final": False,
|
||||
"Url": "/static/img/placeholder.png"
|
||||
})
|
||||
ContentHash = thumbnail.full_contenthash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetWidth,
|
||||
TargetHeight = TargetHeight,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120",
|
||||
ReturnAsJSON = True
|
||||
)
|
||||
|
||||
@ImageRoute.route('/headshot-thumbnail/image', methods=['GET'])
|
||||
@ImageRoute.route('/Thumbs/Head.ashx', methods=['GET'])
|
||||
def head():
|
||||
userId = request.args.get('userId', default = None, type = int)
|
||||
if userId is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
TargetX, TargetY = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'x', 'width' ],
|
||||
HeightParametersName = [ 'y', 'height' ],
|
||||
AllowedWidths = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
AllowedHeights = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
|
||||
MustBeSquare = True,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
|
||||
if ThumbnailObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
ContentHash = ThumbnailObj.headshot_contenthash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetX,
|
||||
TargetHeight = TargetY,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120"
|
||||
)
|
||||
|
||||
@ImageRoute.route('/asset-thumbnail/json', methods=['GET'])
|
||||
def asset_json():
|
||||
assetId = request.args.get( 'assetId', None, type=int )
|
||||
if assetId is None:
|
||||
return jsonify({
|
||||
"Final": False,
|
||||
"Url": "/static/img/placeholder.png"
|
||||
})
|
||||
|
||||
TargetWidth, TargetHeight = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'width', 'x' ],
|
||||
HeightParametersName = [ 'height', 'y' ],
|
||||
AllowedWidths = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,360,1280,720],
|
||||
AllowedHeights = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,360,1280,720],
|
||||
MustBeSquare = False,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
thumbnailObj : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=assetId).order_by(AssetThumbnail.asset_version_id.desc()).first()
|
||||
if thumbnailObj is None:
|
||||
return jsonify({
|
||||
"Final": False,
|
||||
"Url": "/static/img/placeholder.png"
|
||||
})
|
||||
if thumbnailObj.moderation_status != 0 or thumbnailObj.asset.moderation_status != 0:
|
||||
if thumbnailObj.moderation_status == 2 or thumbnailObj.asset.moderation_status == 2:
|
||||
return jsonify({
|
||||
"Final": True,
|
||||
"Url": "/static/img/ContentDeleted.png"
|
||||
})
|
||||
return jsonify({
|
||||
"Final": False,
|
||||
"Url": "/static/img/placeholder.png"
|
||||
})
|
||||
|
||||
ContentHash = thumbnailObj.content_hash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetWidth,
|
||||
TargetHeight = TargetHeight,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120",
|
||||
ReturnAsJSON = True
|
||||
)
|
||||
|
||||
@ImageRoute.route('/asset-thumbnail/image', methods=['GET'])
|
||||
@ImageRoute.route('/thumbs/asset.ashx', methods=['GET'])
|
||||
@ImageRoute.route('/Thumbs/Asset.ashx', methods=['GET'])
|
||||
def asset():
|
||||
assetId = request.args.get('assetId', default = None, type = int) or request.args.get('assetid', default = None, type = int)
|
||||
if assetId is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
TargetX, TargetY = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'x', 'width' ],
|
||||
HeightParametersName = [ 'y', 'height' ],
|
||||
AllowedWidths = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,360,1280,720],
|
||||
AllowedHeights = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,36,1280,720],
|
||||
MustBeSquare = False,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
ThumbnailObj : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=assetId).order_by(AssetThumbnail.asset_version_id.desc()).first()
|
||||
if ThumbnailObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
if ThumbnailObj.moderation_status != 0 or ThumbnailObj.asset.moderation_status != 0:
|
||||
if ThumbnailObj.moderation_status == 2 or ThumbnailObj.asset.moderation_status == 2:
|
||||
return redirect("/static/img/ContentDeleted.png")
|
||||
return redirect("/static/img/placeholder.png")
|
||||
ContentHash = ThumbnailObj.content_hash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetX,
|
||||
TargetHeight = TargetY,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120"
|
||||
)
|
||||
|
||||
@ImageRoute.route('/Thumbs/GroupIcon.ashx', methods=['GET'])
|
||||
def groupicon():
|
||||
groupid = request.args.get( key='groupid', default=None, type=int )
|
||||
if groupid is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
TargetX, TargetY = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'x', 'width' ],
|
||||
HeightParametersName = [ 'y', 'height' ],
|
||||
AllowedWidths = [48,180,420,60,100,150,352],
|
||||
AllowedHeights = [48,180,420,60,100,150,352],
|
||||
MustBeSquare = True,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
ThumbnailObj : GroupIcon = GroupIcon.query.filter_by(group_id=groupid).first()
|
||||
if ThumbnailObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
if ThumbnailObj.moderation_status != 0:
|
||||
if ThumbnailObj.moderation_status == 2:
|
||||
return redirect("/static/img/ContentDeleted.png")
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
ContentHash = ThumbnailObj.content_hash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetX,
|
||||
TargetHeight = TargetY,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120"
|
||||
)
|
||||
|
||||
@ImageRoute.route('/Thumbs/GameIcon.ashx', methods=['GET'])
|
||||
@ImageRoute.route('/Thumbs/PlaceIcon.ashx', methods=['GET'])
|
||||
def placeicon():
|
||||
assetId = request.args.get('assetId', default = None, type = int) or request.args.get('assetid', default = None, type = int)
|
||||
if assetId is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
TargetX, TargetY = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'x', 'width' ],
|
||||
HeightParametersName = [ 'y', 'height' ],
|
||||
AllowedWidths = [48,180,420,60,100,150,352,324,576],
|
||||
AllowedHeights = [48,180,420,60,100,150,352,324,576],
|
||||
MustBeSquare = False,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
PlaceIconObj : PlaceIcon = PlaceIcon.query.filter_by(placeid=assetId).first()
|
||||
if PlaceIconObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
if PlaceIconObj.moderation_status != 0 or PlaceIconObj.asset.moderation_status != 0:
|
||||
if PlaceIconObj.moderation_status == 2 or PlaceIconObj.asset.moderation_status == 2:
|
||||
return redirect("/static/img/ContentDeleted.png")
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
ContentHash = PlaceIconObj.contenthash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetX,
|
||||
TargetHeight = TargetY,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120"
|
||||
)
|
||||
|
||||
@ImageRoute.route('/Game/Tools/ThumbnailAsset.ashx', methods=['GET'])
|
||||
def thumbnail_asset():
|
||||
ExpectedFormat : str = request.args.get( key='fmt', default='png', type=str )
|
||||
AssetId : int = request.args.get( key='aid', default=None, type=int )
|
||||
|
||||
if AssetId is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
if ExpectedFormat.lower() != "png":
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
AssetObj : Asset = Asset.query.filter_by(id=AssetId).first()
|
||||
if AssetObj is None:
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
TargetWidth, TargetHeight = HandleResolutionCheck(
|
||||
WidthParametersName = [ 'wd', 'width' ],
|
||||
HeightParametersName = [ 'ht', 'height' ],
|
||||
AllowedWidths = [48,180,420,60,100,150,352,75],
|
||||
AllowedHeights = [48,180,420,60,100,150,352,75],
|
||||
MustBeSquare = True,
|
||||
CanRoundToNearest = True
|
||||
)
|
||||
|
||||
thumbnail : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=AssetId).order_by(AssetThumbnail.asset_version_id.desc()).first()
|
||||
if thumbnail is None:
|
||||
TakeThumbnail(AssetId)
|
||||
return redirect("/static/img/placeholder.png")
|
||||
if thumbnail.moderation_status != 0:
|
||||
if thumbnail.moderation_status == 2 or thumbnail.asset.moderation_status == 2:
|
||||
return redirect("/static/img/ContentDeleted.png")
|
||||
return redirect("/static/img/placeholder.png")
|
||||
|
||||
ContentHash = thumbnail.content_hash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
|
||||
|
||||
return HandleImageResize(
|
||||
ImageContentHash = ContentHash,
|
||||
TargetWidth = TargetWidth,
|
||||
TargetHeight = TargetHeight,
|
||||
CroppedHash = CroppedHash,
|
||||
CacheControl = "max-age=120"
|
||||
)
|
||||
|
||||
import urllib.parse
|
||||
|
||||
@ImageRoute.route("/v1/batch", methods=["POST"])
|
||||
@csrf.exempt
|
||||
def BatchImageRequest():
|
||||
if request.headers.get("Content-Encoding") == "gzip":
|
||||
try:
|
||||
data = gzip.decompress(request.data)
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "message": "Invalid gzip data"}), 400
|
||||
try:
|
||||
JSONData = json.loads(data)
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "message": "Invalid JSON data"}), 400
|
||||
else:
|
||||
JSONData = request.json
|
||||
if JSONData is None:
|
||||
return jsonify({"success": False, "message": "Missing JSON data"}), 400
|
||||
|
||||
# [{'requestId': 'type=GameIcon&id=1&w=128&h=128&filters=', 'targetId': 1, 'type': 'GameIcon', 'size': '128x128', 'isCircular': False}]
|
||||
|
||||
if len(JSONData) > 15:
|
||||
return jsonify({"success": False, "message": "Too many requests"}), 400
|
||||
if len(JSONData) == 0:
|
||||
return jsonify({"data":[]}), 200
|
||||
|
||||
ProcessedRequests = []
|
||||
for RequestObj in JSONData:
|
||||
if "requestId" not in RequestObj or "targetId" not in RequestObj or "type" not in RequestObj or "size" not in RequestObj:
|
||||
continue
|
||||
if RequestObj["type"] not in [ "Avatar", "AvatarHeadShot", "GameIcon", "GameThumbnail", "Asset", "GroupIcon"]:
|
||||
continue
|
||||
|
||||
if "x" not in RequestObj["size"]:
|
||||
continue
|
||||
SplittedSize = RequestObj["size"].split("x")
|
||||
if len(SplittedSize) != 2:
|
||||
continue
|
||||
try:
|
||||
TargetWidth = int(SplittedSize[0])
|
||||
TargetHeight = int(SplittedSize[1])
|
||||
except:
|
||||
continue
|
||||
|
||||
AllowedSizes = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,36,1280,720]
|
||||
TargetWidth = min(AllowedSizes, key=lambda x:abs(x-TargetWidth))
|
||||
TargetHeight = min(AllowedSizes, key=lambda x:abs(x-TargetHeight))
|
||||
|
||||
RequestType = RequestObj["type"]
|
||||
|
||||
if RequestType == "Avatar":
|
||||
ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=RequestObj["targetId"]).first()
|
||||
if ThumbnailObj is None:
|
||||
continue
|
||||
ContentHash = ThumbnailObj.full_contenthash
|
||||
elif RequestType == "AvatarHeadShot":
|
||||
ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=RequestObj["targetId"]).first()
|
||||
if ThumbnailObj is None:
|
||||
continue
|
||||
ContentHash = ThumbnailObj.headshot_contenthash
|
||||
elif RequestType == "GameIcon":
|
||||
PlaceIconObj : PlaceIcon = PlaceIcon.query.filter_by(placeid=RequestObj["targetId"]).first()
|
||||
if PlaceIconObj is None:
|
||||
continue
|
||||
ContentHash = PlaceIconObj.contenthash
|
||||
elif RequestType == "GameThumbnail" or RequestType == "Asset":
|
||||
thumbnailObj : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=RequestObj["targetId"]).order_by(AssetThumbnail.asset_version_id.desc()).first()
|
||||
if thumbnailObj is None:
|
||||
continue
|
||||
if thumbnailObj.moderation_status != 0:
|
||||
continue
|
||||
ContentHash = thumbnailObj.content_hash
|
||||
elif RequestType == "GroupIcon":
|
||||
ThumbnailObj : GroupIcon = GroupIcon.query.filter_by(group_id=RequestObj["targetId"]).first()
|
||||
if ThumbnailObj is None:
|
||||
continue
|
||||
if ThumbnailObj.moderation_status != 0:
|
||||
continue
|
||||
ContentHash = ThumbnailObj.content_hash
|
||||
else:
|
||||
continue
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
|
||||
if not s3helper.DoesKeyExist(CroppedHash):
|
||||
if not s3helper.DoesKeyExist(ContentHash):
|
||||
continue
|
||||
ImageContent = BytesIO( s3helper.GetFileFromS3(ContentHash) )
|
||||
ImageObj = Image.open(ImageContent)
|
||||
ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
|
||||
|
||||
VirtualFile = BytesIO()
|
||||
ImageObj.save(VirtualFile, "PNG")
|
||||
VirtualFile.seek(0)
|
||||
s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
|
||||
ProcessedRequests.append({
|
||||
"requestId": RequestObj["requestId"],
|
||||
"targetId": RequestObj["targetId"],
|
||||
"state": "Completed",
|
||||
"imageUrl": f"{config.CDN_URL}/{CroppedHash}",
|
||||
"version": None
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"data": ProcessedRequests
|
||||
})
|
||||
|
||||
@ImageRoute.route("/v1/users/avatar-headshot", methods=["GET"])
|
||||
def multi_avatar_headshot():
|
||||
userIdsCSV = request.args.get('userIds', default = None, type = str)
|
||||
if userIdsCSV is None:
|
||||
return jsonify( { "errors": [ { "code": 4, "message": "The requested Ids are invalid, of an invalid type or missing." } ] } ), 400
|
||||
|
||||
userIds = userIdsCSV.split(",")
|
||||
if len(userIds) > 100:
|
||||
return jsonify( { "errors": [ { "code": 1, "message": "There are too many requested Ids." } ] } ), 400
|
||||
|
||||
requestedSize = request.args.get('size', default = "48x48", type = str)
|
||||
|
||||
if "x" not in requestedSize:
|
||||
return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
|
||||
|
||||
SplittedSize = requestedSize.split("x")
|
||||
if len(SplittedSize) != 2:
|
||||
return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
|
||||
|
||||
try:
|
||||
TargetWidth = int(SplittedSize[0])
|
||||
TargetHeight = int(SplittedSize[1])
|
||||
|
||||
AllowedSizes = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,36,1280,720]
|
||||
TargetWidth = min(AllowedSizes, key=lambda x:abs(x-TargetWidth))
|
||||
TargetHeight = min(AllowedSizes, key=lambda x:abs(x-TargetHeight))
|
||||
except:
|
||||
return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
|
||||
|
||||
ProcessedRequests = []
|
||||
for userId in userIds:
|
||||
try:
|
||||
userId = int(userId)
|
||||
except:
|
||||
continue
|
||||
ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
|
||||
if ThumbnailObj is None:
|
||||
continue
|
||||
ContentHash = ThumbnailObj.headshot_contenthash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
|
||||
if not s3helper.DoesKeyExist(CroppedHash):
|
||||
if not s3helper.DoesKeyExist(ContentHash):
|
||||
continue
|
||||
ImageContent = BytesIO( s3helper.GetFileFromS3(ContentHash) )
|
||||
ImageObj = Image.open(ImageContent)
|
||||
ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
|
||||
|
||||
VirtualFile = BytesIO()
|
||||
ImageObj.save(VirtualFile, "PNG")
|
||||
VirtualFile.seek(0)
|
||||
s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
|
||||
ProcessedRequests.append({
|
||||
"targetId": userId,
|
||||
"state": "Completed",
|
||||
#"imageUrl": f"{config.CDN_URL}/{CroppedHash}", # 2020 Does not allow things like cdn.syntax.eco to be used directly in the "Texture" property so we have to redirect them to an allowed route on the www. domain
|
||||
"imageUrl": f"{config.BaseURL}/headshot-thumbnail/image?userId={userId}&x={TargetWidth}&y={TargetHeight}",
|
||||
"version": "1"
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"data": ProcessedRequests
|
||||
}), 200
|
||||
|
||||
@ImageRoute.route("/v1/games/icons", methods=["GET"])
|
||||
def get_game_icons():
|
||||
universeIdsCSV = request.args.get('universeIds', default = None, type = str)
|
||||
if universeIdsCSV is None:
|
||||
return jsonify( { "errors": [ { "code": 4, "message": "The requested Ids are invalid, of an invalid type or missing." } ] } ), 400
|
||||
universeIdsList = universeIdsCSV.split(",")
|
||||
if len(universeIdsList) > 100:
|
||||
return jsonify( { "errors": [ { "code": 1, "message": "There are too many requested Ids." } ] } ), 400
|
||||
|
||||
requestedSize = request.args.get('size', default = "50x50", type = str)
|
||||
if "x" not in requestedSize:
|
||||
return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
|
||||
|
||||
SplittedSize = requestedSize.split("x")
|
||||
if len(SplittedSize) != 2:
|
||||
return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
|
||||
|
||||
try:
|
||||
TargetWidth = int(SplittedSize[0])
|
||||
TargetHeight = int(SplittedSize[1])
|
||||
|
||||
AllowedSizes = [50, 128, 150, 256, 420, 512]
|
||||
TargetWidth = min(AllowedSizes, key=lambda x:abs(x-TargetWidth))
|
||||
TargetHeight = min(AllowedSizes, key=lambda x:abs(x-TargetHeight))
|
||||
except:
|
||||
return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
|
||||
|
||||
ProcessedRequests = []
|
||||
for universeId in universeIdsList:
|
||||
try:
|
||||
universeId = int(universeId)
|
||||
except:
|
||||
continue
|
||||
PlaceIconObj : PlaceIcon = PlaceIcon.query.filter_by(placeid=universeId).first()
|
||||
if PlaceIconObj is None:
|
||||
continue
|
||||
ContentHash = PlaceIconObj.contenthash
|
||||
CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
|
||||
if not s3helper.DoesKeyExist(CroppedHash):
|
||||
if not s3helper.DoesKeyExist(ContentHash):
|
||||
continue
|
||||
ImageContent = BytesIO( s3helper.GetFileFromS3(ContentHash) )
|
||||
ImageObj = Image.open(ImageContent)
|
||||
ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
|
||||
|
||||
VirtualFile = BytesIO()
|
||||
ImageObj.save(VirtualFile, "PNG")
|
||||
VirtualFile.seek(0)
|
||||
s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
|
||||
ProcessedRequests.append({
|
||||
"targetId": universeId,
|
||||
"state": "Completed",
|
||||
#"imageUrl": f"{config.CDN_URL}/{CroppedHash}"
|
||||
"imageUrl": f"{config.BaseURL}/Thumbs/GameIcon.ashx?assetId={str(universeId)}&x={str(TargetWidth)}&y={str(TargetHeight)}"
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"data": ProcessedRequests
|
||||
}), 200
|
||||
Reference in New Issue
Block a user