diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 5132d32..d544005 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ version = '1.6.5' revision = ' development' milestone = 'Yoitsu' -release_number = '81' +release_number = '82' projectURL = 'https://syncplay.pl/' diff --git a/syncplay/client.py b/syncplay/client.py index 9822b23..235d5ed 100755 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -204,9 +204,16 @@ class SyncplayClient(object): self.setPosition(-1) self.ui.showDebugMessage("Rewinded after double-check") + def isPlayingMusic(self): + if self.userlist.currentUser.file: + for musicFormat in constants.MUSIC_FORMATS: + if self.userlist.currentUser.file['name'].lower().endswith(musicFormat): + return True + def updatePlayerStatus(self, paused, position): position -= self.getUserOffset() pauseChange, seeked = self._determinePlayerStateChange(paused, position) + positionBeforeSeek = self._playerPosition self._playerPosition = position self._playerPaused = paused currentLength = self.userlist.currentUser.file["duration"] if self.userlist.currentUser.file else 0 @@ -227,7 +234,9 @@ class SyncplayClient(object): if self._lastGlobalUpdate: self._lastPlayerUpdate = time.time() - if (pauseChange or seeked) and self._protocol: + if seeked and not pauseChange and self.isPlayingMusic() and abs(positionBeforeSeek - currentLength) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD and self.playlist.notJustChangedPlaylist(): + self.playlist.loadNextFileInPlaylist() + elif (pauseChange or seeked) and self._protocol: if seeked: self.playerPositionBeforeLastSeek = self.getGlobalPosition() self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), seeked, None, True) @@ -235,7 +244,10 @@ class SyncplayClient(object): def prepareToAdvancePlaylist(self): if self.playlist.canSwitchToNextPlaylistIndex(): self.ui.showDebugMessage("Preparing to advance playlist...") - self._protocol.sendState(0, True, True, None, True) + if self.isPlayingMusic(): + self._protocol.sendState(0, False, True, None, True) + else: + self._protocol.sendState(0, True, True, None, True) else: self.ui.showDebugMessage("Not preparing to advance playlist because the next file cannot be switched to") @@ -513,7 +525,11 @@ class SyncplayClient(object): return True return False - def openFile(self, filePath, resetPosition=False): + def openFile(self, filePath, resetPosition=False, fromUser=False): + if fromUser and filePath.endswith(".txt") or filePath.endswith(".m3u") or filePath.endswith(".m3u8"): + self.playlist.loadPlaylistFromFile(filePath, resetPosition) + return + self.playlist.openedFile() self._player.openFile(filePath, resetPosition) if resetPosition: @@ -532,10 +548,10 @@ class SyncplayClient(object): self.playlist.changeToPlaylistIndex(*args, **kwargs) def loopSingleFiles(self): - return self._config["loopSingleFiles"] + return self._config["loopSingleFiles"] or self.isPlayingMusic() def isPlaylistLoopingEnabled(self): - return self._config["loopAtEndOfPlaylist"] + return self._config["loopAtEndOfPlaylist"] or self.isPlayingMusic() def __executePrivacySettings(self, filename, size): if self._config['filenamePrivacyMode'] == PRIVACY_SENDHASHED_MODE: @@ -666,6 +682,9 @@ class SyncplayClient(object): readyState = self._config['readyAtStart'] if self.userlist.currentUser.isReady() is None else self.userlist.currentUser.isReady() self._protocol.setReady(readyState, manuallyInitiated=False) self.reIdentifyAsController() + if self._config["loadPlaylistFromFile"]: + self.playlist.loadPlaylistFromFile(self._config["loadPlaylistFromFile"]) + self._config["loadPlaylistFromFile"] = None def getRoom(self): return self.userlist.currentUser.room @@ -835,12 +854,16 @@ class SyncplayClient(object): self.autoplayCheck() def autoplayCheck(self): + if self.isPlayingMusic(): + return True if self.autoplayConditionsMet(): self.startAutoplayCountdown() else: self.stopAutoplayCountdown() def instaplayConditionsMet(self): + if self.isPlayingMusic(): + return True if not self.userlist.currentUser.canControl(): return False @@ -1697,6 +1720,15 @@ class SyncplayPlaylist(): filename = _playlist[_index] if len(_playlist) > _index else None return filename + def loadPlaylistFromFile(self, path, shuffle=False): + with open(path) as f: + newPlaylist = f.read().splitlines() + if shuffle: + random.shuffle(newPlaylist) + if newPlaylist: + self.changePlaylist(newPlaylist, username=None, resetIndex=True) + + def changePlaylist(self, files, username=None, resetIndex=False): if self._playlist == files: if self._playlistIndex != 0 and resetIndex: diff --git a/syncplay/constants.py b/syncplay/constants.py index 7f0fcf5..7cde152 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -30,6 +30,7 @@ UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] # Syncplay searches first to last DEFAULT_CONFIG_NAME = "syncplay.ini" RECENT_CLIENT_THRESHOLD = "1.6.4" # This and higher considered 'recent' clients (no warnings) +MUSIC_FORMATS = [".mp3", ".m4a", ".m4p", ".wav", ".aiff", ".r", ".ogg", ".flac"] # ALL LOWER CASE! WARN_OLD_CLIENTS = True # Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 4d1837f..e415256 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -173,6 +173,8 @@ de = { "version-argument": 'gibt die aktuelle Version aus', "version-message": "Du verwendest Syncplay v. {} ({})", + "load-playlist-from-file-argument": "loads playlist from text file (one entry per line)", # TODO: Translate + # Client labels "config-window-title": "Syncplay Konfiguration", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index b51ad1d..113a780 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -173,6 +173,8 @@ en = { "version-argument": 'prints your version', "version-message": "You're using Syncplay version {} ({})", + "load-playlist-from-file-argument": "loads playlist from text file (one entry per line)", + # Client labels "config-window-title": "Syncplay configuration", diff --git a/syncplay/messages_es.py b/syncplay/messages_es.py index 932f68b..1625574 100644 --- a/syncplay/messages_es.py +++ b/syncplay/messages_es.py @@ -173,6 +173,8 @@ es = { "version-argument": 'imprime tu versión', "version-message": "Estás usando la versión de Syncplay {} ({})", + "load-playlist-from-file-argument": "loads playlist from text file (one entry per line)", # TODO: Translate + # Client labels "config-window-title": "Configuración de Syncplay", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index df44bb9..23f8d77 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -173,6 +173,8 @@ it = { "version-argument": 'mostra la tua versione', "version-message": "Stai usando la versione di Syncplay {} ({})", + "load-playlist-from-file-argument": "loads playlist from text file (one entry per line)", # TODO: Translate + # Client labels "config-window-title": "Configurazione di Syncplay", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 26aadd1..cb9b026 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -174,6 +174,8 @@ ru = { "version-argument": 'выводит номер версии', "version-message": "Вы используете Syncplay версии {} ({})", + "load-playlist-from-file-argument": "loads playlist from text file (one entry per line)", # TODO: Translate + # Client labels "config-window-title": "Настройка Syncplay", diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index c27a684..68beb0d 100755 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -197,6 +197,10 @@ class NewMpvPlayer(OldMpvPlayer): return self._position def _storePosition(self, value): + if self._client.isPlayingMusic() and self._paused == False and self._position == value and abs(self._position-self._position) < 0.5: + self._client.ui.showDebugMessage("EOF DETECTED!") + self._position = 0 + self.setPosition(0) self.lastMPVPositionUpdate = time.time() if self._recentlyReset(): self._client.ui.showDebugMessage("Recently reset, so storing position as 0") diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index b606048..7c8141b 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -93,6 +93,7 @@ class ConfigurationGetter(object): "alertTimeout": 5, "chatTimeout": 7, "publicServers": [], + "loadPlaylistFromFile": None, "ca": None } @@ -308,6 +309,8 @@ class ConfigurationGetter(object): key = "noGui" if key == "clear_gui_data": key = "clearGUIData" + if key == "load_playlist_from_file": + key = "loadPlaylistFromFile" self._config[key] = val def _splitPortAndHost(self, host): @@ -496,6 +499,8 @@ class ConfigurationGetter(object): self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help=getMessage("file-argument")) self._argparser.add_argument('--clear-gui-data', action='store_true', help=getMessage("clear-gui-data-argument")) self._argparser.add_argument('-v', '--version', action='store_true', help=getMessage("version-argument")) + self._argparser.add_argument('--load-playlist-from-file', metavar="loadPlaylistFromFile", type=str, help=getMessage("load-playlist-from-file-argument")) + self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("args-argument")) args = self._argparser.parse_args() if args.version: diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 87ffc77..cfb233b 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -692,9 +692,9 @@ class MainWindow(QtWidgets.QMainWindow): pathFound = self._syncplayClient.fileSwitch.findFilepath(firstFile) if not isURL(firstFile) else None if self._syncplayClient.userlist.currentUser.file is None or firstFile != self._syncplayClient.userlist.currentUser.file["name"]: if isURL(firstFile): - menu.addAction(QtGui.QPixmap(resourcespath + "world_go.png"), getMessage("openstreamurl-menu-label"), lambda: self.openFile(firstFile, resetPosition=True)) + menu.addAction(QtGui.QPixmap(resourcespath + "world_go.png"), getMessage("openstreamurl-menu-label"), lambda: self.openFile(firstFile, resetPosition=True, fromUser=True)) elif pathFound: - menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openmedia-menu-label"), lambda: self.openFile(pathFound, resetPosition=True)) + menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openmedia-menu-label"), lambda: self.openFile(pathFound, resetPosition=True, fromUser=True)) if pathFound: menu.addAction(QtGui.QPixmap(resourcespath + "folder_film.png"), getMessage('open-containing-folder'), @@ -753,11 +753,11 @@ class MainWindow(QtWidgets.QMainWindow): if self._syncplayClient.userlist.currentUser.file is None or filename != self._syncplayClient.userlist.currentUser.file["name"]: if isURL(filename): - menu.addAction(QtGui.QPixmap(resourcespath + "world_go.png"), getMessage("openusersstream-menu-label").format(shortUsername), lambda: self.openFile(filename)) + menu.addAction(QtGui.QPixmap(resourcespath + "world_go.png"), getMessage("openusersstream-menu-label").format(shortUsername), lambda: self.openFile(filename, resetPosition=False, fromUser=True)) else: pathFound = self._syncplayClient.fileSwitch.findFilepath(filename) if pathFound: - menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound)) + menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound, resetPosition=False, fromUser=True)) if self._syncplayClient.isUntrustedTrustableURI(filename): domain = utils.getDomainFromURL(filename) menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) @@ -814,11 +814,11 @@ class MainWindow(QtWidgets.QMainWindow): if self._isTryingToChangeToCurrentFile(filename): return if isURL(filename): - self._syncplayClient._player.openFile(filename, resetPosition=True) + self._syncplayClient.openFile(filename, resetPosition=True) else: pathFound = self._syncplayClient.fileSwitch.findFilepath(filename, highPriority=True) if pathFound: - self._syncplayClient._player.openFile(pathFound, resetPosition=True) + self._syncplayClient.openFile(pathFound, resetPosition=True) else: self._syncplayClient.ui.showErrorMessage(getMessage("cannot-find-file-for-playlist-switch-error").format(filename)) @@ -841,11 +841,11 @@ class MainWindow(QtWidgets.QMainWindow): if self._isTryingToChangeToCurrentFile(filename): return if isURL(filename): - self._syncplayClient._player.openFile(filename) + self._syncplayClient.openFile(filename) else: pathFound = self._syncplayClient.fileSwitch.findFilepath(filename, highPriority=True) if pathFound: - self._syncplayClient._player.openFile(pathFound) + self._syncplayClient.openFile(pathFound) else: self._syncplayClient.fileSwitch.updateInfo() self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename)) @@ -1006,7 +1006,7 @@ class MainWindow(QtWidgets.QMainWindow): self.mediadirectory = os.path.dirname(fileName) self._syncplayClient.fileSwitch.setCurrentDirectory(self.mediadirectory) self.saveMediaBrowseSettings() - self._syncplayClient._player.openFile(fileName) + self._syncplayClient.openFile(fileName, resetPosition=False, fromUser=True) @needsClient def OpenAddFilesToPlaylistDialog(self): @@ -1186,7 +1186,7 @@ class MainWindow(QtWidgets.QMainWindow): self, getMessage("promptforstreamurl-msgbox-label"), getMessage("promptforstreamurlinfo-msgbox-label"), QtWidgets.QLineEdit.Normal, "") if ok and streamURL != '': - self._syncplayClient._player.openFile(streamURL) + self._syncplayClient.openFile(streamURL, resetPosition=False, fromUser=True) @needsClient def createControlledRoom(self): @@ -1803,10 +1803,10 @@ class MainWindow(QtWidgets.QMainWindow): else: dropfilepath = os.path.abspath(str(url.toLocalFile())) if rewindFile == False: - self._syncplayClient._player.openFile(dropfilepath) + self._syncplayClient.openFile(dropfilepath, resetPosition=False, fromUser=True) else: self._syncplayClient.setPosition(0) - self._syncplayClient._player.openFile(dropfilepath, resetPosition=True) + self._syncplayClient.openFile(dropfilepath, resetPosition=True, fromUser=True) self._syncplayClient.setPosition(0) def setPlaylist(self, newPlaylist, newIndexFilename=None): @@ -1848,8 +1848,8 @@ class MainWindow(QtWidgets.QMainWindow): else: self.playlist.insertItem(index, filePath) - def openFile(self, filePath, resetPosition=False): - self._syncplayClient._player.openFile(filePath, resetPosition) + def openFile(self, filePath, resetPosition=False, fromUser=False): + self._syncplayClient.openFile(filePath, resetPosition, fromUser=fromUser) def noPlaylistDuplicates(self, filename): if self.isItemInPlaylist(filename):