From e12b6483c33ef49988627feef1ebe2868b5e5e14 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 9 Sep 2020 16:09:38 +0100 Subject: [PATCH] Support AVIF image format See https://jakearchibald.com/2020/avif-has-landed --- blog.py | 2 +- content.py | 3 ++- daemon.py | 25 ++++++++++++++++++++++++- epicyon-profile.css | 2 -- media.py | 6 +++--- outbox.py | 2 ++ theme.py | 2 +- utils.py | 2 +- webinterface.py | 24 +++++++++++++++++++----- 9 files changed, 53 insertions(+), 15 deletions(-) diff --git a/blog.py b/blog.py index 91db9ca66..e1db75975 100644 --- a/blog.py +++ b/blog.py @@ -722,7 +722,7 @@ def htmlEditBlog(mediaInstance: bool, translate: {}, editBlogImageSection += \ ' ' editBlogImageSection += ' ' diff --git a/content.py b/content.py index 488601702..e10f2d66c 100644 --- a/content.py +++ b/content.py @@ -737,6 +737,7 @@ def saveMediaInFormPOST(mediaBytes, debug: bool, 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'webp': 'image/webp', + 'avif': 'image/avif', 'mp4': 'video/mp4', 'ogv': 'video/ogv', 'mp3': 'audio/mpeg', @@ -771,7 +772,7 @@ def saveMediaInFormPOST(mediaBytes, debug: bool, break # 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: if ex == detectedExtension: continue diff --git a/daemon.py b/daemon.py index 1c2852561..d3076d150 100644 --- a/daemon.py +++ b/daemon.py @@ -254,6 +254,7 @@ class PubServer(BaseHTTPRequestHandler): if path.endswith('.png') or \ path.endswith('.jpg') or \ path.endswith('.gif') or \ + path.endswith('.avif') or \ path.endswith('.webp'): return True return False @@ -2522,6 +2523,8 @@ class PubServer(BaseHTTPRequestHandler): mediaFilename = mediaFilenameBase + '.gif' if self.headers['Content-type'].endswith('webp'): mediaFilename = mediaFilenameBase + '.webp' + if self.headers['Content-type'].endswith('avif'): + mediaFilename = mediaFilenameBase + '.avif' with open(mediaFilename, 'wb') as avFile: avFile.write(mediaBytes) if debug: @@ -3552,6 +3555,9 @@ class PubServer(BaseHTTPRequestHandler): if 'image/webp' in self.headers['Accept']: favType = 'image/webp' favFilename = 'favicon.webp' + if 'image/avif' in self.headers['Accept']: + favType = 'image/avif' + favFilename = 'favicon.avif' # custom favicon faviconFilename = baseDir + '/' + favFilename if not os.path.isfile(faviconFilename): @@ -3848,6 +3854,8 @@ class PubServer(BaseHTTPRequestHandler): mediaFileType = 'image/gif' elif mediaFilename.endswith('.webp'): mediaFileType = 'image/webp' + elif mediaFilename.endswith('.avif'): + mediaFileType = 'image/avif' elif mediaFilename.endswith('.mp4'): mediaFileType = 'video/mp4' elif mediaFilename.endswith('.ogv'): @@ -3890,6 +3898,8 @@ class PubServer(BaseHTTPRequestHandler): mediaImageType = 'jpeg' elif emojiFilename.endswith('.webp'): mediaImageType = 'webp' + elif emojiFilename.endswith('.avif'): + mediaImageType = 'avif' else: mediaImageType = 'gif' with open(emojiFilename, 'rb') as avFile: @@ -3974,6 +3984,11 @@ class PubServer(BaseHTTPRequestHandler): 'image/webp', mediaBinary, None, callingDomain) + elif mediaFilename.endswith('.avif'): + self._set_headers_etag(mediaFilename, + 'image/avif', + mediaBinary, None, + callingDomain) else: # default to jpeg self._set_headers_etag(mediaFilename, @@ -6965,7 +6980,7 @@ class PubServer(BaseHTTPRequestHandler): GETstartTime, GETtimings: {}) -> bool: """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'): # follow screen background image if path.endswith('/' + bg + '-background.' + ext): @@ -7028,6 +7043,8 @@ class PubServer(BaseHTTPRequestHandler): mediaFileType = 'jpeg' elif mediaFilename.endswith('.webp'): mediaFileType = 'webp' + elif mediaFilename.endswith('.avif'): + mediaFileType = 'avif' else: mediaFileType = 'gif' with open(mediaFilename, 'rb') as avFile: @@ -7075,6 +7092,8 @@ class PubServer(BaseHTTPRequestHandler): mediaImageType = 'jpeg' elif avatarFile.endswith('.gif'): mediaImageType = 'gif' + elif avatarFile.endswith('.avif'): + mediaImageType = 'avif' else: mediaImageType = 'webp' with open(avatarFilename, 'rb') as avFile: @@ -7819,6 +7838,7 @@ class PubServer(BaseHTTPRequestHandler): if self.path == '/login.png' or \ self.path == '/login.gif' or \ self.path == '/login.webp' or \ + self.path == '/login.avif' or \ self.path == '/login.jpeg' or \ self.path == '/login.jpg' or \ self.path == '/qrcode.png': @@ -9005,6 +9025,8 @@ class PubServer(BaseHTTPRequestHandler): mediaFileType = 'image/gif' elif checkPath.endswith('.webp'): mediaFileType = 'image/webp' + elif checkPath.endswith('.avif'): + mediaFileType = 'image/avif' elif checkPath.endswith('.mp4'): mediaFileType = 'video/mp4' elif checkPath.endswith('.ogv'): @@ -9079,6 +9101,7 @@ class PubServer(BaseHTTPRequestHandler): if filename.endswith('.png') or \ filename.endswith('.jpg') or \ filename.endswith('.webp') or \ + filename.endswith('.avif') or \ filename.endswith('.gif'): if self.server.debug: print('DEBUG: POST media removing metadata') diff --git a/epicyon-profile.css b/epicyon-profile.css index 280bec865..43b9dd4e4 100644 --- a/epicyon-profile.css +++ b/epicyon-profile.css @@ -32,8 +32,6 @@ --font-size-pgp-key2: 8px; --font-size-tox: 16px; --font-size-tox2: 8px; - --text-entry-foreground: #ccc; - --text-entry-background: #111; --time-color: #aaa; --time-vertical-align: 4px; --button-text: #FFFFFF; diff --git a/media.py b/media.py index ec6626958..a231c7906 100644 --- a/media.py +++ b/media.py @@ -56,7 +56,7 @@ def getImageHash(imageFilename: str) -> str: def isMedia(imageFilename: str) -> bool: - permittedMedia = ('png', 'jpg', 'gif', 'webp', + permittedMedia = ('png', 'jpg', 'gif', 'webp', 'avif', 'mp4', 'ogv', 'mp3', 'ogg') for m in permittedMedia: if imageFilename.endswith('.' + m): @@ -84,7 +84,7 @@ def getAttachmentMediaType(filename: str) -> str: """ mediaType = None imageTypes = ('png', 'jpg', 'jpeg', - 'gif', 'webp') + 'gif', 'webp', 'avif') for mType in imageTypes: if filename.endswith('.' + mType): return 'image' @@ -143,7 +143,7 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int, return postJson fileExtension = None - acceptedTypes = ('png', 'jpg', 'gif', 'webp', + acceptedTypes = ('png', 'jpg', 'gif', 'webp', 'avif', 'mp4', 'webm', 'ogv', 'mp3', 'ogg') for mType in acceptedTypes: if imageFilename.endswith('.' + mType): diff --git a/outbox.py b/outbox.py index e532393f7..4254bb101 100644 --- a/outbox.py +++ b/outbox.py @@ -122,6 +122,8 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, fileExtension = 'gif' elif mediaTypeStr.endswith('webp'): fileExtension = 'webp' + elif mediaTypeStr.endswith('avif'): + fileExtension = 'avif' elif mediaTypeStr.endswith('audio/mpeg'): fileExtension = 'mp3' elif mediaTypeStr.endswith('ogg'): diff --git a/theme.py b/theme.py index 2b4b3ff70..6a7bd5377 100644 --- a/theme.py +++ b/theme.py @@ -801,7 +801,7 @@ def setThemeImages(baseDir: str, name: str) -> None: backgroundNames = ('login', 'shares', 'delete', 'follow', '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 acct in dirs: diff --git a/utils.py b/utils.py index 67093ac98..97fccaa8b 100644 --- a/utils.py +++ b/utils.py @@ -51,7 +51,7 @@ def removeAvatarFromCache(baseDir: str, actorStr: str) -> None: """Removes any existing avatar entries from the cache This avoids duplicate entries with differing extensions """ - avatarFilenameExtensions = ('png', 'jpg', 'gif', 'webp') + avatarFilenameExtensions = ('png', 'jpg', 'gif', 'webp', 'avif') for extension in avatarFilenameExtensions: avatarFilename = \ baseDir + '/cache/avatars/' + actorStr + '.' + extension diff --git a/webinterface.py b/webinterface.py index 01dd70822..6a23e7aac 100644 --- a/webinterface.py +++ b/webinterface.py @@ -230,6 +230,11 @@ def updateAvatarImageCache(session, baseDir: str, httpPrefix: str, 'Accept': 'image/webp' } avatarImageFilename = avatarImagePath + '.webp' + elif avatarUrl.endswith('.avif') or '.avif?' in avatarUrl: + sessionHeaders = { + 'Accept': 'image/avif' + } + avatarImageFilename = avatarImagePath + '.avif' else: return None @@ -304,7 +309,7 @@ def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {}, actorStr = personJson['id'].replace('/', '-') avatarImagePath = baseDir + '/cache/avatars/' + actorStr - imageExtension = ('png', 'jpg', 'jpeg', 'gif', 'webp') + imageExtension = ('png', 'jpg', 'jpeg', 'gif', 'webp', 'avif') for ext in imageExtension: if os.path.isfile(avatarImagePath + '.' + ext): return '/avatars/' + actorStr + '.' + ext @@ -1064,7 +1069,7 @@ def htmlEditProfile(translate: {}, baseDir: str, path: str, domain: str, port: int, httpPrefix: str) -> str: """Shows the edit profile screen """ - imageFormats = '.png, .jpg, .jpeg, .gif, .webp' + imageFormats = '.png, .jpg, .jpeg, .gif, .webp, .avif' pathOriginal = path path = path.replace('/inbox', '').replace('/outbox', '') path = path.replace('/shares', '') @@ -1621,6 +1626,9 @@ def htmlLogin(translate: {}, baseDir: str, autocomplete=True) -> str: elif os.path.isfile(baseDir + '/accounts/login.webp'): loginImage = 'login.webp' loginImageFilename = baseDir + '/accounts/' + loginImage + elif os.path.isfile(baseDir + '/accounts/login.avif'): + loginImage = 'login.avif' + loginImageFilename = baseDir + '/accounts/' + loginImage if not loginImageFilename: loginImageFilename = baseDir + '/accounts/' + loginImage @@ -1965,13 +1973,13 @@ def htmlNewPost(mediaInstance: bool, translate: {}, newPostImageSection += \ ' \n' + ' accept=".png, .jpg, .jpeg, .gif, .webp, .avif">\n' else: newPostImageSection += \ ' \n' + ' accept=".png, .jpg, .jpeg, .gif, ' + \ + '.webp, .avif, .mp4, .webm, .ogv, .mp3, .ogg">\n' newPostImageSection += ' \n' scopeIcon = 'scope_public.png' @@ -3623,11 +3631,13 @@ def getPostAttachmentsAsHtml(postJsonObject: {}, boxName: str, translate: {}, if mediaType == 'image/png' or \ mediaType == 'image/jpeg' or \ mediaType == 'image/webp' or \ + mediaType == 'image/avif' or \ mediaType == 'image/gif': if attach['url'].endswith('.png') or \ attach['url'].endswith('.jpg') or \ attach['url'].endswith('.jpeg') or \ attach['url'].endswith('.webp') or \ + attach['url'].endswith('.avif') or \ attach['url'].endswith('.gif'): if attachmentCtr > 0: attachmentStr += '
' @@ -4922,6 +4932,10 @@ def htmlTimeline(defaultTimeline: str, bannerFile = 'banner.gif' bannerFilename = baseDir + '/accounts/' + \ nickname + '@' + domain + '/' + bannerFile + if not os.path.isfile(bannerFilename): + bannerFile = 'banner.avif' + bannerFilename = baseDir + '/accounts/' + \ + nickname + '@' + domain + '/' + bannerFile if not os.path.isfile(bannerFilename): bannerFile = 'banner.webp'