Formatted the client.py

This commit is contained in:
Uriziel 2014-03-26 22:47:16 +01:00
parent d4f4342642
commit 496635848f

View File

@ -1,4 +1,3 @@
#coding:utf8
import hashlib import hashlib
import os.path import os.path
import time import time
@ -9,7 +8,7 @@ from syncplay.protocols import SyncClientProtocol
from syncplay import utils, constants from syncplay import utils, constants
from syncplay.messages import getMessage from syncplay.messages import getMessage
import threading import threading
from syncplay.constants import PRIVACY_SENDHASHED_MODE, PRIVACY_DONTSEND_MODE,\ from syncplay.constants import PRIVACY_SENDHASHED_MODE, PRIVACY_DONTSEND_MODE, \
PRIVACY_HIDDENFILENAME, FILENAME_STRIP_REGEX PRIVACY_HIDDENFILENAME, FILENAME_STRIP_REGEX
# <MAL DISABLE> # <MAL DISABLE>
libMal = None libMal = None
@ -19,13 +18,14 @@ except ImportError:
libMal = None libMal = None
''' '''
# </MAL DISABLE> # </MAL DISABLE>
class SyncClientFactory(ClientFactory): class SyncClientFactory(ClientFactory):
def __init__(self, client, retry = constants.RECONNECT_RETRIES): def __init__(self, client, retry=constants.RECONNECT_RETRIES):
self._client = client self._client = client
self.retry = retry self.retry = retry
self._timesTried = 0 self._timesTried = 0
self.reconnecting = False self.reconnecting = False
def buildProtocol(self, addr): def buildProtocol(self, addr):
self._timesTried = 0 self._timesTried = 0
return SyncClientProtocol(self._client) return SyncClientProtocol(self._client)
@ -42,7 +42,7 @@ class SyncClientFactory(ClientFactory):
self._timesTried += 1 self._timesTried += 1
self._client.ui.showMessage(getMessage("en", "reconnection-attempt-notification")) self._client.ui.showMessage(getMessage("en", "reconnection-attempt-notification"))
self.reconnecting = True self.reconnecting = True
reactor.callLater(0.1*(2**self._timesTried), connector.connect) reactor.callLater(0.1 * (2 ** self._timesTried), connector.connect)
else: else:
message = getMessage("en", "disconnection-notification") message = getMessage("en", "disconnection-notification")
self._client.ui.showErrorMessage(message) self._client.ui.showErrorMessage(message)
@ -52,13 +52,13 @@ class SyncClientFactory(ClientFactory):
self._client.ui.showErrorMessage(getMessage("en", "connection-failed-notification")) self._client.ui.showErrorMessage(getMessage("en", "connection-failed-notification"))
else: else:
self.clientConnectionLost(connector, reason) self.clientConnectionLost(connector, reason)
def resetRetrying(self): def resetRetrying(self):
self._timesTried = 0 self._timesTried = 0
def stopRetrying(self): def stopRetrying(self):
self._timesTried = self.retry self._timesTried = self.retry
class SyncplayClient(object): class SyncplayClient(object):
def __init__(self, playerClass, ui, config): def __init__(self, playerClass, ui, config):
self.protocolFactory = SyncClientFactory(self) self.protocolFactory = SyncClientFactory(self)
@ -68,7 +68,7 @@ class SyncplayClient(object):
self._player = None self._player = None
self.givenmalprivacywarning = False self.givenmalprivacywarning = False
if(config['room'] == None or config['room'] == ''): if(config['room'] == None or config['room'] == ''):
config['room'] = config['name'] # ticket #58 config['room'] = config['name'] # ticket #58
self.defaultRoom = config['room'] self.defaultRoom = config['room']
self.playerPositionBeforeLastSeek = 0.0 self.playerPositionBeforeLastSeek = 0.0
self.setUsername(config['name']) self.setUsername(config['name'])
@ -82,39 +82,39 @@ class SyncplayClient(object):
self.__getUserlistOnLogon = False self.__getUserlistOnLogon = False
self._playerClass = playerClass self._playerClass = playerClass
self._config = config self._config = config
self._running = False self._running = False
self._askPlayerTimer = None self._askPlayerTimer = None
self._lastPlayerUpdate = None self._lastPlayerUpdate = None
self._playerPosition = 0.0 self._playerPosition = 0.0
self._playerPaused = True self._playerPaused = True
self._lastGlobalUpdate = None self._lastGlobalUpdate = None
self._globalPosition = 0.0 self._globalPosition = 0.0
self._globalPaused = 0.0 self._globalPaused = 0.0
self._userOffset = 0.0 self._userOffset = 0.0
self._speedChanged = False self._speedChanged = False
self._warnings = self._WarningManager(self._player, self.userlist, self.ui) self._warnings = self._WarningManager(self._player, self.userlist, self.ui)
self._malUpdater = MalUpdater(config["malUsername"], config["malPassword"], self.ui) self._malUpdater = MalUpdater(config["malUsername"], config["malPassword"], self.ui)
def initProtocol(self, protocol): def initProtocol(self, protocol):
self._protocol = protocol self._protocol = protocol
def destroyProtocol(self): def destroyProtocol(self):
if(self._protocol): if(self._protocol):
self._protocol.drop() self._protocol.drop()
self._protocol = None self._protocol = None
def initPlayer(self, player): def initPlayer(self, player):
self._player = player self._player = player
self.scheduleAskPlayer() self.scheduleAskPlayer()
def scheduleAskPlayer(self, when=constants.PLAYER_ASK_DELAY): def scheduleAskPlayer(self, when=constants.PLAYER_ASK_DELAY):
self._askPlayerTimer = task.LoopingCall(self.askPlayer) self._askPlayerTimer = task.LoopingCall(self.askPlayer)
self._askPlayerTimer.start(when) self._askPlayerTimer.start(when)
def askPlayer(self): def askPlayer(self):
if(not self._running): if(not self._running):
return return
@ -135,7 +135,7 @@ class SyncplayClient(object):
_playerDiff = abs(self.getPlayerPosition() - position) _playerDiff = abs(self.getPlayerPosition() - position)
_globalDiff = abs(self.getGlobalPosition() - position) _globalDiff = abs(self.getGlobalPosition() - position)
seeked = _playerDiff > constants.SEEK_THRESHOLD and _globalDiff > constants.SEEK_THRESHOLD seeked = _playerDiff > constants.SEEK_THRESHOLD and _globalDiff > constants.SEEK_THRESHOLD
return pauseChange, seeked return pauseChange, seeked
def updatePlayerStatus(self, paused, position): def updatePlayerStatus(self, paused, position):
position -= self.getUserOffset() position -= self.getUserOffset()
@ -147,7 +147,7 @@ class SyncplayClient(object):
if((pauseChange or seeked) and self._protocol): if((pauseChange or seeked) and self._protocol):
if(seeked): if(seeked):
self.playerPositionBeforeLastSeek = self.getGlobalPosition() self.playerPositionBeforeLastSeek = self.getGlobalPosition()
self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), seeked, None, True) self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), seeked, None, True)
def getLocalState(self): def getLocalState(self):
paused = self.getPlayerPaused() paused = self.getPlayerPaused()
@ -157,7 +157,7 @@ class SyncplayClient(object):
return position, paused, _, pauseChange return position, paused, _, pauseChange
else: else:
return None, None, None, None return None, None, None, None
def _initPlayerState(self, position, paused): def _initPlayerState(self, position, paused):
if(self.userlist.currentUser.file): if(self.userlist.currentUser.file):
self.setPosition(position) self.setPosition(position)
@ -246,10 +246,10 @@ class SyncplayClient(object):
if(madeChangeOnPlayer): if(madeChangeOnPlayer):
self.askPlayer() self.askPlayer()
self._executePlaystateHooks(position, paused, doSeek, setBy, messageAge) self._executePlaystateHooks(position, paused, doSeek, setBy, messageAge)
def getUserOffset(self): def getUserOffset(self):
return self._userOffset return self._userOffset
def setUserOffset(self, time): def setUserOffset(self, time):
self._userOffset = time self._userOffset = time
self.setPosition(self.getGlobalPosition()) self.setPosition(self.getGlobalPosition())
@ -273,7 +273,7 @@ class SyncplayClient(object):
position = self._playerPosition position = self._playerPosition
if(not self._playerPaused): if(not self._playerPaused):
diff = time.time() - self._lastPlayerUpdate diff = time.time() - self._lastPlayerUpdate
position += diff position += diff
return position return position
def getPlayerPaused(self): def getPlayerPaused(self):
@ -282,8 +282,8 @@ class SyncplayClient(object):
return self.getGlobalPaused() return self.getGlobalPaused()
else: else:
return True return True
return self._playerPaused return self._playerPaused
def getGlobalPosition(self): def getGlobalPosition(self):
if not self._lastGlobalUpdate: if not self._lastGlobalUpdate:
return 0.0 return 0.0
@ -291,18 +291,18 @@ class SyncplayClient(object):
if not self._globalPaused: if not self._globalPaused:
position += time.time() - self._lastGlobalUpdate position += time.time() - self._lastGlobalUpdate
return position return position
def getGlobalPaused(self): def getGlobalPaused(self):
if(not self._lastGlobalUpdate): if(not self._lastGlobalUpdate):
return True return True
return self._globalPaused return self._globalPaused
def updateFile(self, filename, duration, path): def updateFile(self, filename, duration, path):
if not path: if not path:
return return
try: try:
size = os.path.getsize(path) size = os.path.getsize(path)
except OSError: #file not accessible (stream?) except OSError: # file not accessible (stream?)
size = 0 size = 0
rawfilename = filename rawfilename = filename
filename, size = self.__executePrivacySettings(filename, size) filename, size = self.__executePrivacySettings(filename, size)
@ -313,11 +313,11 @@ class SyncplayClient(object):
message = getMessage("en", "mal-noprivacy-notification") message = getMessage("en", "mal-noprivacy-notification")
self.ui.showErrorMessage(message) self.ui.showErrorMessage(message)
self.givenmalprivacywarning = True self.givenmalprivacywarning = True
def __executePrivacySettings(self, filename, size): def __executePrivacySettings(self, filename, size):
if (self._config['filenamePrivacyMode'] == PRIVACY_SENDHASHED_MODE): if (self._config['filenamePrivacyMode'] == PRIVACY_SENDHASHED_MODE):
filename = utils.hashFilename(filename) filename = utils.hashFilename(filename)
elif (self._config['filenamePrivacyMode'] == PRIVACY_DONTSEND_MODE): elif (self._config['filenamePrivacyMode'] == PRIVACY_DONTSEND_MODE):
filename = PRIVACY_HIDDENFILENAME filename = PRIVACY_HIDDENFILENAME
if (self._config['filesizePrivacyMode'] == PRIVACY_SENDHASHED_MODE): if (self._config['filesizePrivacyMode'] == PRIVACY_SENDHASHED_MODE):
@ -333,44 +333,44 @@ class SyncplayClient(object):
def setUsername(self, username): def setUsername(self, username):
self.userlist.currentUser.username = username self.userlist.currentUser.username = username
def getUsername(self): def getUsername(self):
return self.userlist.currentUser.username return self.userlist.currentUser.username
def setRoom(self, roomName): def setRoom(self, roomName):
self.userlist.currentUser.room = roomName self.userlist.currentUser.room = roomName
def sendRoom(self): def sendRoom(self):
room = self.userlist.currentUser.room room = self.userlist.currentUser.room
if(self._protocol and self._protocol.logged and room): if(self._protocol and self._protocol.logged and room):
self._protocol.sendRoomSetting(room) self._protocol.sendRoomSetting(room)
self.getUserList() self.getUserList()
def getRoom(self): def getRoom(self):
return self.userlist.currentUser.room return self.userlist.currentUser.room
def getUserList(self): def getUserList(self):
if(self._protocol and self._protocol.logged): if(self._protocol and self._protocol.logged):
self._protocol.sendList() self._protocol.sendList()
def showUserList(self): def showUserList(self):
self.userlist.showUserList() self.userlist.showUserList()
def getPassword(self): def getPassword(self):
return self._serverPassword return self._serverPassword
def setPosition(self, position): def setPosition(self, position):
position += self.getUserOffset() position += self.getUserOffset()
if(self._player and self.userlist.currentUser.file): if(self._player and self.userlist.currentUser.file):
if(position < 0): if(position < 0):
position = 0 position = 0
self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), True, None, True) self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), True, None, True)
self._player.setPosition(position) self._player.setPosition(position)
def setPaused(self, paused): def setPaused(self, paused):
if(self._player and self.userlist.currentUser.file): if(self._player and self.userlist.currentUser.file):
self._player.setPaused(paused) self._player.setPaused(paused)
def start(self, host, port): def start(self, host, port):
if self._running: if self._running:
return return
@ -382,7 +382,7 @@ class SyncplayClient(object):
reactor.connectTCP(host, port, self.protocolFactory) reactor.connectTCP(host, port, self.protocolFactory)
reactor.run() reactor.run()
def stop(self, promptForAction = False): def stop(self, promptForAction=False):
if not self._running: if not self._running:
return return
self._running = False self._running = False
@ -415,7 +415,7 @@ class SyncplayClient(object):
def checkWarnings(self): def checkWarnings(self):
self._checkIfYouReAloneInTheRoom() self._checkIfYouReAloneInTheRoom()
self._checkRoomForSameFiles() self._checkRoomForSameFiles()
def _checkRoomForSameFiles(self): def _checkRoomForSameFiles(self):
if (not self._userlist.areAllFilesInRoomSame()): if (not self._userlist.areAllFilesInRoomSame()):
self._ui.showMessage(getMessage("en", "room-files-not-same"), True) self._ui.showMessage(getMessage("en", "room-files-not-same"), True)
@ -423,7 +423,7 @@ class SyncplayClient(object):
self._warnings["room-files-not-same"]['timer'].start(constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, True) self._warnings["room-files-not-same"]['timer'].start(constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, True)
elif(self._warnings["room-files-not-same"]['timer'].running): elif(self._warnings["room-files-not-same"]['timer'].running):
self._warnings["room-files-not-same"]['timer'].stop() self._warnings["room-files-not-same"]['timer'].stop()
def _checkIfYouReAloneInTheRoom(self): def _checkIfYouReAloneInTheRoom(self):
if (self._userlist.areYouAloneInRoom()): if (self._userlist.areYouAloneInRoom()):
self._ui.showMessage(getMessage("en", "alone-in-the-room"), True) self._ui.showMessage(getMessage("en", "alone-in-the-room"), True)
@ -431,24 +431,24 @@ class SyncplayClient(object):
self._warnings["alone-in-the-room"]['timer'].start(constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, True) self._warnings["alone-in-the-room"]['timer'].start(constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, True)
elif(self._warnings["alone-in-the-room"]['timer'].running): elif(self._warnings["alone-in-the-room"]['timer'].running):
self._warnings["alone-in-the-room"]['timer'].stop() self._warnings["alone-in-the-room"]['timer'].stop()
def __displayMessageOnOSD(self, warningName): def __displayMessageOnOSD(self, warningName):
if (constants.OSD_WARNING_MESSAGE_DURATION > self._warnings[warningName]["displayedFor"]): if (constants.OSD_WARNING_MESSAGE_DURATION > self._warnings[warningName]["displayedFor"]):
self._ui.showOSDMessage(getMessage("en", warningName ), constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL) self._ui.showOSDMessage(getMessage("en", warningName), constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL)
self._warnings[warningName]["displayedFor"] += constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL self._warnings[warningName]["displayedFor"] += constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL
else: else:
self._warnings[warningName]["displayedFor"] = 0 self._warnings[warningName]["displayedFor"] = 0
self._warnings[warningName]["timer"].stop() self._warnings[warningName]["timer"].stop()
class SyncplayUser(object): class SyncplayUser(object):
def __init__(self, username = None, room = None, file_ = None, position = 0): def __init__(self, username=None, room=None, file_=None, position=0):
self.username = username self.username = username
self.room = room self.room = room
self.file = file_ self.file = file_
self.lastPosition = position self.lastPosition = position
def setFile(self, filename, duration, size): def setFile(self, filename, duration, size):
file_ = { file_ = {
"name": filename, "name": filename,
@ -456,19 +456,19 @@ class SyncplayUser(object):
"size":size "size":size
} }
self.file = file_ self.file = file_
def isFileSame(self, file_): def isFileSame(self, file_):
if(not self.file): if(not self.file):
return False return False
sameName = utils.sameFilename(self.file['name'], file_['name']) sameName = utils.sameFilename(self.file['name'], file_['name'])
sameSize = utils.sameFilesize(self.file['size'], file_['size']) sameSize = utils.sameFilesize(self.file['size'], file_['size'])
sameDuration = utils.sameFileduration(self.file['duration'], file_['duration']) sameDuration = utils.sameFileduration(self.file['duration'], file_['duration'])
return sameName and sameSize and sameDuration return sameName and sameSize and sameDuration
def __lt__(self, other): def __lt__(self, other):
return self.username < other.username return self.username < other.username
class MalUpdater(object): class MalUpdater(object):
def __init__(self, username, password, ui): def __init__(self, username, password, ui):
self._filePlayingFor = 0.0 self._filePlayingFor = 0.0
@ -499,7 +499,7 @@ class MalUpdater(object):
self._filename = filename self._filename = filename
self._filePlayingFor = 0.0 self._filePlayingFor = 0.0
self._lastHookUpdate = None self._lastHookUpdate = None
def _updateMal(self): def _updateMal(self):
try: try:
self._fileDuration = 0 # Disable playingHook self._fileDuration = 0 # Disable playingHook
@ -509,13 +509,13 @@ class MalUpdater(object):
if(len(results) > 0): if(len(results) > 0):
result = results[0] result = results[0]
message = "Updating MAL with: \"{}\", episode: {}".format(result.mainTitle, result.episodeBeingWatched) message = "Updating MAL with: \"{}\", episode: {}".format(result.mainTitle, result.episodeBeingWatched)
reactor.callFromThread(self._ui.showMessage,(message),) reactor.callFromThread(self._ui.showMessage, (message),)
options = {"tags": ["syncplay"]} options = {"tags": ["syncplay"]}
manager.updateEntryOnMal(result, options) manager.updateEntryOnMal(result, options)
self._filename = "" # Make sure no updates will be performed until switch self._filename = "" # Make sure no updates will be performed until switch
except: except:
reactor.callFromThread(self._ui.showMessage, ("MAL Update failure"),) reactor.callFromThread(self._ui.showMessage, ("MAL Update failure"),)
class SyncplayUserlist(object): class SyncplayUserlist(object):
def __init__(self, ui, client): def __init__(self, ui, client):
self.currentUser = SyncplayUser() self.currentUser = SyncplayUser()
@ -542,7 +542,7 @@ class SyncplayUserlist(object):
differentSize = not utils.sameFilesize(self.currentUser.file['size'], file_['size']) differentSize = not utils.sameFilesize(self.currentUser.file['size'], file_['size'])
differentDuration = not utils.sameFileduration(self.currentUser.file['duration'], file_['duration']) differentDuration = not utils.sameFileduration(self.currentUser.file['duration'], file_['duration'])
if(differentName): if(differentName):
differences.append("filename") differences.append("filename")
if(differentSize): if(differentSize):
differences.append("size") differences.append("size")
if(differentDuration): if(differentDuration):
@ -550,7 +550,7 @@ class SyncplayUserlist(object):
message = getMessage("en", "file-differences-notification") + ", ".join(differences) message = getMessage("en", "file-differences-notification") + ", ".join(differences)
self.ui.showMessage(message) self.ui.showMessage(message)
def addUser(self, username, room, file_, position = 0, noMessage = False): def addUser(self, username, room, file_, position=0, noMessage=False):
if(username == self.currentUser.username): if(username == self.currentUser.username):
self.currentUser.lastPosition = position self.currentUser.lastPosition = position
return return
@ -559,14 +559,14 @@ class SyncplayUserlist(object):
if(not noMessage): if(not noMessage):
self.__showUserChangeMessage(username, room, file_) self.__showUserChangeMessage(username, room, file_)
self.userListChange() self.userListChange()
def removeUser(self, username): def removeUser(self, username):
if(self._users.has_key(username)): if(self._users.has_key(username)):
self._users.pop(username) self._users.pop(username)
message = getMessage("en", "left-notification").format(username) message = getMessage("en", "left-notification").format(username)
self.ui.showMessage(message) self.ui.showMessage(message)
self.userListChange() self.userListChange()
def __displayModUserMessage(self, username, room, file_, user): def __displayModUserMessage(self, username, room, file_, user):
if (file_ and not user.isFileSame(file_)): if (file_ and not user.isFileSame(file_)):
self.__showUserChangeMessage(username, room, file_) self.__showUserChangeMessage(username, room, file_)
@ -585,13 +585,13 @@ class SyncplayUserlist(object):
else: else:
self.addUser(username, room, file_) self.addUser(username, room, file_)
self.userListChange() self.userListChange()
def areAllFilesInRoomSame(self): def areAllFilesInRoomSame(self):
for user in self._users.itervalues(): for user in self._users.itervalues():
if(user.room == self.currentUser.room and user.file and not self.currentUser.isFileSame(user.file)): if(user.room == self.currentUser.room and user.file and not self.currentUser.isFileSame(user.file)):
return False return False
return True return True
def areYouAloneInRoom(self): def areYouAloneInRoom(self):
for user in self._users.itervalues(): for user in self._users.itervalues():
if(user.room == self.currentUser.room): if(user.room == self.currentUser.room):
@ -607,13 +607,13 @@ class SyncplayUserlist(object):
def userListChange(self): def userListChange(self):
self._roomUsersChanged = True self._roomUsersChanged = True
self.ui.userListChange() self.ui.userListChange()
def roomStateConfirmed(self): def roomStateConfirmed(self):
self._roomUsersChanged = False self._roomUsersChanged = False
def hasRoomStateChanged(self): def hasRoomStateChanged(self):
return self._roomUsersChanged return self._roomUsersChanged
def showUserList(self): def showUserList(self):
rooms = {} rooms = {}
for user in self._users.itervalues(): for user in self._users.itervalues():
@ -623,41 +623,44 @@ class SyncplayUserlist(object):
if(self.currentUser.room not in rooms): if(self.currentUser.room not in rooms):
rooms[self.currentUser.room] = [] rooms[self.currentUser.room] = []
rooms[self.currentUser.room].append(self.currentUser) rooms[self.currentUser.room].append(self.currentUser)
self.ui.showUserList(self.currentUser, rooms) self.ui.showUserList(self.currentUser, rooms)
def clearList(self): def clearList(self):
self._users = {} self._users = {}
def sortList(self):
pass
class UiManager(object): class UiManager(object):
def __init__(self, client, ui): def __init__(self, client, ui):
self._client = client self._client = client
self.__ui = ui self.__ui = ui
def showMessage(self, message, noPlayer = False, noTimestamp = False): def showMessage(self, message, noPlayer=False, noTimestamp=False):
if(not noPlayer): self.showOSDMessage(message) if(not noPlayer): self.showOSDMessage(message)
self.__ui.showMessage(message, noTimestamp) self.__ui.showMessage(message, noTimestamp)
def showUserList(self, currentUser, rooms): def showUserList(self, currentUser, rooms):
self.__ui.showUserList(currentUser, rooms) self.__ui.showUserList(currentUser, rooms)
def showOSDMessage(self, message, duration = constants.OSD_DURATION): def showOSDMessage(self, message, duration=constants.OSD_DURATION):
if(self._client._player): if(self._client._player):
self._client._player.displayMessage(message, duration * 1000) self._client._player.displayMessage(message, duration * 1000)
def showErrorMessage(self, message, criticalerror = False): def showErrorMessage(self, message, criticalerror=False):
self.__ui.showErrorMessage(message, criticalerror) self.__ui.showErrorMessage(message, criticalerror)
def promptFor(self, prompt): def promptFor(self, prompt):
return self.__ui.promptFor(prompt) return self.__ui.promptFor(prompt)
def userListChange(self): def userListChange(self):
self.__ui.userListChange() self.__ui.userListChange()
def markEndOfUserlist(self): def markEndOfUserlist(self):
self.__ui.markEndOfUserlist() self.__ui.markEndOfUserlist()
def drop(self): def drop(self):
self.__ui.drop() self.__ui.drop()