# Copyright (c) 2021 by xfangfang. All Rights Reserved.
#
# Using potplayer as DLNA media renderer
#
# Macast Metadata
# PotPlayer Renderer
# PotplayerRenderer
# win32
# 0.4
# 0.7
# xfangfang
# PotPlayer support for Macast, this is a simple plugin that only supports play and stop.
import os
import time
import logging
import cherrypy
import threading
import subprocess
import win32api, win32con
from enum import Enum
from contextlib import contextmanager
from macast import gui, Setting
from macast.renderer import Renderer
from macast.utils import SETTING_DIR
POTPLAYER_PATH_64 = r'C:\Program Files\DAUM\PotPlayer\PotPlayerMini64.exe'
POTPLAYER_PATH_32 = r'C:\Program Files (x86)\DAUM\PotPlayer\PotPlayerMini.exe'
logger = logging.getLogger("PotPlayer")
subtitle = os.path.join(SETTING_DIR, r"macast.ass")
class SettingProperty(Enum):
Potplayer_Path = 0
@contextmanager
def win32_reg_open(key, access=None, hive=None):
if access is None:
access = win32con.KEY_SET_VALUE
if hive is None:
hive = win32con.HKEY_CURRENT_USER
handle = win32api.RegOpenKey(
hive,
key,
0,
access)
yield handle
win32api.RegCloseKey(handle)
def get_potplayer_path():
# read Windows registry
try:
with win32_reg_open(r'Software\DAUM\PotPlayer64', win32con.KEY_SET_VALUE|win32con.KEY_QUERY_VALUE) as key:
path = win32api.RegQueryValueEx(key, 'ProgramPath')[0]
if os.path.exists(path):
return path
except:
pass
try:
with win32_reg_open(r'Software\DAUM\PotPlayer', win32con.KEY_SET_VALUE|win32con.KEY_QUERY_VALUE) as key:
path = win32api.RegQueryValueEx(key, 'ProgramPath')[0]
if os.path.exists(path):
return path
except:
pass
# read macast configuration
path = Setting.get(SettingProperty.Potplayer_Path, None)
# using default location
if path is None or not os.path.exists(path):
if os.path.exists(POTPLAYER_PATH_64):
return POTPLAYER_PATH_64
elif os.path.exists(POTPLAYER_PATH_32):
return POTPLAYER_PATH_32
else:
return path
# cannot find potplayer
if path is None:
Setting.set(SettingProperty.Potplayer_Path, POTPLAYER_PATH_64)
return None
class PotplayerRenderer(Renderer):
def __init__(self):
super(PotplayerRenderer, self).__init__()
self.pid = None
self.start_position = 0
self.position_thread_running = True
self.position_thread = threading.Thread(target=self.position_tick, daemon=True)
self.position_thread.start()
# a thread is started here to increase the playback position once per second
# to simulate that the media is playing.
def position_tick(self):
while self.position_thread_running:
time.sleep(1)
self.start_position += 1
sec = self.start_position
position = '%d:%02d:%02d' % (sec // 3600, (sec % 3600) // 60, sec % 60)
self.set_state_position(position)
def set_media_stop(self):
if self.pid is not None:
subprocess.Popen(['taskkill', '/f', '/pid', str(self.pid)], creationflags=subprocess.CREATE_NO_WINDOW).communicate()
try:
os.remove(subtitle)
except:
pass
self.pid = None
self.set_state_transport('STOPPED')
cherrypy.engine.publish('renderer_av_stop')
def start_player(self, url):
path = get_potplayer_path()
if path is None:
subprocess.Popen(['notepad.exe', Setting.setting_path], creationflags=subprocess.CREATE_NO_WINDOW)
cherrypy.engine.publish('app_notify', "Error", "You should modify 'Potplayer_Path' to your local potplayer and restart Macast.")
logger.error(f'cannot find potplayer at: {Setting.get(SettingProperty.Potplayer_Path, None)}')
logger.error(f'cannot find potplayer at: {POTPLAYER_PATH_64}')
logger.error(f'cannot find potplayer at: {POTPLAYER_PATH_32}')
return
try:
proc = subprocess.Popen(f'"{path}" "{url}" /autoplay /sub="{subtitle}"', creationflags=subprocess.CREATE_NO_WINDOW)
self.pid = proc.pid
# wait potplayer to stop
proc.communicate()
logger.info('Potplayer stopped')
except Exception as e:
logger.exception("cannot start potplayer", exc_info=e)
self.set_media_stop()
cherrypy.engine.publish('app_notify', "Error", str(e))
def set_media_url(self, url, start=0):
self.set_media_stop()
self.start_position = 0
threading.Thread(target=self.start_player, daemon=True, kwargs={'url': url}).start()
self.set_state_transport("PLAYING")
cherrypy.engine.publish('renderer_av_uri', url)
def stop(self):
super(PotplayerRenderer, self).stop()
self.set_media_stop()
logger.info("PotPlayer stop")
def start(self):
super(PotplayerRenderer, self).start()
logger.info("PotPlayer start")
if __name__ == '__main__':
gui(PotplayerRenderer())
# or using cli to disable taskbar menu
# from macast import cli
# cli(PotplayerRenderer())