优化交互, 增加读取歌曲内置封面的功能
parent
cd87a44513
commit
4e85a32b15
29
main.py
29
main.py
|
@ -4,7 +4,6 @@ import gi, sys, os, threading, time
|
|||
# import dbus
|
||||
# import dbus.service, dbus.mainloop.glib
|
||||
from pprint import pprint as print
|
||||
import musicbrainzngs as mus
|
||||
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
@ -13,7 +12,6 @@ from gi.repository import Gtk, Gdk, GLib, GdkPixbuf, GObject
|
|||
|
||||
from window import SonistWindow
|
||||
|
||||
# from mpd.asyncio import MPDClient
|
||||
from mpd.base import MPDClient
|
||||
|
||||
app_id = 'fun.wkit.sonist'
|
||||
|
@ -45,27 +43,11 @@ class Application(Gtk.Application):
|
|||
|
||||
self.mpd = MPDClient()
|
||||
|
||||
self.mpd_is_online = self.mpd_connect()
|
||||
|
||||
self.connect('window-removed', self.on_window_removed)
|
||||
|
||||
mus.set_useragent('Sonist Gtk', '0.0.1', 'https://github.com/app-cat/sonist-gtk')
|
||||
|
||||
|
||||
|
||||
@run_async
|
||||
def get_cover(self, song, filepath, callback):
|
||||
try:
|
||||
data = mus.search_releases(song["artist"], song["title"], 1)
|
||||
release_id = data["release-list"][0]["release-group"]["id"]
|
||||
print(release_id)
|
||||
buff = mus.get_release_group_image_front(release_id, size = 128)
|
||||
with open(filepath, 'wb') as file:
|
||||
output = file.write(buff)
|
||||
callback(filepath)
|
||||
except:
|
||||
pass
|
||||
|
||||
@run_async
|
||||
def ping(self):
|
||||
if self.mpd_is_online:
|
||||
|
@ -87,14 +69,11 @@ class Application(Gtk.Application):
|
|||
self.emit('state_changed', state)
|
||||
|
||||
self.mpd_state = state
|
||||
time.sleep(1)
|
||||
time.sleep(0.5)
|
||||
|
||||
except Exception as e:
|
||||
print('<><><><><><><><>')
|
||||
print(e)
|
||||
print('<><><><><><><><>')
|
||||
self.mpd.kill()
|
||||
time.sleep(1)
|
||||
time.sleep(2)
|
||||
self.mpd_is_online = self.mpd_connect()
|
||||
|
||||
|
||||
|
@ -111,10 +90,12 @@ class Application(Gtk.Application):
|
|||
self.set_app_menu(None)
|
||||
self.set_menubar(None)
|
||||
|
||||
self.mpd_is_online = self.mpd_connect()
|
||||
|
||||
self.window = SonistWindow(self)
|
||||
self.add_window(self.window)
|
||||
|
||||
self.window.show_all()
|
||||
|
||||
self.ping()
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,10 @@ class ScaleImage(Gtk.Image):
|
|||
|
||||
|
||||
def reset(self, filepath):
|
||||
if type(filepath) == str:
|
||||
self.origin = GdkPixbuf.Pixbuf.new_from_file(filepath)
|
||||
else:
|
||||
self.origin = filepath
|
||||
|
||||
if self.width is None:
|
||||
self.pixbuf = self.origin
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
from .slider import Slider
|
||||
|
||||
def time_to_str(stamp = 0):
|
||||
m = stamp // 60
|
||||
s = stamp % 60
|
||||
if m < 10:
|
||||
m = f"0{m}"
|
||||
if s < 10:
|
||||
s = f"0{s}"
|
||||
return f"{m}:{s}"
|
||||
|
||||
|
||||
class Timebar(Gtk.Fixed):
|
||||
|
||||
__gsignals__ = {
|
||||
'seeked': (GObject.SignalFlags.RUN_FIRST, None, (float,))
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
Gtk.Fixed.__init__(self)
|
||||
|
||||
self._duration = 0
|
||||
self._time = 0
|
||||
|
||||
self.slider = Slider(272)
|
||||
self.curr = Gtk.Label()
|
||||
self.duration = Gtk.Label()
|
||||
|
||||
self.duration.set_justify(Gtk.Justification.RIGHT)
|
||||
self.duration.set_xalign(1)
|
||||
|
||||
self.slider.connect('change-value', self.on_timeupdate)
|
||||
|
||||
self.put(self.curr, 3, 0)
|
||||
self.put(self.duration, 233, 0)
|
||||
self.put(self.slider, 0, 12)
|
||||
|
||||
|
||||
def update_time(self, curr = 0, duration = 0):
|
||||
self._duration = duration
|
||||
self._time = curr
|
||||
progress = curr * 100 / duration
|
||||
self.curr.set_text(time_to_str(curr))
|
||||
self.duration.set_text(time_to_str(duration))
|
||||
self.slider.set_value(progress)
|
||||
|
||||
|
||||
def on_timeupdate(self, slider, a, b):
|
||||
# print(slider.get_value(), a, b)
|
||||
p = slider.get_value()
|
||||
time = p * self._duration / 100
|
||||
self.emit('seeked', time)
|
17
utils.py
17
utils.py
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
import gi, io, base64
|
||||
from gi.repository import Gdk, GLib, GdkPixbuf
|
||||
from gi.repository import Gdk, GLib, GdkPixbuf, Gio
|
||||
from PIL import Image, ImageFilter
|
||||
|
||||
def pixbuf_to_pil(pixbuf):
|
||||
|
@ -26,14 +26,15 @@ def pil_to_pixbuf(img):
|
|||
return GdkPixbuf.Pixbuf.new_from_bytes(GLib.Bytes.new(data), GdkPixbuf.Colorspace.RGB, alpha, 8, w, h, rowstride)
|
||||
|
||||
|
||||
def pic_to_pixbuf(pic):
|
||||
data = pic.data
|
||||
input_stream = Gio.MemoryInputStream.new_from_data(data, None)
|
||||
return GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
|
||||
|
||||
def base64_to_pixbuf(base64_str):
|
||||
# 解码base64
|
||||
decoded = base64.b64decode(base64_str)
|
||||
# 创建输入流
|
||||
input_stream = io.BytesIO(decoded)
|
||||
# 从输入流创建pixbuf
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
|
||||
return pixbuf
|
||||
data = base64.b64decode(base64_str)
|
||||
input_stream = Gio.MemoryInputStream.new_from_data(data, None)
|
||||
return GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
|
||||
|
||||
def blur_image(pixbuf):
|
||||
# 加载图片并确认该图片为RGBA模式,保证透明度
|
||||
|
|
84
window.py
84
window.py
|
@ -2,17 +2,20 @@
|
|||
|
||||
import gi, sys, os, mutagen
|
||||
from pprint import pprint as print
|
||||
from mutagen.flac import FLAC
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
||||
|
||||
from utils import blur_image, base64_to_pixbuf
|
||||
from utils import blur_image, pic_to_pixbuf, base64_to_pixbuf
|
||||
|
||||
from ui.image import ScaleImage
|
||||
from ui.slider import Slider
|
||||
from ui.image_button import ImageButton
|
||||
from ui.text import TextBox
|
||||
from ui.ctrl_box import CtrlBox
|
||||
from ui.timebar import Timebar
|
||||
|
||||
|
||||
|
||||
|
@ -28,6 +31,8 @@ class SonistWindow(Gtk.Window):
|
|||
|
||||
self.app.connect('state_changed', lambda a, x: self.update_play_stat(x == 'play'))
|
||||
self.app.connect('song_changed', lambda a, id: self.sync_state(False))
|
||||
self.app.connect('playing', lambda a, id: self.update_playtime())
|
||||
|
||||
|
||||
self.set_name('SonistWindow')
|
||||
self.set_default_size(320, 384)
|
||||
|
@ -75,12 +80,13 @@ class SonistWindow(Gtk.Window):
|
|||
self.title_box = Gtk.Label()
|
||||
self.title_box.set_text('mpd loading...')
|
||||
|
||||
layout.put(self.title_box, 32, 244)
|
||||
layout.put(self.title_box, 27, 244)
|
||||
|
||||
|
||||
# 播放进度
|
||||
slider = Slider(272)
|
||||
layout.put(slider, 24, 270)
|
||||
self.timebar = Timebar()
|
||||
self.timebar.connect('seeked', lambda a,v: self.app.mpd.seekcur(v))
|
||||
layout.put(self.timebar, 24, 270)
|
||||
|
||||
|
||||
# 控制条
|
||||
|
@ -89,13 +95,22 @@ class SonistWindow(Gtk.Window):
|
|||
|
||||
self.ctrl_box.disabled = not self.app.mpd_is_online
|
||||
|
||||
layout.put(self.ctrl_box, 48, 300)
|
||||
layout.put(self.ctrl_box, 48, 312)
|
||||
|
||||
self.add(layout)
|
||||
|
||||
self.sync_state(True)
|
||||
|
||||
|
||||
def get_mpd_stat(self):
|
||||
try:
|
||||
self.stat = self.app.mpd.status()
|
||||
except:
|
||||
self.app.ping()
|
||||
self.stat = self.app.mpd.status()
|
||||
|
||||
return self.stat
|
||||
|
||||
|
||||
def set_background_image(self, filepath):
|
||||
if type(filepath) == str:
|
||||
|
@ -155,22 +170,37 @@ class SonistWindow(Gtk.Window):
|
|||
|
||||
|
||||
def toggle_play(self):
|
||||
try:
|
||||
if self.stat.get('state') == 'stop':
|
||||
self.app.mpd.play()
|
||||
else:
|
||||
self.app.mpd.pause()
|
||||
except:
|
||||
self.app.ping()
|
||||
self.toggle_play()
|
||||
return
|
||||
|
||||
self.sync_state()
|
||||
# self.sync_state()
|
||||
self.update_play_stat(self.stat.get('state') == 'play')
|
||||
|
||||
|
||||
def prev_song(self):
|
||||
try:
|
||||
self.app.mpd.previous()
|
||||
self.sync_state()
|
||||
except:
|
||||
self.app.ping()
|
||||
self.prev_song()
|
||||
return
|
||||
# self.sync_state()
|
||||
|
||||
def next_song(self):
|
||||
try:
|
||||
self.app.mpd.next()
|
||||
self.sync_state()
|
||||
except:
|
||||
self.app.ping()
|
||||
self.next_song()
|
||||
return
|
||||
# self.sync_state()
|
||||
|
||||
|
||||
def update_play_stat(self, played = True):
|
||||
|
@ -186,11 +216,16 @@ class SonistWindow(Gtk.Window):
|
|||
self.ctrl_box.toggle_play_btn(played)
|
||||
|
||||
|
||||
def update_playtime(self):
|
||||
stat = self.get_mpd_stat()
|
||||
times = stat['time'].split(':')
|
||||
self.timebar.update_time(int(times[0]), int(times[1]))
|
||||
|
||||
def sync_state(self, first = False):
|
||||
if not self.app.mpd_is_online:
|
||||
return
|
||||
|
||||
self.stat = self.app.mpd.status()
|
||||
self.stat = self.get_mpd_stat()
|
||||
|
||||
played = self.stat.get('state')
|
||||
song = self.app.mpd.currentsong()
|
||||
|
@ -203,30 +238,33 @@ class SonistWindow(Gtk.Window):
|
|||
elif self.stat.get('random') == '1':
|
||||
self.ctrl_box.toggle_mode_btn(mode = 'random')
|
||||
|
||||
|
||||
# 更新歌曲信息
|
||||
self.title_box.set_text("%s - %s" % (song.get('artist'), song.get('title')))
|
||||
|
||||
filepath = f"./album/{song['title']}.png"
|
||||
self.update_playtime()
|
||||
|
||||
# print(self.stat)
|
||||
# print(song)
|
||||
filepath = f"./album/{song['title']}.png"
|
||||
songpath = f"/data/music/{song['file']}"
|
||||
|
||||
|
||||
if os.path.isfile(filepath):
|
||||
self.update_album(filepath)
|
||||
else:
|
||||
|
||||
id3 = mutagen.File(songpath)
|
||||
|
||||
try:
|
||||
if id3.tags.get('APIC:'):
|
||||
pic = id3.tags['APIC:']
|
||||
elif len(id3.pictures) > 0:
|
||||
pic = id3.pictures[0]
|
||||
|
||||
if pic is not None:
|
||||
album = pic_to_pixbuf(pic)
|
||||
self.update_album(album)
|
||||
except:
|
||||
pass
|
||||
# audio = mutagen.File()
|
||||
# buff = self.app.get_cover(song, filepath, self.update_album)
|
||||
# print(buff)
|
||||
# with open(filepath, 'wb') as file:
|
||||
# output = file.write(buff)
|
||||
|
||||
# self.set_background_image(filepath)
|
||||
|
||||
|
||||
|
||||
# print(self.app.mpd.readpicture(self.stat.get('songid')))
|
||||
|
||||
|
||||
def update_album(self, filepath):
|
||||
|
|
Loading…
Reference in New Issue