Refactor project into two standalone components
Split monorepo into separate units for future independent repositories: - media-server/: Standalone FastAPI server with own README, requirements, config example, and CLAUDE.md - haos-integration/: HACS-ready Home Assistant integration with hacs.json, own README, and CLAUDE.md Both components now have their own .gitignore files and can be easily extracted into separate repositories. Also adds custom icon support for scripts configuration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
151
media-server/media_server/service/install_windows.py
Normal file
151
media-server/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