From c9cbfdb70590e498e7e76f9698d6570e866d052a Mon Sep 17 00:00:00 2001 From: tom lot Date: Mon, 22 Feb 2021 00:28:54 +0200 Subject: [PATCH] added the new http player and lf command --- syncplay/constants.py | 1 + syncplay/messages_de.py | 1 + syncplay/messages_en.py | 1 + syncplay/messages_es.py | 1 + syncplay/messages_it.py | 1 + syncplay/messages_pt_BR.py | 1 + syncplay/messages_pt_PT.py | 1 + syncplay/messages_ru.py | 1 + syncplay/messages_tr.py | 1 + syncplay/players/__init__.py | 3 +- syncplay/players/httpPlayer.py | 201 +++++++++++++++++++++++++++++++++ syncplay/ui/consoleUI.py | 84 ++++++++++---- 12 files changed, 272 insertions(+), 25 deletions(-) create mode 100644 syncplay/players/httpPlayer.py diff --git a/syncplay/constants.py b/syncplay/constants.py index aa9ae1f..bec6d05 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -128,6 +128,7 @@ COMMANDS_QUEUE = ['queue', 'qa', 'add'] COMMANDS_PLAYLIST = ['playlist', 'ql', 'pl'] COMMANDS_SELECT = ['select', 'qs'] COMMANDS_DELETE = ['delete', 'd', 'qd'] +COMMANDS_FILE = ['lf'] MPC_MIN_VER = "1.6.4" MPC_BE_MIN_VER = "1.5.2.3123" VLC_MIN_VERSION = "2.2.1" diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index bcb9339..f045c48 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -91,6 +91,7 @@ de = { "commandList-notification/playlist": "\tql - show the current playlist", # TO DO: Translate "commandList-notification/select": "\tqs [index] - select given entry in the playlist", # TO DO: Translate "commandList-notification/delete": "\tqd [index] - delete the given entry from the playlist", # TO DO: Translate + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Syncplay Version: {}", # syncplay.version "more-info-notification": "Weitere Informationen auf: {}", # projectURL diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 970df4c..5aa1209 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -91,6 +91,7 @@ en = { "commandList-notification/playlist": "\tql - show the current playlist", "commandList-notification/select": "\tqs [index] - select given entry in the playlist", "commandList-notification/delete": "\tqd [index] - delete the given entry from the playlist", + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Syncplay version: {}", # syncplay.version "more-info-notification": "More info available at: {}", # projectURL diff --git a/syncplay/messages_es.py b/syncplay/messages_es.py index 60ca741..32e747a 100644 --- a/syncplay/messages_es.py +++ b/syncplay/messages_es.py @@ -91,6 +91,7 @@ es = { "commandList-notification/playlist": "\tql - show the current playlist", # TO DO: Translate "commandList-notification/select": "\tqs [index] - select given entry in the playlist", # TO DO: Translate "commandList-notification/delete": "\tqd [index] - delete the given entry from the playlist", # TO DO: Translate + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Versión de Syncplay: {}", # syncplay.version "more-info-notification": "Más información disponible en: {}", # projectURL diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index d23de3b..28df3ac 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -91,6 +91,7 @@ it = { "commandList-notification/playlist": "\tql - show the current playlist", # TO DO: Translate "commandList-notification/select": "\tqs [index] - select given entry in the playlist", # TO DO: Translate "commandList-notification/delete": "\tqd [index] - delete the given entry from the playlist", # TO DO: Translate + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Versione di Syncplay: {}", # syncplay.version "more-info-notification": "Maggiori informazioni a: {}", # projectURL diff --git a/syncplay/messages_pt_BR.py b/syncplay/messages_pt_BR.py index eaabb17..af32670 100644 --- a/syncplay/messages_pt_BR.py +++ b/syncplay/messages_pt_BR.py @@ -91,6 +91,7 @@ pt_BR = { "commandList-notification/playlist": "\tql - show the current playlist", # TO DO: Translate "commandList-notification/select": "\tqs [index] - select given entry in the playlist", # TO DO: Translate "commandList-notification/delete": "\tqd [index] - delete the given entry from the playlist", # TO DO: Translate + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Versão do Syncplay: {}", # syncplay.version "more-info-notification": "Mais informações disponíveis em: {}", # projectURL diff --git a/syncplay/messages_pt_PT.py b/syncplay/messages_pt_PT.py index 4ee43c9..e37c18e 100644 --- a/syncplay/messages_pt_PT.py +++ b/syncplay/messages_pt_PT.py @@ -91,6 +91,7 @@ pt_PT = { "commandList-notification/playlist": "\tql - show the current playlist", # TO DO: Translate "commandList-notification/select": "\tqs [index] - select given entry in the playlist", # TO DO: Translate "commandList-notification/delete": "\tqd [index] - delete the given entry from the playlist", # TO DO: Translate + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Versão do Syncplay: {}", # syncplay.version "more-info-notification": "Mais informações disponíveis em: {}", # projectURL diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 08ee983..5106cf8 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -90,6 +90,7 @@ ru = { "commandList-notification/playlist": "\tql - показать текущий список воспроизведения", "commandList-notification/select": "\tqs [индекс] - выделить указанный пункт в списке воспроизведения", "commandList-notification/delete": "\tqd [индекс] - удалить указанный пункт из списка воспроизведения", + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Версия Syncplay: {}", # syncplay.version "more-info-notification": "Больше информации на {}", # projectURL diff --git a/syncplay/messages_tr.py b/syncplay/messages_tr.py index 88e351a..6e56d04 100644 --- a/syncplay/messages_tr.py +++ b/syncplay/messages_tr.py @@ -91,6 +91,7 @@ tr = { "commandList-notification/playlist": "\tql - mevcut oynatma listesini gösterir", "commandList-notification/select": "\tqs [index] - oynatma listesinde verilen girişi seçer", "commandList-notification/delete": "\tqd [index] - verilen girişi oynatma listesinden siler", + "commandList-notification/load": "\tlf [path] - load file", "syncplay-version-notification": "Syncplay sürümü: {}", # syncplay.version "more-info-notification": "Daha fazla bilgiye şu adresten ulaşabilirsiniz: {}", # projectURL diff --git a/syncplay/players/__init__.py b/syncplay/players/__init__.py index 90fa73b..46cb015 100755 --- a/syncplay/players/__init__.py +++ b/syncplay/players/__init__.py @@ -18,6 +18,7 @@ except ImportError: from syncplay.players.basePlayer import DummyPlayer IinaPlayer = DummyPlayer +from syncplay.players.httpPlayer import HttpPlayer def getAvailablePlayers(): - return [MPCHCAPIPlayer, MpvPlayer, MpvnetPlayer, VlcPlayer, MpcBePlayer, MplayerPlayer, IinaPlayer] + return [MPCHCAPIPlayer, MpvPlayer, MpvnetPlayer, VlcPlayer, MpcBePlayer, MplayerPlayer, IinaPlayer, HttpPlayer] diff --git a/syncplay/players/httpPlayer.py b/syncplay/players/httpPlayer.py new file mode 100644 index 0000000..e920661 --- /dev/null +++ b/syncplay/players/httpPlayer.py @@ -0,0 +1,201 @@ +from syncplay.players.basePlayer import BasePlayer +from syncplay import constants +from mutagen.mp3 import MP3 +from time import time +import requests +import os + + +class StreamData(): + def __init__(self): + pass + + def close(self): + pass + + ''' + @type file_name: string + @type position: float + @type speed: float + @type paused: bool + @type address: tuple + + Send formatted data to the given address + ''' + + def sendto(self, position, speed, paused, address): + try: + data = {"position": position, "speed": speed, + "paused": paused, "time": time()} + requests.post(address, json=data) + except: + print("can't connect to the server right now") + + +class HttpPlayer(BasePlayer): + speedSupported = True + customOpenDialog = False + chatOSDSupported = False + alertOSDSupported = False + osdMessageSeparator = "" + + SEND_ADDRESS = "http://127.0.0.1:5000/data" + DEFAULT_PATH = "/HttpPlayer" + DEFAULT_FILE = "want.mp3" + + def __init__(self, client, playerPath, filePath, args): + self._stream = StreamData() + self._client = client + + self._filepath = filePath if filePath else self.DEFAULT_FILE # For the audio file + self._position = 0.0 # In seconds + self._speed = 1.0 # Percentage + self._paused = False + self._done = False + self._last_send = time() + + try: + self.openFile(self._filepath) + self._client.updateFile(os.path.basename(self._filepath), MP3( + self._filepath).info.length, self._filepath) + self.setPaused(True) + self.setPosition(0.0) + except Exception as e: + print(e) + + self._client.initPlayer(self) + + ''' + Send stream properties to the server through the StreamData object + ''' + + def streamUpdate(self): + self._stream.sendto(self._position, self._speed, + self._paused, self.SEND_ADDRESS) + + ''' + This method is supposed to + execute updatePlayerStatus(paused, position) on client + Given the arguments: boolean paused and float position in seconds + ''' + + def askForStatus(self): + if not self._client.userlist.currentUser.ready: + self._client.toggleReady() + if time() - self._last_send >= 5: + self._position = self._client.getGlobalPosition() + self._paused = self._client.getGlobalPaused() + self.streamUpdate() + self._last_send = time() + + self._client.updatePlayerStatus( + self._client.getGlobalPaused(), self._client.getGlobalPosition()) + + ''' + Display given message on player's OSD or similar means + ''' + + def displayMessage( + self, message, duration=(constants.OSD_DURATION*1000), secondaryOSD=False, mood=constants.MESSAGE_NEUTRAL + ): + pass # Nothing to see here + + ''' + Cleanup connection with player before syncplay will close down + ''' + + def drop(self): + self._stream.close() + self._done = True + + ''' + Start up the player, returns its instance + ''' + @ staticmethod + def run(client, playerPath, filePath, args): + return HttpPlayer(client, playerPath, filePath, args) + + ''' + @type value: boolean + ''' + + def setPaused(self, value): + print(f"set pause {value}") + self._paused = value + self.streamUpdate() + + ''' + @type value: list + ''' + + def setFeatures(self, featureList): + pass # Nothing to see here... + + ''' + @type value: float + ''' + + def setPosition(self, value): + print(f"set position: {value}") + self._position = value + self.streamUpdate() + + ''' + @type value: float + ''' + + def setSpeed(self, value): + print(f"set speed {value}") + self._speed = value + self.streamUpdate() + + ''' + @type filePath: string + ''' + + def openFile(self, filePath, resetPosition=False): + print(f"open file: {filePath}") + self._filepath = filePath + self.streamUpdate() + + ''' + @return: list of strings + ''' + @ staticmethod + def getDefaultPlayerPathsList(): + pass + + ''' + @type path: string + ''' + @ staticmethod + def isValidPlayerPath(path): + # Always true because it's really a player + return path == HTTPPlayer.DEFAULT_PATH + + ''' + @type path: string + @return: string + ''' + @ staticmethod + def getIconPath(path): + return None # Nothing to see here + + ''' + @type path: string + @return: string + ''' + @ staticmethod + def getExpandedPath(path): + return path # Not really a player + + ''' + @type playerPath: string + @type filePath: string + @return errorMessage: string + + Checks if the player has any problems with the given player/file path + ''' + @ staticmethod + def getPlayerPathErrors(playerPath, filePath): + return None # Nothing to see here diff --git a/syncplay/ui/consoleUI.py b/syncplay/ui/consoleUI.py index 1e96494..21c1f95 100755 --- a/syncplay/ui/consoleUI.py +++ b/syncplay/ui/consoleUI.py @@ -10,6 +10,7 @@ from syncplay import constants from syncplay import utils from syncplay.messages import getMessage from syncplay.utils import formatTime, isURL +from mutagen.mp3 import MP3 class ConsoleUI(threading.Thread): @@ -73,17 +74,22 @@ class ConsoleUI(threading.Thread): if user.isReady(): userflags += "({}) ".format(getMessage("ready-userlist-userflag")) - username = userflags + "*<{}>*".format(user.username) if user == currentUser else userflags + "<{}>".format(user.username) + username = userflags + \ + "*<{}>*".format(user.username) if user == currentUser else userflags + \ + "<{}>".format(user.username) if user.file: - message = getMessage("userlist-playing-notification").format(username) + message = getMessage( + "userlist-playing-notification").format(username) self.showMessage(message, True) - message = " {}: '{}' ({})".format(getMessage("userlist-file-notification"), user.file['name'], formatTime(user.file['duration'])) + message = " {}: '{}' ({})".format(getMessage( + "userlist-file-notification"), user.file['name'], formatTime(user.file['duration'])) if currentUser.file: if user.file['name'] == currentUser.file['name'] and user.file['size'] != currentUser.file['size']: message += getMessage("different-filesize-notification") self.showMessage(message, True) else: - message = getMessage("no-file-played-notification").format(username) + message = getMessage( + "no-file-played-notification").format(username) self.showMessage(message, True) def userListChange(self): @@ -107,7 +113,8 @@ class ConsoleUI(threading.Thread): if noTimestamp: print(message) else: - print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message) + print(time.strftime(constants.UI_TIME_FORMAT, + time.localtime()) + message) def showDebugMessage(self, message): print(message) @@ -136,9 +143,9 @@ class ConsoleUI(threading.Thread): if t is None: return if o.group('sign') == "/": - t = self._syncplayClient.getPlayerPosition() - t + t = self._syncplayClient.getPlayerPosition() - t elif sign: - t = self._syncplayClient.getUserOffset() + sign * t + t = self._syncplayClient.getUserOffset() + sign * t self._syncplayClient.setUserOffset(t) return True elif s: @@ -158,7 +165,8 @@ class ConsoleUI(threading.Thread): return if command.group('command') in constants.COMMANDS_UNDO: tmp_pos = self._syncplayClient.getPlayerPosition() - self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek) + self._syncplayClient.setPosition( + self._syncplayClient.playerPositionBeforeLastSeek) self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos elif command.group('command') in constants.COMMANDS_LIST: self.getUserlist() @@ -166,7 +174,8 @@ class ConsoleUI(threading.Thread): message = command.group('parameter') self._syncplayClient.sendChat(message) elif command.group('command') in constants.COMMANDS_PAUSE: - self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused()) + self._syncplayClient.setPaused( + not self._syncplayClient.getPlayerPaused()) elif command.group('command') in constants.COMMANDS_ROOM: room = command.group('parameter') if room is None: @@ -197,7 +206,8 @@ class ConsoleUI(threading.Thread): self._syncplayClient.ui.addFileToPlaylist(filename) elif command.group('command') in constants.COMMANDS_PLAYLIST: playlist = self._syncplayClient.playlist - playlist_elements = ["\t{}: {}".format(i+1, el) for i, el in enumerate(playlist._playlist)] + playlist_elements = ["\t{}: {}".format( + i+1, el) for i, el in enumerate(playlist._playlist)] if playlist_elements: i = playlist._playlistIndex @@ -213,41 +223,67 @@ class ConsoleUI(threading.Thread): if index < 0 or index >= len(self._syncplayClient.playlist._playlist): raise TypeError("Invalid playlist index") - self._syncplayClient.playlist.changeToPlaylistIndex(index, resetPosition=True) + self._syncplayClient.playlist.changeToPlaylistIndex( + index, resetPosition=True) self._syncplayClient.rewindFile() except (TypeError, AttributeError): - self.showErrorMessage(getMessage("playlist-invalid-index-error")) + self.showErrorMessage(getMessage( + "playlist-invalid-index-error")) elif command.group('command') in constants.COMMANDS_DELETE: try: index = int(command.group('parameter').strip()) - 1 self._syncplayClient.playlist.deleteAtIndex(index) except (TypeError, AttributeError): - self.showErrorMessage(getMessage("playlist-invalid-index-error")) - + self.showErrorMessage(getMessage( + "playlist-invalid-index-error")) + elif command.group('command') in constants.COMMANDS_FILE: + try: + path = command.group('parameter'.strip()) + if os.path.exists(path): + self.showMessage(f'loading {path}', True) + self._syncplayClient.updateFile( + os.path.basename(path), MP3(path).info.length, path) + self._syncplayClient._player.openFile(path) + self.showMessage(f'{path} loaded', True) + else: + self.showErrorMessage("File doesn't exists") + except Exception as e: + print(e) else: if self._tryAdvancedCommands(data): return if command.group('command') not in constants.COMMANDS_HELP: - self.showMessage(getMessage("unrecognized-command-notification")) + self.showMessage(getMessage( + "unrecognized-command-notification")) self.showMessage(getMessage("commandlist-notification"), True) self.showMessage(getMessage("commandlist-notification/room"), True) self.showMessage(getMessage("commandlist-notification/list"), True) self.showMessage(getMessage("commandlist-notification/undo"), True) - self.showMessage(getMessage("commandlist-notification/pause"), True) + self.showMessage(getMessage( + "commandlist-notification/pause"), True) self.showMessage(getMessage("commandlist-notification/seek"), True) self.showMessage(getMessage("commandlist-notification/help"), True) - self.showMessage(getMessage("commandlist-notification/toggle"), True) - self.showMessage(getMessage("commandlist-notification/create"), True) + self.showMessage(getMessage( + "commandlist-notification/toggle"), True) + self.showMessage(getMessage( + "commandlist-notification/create"), True) self.showMessage(getMessage("commandlist-notification/auth"), True) self.showMessage(getMessage("commandlist-notification/chat"), True) - self.showMessage(getMessage("commandList-notification/queue"), True) - self.showMessage(getMessage("commandList-notification/playlist"), True) - self.showMessage(getMessage("commandList-notification/select"), True) - self.showMessage(getMessage("commandList-notification/delete"), True) - self.showMessage(getMessage("syncplay-version-notification").format(syncplay.version), True) - self.showMessage(getMessage("more-info-notification").format(syncplay.projectURL), True) + self.showMessage(getMessage( + "commandList-notification/queue"), True) + self.showMessage(getMessage( + "commandList-notification/playlist"), True) + self.showMessage(getMessage( + "commandList-notification/select"), True) + self.showMessage(getMessage( + "commandList-notification/delete"), True) + self.showMessage(getMessage("commandList-notification/load"), True) + self.showMessage(getMessage( + "syncplay-version-notification").format(syncplay.version), True) + self.showMessage(getMessage( + "more-info-notification").format(syncplay.projectURL), True) def getUserlist(self): self._syncplayClient.getUserList()