Python版本 """ 基于音节/字数精确分配时间 """ import re import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext # ==================== 核心逻辑 ==================== def count_english_syllables(word): w = word.strip('.,;:!?"\'()[]{}').lower() if not w: return 0 vowels = set("aeiouy") count = 0 prev_is_vowel = False for ch in w: is_vowel = ch in vowels if is_vowel and not prev_is_vowel: count += 1 prev_is_vowel = is_vowel if w.endswith("e") and not w.endswith("le") and count > 1: count -= 1 if w.endswith("le") and len(w) > 2 and w[-3] not in vowels: count += 1 return max(count, 1) def count_pronunciation_units(text): total = 0 tokens = re.findall(r'[\u4e00-\u9fff]|[a-zA-Z]+|\d+%?|[^\s]', text) for token in tokens: if re.match(r'[\u4e00-\u9fff]', token): total += 1 elif re.match(r'[a-zA-Z]+', token): total += count_english_syllables(token) elif re.match(r'\d+%?', token): digits = token.replace('%', '') total += min(len(digits) + 1, 5) return total def split_sentences(text): parts = re.split(r'(?<=[.,;])\s+', text) return [p.strip() for p in parts if p.strip()] def ms_to_parts(ms): total_sec = ms / 1000 m = int(total_sec // 60) s = total_sec - m * 60 return m, s def fmt_lyrics3(ms): m, s = ms_to_parts(ms) return f"[{m:02d}:{s:05.2f}]" def fmt_srt(ms): m, s = ms_to_parts(ms) return f"{m:02d}:{s:05.2f}".replace(".", ",") def fmt_ass(ms): m, s = ms_to_parts(ms) h = m // 60 m = m % 60 return f"{h}:{m:02d}:{s:05.2f}" def fmt_vtt(ms): m, s = ms_to_parts(ms) return f"{m:02d}:{s:06.3f}" def generate_subtitles(text, total_seconds, fmt="lyrics3"): total_ms = float(total_seconds) * 1000 sentences = split_sentences(text) if not sentences: return "", 0 units_per_sentence = [count_pronunciation_units(s) for s in sentences] total_units = sum(units_per_sentence) if total_units == 0: return "", 0 ms_per_unit = total_ms / total_units timings = [] cur = 0.0 for i, s in enumerate(sentences): dur = units_per_sentence[i] * ms_per_unit timings.append((cur, cur + dur, s)) cur += dur formatters = {"lyrics3": fmt_lyrics3, "srt": fmt_srt, "ass": fmt_ass, "vtt": fmt_vtt} f = formatters.get(fmt, fmt_lyrics3) lines = [] if fmt == "vtt": lines.append("WEBVTT\n") elif fmt == "ass": lines.append("[Script Info]") lines.append("ScriptType: v4.00+") lines.append("PlayResX: 384") lines.append("PlayResY: 288\n") lines.append("[V4+ Styles]") lines.append("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding") lines.append("Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1\n") lines.append("[Events]") lines.append("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text") for i, (start, end, sent) in enumerate(timings): if fmt == "srt": lines.append(str(i + 1)) lines.append(f"{f(start)} --> {f(end)}") lines.append(sent) lines.append("") elif fmt == "ass": lines.append(f"Dialogue: 0,{f(start)},{f(end)},Default,,0,0,0,,{sent}") elif fmt == "vtt": lines.append(f"{f(start)} --> {f(end)}") lines.append(sent) lines.append("") else: lines.append(f"{f(start)}{sent}") return "\n".join(lines), len(sentences) # ==================== GUI ==================== class SubtitleApp: def __init__(self, root): self.root = root self.root.title("字幕时间轴生成器") self.root.geometry("900x720") self.root.configure(bg="#1e1e2e") self.root.minsize(700, 550) # 样式 style = ttk.Style() style.theme_use("clam") style.configure("TFrame", background="#1e1e2e") style.configure("TLabel", background="#1e1e2e", foreground="#cdd6f4", font=("Segoe UI", 11)) style.configure("TButton", background="#45475a", foreground="#cdd6f4", font=("Segoe UI", 10), borderwidth=0, padding=8) style.map("TButton", background=[("active", "#585b70")]) style.configure("TEntry", fieldbackground="#313244", foreground="#cdd6f4", font=("Segoe UI", 11), padding=6) style.configure("TCombobox", fieldbackground="#313244", foreground="#cdd6f4", font=("Segoe UI", 11), padding=4) self.build_ui() def build_ui(self): # 主容器 main = ttk.Frame(self.root, padding=20) main.pack(fill="both", expand=True) # 标题 title = tk.Label(main, text="🎬 字幕时间轴生成器", font=("Segoe UI", 20, "bold"), bg="#1e1e2e", fg="#cba6f7") title.pack(pady=(0, 5)) subtitle = tk.Label(main, text="基于音节/字数精确分配时间 · 支持中英混合", font=("Segoe UI", 10), bg="#1e1e2e", fg="#6c7086") subtitle.pack(pady=(0, 20)) # 输入区域 input_frame = ttk.Frame(main) input_frame.pack(fill="both", expand=True) lbl_input = tk.Label(input_frame, text="📝 输入文本(按句号/逗号/分号自动分句)", font=("Segoe UI", 11, "bold"), bg="#1e1e2e", fg="#a6adc8") lbl_input.pack(anchor="w") self.text_input = scrolledtext.ScrolledText( input_frame, height=10, font=("Segoe UI", 11), bg="#313244", fg="#cdd6f4", insertbackground="#cdd6f4", relief="flat", borderwidth=0, padx=12, pady=12, wrap=tk.WORD, highlightthickness=1, highlightbackground="#45475a" ) self.text_input.pack(fill="both", expand=True, pady=(5, 15)) # 设置行 settings_frame = ttk.Frame(main) settings_frame.pack(fill="x", pady=(0, 15)) # 时长 dur_frame = ttk.Frame(settings_frame) dur_frame.pack(side="left", padx=(0, 30)) ttk.Label(dur_frame, text="⏱ 总时长(秒)").pack(side="left", padx=(0, 8)) self.duration_var = tk.StringVar(value="73") self.duration_entry = ttk.Entry(dur_frame, textvariable=self.duration_var, width=10) self.duration_entry.pack(side="left") # 格式 fmt_frame = ttk.Frame(settings_frame) fmt_frame.pack(side="left", padx=(0, 30)) ttk.Label(fmt_frame, text="📄 输出格式").pack(side="left", padx=(0, 8)) self.format_var = tk.StringVar(value="lyrics3") fmt_combo = ttk.Combobox(fmt_frame, textvariable=self.format_var, values=["lyrics3", "srt", "ass", "vtt"], state="readonly", width=10) fmt_combo.pack(side="left") # 统计 self.stats_var = tk.StringVar(value="分句: 0 句 | 总发音单位: 0") stats_label = tk.Label(settings_frame, textvariable=self.stats_var, font=("Segoe UI", 10), bg="#1e1e2e", fg="#6c7086") stats_label.pack(side="right") # 按钮 btn_frame = ttk.Frame(main) btn_frame.pack(fill="x", pady=(0, 15)) self.btn_generate = tk.Button( btn_frame, text="✨ 生成字幕", font=("Segoe UI", 12, "bold"), bg="#cba6f7", fg="#1e1e2e", activebackground="#b4befe", activeforeground="#1e1e2e", relief="flat", borderwidth=0, padx=24, pady=10, cursor="hand2", command=self.on_generate ) self.btn_generate.pack(side="left", padx=(0, 10)) self.btn_copy = tk.Button( btn_frame, text="📋 复制全部", font=("Segoe UI", 11), bg="#45475a", fg="#cdd6f4", activebackground="#585b70", activeforeground="#cdd6f4", relief="flat", borderwidth=0, padx=20, pady=10, cursor="hand2", command=self.on_copy ) self.btn_copy.pack(side="left", padx=(0, 10)) self.btn_save = tk.Button( btn_frame, text="💾 保存文件", font=("Segoe UI", 11), bg="#45475a", fg="#cdd6f4", activebackground="#585b70", activeforeground="#cdd6f4", relief="flat", borderwidth=0, padx=20, pady=10, cursor="hand2", command=self.on_save ) self.btn_save.pack(side="left") # 输出区域 output_frame = ttk.Frame(main) output_frame.pack(fill="both", expand=True) lbl_output = tk.Label(output_frame, text="📤 生成结果", font=("Segoe UI", 11, "bold"), bg="#1e1e2e", fg="#a6adc8") lbl_output.pack(anchor="w") self.text_output = scrolledtext.ScrolledText( output_frame, height=12, font=("Cascadia Code", 10), bg="#11111b", fg="#a6e3a1", insertbackground="#a6e3a1", relief="flat", borderwidth=0, padx=12, pady=12, wrap=tk.NONE, highlightthickness=1, highlightbackground="#45475a" ) self.text_output.pack(fill="both", expand=True, pady=(5, 0)) def on_generate(self): text = self.text_input.get("1.0", "end-1c").strip() if not text: messagebox.showwarning("提示", "请先输入文本。") return try: duration = float(self.duration_var.get()) if duration <= 0: raise ValueError except ValueError: messagebox.showwarning("提示", "请输入有效的正数时长(秒)。") return fmt = self.format_var.get() result, count = generate_subtitles(text, duration, fmt) self.text_output.delete("1.0", "end") if result: self.text_output.insert("1.0", result) total_units = sum(count_pronunciation_units(s) for s in split_sentences(text)) self.stats_var.set(f"分句: {count} 句 | 总发音单位: {total_units}") def on_copy(self): result = self.text_output.get("1.0", "end-1c").strip() if result: self.root.clipboard_clear() self.root.clipboard_append(result) messagebox.showinfo("提示", "已复制到剪贴板!") def on_save(self): result = self.text_output.get("1.0", "end-1c").strip() if not result: messagebox.showwarning("提示", "没有可保存的内容,请先生成字幕。") return fmt = self.format_var.get() exts = {"lyrics3": ".txt", "srt": ".srt", "ass": ".ass", "vtt": ".vtt"} ext = exts.get(fmt, ".txt") path = filedialog.asksaveasfilename( defaultextension=ext, filetypes=[(f"{fmt.upper()} 字幕", f"*{ext}"), ("所有文件", "*.*")] ) if path: with open(path, "w", encoding="utf-8") as f: f.write(result) messagebox.showinfo("提示", f"已保存到:{path}") if __name__ == "__main__": root = tk.Tk() app = SubtitleApp(root) root.mainloop() HTML版本 3 个帖子 - 3 位参与者 阅读完整话题
6月9日消息,Meta要为自己的AI数据中心建设培养技工。据路透社报道,Meta将投入1.15亿美元,设立名为"美国劳动力学院"(America's Workforce Academy)的免费培训项目,面向数据中心技术岗位培养新人,并承诺毕业生将获得工作机会。 Meta发言人称,该项目将提供数据中心技术员的通用培训。毕业后的岗位不是直接进入Meta,而是在参与Meta数据中心建设的总承包商处担任全职工作。发言人没有说明具体会开放多少岗位、涉及哪些承包商,也没有说明这些岗位是否属于工会岗位。 AI数据中心不只缺芯片,也缺施工人员 这笔1.15亿美元放在Meta的整体投入中不算大。路透社称,Meta已承诺未来三年在美国基础设施和就业方面投入总计6,000亿美元,其中包括建设大型数据中心,为CEO马克·扎克伯格(Mark Zuckerberg)在AI智能体技术上的押注提供算力。 但AI基础设施不是只靠显卡、服务器和电力合同就能落地。数据中心要建起来,还需要铺设线路、安装设备、完成机电和现场施工。美国建造商与承包商协会(Associated Builders and Contractors)表示,预计将在该项目期间培训数千人。 Meta总裁兼副董事长迪娜·鲍威尔·麦考密克(Dina Powell McCormick)称,AI革命带来变化,也带来"历史性机会"。这意味着AI公司正在把一部分基础设施压力,从算力采购扩展到劳动力供给。 保证就业,但不是保证进Meta 这个项目最具吸引力的地方,是"免费培训"和"毕业后有工作机会"。对不想上大学、或需要尽快进入收入稳定岗位的人来说,数据中心建设潮正在创造一批新的蓝领就业通道。 不过按路透社的说法,毕业生将获得工作机会,岗位来自参与Meta数据中心建设的总承包商,而非Meta直接雇佣。Meta也没有披露岗位数量、用工企业名单和是否为工会岗位。这更接近Meta为自己的建设链条提前储备人力,而不是传统意义上的企业校园招聘。 这一区别很重要。对求职者而言,培训是否真能转化为长期职业路径,取决于岗位地点、薪酬、合同稳定性和后续项目是否持续。对Meta而言,保证有人能进场施工,才是AI数据中心扩张不被工地进度拖住的现实前提。 建设期用工密集,长期岗位有限 路透社还提到,数据中心通常会带来短期建设岗位高峰,但永久岗位数量较少。Meta去年在得克萨斯州开工的一座大型数据中心,预计建设高峰期现场工人超过1,800人,但投入运营后约创造100个岗位;另一座位于俄克拉荷马州的数据中心,预计建设高峰期超过1,000个施工岗位,完工后约有100个运营岗位。 这也是此类培训项目面临的公共争议所在。地方政府和社区常常期望数据中心带来长期就业,但真正密集用工的阶段往往集中在建设期。美国劳动力学院能为一批人打开进入技能工种的入口,但它无法把数据中心变成长期大规模雇主。 AI基础设施建设正在把电工、机电、数据中心技术员等岗位重新推到公司战略前台。AI最终要落在服务器、机房、供电和施工现场上,谁能把这些设施建起来,也会成为科技公司竞争的一部分。 查看评论
现在通过sso登录会直接分配codex席位,但是之前的chatgpt席位保持不变 我自己的bug team截图 1 个帖子 - 1 位参与者 阅读完整话题
目前想的还是Lang或者n8n,然后用crewai或者autogen的方式,做任务分配,但求教如何提高代码的准确度和测试的靠谱程度?虽然有主agent做审核,但是也很容易改飞。 有大佬有什么更好的方式么?求学习!! 1 个帖子 - 1 位参与者 阅读完整话题
不过 awifi 也很罕见
不过 awifi 也很罕见
不过 awifi 也很罕见
不过 awifi 也很罕见
不过 awifi 也很罕见
不过 awifi 也很罕见
不过 awifi 也很罕见
如果接入分配的内网 IPv4 ,由于 CGNAT 所以会有最大连接数限制。但如果是公网 IPv6 的话,因为不存在 CGNAT 了,是不是运营商那头就没有连接数限制了(假定内网设备性能支持)?
如果接入分配的内网 IPv4 ,由于 CGNAT 所以会有最大连接数限制。但如果是公网 IPv6 的话,因为不存在 CGNAT 了,是不是运营商那头就没有连接数限制了(假定内网设备性能支持)?
如果接入分配的内网 IPv4 ,由于 CGNAT 所以会有最大连接数限制。但如果是公网 IPv6 的话,因为不存在 CGNAT 了,是不是运营商那头就没有连接数限制了(假定内网设备性能支持)?
如果接入分配的内网 IPv4 ,由于 CGNAT 所以会有最大连接数限制。但如果是公网 IPv6 的话,因为不存在 CGNAT 了,是不是运营商那头就没有连接数限制了(假定内网设备性能支持)?
如果接入分配的内网 IPv4 ,由于 CGNAT 所以会有最大连接数限制。但如果是公网 IPv6 的话,因为不存在 CGNAT 了,是不是运营商那头就没有连接数限制了(假定内网设备性能支持)?
请教下大佬,最近翻出个历史遗留问题,折腾几天无果,特来求助: 背景:2008 年用常用邮箱(账号 A )注册了国区 Apple ID ; 2009 年苹果自动分配了一个同前缀的 @ mac .com 邮箱给我。 变故:2018 年我想把账号 A 挪去美区用,于是登录国区 ID ,把主邮箱改绑成了一个备用邮箱(账号 B )。 报错: 现在用这个 @ mac .com 登录或找回密码,系统直接报错:“此 Apple 账户无效或不受支持。” 和客服沟通后,对方说隐私政策限制,无法用 @ mac .com 反查。必须提供 2018 年改绑后的“账号 B”才能处理。 时隔多年,我实在想不起当年改绑的账号 B 是哪一个了,可能的邮箱都翻看过了。但我手里有当年账号 A 里保存的所有苹果历史邮件和购买凭证。 请问下,这种“账户无效或不受支持”还有救吗?是被注销了还是冻结了? 既然客服不给反查,有没有什么办法能联系高级顾问,通过提供原始注册邮箱(账号 A )和历史邮件凭证来证明所有权并找回?
请教下大佬,最近翻出个历史遗留问题,折腾几天无果,特来求助: 背景:2008 年用常用邮箱(账号 A )注册了国区 Apple ID ; 2009 年苹果自动分配了一个同前缀的 @ mac .com 邮箱给我。 变故:2018 年我想把账号 A 挪去美区用,于是登录国区 ID ,把主邮箱改绑成了一个备用邮箱(账号 B )。 报错: 现在用这个 @ mac .com 登录或找回密码,系统直接报错:“此 Apple 账户无效或不受支持。” 和客服沟通后,对方说隐私政策限制,无法用 @ mac .com 反查。必须提供 2018 年改绑后的“账号 B”才能处理。 时隔多年,我实在想不起当年改绑的账号 B 是哪一个了,可能的邮箱都翻看过了。但我手里有当年账号 A 里保存的所有苹果历史邮件和购买凭证。 请问下,这种“账户无效或不受支持”还有救吗?是被注销了还是冻结了? 既然客服不给反查,有没有什么办法能联系高级顾问,通过提供原始注册邮箱(账号 A )和历史邮件凭证来证明所有权并找回?
请教下大佬,最近翻出个历史遗留问题,折腾几天无果,特来求助: 背景:2008 年用常用邮箱(账号 A )注册了国区 Apple ID ; 2009 年苹果自动分配了一个同前缀的 @ mac .com 邮箱给我。 变故:2018 年我想把账号 A 挪去美区用,于是登录国区 ID ,把主邮箱改绑成了一个备用邮箱(账号 B )。 报错: 现在用这个 @ mac .com 登录或找回密码,系统直接报错:“此 Apple 账户无效或不受支持。” 和客服沟通后,对方说隐私政策限制,无法用 @ mac .com 反查。必须提供 2018 年改绑后的“账号 B”才能处理。 时隔多年,我实在想不起当年改绑的账号 B 是哪一个了,可能的邮箱都翻看过了。但我手里有当年账号 A 里保存的所有苹果历史邮件和购买凭证。 请问下,这种“账户无效或不受支持”还有救吗?是被注销了还是冻结了? 既然客服不给反查,有没有什么办法能联系高级顾问,通过提供原始注册邮箱(账号 A )和历史邮件凭证来证明所有权并找回?
请教下大佬,最近翻出个历史遗留问题,折腾几天无果,特来求助: 背景:2008 年用常用邮箱(账号 A )注册了国区 Apple ID ; 2009 年苹果自动分配了一个同前缀的 @ mac .com 邮箱给我。 变故:2018 年我想把账号 A 挪去美区用,于是登录国区 ID ,把主邮箱改绑成了一个备用邮箱(账号 B )。 报错: 现在用这个 @ mac .com 登录或找回密码,系统直接报错:“此 Apple 账户无效或不受支持。” 和客服沟通后,对方说隐私政策限制,无法用 @ mac .com 反查。必须提供 2018 年改绑后的“账号 B”才能处理。 时隔多年,我实在想不起当年改绑的账号 B 是哪一个了,可能的邮箱都翻看过了。但我手里有当年账号 A 里保存的所有苹果历史邮件和购买凭证。 请问下,这种“账户无效或不受支持”还有救吗?是被注销了还是冻结了? 既然客服不给反查,有没有什么办法能联系高级顾问,通过提供原始注册邮箱(账号 A )和历史邮件凭证来证明所有权并找回?