forked from indymedia/epicyon
Functions for image attachments
parent
050ce03ad0
commit
83b172d046
|
@ -206,7 +206,7 @@ Even if Eve has an account on Alice's instance this won't help her very much unl
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
sudo pacman -S tor python-pip python-pysocks python-pycryptodome python-beautifulsoup4 imagemagick
|
sudo pacman -S tor python-pip python-pysocks python-pycryptodome python-beautifulsoup4 imagemagick python-pillow python-numpy
|
||||||
sudo pip install commentjson
|
sudo pip install commentjson
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,254 @@
|
||||||
|
"""
|
||||||
|
Copyright (c) 2019 Lorenz Diener
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
* You and any organization you work for may not promote white supremacy, hate
|
||||||
|
speech and homo- or transphobia - this license is void if you do.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
https://github.com/halcy/blurhash-python
|
||||||
|
|
||||||
|
Pure python blurhash decoder with no additional dependencies, for
|
||||||
|
both de- and encoding.
|
||||||
|
|
||||||
|
Very close port of the original Swift implementation by Dag Ågren.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
# Alphabet for base 83
|
||||||
|
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"
|
||||||
|
alphabet_values = dict(zip(alphabet, range(len(alphabet))))
|
||||||
|
|
||||||
|
def base83_decode(base83_str):
|
||||||
|
"""
|
||||||
|
Decodes a base83 string, as used in blurhash, to an integer.
|
||||||
|
"""
|
||||||
|
value = 0
|
||||||
|
for base83_char in base83_str:
|
||||||
|
value = value * 83 + alphabet_values[base83_char]
|
||||||
|
return value
|
||||||
|
|
||||||
|
def base83_encode(value, length):
|
||||||
|
"""
|
||||||
|
Decodes an integer to a base83 string, as used in blurhash.
|
||||||
|
|
||||||
|
Length is how long the resulting string should be. Will complain
|
||||||
|
if the specified length is too short.
|
||||||
|
"""
|
||||||
|
if int(value) // (83 ** (length)) != 0:
|
||||||
|
raise ValueError("Specified length is too short to encode given value.")
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
for i in range(1, length + 1):
|
||||||
|
digit = int(value) // (83 ** (length - i)) % 83
|
||||||
|
result += alphabet[int(digit)]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def srgb_to_linear(value):
|
||||||
|
"""
|
||||||
|
srgb 0-255 integer to linear 0.0-1.0 floating point conversion.
|
||||||
|
"""
|
||||||
|
value = float(value) / 255.0
|
||||||
|
if value <= 0.04045:
|
||||||
|
return value / 12.92
|
||||||
|
return math.pow((value + 0.055) / 1.055, 2.4)
|
||||||
|
|
||||||
|
def sign_pow(value, exp):
|
||||||
|
"""
|
||||||
|
Sign-preserving exponentiation.
|
||||||
|
"""
|
||||||
|
return math.copysign(math.pow(abs(value), exp), value)
|
||||||
|
|
||||||
|
def linear_to_srgb(value):
|
||||||
|
"""
|
||||||
|
linear 0.0-1.0 floating point to srgb 0-255 integer conversion.
|
||||||
|
"""
|
||||||
|
value = max(0.0, min(1.0, value))
|
||||||
|
if value <= 0.0031308:
|
||||||
|
return int(value * 12.92 * 255 + 0.5)
|
||||||
|
return int((1.055 * math.pow(value, 1 / 2.4) - 0.055) * 255 + 0.5)
|
||||||
|
|
||||||
|
def blurhash_components(blurhash):
|
||||||
|
"""
|
||||||
|
Decodes and returns the number of x and y components in the given blurhash.
|
||||||
|
"""
|
||||||
|
if len(blurhash) < 6:
|
||||||
|
raise ValueError("BlurHash must be at least 6 characters long.")
|
||||||
|
|
||||||
|
# Decode metadata
|
||||||
|
size_info = base83_decode(blurhash[0])
|
||||||
|
size_y = int(size_info / 9) + 1
|
||||||
|
size_x = (size_info % 9) + 1
|
||||||
|
|
||||||
|
return size_x, size_y
|
||||||
|
|
||||||
|
def blurhash_decode(blurhash, width, height, punch = 1.0, linear = False):
|
||||||
|
"""
|
||||||
|
Decodes the given blurhash to an image of the specified size.
|
||||||
|
|
||||||
|
Returns the resulting image a list of lists of 3-value sRGB 8 bit integer
|
||||||
|
lists. Set linear to True if you would prefer to get linear floating point
|
||||||
|
RGB back.
|
||||||
|
|
||||||
|
The punch parameter can be used to de- or increase the contrast of the
|
||||||
|
resulting image.
|
||||||
|
|
||||||
|
As per the original implementation it is suggested to only decode
|
||||||
|
to a relatively small size and then scale the result up, as it
|
||||||
|
basically looks the same anyways.
|
||||||
|
"""
|
||||||
|
if len(blurhash) < 6:
|
||||||
|
raise ValueError("BlurHash must be at least 6 characters long.")
|
||||||
|
|
||||||
|
# Decode metadata
|
||||||
|
size_info = base83_decode(blurhash[0])
|
||||||
|
size_y = int(size_info / 9) + 1
|
||||||
|
size_x = (size_info % 9) + 1
|
||||||
|
|
||||||
|
quant_max_value = base83_decode(blurhash[1])
|
||||||
|
real_max_value = (float(quant_max_value + 1) / 166.0) * punch
|
||||||
|
|
||||||
|
# Make sure we at least have the right number of characters
|
||||||
|
if len(blurhash) != 4 + 2 * size_x * size_y:
|
||||||
|
raise ValueError("Invalid BlurHash length.")
|
||||||
|
|
||||||
|
# Decode DC component
|
||||||
|
dc_value = base83_decode(blurhash[2:6])
|
||||||
|
colours = [(
|
||||||
|
srgb_to_linear(dc_value >> 16),
|
||||||
|
srgb_to_linear((dc_value >> 8) & 255),
|
||||||
|
srgb_to_linear(dc_value & 255)
|
||||||
|
)]
|
||||||
|
|
||||||
|
# Decode AC components
|
||||||
|
for component in range(1, size_x * size_y):
|
||||||
|
ac_value = base83_decode(blurhash[4+component*2:4+(component+1)*2])
|
||||||
|
colours.append((
|
||||||
|
sign_pow((float(int(ac_value / (19 * 19))) - 9.0) / 9.0, 2.0) * real_max_value,
|
||||||
|
sign_pow((float(int(ac_value / 19) % 19) - 9.0) / 9.0, 2.0) * real_max_value,
|
||||||
|
sign_pow((float(ac_value % 19) - 9.0) / 9.0, 2.0) * real_max_value
|
||||||
|
))
|
||||||
|
|
||||||
|
# Return image RGB values, as a list of lists of lists,
|
||||||
|
# consumable by something like numpy or PIL.
|
||||||
|
pixels = []
|
||||||
|
for y in range(height):
|
||||||
|
pixel_row = []
|
||||||
|
for x in range(width):
|
||||||
|
pixel = [0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
for j in range(size_y):
|
||||||
|
for i in range(size_x):
|
||||||
|
basis = math.cos(math.pi * float(x) * float(i) / float(width)) * \
|
||||||
|
math.cos(math.pi * float(y) * float(j) / float(height))
|
||||||
|
colour = colours[i + j * size_x]
|
||||||
|
pixel[0] += colour[0] * basis
|
||||||
|
pixel[1] += colour[1] * basis
|
||||||
|
pixel[2] += colour[2] * basis
|
||||||
|
if linear == False:
|
||||||
|
pixel_row.append([
|
||||||
|
linear_to_srgb(pixel[0]),
|
||||||
|
linear_to_srgb(pixel[1]),
|
||||||
|
linear_to_srgb(pixel[2]),
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
pixel_row.append(pixel)
|
||||||
|
pixels.append(pixel_row)
|
||||||
|
return pixels
|
||||||
|
|
||||||
|
def blurhash_encode(image, components_x = 4, components_y = 4, linear = False):
|
||||||
|
"""
|
||||||
|
Calculates the blurhash for an image using the given x and y component counts.
|
||||||
|
|
||||||
|
Image should be a 3-dimensional array, with the first dimension being y, the second
|
||||||
|
being x, and the third being the three rgb components that are assumed to be 0-255
|
||||||
|
srgb integers (incidentally, this is the format you will get from a PIL RGB image).
|
||||||
|
|
||||||
|
You can also pass in already linear data - to do this, set linear to True. This is
|
||||||
|
useful if you want to encode a version of your image resized to a smaller size (which
|
||||||
|
you should ideally do in linear colour).
|
||||||
|
"""
|
||||||
|
if components_x < 1 or components_x > 9 or components_y < 1 or components_y > 9:
|
||||||
|
raise ValueError("x and y component counts must be between 1 and 9 inclusive.")
|
||||||
|
height = float(len(image))
|
||||||
|
width = float(len(image[0]))
|
||||||
|
|
||||||
|
# Convert to linear if neeeded
|
||||||
|
image_linear = []
|
||||||
|
if linear == False:
|
||||||
|
for y in range(int(height)):
|
||||||
|
image_linear_line = []
|
||||||
|
for x in range(int(width)):
|
||||||
|
image_linear_line.append([
|
||||||
|
srgb_to_linear(image[y][x][0]),
|
||||||
|
srgb_to_linear(image[y][x][1]),
|
||||||
|
srgb_to_linear(image[y][x][2])
|
||||||
|
])
|
||||||
|
image_linear.append(image_linear_line)
|
||||||
|
else:
|
||||||
|
image_linear = image
|
||||||
|
|
||||||
|
# Calculate components
|
||||||
|
components = []
|
||||||
|
max_ac_component = 0.0
|
||||||
|
for j in range(components_y):
|
||||||
|
for i in range(components_x):
|
||||||
|
norm_factor = 1.0 if (i == 0 and j == 0) else 2.0
|
||||||
|
component = [0.0, 0.0, 0.0]
|
||||||
|
for y in range(int(height)):
|
||||||
|
for x in range(int(width)):
|
||||||
|
basis = norm_factor * math.cos(math.pi * float(i) * float(x) / width) * \
|
||||||
|
math.cos(math.pi * float(j) * float(y) / height)
|
||||||
|
component[0] += basis * image_linear[y][x][0]
|
||||||
|
component[1] += basis * image_linear[y][x][1]
|
||||||
|
component[2] += basis * image_linear[y][x][2]
|
||||||
|
|
||||||
|
component[0] /= (width * height)
|
||||||
|
component[1] /= (width * height)
|
||||||
|
component[2] /= (width * height)
|
||||||
|
components.append(component)
|
||||||
|
|
||||||
|
if not (i == 0 and j == 0):
|
||||||
|
max_ac_component = max(max_ac_component, abs(component[0]), abs(component[1]), abs(component[2]))
|
||||||
|
|
||||||
|
# Encode components
|
||||||
|
dc_value = (linear_to_srgb(components[0][0]) << 16) + \
|
||||||
|
(linear_to_srgb(components[0][1]) << 8) + \
|
||||||
|
linear_to_srgb(components[0][2])
|
||||||
|
|
||||||
|
quant_max_ac_component = int(max(0, min(82, math.floor(max_ac_component * 166 - 0.5))))
|
||||||
|
ac_component_norm_factor = float(quant_max_ac_component + 1) / 166.0
|
||||||
|
|
||||||
|
ac_values = []
|
||||||
|
for r, g, b in components[1:]:
|
||||||
|
ac_values.append(
|
||||||
|
int(max(0.0, min(18.0, math.floor(sign_pow(r / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 * 19 + \
|
||||||
|
int(max(0.0, min(18.0, math.floor(sign_pow(g / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 + \
|
||||||
|
int(max(0.0, min(18.0, math.floor(sign_pow(b / ac_component_norm_factor, 0.5) * 9.0 + 9.5))))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build final blurhash
|
||||||
|
blurhash = ""
|
||||||
|
blurhash += base83_encode((components_x - 1) + (components_y - 1) * 9, 1)
|
||||||
|
blurhash += base83_encode(quant_max_ac_component, 1)
|
||||||
|
blurhash += base83_encode(dc_value, 4)
|
||||||
|
for ac_value in ac_values:
|
||||||
|
blurhash += base83_encode(ac_value, 2)
|
||||||
|
|
||||||
|
return blurhash
|
18
epicyon.py
18
epicyon.py
|
@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
|
||||||
__email__ = "bob@freedombone.net"
|
__email__ = "bob@freedombone.net"
|
||||||
__status__ = "Production"
|
__status__ = "Production"
|
||||||
|
|
||||||
|
|
||||||
from person import createPerson
|
from person import createPerson
|
||||||
from person import createSharedInbox
|
from person import createSharedInbox
|
||||||
from person import createCapabilitiesInbox
|
from person import createCapabilitiesInbox
|
||||||
|
@ -442,6 +443,7 @@ if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
|
||||||
createPerson(baseDir,nickname,domain,port,httpPrefix,True,adminPassword)
|
createPerson(baseDir,nickname,domain,port,httpPrefix,True,adminPassword)
|
||||||
|
|
||||||
if args.testdata:
|
if args.testdata:
|
||||||
|
useBlurhash=False
|
||||||
nickname='testuser567'
|
nickname='testuser567'
|
||||||
print('Generating some test data for user: '+nickname)
|
print('Generating some test data for user: '+nickname)
|
||||||
createPerson(baseDir,nickname,domain,port,httpPrefix,True,'likewhateveryouwantscoob')
|
createPerson(baseDir,nickname,domain,port,httpPrefix,True,'likewhateveryouwantscoob')
|
||||||
|
@ -449,14 +451,14 @@ if args.testdata:
|
||||||
deleteAllPosts(baseDir,nickname,domain,'outbox')
|
deleteAllPosts(baseDir,nickname,domain,'outbox')
|
||||||
followPerson(baseDir,nickname,domain,'admin',domain,federationList,True)
|
followPerson(baseDir,nickname,domain,'admin',domain,federationList,True)
|
||||||
followerOfPerson(baseDir,nickname,domain,'admin',domain,federationList,True)
|
followerOfPerson(baseDir,nickname,domain,'admin',domain,federationList,True)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a test, man",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a test, man",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more milkshakes",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more milkshakes",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Getting kinda spooky around here",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Getting kinda spooky around here",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"And they would have gotten away with it too if it wasn't for those pesky hackers",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"And they would have gotten away with it too if it wasn't for those pesky hackers",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,None,None,useBlurhash)
|
||||||
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False)
|
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,None,None,useBlurhash)
|
||||||
|
|
||||||
runDaemon(baseDir,domain,port,httpPrefix,federationList, \
|
runDaemon(baseDir,domain,port,httpPrefix,federationList, \
|
||||||
args.noreply,args.nolike,args.nopics, \
|
args.noreply,args.nolike,args.nopics, \
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
__filename__ = "media.py"
|
||||||
|
__author__ = "Bob Mottram"
|
||||||
|
__license__ = "AGPL3+"
|
||||||
|
__version__ = "0.0.1"
|
||||||
|
__maintainer__ = "Bob Mottram"
|
||||||
|
__email__ = "bob@freedombone.net"
|
||||||
|
__status__ = "Production"
|
||||||
|
|
||||||
|
from blurhash import blurhash_encode as blurencode
|
||||||
|
from PIL import Image
|
||||||
|
import numpy
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import commentjson
|
||||||
|
import datetime
|
||||||
|
from auth import createPassword
|
||||||
|
from shutil import copyfile
|
||||||
|
|
||||||
|
def getImageHash(imageFilename: str):
|
||||||
|
return blurencode(numpy.array(Image.open("img/logo.png").convert("RGB")))
|
||||||
|
|
||||||
|
def isImage(imageFilename: str) -> bool:
|
||||||
|
if imageFilename.endswith('.png') or \
|
||||||
|
imageFilename.endswith('.jpg') or \
|
||||||
|
imageFilename.endswith('.gif'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def createMediaPath(baseDir: str,weeksSinceEpoch: int) -> None:
|
||||||
|
if not os.path.isdir(baseDir+'/media'):
|
||||||
|
os.mkdir(baseDir+'/media')
|
||||||
|
if not os.path.isdir(baseDir+'/media/'+str(weeksSinceEpoch)):
|
||||||
|
os.mkdir(baseDir+'/media/'+str(weeksSinceEpoch))
|
||||||
|
|
||||||
|
def attachImage(baseDir: str,httpPrefix: str,domain: str,port: int, \
|
||||||
|
postJson: {},imageFilename: str,description: str, \
|
||||||
|
useBlurhash: bool) -> {}:
|
||||||
|
"""Attaches an image to a json object post
|
||||||
|
The description can be None
|
||||||
|
Blurhash is optional, since low power systems may take a long time to calculate it
|
||||||
|
"""
|
||||||
|
if not isImage(imageFilename):
|
||||||
|
return postJson
|
||||||
|
|
||||||
|
mediaType='image/png'
|
||||||
|
fileExtension='png'
|
||||||
|
if imageFilename.endswith('.jpg'):
|
||||||
|
mediaType='image/jpeg'
|
||||||
|
fileExtension='jpg'
|
||||||
|
if imageFilename.endswith('.gif'):
|
||||||
|
mediaType='image/gif'
|
||||||
|
fileExtension='gif'
|
||||||
|
|
||||||
|
if port!=80 and port!=443:
|
||||||
|
if ':' not in domain:
|
||||||
|
domain=domain+':'+str(port)
|
||||||
|
|
||||||
|
currTime=datetime.datetime.utcnow()
|
||||||
|
weeksSinceEpoch=(currTime - datetime.datetime(1970,1,1)).days/7
|
||||||
|
createMediaPath(baseDir,weeksSinceEpoch)
|
||||||
|
mediaPath='media/'+str(weeksSinceEpoch)+'/'+createPassword(32)+'.'+fileExtension
|
||||||
|
mediaFilename=baseDir+'/'+mediaPath
|
||||||
|
|
||||||
|
attachmentJson={
|
||||||
|
'mediaType': mediaType,
|
||||||
|
'name': description,
|
||||||
|
'type': 'Document',
|
||||||
|
'url': httpPrefix+'://'+domain+'/'+mediaPath
|
||||||
|
}
|
||||||
|
if useBlurhash:
|
||||||
|
attachmentJson['blurhash']=getImageHash(imageFilename)
|
||||||
|
postJson['attachment']=[attachmentJson]
|
||||||
|
|
||||||
|
copyfile(imageFilename,mediaFilename)
|
||||||
|
|
||||||
|
return postJson
|
||||||
|
|
||||||
|
def removeAttachment(baseDir: str,httpPrefix: str,domain: str,postJson: {}):
|
||||||
|
if not postJson.get('attachment'):
|
||||||
|
return
|
||||||
|
if not postJson['attachment'][0].get('url'):
|
||||||
|
return
|
||||||
|
if port!=80 and port!=443:
|
||||||
|
if ':' not in domain:
|
||||||
|
domain=domain+':'+str(port)
|
||||||
|
attachmentUrl=postJson['attachment'][0]['url']
|
||||||
|
if not attachmentUrl:
|
||||||
|
return
|
||||||
|
mediaFilename=baseDir+'/'+attachmentUrl.replace(httpPrefix+'://'+domain+'/','')
|
||||||
|
if os.path.isfile(mediaFilename):
|
||||||
|
os.remove(mediaFilename)
|
||||||
|
postJson['attachment']=[]
|
28
posts.py
28
posts.py
|
@ -34,6 +34,7 @@ from utils import getNicknameFromActor
|
||||||
from utils import getDomainFromActor
|
from utils import getDomainFromActor
|
||||||
from capabilities import getOcapFilename
|
from capabilities import getOcapFilename
|
||||||
from capabilities import capabilitiesUpdate
|
from capabilities import capabilitiesUpdate
|
||||||
|
from media import attachImage
|
||||||
try:
|
try:
|
||||||
from BeautifulSoup import BeautifulSoup
|
from BeautifulSoup import BeautifulSoup
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -321,7 +322,8 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \
|
||||||
|
|
||||||
def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
toUrl: str, ccUrl: str, httpPrefix: str, content: str, \
|
toUrl: str, ccUrl: str, httpPrefix: str, content: str, \
|
||||||
followersOnly: bool, saveToFile: bool, clientToServer: bool, \
|
followersOnly: bool, saveToFile: bool, clientToServer: bool,
|
||||||
|
attachImageFilename: str,imageDescription: str,useBlurhash: bool, \
|
||||||
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
||||||
"""Creates a message
|
"""Creates a message
|
||||||
"""
|
"""
|
||||||
|
@ -396,6 +398,11 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if attachImageFilename:
|
||||||
|
newPost['object']= \
|
||||||
|
attachImage(baseDir,httpPrefix,domain,port, \
|
||||||
|
postJson['object'],attachImageFilename, \
|
||||||
|
imageDescription,useBlurhash)
|
||||||
else:
|
else:
|
||||||
newPost = {
|
newPost = {
|
||||||
'id': newPostId,
|
'id': newPostId,
|
||||||
|
@ -417,8 +424,21 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
},
|
},
|
||||||
'attachment': [],
|
'attachment': [],
|
||||||
'tag': [],
|
'tag': [],
|
||||||
'replies': {}
|
'replies': {
|
||||||
|
'id': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies',
|
||||||
|
'type': 'Collection',
|
||||||
|
'first': {
|
||||||
|
'type': 'CollectionPage',
|
||||||
|
'partOf': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies',
|
||||||
|
'items': []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if attachImageFilename:
|
||||||
|
newPost= \
|
||||||
|
attachImage(baseDir,httpPrefix,domain,port, \
|
||||||
|
postJson,attachImageFilename, \
|
||||||
|
imageDescription,useBlurhash)
|
||||||
if ccUrl:
|
if ccUrl:
|
||||||
if len(ccUrl)>0:
|
if len(ccUrl)>0:
|
||||||
newPost['cc']=ccUrl
|
newPost['cc']=ccUrl
|
||||||
|
@ -490,6 +510,7 @@ def createPublicPost(baseDir: str,
|
||||||
nickname: str, domain: str, port: int,httpPrefix: str, \
|
nickname: str, domain: str, port: int,httpPrefix: str, \
|
||||||
content: str, followersOnly: bool, saveToFile: bool,
|
content: str, followersOnly: bool, saveToFile: bool,
|
||||||
clientToServer: bool,\
|
clientToServer: bool,\
|
||||||
|
attachImageFilename: str,imageDescription: str,useBlurhash: bool, \
|
||||||
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
||||||
"""Public post to the outbox
|
"""Public post to the outbox
|
||||||
"""
|
"""
|
||||||
|
@ -498,6 +519,7 @@ def createPublicPost(baseDir: str,
|
||||||
httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \
|
httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \
|
||||||
httpPrefix, content, followersOnly, saveToFile, \
|
httpPrefix, content, followersOnly, saveToFile, \
|
||||||
clientToServer, \
|
clientToServer, \
|
||||||
|
attachImageFilename,imageDescription,useBlurhash, \
|
||||||
inReplyTo, inReplyToAtomUri, subject)
|
inReplyTo, inReplyToAtomUri, subject)
|
||||||
|
|
||||||
def threadSendPost(session,postJsonObject: {},federationList: [],\
|
def threadSendPost(session,postJsonObject: {},federationList: [],\
|
||||||
|
@ -536,6 +558,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
toNickname: str, toDomain: str, toPort: int, cc: str, \
|
toNickname: str, toDomain: str, toPort: int, cc: str, \
|
||||||
httpPrefix: str, content: str, followersOnly: bool, \
|
httpPrefix: str, content: str, followersOnly: bool, \
|
||||||
saveToFile: bool, clientToServer: bool, \
|
saveToFile: bool, clientToServer: bool, \
|
||||||
|
attachImageFilename: str,imageDescription: str,useBlurhash: bool, \
|
||||||
federationList: [],\
|
federationList: [],\
|
||||||
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \
|
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \
|
||||||
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int:
|
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int:
|
||||||
|
@ -585,6 +608,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
createPostBase(baseDir,nickname,domain,port, \
|
createPostBase(baseDir,nickname,domain,port, \
|
||||||
toPersonId,cc,httpPrefix,content, \
|
toPersonId,cc,httpPrefix,content, \
|
||||||
followersOnly,saveToFile,clientToServer, \
|
followersOnly,saveToFile,clientToServer, \
|
||||||
|
attachImageFilename,imageDescription,useBlurhash, \
|
||||||
inReplyTo,inReplyToAtomUri,subject)
|
inReplyTo,inReplyToAtomUri,subject)
|
||||||
|
|
||||||
# get the senders private key
|
# get the senders private key
|
||||||
|
|
27
tests.py
27
tests.py
|
@ -131,6 +131,7 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],hasFoll
|
||||||
nopics=False
|
nopics=False
|
||||||
noannounce=False
|
noannounce=False
|
||||||
cw=False
|
cw=False
|
||||||
|
useBlurhash=True
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password)
|
||||||
deleteAllPosts(path,nickname,domain,'inbox')
|
deleteAllPosts(path,nickname,domain,'inbox')
|
||||||
deleteAllPosts(path,nickname,domain,'outbox')
|
deleteAllPosts(path,nickname,domain,'outbox')
|
||||||
|
@ -138,9 +139,9 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],hasFoll
|
||||||
followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True)
|
followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True)
|
||||||
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True)
|
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True)
|
||||||
if hasPosts:
|
if hasPosts:
|
||||||
createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer)
|
createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer,None,None,useBlurhash)
|
||||||
createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer)
|
createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer,None,None,useBlurhash)
|
||||||
createPublicPost(path,nickname, domain, port,httpPrefix, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True, clientToServer)
|
createPublicPost(path,nickname, domain, port,httpPrefix, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True, clientToServer,None,None,useBlurhash)
|
||||||
global testServerAliceRunning
|
global testServerAliceRunning
|
||||||
testServerAliceRunning = True
|
testServerAliceRunning = True
|
||||||
print('Server running: Alice')
|
print('Server running: Alice')
|
||||||
|
@ -162,6 +163,7 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],hasFollow
|
||||||
nopics=False
|
nopics=False
|
||||||
noannounce=False
|
noannounce=False
|
||||||
cw=False
|
cw=False
|
||||||
|
useBlurhash=False
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password)
|
||||||
deleteAllPosts(path,nickname,domain,'inbox')
|
deleteAllPosts(path,nickname,domain,'inbox')
|
||||||
deleteAllPosts(path,nickname,domain,'outbox')
|
deleteAllPosts(path,nickname,domain,'outbox')
|
||||||
|
@ -169,9 +171,9 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],hasFollow
|
||||||
followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True)
|
followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True)
|
||||||
followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True)
|
followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True)
|
||||||
if hasPosts:
|
if hasPosts:
|
||||||
createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer)
|
createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer,None,None,useBlurhash)
|
||||||
createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer)
|
createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer,None,None,useBlurhash)
|
||||||
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer)
|
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer,None,None,useBlurhash)
|
||||||
global testServerBobRunning
|
global testServerBobRunning
|
||||||
testServerBobRunning = True
|
testServerBobRunning = True
|
||||||
print('Server running: Bob')
|
print('Server running: Bob')
|
||||||
|
@ -257,12 +259,12 @@ def testPostMessageBetweenServers():
|
||||||
ccUrl=None
|
ccUrl=None
|
||||||
alicePersonCache={}
|
alicePersonCache={}
|
||||||
aliceCachedWebfingers={}
|
aliceCachedWebfingers={}
|
||||||
|
useBlurhash=False
|
||||||
# nothing in Alice's outbox
|
# nothing in Alice's outbox
|
||||||
outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
|
outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
|
||||||
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
|
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
|
||||||
|
|
||||||
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer,None,None,useBlurhash, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
||||||
print('sendResult: '+str(sendResult))
|
print('sendResult: '+str(sendResult))
|
||||||
|
|
||||||
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
|
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
|
||||||
|
@ -469,7 +471,8 @@ def testFollowBetweenServers():
|
||||||
eveCachedWebfingers={}
|
eveCachedWebfingers={}
|
||||||
eveSendThreads=[]
|
eveSendThreads=[]
|
||||||
evePostLog=[]
|
evePostLog=[]
|
||||||
sendResult = sendPost(sessionEve,eveDir,'eve', eveDomain, evePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Eve message', followersOnly, saveToFile, clientToServer, federationList, eveSendThreads, evePostLog, eveCachedWebfingers,evePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
useBlurhash=False
|
||||||
|
sendResult = sendPost(sessionEve,eveDir,'eve', eveDomain, evePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Eve message', followersOnly, saveToFile, clientToServer,None,None,useBlurhash, federationList, eveSendThreads, evePostLog, eveCachedWebfingers,evePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
||||||
print('sendResult: '+str(sendResult))
|
print('sendResult: '+str(sendResult))
|
||||||
|
|
||||||
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
|
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
|
||||||
|
@ -495,7 +498,8 @@ def testFollowBetweenServers():
|
||||||
aliceCachedWebfingers={}
|
aliceCachedWebfingers={}
|
||||||
aliceSendThreads=[]
|
aliceSendThreads=[]
|
||||||
alicePostLog=[]
|
alicePostLog=[]
|
||||||
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Alice message', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
useBlurhash=False
|
||||||
|
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Alice message', followersOnly, saveToFile, clientToServer,None,None,useBlurhash, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
||||||
print('sendResult: '+str(sendResult))
|
print('sendResult: '+str(sendResult))
|
||||||
|
|
||||||
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
|
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
|
||||||
|
@ -783,6 +787,7 @@ def testCreatePerson():
|
||||||
port=80
|
port=80
|
||||||
httpPrefix='https'
|
httpPrefix='https'
|
||||||
clientToServer=False
|
clientToServer=False
|
||||||
|
useBlurhash=False
|
||||||
baseDir=currDir+'/.tests_createperson'
|
baseDir=currDir+'/.tests_createperson'
|
||||||
if os.path.isdir(baseDir):
|
if os.path.isdir(baseDir):
|
||||||
shutil.rmtree(baseDir)
|
shutil.rmtree(baseDir)
|
||||||
|
@ -797,7 +802,7 @@ def testCreatePerson():
|
||||||
setBio(baseDir,nickname,domain,'Randomly roaming in your backyard')
|
setBio(baseDir,nickname,domain,'Randomly roaming in your backyard')
|
||||||
archivePosts(nickname,domain,baseDir,'inbox',4)
|
archivePosts(nickname,domain,baseDir,'inbox',4)
|
||||||
archivePosts(nickname,domain,baseDir,'outbox',4)
|
archivePosts(nickname,domain,baseDir,'outbox',4)
|
||||||
createPublicPost(baseDir,nickname, domain, port,httpPrefix, "G'day world!", False, True, clientToServer, None, None, 'Not suitable for Vogons')
|
createPublicPost(baseDir,nickname, domain, port,httpPrefix, "G'day world!", False, True, clientToServer,None,None,useBlurhash, None, None, 'Not suitable for Vogons')
|
||||||
|
|
||||||
os.chdir(currDir)
|
os.chdir(currDir)
|
||||||
shutil.rmtree(baseDir)
|
shutil.rmtree(baseDir)
|
||||||
|
|
Loading…
Reference in New Issue