Image watermark is a fixed percentage of the attached image width

merge-requests/30/head
Bob Mottram 2024-07-25 13:34:18 +01:00
parent 8766ef9aef
commit 5200238861
8 changed files with 134 additions and 99 deletions

View File

@ -758,7 +758,8 @@ def run_daemon(accounts_data_dir: str,
unit_test: bool, unit_test: bool,
instance_only_skills_search: bool, instance_only_skills_search: bool,
send_threads: [], send_threads: [],
manual_follower_approval: bool) -> None: manual_follower_approval: bool,
watermark_width_percent: int) -> None:
if len(domain) == 0: if len(domain) == 0:
domain = 'localhost' domain = 'localhost'
if '.' not in domain: if '.' not in domain:
@ -802,6 +803,10 @@ def run_daemon(accounts_data_dir: str,
httpd.starting_daemon = True httpd.starting_daemon = True
# width of watermark applied to attached images
# as a percentage of the attached image width
httpd.watermark_width_percent = watermark_width_percent
# for each account whether to hide announces # for each account whether to hide announces
httpd.hide_announces = {} httpd.hide_announces = {}
hide_announces_filename = data_dir(base_dir) + '/hide_announces.json' hide_announces_filename = data_dir(base_dir) + '/hide_announces.json'

View File

@ -867,7 +867,8 @@ def daemon_http_post(self) -> None:
self.server.block_federated, self.server.block_federated,
self.server.onion_domain, self.server.onion_domain,
self.server.i2p_domain, self.server.i2p_domain,
self.server.max_shares_on_profile) self.server.max_shares_on_profile,
self.server.watermark_width_percent)
if page_number: if page_number:
print(curr_post_type + ' post received') print(curr_post_type + ' post received')
nickname = self.path.split('/users/')[1] nickname = self.path.split('/users/')[1]

View File

@ -11,7 +11,6 @@ import os
import time import time
import copy import copy
import errno import errno
import subprocess
from socket import error as SocketError from socket import error as SocketError
from shares import add_share from shares import add_share
from languages import get_understood_languages from languages import get_understood_languages
@ -22,13 +21,13 @@ from content import add_html_tags
from content import extract_text_fields_in_post from content import extract_text_fields_in_post
from content import extract_media_in_form_post from content import extract_media_in_form_post
from content import save_media_in_form_post from content import save_media_in_form_post
from media import apply_watermark_to_image
from media import replace_twitter from media import replace_twitter
from media import replace_you_tube from media import replace_you_tube
from media import process_meta_data from media import process_meta_data
from media import convert_image_to_low_bandwidth from media import convert_image_to_low_bandwidth
from media import attach_media from media import attach_media
from city import get_spoofed_city from city import get_spoofed_city
from utils import safe_system_string
from utils import get_instance_url from utils import get_instance_url
from utils import is_float from utils import is_float
from utils import save_json from utils import save_json
@ -55,7 +54,6 @@ from inbox import populate_replies
from inbox import update_edited_post from inbox import update_edited_post
from daemon_utils import post_to_outbox from daemon_utils import post_to_outbox
from webapp_column_right import html_citations from webapp_column_right import html_citations
from webapp_utils import get_watermark_file
from httpheaders import set_headers from httpheaders import set_headers
from httpcodes import write2 from httpcodes import write2
from cache import store_person_in_cache from cache import store_person_in_cache
@ -69,44 +67,6 @@ NEW_POST_FAILED = -1
NEW_POST_CANCELLED = 2 NEW_POST_CANCELLED = 2
def _apply_watermark_to_image(base_dir: str, nickname: str, domain: str,
post_image_filename: str) -> bool:
"""Applies a watermark to the given image
"""
if not os.path.isfile(post_image_filename):
return False
if not os.path.isfile('/usr/bin/composite'):
return False
_, watermark_filename = get_watermark_file(base_dir, nickname, domain)
if not watermark_filename:
return False
if not os.path.isfile(watermark_filename):
return False
cmd = \
'/usr/bin/composite -watermark 10% -gravity east ' + \
safe_system_string(watermark_filename) + ' ' + \
safe_system_string(post_image_filename) + ' ' + \
safe_system_string(post_image_filename + '.watermarked')
subprocess.call(cmd, shell=True)
if not os.path.isfile(post_image_filename + '.watermarked'):
return False
try:
os.remove(post_image_filename)
except OSError:
print('EX: _apply_watermark_to_image unable to remove ' +
post_image_filename)
return False
try:
os.rename(post_image_filename + '.watermarked', post_image_filename)
except OSError:
print('EX: _apply_watermark_to_image unable to rename ' +
post_image_filename + '.watermarked')
return False
return True
def _receive_new_post_process_newpost(self, fields: {}, def _receive_new_post_process_newpost(self, fields: {},
base_dir: str, nickname: str, base_dir: str, nickname: str,
domain: str, domain_full: str, port: int, domain: str, domain_full: str, port: int,
@ -1635,7 +1595,8 @@ def _receive_new_post_process(self, post_type: str, path: str, headers: {},
block_federated: [], block_federated: [],
onion_domain: str, onion_domain: str,
i2p_domain: str, i2p_domain: str,
max_shares_on_profile: int) -> int: max_shares_on_profile: int,
watermark_width_percent: int) -> int:
# Note: this needs to happen synchronously # Note: this needs to happen synchronously
# 0=this is not a new post # 0=this is not a new post
# 1=new post success # 1=new post success
@ -1727,8 +1688,8 @@ def _receive_new_post_process(self, post_type: str, path: str, headers: {},
if low_bandwidth: if low_bandwidth:
print('Converting to low bandwidth ' + filename) print('Converting to low bandwidth ' + filename)
convert_image_to_low_bandwidth(filename) convert_image_to_low_bandwidth(filename)
_apply_watermark_to_image(base_dir, nickname, domain, apply_watermark_to_image(base_dir, nickname, domain,
filename) filename, watermark_width_percent)
post_image_filename = filename.replace('.temp', '') post_image_filename = filename.replace('.temp', '')
print('Removing metadata from ' + post_image_filename) print('Removing metadata from ' + post_image_filename)
city = get_spoofed_city(city, base_dir, nickname, domain) city = get_spoofed_city(city, base_dir, nickname, domain)
@ -2235,7 +2196,8 @@ def receive_new_post(self, post_type, path: str,
block_federated: [], block_federated: [],
onion_domain: str, onion_domain: str,
i2p_domain: str, i2p_domain: str,
max_shares_on_profile: int) -> int: max_shares_on_profile: int,
watermark_width_percent: int) -> int:
"""A new post has been created """A new post has been created
This creates a thread to send the new post This creates a thread to send the new post
""" """
@ -2373,7 +2335,8 @@ def receive_new_post(self, post_type, path: str,
dm_license_url, block_federated, dm_license_url, block_federated,
onion_domain, onion_domain,
i2p_domain, i2p_domain,
max_shares_on_profile) max_shares_on_profile,
watermark_width_percent)
if debug: if debug:
print('DEBUG: _receive_new_post_process returned ' + print('DEBUG: _receive_new_post_process returned ' +
str(retval)) str(retval))

View File

@ -251,6 +251,11 @@ def _command_options() -> None:
default=None, default=None,
help='Number of days after which posts expire ' + help='Number of days after which posts expire ' +
'for the given account') 'for the given account')
parser.add_argument('--watermarkWidthPercent',
dest='watermark_width_percent', type=int,
default=20,
help='Width of the watermark applied to attached ' +
'images as a percentage of the attached image width')
parser.add_argument('--check-actor-timeout', dest='check_actor_timeout', parser.add_argument('--check-actor-timeout', dest='check_actor_timeout',
type=int, default=2, type=int, default=2,
help='Timeout in seconds used for checking is ' + help='Timeout in seconds used for checking is ' +
@ -4080,4 +4085,5 @@ if __name__ == "__main__":
argb2.account_max_posts_per_day, argb2.account_max_posts_per_day,
argb2.allowdeletion, opt2['debug'], False, argb2.allowdeletion, opt2['debug'], False,
argb2.instance_only_skills_search, [], argb2.instance_only_skills_search, [],
not argb2.noapproval) not argb2.noapproval,
argb2.watermark_width_percent)

View File

@ -26,6 +26,7 @@ from utils import get_audio_extensions
from utils import get_media_extensions from utils import get_media_extensions
from utils import has_object_dict from utils import has_object_dict
from utils import acct_dir from utils import acct_dir
from utils import get_watermark_file
from shutil import copyfile from shutil import copyfile
from shutil import rmtree from shutil import rmtree
from shutil import move from shutil import move
@ -760,3 +761,57 @@ def get_image_dimensions(image_filename: str) -> (int, int):
if not height_str.isdigit(): if not height_str.isdigit():
return None, None return None, None
return int(width_str), int(height_str) return int(width_str), int(height_str)
def apply_watermark_to_image(base_dir: str, nickname: str, domain: str,
post_image_filename: str,
watermark_width_percent: int) -> bool:
"""Applies a watermark to the given image
"""
if not os.path.isfile(post_image_filename):
return False
if not os.path.isfile('/usr/bin/composite'):
return False
_, watermark_filename = get_watermark_file(base_dir, nickname, domain)
if not watermark_filename:
return False
if not os.path.isfile(watermark_filename):
return False
# scale the watermark so that it is a fixed percentage of the image width
post_image_width, _ = \
get_image_dimensions(post_image_filename)
watermark_image_width, watermark_image_height = \
get_image_dimensions(post_image_filename)
scaled_watermark_image_width = \
int(post_image_width * watermark_width_percent / 100)
scaled_watermark_image_height = \
int(watermark_image_height *
scaled_watermark_image_width / watermark_image_width)
cmd = \
'/usr/bin/composite ' + \
'-geometry ' + str(scaled_watermark_image_width) + 'x' + \
str(scaled_watermark_image_height) + '+30+5 ' + \
'-watermark 10% -gravity east ' + \
safe_system_string(watermark_filename) + ' ' + \
safe_system_string(post_image_filename) + ' ' + \
safe_system_string(post_image_filename + '.watermarked')
subprocess.call(cmd, shell=True)
if not os.path.isfile(post_image_filename + '.watermarked'):
return False
try:
os.remove(post_image_filename)
except OSError:
print('EX: _apply_watermark_to_image unable to remove ' +
post_image_filename)
return False
try:
os.rename(post_image_filename + '.watermarked', post_image_filename)
except OSError:
print('EX: _apply_watermark_to_image unable to rename ' +
post_image_filename + '.watermarked')
return False
return True

View File

@ -893,6 +893,7 @@ def create_server_alice(path: str, domain: str, port: int,
public_replies_unlisted = False public_replies_unlisted = False
no_of_books = 10 no_of_books = 10
accounts_data_dir = None accounts_data_dir = None
watermark_width_percent = 20
print('Server running: Alice') print('Server running: Alice')
run_daemon(accounts_data_dir, run_daemon(accounts_data_dir,
no_of_books, public_replies_unlisted, no_of_books, public_replies_unlisted,
@ -924,7 +925,7 @@ def create_server_alice(path: str, domain: str, port: int,
proxy_type, max_replies, proxy_type, max_replies,
domain_max_posts_per_day, account_max_posts_per_day, domain_max_posts_per_day, account_max_posts_per_day,
allow_deletion, True, True, False, send_threads, allow_deletion, True, True, False, send_threads,
False) False, watermark_width_percent)
def create_server_bob(path: str, domain: str, port: int, def create_server_bob(path: str, domain: str, port: int,
@ -1079,6 +1080,7 @@ def create_server_bob(path: str, domain: str, port: int,
public_replies_unlisted = False public_replies_unlisted = False
no_of_books = 10 no_of_books = 10
accounts_data_dir = None accounts_data_dir = None
watermark_width_percent = 20
print('Server running: Bob') print('Server running: Bob')
run_daemon(accounts_data_dir, run_daemon(accounts_data_dir,
no_of_books, public_replies_unlisted, no_of_books, public_replies_unlisted,
@ -1110,7 +1112,7 @@ def create_server_bob(path: str, domain: str, port: int,
proxy_type, max_replies, proxy_type, max_replies,
domain_max_posts_per_day, account_max_posts_per_day, domain_max_posts_per_day, account_max_posts_per_day,
allow_deletion, True, True, False, send_threads, allow_deletion, True, True, False, send_threads,
False) False, watermark_width_percent)
def create_server_eve(path: str, domain: str, port: int, federation_list: [], def create_server_eve(path: str, domain: str, port: int, federation_list: [],
@ -1173,6 +1175,7 @@ def create_server_eve(path: str, domain: str, port: int, federation_list: [],
domain_max_posts_per_day = 1000 domain_max_posts_per_day = 1000
account_max_posts_per_day = 1000 account_max_posts_per_day = 1000
accounts_data_dir = None accounts_data_dir = None
watermark_width_percent = 20
print('Server running: Eve') print('Server running: Eve')
run_daemon(accounts_data_dir, no_of_books, run_daemon(accounts_data_dir, no_of_books,
public_replies_unlisted, public_replies_unlisted,
@ -1224,7 +1227,8 @@ def create_server_eve(path: str, domain: str, port: int, federation_list: [],
account_max_posts_per_day, account_max_posts_per_day,
allow_deletion, allow_deletion,
True, True, False, True, True, False,
send_threads, False) send_threads, False,
watermark_width_percent)
def create_server_group(path: str, domain: str, port: int, def create_server_group(path: str, domain: str, port: int,
@ -1289,6 +1293,7 @@ def create_server_group(path: str, domain: str, port: int,
public_replies_unlisted = False public_replies_unlisted = False
no_of_books = 10 no_of_books = 10
accounts_data_dir = None accounts_data_dir = None
watermark_width_percent = 20
print('Server running: Group') print('Server running: Group')
run_daemon(accounts_data_dir, run_daemon(accounts_data_dir,
no_of_books, public_replies_unlisted, no_of_books, public_replies_unlisted,
@ -1320,7 +1325,7 @@ def create_server_group(path: str, domain: str, port: int,
proxy_type, max_replies, proxy_type, max_replies,
domain_max_posts_per_day, account_max_posts_per_day, domain_max_posts_per_day, account_max_posts_per_day,
allow_deletion, True, True, False, send_threads, allow_deletion, True, True, False, send_threads,
False) False, watermark_width_percent)
def test_post_message_between_servers(base_dir: str) -> None: def test_post_message_between_servers(base_dir: str) -> None:

View File

@ -5586,3 +5586,43 @@ def remove_link_tracking(url: str) -> str:
if '?utm_' not in url: if '?utm_' not in url:
return url return url
return url.split('?utm_')[0] return url.split('?utm_')[0]
def get_image_file(base_dir: str, name: str, directory: str,
theme: str) -> (str, str):
"""returns the filenames for an image with the given name
"""
banner_extensions = get_image_extensions()
banner_file = ''
banner_filename = ''
im_name = name
for ext in banner_extensions:
banner_file_test = im_name + '.' + ext
banner_filename_test = directory + '/' + banner_file_test
if os.path.isfile(banner_filename_test):
banner_file = banner_file_test
banner_filename = banner_filename_test
return banner_file, banner_filename
# if not found then use the default image
curr_theme = 'default'
if theme:
curr_theme = theme
directory = base_dir + '/theme/' + curr_theme
for ext in banner_extensions:
banner_file_test = name + '.' + ext
banner_filename_test = directory + '/' + banner_file_test
if os.path.isfile(banner_filename_test):
banner_file = name + '_' + curr_theme + '.' + ext
banner_filename = banner_filename_test
break
return banner_file, banner_filename
def get_watermark_file(base_dir: str,
nickname: str, domain: str) -> (str, str):
"""Gets the filename for watermarking when an image is attached to a post
"""
account_dir = acct_dir(base_dir, nickname, domain)
watermark_file, watermark_filename = \
get_image_file(base_dir, 'watermark_image', account_dir, '')
return watermark_file, watermark_filename

View File

@ -12,6 +12,7 @@ from shutil import copyfile
from collections import OrderedDict from collections import OrderedDict
from session import get_json from session import get_json
from session import get_json_valid from session import get_json_valid
from utils import get_image_file
from utils import data_dir from utils import data_dir
from utils import string_contains from utils import string_contains
from utils import get_post_attachments from utils import get_post_attachments
@ -643,44 +644,13 @@ def post_contains_public(post_json_object: {}) -> bool:
return contains_public return contains_public
def _get_image_file(base_dir: str, name: str, directory: str,
theme: str) -> (str, str):
"""
returns the filenames for an image with the given name
"""
banner_extensions = get_image_extensions()
banner_file = ''
banner_filename = ''
im_name = name
for ext in banner_extensions:
banner_file_test = im_name + '.' + ext
banner_filename_test = directory + '/' + banner_file_test
if os.path.isfile(banner_filename_test):
banner_file = banner_file_test
banner_filename = banner_filename_test
return banner_file, banner_filename
# if not found then use the default image
curr_theme = 'default'
if theme:
curr_theme = theme
directory = base_dir + '/theme/' + curr_theme
for ext in banner_extensions:
banner_file_test = name + '.' + ext
banner_filename_test = directory + '/' + banner_file_test
if os.path.isfile(banner_filename_test):
banner_file = name + '_' + curr_theme + '.' + ext
banner_filename = banner_filename_test
break
return banner_file, banner_filename
def get_banner_file(base_dir: str, def get_banner_file(base_dir: str,
nickname: str, domain: str, theme: str) -> (str, str): nickname: str, domain: str, theme: str) -> (str, str):
"""Gets the image for the timeline banner """Gets the image for the timeline banner
""" """
account_dir = acct_dir(base_dir, nickname, domain) account_dir = acct_dir(base_dir, nickname, domain)
banner_file, banner_filename = \ banner_file, banner_filename = \
_get_image_file(base_dir, 'banner', account_dir, theme) get_image_file(base_dir, 'banner', account_dir, theme)
return banner_file, banner_filename return banner_file, banner_filename
@ -691,7 +661,7 @@ def get_profile_background_file(base_dir: str,
""" """
account_dir = acct_dir(base_dir, nickname, domain) account_dir = acct_dir(base_dir, nickname, domain)
banner_file, banner_filename = \ banner_file, banner_filename = \
_get_image_file(base_dir, 'image', account_dir, theme) get_image_file(base_dir, 'image', account_dir, theme)
return banner_file, banner_filename return banner_file, banner_filename
@ -702,7 +672,7 @@ def get_search_banner_file(base_dir: str,
""" """
account_dir = acct_dir(base_dir, nickname, domain) account_dir = acct_dir(base_dir, nickname, domain)
banner_file, banner_filename = \ banner_file, banner_filename = \
_get_image_file(base_dir, 'search_banner', account_dir, theme) get_image_file(base_dir, 'search_banner', account_dir, theme)
return banner_file, banner_filename return banner_file, banner_filename
@ -712,7 +682,7 @@ def get_left_image_file(base_dir: str,
""" """
account_dir = acct_dir(base_dir, nickname, domain) account_dir = acct_dir(base_dir, nickname, domain)
banner_file, banner_filename = \ banner_file, banner_filename = \
_get_image_file(base_dir, 'left_col_image', account_dir, theme) get_image_file(base_dir, 'left_col_image', account_dir, theme)
return banner_file, banner_filename return banner_file, banner_filename
@ -722,20 +692,10 @@ def get_right_image_file(base_dir: str,
""" """
account_dir = acct_dir(base_dir, nickname, domain) account_dir = acct_dir(base_dir, nickname, domain)
banner_file, banner_filename = \ banner_file, banner_filename = \
_get_image_file(base_dir, 'right_col_image', account_dir, theme) get_image_file(base_dir, 'right_col_image', account_dir, theme)
return banner_file, banner_filename return banner_file, banner_filename
def get_watermark_file(base_dir: str,
nickname: str, domain: str) -> (str, str):
"""Gets the filename for watermarking when an image is attached to a post
"""
account_dir = acct_dir(base_dir, nickname, domain)
watermark_file, watermark_filename = \
_get_image_file(base_dir, 'watermark_image', account_dir, '')
return watermark_file, watermark_filename
def html_header_with_external_style(css_filename: str, instance_title: str, def html_header_with_external_style(css_filename: str, instance_title: str,
metadata: str, lang='en') -> str: metadata: str, lang='en') -> str:
if metadata is None: if metadata is None: