Skip to content

Commit faa5b6c

Browse files
soso
authored andcommitted
release: v0.3.2
1 parent 8b1704c commit faa5b6c

9 files changed

Lines changed: 51 additions & 83 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@
183183
- OpenList 批量移动超时时会刷新源/目标目录做二次核验,实际已完成的文件会补成功日志。
184184
- 任务完成、部分失败、失败或中止后,会复用通知配置发送媒体刮削任务结果。
185185

186-
TMDb API 服务器默认为 `https://api.themoviedb.org`,可选择备用的 `https://api.tmdb.org`,也可输入自定义 HTTP/HTTPS 地址。如果所在网络需要代理,可在 `系统设置 -> 代理服务器` 中填写 `http://host:port``socks://host:port`;也支持 `user:password` 认证以及 `:password` 形式的仅密码认证。SOCKS5 仅密码认证使用零长度用户名兼容形式,需要代理服务器支持。代理地址保存后默认隐藏认证信息,点击输入框右侧的显示按钮可按需查看完整连接串。代理设置页可使用固定地址 `http://www.google.com/generate_204` 测试代理延迟,当前代理用于后端 TMDb API 请求。
186+
TMDb API 服务器默认为 `https://api.themoviedb.org`,可选择备用的 `https://api.tmdb.org`,也可输入自定义 HTTP/HTTPS 地址。如果所在网络需要代理,可在 `系统设置 -> 代理服务器` 中填写 `http://host:port``socks://host:port`;也支持 `user:password` 认证以及 `:password` 形式的仅密码认证。SOCKS5 仅密码认证使用零长度用户名兼容形式,需要代理服务器支持。代理地址保存后默认隐藏认证信息,点击输入框右侧的显示按钮可按需查看完整连接串。代理设置页可使用固定地址 `http://www.google.com/generate_204` 测试延迟;未填写代理地址时会直接连接,当前代理用于后端 TMDb API 请求。
187187

188188
## 使用方法
189189

buildozer.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ source.include_exts = py,png,jpg,jpeg,html,js,css,ttf,otf,svg,ico,json,gif,woff,
99
source.include_patterns = front/**,locales/**,common/**,controller/**,mapper/**,media_tools/**,service/**,doc/config.ini
1010
source.exclude_patterns = .git/**,.venv/**,data/**,tests/**,web/**
1111

12-
version = 0.3.1
12+
version = 0.3.2
1313

1414
requirements = python3,pyjnius,android,tornado,requests,pysocks,urllib3,certifi,chardet,idna,apscheduler,tzlocal,tzdata,setuptools,configparser,pathspec,pyyaml,openssl,sqlite3
1515

doc/changelog/v0.3.2.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# v0.3.2(2026-08-01)
2+
3+
变更概述
4+
5+
- 代理服务器:延迟测试允许未填写代理地址时直接连接固定 Google 204 地址;已填写地址时仍使用该代理测试。
6+
- 代理服务器:移除清除已保存认证信息的按钮、文案和后端处理,保留完整连接串显示、地址修改和认证信息保护。
7+
- 文档:更新代理延迟测试的直连行为说明。
8+
9+
验证说明
10+
11+
- 已使用源码模式完成 Python 语法、后端单元测试、前端单元测试、ESLint、Vue dev server 和本地服务连通性验证。
12+
- 未在本地执行前端生产构建、Docker 构建、PyInstaller、StaticX、Android APK 或发布包构建。

service/system/configService.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,6 @@ def _safe_proxy_url(url):
179179
))
180180

181181

182-
def _strip_proxy_credentials(url):
183-
if not url:
184-
return ''
185-
parts = urllib.parse.urlsplit(url)
186-
host_port = parts.netloc.rsplit('@', 1)[-1]
187-
return urllib.parse.urlunsplit((parts.scheme, host_port, '', '', ''))
188-
189-
190182
def _public_proxy_server(proxy):
191183
url = proxy.get('url') or ''
192184
parts = urllib.parse.urlsplit(url)
@@ -210,19 +202,13 @@ def _proxy_url_for_test(req):
210202
if 'url' in req:
211203
submitted_url = _normalize_proxy_url(req.get('url'))
212204

213-
if _to_bool(req.get('clearCredentials'), False):
214-
proxy_url = _strip_proxy_credentials(
215-
current_url if submitted_url is None else submitted_url
216-
)
217-
elif submitted_url is None:
205+
if submitted_url is None:
218206
proxy_url = current_url
219207
elif submitted_url == _safe_proxy_url(current_url):
220208
proxy_url = current_url
221209
else:
222210
proxy_url = submitted_url
223211

224-
if not proxy_url:
225-
raise Exception('Proxy server URL is required for latency test')
226212
return proxy_url
227213

228214

@@ -304,9 +290,6 @@ def updateConfig(req):
304290
else submitted_url
305291
)
306292
update_proxy = True
307-
if _to_bool(update.get('clearCredentials'), False):
308-
proxy_url_to_save = _strip_proxy_credentials(proxy_url_to_save)
309-
update_proxy = True
310293
if 'enabled' in update:
311294
update_proxy = True
312295
if proxy_enabled and not proxy_url_to_save:

tests/test_backend_v040.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ def set_value(key, value):
430430
side_effect=lambda key: store.get(key)), mock.patch.object(
431431
configService.systemConfigMapper,
432432
'setConfigValue',
433-
side_effect=set_value) as set_mock:
433+
side_effect=set_value):
434434
result = configService.getConfig()
435435
self.assertEqual({
436436
'enabled': True,
@@ -460,15 +460,6 @@ def set_value(key, value):
460460
self.assertEqual('*.nfo:@eaDir', result['globalExclude'])
461461
self.assertEqual(previous_proxy, store[configService.PROXY_SERVER_KEY])
462462

463-
result = configService.updateConfig({
464-
'proxyServer': {'clearCredentials': True},
465-
})
466-
saved_proxy = json.loads(store[configService.PROXY_SERVER_KEY])
467-
self.assertEqual('http://old-proxy.local:8080', saved_proxy['url'])
468-
self.assertFalse(result['proxyServer']['passwordSet'])
469-
self.assertNotIn('password', result['proxyServer'])
470-
self.assertGreaterEqual(set_mock.call_count, 3)
471-
472463
def test_system_config_accepts_proxy_url_auth_variants_and_new_key_wins(self):
473464
cases = [
474465
('http://proxy.local:8080', 'http://proxy.local:8080', False),
@@ -706,6 +697,38 @@ def test_proxy_latency_test_uses_submitted_url_without_persisting(self):
706697
response.close.assert_called_once_with()
707698
session.close.assert_called_once_with()
708699

700+
def test_proxy_latency_test_uses_direct_connection_without_proxy_url(self):
701+
response = mock.Mock(status_code=204)
702+
session = mock.Mock()
703+
session.get.return_value = response
704+
705+
with mock.patch.object(
706+
configService,
707+
'getProxyServer',
708+
return_value={'enabled': False, 'url': ''}), mock.patch.object(
709+
configService.mediaRenamer.requests,
710+
'Session',
711+
return_value=session), mock.patch.object(
712+
configService.time,
713+
'monotonic',
714+
side_effect=[10.0, 10.123]):
715+
result = configService.testProxyServer({})
716+
717+
self.assertEqual({
718+
'url': configService.PROXY_TEST_URL,
719+
'latencyMs': 123,
720+
'statusCode': 204,
721+
}, result)
722+
self.assertFalse(session.trust_env)
723+
session.get.assert_called_once_with(
724+
configService.PROXY_TEST_URL,
725+
timeout=configService.PROXY_TEST_TIMEOUT,
726+
proxies={},
727+
allow_redirects=False,
728+
)
729+
response.close.assert_called_once_with()
730+
session.close.assert_called_once_with()
731+
709732
def test_proxy_latency_test_preserves_saved_credentials_for_redacted_url(self):
710733
response = mock.Mock(status_code=204)
711734
session = mock.Mock()

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
v0.3.1,latest
1+
v0.3.2,latest
22

33
该文件仅第一行有效,表示打包的版本。
44
如果要打包多个,以英文,分隔,例如【v1.0.0,latest】;

web/src/locales/en.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,10 @@ setting:
484484
proxyUrlExamples: "Supports http://host:port, http://user:password{'@'}host:port, and socks://:password{'@'}host:port; percent-encode special characters"
485485
proxyCredentialsStored: Credentials are saved and remain unchanged while the URL is unchanged
486486
proxyCredentialsReplace: Re-enter the complete credentials when changing the URL
487-
proxyClearCredentials: Clear saved credentials
488-
proxyCancelClearCredentials: Cancel clearing credentials
489-
proxyCredentialsWillClear: The username and password will be cleared when you save
490487
proxyShowCredentials: Show complete connection string
491488
proxyHideCredentials: Hide complete connection string
492489
proxyTest: Test Latency
493490
proxyTestTarget: "Test URL: http://www.google.com/generate_204"
494-
proxyTestUrlRequired: Enter a proxy URL before testing
495491
proxyTestResult: "{latency} ms · HTTP {status}"
496492
saveProxy: Save Proxy Settings
497493
aboutDescription: A focused workspace for directory sync and media naming

web/src/locales/zh-CN.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,10 @@ setting:
484484
proxyUrlExamples: "支持 http://host:port、http://user:password{'@'}host:port、socks://:password{'@'}host:port;特殊字符请使用 URL 编码"
485485
proxyCredentialsStored: 已保存认证信息;地址未修改时将保持不变
486486
proxyCredentialsReplace: 修改地址时请重新填写完整认证信息
487-
proxyClearCredentials: 清除已保存的认证信息
488-
proxyCancelClearCredentials: 取消清除认证信息
489-
proxyCredentialsWillClear: 保存后将清除用户名和密码
490487
proxyShowCredentials: 显示完整连接串
491488
proxyHideCredentials: 隐藏完整连接串
492489
proxyTest: 测试延迟
493490
proxyTestTarget: "测试地址:http://www.google.com/generate_204"
494-
proxyTestUrlRequired: 测试前请输入代理地址
495491
proxyTestResult: "{latency} ms · HTTP {status}"
496492
saveProxy: 保存代理设置
497493
aboutDescription: 一个专注于目录同步与媒体命名的轻量工作台

web/src/views/pages/setting/ProxyServerSettings.vue

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { computed, onMounted, ref } from "vue";
33
import { getSystemConfig, revealProxyServer, saveSystemConfig, testProxyServer } from "@/api/system";
44
import { ElMessage } from "element-plus";
55
import { useI18n } from "vue-i18n";
6-
import { Eye, EyeOff, Gauge, LoaderCircle, Network, Save, Trash2, Undo2 } from "@lucide/vue";
6+
import { Eye, EyeOff, Gauge, LoaderCircle, Network, Save } from "@lucide/vue";
77
88
const { t } = useI18n();
99
const formRef = ref();
@@ -20,7 +20,6 @@ const proxy = ref({
2020
enabled: false,
2121
url: "",
2222
passwordSet: false,
23-
clearCredentials: false,
2423
});
2524
2625
const urlChanged = computed(() => proxy.value.url.trim() !== initialUrl.value);
@@ -31,7 +30,6 @@ const applyConfig = (config = {}) => {
3130
enabled: Boolean(value.enabled),
3231
url: String(value.url || ""),
3332
passwordSet: Boolean(value.passwordSet),
34-
clearCredentials: false,
3533
};
3634
initialUrl.value = proxy.value.url;
3735
credentialsVisible.value = false;
@@ -76,12 +74,6 @@ const loadConfig = () => {
7674
};
7775
7876
const onUrlInput = () => {
79-
proxy.value.clearCredentials = false;
80-
testResult.value = null;
81-
};
82-
83-
const toggleClearCredentials = () => {
84-
proxy.value.clearCredentials = !proxy.value.clearCredentials;
8577
testResult.value = null;
8678
};
8779
@@ -117,20 +109,12 @@ const buildProxyTestPayload = () => {
117109
if (urlChanged.value) {
118110
payload.url = proxy.value.url.trim();
119111
}
120-
if (proxy.value.clearCredentials) {
121-
payload.clearCredentials = true;
122-
}
123112
return payload;
124113
};
125114
126115
const runProxyTest = () => {
127116
formRef.value.validate((valid) => {
128117
if (!valid) return;
129-
if (!proxy.value.url.trim()) {
130-
ElMessage({ message: t("setting.proxyTestUrlRequired"), type: "warning" });
131-
return;
132-
}
133-
134118
testing.value = true;
135119
testResult.value = null;
136120
testProxyServer(buildProxyTestPayload())
@@ -152,9 +136,6 @@ const saveConfig = () => {
152136
if (urlChanged.value) {
153137
proxyServer.url = proxy.value.url.trim();
154138
}
155-
if (proxy.value.clearCredentials) {
156-
proxyServer.clearCredentials = true;
157-
}
158139
159140
saving.value = true;
160141
testResult.value = null;
@@ -224,24 +205,9 @@ onMounted(loadConfig);
224205
</button>
225206
</template>
226207
</el-input>
227-
<el-tooltip
228-
v-if="proxy.passwordSet && !urlChanged"
229-
:content="proxy.clearCredentials ? $t('setting.proxyCancelClearCredentials') : $t('setting.proxyClearCredentials')"
230-
placement="top"
231-
>
232-
<el-button
233-
class="credential-button"
234-
:type="proxy.clearCredentials ? 'default' : 'danger'"
235-
:icon="proxy.clearCredentials ? Undo2 : Trash2"
236-
:disabled="revealing"
237-
:aria-label="proxy.clearCredentials ? $t('setting.proxyCancelClearCredentials') : $t('setting.proxyClearCredentials')"
238-
@click="toggleClearCredentials"
239-
/>
240-
</el-tooltip>
241208
</div>
242209
<p class="field-tip">{{ $t("setting.proxyUrlExamples") }}</p>
243-
<p v-if="proxy.clearCredentials" class="credential-warning">{{ $t("setting.proxyCredentialsWillClear") }}</p>
244-
<p v-else-if="proxy.passwordSet && !urlChanged" class="credential-status">{{ $t("setting.proxyCredentialsStored") }}</p>
210+
<p v-if="proxy.passwordSet && !urlChanged" class="credential-status">{{ $t("setting.proxyCredentialsStored") }}</p>
245211
<p v-else-if="proxy.passwordSet && urlChanged" class="credential-warning">{{ $t("setting.proxyCredentialsReplace") }}</p>
246212
</el-form-item>
247213
@@ -383,14 +349,6 @@ onMounted(loadConfig);
383349
}
384350
}
385351
386-
.credential-button {
387-
width: 42px;
388-
height: 42px;
389-
flex: 0 0 42px;
390-
margin: 0;
391-
padding: 0;
392-
}
393-
394352
.reveal-button {
395353
width: 28px;
396354
height: 28px;

0 commit comments

Comments
 (0)