from PIL import Image, ImageDraw, ImageFont
config = {
"ids": ["simple_bar"],
"name": "Simple Bar",
"panels": ["font", "size", "position"],
"default_font": "vazir"
}
# قالب HTML برای نمایش زنده در ادیتور (فرانتاند)
frontend_template = """
"""
# منطق تولید ویدیو (بکاند)
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