summarylogtreecommitdiffstats
path: root/sqlchtray.py
blob: 0365b3aa2797e9ababef3669ff87b9d23b6785a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
# sqlchtray-3.1.py — sqlchTray Reborn

import gi
import subprocess
import threading
import time
import os

gi.require_version('AppIndicator3', '0.1')
gi.require_version('Gtk', '3.0')
from gi.repository import AppIndicator3, Gtk, GLib

CONFIG_DIR = os.path.expanduser("~/.config/sqlch")
STATION_FILE = os.path.join(CONFIG_DIR, "stations")
LAST_PLAYED = os.path.join(CONFIG_DIR, "last")

sqlchCTL = "/home/prepko/.local/bin/sqlchctl"
sqlchKNOB = "/home/prepko/.local/bin/sqlchknob"


import shutil

def launch_command(cmd):
    for term in ["kitty", "alacritty", "konsole", "xfce4-terminal", "gnome-terminal", "xterm"]:
        if shutil.which(term):
            if isinstance(cmd, str):
                subprocess.Popen([
                    term, "-e", "bash", "-ic", f"{cmd}; echo; read -p 'Press enter to close...'"
                ])
            else:
                subprocess.Popen([
                    term, "-e", "bash", "-ic", f"{' '.join(cmd)}; echo; read -p 'Press enter to close...'"
                ])
            return  # Exit after launching once
    subprocess.Popen(["notify-send", "sqlch Error", "No terminal emulator found to launch command."])

def add_item(menu, label, callback):
    item = Gtk.MenuItem(label=label)
    item.connect("activate", lambda _: callback())
    menu.append(item)
    item.show()

class sqlchTray:
    def __init__(self):
        self.app_id = "sqlchtray"
        self.indicator = AppIndicator3.Indicator.new(
            self.app_id,
            "/home/prepko/.local/share/icons/squelch_icons/sqrrlch_icon.png",
            AppIndicator3.IndicatorCategory.APPLICATION_STATUS
        )
        self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
        self.menu = Gtk.Menu()
        self.indicator.set_menu(self.menu)
        self.last_display = ""

        self.build_menu()
        self.refresh_label()
        threading.Thread(target=self.poll_status, daemon=True).start()

    def build_menu(self):
        add_item(self.menu, "▶ Play", lambda: subprocess.Popen([sqlchCTL, "start"]))
        add_item(self.menu, "⏸ Pause", lambda: subprocess.Popen([sqlchCTL, "pause"]))
        add_item(self.menu, "⛔ Stop", lambda: subprocess.Popen([sqlchCTL, "stop"]))

        self.menu.append(Gtk.SeparatorMenuItem())
        self.add_volume_controls()

        header = Gtk.MenuItem(label="🎙 Stations:")
        header.set_sensitive(False)
        self.menu.append(header)
        header.show()
        self.populate_stations()

        self.menu.append(Gtk.SeparatorMenuItem())

        add_item(self.menu, "🎛 Open sqlchKnob", lambda: launch_command(["bash", sqlchKNOB]))
        add_item(self.menu, "📜 Show Logs", lambda: launch_command("journalctl --user -u sqlchtray.service -n 50"))

        quit_item = Gtk.MenuItem(label="❌ Quit")
        quit_item.connect("activate", lambda _: Gtk.main_quit())
        self.menu.append(quit_item)
        quit_item.show()

    def add_volume_controls(self):
        volume = Gtk.MenuItem(label="🔊 Volume")
        submenu = Gtk.Menu()
        for label, pct in [("Mute", 0), ("25%", 25), ("50%", 50), ("75%", 75), ("100%", 100)]:
            item = Gtk.MenuItem(label=label)
            item.connect("activate", lambda _, p=pct: self.set_volume(p))
            submenu.append(item)
            item.show()
        volume.set_submenu(submenu)
        self.menu.append(volume)
        volume.show()

    def set_volume(self, percent):
        try:
            subprocess.Popen(["pactl", "set-sink-volume", "@DEFAULT_SINK@", f"{percent}%"])
        except Exception as e:
            subprocess.Popen(["notify-send", "sqlch", f"Volume error: {e}"])

    def populate_stations(self):
        if not os.path.exists(STATION_FILE):
            return
        with open(STATION_FILE, "r") as f:
            for line in f:
                if "=" not in line:
                    continue
                name = line.split("=")[0].strip()
                item = Gtk.MenuItem(label=f"🎧 {name}")
                item.connect("activate", lambda _, n=name: self.play_station(n))
                self.menu.append(item)
                item.show()

    def play_station(self, name):
        subprocess.Popen([sqlchCTL, "play", name])

    def poll_status(self):
        while True:
            GLib.idle_add(self.refresh_label)
            time.sleep(10)

    def refresh_label(self):
        try:
            output = subprocess.check_output([sqlchCTL, "status"], stderr=subprocess.DEVNULL).decode().strip()
        except:
            output = "sqlch: Not Playing"
        if output != self.last_display:
            self.indicator.set_label(output, self.app_id)
            if "Now playing" in output or output != "sqlch: Not Playing":
                subprocess.Popen(["notify-send", "🎶 sqlch", output])
            self.last_display = output

def start_gui():
    try:
        sqlchTray()
    except Exception as e:
        subprocess.Popen(["notify-send", "sqlchTray Error", str(e)])

if __name__ == "__main__":
    GLib.idle_add(start_gui)
    Gtk.main()