from PIL import Image, ImageDraw, ImageFont config = { "ids": ["simple_bar"], "name": "Simple Bar", "panels": ["font", "size", "position"], "default_font": "vazir" } # قالب HTML برای نمایش زنده در ادیتور (فرانت‌اند) frontend_template = """
{{WORD}}
""" # منطق تولید ویدیو (بک‌اند) def draw_frame(draw, img, width, height, style_config, lines, line_metrics, active_idx, font, color_parser, word_infos=None): is_rtl = getattr(style_config, 'text_direction', 'rtl') != 'ltr' # دریافت رنگ کادر اصلی از تنظیمات (کلید اختصاصی simple_bar_main_box) bg_hex = style_config.styleBgColors.get('simple_bar_main_box', '#00000080') box_color = color_parser(bg_hex, (0, 0, 0, 128)) # دریافت رنگ متن از تنظیمات (تغییر جدید) custom_hex = style_config.styleColors.get('simple_bar', '#ffffff') text_color = color_parser(custom_hex, (255, 255, 255, 255)) track_color = (255, 255, 255, 255) # سفید کامل برای خط زیرین # دریافت رنگ نوار پیشرفت (پیش‌فرض: زرد) # ساخت کلید اختصاصی: simple_bar_progress progress_key = f"{style_config.name}_progress" bar_hex = style_config.styleBgColors.get(progress_key, '#FDD835') bar_color = color_parser(bar_hex, (253, 216, 53, 255)) # تنظیمات هندسی padding_x = 30 padding_y = 10 radius = 15 bar_margin_bottom = 20 # 1. آماده‌سازی متن (چند خطی) line_height_px = int(style_config.fontSize * 1.5) text_h = len(lines) * line_height_px # پیدا کردن عرض عریض‌ترین خط text_w = 0 for line in lines: line_str = " ".join(line) try: w = draw.textlength(line_str, font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) except: w = font.getlength(line_str) if w > text_w: text_w = w # 2. محاسبه ابعاد کادر متن box_w = text_w + (padding_x * 2) box_h = text_h + (padding_y * 2) center_x = width / 2 + (style_config.x or 0) # تغییر: نقطه شروع (بالای کادر) را ثابت می‌گیریم تا نوار زرد جابجا نشود top_y = height - style_config.marginV box_x1 = center_x - (box_w / 2) box_y1 = top_y box_x2 = center_x + (box_w / 2) box_y2 = top_y + box_h # 3. محاسبه درصد پیشرفت # اصلاح باگ NoneType: بررسی می‌کنیم اگر None بود صفر در نظر بگیرد current_t = style_config.current_render_time if current_t is None: current_t = 0.0 total_dur = style_config.total_video_duration if total_dur is None or total_dur == 0: total_dur = 1.0 progress = 0.0 if total_dur > 0: progress = max(0.0, min(1.0, current_t / total_dur)) # 4. ابعاد نوار پیشرفت (ثابت نگه داشتن عرض نوار در کل پروژه) # با این ضریب (13) عرض نوار بر اساس حداکثر ظرفیت 5 کلمه بزرگ تنظیم و ثابت می‌شود bar_w_total = style_config.fontSize * 13 bar_x_start = center_x - (bar_w_total / 2) # موقعیت عمودی نوار (بالای باکس متن) # ارتفاع نوار 6 پیکسل است، ترَک 3 پیکسل. وسط‌چین عمودی نسبت به هم. track_y_center = box_y1 - bar_margin_bottom - 3 # 5. رسم خط سفید (Track) track_h = 4 draw.rounded_rectangle( [bar_x_start, track_y_center - (track_h/2), bar_x_start + bar_w_total, track_y_center + (track_h/2)], radius=10, fill=track_color ) # 6. رسم نوار زرد (Progress) bar_h = 10 filled_w = bar_w_total * progress if filled_w > 0: draw.rounded_rectangle( [bar_x_start, track_y_center - (bar_h/2), bar_x_start + filled_w, track_y_center + (bar_h/2)], radius=10, fill=bar_color ) # 7. رسم کادر سیاه شفاف draw.rounded_rectangle([box_x1, box_y1, box_x2, box_y2], radius=radius, fill=box_color) # 8. رسم متن (حلقه روی خطوط) # محاسبه نقطه شروع عمودی برای وسط‌چین کردن کل بلوک متن current_text_y = box_y1 + (box_h - text_h) / 2 # متغیرهای کمکی برای رسم کلمه به کلمه word_idx_counter = 0 space_w = draw.textlength(" ", font=font) for line in lines: # محاسبه عرض کل خط برای وسط‌چین کردن line_str = " ".join(line) try: lw = draw.textlength(line_str, font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) except: lw = font.getlength(line_str) # نقطه شروع رسم (وسط‌چین) بر اساس جهت زبان cursor_x = (center_x + (lw / 2)) if is_rtl else (center_x - (lw / 2)) for word in line: try: w_len = draw.textlength(word, font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) except: w_len = font.getlength(word) # 1. تنظیم رنگ پیش‌فرض current_word_fill = text_color # 2. اعمال رنگ کلمه فعال (Active Word) if getattr(style_config, 'useActiveColor', True) and active_idx != -1 and word_idx_counter == active_idx: active_hex = style_config.styleActiveColors.get('simple_bar', '#ffffff') current_word_fill = color_parser(active_hex, text_color) # 3. بررسی رنگ اختصاصی کاربر (اولویت بالاتر) if word_infos and word_idx_counter < len(word_infos): w_info = word_infos[word_idx_counter] if hasattr(w_info, 'color') and w_info.color: current_word_fill = color_parser(w_info.color, text_color) # منطق تایپ حرف به حرف برای بک‌اند display_word = word draw_x = (cursor_x - w_len) if is_rtl else cursor_x should_draw = True is_typewriter = getattr(style_config, 'typewriter', False) if is_typewriter and word_infos and word_idx_counter < len(word_infos): w_info = word_infos[word_idx_counter] current_t = getattr(style_config, 'current_render_time', 0.0) if current_t < w_info.start: should_draw = False elif current_t >= w_info.start and current_t < w_info.end: char_len = len(word) progress = (current_t - w_info.start) / max(0.001, w_info.end - w_info.start) visible_chars = min(char_len, int(progress * char_len) + 1) display_word = word[:visible_chars] # محاسبه عرض حروف تایپ شده برای چسبیدن به راست try: sub_w = draw.textlength(display_word, font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) except: sub_w = font.getlength(display_word) draw_x = (cursor_x - sub_w) if is_rtl else draw_x # رسم کلمه if should_draw and display_word: draw.text((draw_x, current_text_y), display_word, font=font, fill=current_word_fill, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) # حرکت به کلمه بعدی (بر اساس جهت زبان) cursor_x += (-(w_len + space_w) if is_rtl else (w_len + space_w)) word_idx_counter += 1 current_text_y += line_height_px