free-short-video 5.2.0 โ 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/core/api/agnes_image.py +3 -3
- package/package.json +1 -1
- package/static/index.html +137 -0
package/README.md
CHANGED
|
@@ -33,6 +33,13 @@ free-short-video
|
|
|
33
33
|
|
|
34
34
|
> **๐ฅ๏ธ Try it now โ no install needed:** Visit [video.lichuanyang.top](https://video.lichuanyang.top) and experience **Simple Video** mode directly in your browser. Just enter a prompt and generate a free AI video instantly.
|
|
35
35
|
|
|
36
|
+
## ๐ Two Ways to Use โ Both Completely Free
|
|
37
|
+
|
|
38
|
+
| Project | Run Where | Features | Link |
|
|
39
|
+
|---------|-----------|----------|------|
|
|
40
|
+
| **[Agnes Video Generator](https://github.com/lcy362/agnes-video-generator)** (this project) | **Download & run locally** | **More powerful** โ TTS narration, auto subtitles, digital anchor, image-to-video, keyframes animation, manuscript-to-video, checkpoint resume & more | [GitHub](https://github.com/lcy362/agnes-video-generator) |
|
|
41
|
+
| **[FreeShortVideoStudio](https://github.com/lcy362/free-short-video-studio)** | **Fully online, in the browser** | Lightweight, zero install โ no setup at all, **features under active construction** | [video.lichuanyang.top/studio](https://video.lichuanyang.top/studio) ยท [GitHub](https://github.com/lcy362/free-short-video-studio) |
|
|
42
|
+
|
|
36
43
|
## โญ Support & Contribute
|
|
37
44
|
|
|
38
45
|
If you find this project useful, please **star the [GitHub repository](https://github.com/lcy362/agnes-video-generator)** โญ โ your support helps more people discover this free and open-source AI video generator.
|
package/core/api/agnes_image.py
CHANGED
|
@@ -82,7 +82,7 @@ class AgnesImageAPI:
|
|
|
82
82
|
prompt: str,
|
|
83
83
|
reference_image_paths: List[str] = [],
|
|
84
84
|
size: Optional[str] = None,
|
|
85
|
-
max_retries: int =
|
|
85
|
+
max_retries: int = 4,
|
|
86
86
|
retry_base_delay: float = 20.0,
|
|
87
87
|
**kwargs,
|
|
88
88
|
) -> ImageOutput:
|
|
@@ -114,8 +114,8 @@ class AgnesImageAPI:
|
|
|
114
114
|
try:
|
|
115
115
|
# ๅ
จๅฑ้้๏ผๅจๅ่ตท HTTP ่ฏทๆฑๅ่ทๅไปค็
|
|
116
116
|
await asyncio.to_thread(get_rate_limiter().acquire)
|
|
117
|
-
# ๅจๆ่ถ
ๆถ๏ผ็ฌฌไธๆฌก
|
|
118
|
-
read_timeout =
|
|
117
|
+
# ๅจๆ่ถ
ๆถ๏ผ็ฌฌไธๆฌก 120s๏ผๅ็ปญ้ๆญฅๅขๅ ๏ผๅพๅ็ๆ่พๆ
ข๏ผๆพๅฎฝ่ฏป่ถ
ๆถ๏ผ
|
|
118
|
+
read_timeout = 120 * (attempt + 1)
|
|
119
119
|
resp = await asyncio.to_thread(
|
|
120
120
|
requests.post,
|
|
121
121
|
f"{get_agnes_base_url()}/images/generations",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-short-video",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.1",
|
|
4
4
|
"description": "Free AI short-video generator โ run the full free-short-video service locally with one command. Text-to-video, image generation, Edge-TTS narration, subtitles, and compositing, all free.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"free-short-video": "bin/cli.js",
|
package/static/index.html
CHANGED
|
@@ -1552,6 +1552,78 @@
|
|
|
1552
1552
|
</div>
|
|
1553
1553
|
|
|
1554
1554
|
<script>
|
|
1555
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1556
|
+
// GA4 ๅ็น๏ผๅบๅฎ Measurement ID๏ผ็ปไธๆถ้็จๆทๆไฝๆฐๆฎ๏ผ
|
|
1557
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1558
|
+
|
|
1559
|
+
const GA_MEASUREMENT_ID = 'G-RQW2189QSK';
|
|
1560
|
+
let _gaEnabled = false;
|
|
1561
|
+
|
|
1562
|
+
// ๅจๆๅ ่ฝฝ gtag.js๏ผๅน็ญ๏ผๅทฒๅๅงๅๅ่ทณ่ฟ๏ผ
|
|
1563
|
+
function initGA() {
|
|
1564
|
+
if (_gaEnabled) return;
|
|
1565
|
+
_gaEnabled = true;
|
|
1566
|
+
try {
|
|
1567
|
+
window.dataLayer = window.dataLayer || [];
|
|
1568
|
+
window.gtag = function(){ window.dataLayer.push(arguments); };
|
|
1569
|
+
const s = document.createElement('script');
|
|
1570
|
+
s.async = true;
|
|
1571
|
+
s.src = 'https://www.googletagmanager.com/gtag/js?id=' + encodeURIComponent(GA_MEASUREMENT_ID);
|
|
1572
|
+
document.head.appendChild(s);
|
|
1573
|
+
window.gtag('js', new Date());
|
|
1574
|
+
window.gtag('config', GA_MEASUREMENT_ID);
|
|
1575
|
+
} catch(e) {
|
|
1576
|
+
console.warn('GA init failed:', e);
|
|
1577
|
+
_gaEnabled = false;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// ็ปไธไบไปถไธๆฅ๏ผGA ๆชๅฏ็จๆๅๅงๅๅคฑ่ดฅๆถ้้ป่ทณ่ฟ
|
|
1582
|
+
function trackEvent(name, params) {
|
|
1583
|
+
if (!_gaEnabled || typeof window.gtag !== 'function') return;
|
|
1584
|
+
try { window.gtag('event', name, params || {}); } catch(e) { /* ้้ป */ }
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// ไปปๅก็ปๆๅป้ไธๆฅ๏ผ่ฝฎ่ฏขไผๅๅค่ฐ็จ๏ผๅไธไปปๅกๅชไธๆฅไธๆฌก็ปๆ
|
|
1588
|
+
const _trackedTaskResults = {};
|
|
1589
|
+
function trackTaskResultOnce(name, taskId, params) {
|
|
1590
|
+
if (_trackedTaskResults[taskId] === name) return;
|
|
1591
|
+
_trackedTaskResults[taskId] = name;
|
|
1592
|
+
trackEvent(name, params || {});
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// ๅ
จๅฑๆฅ้ๆ่ท โ GA4 exception ไบไปถ๏ผdescription + fatal ๅๆฐ๏ผ
|
|
1596
|
+
let _lastErrSig = '';
|
|
1597
|
+
let _lastErrTime = 0;
|
|
1598
|
+
function reportException(description, fatal) {
|
|
1599
|
+
const now = Date.now();
|
|
1600
|
+
// ๅ็ฑป้่ฏฏ 10 ็งๅ
ๅชไธๆฅไธๆฌก๏ผ้ฟๅ
่ฝฎ่ฏข/ๅพช็ฏๅฏผ่ด็ๅทๅฑ
|
|
1601
|
+
if (description === _lastErrSig && now - _lastErrTime < 10000) return;
|
|
1602
|
+
_lastErrSig = description;
|
|
1603
|
+
_lastErrTime = now;
|
|
1604
|
+
trackEvent('exception', {
|
|
1605
|
+
description: (description || '').slice(0, 500),
|
|
1606
|
+
fatal: fatal ? '1' : '0',
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// JS ่ฟ่กๆถ้่ฏฏ๏ผๅซๅ ๆ ไฝ็ฝฎ๏ผ
|
|
1611
|
+
window.addEventListener('error', function(e) {
|
|
1612
|
+
reportException(
|
|
1613
|
+
(e.message || 'Unknown error') + ' @ ' + (e.filename || 'inline') + ':' + (e.lineno || 0),
|
|
1614
|
+
true
|
|
1615
|
+
);
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
// ๆชๅค็็ Promise ๆ็ป
|
|
1619
|
+
window.addEventListener('unhandledrejection', function(e) {
|
|
1620
|
+
let msg = '';
|
|
1621
|
+
const r = e.reason;
|
|
1622
|
+
if (r instanceof Error) msg = r.message;
|
|
1623
|
+
else if (r != null) msg = String(r);
|
|
1624
|
+
reportException('Unhandled promise rejection: ' + (msg || 'unknown'), false);
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1555
1627
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1556
1628
|
// i18n
|
|
1557
1629
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -3283,6 +3355,8 @@ const STEPS = {
|
|
|
3283
3355
|
updateDomainCompact(d.agnes_domain);
|
|
3284
3356
|
updateDomainStatus(d.agnes_domain);
|
|
3285
3357
|
}
|
|
3358
|
+
// GA4 ๅ็น๏ผๅบๅฎ Measurement ID๏ผ้กต้ขๅ ่ฝฝๅณๅฏ็จ๏ผ็ปไธๆถ้ๆไฝๆฐๆฎ๏ผ
|
|
3359
|
+
initGA();
|
|
3286
3360
|
} catch(e) { console.error('init config load error:', e); }
|
|
3287
3361
|
|
|
3288
3362
|
// Character count for manuscript
|
|
@@ -3562,6 +3636,7 @@ function showToast(msg, duration = 2500) {
|
|
|
3562
3636
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3563
3637
|
|
|
3564
3638
|
function switchMainTab(tab) {
|
|
3639
|
+
trackEvent('ui_action', { action: 'switch_main_tab', tab });
|
|
3565
3640
|
document.querySelectorAll('.mtab-btn').forEach(b => {
|
|
3566
3641
|
b.className = b.className.replace(/tab-active/, 'tab-inactive');
|
|
3567
3642
|
});
|
|
@@ -3581,6 +3656,7 @@ function switchMainTab(tab) {
|
|
|
3581
3656
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3582
3657
|
|
|
3583
3658
|
function switchTaskType(type) {
|
|
3659
|
+
trackEvent('ui_action', { action: 'switch_task_type', type });
|
|
3584
3660
|
currentTaskType = type;
|
|
3585
3661
|
document.querySelectorAll('.ttype-btn').forEach(b => {
|
|
3586
3662
|
b.className = b.className.replace(/tab-active/, 'tab-inactive');
|
|
@@ -3712,6 +3788,12 @@ async function submitSimple() {
|
|
|
3712
3788
|
const r = await fetch('/api/tasks/simple', { method: 'POST', body: form });
|
|
3713
3789
|
const d = await r.json();
|
|
3714
3790
|
if (!d.ok) throw new Error(d.detail || t('failCreate'));
|
|
3791
|
+
trackEvent('create_task', {
|
|
3792
|
+
task_type: 'simple',
|
|
3793
|
+
mode: document.getElementById('s_mode').value,
|
|
3794
|
+
duration: document.getElementById('s_duration').value,
|
|
3795
|
+
resolution: document.getElementById('s_resolution').value,
|
|
3796
|
+
});
|
|
3715
3797
|
currentTaskType = 'simple';
|
|
3716
3798
|
setRunning(d.task_id);
|
|
3717
3799
|
currentDirName = d.dir_name;
|
|
@@ -3719,6 +3801,7 @@ async function submitSimple() {
|
|
|
3719
3801
|
await startPolling(d.task_id);
|
|
3720
3802
|
showToast(t('submitted'), 5000);
|
|
3721
3803
|
} catch(e) {
|
|
3804
|
+
trackEvent('create_task_failed', { task_type: 'simple', error: (e.message || '').slice(0, 120) });
|
|
3722
3805
|
alert(t('failCreate') + ': ' + e.message);
|
|
3723
3806
|
btn.disabled = false; btn.textContent = t('startGenerate');
|
|
3724
3807
|
}
|
|
@@ -3830,6 +3913,15 @@ async function submitCreative() {
|
|
|
3830
3913
|
const r = await fetch('/api/tasks/creative', { method: 'POST', body: form });
|
|
3831
3914
|
const d = await r.json();
|
|
3832
3915
|
if (!d.ok) throw new Error(d.detail || t('failCreate'));
|
|
3916
|
+
trackEvent('create_task', {
|
|
3917
|
+
task_type: 'creative',
|
|
3918
|
+
style: document.getElementById('c_style').value,
|
|
3919
|
+
chaining_mode: document.getElementById('c_chaining').value,
|
|
3920
|
+
duration_source: durationSource,
|
|
3921
|
+
resolution: document.getElementById('c_resolution').value,
|
|
3922
|
+
audio: document.getElementById('c_audio_enabled').checked ? 'on' : 'off',
|
|
3923
|
+
subtitle: document.getElementById('c_subtitle_enabled').checked ? 'on' : 'off',
|
|
3924
|
+
});
|
|
3833
3925
|
currentTaskType = 'creative';
|
|
3834
3926
|
setRunning(d.task_id);
|
|
3835
3927
|
currentDirName = d.dir_name;
|
|
@@ -3837,6 +3929,7 @@ async function submitCreative() {
|
|
|
3837
3929
|
await startPolling(d.task_id);
|
|
3838
3930
|
showToast(t('submitted'), 5000);
|
|
3839
3931
|
} catch(e) {
|
|
3932
|
+
trackEvent('create_task_failed', { task_type: 'creative', error: (e.message || '').slice(0, 120) });
|
|
3840
3933
|
alert(t('failCreate') + ': ' + e.message);
|
|
3841
3934
|
btn.disabled = false; btn.textContent = t('startGenerate');
|
|
3842
3935
|
}
|
|
@@ -3877,6 +3970,13 @@ async function submitManuscript() {
|
|
|
3877
3970
|
const r = await fetch('/api/tasks/manuscript', { method: 'POST', body: form });
|
|
3878
3971
|
const d = await r.json();
|
|
3879
3972
|
if (!d.ok) throw new Error(d.detail || t('failCreate'));
|
|
3973
|
+
trackEvent('create_task', {
|
|
3974
|
+
task_type: 'manuscript',
|
|
3975
|
+
resolution: document.getElementById('m_resolution').value,
|
|
3976
|
+
text_len: (text || '').length,
|
|
3977
|
+
audio: document.getElementById('m_audio_enabled').checked ? 'on' : 'off',
|
|
3978
|
+
subtitle: document.getElementById('m_subtitle_enabled').checked ? 'on' : 'off',
|
|
3979
|
+
});
|
|
3880
3980
|
currentTaskType = 'manuscript';
|
|
3881
3981
|
setRunning(d.task_id);
|
|
3882
3982
|
currentDirName = d.dir_name;
|
|
@@ -3884,6 +3984,7 @@ async function submitManuscript() {
|
|
|
3884
3984
|
await startPolling(d.task_id);
|
|
3885
3985
|
showToast(t('submitted'), 5000);
|
|
3886
3986
|
} catch(e) {
|
|
3987
|
+
trackEvent('create_task_failed', { task_type: 'manuscript', error: (e.message || '').slice(0, 120) });
|
|
3887
3988
|
alert(t('failCreate') + ': ' + e.message);
|
|
3888
3989
|
btn.disabled = false; btn.textContent = t('startGenerate');
|
|
3889
3990
|
}
|
|
@@ -3931,6 +4032,13 @@ async function submitAnchor() {
|
|
|
3931
4032
|
const r = await fetch('/api/tasks/anchor', { method: 'POST', body: form });
|
|
3932
4033
|
const d = await r.json();
|
|
3933
4034
|
if (!d.ok) throw new Error(d.detail || t('failCreate'));
|
|
4035
|
+
trackEvent('create_task', {
|
|
4036
|
+
task_type: 'anchor',
|
|
4037
|
+
resolution: document.getElementById('a_resolution').value,
|
|
4038
|
+
audio_source: (audioSource ? audioSource.value : 'post_stitch'),
|
|
4039
|
+
audio: document.getElementById('a_audio_enabled').checked ? 'on' : 'off',
|
|
4040
|
+
subtitle: document.getElementById('a_subtitle_enabled').checked ? 'on' : 'off',
|
|
4041
|
+
});
|
|
3934
4042
|
currentTaskType = 'anchor';
|
|
3935
4043
|
setRunning(d.task_id);
|
|
3936
4044
|
currentDirName = d.dir_name;
|
|
@@ -3938,6 +4046,7 @@ async function submitAnchor() {
|
|
|
3938
4046
|
await startPolling(d.task_id);
|
|
3939
4047
|
showToast(t('submitted'), 5000);
|
|
3940
4048
|
} catch(e) {
|
|
4049
|
+
trackEvent('create_task_failed', { task_type: 'anchor', error: (e.message || '').slice(0, 120) });
|
|
3941
4050
|
alert(t('failCreate') + ': ' + e.message);
|
|
3942
4051
|
btn.disabled = false; btn.textContent = t('startGenerate');
|
|
3943
4052
|
}
|
|
@@ -3990,6 +4099,14 @@ async function submitPoetry() {
|
|
|
3990
4099
|
const r = await fetch('/api/tasks/poetry', { method: 'POST', body: form });
|
|
3991
4100
|
const d = await r.json();
|
|
3992
4101
|
if (!d.ok) throw new Error(d.detail || t('failCreate'));
|
|
4102
|
+
trackEvent('create_task', {
|
|
4103
|
+
task_type: 'poetry',
|
|
4104
|
+
style: document.getElementById('p_style').value,
|
|
4105
|
+
scene_count: sceneCount,
|
|
4106
|
+
resolution: document.getElementById('p_resolution').value,
|
|
4107
|
+
audio: document.getElementById('p_audio_enabled').checked ? 'on' : 'off',
|
|
4108
|
+
subtitle: document.getElementById('p_subtitle_enabled').checked ? 'on' : 'off',
|
|
4109
|
+
});
|
|
3993
4110
|
currentTaskType = 'poetry';
|
|
3994
4111
|
setRunning(d.task_id);
|
|
3995
4112
|
currentDirName = d.dir_name;
|
|
@@ -3997,6 +4114,7 @@ async function submitPoetry() {
|
|
|
3997
4114
|
await startPolling(d.task_id);
|
|
3998
4115
|
showToast(t('submitted'), 5000);
|
|
3999
4116
|
} catch(e) {
|
|
4117
|
+
trackEvent('create_task_failed', { task_type: 'poetry', error: (e.message || '').slice(0, 120) });
|
|
4000
4118
|
alert(t('failCreate') + ': ' + e.message);
|
|
4001
4119
|
btn.disabled = false; btn.textContent = t('startGenerate');
|
|
4002
4120
|
}
|
|
@@ -4144,10 +4262,15 @@ async function submitImage() {
|
|
|
4144
4262
|
const d = await r.json();
|
|
4145
4263
|
if (!d.ok) throw new Error(d.detail || t('failCreate'));
|
|
4146
4264
|
|
|
4265
|
+
trackEvent('create_task', {
|
|
4266
|
+
task_type: 'image',
|
|
4267
|
+
size: document.getElementById('i_size').value,
|
|
4268
|
+
});
|
|
4147
4269
|
document.getElementById('result-image').src = '/api/image/' + d.task_id;
|
|
4148
4270
|
document.getElementById('image-result').classList.remove('hidden');
|
|
4149
4271
|
showToast(t('imgComplete'), 5000);
|
|
4150
4272
|
} catch(e) {
|
|
4273
|
+
trackEvent('create_task_failed', { task_type: 'image', error: (e.message || '').slice(0, 120) });
|
|
4151
4274
|
alert(t('failCreate') + ': ' + e.message);
|
|
4152
4275
|
} finally {
|
|
4153
4276
|
btn.disabled = false; btn.textContent = t('imgGenerate');
|
|
@@ -4278,6 +4401,9 @@ async function pollTaskProgress(taskId) {
|
|
|
4278
4401
|
|
|
4279
4402
|
// ไปปๅกๅฎๆ
|
|
4280
4403
|
if (state.status === 'completed') {
|
|
4404
|
+
trackTaskResultOnce('task_completed', taskId, {
|
|
4405
|
+
task_type: state.task_type || currentTaskType,
|
|
4406
|
+
});
|
|
4281
4407
|
showResult(state.final_video_file);
|
|
4282
4408
|
clearRunning();
|
|
4283
4409
|
scheduleArtifactRefresh();
|
|
@@ -4285,6 +4411,10 @@ async function pollTaskProgress(taskId) {
|
|
|
4285
4411
|
|
|
4286
4412
|
// ไปปๅกๅคฑ่ดฅ
|
|
4287
4413
|
if (state.status === 'failed' || (step === 'error' && status === 'failed')) {
|
|
4414
|
+
trackTaskResultOnce('task_failed', taskId, {
|
|
4415
|
+
task_type: state.task_type || currentTaskType,
|
|
4416
|
+
error: (state.current_message || '').slice(0, 120),
|
|
4417
|
+
});
|
|
4288
4418
|
clearRunning();
|
|
4289
4419
|
document.getElementById('progress-message').innerHTML = `
|
|
4290
4420
|
<div class="p-4 bg-red-950 border border-red-800 rounded-lg space-y-2">
|
|
@@ -4532,6 +4662,7 @@ async function stopTask() {
|
|
|
4532
4662
|
const r = await fetch('/api/tasks/' + currentTaskId + '/stop', { method: 'POST' });
|
|
4533
4663
|
const d = await r.json();
|
|
4534
4664
|
if (!d.ok) throw new Error(d.detail || t('failStop'));
|
|
4665
|
+
trackEvent('task_stopped', { task_type: currentTaskType || '', source: 'progress' });
|
|
4535
4666
|
showToast(t('stopped'), 4000);
|
|
4536
4667
|
} catch(e) {
|
|
4537
4668
|
alert(t('failStop') + ': ' + e.message);
|
|
@@ -4545,6 +4676,7 @@ async function stopTaskById(taskId) {
|
|
|
4545
4676
|
const r = await fetch('/api/tasks/' + taskId + '/stop', { method: 'POST' });
|
|
4546
4677
|
const d = await r.json();
|
|
4547
4678
|
if (!d.ok) throw new Error(d.detail || t('failStop'));
|
|
4679
|
+
trackEvent('task_stopped', { task_type: currentTaskType || '', source: 'list' });
|
|
4548
4680
|
showToast(t('stoppedById'), 3000);
|
|
4549
4681
|
loadTaskList();
|
|
4550
4682
|
} catch(e) { alert(t('failStop') + ': ' + e.message); }
|
|
@@ -5019,6 +5151,7 @@ document.getElementById('apikey-save').onclick = async () => {
|
|
|
5019
5151
|
form.append('api_key', key);
|
|
5020
5152
|
const r = await fetch('/api/config', { method: 'POST', body: form });
|
|
5021
5153
|
if (r.ok) {
|
|
5154
|
+
trackEvent('config_action', { action: 'save_api_key' });
|
|
5022
5155
|
setApiKeyConfiguredUI('config', true);
|
|
5023
5156
|
}
|
|
5024
5157
|
};
|
|
@@ -5032,6 +5165,7 @@ document.getElementById('apikey-clear').onclick = async () => {
|
|
|
5032
5165
|
try {
|
|
5033
5166
|
const r = await fetch('/api/config', { method: 'DELETE' });
|
|
5034
5167
|
if (r.ok) {
|
|
5168
|
+
trackEvent('config_action', { action: 'clear_api_key' });
|
|
5035
5169
|
resetApiKeyUI();
|
|
5036
5170
|
} else {
|
|
5037
5171
|
const d = await r.json();
|
|
@@ -5182,6 +5316,7 @@ document.getElementById('model-save').onclick = async () => {
|
|
|
5182
5316
|
try {
|
|
5183
5317
|
const r = await fetch('/api/config/models', { method: 'POST', body: form });
|
|
5184
5318
|
if (r.ok) {
|
|
5319
|
+
trackEvent('config_action', { action: 'save_models', text_model: text });
|
|
5185
5320
|
okEl.classList.remove('hidden');
|
|
5186
5321
|
updateModelCompact({ text });
|
|
5187
5322
|
setTimeout(() => okEl.classList.add('hidden'), 2000);
|
|
@@ -5235,6 +5370,7 @@ document.getElementById('domain-save').onclick = async () => {
|
|
|
5235
5370
|
try {
|
|
5236
5371
|
const r = await fetch('/api/config/domain', { method: 'POST', body: form });
|
|
5237
5372
|
if (r.ok) {
|
|
5373
|
+
trackEvent('config_action', { action: 'save_domain', domain });
|
|
5238
5374
|
okEl.classList.remove('hidden');
|
|
5239
5375
|
updateDomainCompact(domain);
|
|
5240
5376
|
updateDomainStatus(domain);
|
|
@@ -5261,6 +5397,7 @@ async function toggleWatermark(el) {
|
|
|
5261
5397
|
try {
|
|
5262
5398
|
const r = await fetch('/api/config/watermark', { method: 'POST', body: form });
|
|
5263
5399
|
if (r.ok) {
|
|
5400
|
+
trackEvent('config_action', { action: 'toggle_watermark', enabled: enabled ? 'on' : 'off' });
|
|
5264
5401
|
document.querySelectorAll('.watermark-toggle').forEach(t => t.checked = enabled);
|
|
5265
5402
|
showToast(enabled ? t('watermarkEnabled') : t('watermarkDisabled'));
|
|
5266
5403
|
}
|