Support AVIF image format

See https://jakearchibald.com/2020/avif-has-landed
merge-requests/8/head
Bob Mottram 2020-09-09 16:09:38 +01:00
parent 717a62d397
commit e12b6483c3
9 changed files with 53 additions and 15 deletions

View File

@ -722,7 +722,7 @@ def htmlEditBlog(mediaInstance: bool, translate: {},
editBlogImageSection += \ editBlogImageSection += \
' <input type="file" id="attachpic" name="attachpic"' ' <input type="file" id="attachpic" name="attachpic"'
editBlogImageSection += \ editBlogImageSection += \
' accept=".png, .jpg, .jpeg, .gif, .webp, ' + \ ' accept=".png, .jpg, .jpeg, .gif, .webp, .avif, ' + \
'.mp4, .webm, .ogv, .mp3, .ogg">' '.mp4, .webm, .ogv, .mp3, .ogg">'
editBlogImageSection += ' </div>' editBlogImageSection += ' </div>'

View File

@ -737,6 +737,7 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
'jpeg': 'image/jpeg', 'jpeg': 'image/jpeg',
'gif': 'image/gif', 'gif': 'image/gif',
'webp': 'image/webp', 'webp': 'image/webp',
'avif': 'image/avif',
'mp4': 'video/mp4', 'mp4': 'video/mp4',
'ogv': 'video/ogv', 'ogv': 'video/ogv',
'mp3': 'audio/mpeg', 'mp3': 'audio/mpeg',
@ -771,7 +772,7 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
break break
# remove any existing image files with a different format # remove any existing image files with a different format
extensionTypes = ('png', 'jpg', 'jpeg', 'gif', 'webp') extensionTypes = ('png', 'jpg', 'jpeg', 'gif', 'webp', 'avif')
for ex in extensionTypes: for ex in extensionTypes:
if ex == detectedExtension: if ex == detectedExtension:
continue continue

View File

@ -254,6 +254,7 @@ class PubServer(BaseHTTPRequestHandler):
if path.endswith('.png') or \ if path.endswith('.png') or \
path.endswith('.jpg') or \ path.endswith('.jpg') or \
path.endswith('.gif') or \ path.endswith('.gif') or \
path.endswith('.avif') or \
path.endswith('.webp'): path.endswith('.webp'):
return True return True
return False return False
@ -2522,6 +2523,8 @@ class PubServer(BaseHTTPRequestHandler):
mediaFilename = mediaFilenameBase + '.gif' mediaFilename = mediaFilenameBase + '.gif'
if self.headers['Content-type'].endswith('webp'): if self.headers['Content-type'].endswith('webp'):
mediaFilename = mediaFilenameBase + '.webp' mediaFilename = mediaFilenameBase + '.webp'
if self.headers['Content-type'].endswith('avif'):
mediaFilename = mediaFilenameBase + '.avif'
with open(mediaFilename, 'wb') as avFile: with open(mediaFilename, 'wb') as avFile:
avFile.write(mediaBytes) avFile.write(mediaBytes)
if debug: if debug:
@ -3552,6 +3555,9 @@ class PubServer(BaseHTTPRequestHandler):
if 'image/webp' in self.headers['Accept']: if 'image/webp' in self.headers['Accept']:
favType = 'image/webp' favType = 'image/webp'
favFilename = 'favicon.webp' favFilename = 'favicon.webp'
if 'image/avif' in self.headers['Accept']:
favType = 'image/avif'
favFilename = 'favicon.avif'
# custom favicon # custom favicon
faviconFilename = baseDir + '/' + favFilename faviconFilename = baseDir + '/' + favFilename
if not os.path.isfile(faviconFilename): if not os.path.isfile(faviconFilename):
@ -3848,6 +3854,8 @@ class PubServer(BaseHTTPRequestHandler):
mediaFileType = 'image/gif' mediaFileType = 'image/gif'
elif mediaFilename.endswith('.webp'): elif mediaFilename.endswith('.webp'):
mediaFileType = 'image/webp' mediaFileType = 'image/webp'
elif mediaFilename.endswith('.avif'):
mediaFileType = 'image/avif'
elif mediaFilename.endswith('.mp4'): elif mediaFilename.endswith('.mp4'):
mediaFileType = 'video/mp4' mediaFileType = 'video/mp4'
elif mediaFilename.endswith('.ogv'): elif mediaFilename.endswith('.ogv'):
@ -3890,6 +3898,8 @@ class PubServer(BaseHTTPRequestHandler):
mediaImageType = 'jpeg' mediaImageType = 'jpeg'
elif emojiFilename.endswith('.webp'): elif emojiFilename.endswith('.webp'):
mediaImageType = 'webp' mediaImageType = 'webp'
elif emojiFilename.endswith('.avif'):
mediaImageType = 'avif'
else: else:
mediaImageType = 'gif' mediaImageType = 'gif'
with open(emojiFilename, 'rb') as avFile: with open(emojiFilename, 'rb') as avFile:
@ -3974,6 +3984,11 @@ class PubServer(BaseHTTPRequestHandler):
'image/webp', 'image/webp',
mediaBinary, None, mediaBinary, None,
callingDomain) callingDomain)
elif mediaFilename.endswith('.avif'):
self._set_headers_etag(mediaFilename,
'image/avif',
mediaBinary, None,
callingDomain)
else: else:
# default to jpeg # default to jpeg
self._set_headers_etag(mediaFilename, self._set_headers_etag(mediaFilename,
@ -6965,7 +6980,7 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> bool: GETstartTime, GETtimings: {}) -> bool:
"""Show a background image """Show a background image
""" """
for ext in ('webp', 'gif', 'jpg', 'png'): for ext in ('webp', 'gif', 'jpg', 'png', 'avif'):
for bg in ('follow', 'options', 'login'): for bg in ('follow', 'options', 'login'):
# follow screen background image # follow screen background image
if path.endswith('/' + bg + '-background.' + ext): if path.endswith('/' + bg + '-background.' + ext):
@ -7028,6 +7043,8 @@ class PubServer(BaseHTTPRequestHandler):
mediaFileType = 'jpeg' mediaFileType = 'jpeg'
elif mediaFilename.endswith('.webp'): elif mediaFilename.endswith('.webp'):
mediaFileType = 'webp' mediaFileType = 'webp'
elif mediaFilename.endswith('.avif'):
mediaFileType = 'avif'
else: else:
mediaFileType = 'gif' mediaFileType = 'gif'
with open(mediaFilename, 'rb') as avFile: with open(mediaFilename, 'rb') as avFile:
@ -7075,6 +7092,8 @@ class PubServer(BaseHTTPRequestHandler):
mediaImageType = 'jpeg' mediaImageType = 'jpeg'
elif avatarFile.endswith('.gif'): elif avatarFile.endswith('.gif'):
mediaImageType = 'gif' mediaImageType = 'gif'
elif avatarFile.endswith('.avif'):
mediaImageType = 'avif'
else: else:
mediaImageType = 'webp' mediaImageType = 'webp'
with open(avatarFilename, 'rb') as avFile: with open(avatarFilename, 'rb') as avFile:
@ -7819,6 +7838,7 @@ class PubServer(BaseHTTPRequestHandler):
if self.path == '/login.png' or \ if self.path == '/login.png' or \
self.path == '/login.gif' or \ self.path == '/login.gif' or \
self.path == '/login.webp' or \ self.path == '/login.webp' or \
self.path == '/login.avif' or \
self.path == '/login.jpeg' or \ self.path == '/login.jpeg' or \
self.path == '/login.jpg' or \ self.path == '/login.jpg' or \
self.path == '/qrcode.png': self.path == '/qrcode.png':
@ -9005,6 +9025,8 @@ class PubServer(BaseHTTPRequestHandler):
mediaFileType = 'image/gif' mediaFileType = 'image/gif'
elif checkPath.endswith('.webp'): elif checkPath.endswith('.webp'):
mediaFileType = 'image/webp' mediaFileType = 'image/webp'
elif checkPath.endswith('.avif'):
mediaFileType = 'image/avif'
elif checkPath.endswith('.mp4'): elif checkPath.endswith('.mp4'):
mediaFileType = 'video/mp4' mediaFileType = 'video/mp4'
elif checkPath.endswith('.ogv'): elif checkPath.endswith('.ogv'):
@ -9079,6 +9101,7 @@ class PubServer(BaseHTTPRequestHandler):
if filename.endswith('.png') or \ if filename.endswith('.png') or \
filename.endswith('.jpg') or \ filename.endswith('.jpg') or \
filename.endswith('.webp') or \ filename.endswith('.webp') or \
filename.endswith('.avif') or \
filename.endswith('.gif'): filename.endswith('.gif'):
if self.server.debug: if self.server.debug:
print('DEBUG: POST media removing metadata') print('DEBUG: POST media removing metadata')

View File

@ -32,8 +32,6 @@
--font-size-pgp-key2: 8px; --font-size-pgp-key2: 8px;
--font-size-tox: 16px; --font-size-tox: 16px;
--font-size-tox2: 8px; --font-size-tox2: 8px;
--text-entry-foreground: #ccc;
--text-entry-background: #111;
--time-color: #aaa; --time-color: #aaa;
--time-vertical-align: 4px; --time-vertical-align: 4px;
--button-text: #FFFFFF; --button-text: #FFFFFF;

View File

@ -56,7 +56,7 @@ def getImageHash(imageFilename: str) -> str:
def isMedia(imageFilename: str) -> bool: def isMedia(imageFilename: str) -> bool:
permittedMedia = ('png', 'jpg', 'gif', 'webp', permittedMedia = ('png', 'jpg', 'gif', 'webp', 'avif',
'mp4', 'ogv', 'mp3', 'ogg') 'mp4', 'ogv', 'mp3', 'ogg')
for m in permittedMedia: for m in permittedMedia:
if imageFilename.endswith('.' + m): if imageFilename.endswith('.' + m):
@ -84,7 +84,7 @@ def getAttachmentMediaType(filename: str) -> str:
""" """
mediaType = None mediaType = None
imageTypes = ('png', 'jpg', 'jpeg', imageTypes = ('png', 'jpg', 'jpeg',
'gif', 'webp') 'gif', 'webp', 'avif')
for mType in imageTypes: for mType in imageTypes:
if filename.endswith('.' + mType): if filename.endswith('.' + mType):
return 'image' return 'image'
@ -143,7 +143,7 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
return postJson return postJson
fileExtension = None fileExtension = None
acceptedTypes = ('png', 'jpg', 'gif', 'webp', acceptedTypes = ('png', 'jpg', 'gif', 'webp', 'avif',
'mp4', 'webm', 'ogv', 'mp3', 'ogg') 'mp4', 'webm', 'ogv', 'mp3', 'ogg')
for mType in acceptedTypes: for mType in acceptedTypes:
if imageFilename.endswith('.' + mType): if imageFilename.endswith('.' + mType):

View File

@ -122,6 +122,8 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str,
fileExtension = 'gif' fileExtension = 'gif'
elif mediaTypeStr.endswith('webp'): elif mediaTypeStr.endswith('webp'):
fileExtension = 'webp' fileExtension = 'webp'
elif mediaTypeStr.endswith('avif'):
fileExtension = 'avif'
elif mediaTypeStr.endswith('audio/mpeg'): elif mediaTypeStr.endswith('audio/mpeg'):
fileExtension = 'mp3' fileExtension = 'mp3'
elif mediaTypeStr.endswith('ogg'): elif mediaTypeStr.endswith('ogg'):

View File

@ -801,7 +801,7 @@ def setThemeImages(baseDir: str, name: str) -> None:
backgroundNames = ('login', 'shares', 'delete', 'follow', backgroundNames = ('login', 'shares', 'delete', 'follow',
'options', 'block', 'search', 'calendar') 'options', 'block', 'search', 'calendar')
extensions = ('webp', 'gif', 'jpg', 'png') extensions = ('webp', 'gif', 'jpg', 'png', 'avif')
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs: for acct in dirs:

View File

@ -51,7 +51,7 @@ def removeAvatarFromCache(baseDir: str, actorStr: str) -> None:
"""Removes any existing avatar entries from the cache """Removes any existing avatar entries from the cache
This avoids duplicate entries with differing extensions This avoids duplicate entries with differing extensions
""" """
avatarFilenameExtensions = ('png', 'jpg', 'gif', 'webp') avatarFilenameExtensions = ('png', 'jpg', 'gif', 'webp', 'avif')
for extension in avatarFilenameExtensions: for extension in avatarFilenameExtensions:
avatarFilename = \ avatarFilename = \
baseDir + '/cache/avatars/' + actorStr + '.' + extension baseDir + '/cache/avatars/' + actorStr + '.' + extension

View File

@ -230,6 +230,11 @@ def updateAvatarImageCache(session, baseDir: str, httpPrefix: str,
'Accept': 'image/webp' 'Accept': 'image/webp'
} }
avatarImageFilename = avatarImagePath + '.webp' avatarImageFilename = avatarImagePath + '.webp'
elif avatarUrl.endswith('.avif') or '.avif?' in avatarUrl:
sessionHeaders = {
'Accept': 'image/avif'
}
avatarImageFilename = avatarImagePath + '.avif'
else: else:
return None return None
@ -304,7 +309,7 @@ def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {},
actorStr = personJson['id'].replace('/', '-') actorStr = personJson['id'].replace('/', '-')
avatarImagePath = baseDir + '/cache/avatars/' + actorStr avatarImagePath = baseDir + '/cache/avatars/' + actorStr
imageExtension = ('png', 'jpg', 'jpeg', 'gif', 'webp') imageExtension = ('png', 'jpg', 'jpeg', 'gif', 'webp', 'avif')
for ext in imageExtension: for ext in imageExtension:
if os.path.isfile(avatarImagePath + '.' + ext): if os.path.isfile(avatarImagePath + '.' + ext):
return '/avatars/' + actorStr + '.' + ext return '/avatars/' + actorStr + '.' + ext
@ -1064,7 +1069,7 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str,
domain: str, port: int, httpPrefix: str) -> str: domain: str, port: int, httpPrefix: str) -> str:
"""Shows the edit profile screen """Shows the edit profile screen
""" """
imageFormats = '.png, .jpg, .jpeg, .gif, .webp' imageFormats = '.png, .jpg, .jpeg, .gif, .webp, .avif'
pathOriginal = path pathOriginal = path
path = path.replace('/inbox', '').replace('/outbox', '') path = path.replace('/inbox', '').replace('/outbox', '')
path = path.replace('/shares', '') path = path.replace('/shares', '')
@ -1621,6 +1626,9 @@ def htmlLogin(translate: {}, baseDir: str, autocomplete=True) -> str:
elif os.path.isfile(baseDir + '/accounts/login.webp'): elif os.path.isfile(baseDir + '/accounts/login.webp'):
loginImage = 'login.webp' loginImage = 'login.webp'
loginImageFilename = baseDir + '/accounts/' + loginImage loginImageFilename = baseDir + '/accounts/' + loginImage
elif os.path.isfile(baseDir + '/accounts/login.avif'):
loginImage = 'login.avif'
loginImageFilename = baseDir + '/accounts/' + loginImage
if not loginImageFilename: if not loginImageFilename:
loginImageFilename = baseDir + '/accounts/' + loginImage loginImageFilename = baseDir + '/accounts/' + loginImage
@ -1965,13 +1973,13 @@ def htmlNewPost(mediaInstance: bool, translate: {},
newPostImageSection += \ newPostImageSection += \
' <input type="file" id="attachpic" name="attachpic"' ' <input type="file" id="attachpic" name="attachpic"'
newPostImageSection += \ newPostImageSection += \
' accept=".png, .jpg, .jpeg, .gif, .webp">\n' ' accept=".png, .jpg, .jpeg, .gif, .webp, .avif">\n'
else: else:
newPostImageSection += \ newPostImageSection += \
' <input type="file" id="attachpic" name="attachpic"' ' <input type="file" id="attachpic" name="attachpic"'
newPostImageSection += \ newPostImageSection += \
' accept=".png, .jpg, .jpeg, .gif, .webp, .mp4, ' + \ ' accept=".png, .jpg, .jpeg, .gif, ' + \
'.webm, .ogv, .mp3, .ogg">\n' '.webp, .avif, .mp4, .webm, .ogv, .mp3, .ogg">\n'
newPostImageSection += ' </div>\n' newPostImageSection += ' </div>\n'
scopeIcon = 'scope_public.png' scopeIcon = 'scope_public.png'
@ -3623,11 +3631,13 @@ def getPostAttachmentsAsHtml(postJsonObject: {}, boxName: str, translate: {},
if mediaType == 'image/png' or \ if mediaType == 'image/png' or \
mediaType == 'image/jpeg' or \ mediaType == 'image/jpeg' or \
mediaType == 'image/webp' or \ mediaType == 'image/webp' or \
mediaType == 'image/avif' or \
mediaType == 'image/gif': mediaType == 'image/gif':
if attach['url'].endswith('.png') or \ if attach['url'].endswith('.png') or \
attach['url'].endswith('.jpg') or \ attach['url'].endswith('.jpg') or \
attach['url'].endswith('.jpeg') or \ attach['url'].endswith('.jpeg') or \
attach['url'].endswith('.webp') or \ attach['url'].endswith('.webp') or \
attach['url'].endswith('.avif') or \
attach['url'].endswith('.gif'): attach['url'].endswith('.gif'):
if attachmentCtr > 0: if attachmentCtr > 0:
attachmentStr += '<br>' attachmentStr += '<br>'
@ -4922,6 +4932,10 @@ def htmlTimeline(defaultTimeline: str,
bannerFile = 'banner.gif' bannerFile = 'banner.gif'
bannerFilename = baseDir + '/accounts/' + \ bannerFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/' + bannerFile nickname + '@' + domain + '/' + bannerFile
if not os.path.isfile(bannerFilename):
bannerFile = 'banner.avif'
bannerFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/' + bannerFile
if not os.path.isfile(bannerFilename): if not os.path.isfile(bannerFilename):
bannerFile = 'banner.webp' bannerFile = 'banner.webp'