From 60f055f07d9793314091bb56fbe94187d96bc884 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sun, 26 Oct 2025 02:46:39 +0300 Subject: [PATCH] initial commit of musicRecognition --- .../quickshell/ii/modules/common/Config.qml | 1 + .../common/widgets/notification_utils.js | 3 + .../ii/modules/settings/ServicesConfig.qml | 11 +++ .../quickToggles/AndroidQuickPanel.qml | 2 +- .../androidStyle/AndroidEasyEffectsToggle.qml | 2 +- .../androidStyle/AndroidMusicRecognition.qml | 77 +++++++++++++++++++ .../AndroidToggleDelegateChooser.qml | 13 ++++ .../musicRecognition/musicRecognition.sh | 43 +++++++++++ 8 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml create mode 100755 dots/.config/quickshell/ii/scripts/musicRecognition/musicRecognition.sh diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index f496f7bab..83fda7cee 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -374,6 +374,7 @@ Singleton { property JsonObject resources: JsonObject { property int updateInterval: 3000 + property int musicRecognitionTimeout: 16 } property JsonObject search: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js b/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js index 7ab21c3bb..fb32a5d51 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js +++ b/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js @@ -17,13 +17,16 @@ function findSuitableMaterialSymbol(summary = "") { 'time': 'scheduleb', 'installed': 'download', 'configuration reloaded': 'reset_wrench', + 'unable': 'indeterminate_question_box', 'config': 'reset_wrench', 'update': 'update', 'ai response': 'neurology', 'control': 'settings', 'upsca': 'compare', + 'music': 'music_note', 'install': 'deployed_code_update', 'startswith:file': 'folder_copy', // Declarative startsWith check + }; const lowerSummary = summary.toLowerCase(); diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index fae5bf4de..64c015ff2 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -54,6 +54,17 @@ ContentPage { Config.options.resources.updateInterval = value; } } + ConfigSpinBox { + icon: "timer_off" + text: Translation.tr("Music recognition timeout (s)") + value: Config.options.resources.musicRecognitionTimeout + from: 2 + to: 100 + stepSize: 2 + onValueChanged: { + Config.options.resources.musicRecognitionTimeout = value; + } + } } ContentSection { diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml index a7d743585..5d450adec 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml @@ -29,7 +29,7 @@ AbstractQuickPanel { readonly property real baseCellHeight: 56 // Toggles - readonly property list availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"] + readonly property list availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile","musicrecognition"] readonly property int columns: Config.options.sidebar.quickToggles.android.columns readonly property list toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : [] readonly property list toggleRows: toggleRowsForList(toggles) diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml index c907abf77..86605becd 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml @@ -10,7 +10,7 @@ AndroidQuickToggleButton { name: Translation.tr("EasyEffects") toggled: EasyEffects.active - buttonIcon: "graphic_eq" + buttonIcon: "instant_mix" Component.onCompleted: { EasyEffects.fetchActiveState() diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml new file mode 100644 index 000000000..30487453d --- /dev/null +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml @@ -0,0 +1,77 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import Quickshell +import Quickshell.Io +import qs.services + + +AndroidQuickToggleButton { + id: root + + property int timeoutInterval: 5 + property int timeoutDuration: Config.options.resources.musicRecognitionTimeout + property string resultsJSON + + property string recognizedTrackTitle + property string recognizedTrackSubtitle + property string recognizedTrackURL + + name: Translation.tr("Identify Music") + statusText: toggled ? Translation.tr("Listening...") : Translation.tr("Inactive") + toggled: false + buttonIcon: toggled ? "cadence" : "graphic_eq" + onClicked: { + if (!toggled){ + recognizeMusicProc.running = true + } else { + recognizeMusicProc.running = false + } + + root.toggled = !root.toggled + } + + Process { + id: recognizeMusicProc + running: false + command: [`${Directories.scriptPath}/musicRecognition/musicRecognition.sh`, "-i", root.timeoutInterval, "-t", root.timeoutDuration] + stdout: StdioCollector { + onStreamFinished: { + root.resultsJSON = this.text + if (this.text.length < 100) { + Quickshell.execDetached(["notify-send", "No music recognized", "Please make sure your music is playing and try again", "-a", "Shell"]) + toggled = false + return + } + var obj = JSON.parse(root.resultsJSON) + root.recognizedTrackTitle = obj.track.title + root.recognizedTrackSubtitle = obj.track.subtitle + root.recognizedTrackURL = obj.track.url + musicReconizedProc.running = true + toggled = false + } + } + } + + + Process { + id: musicReconizedProc + running: false + command: [ "notify-send" , "Music Recognized" , root.recognizedTrackTitle + " by " + root.recognizedTrackSubtitle , "-A" , "Shazam Link" , "-a" , "Shell"] + stdout: StdioCollector { + onStreamFinished: { + if (this.text !== ""){ + Qt.openUrlExternally(root.recognizedTrackURL); + } + } + } + } + + + + StyledToolTip { + //text: Translation.tr("Identifies the song that’s playing right now") + text: "Identifies the song that’s playing right now" + } +} diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml index a8f79c843..b97243d6b 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml @@ -232,4 +232,17 @@ DelegateChooser { cellSize: modelData.size } } + DelegateChoice { roleValue: "musicrecognition"; AndroidMusicRecognition { + required property int index + required property var modelData + buttonIndex: root.startingIndex + index + buttonData: modelData + editMode: root.editMode + expandedSize: modelData.size > 1 + baseCellWidth: root.baseCellWidth + baseCellHeight: root.baseCellHeight + cellSpacing: root.spacing + cellSize: modelData.size + } } + } diff --git a/dots/.config/quickshell/ii/scripts/musicRecognition/musicRecognition.sh b/dots/.config/quickshell/ii/scripts/musicRecognition/musicRecognition.sh new file mode 100755 index 000000000..6bb671d5a --- /dev/null +++ b/dots/.config/quickshell/ii/scripts/musicRecognition/musicRecognition.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +MONITOR_SOURCE="alsa_output.pci-0000_00_1f.3.analog-stereo.monitor" + +# Default değerler +INTERVAL=5 +TOTAL_DURATION=30 + +# Parametreleri oku +while getopts "i:t:" opt; do + case $opt in + i) INTERVAL=$OPTARG ;; + t) TOTAL_DURATION=$OPTARG ;; + *) echo "Usage: $0 [-i interval_seconds] [-t total_duration_seconds]" + exit 1 ;; + esac +done + +START_TIME=$(date +%s) + +while true; do + CURRENT_TIME=$(date +%s) + ELAPSED=$((CURRENT_TIME - START_TIME)) + + if (( ELAPSED >= TOTAL_DURATION )); then + echo "Total duration reached. Exiting." + exit 0 + fi + + TMP_FILE=$(mktemp /tmp/recording.XXXXXX.wav) + + parec --device="$MONITOR_SOURCE" --format=s16le --rate=44100 --channels=2 \ + > >(ffmpeg -f s16le -ar 44100 -ac 2 -i - -t $INTERVAL -acodec libmp3lame "$TMP_FILE" -y -hide_banner -loglevel error) \ + 2>/dev/null + + RESULT=$(songrec audio-file-to-recognized-song "$TMP_FILE" 2>/dev/null || true) + rm -f "$TMP_FILE" + + if [ -n "$RESULT" ] && [ ${#RESULT} -gt 300 ]; then + echo "$RESULT" + exit 0 + fi +done