Skip to content

Commit

Permalink
gui: add nowplaying overlay (#791)
Browse files Browse the repository at this point in the history
使用场景(设计目标):沉浸式听歌的页面,类似编辑器的 zen mode。
脑补一下:使用者有两块屏幕,一块屏幕是写代码的,一块屏幕放置这个播放页面。
FeelUOwn 其它页面都是内容+功能驱动,这个页面 *期望* 是审美驱动的。

- [x] 播放、上、下一首的按钮,用程序绘制
- [x] 歌曲标题和歌手不要放在一行显示
- [x] 可以在这个页面播放 mv
- [x] 可以查看热门评论、相似歌曲
- [x] 歌词滚动部分可以显示“相似歌曲”或者播放列表的候选歌曲

![image](https://github.com/feeluown/FeelUOwn/assets/4962134/323935e1-7019-4c54-b0b1-d1d6af99d9dd)
  • Loading branch information
cosven authored Feb 15, 2024
1 parent ebf8f77 commit 2cc13e5
Show file tree
Hide file tree
Showing 45 changed files with 1,446 additions and 530 deletions.
40 changes: 30 additions & 10 deletions examples/research/mpv_render.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
from PyQt5.QtCore import Qt, QMetaObject, pyqtSlot
from PyQt5.QtWidgets import QOpenGLWidget, QApplication
from PyQt5.QtWidgets import QOpenGLWidget, QApplication, QWidget, QHBoxLayout
from PyQt5.QtOpenGL import QGLContext

# HELP: currently, we need import GL module,otherwise it will raise seg fault on Linux(Ubuntu 18.04)
from OpenGL import GL # noqa
# from OpenGL import GL # noqa

from mpv import MPV, _mpv_get_sub_api, _mpv_opengl_cb_set_update_callback, \
_mpv_opengl_cb_init_gl, OpenGlCbGetProcAddrFn, _mpv_opengl_cb_draw, \
_mpv_opengl_cb_report_flip, MpvSubApi, OpenGlCbUpdateFn, _mpv_opengl_cb_uninit_gl, \
MpvRenderContext
from mpv import MPV, OpenGlCbGetProcAddrFn, MpvRenderContext


def get_proc_addr(_, name):
Expand All @@ -20,11 +17,12 @@ def get_proc_addr(_, name):


class MpvWidget(QOpenGLWidget):
def __init__(self, parent=None):
def __init__(self, window, parent=None):
super().__init__(parent=parent)
self.mpv = MPV(ytdl=True)
self.ctx = None
self.get_proc_addr_c = OpenGlCbGetProcAddrFn(get_proc_addr)
self._window = window

def initializeGL(self):
params = {'get_proc_address': self.get_proc_addr_c}
Expand All @@ -35,7 +33,7 @@ def initializeGL(self):

def paintGL(self):
# compatible with HiDPI display
ratio = self.windowHandle().devicePixelRatio()
ratio = self._window.devicePixelRatio()
w = int(self.width() * ratio)
h = int(self.height() * ratio)
opengl_fbo = {'w': w,
Expand Down Expand Up @@ -69,11 +67,33 @@ def closeEvent(self, _):


if __name__ == '__main__':
import locale
import locale, time, threading

app = QApplication([])
locale.setlocale(locale.LC_NUMERIC, 'C')
widget = MpvWidget()
root = QWidget()
layout = QHBoxLayout(root)
p1 = QWidget()
p2 = QWidget()
l1 = QHBoxLayout(p1)
l2 = QHBoxLayout(p2)
layout.addWidget(p1)
layout.addWidget(p2)
root.resize(600, 400)
root.show()

widget = MpvWidget(root)
l1.addWidget(widget)
widget.show()

url = 'data/test.webm'
widget.mpv.play(url)

def reparent():
time.sleep(1)
widget.mpv.pause = True
l2.addWidget(widget)
widget.mpv.pause = False

threading.Thread(target=reparent).start()
app.exec()
2 changes: 2 additions & 0 deletions feeluown/app/gui_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def __init__(self, *args, **kwargs):
QApplication.instance().setQuitOnLastWindowClosed(not config.ENABLE_TRAY)
QApplication.instance().setApplicationName('FeelUOwn')

# Note that QApplication.setFont only works for those widgets that created
# after `QApplication.exec` (tested on macOS).
if sys.platform == 'win32':
font = QApplication.font()
# By default, it uses SimSun(宋体) on windows, which is a little ugly.
Expand Down
Binary file removed feeluown/gui/assets/icons/last.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/last_dark.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/next.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/next_dark.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/pause.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/pause_dark.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/play.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/play_dark.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/volume.png
Binary file not shown.
Binary file removed feeluown/gui/assets/icons/volume_dark.png
Binary file not shown.
8 changes: 5 additions & 3 deletions feeluown/gui/assets/themes/common.qss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ TextButton {
padding: 2px 8px;
border-radius: 3px;
}

VideoPlayerCtlBar Button {
padding: 0px;
}

ToolbarButton {
width: 30px;
height: 30px;
Expand Down Expand Up @@ -102,9 +107,6 @@ ClickableHeader TextButton:checked {
#toggle_lyric_btn {
padding: 0px 5px;
}
#toggle_video_btn, #toggle_pip_btn {
padding: 0px 3px;
}

VideoCtlToolbar QPushButton {
width: 24px;
Expand Down
18 changes: 0 additions & 18 deletions feeluown/gui/assets/themes/dark.qss
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,6 @@ TextButton:pressed, ToolbarButton:pressed {
background-color: #3e3e3e;
}

#pp_btn, #video_pp_btn {
border-image: url(icons:play_dark.png);
}
#pp_btn:checked, #video_pp_btn:checked {
border-image: url(icons:pause_dark.png);
}
#previous_btn {
border-image: url(icons:last_dark.png);
}
#next_btn {
border-image: url(icons:next_dark.png);
}
#playlist_btn {
border-image: url(icons:cur_playlist_dark.png);
}
#volume_btn {
border-image: url(icons:volume_dark.png);
}
#like_btn {
border-image: url(icons:like_dark.png);
}
Expand Down
18 changes: 0 additions & 18 deletions feeluown/gui/assets/themes/light.qss
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,6 @@ TextButton:pressed, ToolbarButton:pressed {
background-color: #DDD;
}

#pp_btn, #video_pp_btn {
border-image: url(icons:play.png);
}
#pp_btn:checked, #video_pp_btn:checked {
border-image: url(icons:pause.png);
}
#previous_btn {
border-image: url(icons:last.png);
}
#next_btn {
border-image: url(icons:next.png);
}
#playlist_btn {
border-image: url(icons:cur_playlist.png);
}
#volume_btn {
border-image: url(icons:volume.png);
}
#like_btn {
border-image: url(icons:like.png);
}
Expand Down
1 change: 1 addition & 0 deletions feeluown/gui/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
from .volume_slider import * # noqa
from .song_tag import SongSourceTag # noqa
from .collections import CollectionListView # noqa
from .player_progress import PlayerProgressSliderAndLabel # noqa
60 changes: 30 additions & 30 deletions feeluown/gui/components/btns.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QPushButton, QWidget, QHBoxLayout
from feeluown.gui.widgets.selfpaint_btn import TriagleButton

from feeluown.player import State
from feeluown.excs import ProviderIOError
from feeluown.utils.aio import run_fn
from feeluown.gui.widgets.textbtn import TextButton
from feeluown.gui.widgets import PlayPauseButton, PlayNextButton, PlayPreviousButton
from feeluown.gui.helpers import resize_font

if TYPE_CHECKING:
Expand Down Expand Up @@ -123,11 +125,12 @@ def __init__(self, app: 'GuiApp', song=None, text='MV', **kwargs):
self._mv = None

self.bind_song(song)
self.setDisabled(True)
self.clicked.connect(self.on_clicked)

def on_clicked(self):
if self._mv is not None:
self._app.playlist.play_model(self._mv)
self._app.watch_mgr.play_video(self._mv)

def bind_song(self, song):
if song != self._song:
Expand All @@ -154,58 +157,55 @@ async def get_mv(self):
return self._mv


class MVButton(SongMVTextButton):
class NowplayingMVTextButton(SongMVTextButton):
def __init__(self, app: 'GuiApp', parent=None, **kwargs):
super().__init__(app, song=None, parent=parent, **kwargs)

self.setObjectName('mv_btn')
self._app.playlist.song_changed.connect(
self.on_player_song_changed, aioqueue=True)
self._app.playlist.song_mv_changed.connect(
self.on_song_mv_changed, aioqueue=True)

def on_player_song_changed(self, song):
task_spec = self._app.task_mgr.get_or_create('update-mv-btn-status')
task_spec.bind_coro(self.update_mv_btn_status(song))

async def update_mv_btn_status(self, song):
self.bind_song(song)
await self.get_mv()
def on_song_mv_changed(self, _, mv):
self.setEnabled(mv is not None)
self._mv = mv


class MediaButtons(QWidget):
def __init__(self, app: 'GuiApp', spacing=8, button_width=30, parent=None):
class MediaButtonsV2(QWidget):
def __init__(self, app: 'GuiApp', button_width=30, spacing=0, parent=None):
super().__init__(parent=parent)

self._app = app

self.button_width = button_width

size = (self.button_width, self.button_width)

self.previous_btn = QPushButton(self)
self.pp_btn = QPushButton(self)
self.next_btn = QPushButton(self)
self.previous_btn = PlayPreviousButton(parent=self, length=button_width)
self.pp_btn = PlayPauseButton(parent=self, length=button_width)
self.next_btn = PlayNextButton(parent=self, length=button_width)
self.toggle_video_btn = TriagleButton(length=button_width)
self.toggle_video_btn.hide()
self.pp_btn.setCheckable(True)

self.previous_btn.setFixedSize(*size)
self.pp_btn.setFixedSize(*size)
self.next_btn.setFixedSize(*size)

self.previous_btn.setObjectName('previous_btn')
self.pp_btn.setObjectName('pp_btn')
self.next_btn.setObjectName('next_btn')
self.toggle_video_btn.setToolTip('展示视频画面')

self._layout = QHBoxLayout(self)
self._layout.setSpacing(spacing)
self._layout.setContentsMargins(0, 0, 0, 0)
self._layout.addWidget(self.previous_btn)
self._layout.addWidget(self.pp_btn)
self._layout.addWidget(self.next_btn)
self._layout.addWidget(self.toggle_video_btn)

self.next_btn.clicked.connect(self._app.playlist.next)
self.previous_btn.clicked.connect(self._app.playlist.previous)
self.pp_btn.clicked.connect(self._app.player.toggle)
self._app.player.state_changed.connect(
self._on_player_state_changed, aioqueue=True)
self._on_player_state_changed, aioqueue=True
)
self._app.player.video_format_changed.connect(
self.on_video_format_changed, aioqueue=True
)

def _on_player_state_changed(self, state):
self.pp_btn.setChecked(state == State.playing)

def on_video_format_changed(self, video_format):
if video_format is None:
self.toggle_video_btn.hide()
else:
self.toggle_video_btn.show()
Loading

0 comments on commit 2cc13e5

Please sign in to comment.