#!/usr/bin/env python # -*- coding: utf-8 -*- from yaml import safe_load from typing import Iterable from pathlib import Path import logging path_stickers_root = Path('.') path_stickers_output_dir = path_stickers_root / 'stickers' path_stickers_output_dir.mkdir(exist_ok=True) path_stickers_source_dir = path_stickers_root / 'src' path_stickers_data_dir = path_stickers_root / 'data' logging.basicConfig( filename=str(path_stickers_root / 'make.log'), level=logging.DEBUG) log = logging.getLogger('stickers.actions') log.setLevel(logging.DEBUG) log.debug('path_stickers_root = %s', path_stickers_root) log.debug('path_stickers_data_dir = %s', path_stickers_data_dir) class StickerTasks: defs: Iterable dst_width: int dst_height: int dst_format: str ffmpeg_opts: list[str] def __init__(self, defs_src: Iterable | None = None, dst_width: int = 512, dst_height: int = 512, dst_format: str = 'png', dst_ext: str = 'png', ffmpeg_opts: list[str] = list()): if defs_src is None: defs_src = path_stickers_data_dir.glob("*.yaml") self._log = log.getChild(self.__class__.__name__) self.defs = defs_src self.dst_height = dst_height self.dst_width = dst_width self.dst_format = dst_format self.dst_ext = dst_ext self.ffmpeg_opts = ffmpeg_opts def create_doit_tasks(self=None): log = self._log if self is None: log.debug('method called as class method, silently returning') return log.info('creating %s stickers with %s dimensions and %s opts', self.dst_format, f"{self.dst_width}x{self.dst_height}", self.ffmpeg_opts) for def_filepath in self.defs: log.debug("def file: %s", def_filepath) with open(def_filepath) as stream: sticker_info = safe_load(stream) if sticker_info.get("disabled", False): log.info("file %s is disabled, skipping", def_filepath) continue yield self.create_doit_sticker_task(sticker_info, def_filepath) def create_doit_sticker_task(self, sticker_info, def_filepath): # Handling logging sticker_log = self._log.getChild(sticker_info['name']) out_filename = f'{sticker_info["name"]}.{self.dst_ext}' target_filename = path_stickers_output_dir / out_filename sticker_log.debug('target filename: %s', target_filename) # Handling dependencies file_dep = [def_filepath] try: if ( sticker_info['input']['base'].get('format', '') not in ('lavfi', 'image2') ): base_input = path_stickers_source_dir / \ sticker_info["input"]["base"]["input"] file_dep.append(base_input) sticker_log.debug("file_dep updated: %s", base_input) except KeyError: sticker_log.info("sticker does not have base file input") base_input = None # Handling ffmpeg invocation argument array ffmpeg_argv = ['ffmpeg'] # ffmpeg: input format if sticker_info['input']['base'].get('format'): ffmpeg_argv += ['-f', sticker_info['input']['base']['format']] # ffmpeg: input frame rate if sticker_info['input']['base'].get('framerate'): ffmpeg_argv += ['-framerate', sticker_info['input']['base']['framerate']] # ffmpeg: input file itself ffmpeg_argv += ['-i', base_input] # ffmpeg: video filter ffmpeg_argv.append('-vf') filterspec = '' if sticker_info['input'].get('colors', {}).get('transparent'): transparent = sticker_info['input']['colors']['transparent'] filterspec += 'geq=\'' filterspec += f'r=if(lt(alpha(X,Y),128),{transparent[0]},r(X,Y)):' filterspec += f'g=if(lt(alpha(X,Y),128),{transparent[1]},g(X,Y)):' filterspec += f'b=if(lt(alpha(X,Y),128),{transparent[2]},b(X,Y)):' filterspec += 'a=alpha(X,Y)\',' filterspec += f'scale=w={self.dst_width}:h={ self.dst_height}:force_original_aspect_ratio=decrease' if sticker_info.get('output', {}).get('speed'): filterspec += f',setpts={ sticker_info['output']['speed']}' ffmpeg_argv.append(filterspec) ffmpeg_argv.append('-y') # ffmpeg: extra arguments ffmpeg_argv += self.ffmpeg_opts # ffmpeg: specify outut ffmpeg_argv.append(target_filename) sticker_log.debug('ffmpeg argv: %s', ffmpeg_argv) ret = { 'actions': [ffmpeg_argv], 'name': out_filename, 'targets': [target_filename], 'file_dep': file_dep } sticker_log.debug('task spec: %s', ret) return ret png = StickerTasks(ffmpeg_opts=['-preset:v', 'icon', '-frames:v', '1']) webm = StickerTasks( dst_format='webm', dst_ext='webm', ffmpeg_opts=['-codec:v', 'libvpx-vp9', '-map', 'v', '-pix_fmt', 'yuva420p'] ) webp = StickerTasks( dst_format='webp', dst_ext='webp', ffmpeg_opts=['-lossless', '1', '-frames:v', '1'] ) emoji_webp = StickerTasks( dst_format='webp', dst_ext='emoji.webp', ffmpeg_opts=['-lossless', '1', '-frames:v', '1'], dst_height=100, dst_width=100 )