Initial commit: Media server and Home Assistant integration
- FastAPI server for Windows media control via WinRT/SMTC - Home Assistant custom integration with media player entity - Script button entities for system commands - Position tracking with grace period for track skip handling - Server availability detection in HA entity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
151
media_server/service/install_windows.py
Normal file
151
media_server/service/install_windows.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""Windows service installer for Media Server.
|
||||
|
||||
This module allows the media server to be installed as a Windows service
|
||||
that starts automatically on boot.
|
||||
|
||||
Usage:
|
||||
Install: python -m media_server.service.install_windows install
|
||||
Start: python -m media_server.service.install_windows start
|
||||
Stop: python -m media_server.service.install_windows stop
|
||||
Remove: python -m media_server.service.install_windows remove
|
||||
Debug: python -m media_server.service.install_windows debug
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import logging
|
||||
|
||||
try:
|
||||
import win32serviceutil
|
||||
import win32service
|
||||
import win32event
|
||||
import servicemanager
|
||||
import win32api
|
||||
|
||||
WIN32_AVAILABLE = True
|
||||
except ImportError:
|
||||
WIN32_AVAILABLE = False
|
||||
print("pywin32 not installed. Install with: pip install pywin32")
|
||||
|
||||
|
||||
class MediaServerService:
|
||||
"""Windows service wrapper for the Media Server."""
|
||||
|
||||
_svc_name_ = "MediaServer"
|
||||
_svc_display_name_ = "Media Server"
|
||||
_svc_description_ = "REST API server for controlling system media playback"
|
||||
|
||||
def __init__(self, args=None):
|
||||
if WIN32_AVAILABLE:
|
||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||
self.stop_event = win32event.CreateEvent(None, 0, 0, None)
|
||||
self.is_running = False
|
||||
self.server = None
|
||||
|
||||
def SvcStop(self):
|
||||
"""Stop the service."""
|
||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||
win32event.SetEvent(self.stop_event)
|
||||
self.is_running = False
|
||||
if self.server:
|
||||
self.server.should_exit = True
|
||||
|
||||
def SvcDoRun(self):
|
||||
"""Run the service."""
|
||||
servicemanager.LogMsg(
|
||||
servicemanager.EVENTLOG_INFORMATION_TYPE,
|
||||
servicemanager.PYS_SERVICE_STARTED,
|
||||
(self._svc_name_, ""),
|
||||
)
|
||||
self.is_running = True
|
||||
self.main()
|
||||
|
||||
def main(self):
|
||||
"""Main service loop."""
|
||||
import uvicorn
|
||||
from media_server.main import app
|
||||
from media_server.config import settings
|
||||
|
||||
config = uvicorn.Config(
|
||||
app,
|
||||
host=settings.host,
|
||||
port=settings.port,
|
||||
log_level=settings.log_level.lower(),
|
||||
)
|
||||
self.server = uvicorn.Server(config)
|
||||
self.server.run()
|
||||
|
||||
|
||||
if WIN32_AVAILABLE:
|
||||
# Dynamically inherit from ServiceFramework when available
|
||||
MediaServerService = type(
|
||||
"MediaServerService",
|
||||
(win32serviceutil.ServiceFramework,),
|
||||
dict(MediaServerService.__dict__),
|
||||
)
|
||||
|
||||
|
||||
def install_service():
|
||||
"""Install the Windows service."""
|
||||
if not WIN32_AVAILABLE:
|
||||
print("Error: pywin32 is required for Windows service installation")
|
||||
print("Install with: pip install pywin32")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Get the path to the Python executable
|
||||
python_exe = sys.executable
|
||||
|
||||
# Get the path to this module
|
||||
module_path = os.path.abspath(__file__)
|
||||
|
||||
win32serviceutil.InstallService(
|
||||
MediaServerService._svc_name_,
|
||||
MediaServerService._svc_name_,
|
||||
MediaServerService._svc_display_name_,
|
||||
startType=win32service.SERVICE_AUTO_START,
|
||||
description=MediaServerService._svc_description_,
|
||||
)
|
||||
print(f"Service '{MediaServerService._svc_display_name_}' installed successfully")
|
||||
print("Start the service with: sc start MediaServer")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Failed to install service: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def remove_service():
|
||||
"""Remove the Windows service."""
|
||||
if not WIN32_AVAILABLE:
|
||||
print("Error: pywin32 is required")
|
||||
return False
|
||||
|
||||
try:
|
||||
win32serviceutil.RemoveService(MediaServerService._svc_name_)
|
||||
print(f"Service '{MediaServerService._svc_display_name_}' removed successfully")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Failed to remove service: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for service management."""
|
||||
if not WIN32_AVAILABLE:
|
||||
print("Error: pywin32 is required for Windows service support")
|
||||
print("Install with: pip install pywin32")
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
# Running as a service
|
||||
servicemanager.Initialize()
|
||||
servicemanager.PrepareToHostSingle(MediaServerService)
|
||||
servicemanager.StartServiceCtrlDispatcher()
|
||||
else:
|
||||
# Command line management
|
||||
win32serviceutil.HandleCommandLine(MediaServerService)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user