Repository navigation
Expand file tree
/
Copy pathwindow.py
More file actions
executable file
·148 lines (127 loc) · 5.61 KB
/
Copy pathwindow.py
File metadata and controls
executable file
·148 lines (127 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
"""原生桌面窗口 (GTK3 + WebKit2GTK), 给 vocal-editor 一个真正的应用窗口 —— 不是浏览器标签页。
用【系统的 python3 + python3-gi】运行 (gi 是系统绑定, 没法装进 venv):
python3 window.py [url]
桌面发行版基本都自带 python3-gi + gir1.2-webkit2-4.1 (GNOME/KDE 一堆程序用它);
没有的话 launch.sh 会自动退回 chromium --app= / 浏览器。
- 关窗口 = 退出 (launch.sh 收到退出码后关掉后端服务)
- 下载 (导出混音) 存到 ~/Downloads, 并在终端打印落盘路径
"""
import os
import sys
try:
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("WebKit2", "4.1")
from gi.repository import Gtk, WebKit2, GLib
except Exception as e: # 系统没有 gi/webkit → 让 launch.sh 走别的路子
sys.stderr.write("window.py: 没有可用的 GTK/WebKit2 (%s)\n" % e)
sys.exit(3)
URL = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:8765/"
SELFTEST = os.environ.get("VE_SELFTEST") == "1"
def download_dir():
d = os.path.join(os.path.expanduser("~"), "Downloads")
if not os.path.isdir(d):
try:
os.makedirs(d, exist_ok=True)
except Exception:
d = os.path.expanduser("~")
return d
def pick_name(suggested):
"""同名不覆盖: mixdown.wav → mixdown-2.wav"""
base, ext = os.path.splitext(suggested or "download")
path = os.path.join(download_dir(), (base + ext))
i = 2
while os.path.exists(path):
path = os.path.join(download_dir(), "%s-%d%s" % (base, i, ext))
i += 1
return path
def main():
win = Gtk.Window(title="Vocal Editor")
win.set_default_size(1500, 950)
win.set_position(Gtk.WindowPosition.CENTER)
view = WebKit2.WebView()
view.set_zoom_level(float(os.environ.get("VE_ZOOM", "1")))
win.add(view)
# WebAudio / 媒体设置 —— 不设这些的话 WebKit 默认会要求"用户手势"才让出声, 表现就是"窗口里没声音"
st = view.get_settings()
for k, v in (("enable-webaudio", True), ("enable-media", True), ("enable-mediasource", True),
("enable-media-capabilities", True), ("media-playback-requires-user-gesture", False),
("enable-encrypted-media", False),
("enable-write-console-messages-to-stdout", True)): # 页面 console.log → 终端, 排查用
try:
st.set_property(k, v)
except Exception:
pass
# 音频自检: 结果通过 document.title 回传 (WebKit 会发 notify::title, 这是最可靠的 JS→Python 通道;
# 只在自测时改标题, 免得正常使用时标题被顶掉)
PROBE_JS = (
"(function(){try{"
"var C=window.AudioContext||window.webkitAudioContext;var c=new C();"
"var o={before:c.state,sr:c.sampleRate};"
"var fin=function(tag){try{o.after=c.state;o.app=(typeof window.__audbg==='function')?window.__audbg():null;}catch(e){o.appErr=''+e;}"
"var s='AUDIOPROBE '+tag+' '+JSON.stringify(o);console.log(s);"
+ ("document.title=s;" if SELFTEST else "") +
"};"
"if(c.resume&&c.state!=='running'){c.resume().then(function(){fin('resumed')},function(e){o.resumeErr=''+e;fin('resumeErr');});}"
"else fin('sync');"
"}catch(e){console.log('[audio-probe ERR] '+e.message)}})()")
def on_title(v, _p):
try:
print("[窗口标题] %s" % v.get_title(), flush=True)
except Exception:
pass
try:
view.connect("notify::title", on_title)
except Exception:
pass
def on_load(_v, ev):
if ev == WebKit2.LoadEvent.FINISHED:
sys.stdout.write("窗口已加载: %s\n" % URL)
sys.stdout.flush()
GLib.timeout_add(700, lambda: (view.run_javascript(PROBE_JS, None, None, None), False)[1])
if SELFTEST:
# 自测: 走应用导出用的同一条路 (Blob + <a download>) → 验证下载落盘
view.run_javascript(
"(function(){var b=new Blob([new Uint8Array([82,73,70,70])],{type:'audio/wav'});"
"var a=document.createElement('a');a.href=URL.createObjectURL(b);"
"a.download='ve-download-test.wav';document.body.appendChild(a);a.click();})()",
None, None, None)
GLib.timeout_add(3500, Gtk.main_quit)
def on_download(_ctx, dl):
def decide(d, suggested):
p = pick_name(suggested)
d.set_destination("file://" + p)
sys.stdout.write("下载中: %s\n" % p)
sys.stdout.flush()
return True
try:
dl.connect("decide-destination", decide)
except Exception:
pass
def finished(d):
dest = d.get_destination() or ""
sys.stdout.write("已保存: %s\n" % dest.replace("file://", ""))
sys.stdout.flush()
if SELFTEST:
Gtk.main_quit()
try:
dl.connect("finished", finished)
except Exception:
pass
# 下载信号在 WebKitWebView / WebContext 上都有版本差异, 两边都试
for obj, sig in ((view, "download-started"), (view.get_context(), "download-started")):
try:
obj.connect(sig, on_download)
except Exception:
pass
view.connect("load-changed", on_load)
win.connect("destroy", Gtk.main_quit)
win.show_all()
if SELFTEST:
GLib.timeout_add(30000, Gtk.main_quit) # 兜底, 别把自测挂死
view.load_uri(URL)
Gtk.main()
return 0
if __name__ == "__main__":
sys.exit(main())