Skip to content

Instantly share code, notes, and snippets.

@Ziaeemehr
Created October 29, 2025 08:34
Show Gist options
  • Select an option

  • Save Ziaeemehr/517a6804584b3f07fdd0b6ada5ea0ad5 to your computer and use it in GitHub Desktop.

Select an option

Save Ziaeemehr/517a6804584b3f07fdd0b6ada5ea0ad5 to your computer and use it in GitHub Desktop.
anki_add_tts_both_side
import base64
import os
import re
import time
from gtts import gTTS
import requests
from tqdm import tqdm
# -----------------------------
DECK_NAME = "Mobile_A1" # change it accordingly
FRONT_FIELD = "Front" # فیلدی که متن فرانسوی جلوی کارت در آن است
BACK_FIELD = "Back" # فیلدی که پشت کارت است (ممکن است شامل فارسی هم باشد)
FIELD_NAME = "Front" # فیلدی که صدا به آن افزوده می‌شود (در اینجا همان Front)
LANG = "fr"
TTS_SLOW = False
SLEEP_TIME = 0.5
CACHE_DIR = "tts_cache"
# -----------------------------
os.makedirs(CACHE_DIR, exist_ok=True)
def invoke(action, **params):
r = requests.post("http://localhost:8765", json={
"action": action,
"version": 6,
"params": params
}).json()
if r.get("error"):
raise Exception(r["error"])
return r["result"]
def strip_html(text):
return re.sub(r"<.*?>", "", text).strip()
def extract_french_text(text):
"""برمی‌گرداند فقط جمله‌ها و کلمات فرانسوی از متن (حذف فارسی و انگلیسی)"""
text = strip_html(text)
# فقط حروف و علائم فرانسوی را نگه می‌دارد
french_only = re.findall(r"[A-Za-zÀ-ÖØ-öø-ÿœŒçÇ'\- ]+", text)
return " ".join(french_only).strip()
# 1️⃣ یافتن کارت‌ها
cards = invoke("findCards", query=f'deck:"{DECK_NAME}"')
print(f"✅ {len(cards)} کارت در دک '{DECK_NAME}' یافت شد.\n")
# 2️⃣ استخراج note ID ها از کارت‌ها
notes = invoke("cardsToNotes", cards=cards)
notes_info = invoke("notesInfo", notes=notes)
for note in tqdm(notes_info, desc="🔊 در حال تولید تلفظ‌ها"):
note_id = note["noteId"]
fields = note["fields"]
# ========== FRONT ==========
front_text = fields.get(FRONT_FIELD, {}).get("value", "")
if front_text.strip() and "[sound:" not in fields.get(FRONT_FIELD, {}).get("value", ""):
clean_text = strip_html(front_text)
if clean_text:
filename = f"{clean_text}.mp3".replace(" ", "_").replace("/", "_")
audio_path = os.path.join(CACHE_DIR, filename)
if not os.path.exists(audio_path):
try:
tts = gTTS(clean_text, lang=LANG, slow=TTS_SLOW)
tts.save(audio_path)
time.sleep(SLEEP_TIME)
except Exception as e:
print(f"\n⚠️ خطا در ساخت صدا برای '{clean_text}': {e}")
continue
with open(audio_path, "rb") as f:
audio_b64 = base64.b64encode(f.read()).decode()
invoke("storeMediaFile", filename=filename, data=audio_b64)
sound_tag = f"[sound:{filename}]"
new_value = fields[FRONT_FIELD]["value"] + "<br>" + sound_tag
invoke("updateNoteFields", note={"id": note_id, "fields": {FRONT_FIELD: new_value}})
# ========== BACK ==========
back_text = fields.get(BACK_FIELD, {}).get("value", "")
if back_text.strip() and "[sound:" not in back_text:
french_part = extract_french_text(back_text)
if not french_part:
continue
filename_back = f"{french_part}_back.mp3".replace(" ", "_").replace("/", "_")
audio_path_back = os.path.join(CACHE_DIR, filename_back)
if not os.path.exists(audio_path_back):
try:
tts = gTTS(french_part, lang=LANG, slow=TTS_SLOW)
tts.save(audio_path_back)
time.sleep(SLEEP_TIME)
except Exception as e:
print(f"\n⚠️ خطا در ساخت صدا برای پشت کارت '{french_part}': {e}")
continue
with open(audio_path_back, "rb") as f:
audio_b64_back = base64.b64encode(f.read()).decode()
invoke("storeMediaFile", filename=filename_back, data=audio_b64_back)
sound_tag_back = f"[sound:{filename_back}]"
new_value_back = back_text + "<br>" + sound_tag_back
invoke("updateNoteFields", note={"id": note_id, "fields": {BACK_FIELD: new_value_back}})
print("\n✅ تمام تلفظ‌ها (Front + Back فرانسوی) اضافه شدند (با پشتیبانی Cache).")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment