Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc91716e

Browse files
authored
Merge pull request#162 from Dusk-afk/feat/media-session-switch
feat(media): add session switching functionality
2 parents1e3e1c4 +2f0e4bc commitc91716e

File tree

2 files changed

+92
-34
lines changed

2 files changed

+92
-34
lines changed

‎src/core/utils/win32/media.py

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
importctypes
21
importlogging
32
fromsettingsimportDEBUG
43
fromtypingimportAny,Callable
@@ -11,7 +10,6 @@
1110
importio
1211

1312
fromcore.utils.utilitiesimportSingleton
14-
fromcore.utils.win32.system_functionimportKEYEVENTF_EXTENDEDKEY,KEYEVENTF_KEYUP
1513

1614
fromwinsdk.windows.media.controlimport (GlobalSystemMediaTransportControlsSessionManagerasSessionManager,
1715
GlobalSystemMediaTransportControlsSessionasSession,
@@ -98,12 +96,19 @@ def _run_setup(self):
9896
# Manually trigger the callback on startup
9997
self._on_current_session_changed(self._session_manager,None,is_setup=True)
10098

101-
def_on_current_session_changed(self,manager:SessionManager,args:SessionsChangedEventArgs,is_setup=False):
99+
def_on_current_session_changed(
100+
self,
101+
manager:SessionManager,
102+
args:SessionsChangedEventArgs,
103+
is_setup=False,
104+
is_overridden=False,
105+
):
102106
ifDEBUG:
103107
self._log.debug('MediaCallback: _on_current_session_changed')
104108

105109
withself._current_session_lock:
106-
self._current_session=manager.get_current_session()
110+
ifnotis_overridden:
111+
self._current_session=manager.get_current_session()
107112

108113
ifself._current_sessionisnotNone:
109114

@@ -121,7 +126,19 @@ def _on_current_session_changed(self, manager: SessionManager, args: SessionsCha
121126

122127
forcallbackincallbacks:
123128
callback(self._current_sessionisnotNone)
129+
130+
def_current_session_only(fn):
131+
"""
132+
Decorator to ensure that the function is only called if the session is the same as the current session
133+
"""
124134

135+
defwrapper(self:"WindowsMedia",session:Session,*args,**kwargs):
136+
withself._current_session_lock:
137+
ifself._are_same_sessions(session,self._current_session):
138+
returnfn(self,session,*args,**kwargs)
139+
returnwrapper
140+
141+
@_current_session_only
125142
def_on_playback_info_changed(self,session:Session,args:PlaybackInfoChangedEventArgs):
126143
ifDEBUG:
127144
self._log.info('MediaCallback: _on_playback_info_changed')
@@ -136,6 +153,7 @@ def _on_playback_info_changed(self, session: Session, args: PlaybackInfoChangedE
136153
forcallbackincallbacks:
137154
callback(self._playback_info)
138155

156+
@_current_session_only
139157
def_on_timeline_properties_changed(self,session:Session,args:TimelinePropertiesChangedEventArgs):
140158
ifDEBUG:
141159
self._log.info('MediaCallback: _on_timeline_properties_changed')
@@ -150,18 +168,25 @@ def _on_timeline_properties_changed(self, session: Session, args: TimelineProper
150168
forcallbackincallbacks:
151169
callback(self._timeline_info)
152170

171+
@_current_session_only
153172
def_on_media_properties_changed(self,session:Session,args:MediaPropertiesChangedEventArgs):
154173
ifDEBUG:
155174
self._log.debug('MediaCallback: _on_media_properties_changed')
156175
try:
157-
asyncio.get_event_loop()
176+
# Only for the initial timer based update, because it is called from an event loop
177+
asyncio.create_task(self._update_media_properties(session))
158178
exceptRuntimeError:
159179
withself._media_info_lock:
160180
self._event_loop.run_until_complete(self._update_media_properties(session))
161-
else:
162-
# Only for the initial timer based update, because it is called from an event loop
163-
asyncio.create_task(self._update_media_properties(session))
164181

182+
ifself._media_infoandself._is_media_info_empty(self._media_info):
183+
sessions=self._session_manager.get_sessions()
184+
185+
# If current session isn't in the list of sessions, switch the session
186+
ifnotany(self._are_same_sessions(sessions[i],self._current_session)foriinrange(sessions.size)):
187+
self.switch_session(1)
188+
189+
@_current_session_only
165190
asyncdef_update_media_properties(self,session:Session):
166191
ifDEBUG:
167192
self._log.debug('MediaCallback: Attempting media info update')
@@ -171,14 +196,9 @@ async def _update_media_properties(self, session: Session):
171196

172197
media_info=self._properties_2_dict(media_info)
173198

174-
# Skip initial change calls where the thumbnail is None. This prevents processing multiple updates.
175-
# Might prevent showing info for no-thumbnail media
176-
ifmedia_info['thumbnail']isNone:
177-
ifDEBUG:
178-
self._log.debug('MediaCallback: Skipping media info update: no thumbnail')
179-
return
199+
ifmedia_info['thumbnail']isnotNone:
200+
media_info['thumbnail']=awaitself.get_thumbnail(media_info['thumbnail'])
180201

181-
media_info['thumbnail']=awaitself.get_thumbnail(media_info['thumbnail'])
182202
exceptExceptionase:
183203
self._log.error(f'MediaCallback: Error occurred whilst fetching media properties and thumbnail:{e}')
184204
return
@@ -228,21 +248,48 @@ async def get_thumbnail(thumbnail_stream_reference: IRandomAccessStreamReference
228248
finally:
229249
# Close the stream
230250
readable_stream.close()
231-
251+
232252
@staticmethod
233-
defplay_pause():
234-
user32=ctypes.windll.user32
235-
user32.keybd_event(VK_MEDIA_PLAY_PAUSE,0,KEYEVENTF_EXTENDEDKEY,0)
236-
user32.keybd_event(VK_MEDIA_PLAY_PAUSE,0,KEYEVENTF_KEYUP,0)
253+
def_is_media_info_empty(media_info:dict[str,Any])->bool:
254+
keys= ['album_artist','album_title','album_track_count','artist','playback_type','subtitle','title','track_number']
255+
# Check if all keys have 'zero' values
256+
returnall(notmedia_info.get(key)forkeyinkeys)
257+
258+
def_are_same_sessions(self,session1:Session,session2:Session)->bool:
259+
returnsession1.source_app_user_model_id==session2.source_app_user_model_id
260+
261+
defswitch_session(self,direction:int):
262+
sessions=self._session_manager.get_sessions()
263+
iflen(sessions)==0:
264+
return
237265

238-
@staticmethod
239-
defprev():
240-
user32=ctypes.windll.user32
241-
user32.keybd_event(VK_MEDIA_PREV_TRACK,0,KEYEVENTF_EXTENDEDKEY,0)
242-
user32.keybd_event(VK_MEDIA_PREV_TRACK,0,KEYEVENTF_KEYUP,0)
266+
withself._current_session_lock:
267+
current_session_idx=-1
268+
fori,sessioninenumerate(sessions):
269+
ifself._current_sessionisNoneorself._are_same_sessions(session,self._current_session):
270+
current_session_idx=i
271+
break
272+
273+
idx= (current_session_idx+direction)%len(sessions)
274+
ifself._are_same_sessions(sessions[idx],self._current_session):
275+
return
276+
ifDEBUG:
277+
self._log.info(f"Switching to session{idx} ({sessions[idx].source_app_user_model_id})")
278+
self._current_session=sessions[idx]
243279

244-
@staticmethod
245-
defnext():
246-
user32=ctypes.windll.user32
247-
user32.keybd_event(VK_MEDIA_NEXT_TRACK,0,KEYEVENTF_EXTENDEDKEY,0)
248-
user32.keybd_event(VK_MEDIA_NEXT_TRACK,0,KEYEVENTF_KEYUP,0)
280+
self._on_current_session_changed(self._session_manager,None,is_overridden=True)
281+
282+
defplay_pause(self):
283+
withself._current_session_lock:
284+
ifself._current_sessionisnotNone:
285+
self._current_session.try_toggle_play_pause_async()
286+
287+
defprev(self):
288+
withself._current_session_lock:
289+
ifself._current_sessionisnotNone:
290+
self._current_session.try_skip_previous_async()
291+
292+
defnext(self):
293+
withself._current_session_lock:
294+
ifself._current_sessionisnotNone:
295+
self._current_session.try_skip_next_async()

‎src/core/widgets/yasb/media.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
fromPIL.ImageQtimportQPixmap
77
fromPyQt6importQtCore
88
fromPyQt6.QtCoreimportQt
9+
fromPyQt6.QtGuiimportQWheelEvent
910
fromPIL.ImageQtimportImageQt
1011
fromwinsdk.windows.media.controlimportGlobalSystemMediaTransportControlsSessionPlaybackInfo
1112

@@ -193,6 +194,11 @@ def _on_media_properties_changed(self, media_info: Optional[dict[str, Any]]):
193194
ifnotself._show_thumbnail:
194195
return
195196

197+
# If no media in session, hide thumbnail and stop here
198+
ifmedia_info['thumbnail']isNoneandnotmedia_info['title']:
199+
self._thumbnail_label.hide()
200+
return
201+
196202
# Only update the thumbnail if the title/artist changes or if we did a toggle (resize)
197203
try:
198204
ifmedia_info['thumbnail']isnotNone:
@@ -248,16 +254,21 @@ def _create_media_button(self, icon, action):
248254
returnlabel
249255

250256
def_create_media_buttons(self):
251-
return (self._create_media_button(self._media_button_icons['prev_track'],WindowsMedia.prev),
252-
self._create_media_button(
253-
self._media_button_icons['play'],WindowsMedia.play_pause),self._create_media_button(
254-
self._media_button_icons['next_track'],WindowsMedia.next))
257+
return (self._create_media_button(self._media_button_icons['prev_track'],WindowsMedia().prev),
258+
self._create_media_button(self._media_button_icons['play'],WindowsMedia().play_pause),
259+
self._create_media_button(self._media_button_icons['next_track'],WindowsMedia().next))
255260

256261
defexecute_code(self,func):
257262
try:
258263
func()
259264
exceptExceptionase:
260265
logging.error(f"Error executing code:{e}")
266+
267+
defwheelEvent(self,event:QWheelEvent):
268+
ifevent.angleDelta().y()>0:
269+
self.media.switch_session(+1)# Next
270+
elifevent.angleDelta().y()<0:
271+
self.media.switch_session(-1)# Prev
261272

262273
classClickableLabel(QLabel):
263274
def__init__(self,parent=None):

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp