Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#build--distribution)
[![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/)
[![Downloads](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=downloads)](https://github.com/CaYatur/SoundVisualizer/releases)
[![Tests](https://img.shields.io/badge/tests-2556%20passing-2ea043.svg)](#tests)
[![Tests](https://img.shields.io/badge/tests-2561%20passing-2ea043.svg)](#tests)
[![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com)

</div>
Expand Down Expand Up @@ -1341,7 +1341,7 @@ npm test
npm start -- --smoke
```

**2556 unit tests, all passing.** They are written to check answers, not to exercise lines:
**2561 unit tests, all passing.** They are written to check answers, not to exercise lines:

- **Formulas** are checked against values derived by hand from their definitions — Viviani's curve
staying on its sphere, the torus tube radius, Chladni's m↔n antisymmetry, every attractor
Expand Down
4 changes: 2 additions & 2 deletions README.tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#paketleme--dağıtım)
[![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/)
[![İndirme](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=indirme)](https://github.com/CaYatur/SoundVisualizer/releases)
[![Test](https://img.shields.io/badge/test-2556%20geçiyor-2ea043.svg)](#testler)
[![Test](https://img.shields.io/badge/test-2561%20geçiyor-2ea043.svg)](#testler)
[![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com)

</div>
Expand Down Expand Up @@ -1341,7 +1341,7 @@ npm test
npm start -- --smoke
```

**2556 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar:
**2561 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar:

- **Formüller**, tanımlarından elle türetilmiş değerlerle sınanıyor — Viviani eğrisinin küre
üzerinde kalması, simidin boru yarıçapı, Chladni'nin m↔n antisimetrisi, her çekicinin sınırlı
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ npm test
npm start -- --smoke
```

- **2556 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0;
- **2561 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0;
105 came with v3.1.1; 163 came with v3.1.2; 157 came with v3.1.3 — 1128 at
that tag — 469 more with v3.1.4, most of them from the MilkDrop work, and 878
on `main` since.
Expand Down
112 changes: 107 additions & 5 deletions src/admin/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@
return imagesCtrl(def);
case 'logofile':
return logoFileCtrl(def);
case 'npcoverfile':
return npCoverFileCtrl(def);
case 'logolibrary':
return logoLibraryCtrl(def);
case 'floatingtoggle':
Expand Down Expand Up @@ -1092,6 +1094,8 @@
}

function logoFileCtrl() {
/* macOS/Linux albüm kapağını okuyamaz. Kayıtlı auto/track burada özel resme iner. */
if (!isWindows() && cfg.logo && cfg.logo.source !== 'manual') cfg.logo.source = 'manual';
const fileInput = el('input', { type: 'file', accept: 'image/*' });
fileInput.style.display = 'none';
fileInput.addEventListener('change', (e) => {
Expand Down Expand Up @@ -1126,7 +1130,7 @@
});
removeBtn.style.marginLeft = '8px';

const mode = cfg.logo.source || 'auto';
const mode = isWindows() ? (cfg.logo.source || 'auto') : 'manual';
if (mode === 'track') {
const live = (window.SVNowLive && window.SVNowLive.state && window.SVNowLive.state.has) ? window.SVNowLive.state : null;
const note = el('div', {
Expand Down Expand Up @@ -1159,6 +1163,46 @@
]);
}

/* Çalan parça kapağı: Windows'ta otomatik kapağın yedeği, diğer platformlarda tek kaynak. */
function npCoverFileCtrl() {
const C = cfg.nowplaying || (cfg.nowplaying = {});
C.manual = C.manual || {};
if (!isWindows()) C.coverSource = 'manual';
const fileInput = el('input', { type: 'file', accept: 'image/*' });
fileInput.style.display = 'none';
fileInput.addEventListener('change', (e) => {
const file = e.target.files && e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
C.manual.artwork = reader.result;
push(true);
render();
};
reader.readAsDataURL(file);
});
const pick = el('button', {
class: 'btn small', type: 'button', icon: 'image',
text: C.manual.artwork ? 'Kapağı Değiştir' : 'Kapak Seç',
onclick: () => fileInput.click(),
});
const remove = C.manual.artwork ? el('button', {
class: 'btn ghost small danger', type: 'button', text: 'Kapağı Kaldır',
onclick: () => {
C.manual.artwork = '';
push(true);
render();
},
}) : null;
const preview = C.manual.artwork
? el('img', { class: 'logo-preview', src: C.manual.artwork, alt: 'Kapak Görseli', style: 'display:block' })
: null;
return el('div', { class: 'ctrl' }, [
el('div', { class: 'row' }, [pick, remove, fileInput].filter(Boolean)),
preview,
].filter(Boolean));
}

function isGifLogo(lg) {
if (!lg) return false;
if (window.SVGif && window.SVGif.isAnimatedLogo) return window.SVGif.isAnimatedLogo(lg, lg.src);
Expand Down Expand Up @@ -2278,6 +2322,8 @@
{
type: 'color', path: 'nowplaying.colorBar', label: 'Çubuk',
show: () => v.type === 'nowplaying'
&& isWindows()
&& ((cfg.nowplaying && cfg.nowplaying.source) || 'system') === 'system'
&& (v.colorMode || (v.rainbow ? 'rainbow' : 'custom')) === 'custom',
},
{
Expand All @@ -2290,6 +2336,29 @@
text: 'Çalan parçanın albüm kapağını yazının yanına veya üstüne yerleştirir. Boyut ekranın kısa kenarına göredir. Kapak yoksa bindirme çizilmez. Varsayılan kapalıdır.',
show: () => v.type === 'nowplaying' && !!(cfg.nowplaying && cfg.nowplaying.coverOverlay),
},
{
type: 'select', path: 'nowplaying.coverSource', label: 'Kapak Kaynağı', rebuild: true,
options: [
{ value: 'auto', label: 'Otomatik (Sistem)' },
{ value: 'manual', label: 'Elle Yükle' },
],
show: () => isWindows() && v.type === 'nowplaying' && !!(cfg.nowplaying && cfg.nowplaying.coverOverlay),
},
{
type: 'note',
text: () => {
if (!isWindows()) return 'Bu platformda albüm kapağı otomatik okunamaz. Gösterilecek resmi elle yükleyin.';
const src = (cfg.nowplaying && cfg.nowplaying.coverSource) || 'auto';
return src === 'manual'
? 'Elle yüklenen resim yazının yanında gösterilir.'
: 'Windows’ta kapak çalan parçadan otomatik gelir. İsterseniz kendi resminizi de yükleyebilirsiniz; otomatik kapak yoksa o resim kullanılır.';
},
show: () => v.type === 'nowplaying' && !!(cfg.nowplaying && cfg.nowplaying.coverOverlay),
},
{
type: 'npcoverfile',
show: () => v.type === 'nowplaying' && !!(cfg.nowplaying && cfg.nowplaying.coverOverlay),
},
{
type: 'slider', path: 'nowplaying.coverSize', label: 'Kapak Boyutu',
min: 0.05, max: 0.5, step: 0.01, percent: true,
Expand Down Expand Up @@ -2505,7 +2574,9 @@
category: 'scene',
icon: 'music',
title: 'Çalan Parça',
desc: 'Bilgisayarda çalan parçayı ekrana getirir: ad, sanatçı, geçen ve kalan süre, ilerleme çubuğu. Sürekli görünebilir ya da yalnızca parça değişince canlandırmayla belirir.',
desc: isWindows()
? 'Bilgisayarda çalan parçayı ekrana getirir: ad, sanatçı, geçen ve kalan süre, ilerleme çubuğu. Sürekli görünebilir ya da yalnızca parça değişince canlandırmayla belirir.'
: 'Elle yazılan parça adı ve sanatçı ekrana gelir. Albüm kapağı elle yüklenir. Süre ve ilerleme çubuğu sistemden okunduğu için bu platformda yoktur.',
controls: [{ type: 'nowplayingpanel' }],
show: () => notStack() && v.type === 'nowplaying',
},
Expand Down Expand Up @@ -2791,7 +2862,7 @@
{ value: 'manual', label: 'Özel Resim (Yalnızca seçilen dosya)' },
{ value: 'track', label: 'Sadece Çalan Şarkı Resmi' },
],
show: () => cfg.logo.enabled,
show: () => cfg.logo.enabled && isWindows(),
rebuild: true,
},
{ type: 'logofile', show: () => cfg.logo.enabled },
Expand Down Expand Up @@ -4725,8 +4796,39 @@
if (advBox) advBox.checked = advancedOn;
if (!CATEGORIES.some((c) => c.id === activeCategory)) activeCategory = CATEGORIES[0].id;
if (!(window.SV_PLATFORM && window.SV_PLATFORM.isWindows)) {
if (cfg.dynamicTheme) cfg.dynamicTheme.enabled = false;
if (cfg.nowplaying && cfg.nowplaying.source === 'system') cfg.nowplaying.source = 'manual';
let platformTouched = false;
if (cfg.dynamicTheme && cfg.dynamicTheme.enabled) {
cfg.dynamicTheme.enabled = false;
platformTouched = true;
}
if (cfg.nowplaying) {
if (cfg.nowplaying.source === 'system') { cfg.nowplaying.source = 'manual'; platformTouched = true; }
if (cfg.nowplaying.coverSource !== 'manual') { cfg.nowplaying.coverSource = 'manual'; platformTouched = true; }
}
if (cfg.logo && cfg.logo.source && cfg.logo.source !== 'manual') {
cfg.logo.source = 'manual';
platformTouched = true;
}
if (cfg.text && cfg.text.nowSource === 'system') {
cfg.text.nowSource = 'manual';
platformTouched = true;
}
if (Array.isArray(cfg.layers)) {
cfg.layers.forEach((l) => {
if (!l || !l.settings) return;
if (l.kind === 'logo' && l.settings.logo && l.settings.logo.source && l.settings.logo.source !== 'manual') {
l.settings.logo.source = 'manual';
platformTouched = true;
}
if (l.settings.nowplaying && l.settings.nowplaying.coverSource !== 'manual') {
l.settings.nowplaying.coverSource = 'manual';
platformTouched = true;
}
const lt = l.settings.text;
if (lt && lt.nowSource === 'system') { lt.nowSource = 'manual'; platformTouched = true; }
});
}
if (platformTouched) push(true);
}

// Seçili arayüz dilini ana sürece bildir (diyaloglar ve yayın sayfaları)
Expand Down
85 changes: 73 additions & 12 deletions src/admin/nowplaying-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,43 @@
setInterval(paintStatus, 500);
}

function coverFileRow(el, P, C, rerender) {
C.manual = C.manual || {};
const fileInput = el('input', { type: 'file', accept: 'image/*' });
fileInput.style.display = 'none';
fileInput.addEventListener('change', (e) => {
const file = e.target.files && e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
C.manual.artwork = reader.result;
P().push(true);
rerender();
};
reader.readAsDataURL(file);
});
const pick = el('button', {
class: 'btn small', type: 'button', icon: 'image',
text: C.manual.artwork ? 'Kapağı Değiştir' : 'Kapak Seç',
onclick: () => fileInput.click(),
});
const remove = C.manual.artwork ? el('button', {
class: 'btn ghost small danger', type: 'button', text: 'Kapağı Kaldır',
onclick: () => {
C.manual.artwork = '';
P().push(true);
rerender();
},
}) : null;
const preview = C.manual.artwork
? el('img', { class: 'logo-preview', src: C.manual.artwork, alt: 'Kapak Görseli', style: 'display:block' })
: null;
return el('div', { class: 'ctrl' }, [
el('div', { class: 'row' }, [pick, remove, fileInput].filter(Boolean)),
preview,
].filter(Boolean));
}

// ------------------------------------------------------------------ panel
function panel() {
const el = P().el;
Expand All @@ -105,7 +142,9 @@

const isWin = !!(window.SV_PLATFORM && window.SV_PLATFORM.isWindows);
if (!isWin && (C.source || 'system') === 'system') C.source = 'manual';
if (!isWin) C.coverSource = 'manual';
const src = C.source || (isWin ? 'system' : 'manual');
const fromSystem = isWin && src === 'system';
if (src === 'system' && isWin && window.api && window.api.nowPlayingSubscribe) {
window.api.nowPlayingSubscribe(true);
}
Expand Down Expand Up @@ -164,25 +203,33 @@
f('title', 'Parça Adı', true),
f('artist', 'Sanatçı', true),
f('album', 'Albüm', false),
f('appName', 'Oynatıcı Adı', false),
f('elapsed', 'Geçen Süre', true),
f('remaining', 'Kalan Süre', false),
f('total', 'Toplam Süre', true),
f('bar', 'İlerleme Çubuğu', true),
];
/* Süre ve oynatıcı adı sistem oturumundan gelir. Elle yazılan
parçada bu alanlar 0:00 üretirdi; o yüzden gizlenir. */
if (fromSystem) {
kids.push(f('appName', 'Oynatıcı Adı', false));
kids.push(f('elapsed', 'Geçen Süre', true));
kids.push(f('remaining', 'Kalan Süre', false));
kids.push(f('total', 'Toplam Süre', true));
kids.push(f('bar', 'İlerleme Çubuğu', true));
}
kids.push(SP().miniToggle('Parça ve Sanatçı Tek Satırda', () => !!C.oneLine, (v) => { C.oneLine = v; }, rerender));
if (C.oneLine) {
kids.push(P().row('Ayırıcı', el('input', {
class: 'p-in', type: 'text', value: C.separator === undefined ? ' — ' : C.separator,
oninput: (e) => { C.separator = e.target.value; P().push(false); },
})));
}
kids.push(P().row('Süre Ayırıcı', el('input', {
class: 'p-in', type: 'text', value: C.timeSeparator === undefined ? ' / ' : C.timeSeparator,
oninput: (e) => { C.timeSeparator = e.target.value; P().push(false); },
})));
if (fromSystem) {
kids.push(P().row('Süre Ayırıcı', el('input', {
class: 'p-in', type: 'text', value: C.timeSeparator === undefined ? ' / ' : C.timeSeparator,
oninput: (e) => { C.timeSeparator = e.target.value; P().push(false); },
})));
}
kids.push(el('div', { class: 'studio-note dim-hint',
text: 'Her alan tek tek kapatılabilir: yalnızca parça adı, yalnızca süre ya da yalnızca çubuk gösterilebilir.' }));
text: fromSystem
? 'Her alan tek tek kapatılabilir: yalnızca parça adı, yalnızca süre ya da yalnızca çubuk gösterilebilir.'
: 'Parça adı, sanatçı ve albüm tek tek kapatılabilir.' }));
return kids;
}));

Expand Down Expand Up @@ -226,7 +273,7 @@
}));

// ---------------------------------------------------------------- çubuk
if (C.show.bar !== false) {
if (fromSystem && C.show.bar !== false) {
nodes.push(SP().foldable('İlerleme Çubuğu', () => {
const style = NP() ? NP().styleOf(C.style) : { barHeight: 0.005, barSegments: 0, barBackOpacity: 0.22 };
return [
Expand All @@ -251,6 +298,20 @@
if (C.coverOverlay) {
nodes.push(el('div', { class: 'studio-note dim-hint',
text: 'Çalan parçanın albüm kapağını yazının yanına veya üstüne yerleştirir. Boyut ekranın kısa kenarına göredir. Kapak yoksa bindirme çizilmez. Varsayılan kapalıdır.' }));
if (isWin) {
nodes.push(SP().miniSelect('Kapak Kaynağı', [
['auto', 'Otomatik (Sistem)'],
['manual', 'Elle Yükle'],
], () => C.coverSource || 'auto', (v) => { C.coverSource = v; }, rerender));
nodes.push(el('div', { class: 'studio-note dim-hint',
text: (C.coverSource || 'auto') === 'manual'
? 'Elle yüklenen resim yazının yanında gösterilir.'
: 'Windows’ta kapak çalan parçadan otomatik gelir. İsterseniz kendi resminizi de yükleyebilirsiniz; otomatik kapak yoksa o resim kullanılır.' }));
} else {
nodes.push(el('div', { class: 'studio-note dim-hint',
text: 'Bu platformda albüm kapağı otomatik okunamaz. Gösterilecek resmi elle yükleyin.' }));
}
nodes.push(coverFileRow(el, P, C, rerender));
nodes.push(SP().foldable('Albüm Kapağı (Bindirme)', () => [
SP().miniSlider('Boyut', () => (C.coverSize == null ? 0.14 : C.coverSize),
(v) => { C.coverSize = v; }, { min: 0.05, max: 0.5, step: 0.01, percent: true }),
Expand Down Expand Up @@ -295,7 +356,7 @@
if (getMode() === 'custom') {
kids.push(P().color('Parça Adı', 'nowplaying.color'));
kids.push(P().color('İkincil Yazı', 'nowplaying.colorDim'));
kids.push(P().color('Çubuk', 'nowplaying.colorBar'));
if (fromSystem) kids.push(P().color('Çubuk', 'nowplaying.colorBar'));
} else if (getMode() === 'theme') {
kids.push(el('div', { class: 'studio-note dim-hint',
text: 'Renkler sahne paletinden alınır; palet değişince yazı da değişir.' }));
Expand Down
Loading
Loading