๐ ๐ ืคืชืืืช ืฉืืืืืช ืืจืืืืช ืขื ืงืืฆื ืฉืืข / ืืงืกื
-
ืืฆืืจืช ืฉืืืืืช ืืจืืืืช
ืฉืืื ืืืืื!
ื"ื ืืฆื ืื ืืขืฉืืช ืกืงืจืืคื ืคืฉืื, ืฉืขืืืจ ืืืฆืืจ ืืืืขืืืช ืฉืืืืืช ืืืขืจืืช ืืืืช ืืืฉืื ืืงืืืช, ืืืื ืืฆืืจืช ืงืืฆื ืฉืืข (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
ืืฉืื ืืืื: ืฉืืืจื ืขื ืืื ื ืืืงืกื ืืื ืืืืืื ืืืงืืจืืช ืฉืงืืืืชื.
ืืฉืื ืืืื ืฉืฉืชื ืืงืืฆืื (ืืงืื ืืืจืฆื ืืืงืืืฅ ืืงืกื) ืืืื ืืืืชื ืชืืงืืื ืขื ืืืืฉื!!ืฉืื ืฉืืืฉ:
ืคืฉืื ืชืืืื ืืช ืืงืืืฅ ืืงืกื ืืคื ืืฉืืืืืช ืฉืชืจืฆื ืืืืฆืจ ืืืืืื:
ืืื ืืืืื ืื ืืืข ืืืืืื ืืฉืืืช:
-
ืืฉืืืช ืฉืื ืืืื ืื - ืืืขืื ืืฉื ืืฉืชืืฉ ืืืกืืกืื, ืืืฅ ืืื ืืื ืืืื ืืืืฉืืจ ืจืืง - ืื "ืืืืจืืช ืฉืืืื" ื ืฉืืจ ืจืืง - ืื ืืืืฆืจ ืชืืงืืื ืืืื ืืื ืฉืื ืืืืจืืช.
-
ืืฉืืืืืื ืืืืจืืช ืฉืืืื ืืขืืงืจืื ืฆืจืื ืืืื ืืก ืืฉืื ืืช ืืืืืจืืช ืฉื ืืฉืืืื ืืื ืื ืื ืฉืืืื - ืื ืชืคืจืื ืื ืืฉืืขืช ืงืืฆืื ืื ืืงืืื - ืืคืฉืจ ืืืื ืืก ืืช ืืืืืื "ืืงืืื" ืื "ืืฉืืขืช ืงืืฆืื" ืื "ืชืคืจืื" ืืื ืืืฆืจ ืืื ืืช ืืืืจืืช ืืฉืืืื.
-
ืื ืจืืฆืื ืืืฉื ืืขืฉืืช ืืื ืชืืงืืืช / ืฉืืืืืช - ืื ืฆืจืื ืืืืืืช ืืื ืฉืืืช ืืคืฉืจ ืคืฉืื ืืขืฉืืช ืฉืชื ืฉืืืช ืืืฉื:
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ืืื ืืืจืืจ ืืช ืืงืืืฅ ืคืืืชืื ืืชืื ืืฉืืจืช ืคืงืืื (ืื ืืืขืชืืง ืืช ืื ืชืื ืฉืื) ืืืืืื - ืื ืืจ!
ืืฉืืืืฉ ืืืืจืืืช ืืืฉืชืืฉ ืืืื!
ืื ืืืฉืื ืืกืชืื ืขื ืืืจืฆื ืฉื ืืงืื ืืฉืื ืืขืืืจ
ืืฉืื ืืฉืืืข ืฉืื ืขืืจ ืืืืฉืื... ืืืคืืื ืืืืฉืื ืืื... ืืืื ื! -
ืช ืชืคืืจืช ืืขืงืื ืืชืืืืก ืื ืืฉื ืื ื
-
Y y6714453 ืืชืืืืก ืื ืืฉื ืื ื
-
@y6714453
ืชืืื ืจืื
ื ืขืืจืชื ืืื ืืื ืืฆืืื
ืืฆืืื ืฉืืื ืื 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()) -
@NA73438
ืฉืื ืืฉืืืข ืฉืื ืขืืจ...
ืงืืืืชื ืืช ืืืขืจืืช- ืื ื ืืืืืง ืืื ืืืืช ืื ืื ืืืจืื
- ืืืืช ืฆืจืื ืืฉื ืืช ืืช ืื ืชืืื.
ืืื ืื ืืชื ืจืืฆื ืืขืฉืืช ืืงืกื ืฉืืืคืืข ืืื ืงืื ืืชื ืฆืจืื ืืืืจืื ืฉืืจื ืืคื ื ืืืืจื - (ืื ื ืจืืื ืฉืืืืง ืืฆืืืช ืืืืืง ืื...)
-
ืคืืกื ืื ื ืืืง! -
@y6714453
ืชืืื ืืขืืืจ ืื ืืืจืืฅ ืืช ืืงืื? -
@ืA ืขืืฉืื ืชืฆืืจื ืืื ืืขืฉืืช ืืช ืื ืืืืืืช ืืืืฉ
-
@ืขืืื
ืืืืื ื?
ืืืคื ืื ื ืืืื ืืขืืื?
ืืื ืคื ืฉืืืื. -
@ืA https://f2.freeivr.co.il/topic/18488/ืืืืื-ืืืืืืช-ืื-ืฉืืื-ืืขืชืื/347
ืืจืืข ืฉืืชื ื ืืืฉ ืAPI ืชืฆืืจื ืืขืืืจ ืืืืืช ืื ืฉืืื (ืื ืืืคืขื ืืฆืื ืืืขืจืืช)
-
@ืขืืื
ืืื ืชื.
ืื ื ืจืง ืฉืืื ืืืงืจื ืื ื ืืืคื ืื ื ืืขืืื ืืช ืืืืงื? -
@ืA ืืืงื ืื ืืืขืื, ืฆืจืื ืืช ืืืืื ืฉื ืlogin
-
@ืขืืื
ืืืงื.
ืชืืื. -
-
@y6714453
ืชืืื ืจืื.
ืฉืืื! ื ืจืื ืฉืืฉืืื ืืื ืืขื ืืื ืช ืืืชื, ืืื ืขืืืื ืืื ืื ืืฉืืื.
ื ืืืก ืืื ืืืืื ืืื ืืืชื ืืคืืกืืื ืืื ืืืงืืจ? ืืฉื ืจืฉืืื ืืืฉืืื, ืชืืื ืชืืืจื ืืืืืง ืืืงืื ืฉืื ืืืืชื ืงืืื, ืืชืืืื ืืืืืจ ืืงืื ืืชืจืืืช ืขื ืชืืืืืช ืืืฉืืช (ืืื ืื ืืืืื, ืืืื ืื ืืืชืจืืช ืคืืฉ). ืชืืืื ืื ืืฉืืืจ ืกืืื ืืืช ืืืคืจืื ื-upvote ืืคืืกืืื ืืื ืืืืืข ืืขืจืื ืืืืจื ืงืืืื ืืืจืื.
ืืขืืจืช ืืชืจืืื ืฉืื, ืืคืืกื ืืื ืืืื ืืืืืช ืืคืืื ืืื ืืืชืจ ๐
ืืจืฉืื ืืชืืืจืืช