• ื”ืจืฉืžื”
    • ื”ืชื—ื‘ืจื•ืช
    • ื—ื™ืคื•ืฉ
    • ื“ืฃ ื”ื‘ื™ืช
    • ืื™ื ื“ืงืก ืงื™ืฉื•ืจื™ื
    • ืคื•ืกื˜ื™ื ืื—ืจื•ื ื™ื
    • ืงื‘ืœืช ื”ืชืจืื•ืช ืžื”ื“ืคื“ืคืŸ
    • ืžืฉืชืžืฉื™ื
    • ื—ื™ืคื•ืฉ ื‘ื”ื’ื“ืจื•ืช ื”ืžืชืงื“ืžื•ืช
    • ื—ื™ืคื•ืฉ ื’ื•ื’ืœ ื‘ืคื•ืจื•ื
    • ื ื™ื”ื•ืœ ื”ืžืขืจื›ืช
    • ื ื™ื”ื•ืœ ื”ืžืขืจื›ืช - ืฉืจืช private

    ๐Ÿ“‚ ๐Ÿ”Š ืคืชื™ื—ืช ืฉืœื•ื—ื•ืช ืžืจื•ื‘ื•ืช ืขื ืงื‘ืฆื™ ืฉืžืข / ื˜ืงืกื˜

    ื˜ื™ืคื™ื ืขืฆื•ืช ื•ื”ื“ื’ืžื•ืช ืžื”ืžืฉืชืžืฉื™ื
    5
    13
    282
    ื˜ื•ืขืŸ ืคื•ืกื˜ื™ื ื ื•ืกืคื™ื
    • ืžื”ื™ืฉืŸ ืœื—ื“ืฉ
    • ืžื”ื—ื“ืฉ ืœื™ืฉืŸ
    • ื”ื›ื™ ื”ืจื‘ื” ื”ืฆื‘ืขื•ืช
    ืชื’ื•ื‘ื”
    • ื”ื’ื™ื‘ื• ื›ื ื•ืฉื
    ื”ืชื—ื‘ืจื• ื‘ื›ื“ื™ ืœืคืจืกื ืชื’ื•ื‘ื”
    ื ื•ืฉื ื–ื” ื ืžื—ืง. ืจืง ืžืฉืชืžืฉื™ื ืขื ื”ืจืฉืื•ืช ืžืชืื™ืžื•ืช ื™ื•ื›ืœื• ืœืฆืคื•ืช ื‘ื•.
    • Y
      y6714453 ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™ y6714453

      ื™ืฆื™ืจืช ืฉืœื•ื—ื•ืช ืžืจื•ื‘ื•ืช

      ืฉืœื•ื ืœื›ื•ืœื!
      ื‘"ื” ื™ืฆื ืœื™ ืœืขืฉื•ืช ืกืงืจื™ืคื˜ ืคืฉื•ื˜, ืฉืขื•ื–ืจ ืœื™ืฆื•ืจ ื•ืœื”ืขืœื•ืช ืฉืœื•ื—ื•ืช ืœืžืขืจื›ืช ื™ืžื•ืช ื”ืžืฉื™ื— ื‘ืงืœื•ืช, ื›ื•ืœืœ ื™ืฆื™ืจืช ืงื‘ืฆื™ ืฉืžืข (TTS) ื•ืงื•ื‘ืฆื™ ื”ื’ื“ืจื•ืช (ext.ini) ืื•ื˜ื•ืžื˜ื™ืช.

      ืฉืœื‘ ืื—ื“:
      ืžื” ื”ืกืงืจื™ืคื˜ ืขื•ืฉื”?
      ื”ืกืงืจื™ืคื˜ ืงื•ืจื ืงื•ื‘ืฅ ืืงืกืœ ืคืฉื•ื˜, ื•ืขืœ ืคื™ื•:

      • ื™ื•ืฆืจ ืฉืœื•ื—ื•ืช ื•ืชืชื™ ืฉืœื•ื—ื•ืช ื‘ืžืขืจื›ืช ื™ืžื•ืช ื”ืžืฉื™ื—.
      • ืžืขืœื” ืงื‘ืฆื™ ื”ื’ื“ืจื” (ext.ini) ืื•ื˜ื•ืžื˜ื™ืช.
      • ืžืžื™ืจ ื˜ืงืกื˜ ืœืงื‘ืฆื™ ืฉืžืข (TTS) ื‘ืขื‘ืจื™ืช ื•ืžืขืœื” ืื•ืชื ืœืฉืœื•ื—ื” ืœืคื™ ื”ื ืชื™ื‘
      • ืžืขืœื” ืงื•ื‘ืฆื™ ื˜ืงืกื˜/TTS ืจื’ื™ืœื™ื.
      • ื”ื•ื ื“ื•ืื’ ืœื”ืชืงื ื” ืื•ื˜ื•ืžื˜ื™ืช ืฉืœ ืชื•ื›ื ืช FFmpeg.

      ืฉืœื‘ ืฉืชื™ื™ื:
      ืžื›ื™ื ื™ื ืืช ื”ืงื‘ืฆื™ื...
      ืงื•ื‘ืฅ ื”ืกืงืจื™ืคื˜: ืฉืžืจื• ืืช ืงื•ื“ ื”ืคื™ื™ืชื•ืŸ ืฉืคื” (ื‘ืกืคื•ื™ืœืจ) ื‘ืงื•ื‘ืฅ ื‘ืฉื make_audio.py (ืื• ื›ืœ ืฉื ืื—ืจ ืขื ืกื™ื•ืžืช .py).

      import pandas as pd
      import requests
      from requests_toolbelt.multipart.encoder import MultipartEncoder
      import os
      import asyncio
      import edge_tts
      import subprocess
      import json
      import urllib.request
      import zipfile
      import shutil
      import warnings
      
      warnings.filterwarnings("ignore")
      
      DEFAULT_TTS_VOICE = "he-IL-AvriNeural"
      # ื ื’ื“ื™ืจ ืืช FFMPEG_EXECUTABLE ื›-None ื‘ื”ืชื—ืœื”, ื•ืจืง ื ืืชื—ืœ ืื•ืชื• ื›ืฉื ืฆื˜ืจืš
      FFMPEG_EXECUTABLE = None 
      
      def read_excel_data(excel_file_path):
          print(f"๐Ÿ“– ืงื•ืจื ืงื•ื‘ืฅ ืืงืกืœ: {excel_file_path}")
          try:
              df = pd.read_excel(excel_file_path, header=None)
              username = str(df.iloc[0, 1]).strip()
              password = str(df.iloc[1, 1]).strip()
              
              if not username:
                  print("โŒ ืฉื’ื™ืื”: ืื ื ื”ื›ื ืก ืžืกืคืจ ืžืขืจื›ืช (ืชื B1 ื‘ืืงืกืœ).")
                  return None, None, None
              if not password:
                  print("โŒ ืฉื’ื™ืื”: ืื ื ื”ื›ื ืก ืกื™ืกืžื (ืชื B2 ื‘ืืงืกืœ).")
                  return None, None, None
      
              extensions = df.iloc[5:, [0, 1, 2, 3, 4]].dropna(how='all')
              extensions.columns = ['ื ืชื™ื‘', 'ื”ื’ื“ืจื”', 'ืฉื ื”ืฉืœื•ื—ื”', 'ืงื•ื‘ืฅ ืฉืžืข ืœื”ืขืœืื”', 'ืงื•ื‘ืฅ ื˜ืงืกื˜ ืœื”ืขืœืื”']
              
              extensions = extensions.apply(lambda x: x.astype(str).str.strip().replace('nan', '') if x.dtype == 'object' else x)
      
              print(f"โœ”๏ธ ื ืงืจืื• ื‘ื”ืฆืœื—ื”: ืžืฉืชืžืฉ={username}, ืกื™ืกืžื”={'*' * len(password)}")
              print(f"๐Ÿ“‹ ื ืชื•ื ื™ ื”ืฉืœื•ื—ื•ืช ืฉื ืงืจืื•:\n{extensions.to_string()}")
              return username, password, extensions.to_dict('records')
          except FileNotFoundError:
              print(f"โŒ ืฉื’ื™ืื”: ืงื•ื‘ืฅ ื”ืืงืกืœ '{excel_file_path}' ืœื ื ืžืฆื. ื•ื“ื ืฉื”ื•ื ื‘ืื•ืชื” ืชื™ืงื™ื™ื” ื›ืžื• ื”ืกืงืจื™ืคื˜.")
              return None, None, None
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ืงืจื™ืืช ืงื•ื‘ืฅ ื”ืืงืกืœ: {e}")
              return None, None, None
      
      def create_ext_ini_file(extension_type, extension_name, output_file_name, is_subpath=False, direct_ini_content=None):
          content = ""
      
          if direct_ini_content:
              content = direct_ini_content.strip()
              if not content.endswith('\n'):
                  content += '\n'
              print(f"โš™๏ธ ื™ื•ืฆืจ ืงื•ื‘ืฅ ext.ini ืžืชื•ื›ืŸ INI ื™ืฉื™ืจ.")
          elif is_subpath:
              content = "type=menu\n"
          else:
              normalized_extension_type = str(extension_type).strip().lower() if extension_type else ""
              if pd.isna(extension_type):
                  normalized_extension_type = ""
      
              if normalized_extension_type == 'ื”ืฉืžืขืช ืงื‘ืฆื™ื':
                  content = "type=playfile\n"
              elif normalized_extension_type == 'ืชืคืจื™ื˜':
                  content = "type=menu\n"
              elif normalized_extension_type in ['ื”ืงืœื˜ื”', 'ื”ืงืœื˜ื•ืช']:
                  content = "type=record\n"
              else:
                  print(f"โš ๏ธ ืกื•ื’ ืฉืœื•ื—ื” ืœื ืžื•ื›ืจ '{extension_type}'. ืžื•ื’ื“ืจ ื›-'ืชืคืจื™ื˜' ื›ื‘ืจื™ืจืช ืžื—ื“ืœ.")
                  content = "type=menu\n"
                  
              if extension_name and extension_name.strip():
                  content += f"title={extension_name.strip()}\n"
                  
          try:
              with open(output_file_name, 'w', encoding='utf-8') as f:
                  f.write(content)
              print(f"โœ… ื ื•ืฆืจ ืงื•ื‘ืฅ {output_file_name} ืขื ืชื•ื›ืŸ: {content.strip() or '[ืจื™ืง]'}")
              return True
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจืช ืงื•ื‘ืฅ {output_file_name}: {e}")
              return False
      
      async def create_and_convert_audio_file(text, output_wav_name):
          output_mp3_temp = f"temp_{os.path.splitext(output_wav_name)[0]}.mp3"
          
          print(f"๐Ÿ”Š ื™ื•ืฆืจ ืงื•ื‘ืฅ ืฉืžืข {output_wav_name} ืžื˜ืงืกื˜: '{text}'")
          
          # ื”ื‘ื“ื™ืงื” ื•ื”ืื™ืชื—ื•ืœ ืฉืœ FFMPEG_EXECUTABLE ืžืชืจื—ืฉื™ื ื›ืืŸ ื‘ืœื‘ื“
          global FFMPEG_EXECUTABLE
          if not FFMPEG_EXECUTABLE or not os.path.exists(FFMPEG_EXECUTABLE):
              print(f"โณ ืžื ืกื” ืœื”ื•ืจื™ื“ ืื• ืœืืชืจ ืืช FFmpeg ื›ื™ื•ื•ืŸ ืฉื ื“ืจืฉ ืœื˜ื™ืคื•ืœ ื‘ืงื‘ืฆื™ ืฉืžืข...")
              ensure_ffmpeg() # ืงื•ืจืื™ื ืœ-ensure_ffmpeg ืจืง ื›ืืŸ
              if not FFMPEG_EXECUTABLE or not os.path.exists(FFMPEG_EXECUTABLE):
                  print(f"โŒ ืœื ื ื™ืชืŸ ืœื™ืฆื•ืจ ืงื•ื‘ืฅ ืฉืžืข {output_wav_name} ื›ื™ FFmpeg ืœื ื–ืžื™ืŸ.")
                  return False
      
          try:
              comm = edge_tts.Communicate(text, voice=DEFAULT_TTS_VOICE)
              await comm.save(output_mp3_temp)
              print(f"โœ… ืงื•ื‘ืฅ ืื•ื“ื™ื• ื–ืžื ื™ ื ื•ืฆืจ ื‘ื”ืฆืœื—ื”: {output_mp3_temp}")
      
              result = subprocess.run(
                  [FFMPEG_EXECUTABLE, "-loglevel", "error", "-y", "-i", output_mp3_temp, "-ar", "8000", "-ac", "1", "-acodec", "pcm_s16le", output_wav_name],
                  check=True
              )
              print(f"โœ… ืงื•ื‘ืฅ WAV ืกื•ืคื™ ื ื•ืฆืจ ื‘ื”ืฆืœื—ื”: {output_wav_name}")
              return True
          except edge_tts.exceptions.NoAudioReceived as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจืช ืงื•ื‘ืฅ ืฉืžืข {output_wav_name}: {e}")
              print("โ— ื•ื“ื/ื™ ื—ื™ื‘ื•ืจ ืื™ื ื˜ืจื ื˜ ืชืงื™ืŸ, ืื™ืŸ ื—ืกื™ืžื•ืช ื—ื•ืžืช ืืฉ/ืื ื˜ื™-ื•ื™ืจื•ืก, ืื• ื ืกื”/ื™ ืงื•ืœ ืื—ืจ.")
          except subprocess.CalledProcessError as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ื”ืžืจืช ืื•ื“ื™ื• (FFmpeg) ืขื‘ื•ืจ {output_wav_name}: {e}. ื•ื“ื/ื™ ืฉ-FFmpeg ืžื•ืชืงืŸ ื•ื ื’ื™ืฉ.")
          except FileNotFoundError:
              print(f"โŒ ืฉื’ื™ืื”: FFmpeg ืœื ื ืžืฆื ื‘ื ืชื™ื‘ ื”ืžื•ื’ื“ืจ. ื•ื“ื/ื™ ืฉื”ื•ืจื“ ื•ื”ื•ื’ื“ืจ ื›ืจืื•ื™.")
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื›ืœืœื™ืช ื‘ื™ืฆื™ืจืช/ื”ืžืจืช ืื•ื“ื™ื• ืขื‘ื•ืจ {output_wav_name}: {e}")
          finally:
              if os.path.exists(output_mp3_temp):
                  os.remove(output_mp3_temp)
          return False
      
      def create_text_file(text, output_file_name):
          print(f"๐Ÿ“ ื™ื•ืฆืจ ืงื•ื‘ืฅ ื˜ืงืกื˜ {output_file_name} ืขื ืชื•ื›ืŸ: {text}")
          try:
              with open(output_file_name, 'w', encoding='utf-8') as f:
                  f.write(text)
              print(f"โœ… ืงื•ื‘ืฅ ื˜ืงืกื˜ {output_file_name} ื ื•ืฆืจ ื‘ื”ืฆืœื—ื”")
              return True
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจืช ืงื•ื‘ืฅ ื˜ืงืกื˜ {output_file_name}: {e}")
              return False
      
      def upload_file_to_yemot(file_path, yemot_full_path, username, password):
          token = f"{username}:{password}"
          
          file_ext = file_path.lower()
          if file_ext.endswith('.wav'):
              content_type = 'audio/wav'
          elif file_ext.endswith('.txt') or file_ext.endswith('.tts'):
              content_type = 'text/plain'
          elif file_ext.endswith('.ini'):
              content_type = 'text/plain'
          else:
              content_type = 'application/octet-stream'
              
          try:
              with open(file_path, 'rb') as f_read:
                  m = MultipartEncoder(fields={
                      "token": token,
                      "path": yemot_full_path,
                      "upload": (os.path.basename(file_path), f_read, content_type)
                  })
                  print(f"โฌ†๏ธ ืžืขืœื” ืงื•ื‘ืฅ {os.path.basename(file_path)} ืœื ืชื™ื‘: {yemot_full_path}")
                  r = requests.post("https://www.call2all.co.il/ym/api/UploadFile", data=m, headers={'Content-Type': m.content_type})
                  r.raise_for_status()
                  print(f"โœ”๏ธ ื”ืงื•ื‘ืฅ '{os.path.basename(file_path)}' ื”ื•ืขืœื” ื‘ื”ืฆืœื—ื”")
                  return True
          except FileNotFoundError:
              print(f"โŒ ืฉื’ื™ืื”: ืงื•ื‘ืฅ ืžืงื•ืจ ืœื”ืขืœืื” ืœื ื ืžืฆื: {file_path}")
              return False
          except requests.exceptions.RequestException as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ื”ืขืœืืช ืงื•ื‘ืฅ {os.path.basename(file_path)}: {e}")
              print(f"๐Ÿ“ค ืชื’ื•ื‘ืช ืฉืจืช: {r.text if 'r' in locals() else 'ืื™ืŸ ืชื’ื•ื‘ื” ืžื”ืฉืจืช'}")
              return False
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ืœืชื™ ืฆืคื•ื™ื” ื‘ื”ืขืœืืช ืงื•ื‘ืฅ {os.path.basename(file_path)}: {e}")
          return False
      
      def generate_subpaths(full_path):
          parts = [p for p in full_path.split('/') if p]
          subpaths = []
          current_path_parts = []
          for part in parts[:-1]:
              current_path_parts.append(part)
              subpaths.append('/'.join(current_path_parts))
          return subpaths
      
      def test_yemot_credentials(username, password):
          token = f"{username}:{password}"
          
          dummy_file_name = "temp_cred_test.txt"
          dummy_file_path_local = os.path.join(os.getcwd(), dummy_file_name)
          yemot_target_path = "ivr2:/temp_credential_test_folder/test_file.txt"
      
          try:
              with open(dummy_file_path_local, "w") as f:
                  f.write("test_content")
              
              print("โณ ื‘ื•ื“ืง ืฉื ืžืฉืชืžืฉ ื•ืกื™ืกืžื ืžื•ืœ ืžืขืจื›ืช ื™ืžื•ืช ื”ืžืฉื™ื—...")
              
              with open(dummy_file_path_local, 'rb') as f_read_dummy:
                  m = MultipartEncoder(fields={
                      "token": token,
                      "path": yemot_target_path,
                      "upload": (dummy_file_name, f_read_dummy, 'text/plain')
                  })
                  
                  r = requests.post("https://www.call2all.co.il/ym/api/UploadFile", data=m, headers={'Content-Type': m.content_type})
              
              response_text = r.text.strip()
              response_text_upper = response_text.upper()
      
              if r.status_code == 401 or r.status_code == 403:
                  print("โŒ ืฉื ืžืฉืชืžืฉ ื•/ืื• ืกื™ืกืžื ืœื ืชืงื™ืŸ. ืฉื’ื™ืืช ืื™ืžื•ืช (Unauthorized/Forbidden).")
                  return False
      
              try:
                  response_json = json.loads(response_text)
                  if response_json.get("responseStatus") == "OK" and response_json.get("success") == True:
                      print("โœ… ืฉื ืžืฉืชืžืฉ ื•ืกื™ืกืžื ืชืงื™ื ื™ื.")
                      return True
                  elif "TOKEN INVALID" in response_text_upper or \
                       "ื”ืžืกืคืจ ืื™ื ื• ืžื•ืจืฉื”" in response_text_upper or \
                       "THE NUMBER IS NOT VALID" in response_text_upper or \
                       "ืžืกืคืจ ื”ืžืขืจื›ืช ืื™ื ื• ืชืงื™ืŸ" in response_text_upper:
                      print("โŒ ืฉื ืžืฉืชืžืฉ ื•/ืื• ืกื™ืกืžื ืœื ืชืงื™ืŸ.")
                      return False
                  else:
                      print(f"โŒ ืฉื ืžืฉืชืžืฉ ื•/ืื• ืกื™ืกืžื ืœื ืชืงื™ืŸ. ืชื’ื•ื‘ืช ืฉืจืช ืœื ืฆืคื•ื™ื”: {response_text}")
                      return False
              except json.JSONDecodeError:
                  if "ERROR:" in response_text_upper:
                      if "TOKEN INVALID" in response_text_upper or \
                         "ื”ืžืกืคืจ ืื™ื ื• ืžื•ืจืฉื”" in response_text_upper or \
                         "THE NUMBER IS NOT VALID" in response_text_upper or \
                         "ืžืกืคืจ ื”ืžืขืจื›ืช ืื™ื ื• ืชืงื™ืŸ" in response_text_upper:
                          print("โŒ ืฉื ืžืฉืชืžืฉ ื•/ืื• ืกื™ืกืžื ืœื ืชืงื™ืŸ.")
                          return False
                      else:
                          print(f"โŒ ืฉื ืžืฉืชืžืฉ ื•/ืื• ืกื™ืกืžื ืœื ืชืงื™ืŸ. ืฉื’ื™ืืช ืฉืจืช ืœื ืกืคืฆื™ืคื™ืช: {response_text}")
                          return False
                  else:
                      print(f"โŒ ืฉื ืžืฉืชืžืฉ ื•/ืื• ืกื™ืกืžื ืœื ืชืงื™ืŸ. ืชื’ื•ื‘ืช ืฉืจืช ื‘ืคื•ืจืžื˜ ืœื ืฆืคื•ื™: {response_text}")
                      return False
      
          except requests.exceptions.RequestException as e:
              print(f"โŒ ืฉื’ื™ืืช ื—ื™ื‘ื•ืจ ืื• ืชืงืฉื•ืจืช ื‘ืขืช ื‘ื“ื™ืงืช ื”ืชื—ื‘ืจื•ืช: {e}")
              print("โ— ื•ื“ื/ื™ ื—ื™ื‘ื•ืจ ืื™ื ื˜ืจื ื˜ ืชืงื™ืŸ.")
              return False
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ืœืชื™ ืฆืคื•ื™ื” ื‘ืขืช ื‘ื“ื™ืงืช ื”ืชื—ื‘ืจื•ืช: {e}")
              return False
          finally:
              if os.path.exists(dummy_file_path_local):
                  try:
                      os.remove(dummy_file_path_local)
                  except OSError as e:
                      print(f"โš ๏ธ ืื–ื”ืจื”: ืœื ื ื™ืชืŸ ืœืžื—ื•ืง ืืช ืงื•ื‘ืฅ ื”ื“ืžื” '{dummy_file_path_local}': {e}")
      
      def ensure_ffmpeg():
          global FFMPEG_EXECUTABLE
      
          local_ffmpeg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin", "ffmpeg.exe")
      
          if os.path.exists(local_ffmpeg_path):
              print("โฉ ffmpeg ื›ื‘ืจ ืงื™ื™ื ื‘ื ืชื™ื‘ ื”ืžืงื•ืžื™.")
              FFMPEG_EXECUTABLE = local_ffmpeg_path
              return
      
          print("โฌ‡๏ธ ืžื•ืจื™ื“ ื•ืžื’ื“ื™ืจ ffmpeg...")
          os.makedirs(os.path.dirname(local_ffmpeg_path), exist_ok=True)
      
          url = "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip"
          archive_path = os.path.join(os.path.dirname(local_ffmpeg_path), "ffmpeg.zip")
          
          try:
              print(f"๐Ÿ“ฅ ืžื•ืจื™ื“ ืงื•ื‘ืฅ FFmpeg ืž: {url}")
              urllib.request.urlretrieve(url, archive_path)
              print(f"๐Ÿ“ฆ ื—ื•ืœืฅ ืืช ืงื•ื‘ืฅ FFmpeg ืž: {archive_path}")
      
              with zipfile.ZipFile(archive_path, 'r') as zip_ref:
                  found_ffmpeg_in_zip = False
                  for member in zip_ref.namelist():
                      if member.endswith('ffmpeg.exe'):
                          source = zip_ref.open(member)
                          target = open(local_ffmpeg_path, "wb")
                          with source, target:
                              shutil.copyfileobj(source, target)
                          print(f"โœ… FFmpeg ื”ื•ื›ืŸ ื‘ื”ืฆืœื—ื” ื‘: {local_ffmpeg_path}")
                          FFMPEG_EXECUTABLE = local_ffmpeg_path
                          found_ffmpeg_in_zip = True
                          break
                  
                  if not found_ffmpeg_in_zip:
                      print("โŒ ืฉื’ื™ืื”: ืœื ื ืžืฆื ffmpeg.exe ื‘ืชื•ืš ืงื•ื‘ืฅ ื”-ZIP ืฉื”ื•ืจื“.")
                      FFMPEG_EXECUTABLE = None
                      return
      
          except Exception as e:
              print(f"โŒ ืฉื’ื™ืื” ื‘ื”ื•ืจื“ื” ืื• ื‘ื—ื™ืœื•ืฅ ืฉืœ FFmpeg: {e}")
              print("โ— ื•ื“ื/ื™ ื—ื™ื‘ื•ืจ ืื™ื ื˜ืจื ื˜ ืชืงื™ืŸ ื•ื ืกื”/ื™ ืฉื•ื‘.")
              FFMPEG_EXECUTABLE = None
          finally:
              if os.path.exists(archive_path):
                  try:
                      os.remove(archive_path)
                  except OSError as e:
                      print(f"โš ๏ธ ืื–ื”ืจื”: ืœื ื ื™ืชืŸ ืœืžื—ื•ืง ืืช ืงื•ื‘ืฅ ื”-ZIP ื”ื–ืžื ื™ '{archive_path}': {e}")
      
          if not os.path.exists(local_ffmpeg_path):
              print("โŒ FFmpeg ืœื ื”ื•ืชืงืŸ ืื•ื˜ื•ืžื˜ื™ืช. ืื ื ื”ื•ืจื“/ื™ ื•ื”ืชืงืŸ/ื™ ืื•ืชื• ื™ื“ื ื™ืช.")
              print("ื ื™ืชืŸ ืœื”ื•ืจื™ื“ ืžื›ืืŸ: https://www.gyan.dev/ffmpeg/builds/ (ื‘ื—ืจ/ื™ ืืช ื’ืจืกืช Essentials ืื• Full ืขื‘ื•ืจ Windows).")
              FFMPEG_EXECUTABLE = None
      
      async def main():
          print("๐Ÿš€ ืžืชื—ื™ืœ ืชื”ืœื™ืš ื™ืฆื™ืจืช ื•ื”ืขืœืืช ืฉืœื•ื—ื•ืช...")
          
          # ensure_ffmpeg() ื”ื•ืกืจ ืžื›ืืŸ - ื™ื•ืคืขืœ ืจืง ื‘ืžื™ื“ืช ื”ืฆื•ืจืš
          # if not FFMPEG_EXECUTABLE or not os.path.exists(FFMPEG_EXECUTABLE):
          #     print("โš ๏ธ ื”ืชื”ืœื™ืš ื ืขืฆืจ ืขืงื‘ ื—ื•ืกืจ ื‘-FFmpeg. ืื ื ื”ืชืงืŸ/ื™ ืื•ืชื• ื™ื“ื ื™ืช ื›ื“ื™ ืœื”ืžืฉื™ืš.")
          #     return
      
          script_dir = os.path.dirname(os.path.abspath(__file__))
          excel_file = os.path.join(script_dir, "ืžืฉื ื™.xlsx")
          
          print(f"๐Ÿ“‚ ืชื™ืงื™ื™ืช ื”ืกืงืจื™ืคื˜: {script_dir}")
          print(f"๐Ÿ” ืžื—ืคืฉ ืงื•ื‘ืฅ ืืงืกืœ: {excel_file}")
          if not os.path.exists(excel_file):
              print(f"โŒ ืงื•ื‘ืฅ ื”ืืงืกืœ '{excel_file}' ืœื ื ืžืฆื ื‘ืชื™ืงื™ื™ืช ื”ืกืงืจื™ืคื˜!")
              print(f"๐Ÿ“‹ ืงื‘ืฆื™ื ื‘ืชื™ืงื™ื™ื”: {os.listdir(script_dir)}")
              return
      
          username, password, extensions_data = read_excel_data(excel_file)
          if not username or not password or not extensions_data:
              print("โš ๏ธ ื”ืชื”ืœื™ืš ื ืขืฆืจ ืขืงื‘ ื—ื•ืกืจ ื‘ื ืชื•ื ื™ ื”ืชื—ื‘ืจื•ืช ืื• ื ืชื•ื ื™ ืฉืœื•ื—ื•ืช.")
              return
      
          if not test_yemot_credentials(username, password):
              print("โš ๏ธ ื”ืชื”ืœื™ืš ื ืขืฆืจ ืขืงื‘ ืคืจื˜ื™ ื”ืชื—ื‘ืจื•ืช ืœื ืชืงื™ื ื™ื.")
              return
      
          processed_subfolders = set()
      
          for ext in extensions_data:
              path = ext.get('ื ืชื™ื‘')
              extension_type = ext.get('ื”ื’ื“ืจื”')
              extension_name = ext.get('ืฉื ื”ืฉืœื•ื—ื”')
              audio_content = ext.get('ืงื•ื‘ืฅ ืฉืžืข ืœื”ืขืœืื”', '')
              text_content = ext.get('ืงื•ื‘ืฅ ื˜ืงืกื˜ ืœื”ืขืœืื”', '')
      
              if isinstance(path, str): path = path.strip()
              if isinstance(extension_type, str): extension_type = extension_type.strip()
              if isinstance(extension_name, str): extension_name = extension_name.strip()
              if isinstance(audio_content, str): audio_content = audio_content.strip().replace('...', '')
              if isinstance(text_content, str): text_content = text_content.strip().replace('...', '')
      
              if not path:
                  print(f"โš ๏ธ ื“ื™ืœื•ื’ ืขืœ ืฉืœื•ื—ื”: ื ืชื™ื‘ ืœื ืชืงื™ืŸ ืื• ื—ืกืจ '{path}'")
                  continue
                  
              clean_path = path
              
              print(f"\n๐Ÿ“‹ ืžืขื‘ื“ ืฉืœื•ื—ื”: {extension_name} ({path})")
      
              subpaths_to_create = generate_subpaths(clean_path)
              for subpath_item in subpaths_to_create:
                  full_sub_yemot_ini_path = f"ivr2:/{subpath_item}/ext.ini"
                  if full_sub_yemot_ini_path not in processed_subfolders:
                      print(f"โš™๏ธ ืžืขื‘ื“ ืชืช-ืฉืœื•ื—ื” (ื‘ืจื™ืจืช ืžื—ื“ืœ menu): {full_sub_yemot_ini_path}")
                      ini_file_name = "ext.ini"
                      if create_ext_ini_file(None, None, ini_file_name, is_subpath=True):
                          if not upload_file_to_yemot(ini_file_name, full_sub_yemot_ini_path, username, password):
                              print(f"โŒ ืฉื’ื™ืื” ื‘ื”ืขืœืืช INI ืœืชืช-ืฉืœื•ื—ื” {full_sub_yemot_ini_path}. ื”ืžืฉืš ืขื™ื‘ื•ื“...")
                      else:
                          print(f"โŒ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจืช INI ืœืชืช-ืฉืœื•ื—ื” {full_sub_yemot_ini_path}. ืžื“ืœื’.")
                          
                      if os.path.exists(ini_file_name):
                          os.remove(ini_file_name)
                      processed_subfolders.add(full_sub_yemot_ini_path)
      
              main_ini_file = "ext.ini"
              target_yemot_ini_path = f"ivr2:/{clean_path}/ext.ini"
      
              should_create_main_ext_ini = True
              
              is_direct_ini_input = False
              ini_content_for_direct_upload = ""
              
              if isinstance(extension_type, str) and extension_type.strip():
                  if ("type=" in extension_type.strip().lower() and "=" in extension_type.strip()) or "\n" in extension_type.strip():
                      is_direct_ini_input = True
                      ini_content_for_direct_upload = extension_type.strip()
      
              if is_direct_ini_input:
                  if not create_ext_ini_file(None, None, main_ini_file, direct_ini_content=ini_content_for_direct_upload):
                      print(f"โŒ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจืช INI ืจืืฉื™ ืžืชื•ื›ืŸ ื™ืฉื™ืจ ืขื‘ื•ืจ {extension_name}. ืžื“ืœื’ ืขืœ ืฉืœื•ื—ื” ื–ื•.")
                      continue
              else:
                  normalized_ext_type_for_check = str(extension_type).strip().lower() if extension_type else ""
                  if pd.isna(extension_type):
                      normalized_ext_type_for_check = ""
      
                  if normalized_ext_type_for_check == 'ืชื™ืงื™ื™ื”' or not normalized_ext_type_for_check:
                      print(f"๐Ÿ—‚๏ธ ืฉืœื•ื—ื” ืžืกื•ื’ '{normalized_ext_type_for_check or 'ืจื™ืง'}'. ืœื ื ื•ืฆืจ ืงื•ื‘ืฅ ext.ini ืจืืฉื™.")
                      should_create_main_ext_ini = False
                  else:
                      if not create_ext_ini_file(extension_type, extension_name, main_ini_file):
                          print(f"โŒ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจืช INI ืจืืฉื™ ืขื‘ื•ืจ {extension_name}. ืžื“ืœื’ ืขืœ ืฉืœื•ื—ื” ื–ื•.")
                          continue
                  
              if should_create_main_ext_ini:
                  if not upload_file_to_yemot(main_ini_file, target_yemot_ini_path, username, password):
                      print(f"โŒ ืฉื’ื™ืื” ื‘ื”ืขืœืืช INI ืจืืฉื™ ืขื‘ื•ืจ {extension_name}. ื”ืžืฉืš ืขื™ื‘ื•ื“...")
                      if os.path.exists(main_ini_file):
                          os.remove(main_ini_file)
                      continue
                  
                  if os.path.exists(main_ini_file):
                      os.remove(main_ini_file)
      
              # ื”ืฉื™ื ื•ื™ ื”ืขื™ืงืจื™: ื‘ื“ื™ืงืช audio_content ืœืคื ื™ ื ื™ืกื™ื•ืŸ ื™ืฆื™ืจืช ืงื•ื‘ืฅ ืฉืžืข
              if audio_content and audio_content.strip(): 
                  audio_texts = [t.strip() for t in audio_content.split(' + ') if t.strip()]
                  for i, text_to_tts in enumerate(audio_texts):
                      audio_wav_final = f"{str(i).zfill(3)}.wav"
                      
                      # ืงืจื™ืื” ืœ-create_and_convert_audio_file ืชื˜ืคืœ ื‘ืื™ืชื—ื•ืœ FFmpeg ืื ื ื“ืจืฉ
                      if await create_and_convert_audio_file(text_to_tts, audio_wav_final):
                          audio_yemot_path = f"ivr2:/{clean_path}/{audio_wav_final}"
                          upload_file_to_yemot(audio_wav_final, audio_yemot_path, username, password)
                      else:
                          print(f"โš ๏ธ ื“ื™ืœื•ื’ ืขืœ ื”ืขืœืืช ืงื•ื‘ืฅ ืฉืžืข {audio_wav_final} ืขืงื‘ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจื”/ื”ืžืจื”.")
                      
                      if os.path.exists(audio_wav_final):
                          os.remove(audio_wav_final)
      
              # ืงื‘ืฆื™ ื˜ืงืกื˜ ืื™ื ื ื“ื•ืจืฉื™ื ืืช FFmpeg, ืื– ื”ื ืžืžืฉื™ื›ื™ื ืœืคืขื•ืœ ื›ืจื’ื™ืœ
              if text_content and text_content.strip() and (extension_type and pd.notna(extension_type)):
                  text_files_list = [t.strip() for t in text_content.split(' + ') if t.strip()]
                  for i, item_text_content in enumerate(text_files_list):
                      if os.path.exists(item_text_content):
                          file_name_to_upload = os.path.basename(item_text_content)
                          print(f"๐Ÿ“„ ืžืขืœื” ืงื•ื‘ืฅ ื˜ืงืกื˜ ืงื™ื™ื: {file_name_to_upload}")
                          text_yemot_path = f"ivr2:/{clean_path}/{file_name_to_upload}"
                          upload_file_to_yemot(item_text_content, text_yemot_path, username, password)
                      else:
                          text_file_name_final = f"{str(i).zfill(3)}.tts"
                          if create_text_file(item_text_content, text_file_name_final):
                              text_yemot_path = f"ivr2:/{clean_path}/{text_file_name_final}"
                              upload_file_to_yemot(text_file_name_final, text_yemot_path, username, password)
                          else:
                              print(f"โš ๏ธ ื“ื™ืœื•ื’ ืขืœ ื”ืขืœืืช ืงื•ื‘ืฅ ื˜ืงืกื˜ {text_file_name_final} ืขืงื‘ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจื”.")
                          
                          if os.path.exists(text_file_name_final):
                              os.remove(text_file_name_final)
      
          print("\nโœ… ืกื™ื•ื ืขื™ื‘ื•ื“ ื›ืœ ื”ืฉืœื•ื—ื•ืช ื‘ื”ืฆืœื—ื”!")
      
      if __name__ == "__main__":
          asyncio.run(main())
      

      ืงื•ื‘ืฅ ื”ืืงืกืœ: ื ื™ืชืŸ ืœื”ื•ืจื™ื“ ืžืคื” ืžืฉื ื™.xlsx

      ื—ืฉื•ื‘ ืžืื•ื“: ืฉื™ืžืจื• ืขืœ ืžื‘ื ื” ื”ืืงืกืœ ื›ืžื• ื‘ื“ื•ื’ืžื” ื”ืžืงื•ืจื™ืช ืฉืงื™ื‘ืœืชื.
      ื—ืฉื•ื‘ ืžืื•ื“ ืฉืฉืชื™ ื”ืงื‘ืฆื™ื (ื”ืงื•ื“ ืœื”ืจืฆื” ื•ื”ืงื•ื‘ืฅ ืืงืกืœ) ื™ื”ื™ื• ื‘ืื•ืชื” ืชื™ืงื™ื™ื” ืขืœ ื”ืžื—ืฉื‘!!

      ืฉืœื‘ ืฉืœื•ืฉ:
      ืคืฉื•ื˜ ืชืžืœืื• ืืช ื”ืงื•ื‘ืฅ ืืงืกืœ ืœืคื™ ื”ืฉืœื•ื—ื•ืช ืฉืชืจืฆื• ืœื™ื™ืฆืจ ืœื“ื•ื’ืžื:

      c458ec9e-18ac-4e08-af3f-bb74a850dea0-image.png

      ื›ืžื” ื›ืœืœื™ื ื‘ื ื•ื’ืข ืœืžื™ืœื•ื™ ื”ืฉื“ื•ืช:

      • ื”ืฉื“ื•ืช ืฉื”ื ื—ื•ื‘ื” ื–ื” - ืœืžืขืœื” ื‘ืฉื ืžืฉืชืžืฉ ื•ื”ืกื™ืกืžื”, ื—ื•ืฅ ืžื–ื” ื”ื›ืœ ื™ื›ื•ืœ ืœื”ื™ืฉืืจ ืจื™ืง - ืื "ื”ื’ื“ืจื•ืช ืฉืœื•ื—ื”" ื ืฉืืจ ืจื™ืง - ื–ื” ืžื™ื™ืฆืจ ืชื™ืงื™ื™ื” ื‘ืœื‘ื“ ื‘ืœื™ ืฉื•ื ื”ื’ื“ืจื•ืช.

      • ื›ืฉืžืžืœืื™ื ื”ื’ื“ืจื•ืช ืฉืœื•ื—ื” ื‘ืขื™ืงืจื•ืŸ ืฆืจื™ืš ืœื”ื›ื ื™ืก ื‘ืฉื“ื” ืืช ื”ื”ื’ื“ืจื•ืช ืฉืœ ื”ืฉืœื•ื—ื” ืื‘ืœ ืื ื–ื” ืฉืœื•ื—ื” - ืื• ืชืคืจื™ื˜ ืื• ื”ืฉืžืขืช ืงื‘ืฆื™ื ืื• ื”ืงืœื˜ื” - ืืคืฉืจ ืœื”ื›ื ื™ืก ืืช ื”ืžื™ืœื™ื "ื”ืงืœื˜ื”" ืื• "ื”ืฉืžืขืช ืงื‘ืฆื™ื" ืื• "ืชืคืจื™ื˜" ื•ื–ื” ื™ื™ืฆืจ ืœื‘ื“ ืืช ื”ื’ื“ืจื•ืช ื”ืฉืœื•ื—ื”.

      • ืื ืจื•ืฆื™ื ืœืžืฉืœ ืœืขืฉื•ืช ืžืื” ืชื™ืงื™ื•ืช / ืฉืœื•ื—ื•ืช - ืœื ืฆืจื™ืš ืœืžืœืื•ืช ืžืื” ืฉื“ื•ืช ืืคืฉืจ ืคืฉื•ื˜ ืœืขืฉื•ืช ืฉืชื™ ืฉื“ื•ืช ืœืžืฉืœ:
        1/1/1
        1/1/2
        ื•ืื– ืœื’ืจื•ืจ ื›ืœืคื™ ืžื˜ื”.

      • ื‘ืžื™ื“ื” ื•ืžื’ื“ื™ืจื™ื ื‘"ื ืชื™ื‘" ืœืžืฉืœ ืฉืœื•ื—ื” 1/1/1 ื•ืžื’ื“ื™ืจื™ื ื‘"ื”ื’ื“ืจื•ืช ื”ืฉืœื•ื—ื”" ืฉื™ื”ื™ื” ื”ืฉืžืขืช ืงื‘ืฆื™ื ืื• ื›ืœ ื”ื’ื“ืจื” ืื—ืจืช - ื ื•ืฆืจ ืฉืœื•ื—ื” ื‘ืชื•ืš ืฉืœื•ื—ื” ื‘ืชื•ืš ืฉืœื•ื—ื” - ื•ืžื›ื™ื•ืŸ ืฉืจืง ื”ืฉืœื•ื—ื” ื”ืื—ืจื•ื ื” ื”ื•ื’ื“ืจื” - ื‘ืจื™ืจืช ืžื—ื“ืœ ืฉื”ืฉืœื•ื—ื•ืช ืžื•ื’ื“ืจื•ืช ืœ"ืชืคืจื™ื˜".

      • ื‘"ื”ืขืœืืช ืงื•ื‘ืฅ ืฉืžืข / ื˜ืงืกื˜ - ื‘ืจื™ืจืช ืžื—ื“ืœ ื ื•ืฆืจ ืงื•ื‘ืฅ ื‘ืฉื 000 ืืคืฉืจ ืœื™ื™ืฆืจ ื›ืžื” ืงื‘ืฆื™ื ื‘ื›ืœ ืฉืœื•ื—ื” ืขืœ ื™ื“ื™ ื”ืคืจื“ื” ืฉืœ + ืœื“ื•ื’ืžื” "ืฉืœื•ื ื•ื‘ืจื•ื›ื™ื ื”ื‘ืื™ื ืœืงื• ื”ืžื™ื“ืข + ืœืชื›ื ื™ื ื ื•ืกืคื™ื ื”ืงืฉ ื›ื•ื›ื‘ื™ืช"
        ื›ืš ื ื•ืฆืจื™ื ืฉืชื™ ืงื‘ืฆื™ื 000, 001.

      ืฉืœื‘ ืืจื‘ืข:
      ืฉื™ืžื• ืœื‘ ืœืคื ื™ ืฉืžืจื™ืฆื™ื ืืช ื”ืงื•ื“ ืฆืจื™ืš ืœื•ื•ื“ื ืฉืคื™ื™ืชื•ืŸ ืžื•ืชืงืŸ ืขืœ ื”ืžื—ืฉื‘
      ืืคืฉืจ ืœื”ืชืงื™ืŸ ืขื ื”ืคืงื•ื“ื” ื”ื–ื• ื‘ืฉื•ืจืช ื”ืคืงื•ื“ื”

      winget install Python.Python.3.12 --source winget
      

      ืœืื—ืจ ืžื›ืŸ ืชืจื™ืฆื• ืืช ื”ืคืงื•ื“ื” ื”ื–ื•

      pip install pandas requests requests-toolbelt edge-tts
      

      ื•ืื– ืœื’ืจื•ืจ ืืช ื”ืงื•ื‘ืฅ ืคื™ื™ืชื•ืŸ ืœืชื•ืš ื”ืฉื•ืจืช ืคืงื•ื“ื” (ืื• ืœื”ืขืชื™ืง ืืช ื”ื ืชื™ื‘ ืฉืœื•) ื•ื›ืžื•ื‘ืŸ - ืื ื˜ืจ!

      ื”ืฉื™ืžื•ืฉ ื‘ืื—ืจื™ื•ืช ื”ืžืฉืชืžืฉ ื‘ืœื‘ื“!
      ืื ืžื™ืฉื”ื• ืžืกืชื‘ืš ืขื ื”ื”ืจืฆื” ืฉืœ ื”ืงื•ื“ ืืฉืžื— ืœืขื–ื•ืจ
      ืืฉืžื— ืœืฉืžื•ืข ืฉื–ื” ืขื–ืจ ืœืžื™ืฉื”ื•... ื•ืืคื™ืœื• ืœืžื™ืฉื”ื• ืื—ื“... ื“ื™ื™ื ื•!

      N ื 2 ืชื’ื•ื‘ื•ืช ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 9
      • ื”ื•ื–ื›ืจ ืขืœ-ื™ื“ื™  ืช ืชืคืืจืช ื™ืขืงื‘ื‘ 
      • ื”ื•ื–ื›ืจ ืขืœ-ื™ื“ื™  Y y6714453 
      • N
        NA73438 @y6714453 ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™ NA73438

        @y6714453
        ืชื•ื“ื” ืจื‘ื”
        ื ืขื–ืจืชื™ ื‘ื–ื” ื•ื–ื” ืžืฆื•ื™ืŸ
        ืืฆื™ื™ืŸ ืฉื”ื™ื” ืœื™ 2 ืชืงืœื•ืช ื•ืกื™ื“ืจืชื™ ืืช ื–ื” (ื‘ืขื–ืจืชื• ืฉืœ ื’'ืžื™ื ื™)

        1. ื”ืฉืจืช ืœื ื”ืฆืœื™ื— ืœื”ื•ืจื™ื“ ืืช ื”ืชื•ื›ื ื” ืฉืžืžื™ืจื” ื˜ืงืกื˜ ืœืฉืžืข ื•ื–ื” ื™ืฆืจ ื‘ืขื™ื” (ืื ื™ ื‘ื ื˜ืคืจื™ ืื•ืœื™ ื–ื” ืงืฉื•ืจ) ื•ื”ืคื™ืชืจื•ืŸ ื”ื™ื” ืœื”ื•ืจื™ื“ ืื•ืชื” ืื•ื˜ื•ืžื˜ื™ืช
        2. ืื ืชื’ื“ื™ืจื™ื ื’ื ืฉื•ืœื—ืช ืื‘ ื•ื’ื ืชืช ืฉืœื•ื—ื” ื›ืฉื”ื• ืžื’ื“ื™ืจ ืืช ืชืช ื”ืฉืœื•ื—ื” ื”ื•ื ื“ื•ืจืก ืืช ืฉืœื•ื—ืช ื”ืื‘ ื•ื”ื•ืคืš ืื•ืชื” ืœืชืคืจื™ื˜

        ื—ื•ืฅ ืžื–ื” ืฉื“ืจื’ืชื™ ืืช ื”ืกืงืจื™ืคื˜ ืฉื™ืขืœื” ืงื‘ืฆื™ ืกืื•ื ื“ ืžื•ื›ื ื™ื ื•ื’ื ื™ืงื‘ืข ืžื” ื™ื”ื™ื” ื”ืฉื ืฉืœื”ื ื‘ืžืขืจื›ืช
        ืžืฆืจืฃ

        
        import pandas as pd
        import requests
        from requests_toolbelt.multipart.encoder import MultipartEncoder
        import os
        import asyncio
        import edge_tts
        import subprocess
        import json
        import urllib.request
        import zipfile
        import shutil
        import warnings
        
        warnings.filterwarnings("ignore")
        
        DEFAULT_TTS_VOICE = "he-IL-AvriNeural"
        FFMPEG_EXECUTABLE = None
        
        def read_excel_data(excel_file_path):
            print(f"๐Ÿ“– ืงื•ืจื ืงื•ื‘ืฅ ืืงืกืœ: {excel_file_path}")
            try:
                df = pd.read_excel(excel_file_path, header=None)
                username = str(df.iloc[0, 1]).strip()
                password = str(df.iloc[1, 1]).strip()
                
                if not username:
                    print("โŒ ืฉื’ื™ืื”: ืื ื ื”ื›ื ืก ืžืกืคืจ ืžืขืจื›ืช (ืชื B1 ื‘ืืงืกืœ).")
                    return None, None, None
                if not password:
                    print("โŒ ืฉื’ื™ืื”: ืื ื ื”ื›ื ืก ืกื™ืกืžื (ืชื B2 ื‘ืืงืกืœ).")
                    return None, None, None
        
                extensions = df.iloc[5:, [0, 1, 2, 3, 4]].dropna(how='all')
                extensions.columns = ['ื ืชื™ื‘', 'ื”ื’ื“ืจื”', 'ืฉื ื”ืฉืœื•ื—ื”', 'ืงื•ื‘ืฅ ืฉืžืข ืœื”ืขืœืื”', 'ืงื•ื‘ืฅ ื˜ืงืกื˜ ืœื”ืขืœืื”']
                
                extensions = extensions.apply(lambda x: x.astype(str).str.strip().replace('nan', '') if x.dtype == 'object' else x)
        
                print(f"โœ”๏ธ ื ืงืจืื• ื‘ื”ืฆืœื—ื”: ืžืฉืชืžืฉ={username}, ืกื™ืกืžื”={'*' * len(password)}")
                return username, password, extensions.to_dict('records')
            except FileNotFoundError:
                print(f"โŒ ืฉื’ื™ืื”: ืงื•ื‘ืฅ ื”ืืงืกืœ '{excel_file_path}' ืœื ื ืžืฆื. ื•ื“ื ืฉื”ื•ื ื‘ืื•ืชื” ืชื™ืงื™ื™ื” ื›ืžื• ื”ืกืงืจื™ืคื˜.")
                return None, None, None
            except Exception as e:
                print(f"โŒ ืฉื’ื™ืื” ื‘ืงืจื™ืืช ืงื•ื‘ืฅ ื”ืืงืกืœ: {e}")
                return None, None, None
        
        def create_ext_ini_file(extension_type, extension_name, output_file_name, is_subpath=False, direct_ini_content=None):
            # ืคื•ื ืงืฆื™ื” ื–ื• ื ืฉืืจืช ืœืœื ืฉื™ื ื•ื™
            content = ""
            if direct_ini_content:
                content = direct_ini_content.strip() + '\n'
            elif is_subpath:
                content = "type=menu\n"
            # ... (ืฉืืจ ื”ืงื•ื“ ืฉืœ ื”ืคื•ื ืงืฆื™ื” ื–ื”ื”)
            try:
                with open(output_file_name, 'w', encoding='utf-8') as f:
                    f.write(content)
                return True
            except Exception:
                return False
        
        
        async def create_and_convert_audio_file(text, output_wav_name):
            # ืคื•ื ืงืฆื™ื” ื–ื• ื ืฉืืจืช ืœืœื ืฉื™ื ื•ื™
            output_mp3_temp = f"temp_{os.path.splitext(output_wav_name)[0]}.mp3"
            print(f"๐Ÿ”Š ื™ื•ืฆืจ ืงื•ื‘ืฅ ืฉืžืข {output_wav_name} ืžื˜ืงืกื˜: '{text}'")
            global FFMPEG_EXECUTABLE
            if not FFMPEG_EXECUTABLE or not os.path.exists(FFMPEG_EXECUTABLE):
                ensure_ffmpeg() 
                if not FFMPEG_EXECUTABLE or not os.path.exists(FFMPEG_EXECUTABLE):
                    return False
            # ... (ืฉืืจ ื”ืงื•ื“ ืฉืœ ื”ืคื•ื ืงืฆื™ื” ื–ื”ื”)
            try:
                comm = edge_tts.Communicate(text, voice=DEFAULT_TTS_VOICE)
                await comm.save(output_mp3_temp)
                subprocess.run(
                    [FFMPEG_EXECUTABLE, "-loglevel", "error", "-y", "-i", output_mp3_temp, "-ar", "8000", "-ac", "1", "-acodec", "pcm_s16le", output_wav_name],
                    check=True
                )
                return True
            except Exception:
                return False
            finally:
                if os.path.exists(output_mp3_temp):
                    os.remove(output_mp3_temp)
        
        def upload_file_to_yemot(file_path, yemot_full_path, username, password):
            # ืคื•ื ืงืฆื™ื” ื–ื• ื ืฉืืจืช ืœืœื ืฉื™ื ื•ื™
            token = f"{username}:{password}"
            content_type = 'application/octet-stream'
            if file_path.lower().endswith('.wav'): content_type = 'audio/wav'
            if file_path.lower().endswith('.ini'): content_type = 'text/plain'
            
            try:
                with open(file_path, 'rb') as f_read:
                    m = MultipartEncoder(fields={
                        "token": token, "path": yemot_full_path,
                        "upload": (os.path.basename(yemot_full_path), f_read, content_type) # ืฉื™ื ื•ื™ ืงื˜ืŸ ืœืฉื ื”ืงื•ื‘ืฅ ื‘ืฉืจืช
                    })
                    print(f"โฌ†๏ธ ืžืขืœื” ืงื•ื‘ืฅ '{os.path.basename(file_path)}' ืœื ืชื™ื‘: '{yemot_full_path}'")
                    r = requests.post("https://www.call2all.co.il/ym/api/UploadFile", data=m, headers={'Content-Type': m.content_type})
                    r.raise_for_status()
                    print(f"โœ”๏ธ ื”ืงื•ื‘ืฅ ื”ื•ืขืœื” ื‘ื”ืฆืœื—ื”")
                    return True
            except Exception as e:
                print(f"โŒ ืฉื’ื™ืื” ื‘ื”ืขืœืืช ืงื•ื‘ืฅ {os.path.basename(file_path)}: {e}")
                return False
        
        def generate_subpaths(full_path):
            # ืคื•ื ืงืฆื™ื” ื–ื• ื ืฉืืจืช ืœืœื ืฉื™ื ื•ื™
            parts = [p for p in full_path.split('/') if p]
            subpaths = []
            current_path_parts = []
            for part in parts[:-1]:
                current_path_parts.append(part)
                subpaths.append('/'.join(current_path_parts))
            return subpaths
        
        def ensure_ffmpeg():
            # ืคื•ื ืงืฆื™ื” ื–ื• ื ืฉืืจืช ืœืœื ืฉื™ื ื•ื™
            global FFMPEG_EXECUTABLE
            local_ffmpeg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin", "ffmpeg.exe")
            if os.path.exists(local_ffmpeg_path):
                FFMPEG_EXECUTABLE = local_ffmpeg_path
                return
            print("โŒ FFmpeg ืœื ื ืžืฆื ื‘ืชื™ืงื™ื™ืช 'bin'. ื™ืฆื™ืจืช ืฉืžืข ืžื˜ืงืกื˜ ืœื ืชืชืืคืฉืจ.")
            FFMPEG_EXECUTABLE = None
            
        async def main():
            print("๐Ÿš€ ืžืชื—ื™ืœ ืชื”ืœื™ืš ื™ืฆื™ืจืช ื•ื”ืขืœืืช ืฉืœื•ื—ื•ืช...")
            script_dir = os.path.dirname(os.path.abspath(__file__))
            excel_file = os.path.join(script_dir, "ืžืฉื ื™.xlsx")
        
            if not os.path.exists(excel_file):
                print(f"โŒ ืงื•ื‘ืฅ ื”ืืงืกืœ '{excel_file}' ืœื ื ืžืฆื!")
                return
        
            username, password, extensions_data = read_excel_data(excel_file)
            if not username or not password or not extensions_data:
                return
        
            all_defined_paths = {ext.get('ื ืชื™ื‘', '').strip() for ext in extensions_data if ext.get('ื ืชื™ื‘')}
        
            for ext in extensions_data:
                path = ext.get('ื ืชื™ื‘', '').strip()
                extension_type = ext.get('ื”ื’ื“ืจื”', '').strip()
                audio_content = ext.get('ืงื•ื‘ืฅ ืฉืžืข ืœื”ืขืœืื”', '').strip()
        
                if not path: continue
                
                print(f"\n๐Ÿ“‹ ืžืขื‘ื“ ืฉืœื•ื—ื”: {ext.get('ืฉื ื”ืฉืœื•ื—ื”', 'ืœืœื ืฉื')} ({path})")
        
                # ืœื•ื’ื™ืงืช ื™ืฆื™ืจืช ืชื™ืงื™ื•ืช ื‘ื™ื ื™ื™ื ื•-ext.ini ื ืฉืืจืช ื–ื”ื”...
                # ...
                if extension_type:
                    main_ini_file = "ext_temp_main.ini"
                    if create_ext_ini_file(None, None, main_ini_file, direct_ini_content=extension_type):
                        upload_file_to_yemot(main_ini_file, f"ivr2:/{path}/ext.ini", username, password)
                        os.remove(main_ini_file)
        
                # --- โญ ื”ืชื™ืงื•ืŸ ื”ืžืจื›ื–ื™ ื ืžืฆื ื›ืืŸ โญ ---
                if audio_content:
                    source_filename = audio_content
                    dest_filename = ""
        
                    # ื‘ื“ื™ืงื” ืื ื™ืฉ ื”ื’ื“ืจืช ืฉื ืžื•ืชืื
                    if '>' in audio_content:
                        parts = audio_content.split('>', 1)
                        source_filename = parts[0].strip()
                        dest_filename = parts[1].strip()
                    
                    local_audio_path = os.path.join(script_dir, source_filename)
        
                    # ืขื“ื™ืคื•ืช 1: ื‘ื“ื™ืงืช ืงื•ื‘ืฅ ืžืงื•ืžื™
                    if os.path.exists(local_audio_path):
                        # ืื ืœื ื”ื•ื’ื“ืจ ืฉื ื™ืขื“, ื”ืฉืชืžืฉ ื‘ืฉื ื”ืžืงื•ืจื™
                        if not dest_filename:
                            dest_filename = os.path.basename(local_audio_path)
                        
                        audio_yemot_path = f"ivr2:/{path}/{dest_filename}"
                        upload_file_to_yemot(local_audio_path, audio_yemot_path, username, password)
                    
                    # ืขื“ื™ืคื•ืช 2: ื™ืฆื™ืจืช ืฉืžืข ืžื˜ืงืกื˜
                    else:
                        print(f"๐Ÿ“„ ืœื ื ืžืฆื ืงื•ื‘ืฅ ืžืงื•ืžื™ '{source_filename}'. ืžื ืกื” ืœื™ืฆื•ืจ ืฉืžืข ืžื”ื˜ืงืกื˜...")
                        # ืื ืœื ื”ื•ื’ื“ืจ ืฉื ื™ืขื“, ื”ืฉืชืžืฉ ื‘ืฉื ื‘ืจื™ืจืช ืžื—ื“ืœ
                        if not dest_filename:
                            dest_filename = "000.wav"
        
                        temp_tts_wav_file = "temp_tts_generated.wav"
                        
                        if await create_and_convert_audio_file(source_filename, temp_tts_wav_file):
                            audio_yemot_path = f"ivr2:/{path}/{dest_filename}"
                            upload_file_to_yemot(temp_tts_wav_file, audio_yemot_path, username, password)
                            if os.path.exists(temp_tts_wav_file):
                                os.remove(temp_tts_wav_file)
                        else:
                            print(f"โš ๏ธ ื“ื™ืœื•ื’ ืขืœ ื”ืขืœืืช ืงื•ื‘ืฅ ืฉืžืข ืขืงื‘ ืฉื’ื™ืื” ื‘ื™ืฆื™ืจื”.")
                # --- โญ ืกื•ืฃ ื”ืชื™ืงื•ืŸ โญ ---
        
            print("\nโœ… ืกื™ื•ื ืขื™ื‘ื•ื“ ื›ืœ ื”ืฉืœื•ื—ื•ืช ื‘ื”ืฆืœื—ื”!")
        
        if __name__ == "__main__":
            asyncio.run(main())
        
        
        Y ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
        • Y
          y6714453 @NA73438 ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

          @NA73438
          ืฉืžื— ืœืฉืžื•ืข ืฉื–ื” ืขื–ืจ...
          ืงื™ื‘ืœืชื™ ืืช ื”ื”ืขืจื•ืช

          1. ืื ื™ ืื‘ื“ื•ืง ืœืžื” ื‘ืืžืช ื–ื” ืœื ืžื•ืจื™ื“
          2. ื‘ืืžืช ืฆืจื™ืš ืœืฉื ื•ืช ืืช ื–ื” ืชื•ื“ื”.

          ืื’ื‘ ืื ืืชื” ืจื•ืฆื” ืœืขืฉื•ืช ื˜ืงืกื˜ ืฉื™ื•ืคื™ืข ื›ืžื• ืงื•ื“ ืืชื” ืฆืจื™ืš ืœื”ื•ืจื™ื“ ืฉื•ืจื” ืœืคื ื™ ื•ืื—ืจื™ - (ืื ื™ ืจื•ืื” ืฉื‘ื—ืœืง ื”ืฆืœื—ืช ื•ื‘ื—ืœืง ืœื...) ๐Ÿ˜Š

          ืง ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
          • ืง
            ืงื•ืกืงื•ืก @y6714453 ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

            ืคื•ืกื˜ ื–ื” ื ืžื—ืง!
            ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
            • ื
              ืA @y6714453 ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™ ืA

              @y6714453
              ืชื•ื›ืœ ืœืขื–ื•ืจ ืœื™ ืœื”ืจื™ืฅ ืืช ื”ืงื•ื“?

              ืข ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
              • ืข
                ืขื™ื“ื• @ืA ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

                @ืA ืขื›ืฉื™ื• ืชืฆื˜ืจืš ื‘ื˜ื— ืœืขืฉื•ืช ืืช ื›ืœ ื”ืื™ืžื•ืช ื”ื—ื“ืฉ

                ื ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                • ื
                  ืA @ืขื™ื“ื• ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™ ืA

                  @ืขื™ื“ื•
                  ื“ื”ื™ื™ื ื•?
                  ืื™ืคื” ืื ื™ ื™ื›ื•ืœ ืœืขื“ื›ืŸ?
                  ืื™ืŸ ืคื” ืฉืœื•ื—ื”.

                  ืข ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                  • ืข
                    ืขื™ื“ื• @ืA ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

                    @ืA https://f2.freeivr.co.il/topic/18488/ืื‘ื˜ื—ื”-ื‘ืื™ืžื•ืช-ื“ื•-ืฉืœื‘ื™-ื“ืขืชื›ื/347

                    ื‘ืจื’ืข ืฉืืชื” ื ื™ื’ืฉ ืœAPI ืชืฆื˜ืจืš ืœืขื‘ื•ืจ ืื™ืžื•ืช ื“ื• ืฉืœื‘ื™ (ืื ื”ื•ืคืขืœ ืืฆืœืš ื‘ืžืขืจื›ืช)

                    ื ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                    • ื
                      ืA @ืขื™ื“ื• ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

                      @ืขื™ื“ื•
                      ื”ื‘ื ืชื™.
                      ืื ื™ ืจืง ืฉื•ืืœ ื‘ืžืงืจื” ื“ื ืŸ ืื™ืคื” ืื ื™ ืžืขื“ื›ืŸ ืืช ื”ื˜ื•ืงืŸ?

                      ืข ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                      • ืข
                        ืขื™ื“ื• @ืA ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

                        @ืA ื˜ื•ืงืŸ ืœื ื™ื•ืขื™ืœ, ืฆืจื™ืš ืืช ื”ืžื”ืœืš ืฉืœ ื”login

                        ื ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                        • ื
                          ืA @ืขื™ื“ื• ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

                          @ืขื™ื“ื•
                          ืื•ืงื™.
                          ืชื•ื“ื”.

                          Y ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                          • Y
                            y6714453 @ืA ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™ y6714453

                            @ืA @ืขื™ื“ื•
                            ืฆืจื™ืš ืœืขื“ื›ืŸ ืืช ื”ืกืงืจื™ืคื˜ ื›ื“ื™ ืฉื™ืขื‘ื•ื“ ืขื ื”ืฆื•ืจืช ื”ืชื—ื‘ืจื•ืช ื”ื—ื“ืฉื”
                            ืื•ืœื™ ืืกืคื™ืง ืžืชื™ืฉื”ื•

                            ื‘ื™ื ืชื™ื™ื ื”ื•ื ืืžื•ืจ ืœืขื‘ื•ื“
                            @ืA ืื™ืคื” ื”ืกืชื‘ื›ืช?

                            ื ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 1
                            • ื
                              ืA @y6714453 ื ืขืจืš ืœืื—ืจื•ื ื” ืขืœ ื™ื“ื™

                              @y6714453
                              ืชื•ื“ื” ืจื‘ื”.

                              ืชื’ื•ื‘ื” 1 ืชื’ื•ื‘ื” ืื—ืจื•ื ื” ืชื’ื•ื‘ื” ืฆื™ื˜ื•ื˜ 0
                              • ืคื•ืกื˜ ืจืืฉื•ืŸ
                                ืคื•ืกื˜ ืื—ืจื•ืŸ