From 480966f97825d8bfc2e3086ce4c35508de4af840 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 30 Oct 2025 23:08:30 +0100 Subject: [PATCH] overview: add music recognition button to search bar --- .../ii/modules/common/Appearance.qml | 4 +- .../common/widgets/IconToolbarButton.qml | 3 +- .../ii/modules/overview/SearchBar.qml | 50 ++++++++- .../androidStyle/AndroidMusicRecognition.qml | 82 +------------- .../quickshell/ii/services/SongRec.qml | 103 ++++++++++++++++++ 5 files changed, 161 insertions(+), 81 deletions(-) create mode 100644 dots/.config/quickshell/ii/services/SongRec.qml diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index 8087f80ae..f78d49a07 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -357,8 +357,8 @@ Singleton { property real mediaControlsHeight: 160 property real notificationPopupWidth: 410 property real osdWidth: 180 - property real searchWidthCollapsed: 260 - property real searchWidth: 400 + property real searchWidthCollapsed: 210 + property real searchWidth: 360 property real sidebarWidth: 460 property real sidebarWidthExtended: 750 property real baseVerticalBarWidth: 46 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml index 8532d0cd4..df87dcb0e 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml @@ -9,6 +9,7 @@ ToolbarButton { colBackgroundToggled: Appearance.colors.colSecondaryContainer colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover colRippleToggled: Appearance.colors.colSecondaryContainerActive + property color colText: toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurfaceVariant contentItem: MaterialSymbol { anchors.centerIn: parent @@ -16,6 +17,6 @@ ToolbarButton { verticalAlignment: Text.AlignVCenter iconSize: 22 text: iconBtn.text - color: iconBtn.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurfaceVariant + color: iconBtn.colText } } diff --git a/dots/.config/quickshell/ii/modules/overview/SearchBar.qml b/dots/.config/quickshell/ii/modules/overview/SearchBar.qml index c054c2395..25723be1a 100644 --- a/dots/.config/quickshell/ii/modules/overview/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/overview/SearchBar.qml @@ -92,8 +92,6 @@ RowLayout { } } - // background: null - cursorDelegate: Rectangle { width: 1 color: searchInput.activeFocus ? Appearance.colors.colPrimary : "transparent" @@ -102,10 +100,58 @@ RowLayout { } IconToolbarButton { + Layout.topMargin: 4 + Layout.bottomMargin: 4 onClicked: { GlobalStates.overviewOpen = false; Hyprland.dispatch("global quickshell:regionSearch") } text: "image_search" + StyledToolTip { + text: Translation.tr("Google Lens") + } + } + + IconToolbarButton { + id: songRecButton + Layout.topMargin: 4 + Layout.bottomMargin: 4 + Layout.rightMargin: 4 + toggled: SongRec.running + onClicked: SongRec.toggleRunning() + text: "music_cast" + + StyledToolTip { + text: Translation.tr("Recognize music") + } + + colText: toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSurfaceVariant + background: MaterialShape { + RotationAnimation on rotation { + running: songRecButton.toggled + duration: 12000 + easing.type: Easing.Linear + loops: Animation.Infinite + from: 0 + to: 360 + } + shape: { + if (songRecButton.down) { + return songRecButton.toggled ? MaterialShape.Shape.Circle : MaterialShape.Shape.Square + } else { + return songRecButton.toggled ? MaterialShape.Shape.SoftBurst : MaterialShape.Shape.Circle + } + } + color: { + if (songRecButton.toggled) { + return songRecButton.hovered ? Appearance.colors.colPrimaryHover : Appearance.colors.colPrimary + } else { + return songRecButton.hovered ? Appearance.colors.colSurfaceContainerHigh : ColorUtils.transparentize(Appearance.colors.colSurfaceContainerHigh) + } + } + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml index a1b9c6738..e8f17f657 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml @@ -10,93 +10,23 @@ import qs.services AndroidQuickToggleButton { id: root - property int timeoutInterval: Config.options.musicRecognition.interval - property int timeoutDuration: Config.options.musicRecognition.timeout - - - property string monitorSource: "monitor" // "monitor" (system sound) , "input" (microphone) + toggled: SongRec.running + property bool sourceIsMonitor: SongRec.monitorSource === SongRec.MonitorSource.Monitor name: Translation.tr("Identify Music") - statusText: toggled ? Translation.tr("Listening...") : monitorSource === "monitor" ? Translation.tr("System sound") : Translation.tr("Microphone") - toggled: false - buttonIcon: toggled ? "music_cast" : (monitorSource === "monitor" ? "music_note" : "frame_person_mic") - - property var recognizedTrack: ({ title:"", subtitle:"", url:""}) - - function handleRecognition(jsonText) { - try { - var obj = JSON.parse(jsonText) - root.recognizedTrack = { - title: obj.track.title, - subtitle: obj.track.subtitle, - url: obj.track.url - } - musicReconizedProc.running = true - } catch(e) { - Quickshell.execDetached(["notify-send", Translation.tr("Couldn't recognize music"), Translation.tr("Perhaps what you're listening to is too niche"), "-a", "Shell"]) - } finally { - root.toggled = false - } - } - + statusText: toggled ? Translation.tr("Listening...") : sourceIsMonitor ? Translation.tr("System sound") : Translation.tr("Microphone") + buttonIcon: toggled ? "music_cast" : (sourceIsMonitor ? "music_note" : "frame_person_mic") StyledToolTip { text: Translation.tr("Recognize music | Right-click to toggle source") } onClicked: { - root.toggled = !root.toggled - recognizeMusicProc.running = root.toggled - musicReconizedProc.running = false + SongRec.toggleRunning() } altAction: () => { - if (root.monitorSource === "monitor"){ - root.monitorSource = "input" - return - }else { - root.monitorSource = "monitor" - } - + SongRec.toggleMonitorSource() } - Process { - id: recognizeMusicProc - running: false - command: [`${Directories.scriptPath}/musicRecognition/recognize-music.sh`, "-i", root.timeoutInterval, "-t", root.timeoutDuration, "-s", root.monitorSource] - stdout: StdioCollector { - onStreamFinished: { - handleRecognition(this.text) - } - } - onExited: (exitCode, exitStatus) => { - if (exitCode === 1) { - Quickshell.execDetached(["notify-send", Translation.tr("Couldn't recognize music"), Translation.tr("Make sure you have songrec installed"), "-a", "Shell"]) - root.toggled = false - } - } - } - - Process { - id: musicReconizedProc - running: false - command: [ - "notify-send", - Translation.tr("Music Recognized"), - root.recognizedTrack.title + " - " + root.recognizedTrack.subtitle, - "-A", "Shazam", - "-A", "YouTube", - "-a", "Shell" - ] - stdout: StdioCollector { - onStreamFinished: { - if (this.text === "") return - if (this.text == 0){ - Qt.openUrlExternally(root.recognizedTrack.url); - } else { - Qt.openUrlExternally("https://www.youtube.com/results?search_query=" + root.recognizedTrack.title + " - " + root.recognizedTrack.subtitle); - } - } - } - } } diff --git a/dots/.config/quickshell/ii/services/SongRec.qml b/dots/.config/quickshell/ii/services/SongRec.qml new file mode 100644 index 000000000..0c75f5c4a --- /dev/null +++ b/dots/.config/quickshell/ii/services/SongRec.qml @@ -0,0 +1,103 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import qs.modules.common +import QtQuick +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + enum MonitorSource { Monitor, Input } + + property var monitorSource: SongRec.MonitorSource.Monitor + property int timeoutInterval: Config.options.musicRecognition.interval + property int timeoutDuration: Config.options.musicRecognition.timeout + readonly property bool running: recognizeMusicProc.running + + function toggleRunning(running) { + if (recognizeMusicProc.running && !running === true) root.manuallyStopped = true; + if (running != undefined) { + recognizeMusicProc.running = running + } else { + recognizeMusicProc.running = !root.running + } + musicReconizedProc.running = false + } + + function toggleMonitorSource(source) { + if (source !== undefined) { + root.monitorSource = source + return + } + root.monitorSource = (root.monitorSource === SongRec.MonitorSource.Monitor) ? SongRec.MonitorSource.Input : SongRec.MonitorSource.Monitor + } + function monitorSourceToString(source) { + if (source === SongRec.MonitorSource.Monitor) { + return "monitor" + } else { + return "input" + } + } + readonly property string monitorSourceString: monitorSourceToString(monitorSource) + property var recognizedTrack: ({ title:"", subtitle:"", url:""}) + property bool manuallyStopped: false + + function handleRecognition(jsonText) { + try { + var obj = JSON.parse(jsonText) + root.recognizedTrack = { + title: obj.track.title, + subtitle: obj.track.subtitle, + url: obj.track.url + } + musicReconizedProc.running = true + } catch(e) { + Quickshell.execDetached(["notify-send", Translation.tr("Couldn't recognize music"), Translation.tr("Perhaps what you're listening to is too niche"), "-a", "Shell"]) + } + } + + Process { + id: recognizeMusicProc + running: false + command: [`${Directories.scriptPath}/musicRecognition/recognize-music.sh`, "-i", root.timeoutInterval, "-t", root.timeoutDuration, "-s", root.monitorSourceString] + stdout: StdioCollector { + onStreamFinished: { + if (root.manuallyStopped) { + root.manuallyStopped = false + return + } + handleRecognition(this.text) + } + } + onExited: (exitCode, exitStatus) => { + if (exitCode === 1) { + Quickshell.execDetached(["notify-send", Translation.tr("Couldn't recognize music"), Translation.tr("Make sure you have songrec installed"), "-a", "Shell"]) + } + } + } + + Process { + id: musicReconizedProc + running: false + command: [ + "notify-send", + Translation.tr("Music Recognized"), + root.recognizedTrack.title + " - " + root.recognizedTrack.subtitle, + "-A", "Shazam", + "-A", "YouTube", + "-a", "Shell" + ] + stdout: StdioCollector { + onStreamFinished: { + if (this.text === "") return + if (this.text == 0) { + Qt.openUrlExternally(root.recognizedTrack.url); + } else { + Qt.openUrlExternally("https://www.youtube.com/results?search_query=" + root.recognizedTrack.title + " - " + root.recognizedTrack.subtitle); + } + } + } + } +} \ No newline at end of file