ok so here is a new version of the script.
This is what I did and I hope that it will work for you.
if you have a single mpc running it is going to use that.
this all works in unlimited number of multiples
if you have more then one in full screen and the rest not it will usee the one running in fullscreen,
if you have multiiple running in fullscreen it is going to pick the one that is considered active (has focus)
if you have multiple running not in fullscreen it is going to use which ever one is active (has focus)
I did not know which version of the script you were using the original layout or the one where the OSD is center justified.
I also threw in a little extra bonus special effect, I thought you may like it. if you do not let me know and I will remove it. You can adjust the first variable in the script
Code: Select all
# this try/except routine is done because the information that is generated
# in a Python script is persistent between runs. Whats this means is that
# anything that is created in a python script when the script gets run a
# second, third, fourth... the objects created the first time are available to
# every run thereafter. So if you have any objects created that are static and
# do not change between runs then there is no need to make them over again.
# To test for this we simply use one of those object names that are static.
# if the object exists it will pass right on by. if it oes not it will
# generate an error that we catch in the except portion of thee code block.
# And that is where we will then create all of the static objects
# adjustable set between 0 and 255
MAX_TRANSPARENT = 90
try:
user32
except NameError:
import wx
import datetime
import time
import threading
import ctypes
from ctypes.wintypes import (
BOOL,
LONG,
UINT,
POINT,
RECT,
HWND,
LPARAM,
DWORD
) # NOQA
from eg.WinApi import (
GetWindowText,
GetTopLevelWindowList,
GetProcessName
) # NOQA
from eg.WinApi.Dynamic import (
SetWindowPos,
SWP_FRAMECHANGED,
SWP_NOACTIVATE,
SWP_NOOWNERZORDER,
SWP_SHOWWINDOW,
)
HWND_FLAGS = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED
GWL_STYLE = -16
GWL_EXSTYLE = -20
WS_BORDER = 0x00800000
WS_DLGFRAME = 0x00400000
WS_THICKFRAME = 0x00040000
WS_EX_WINDOWEDGE = 0x00000100
WS_POPUP = 0x80000000
WS_EX_TOPMOST = 0x00000008
PID = DWORD
user32 = ctypes.windll.User32
# DWORD GetWindowThreadProcessId(
# HWND hWnd,
# LPDWORD lpdwProcessId
# );
_GetWindowThreadProcessId = user32.GetWindowThreadProcessId
_GetWindowThreadProcessId.restype = DWORD
# LONG GetWindowLongW(
# HWND hWnd,
# int nIndex
# );
_GetWindowLong = user32.GetWindowLongW
_GetWindowLong.restype = LONG
# HWND GetActiveWindow(
#
# );
_GetActiveWindow = user32.GetActiveWindow
_GetActiveWindow.restype = HWND
BAR_TEMPLATE = '[{bars}{remaining}] - {percent:.2f}%'
ELAPSED_TOTAL_TEMPLATE = 'Elapsed: {elapsed} / Total: {duration}'
OSD_TEMPLATE = '{title}\n \n{bar}\n{elapsed_total}'
osd_frame = eg.plugins.EventGhost.actions['ShowOSD'].osdFrame
_old_show_osd = osd_frame.ShowOSD
_old_on_timeout = osd_frame.OnTimeout
eg.globals.fade_in_event = threading.Event()
eg.globals.fade_out_event = threading.Event()
# OK so I removed the euse of the eg.WindowMatcher.
# it is a horribly slow thing to use and since we are only looking to see
# if a process is running or not we use GetTopLevelWindowList which returns
# the handles for the base window class of an application. then we ask windows
# to get us the process id (pid) for one of the handles in the returned list.
# Then we use that pid to get the process name. and we match that process name
# up with the one we are looking for.
handles = GetTopLevelWindowList(False)
# OK so I added some code to locate which instance of mpc to use if more then
# one is running.
# I elected to do some crafty coding with this. What happens is after the the
# window handles are found I then query the Windows API to get thee styles of
# the window. i check for specific markers like if the window has a border and
# if the window is set to be the topmost window as these are things that get
# turned on/off when an application goes into a fullscreen mode. at the same
# time I am also checking for a copy of mpc that is the active window. I do
# this to cover my bases, if there is more then a single active copy and none
# of them are set to full screen it is going to choose the one that is active
# (has focus). if there is a fullscreen copy running and a second. and the
# second is active the fullscreen is going to be the one used.
for handle in handles[:]:
pid = PID()
_GetWindowThreadProcessId(HWND(handle), ctypes.byref(pid))
process_name = GetProcessName(pid.value)
# we check the process name of the window. if it does not match then we
# remove it form the list.
if process_name != 'mpc-hc64.exe':
handles.remove(handle)
# if there are no windows found then we want to stop the macro from running and
# exit the script
if not handles:
handle = None
eg.StopMacro()
eg.Exit()
handle = None
# I removed the use of eg.globals.WindowsState as a mechanism for checking if
# the app is in fullscreen mode. Because this is one of the things we are
# looking for when we pick the window to use we no longer need to depend on
# an outside source to determine the window state. So you can remove that from
# your tree if you want.
is_fullscreen = False
for hwnd in handles:
# getting the windows styles and extended styles.
style = _GetWindowLong(HWND(hwnd), GWL_STYLE)
style_ex = _GetWindowLong(HWND(hwnd), GWL_EXSTYLE)
# checking the styles for specific markers.
if (
not style & WS_BORDER and
not style & WS_DLGFRAME and
not style & WS_THICKFRAME and
not style_ex & WS_EX_WINDOWEDGE and
style & WS_POPUP and
style_ex & WS_EX_TOPMOST
):
# if there are 2 copies of mpc running and both are fullscreen then
# we are going to use the one that is active
if is_fullscreen is False:
handle = hwnd
is_fullscreen = True
elif _GetActiveWindow() == hwnd:
handle = hwnd
is_fullscreen = True
# if there is no full screen one found already and this window is active
# then we seet it into place
elif handle is None and _GetActiveWindow() == hwnd:
handle = hwnd
is_fullscreen = False
# fallback if something does not work properly in the code.
if handle is None:
handle = handles[0]
window_text = GetWindowText(handle)
# I removed the use of re and did a simple trial and error using a for loop
# with an else statement.
# If i split a string on a value and that value is not n the string it is
# going to return a list with the original string at index 0. So the list
# only las a length of 1. if it did have the value in it the list would have a
# length greater then 1. and we use that to break the loop. This will cause
# the else to not get run.
for item in (".mkv", ".mp4", ".avi", ".ogm"):
window_text = window_text.split(item)
if len(window_text) > 1:
break
window_text = window_text[0]
else:
window_text = window_text.split('.')
title = window_text[0]
_elapsed, _remaining, _duration = eg.plugins.MediaPlayerClassic.GetTimes()
# print "elaps, rem, total =",elaps, rem, total
# I removed all of that math involved in trying to get the seconds in
# favor of using time.strptime and datetime.timedelta
# time.strptime will take a string formatted time representation and turn it
# into a python object for us. we simply have to provide details as to how the
# string is formatted. you can get more information on the identifiers here
# http://strftime.org/
# once we have that object which only does the simple
# conversion to hours minutes seconds we need to create another object that
# does the match for us and will return the total seconds
while _elapsed.count(':') < 2:
_elapsed = '00:' + _elapsed
while _remaining.count(':') < 2:
_remaining = '00:' + _remaining
while _duration.count(':') < 2:
_duration = '00:' + _duration
elapsed = time.strptime(_elapsed, '%H:%M:%S')
elapsed = datetime.timedelta(
hours=elapsed.tm_hour,
minutes=elapsed.tm_min,
seconds=elapsed.tm_sec
)
remaining = time.strptime(_remaining, '%H:%M:%S')
remaining = datetime.timedelta(
hours=remaining.tm_hour,
minutes=remaining.tm_min,
seconds=remaining.tm_sec
)
duration = time.strptime(_duration, '%H:%M:%S')
duration = datetime.timedelta(
hours=duration.tm_hour,
minutes=duration.tm_min,
seconds=duration.tm_sec
)
# heere we do a test to see if elapsed has any seconds to it. if iit does
# that means the video is playing and to generate the osd
if elapsed.total_seconds():
def fade_in():
for i in range(MAX_TRANSPARENT):
if eg.globals.fade_in_event.isSet():
break
osd_frame.SetTransparent(i)
eg.globals.fade_in_event.wait(0.01)
osd_frame.ShowOSD = _old_show_osd
def fade_out():
for i in range(MAX_TRANSPARENT, 0, -1):
if eg.globals.fade_out_event.isSet():
break
osd_frame.SetTransparent(i)
eg.globals.fade_out_event.wait(0.01)
_old_on_timeout()
osd_frame.OnTimeout = _old_on_timeout
def show_osd(*args, **kwargs):
args = list(args)
args[7] += MAX_TRANSPARENT * 0.01
eg.globals.fade_out_event.set()
eg.globals.fade_in_event.clear()
osd_frame.SetTransparent(0)
_old_show_osd(*args, **kwargs)
fade_in()
def on_timeout():
eg.globals.fade_in_event.set()
eg.globals.fade_out_event.clear()
fade_out()
osd_frame.ShowOSD = show_osd
osd_frame.OnTimeout = on_timeout
# instead of having to calls to ShowOSD we can set the parameters we want
# to pass to ShowOSD into a variable you name the variable the same for
# Fullscreen and not Fullscreen and set the parameters for each variation
# then when we call ShowOSD the proper set of parameters will be passed
if is_fullscreen:
osd_args = (
u'0;-82;0;0;0;700;255;0;0;0;3;2;1;34;Verdana',
(0, 255, 0),
(0, 0, 0),
5,
(0, 0),
1,
3.0,
None
)
else:
osd_args = (
u'0;-030;0;0;0;700;255;0;0;0;3;2;1;34;Verdana',
(0, 255, 0),
(0, 0, 0),
5,
(0, 0),
2,
3.0,
None
)
# I trimmed down the percent math. iif you want to change an int to a
# float you do not need to do 1.0 * int. this will work but the pythonic
# way is to wrap that int with float()
percent = (
(
float(elapsed.total_seconds()) /
float(duration.total_seconds())
) * 100.0
)
# I removed the conversion of the percent form a float to an int because
# i wanted the percent as a float for use further on down the line
bars = '/' * int(round(percent))
remaining = '.' * (100 - len(bars))
# I created string templates for the OSD. i split the osd into 3 sections
# I will explain as to why they are split into 3 sections further down
# I added the percent that has elapsed to the bar line as a 2 decimal place
# float
bar = BAR_TEMPLATE.format(
bars=bars,
remaining=remaining,
percent=percent
)
elapsed_total = ELAPSED_TOTAL_TEMPLATE.format(
elapsed=_elapsed,
duration=_duration
)
# OK so this is an odd thing. wx is thee GUI interface we use to generate
# all of the graphics, windows and controls in EG. because of how text
# gets displayed when using a GUI the characters defined in a font are
# not of equal size, so a "G" does not have the same width as an "!"
# I saw that you were displaying the OSD n the center of the screen with
# what looked like center justification. because of that font issue ths
# is extremely difficult to do. Portions of the wx library allow us to call
# a function called GetTextExtent this function rill return the width and
# height in pixels of a string passed into it. we have to set the font we
# want to use then call the function. in order to get the center
# justification as close as we can and the space is the only thing we can
# really do it with we also want to know the width of a space.
frame = wx.Frame(None, -1)
font = wx.FontFromNativeInfoString(osd_args[0])
frame.SetFont(font)
bar_len = frame.GetTextExtent(bar)[0]
title_len = frame.GetTextExtent(title)[0]
elapsed_total_len = frame.GetTextExtent(elapsed_total)[0]
space_len = frame.GetTextExtent(' ')[0]
# so here we do the checking to see which line is longer then the other to
# adjust the one that is shorter
if bar_len < title_len:
# this is the number of spaces we are going to need. so we find out the
# pixel difference divide that by the number of pixels a space is.
# this is going to return the total number of spaces needed to make the
# line the same length. Now remember center justification. so half of
# those spaces would need to be on thee back end of the line. Those
# we do not need to add to the line. so wee divide the total number of
# spaces by 2 to get the number of spaces we need to add to the front
# of the line
space_count = (
int(round(float(title_len - bar_len) / float(space_len))) / 2
)
bar = ' ' * space_count + bar
elif title_len < bar_len:
space_count = (
int(round(float(bar_len - title_len) / float(space_len))) / 2
)
title = ' ' * space_count + title
bar_len = frame.GetTextExtent(bar)[0]
frame.Destroy()
if elapsed_total_len < bar_len:
space_count = (
int(round(
float(bar_len - elapsed_total_len) / float(space_len))) / 2
)
elapsed_total = ' ' * space_count + elapsed_total
osd = OSD_TEMPLATE.format(
title=title,
bar=bar,
elapsed_total=elapsed_total
)
# and here is the single call to ShowOSD. we prefiix that variable we made
# earlier with a * which expands the variable into the parameters that
# needs to be passed
eg.plugins.EventGhost.ShowOSD(osd, *osd_args)