linjian648 发表于 2024-6-24 14:16

基于edge-tts的剪贴板文本自动朗读

程序主要功能:监控剪贴板内容的变化,并将变化的文本内容转为语音播放。

功能列表
发音人选择:用户可以选择不同的发音人来进行文本转语音。
语速设置:用户可以设置语速/
快捷键监控:用户可以选择是否启用快捷键监控功能,并指定快捷键组合。
只转换英文内容:用户可以选择是否只转换英文内容,忽略其他语言的文本。
配置管理:程序启动时检查是否有配置文件 jtb.ini,存在则读取配置;不存在则让用户设置并保存配置。

安装依赖库
您可以使用以下命令通过 pip 安装以上所需的库:
pip install pyperclip edge-tts pygame keyboard configparser

# -*- coding: utf-8 -*-

import pyperclip
import time
import edge_tts
import os
import asyncio
import pygame
import keyboard# 用于检测全局快捷键
import configparser

# 初始化 pygame
pygame.mixer.init()

# 发音人列表
voices = {
    1: 'zh-CN-XiaoxiaoNeural',
    2: 'zh-CN-XiaoyiNeural',
    3: 'zh-CN-YunjianNeural',
    4: 'zh-CN-YunxiNeural',
    5: 'zh-CN-YunxiaNeural',
    6: 'zh-CN-YunyangNeural'
}

config = configparser.ConfigParser()

if os.path.exists("jtb.ini"):
    config.read("jtb.ini")
    saved_settings = config["SETTINGS"]
    rate_input = saved_settings.get("rate", "1.0").strip()
    voice_choice = int(saved_settings.get("voice_choice", "1"))
    use_hotkey = saved_settings.get("use_hotkey", "n") == 'y'
    hotkey = saved_settings.get("hotkey", "")
    convert_english_only = saved_settings.get("convert_english_only", "n") == 'y'
else:
    rate_input = input("请输入语速(例如 '1.0' 表示正常速度,'1.5' 表示1.5倍速度,默认是 '1.0'): ").strip()
    try:
      rate = float(rate_input)
      rate_percent = f"+{int((rate - 1) * 100)}%"
    except ValueError:
      rate_percent = "+0%"# 默认语速

    # 打印发音人选项
    print("请选择发音人:")
    for key, value in voices.items():
      print(f"{key}: {value}")

    voice_choice = int(input("请输入发音人编号: "))
    selected_voice = voices.get(voice_choice, 'zh-CN-XiaoxiaoNeural')# 默认使用 XiaoXiao

    use_hotkey = input("是否启用快捷键监控功能?(y/n): ").strip().lower() == 'y'

    # 如果启用快捷键监控功能,用户定义一个快捷键组合
    if use_hotkey:
      print("请按下要监控的快捷键组合...")
      hotkey = keyboard.read_hotkey()
      print(f"您选择的快捷键组合是: {hotkey}")
    else:
      hotkey = ""

    convert_english_only = input("是否只转换英文内容?(y/n): ").strip().lower() == 'y'

    config["SETTINGS"] = {
      "rate": rate_input,
      "voice_choice": voice_choice,
      "use_hotkey": 'y' if use_hotkey else 'n',
      "hotkey": hotkey,
      "convert_english_only": 'y' if convert_english_only else 'n'
    }

    with open("jtb.ini", "w") as configfile:
      config.write(configfile)

# 用户配置已读取或设置,现在初始化其他变量
try:
    # 使用读取到或输入的 rate_input 进行语速设置
    rate = float(rate_input)
    rate_percent = f"+{int((rate - 1) * 100)}%"
except ValueError:
    rate_percent = "+0%"# 默认语速

selected_voice = voices.get(voice_choice, 'zh-CN-XiaoxiaoNeural')# 默认使用 XiaoXiao

# 初始化变量
previous_clipboard_content = ""

async def text_to_speech(text, output_file="output.mp3"):
    # 将发音人设定为用户选择的发音人,并设置语速
    communicator = edge_tts.Communicate(text, voice=selected_voice, rate=rate_percent)
    await communicator.save(output_file)

def play_audio_file(file_path):
    pygame.mixer.music.load(file_path)
    pygame.mixer.music.play()

    while pygame.mixer.music.get_busy():
      pygame.time.Clock().tick(10)
   
    # 等待播放完毕后卸载音频文件
    pygame.mixer.music.unload()

def safe_remove(file_path, retries=3, delay=1):
    """
    安全删除文件的方法
    :param file_path: 要删除的文件路径
    :param retries: 最大重试次数
    :param delay: 每次重试之间的延迟(秒)
    """
    for i in range(retries):
      try:
            os.remove(file_path)
            print(f"文件 {file_path} 已成功删除。")
            break
      except PermissionError as e:
            print(f"删除文件失败,重试 {i+1}/{retries}...")
            time.sleep(delay)
      except Exception as e:
            print(f"发生意外错误: {e}")
            break

def is_english(text):
    try:
      # 检查文本是否只包含英文字符和常用标点符号
      return all(ord(c) < 128 for c in text)
    except:
      return False

def is_valid_text(text):
    try:
      return isinstance(text, str) and len(text.strip()) > 0
    except:
      return False

def process_clipboard_text():
    global previous_clipboard_content
    try:
      current_clipboard_content = pyperclip.paste()
      # 仅在剪贴板内容是有效文本且与上次不同的时候处理
      if is_valid_text(current_clipboard_content) and current_clipboard_content != previous_clipboard_content:
            previous_clipboard_content = current_clipboard_content
            
            # 根据用户设置只处理英文内容
            if not convert_english_only or is_english(current_clipboard_content):
                print(f"内容改变: {current_clipboard_content}")
                # 使用 edge-tts 合成语音并播放
                output_file = "output.mp3"
                asyncio.run(text_to_speech(current_clipboard_content, output_file))
               
                # 检查音频文件是否成功生成且有效
                if os.path.exists(output_file) and os.path.getsize(output_file) > 0:
                  play_audio_file(output_file)
               
                # 播放完毕后删除音频文件
                safe_remove(output_file)
    except pyperclip.PyperclipException as e:
      print(f"读取剪贴板内容时出现错误: {e}")

def monitor_clipboard():
    global previous_clipboard_content

    if use_hotkey:
      print(f"按下组合键 '{hotkey}' 进行文本读取...")
      keyboard.add_hotkey(hotkey, process_clipboard_text)
      keyboard.wait()# 等待快捷键被触发
    else:
      while True:
            process_clipboard_text()
            # 每隔一秒检测一次
            time.sleep(1)

if __name__ == "__main__":
    try:
      monitor_clipboard()
    except KeyboardInterrupt:
      pygame.mixer.quit()# 程序退出时清理 pygame
      print("程序已退出")

WXJYXLWMH 发表于 2024-6-24 17:52

感谢发布原创作品 辛苦了

无敌小儿 发表于 2024-6-25 10:14

可以打包下吗,谢谢啊

linjian648 发表于 2024-6-25 15:06

无敌小儿 发表于 2024-6-25 10:14
可以打包下吗,谢谢啊

https://www.123pan.com/s/tQIWjv-XVIr.html提取码:iEpD

fansun52pj 发表于 2024-6-28 22:06

可以加一个美音吗?谢谢大神

qq18215518314 发表于 2024-7-1 14:35

有点意思

adb123a 发表于 2024-7-3 15:03

谢谢分享

jesssy 发表于 2024-10-14 16:05

谢谢分享!
页: [1]
查看完整版本: 基于edge-tts的剪贴板文本自动朗读