Anonymous
Can при изменении позиции песни QDBUS6/Bluez не обновляет метаданные позиции
Сообщение
Anonymous » 03 июн 2025, 03:13
При прохождении назад или изменении позиции воспроизведения песни Qdbus6 или Bluez не освежает < /p>
, если я перемотаю или сканирую в любом направлении, я не получаю надлежащего чтения в значении «позиция», вместо этого она продолжает подсчитать в миллисекундах, пока она не заканчивается на исходном устройстве < /p>
Код: Выделить всё
Browsable: true
Device:
Name: Music
Playlist:
Position: 45044
Repeat: off
Searchable: true
Shuffle: alltracks
Status: playing
Subtype: None
Track: Album: Devil (feat. Bubi) - Single
Artist: Ironmouse, shirobeats & HalaCG
Duration: 189677
Genre: Pop
Item:
NumberOfTracks: 550
Title: Devil (feat. Bubi)
TrackNumber: 100
Type: Audio
< /code>
Я пробовал вручную выпустить различные команды управления, чтобы увидеть, есть ли что -то, что может сделать это, но у меня нет команды «Обновление», поэтому у меня возникают проблемы с выяснением того, что происходит.Browsable: true
Device:
Name: Music
Playlist:
Position: 89961
Repeat: off
Searchable: true
Shuffle: alltracks
Status: playing
Subtype: None
Track: Album: New Classics (2010-2016)
Artist: dj-Nate
Duration: 88163
Genre: Dance
Item:
NumberOfTracks: 550
Title: Dirty Thumpin' Saws
TrackNumber: 107
Type: Audio
< /code>
Код для всего приложения, которое у меня есть, здесь < /p>
import curses
import subprocess
import argparse
import os
import time
def run_qdbus6(args):
try:
return subprocess.check_output(["qdbus6", "--system"] + args, text=True)
except Exception:
return ""
def parse_status(output: str) -> dict:
info = {}
for line in output.splitlines():
if ':' in line:
k, v = line.split(':', 1)
info[k.strip()] = v.strip()
return info
def ms_to_mins_secs(ms):
seconds = int(ms) // 1000
mins = seconds // 60
secs = seconds % 60
return f"{mins}:{secs:02}"
def check_device_connected(mac_addr):
"""Returns True if the device is connected, False otherwise."""
dev_path = f"/org/bluez/hci0/dev_{mac_addr.replace(':', '_')}"
try:
output = subprocess.check_output(
["qdbus6", "--system", "org.bluez", dev_path, "org.freedesktop.DBus.Properties.Get", "org.bluez.Device1", "Connected"],
text=True,
stderr=subprocess.DEVNULL
)
return "true" in output.lower()
except Exception:
return False
def figlet_centered(stdscr, text, color_pair=0):
max_y, max_x = stdscr.getmaxyx()
try:
figlet_out = subprocess.check_output(
["figlet", "-f", "slant", "-l", "-w", str(max_x), text],
text=True,
stderr=subprocess.DEVNULL
)
except Exception:
figlet_out = text
lines = figlet_out.splitlines()
y0 = max((max_y - len(lines)) // 2, 0)
for i, line in enumerate(lines):
x0 = max((max_x - len(line)) // 2, 0)
try:
stdscr.addstr(y0 + i, x0, line, curses.color_pair(color_pair))
except curses.error:
pass
def draw_ui(stdscr, info, highlight_idx=None, button_boxes=None, color_pair=0):
stdscr.clear()
max_y, max_x = stdscr.getmaxyx()
title = info.get("Title", "Unknown Title")
artist = info.get("Artist", "Unknown Artist")
album = info.get("Track: Album", info.get("Album", "Unknown Album"))
status = info.get("Status", "paused").capitalize()
position = int(info.get("Position", "0"))
duration = int(info.get("Duration", "1"))
elapsed = ms_to_mins_secs(position)
remaining = ms_to_mins_secs(max(duration - position, 0))
prog_pos = position // 1000
prog_dur = max(duration // 1000, 1)
stdscr.addstr(0, 0, "=" * max_x, curses.color_pair(color_pair))
stdscr.addstr(1, 0, title.center(max_x), curses.color_pair(color_pair))
stdscr.addstr(2, 0, artist.center(max_x), curses.color_pair(color_pair))
stdscr.addstr(3, 0, album.center(max_x), curses.color_pair(color_pair))
stdscr.addstr(4, 0, "=" * max_x, curses.color_pair(color_pair))
stdscr.addstr(5, 0, f"Status: {status}", curses.color_pair(color_pair))
bar_length = max(max_x - 23, 10)
filled_length = int(bar_length * prog_pos // prog_dur) if prog_dur else 0
bar = "[" + "=" * filled_length + ">" + " " * (bar_length - filled_length - 1) + "]"
stdscr.addstr(7, 0, f"{elapsed.rjust(5)} {bar} -{remaining}", curses.color_pair(color_pair))
labels = ["⏮", "⏯" if status.lower() != "playing" else "⏸", "⏭"]
button_boxes.clear()
btn_y = 9
btn_h = 5
btn_w = 13
gap = 6
total_btns = 3 * btn_w + 2 * gap
start_x = (max_x - total_btns) // 2
for i, label in enumerate(labels):
x = start_x + i * (btn_w + gap)
y = btn_y
box = (y, x, y + btn_h, x + btn_w)
button_boxes.append(box)
for bx in range(x, x+btn_w):
stdscr.addch(y, bx, curses.ACS_HLINE, curses.color_pair(color_pair))
stdscr.addch(y+btn_h-1, bx, curses.ACS_HLINE, curses.color_pair(color_pair))
for by in range(y, y+btn_h):
stdscr.addch(by, x, curses.ACS_VLINE, curses.color_pair(color_pair))
stdscr.addch(by, x+btn_w-1, curses.ACS_VLINE, curses.color_pair(color_pair))
stdscr.addch(y, x, curses.ACS_ULCORNER, curses.color_pair(color_pair))
stdscr.addch(y, x+btn_w-1, curses.ACS_URCORNER, curses.color_pair(color_pair))
stdscr.addch(y+btn_h-1, x, curses.ACS_LLCORNER, curses.color_pair(color_pair))
stdscr.addch(y+btn_h-1, x+btn_w-1, curses.ACS_LRCORNER, curses.color_pair(color_pair))
for by in range(y+1, y+btn_h-1):
stdscr.addstr(by, x+1, " " * (btn_w-2), curses.color_pair(color_pair) | (curses.A_REVERSE if highlight_idx == i else curses.A_NORMAL))
stdscr.addstr(y + btn_h//2, x + (btn_w-len(label))//2, label, curses.A_BOLD | curses.color_pair(color_pair) | (curses.A_REVERSE if highlight_idx == i else 0))
stdscr.addstr(btn_y+btn_h+1, 0, "Controls: [p] Play/Pause [n] Next [b] Previous [q] Quit".center(max_x), curses.color_pair(color_pair))
stdscr.addstr(btn_y+btn_h+2, 0, "=" * max_x, curses.color_pair(color_pair))
stdscr.refresh()
def which_button(mx, my, button_boxes):
for idx, (y1, x1, y2, x2) in enumerate(button_boxes):
if y1 autorefresh_interval:
info = refresh_info()
draw_ui(stdscr, info, None, button_boxes, color_pair=color_pair)
last_refresh = now
if key == ord('q'):
break
elif key in [ord('p'), ord(' ')]:
status = info.get("Status", "paused").lower()
if status == "playing":
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Pause"
])
else:
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Play"
])
info = refresh_info()
draw_ui(stdscr, info, 1, button_boxes, color_pair=color_pair)
last_refresh = time.time()
elif key in [ord('n'), curses.KEY_RIGHT]:
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Next"
])
info = refresh_info()
draw_ui(stdscr, info, 2, button_boxes, color_pair=color_pair)
last_refresh = time.time()
elif key in [ord('b'), curses.KEY_LEFT]:
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Previous"
])
info = refresh_info()
draw_ui(stdscr, info, 0, button_boxes, color_pair=color_pair)
last_refresh = time.time()
elif key == curses.KEY_MOUSE:
_, mx, my, _, bstate = curses.getmouse()
btn_idx = which_button(mx, my, button_boxes)
if btn_idx == 0: # prev
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Previous"
])
elif btn_idx == 1: # play/pause
status = info.get("Status", "paused").lower()
if status == "playing":
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Pause"
])
else:
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Play"
])
elif btn_idx == 2: # next
run_qdbus6([
"org.bluez",
PLAYER_PATH,
"org.bluez.MediaPlayer1.Next"
])
info = refresh_info()
draw_ui(stdscr, info, btn_idx, button_boxes, color_pair=color_pair)
last_refresh = time.time()
elif key == -1:
pass
except KeyboardInterrupt:
break
def parse_args():
parser = argparse.ArgumentParser(description="Bluetooth Music Player UI with Mouse and Keyboard Control")
parser.add_argument('-D', '--device', type=str, required=True, help="Bluetooth MAC address of the device")
parser.add_argument('--color', type=str, default="white", help="Color theme (default: white)")
parser.add_argument('--autorefresh', type=float, default=1.0, help="Autorefresh interval in seconds (default: 1.0)")
return parser.parse_args()
def color_theme(theme):
colors = dict(
black=curses.COLOR_BLACK, red=curses.COLOR_RED, green=curses.COLOR_GREEN, yellow=curses.COLOR_YELLOW,
blue=curses.COLOR_BLUE, magenta=curses.COLOR_MAGENTA, cyan=curses.COLOR_CYAN, white=curses.COLOR_WHITE
)
base = colors.get(theme.lower(), curses.COLOR_WHITE)
curses.init_pair(1, base, curses.COLOR_BLACK)
return 1
def run():
args = parse_args()
mac_addr = args.device
def wrapped(stdscr):
curses.start_color()
color_pair = color_theme(args.color)
main(stdscr, mac_addr, color_pair, autorefresh_interval=args.autorefresh)
curses.wrapper(wrapped)
if __name__ == "__main__":
run()
Я заметил, что здесь были разные вопросы по этому вопросу, но я не видел ответа ни на одну из них.
Подробнее здесь:
https://stackoverflow.com/questions/796 ... n-metadata
1748909581
Anonymous
При прохождении назад или изменении позиции воспроизведения песни Qdbus6 или Bluez не освежает < /p> , если я перемотаю или сканирую в любом направлении, я не получаю надлежащего чтения в значении «позиция», вместо этого она продолжает подсчитать в миллисекундах, пока она не заканчивается на исходном устройстве < /p> [code]Browsable: true Device: Name: Music Playlist: Position: 45044 Repeat: off Searchable: true Shuffle: alltracks Status: playing Subtype: None Track: Album: Devil (feat. Bubi) - Single Artist: Ironmouse, shirobeats & HalaCG Duration: 189677 Genre: Pop Item: NumberOfTracks: 550 Title: Devil (feat. Bubi) TrackNumber: 100 Type: Audio < /code> Я пробовал вручную выпустить различные команды управления, чтобы увидеть, есть ли что -то, что может сделать это, но у меня нет команды «Обновление», поэтому у меня возникают проблемы с выяснением того, что происходит.Browsable: true Device: Name: Music Playlist: Position: 89961 Repeat: off Searchable: true Shuffle: alltracks Status: playing Subtype: None Track: Album: New Classics (2010-2016) Artist: dj-Nate Duration: 88163 Genre: Dance Item: NumberOfTracks: 550 Title: Dirty Thumpin' Saws TrackNumber: 107 Type: Audio < /code> Код для всего приложения, которое у меня есть, здесь < /p> import curses import subprocess import argparse import os import time def run_qdbus6(args): try: return subprocess.check_output(["qdbus6", "--system"] + args, text=True) except Exception: return "" def parse_status(output: str) -> dict: info = {} for line in output.splitlines(): if ':' in line: k, v = line.split(':', 1) info[k.strip()] = v.strip() return info def ms_to_mins_secs(ms): seconds = int(ms) // 1000 mins = seconds // 60 secs = seconds % 60 return f"{mins}:{secs:02}" def check_device_connected(mac_addr): """Returns True if the device is connected, False otherwise.""" dev_path = f"/org/bluez/hci0/dev_{mac_addr.replace(':', '_')}" try: output = subprocess.check_output( ["qdbus6", "--system", "org.bluez", dev_path, "org.freedesktop.DBus.Properties.Get", "org.bluez.Device1", "Connected"], text=True, stderr=subprocess.DEVNULL ) return "true" in output.lower() except Exception: return False def figlet_centered(stdscr, text, color_pair=0): max_y, max_x = stdscr.getmaxyx() try: figlet_out = subprocess.check_output( ["figlet", "-f", "slant", "-l", "-w", str(max_x), text], text=True, stderr=subprocess.DEVNULL ) except Exception: figlet_out = text lines = figlet_out.splitlines() y0 = max((max_y - len(lines)) // 2, 0) for i, line in enumerate(lines): x0 = max((max_x - len(line)) // 2, 0) try: stdscr.addstr(y0 + i, x0, line, curses.color_pair(color_pair)) except curses.error: pass def draw_ui(stdscr, info, highlight_idx=None, button_boxes=None, color_pair=0): stdscr.clear() max_y, max_x = stdscr.getmaxyx() title = info.get("Title", "Unknown Title") artist = info.get("Artist", "Unknown Artist") album = info.get("Track: Album", info.get("Album", "Unknown Album")) status = info.get("Status", "paused").capitalize() position = int(info.get("Position", "0")) duration = int(info.get("Duration", "1")) elapsed = ms_to_mins_secs(position) remaining = ms_to_mins_secs(max(duration - position, 0)) prog_pos = position // 1000 prog_dur = max(duration // 1000, 1) stdscr.addstr(0, 0, "=" * max_x, curses.color_pair(color_pair)) stdscr.addstr(1, 0, title.center(max_x), curses.color_pair(color_pair)) stdscr.addstr(2, 0, artist.center(max_x), curses.color_pair(color_pair)) stdscr.addstr(3, 0, album.center(max_x), curses.color_pair(color_pair)) stdscr.addstr(4, 0, "=" * max_x, curses.color_pair(color_pair)) stdscr.addstr(5, 0, f"Status: {status}", curses.color_pair(color_pair)) bar_length = max(max_x - 23, 10) filled_length = int(bar_length * prog_pos // prog_dur) if prog_dur else 0 bar = "[" + "=" * filled_length + ">" + " " * (bar_length - filled_length - 1) + "]" stdscr.addstr(7, 0, f"{elapsed.rjust(5)} {bar} -{remaining}", curses.color_pair(color_pair)) labels = ["⏮", "⏯" if status.lower() != "playing" else "⏸", "⏭"] button_boxes.clear() btn_y = 9 btn_h = 5 btn_w = 13 gap = 6 total_btns = 3 * btn_w + 2 * gap start_x = (max_x - total_btns) // 2 for i, label in enumerate(labels): x = start_x + i * (btn_w + gap) y = btn_y box = (y, x, y + btn_h, x + btn_w) button_boxes.append(box) for bx in range(x, x+btn_w): stdscr.addch(y, bx, curses.ACS_HLINE, curses.color_pair(color_pair)) stdscr.addch(y+btn_h-1, bx, curses.ACS_HLINE, curses.color_pair(color_pair)) for by in range(y, y+btn_h): stdscr.addch(by, x, curses.ACS_VLINE, curses.color_pair(color_pair)) stdscr.addch(by, x+btn_w-1, curses.ACS_VLINE, curses.color_pair(color_pair)) stdscr.addch(y, x, curses.ACS_ULCORNER, curses.color_pair(color_pair)) stdscr.addch(y, x+btn_w-1, curses.ACS_URCORNER, curses.color_pair(color_pair)) stdscr.addch(y+btn_h-1, x, curses.ACS_LLCORNER, curses.color_pair(color_pair)) stdscr.addch(y+btn_h-1, x+btn_w-1, curses.ACS_LRCORNER, curses.color_pair(color_pair)) for by in range(y+1, y+btn_h-1): stdscr.addstr(by, x+1, " " * (btn_w-2), curses.color_pair(color_pair) | (curses.A_REVERSE if highlight_idx == i else curses.A_NORMAL)) stdscr.addstr(y + btn_h//2, x + (btn_w-len(label))//2, label, curses.A_BOLD | curses.color_pair(color_pair) | (curses.A_REVERSE if highlight_idx == i else 0)) stdscr.addstr(btn_y+btn_h+1, 0, "Controls: [p] Play/Pause [n] Next [b] Previous [q] Quit".center(max_x), curses.color_pair(color_pair)) stdscr.addstr(btn_y+btn_h+2, 0, "=" * max_x, curses.color_pair(color_pair)) stdscr.refresh() def which_button(mx, my, button_boxes): for idx, (y1, x1, y2, x2) in enumerate(button_boxes): if y1 autorefresh_interval: info = refresh_info() draw_ui(stdscr, info, None, button_boxes, color_pair=color_pair) last_refresh = now if key == ord('q'): break elif key in [ord('p'), ord(' ')]: status = info.get("Status", "paused").lower() if status == "playing": run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Pause" ]) else: run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Play" ]) info = refresh_info() draw_ui(stdscr, info, 1, button_boxes, color_pair=color_pair) last_refresh = time.time() elif key in [ord('n'), curses.KEY_RIGHT]: run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Next" ]) info = refresh_info() draw_ui(stdscr, info, 2, button_boxes, color_pair=color_pair) last_refresh = time.time() elif key in [ord('b'), curses.KEY_LEFT]: run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Previous" ]) info = refresh_info() draw_ui(stdscr, info, 0, button_boxes, color_pair=color_pair) last_refresh = time.time() elif key == curses.KEY_MOUSE: _, mx, my, _, bstate = curses.getmouse() btn_idx = which_button(mx, my, button_boxes) if btn_idx == 0: # prev run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Previous" ]) elif btn_idx == 1: # play/pause status = info.get("Status", "paused").lower() if status == "playing": run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Pause" ]) else: run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Play" ]) elif btn_idx == 2: # next run_qdbus6([ "org.bluez", PLAYER_PATH, "org.bluez.MediaPlayer1.Next" ]) info = refresh_info() draw_ui(stdscr, info, btn_idx, button_boxes, color_pair=color_pair) last_refresh = time.time() elif key == -1: pass except KeyboardInterrupt: break def parse_args(): parser = argparse.ArgumentParser(description="Bluetooth Music Player UI with Mouse and Keyboard Control") parser.add_argument('-D', '--device', type=str, required=True, help="Bluetooth MAC address of the device") parser.add_argument('--color', type=str, default="white", help="Color theme (default: white)") parser.add_argument('--autorefresh', type=float, default=1.0, help="Autorefresh interval in seconds (default: 1.0)") return parser.parse_args() def color_theme(theme): colors = dict( black=curses.COLOR_BLACK, red=curses.COLOR_RED, green=curses.COLOR_GREEN, yellow=curses.COLOR_YELLOW, blue=curses.COLOR_BLUE, magenta=curses.COLOR_MAGENTA, cyan=curses.COLOR_CYAN, white=curses.COLOR_WHITE ) base = colors.get(theme.lower(), curses.COLOR_WHITE) curses.init_pair(1, base, curses.COLOR_BLACK) return 1 def run(): args = parse_args() mac_addr = args.device def wrapped(stdscr): curses.start_color() color_pair = color_theme(args.color) main(stdscr, mac_addr, color_pair, autorefresh_interval=args.autorefresh) curses.wrapper(wrapped) if __name__ == "__main__": run() [/code] Я заметил, что здесь были разные вопросы по этому вопросу, но я не видел ответа ни на одну из них. Подробнее здесь: [url]https://stackoverflow.com/questions/79650470/can-when-changing-song-position-qdbus6-bluez-does-not-update-position-metadata[/url]