diff --git a/VERSION b/VERSION
index 42e3846..2f29dcd 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.0.119
+0.0.120
diff --git a/frontend/static/app.js b/frontend/static/app.js
index 1e902e0..c7e9b51 100644
--- a/frontend/static/app.js
+++ b/frontend/static/app.js
@@ -1452,20 +1452,20 @@ const Settings = {
trRowHtml(pr) { // ряд параметров одного профиля
const sel = (f, opts) => ``;
- const active = matchPreset(pr);
- const presets = `
Пресет:` +
- Object.keys(TR_PRESETS).map((k) =>
- ``).join("") +
- `
`;
+ // «Готовый профиль» SOFT/MEDIUM/HARD — выпадающее меню; выставляет все параметры разом
+ const qmatch = matchPreset(pr);
+ const qpresetSel = ``;
return (
- presets +
`` +
`` +
`` +
`` +
- `` +
+ `` +
`` +
- ``
+ `` +
+ ``
);
},
@@ -1493,32 +1493,28 @@ const Settings = {
const codec = row.querySelector('[data-f="codec"]');
const prof = row.querySelector('[data-f="profile"]');
const syncCodec = () => { prof.disabled = codec.value === "vp9"; };
- syncCodec();
- // режим контроля битрейта: VBR/CBR → поле «Битрейт», QP → поле «QP»
+ // QP ↔ битрейт: активен только параметр выбранного режима, второй — неактивен
const rcSel = row.querySelector('[data-f="rc"]');
- const brLabel = row.querySelector('[data-f="bitrate"]').closest("label");
- const qpLabel = row.querySelector('[data-f="qp"]').closest("label");
- const syncRc = () => { const isQp = rcSel.value === "qp"; qpLabel.hidden = !isQp; brLabel.hidden = isQp; };
- syncRc();
- // подсветить пресет, совпадающий с текущими значениями
- const highlight = () => {
- const cur = { codec: codec.value, profile: prof.value, bitrate: row.querySelector('[data-f="bitrate"]').value,
- keyint: row.querySelector('[data-f="keyint"]').value, preset: row.querySelector('[data-f="preset"]').value };
- const name = matchPreset(cur);
- row.querySelectorAll(".tr-preset").forEach((b) => b.classList.toggle("active", b.dataset.preset === name));
+ const brSel = row.querySelector('[data-f="bitrate"]');
+ const qpInp = row.querySelector('[data-f="qp"]');
+ const syncRc = () => { const isQp = rcSel.value === "qp"; brSel.disabled = isQp; qpInp.disabled = !isQp; };
+ // «Готовый профиль» (SOFT/MEDIUM/HARD): меню показывает совпадение или «— свой —»
+ const qpreset = row.querySelector(".qpreset");
+ const syncQuality = () => {
+ qpreset.value = matchPreset({ codec: codec.value, profile: prof.value, bitrate: brSel.value,
+ keyint: row.querySelector('[data-f="keyint"]').value, preset: row.querySelector('[data-f="preset"]').value });
};
- // общий пресет — выставить все параметры разом
- row.querySelectorAll(".tr-preset").forEach((btn) => {
- btn.onclick = () => {
- const p = TR_PRESETS[btn.dataset.preset];
- const put = (f, v) => { const s = row.querySelector(`[data-f="${f}"]`); if (s) s.value = String(v); };
- put("codec", p.codec); put("profile", p.profile); put("bitrate", p.bitrate);
- put("keyint", p.keyint); put("preset", p.preset); put("rc", "vbr");
- syncCodec(); syncRc(); highlight();
- };
- });
+ qpreset.onchange = () => {
+ const p = TR_PRESETS[qpreset.value];
+ if (!p) return;
+ const put = (f, v) => { const s = row.querySelector(`[data-f="${f}"]`); if (s) s.value = String(v); };
+ put("codec", p.codec); put("profile", p.profile); put("bitrate", p.bitrate);
+ put("keyint", p.keyint); put("preset", p.preset); put("rc", "vbr");
+ syncCodec(); syncRc();
+ };
+ syncCodec(); syncRc();
row.querySelectorAll("select[data-f]").forEach((s) => {
- s.addEventListener("change", () => { syncCodec(); syncRc(); highlight(); });
+ s.addEventListener("change", () => { syncCodec(); syncRc(); syncQuality(); });
});
});
document.getElementById("prefs-save").onclick = () => {
diff --git a/frontend/static/style.css b/frontend/static/style.css
index 2de19f9..520ef85 100644
--- a/frontend/static/style.css
+++ b/frontend/static/style.css
@@ -147,7 +147,7 @@ input[type="checkbox"]:checked {
}
input[type="checkbox"]:checked::before { transform: translateX(18px); background: var(--ok); }
input[type="checkbox"]:focus-visible { outline: 2px solid var(--ok); outline-offset: 2px; }
-input[type="date"], select {
+input[type="date"], input[type="number"], select {
background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
padding: 6px 8px; border-radius: 6px;
}
@@ -340,6 +340,8 @@ input[type="date"], select {
.tr-row[hidden] { display: none; }
.tr-row label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--text-dim); }
.tr-row select { display: block; }
+.tr-row input[type="number"] { display: block; width: 80px; }
+.tr-row select:disabled, .tr-row input:disabled { opacity: .4; cursor: not-allowed; }
/* общие пресеты перекодирования (SOFT / MEDIUM / HARD) */
.tr-presets { flex-basis: 100%; display: flex; align-items: center; gap: 8px; padding-left: 28px; }
.tr-preset {