initial commit of musicRecognition

This commit is contained in:
vaguesyntax
2025-10-26 02:46:39 +03:00
parent 5dd0fe2761
commit 60f055f07d
8 changed files with 150 additions and 2 deletions
@@ -374,6 +374,7 @@ Singleton {
property JsonObject resources: JsonObject {
property int updateInterval: 3000
property int musicRecognitionTimeout: 16
}
property JsonObject search: JsonObject {
@@ -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();
@@ -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 {
@@ -29,7 +29,7 @@ AbstractQuickPanel {
readonly property real baseCellHeight: 56
// Toggles
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"]
readonly property list<string> 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<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
readonly property list<var> toggleRows: toggleRowsForList(toggles)
@@ -10,7 +10,7 @@ AndroidQuickToggleButton {
name: Translation.tr("EasyEffects")
toggled: EasyEffects.active
buttonIcon: "graphic_eq"
buttonIcon: "instant_mix"
Component.onCompleted: {
EasyEffects.fetchActiveState()
@@ -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 thats playing right now")
text: "Identifies the song thats playing right now"
}
}
@@ -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
} }
}
@@ -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