- Refactored index.html: Split into separate HTML (309 lines), CSS (908 lines), and JS (1,286 lines) files - Implemented media browser with folder configuration, recursive navigation, and thumbnail display - Added metadata extraction using mutagen library (title, artist, album, duration, bitrate, codec) - Implemented thumbnail generation and caching with SHA256 hash-based keys and LRU eviction - Added platform-specific file playback (os.startfile on Windows, xdg-open on Linux, open on macOS) - Implemented path validation security to prevent directory traversal attacks - Added smooth thumbnail loading with fade-in animation and loading spinner - Added i18n support for browser (English and Russian) - Updated dependencies: mutagen>=1.47.0, pillow>=10.0.0 - Added comprehensive media browser documentation to README Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
109 lines
2.3 KiB
Python
109 lines
2.3 KiB
Python
"""Abstract base class for media controllers."""
|
|
|
|
from abc import ABC, abstractmethod
|
|
|
|
from ..models import MediaStatus
|
|
|
|
|
|
class MediaController(ABC):
|
|
"""Abstract base class for platform-specific media controllers."""
|
|
|
|
@abstractmethod
|
|
async def get_status(self) -> MediaStatus:
|
|
"""Get the current media playback status.
|
|
|
|
Returns:
|
|
MediaStatus with current playback info
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def play(self) -> bool:
|
|
"""Resume or start playback.
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def pause(self) -> bool:
|
|
"""Pause playback.
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def stop(self) -> bool:
|
|
"""Stop playback.
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def next_track(self) -> bool:
|
|
"""Skip to the next track.
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def previous_track(self) -> bool:
|
|
"""Go to the previous track.
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def set_volume(self, volume: int) -> bool:
|
|
"""Set the system volume.
|
|
|
|
Args:
|
|
volume: Volume level (0-100)
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def toggle_mute(self) -> bool:
|
|
"""Toggle the mute state.
|
|
|
|
Returns:
|
|
The new mute state (True = muted)
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def seek(self, position: float) -> bool:
|
|
"""Seek to a position in the current track.
|
|
|
|
Args:
|
|
position: Position in seconds
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def open_file(self, file_path: str) -> bool:
|
|
"""Open a media file with the default system player.
|
|
|
|
Args:
|
|
file_path: Absolute path to the media file
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
pass
|