Initial code to move FileCheck search into separate thread + add caching
This commit is contained in:
parent
7bc9159d33
commit
6607cd5a2d
@ -1,4 +1,4 @@
|
|||||||
version = '1.3.3'
|
version = '1.3.3'
|
||||||
milestone = 'Chami'
|
milestone = 'Chami'
|
||||||
release_number = '19'
|
release_number = '20'
|
||||||
projectURL = 'http://syncplay.pl/'
|
projectURL = 'http://syncplay.pl/'
|
||||||
|
|||||||
@ -49,7 +49,11 @@ SERVER_STATE_INTERVAL = 1
|
|||||||
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
|
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
|
||||||
AUTOPLAY_DELAY = 3.0
|
AUTOPLAY_DELAY = 3.0
|
||||||
SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear on some media players
|
SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear on some media players
|
||||||
FOLDER_SEARCH_TIMEOUT = 2.0 # Secs
|
|
||||||
|
# Options for the File Switch feature:
|
||||||
|
FOLDER_SEARCH_TIMEOUT = 60.0 # Secs - How long to wait until searches in folder to update cache are aborted (may be longer than this if hard drive needs to spin up)
|
||||||
|
FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL = 120.0 # Secs - Frequency of updating cache when someone is playing a file not in current cache
|
||||||
|
MEDIA_CACHE_CHECK_INTERVAL = 0.2 # Secs - Frequency of checking for the cache being updated
|
||||||
|
|
||||||
#Usually there's no need to adjust these
|
#Usually there's no need to adjust these
|
||||||
LAST_PAUSED_DIFF_THRESHOLD = 2
|
LAST_PAUSED_DIFF_THRESHOLD = 2
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import re
|
|||||||
import os
|
import os
|
||||||
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
|
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from twisted.internet import task
|
||||||
|
import threading
|
||||||
lastCheckedForUpdates = None
|
lastCheckedForUpdates = None
|
||||||
|
|
||||||
class UserlistItemDelegate(QtGui.QStyledItemDelegate):
|
class UserlistItemDelegate(QtGui.QStyledItemDelegate):
|
||||||
@ -83,6 +85,153 @@ class UserlistItemDelegate(QtGui.QStyledItemDelegate):
|
|||||||
QtGui.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex)
|
QtGui.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex)
|
||||||
|
|
||||||
class MainWindow(QtGui.QMainWindow):
|
class MainWindow(QtGui.QMainWindow):
|
||||||
|
class FileSwitchManager(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.fileSwitchTimer = task.LoopingCall(self.updateInfo)
|
||||||
|
self.fileSwitchTimer.start(constants.FOLDER_SEARCH_DOUBLE_CHECK_INTERVAL, True)
|
||||||
|
self.fileCheckTimer = task.LoopingCall(self.checkForUpdate)
|
||||||
|
self.fileCheckTimer.start(constants.MEDIA_CACHE_CHECK_INTERVAL, True)
|
||||||
|
|
||||||
|
mediaFilesCache = {}
|
||||||
|
filenameWatchlist = []
|
||||||
|
currentDirectory = None
|
||||||
|
mediaDirectories = None
|
||||||
|
lock = threading.Lock()
|
||||||
|
client = None
|
||||||
|
currentWindow = None
|
||||||
|
folderSearchEnabled = True
|
||||||
|
disabledDir = None
|
||||||
|
newInfo = False
|
||||||
|
currentlyUpdating = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setWindow(window):
|
||||||
|
MainWindow.FileSwitchManager.currentWindow = window
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setClient(newClient):
|
||||||
|
MainWindow.FileSwitchManager.client = newClient
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setCurrentDirectory(curDir):
|
||||||
|
MainWindow.FileSwitchManager.currentDirectory = curDir
|
||||||
|
MainWindow.FileSwitchManager.updateInfo()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setMediaDirectories(mediaDirs):
|
||||||
|
MainWindow.FileSwitchManager.mediaDirectories = mediaDirs
|
||||||
|
MainWindow.FileSwitchManager.updateInfo()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def checkForUpdate():
|
||||||
|
if MainWindow.FileSwitchManager.newInfo:
|
||||||
|
MainWindow.FileSwitchManager.newInfo = False
|
||||||
|
MainWindow.FileSwitchManager.infoUpdated()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def updateInfo():
|
||||||
|
if len(MainWindow.FileSwitchManager.filenameWatchlist) > 0 or len(MainWindow.FileSwitchManager.mediaFilesCache) == 0 and MainWindow.FileSwitchManager.currentlyUpdating == False:
|
||||||
|
newThread = threading.Thread(target=MainWindow.FileSwitchManager._updateInfoThread)
|
||||||
|
newThread.setDaemon(True)
|
||||||
|
newThread.start()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setFilenameWatchlist(unfoundFilenames):
|
||||||
|
MainWindow.FileSwitchManager.filenameWatchlist = unfoundFilenames
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _updateInfoThread():
|
||||||
|
if not MainWindow.FileSwitchManager.folderSearchEnabled:
|
||||||
|
if MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||||
|
MainWindow.FileSwitchManager.newInfo = True
|
||||||
|
return
|
||||||
|
|
||||||
|
with MainWindow.FileSwitchManager.lock:
|
||||||
|
try:
|
||||||
|
MainWindow.FileSwitchManager.currentlyUpdating = True
|
||||||
|
dirsToSearch = MainWindow.FileSwitchManager.mediaDirectories
|
||||||
|
|
||||||
|
if dirsToSearch:
|
||||||
|
newMediaFilesCache = {}
|
||||||
|
startTime = time.time()
|
||||||
|
for directory in dirsToSearch:
|
||||||
|
for root, dirs, files in os.walk(directory):
|
||||||
|
newMediaFilesCache[root] = files
|
||||||
|
if time.time() - startTime > constants.FOLDER_SEARCH_TIMEOUT:
|
||||||
|
if MainWindow.FileSwitchManager.client is not None and MainWindow.FileSwitchManager.currentWindow is not None:
|
||||||
|
MainWindow.FileSwitchManager.disabledDir = directory
|
||||||
|
MainWindow.FileSwitchManager.folderSearchEnabled = False
|
||||||
|
if MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||||
|
MainWindow.FileSwitchManager.newInfo = True
|
||||||
|
return
|
||||||
|
|
||||||
|
if MainWindow.FileSwitchManager.mediaFilesCache <> newMediaFilesCache:
|
||||||
|
MainWindow.FileSwitchManager.mediaFilesCache = newMediaFilesCache
|
||||||
|
MainWindow.FileSwitchManager.newInfo = True
|
||||||
|
elif MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||||
|
MainWindow.FileSwitchManager.newInfo = True
|
||||||
|
finally:
|
||||||
|
MainWindow.FileSwitchManager.currentlyUpdating = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def infoUpdated():
|
||||||
|
if MainWindow.FileSwitchManager.areWatchedFilenamesInCache() or MainWindow.FileSwitchManager.areWatchedFilenamesInCurrentDir():
|
||||||
|
MainWindow.FileSwitchManager.updateListOfWhoIsPlayingWhat()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def updateListOfWhoIsPlayingWhat():
|
||||||
|
if MainWindow.FileSwitchManager.client is not None:
|
||||||
|
MainWindow.FileSwitchManager.client.showUserList()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def findFilepath(filename):
|
||||||
|
if filename is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if MainWindow.FileSwitchManager.currentDirectory is not None:
|
||||||
|
candidatePath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename)
|
||||||
|
if os.path.isfile(candidatePath):
|
||||||
|
return candidatePath
|
||||||
|
|
||||||
|
if MainWindow.FileSwitchManager.mediaFilesCache is not None:
|
||||||
|
for directory in MainWindow.FileSwitchManager.mediaFilesCache:
|
||||||
|
files = MainWindow.FileSwitchManager.mediaFilesCache[directory]
|
||||||
|
|
||||||
|
if len(files) > 0 and filename in files:
|
||||||
|
filepath = os.path.join(directory, filename)
|
||||||
|
if os.path.isfile(filepath):
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def areWatchedFilenamesInCurrentDir():
|
||||||
|
if MainWindow.FileSwitchManager.filenameWatchlist is not None and MainWindow.FileSwitchManager.currentDirectory is not None:
|
||||||
|
for filename in MainWindow.FileSwitchManager.filenameWatchlist:
|
||||||
|
potentialPath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename)
|
||||||
|
if os.path.isfile(potentialPath):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def areWatchedFilenamesInCache():
|
||||||
|
if MainWindow.FileSwitchManager.filenameWatchlist is not None:
|
||||||
|
for filename in MainWindow.FileSwitchManager.filenameWatchlist:
|
||||||
|
if MainWindow.FileSwitchManager.isFilenameInCache(filename):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def isFilenameInCurrentDir(filename):
|
||||||
|
if filename is not None and MainWindow.FileSwitchManager.currentDirectory is not None:
|
||||||
|
potentialPath = os.path.join(MainWindow.FileSwitchManager.currentDirectory,filename)
|
||||||
|
if os.path.isfile(potentialPath):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def isFilenameInCache(filename):
|
||||||
|
if filename is not None and MainWindow.FileSwitchManager.mediaFilesCache is not None:
|
||||||
|
for directory in MainWindow.FileSwitchManager.mediaFilesCache:
|
||||||
|
files = MainWindow.FileSwitchManager.mediaFilesCache[directory]
|
||||||
|
if filename in files:
|
||||||
|
return True
|
||||||
|
|
||||||
class topSplitter(QtGui.QSplitter):
|
class topSplitter(QtGui.QSplitter):
|
||||||
def createHandle(self):
|
def createHandle(self):
|
||||||
return self.topSplitterHandle(self.orientation(), self)
|
return self.topSplitterHandle(self.orientation(), self)
|
||||||
@ -107,9 +256,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def addClient(self, client):
|
def addClient(self, client):
|
||||||
self._syncplayClient = client
|
self._syncplayClient = client
|
||||||
|
MainWindow.FileSwitchManager.setClient(client)
|
||||||
self.roomInput.setText(self._syncplayClient.getRoom())
|
self.roomInput.setText(self._syncplayClient.getRoom())
|
||||||
self.config = self._syncplayClient.getConfig()
|
self.config = self._syncplayClient.getConfig()
|
||||||
try:
|
try:
|
||||||
|
self.FileSwitchManager.setMediaDirectories(self.config["mediaSearchDirectories"])
|
||||||
self.updateReadyState(self.config['readyAtStart'])
|
self.updateReadyState(self.config['readyAtStart'])
|
||||||
autoplayInitialState = self.config['autoplayInitialState']
|
autoplayInitialState = self.config['autoplayInitialState']
|
||||||
if autoplayInitialState is not None:
|
if autoplayInitialState is not None:
|
||||||
@ -150,28 +301,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
return constants.FILEITEM_SWITCH_NO_SWITCH
|
||||||
if isURL(filename):
|
if isURL(filename):
|
||||||
return constants.FILEITEM_SWITCH_STREAM_SWITCH
|
return constants.FILEITEM_SWITCH_STREAM_SWITCH
|
||||||
else:
|
elif filename not in self.newWatchlist:
|
||||||
currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None
|
if MainWindow.FileSwitchManager.findFilepath(filename):
|
||||||
if self.folderSearchEnabled:
|
|
||||||
try:
|
|
||||||
filenamesInDirectories = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"])
|
|
||||||
except IOError as errorMessage:
|
|
||||||
self.showErrorMessage(errorMessage)
|
|
||||||
filenamesInDirectories = None
|
|
||||||
self.folderSearchEnabled = False
|
|
||||||
else:
|
|
||||||
filenamesInDirectories = None
|
|
||||||
if filenamesInDirectories:
|
|
||||||
return constants.FILEITEM_SWITCH_FILE_SWITCH
|
return constants.FILEITEM_SWITCH_FILE_SWITCH
|
||||||
elif currentPath:
|
|
||||||
currentDirectory = os.path.dirname(currentPath)
|
|
||||||
newPath = os.path.join(currentDirectory, filename)
|
|
||||||
if os.path.isfile(newPath):
|
|
||||||
return constants.FILEITEM_SWITCH_FILE_SWITCH
|
|
||||||
else:
|
|
||||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
|
||||||
else:
|
else:
|
||||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
self.newWatchlist.extend([filename])
|
||||||
return constants.FILEITEM_SWITCH_NO_SWITCH
|
return constants.FILEITEM_SWITCH_NO_SWITCH
|
||||||
|
|
||||||
def showUserList(self, currentUser, rooms):
|
def showUserList(self, currentUser, rooms):
|
||||||
@ -179,8 +313,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self._usertreebuffer.setHorizontalHeaderLabels(
|
self._usertreebuffer.setHorizontalHeaderLabels(
|
||||||
(getMessage("roomuser-heading-label"), getMessage("size-heading-label"), getMessage("duration-heading-label"), getMessage("filename-heading-label") ))
|
(getMessage("roomuser-heading-label"), getMessage("size-heading-label"), getMessage("duration-heading-label"), getMessage("filename-heading-label") ))
|
||||||
usertreeRoot = self._usertreebuffer.invisibleRootItem()
|
usertreeRoot = self._usertreebuffer.invisibleRootItem()
|
||||||
|
if self._syncplayClient.userlist.currentUser.file and self._syncplayClient.userlist.currentUser.file and os.path.isfile(self._syncplayClient.userlist.currentUser.file["path"]):
|
||||||
|
MainWindow.FileSwitchManager.setCurrentDirectory(os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]))
|
||||||
|
|
||||||
for room in rooms:
|
for room in rooms:
|
||||||
|
self.newWatchlist = []
|
||||||
roomitem = QtGui.QStandardItem(room)
|
roomitem = QtGui.QStandardItem(room)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setItalic(True)
|
font.setItalic(True)
|
||||||
@ -266,6 +403,13 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.listTreeView.setRootIsDecorated(False)
|
self.listTreeView.setRootIsDecorated(False)
|
||||||
self.listTreeView.expandAll()
|
self.listTreeView.expandAll()
|
||||||
self.updateListGeometry()
|
self.updateListGeometry()
|
||||||
|
MainWindow.FileSwitchManager.setFilenameWatchlist(self.newWatchlist)
|
||||||
|
self.checkForDisabledDir()
|
||||||
|
|
||||||
|
def checkForDisabledDir(self):
|
||||||
|
if MainWindow.FileSwitchManager.disabledDir is not None and MainWindow.FileSwitchManager.currentWindow is not None:
|
||||||
|
self.showErrorMessage(getMessage("folder-search-timeout-error").format(MainWindow.FileSwitchManager.disabledDir))
|
||||||
|
MainWindow.FileSwitchManager.disabledDir = None
|
||||||
|
|
||||||
def updateListGeometry(self):
|
def updateListGeometry(self):
|
||||||
try:
|
try:
|
||||||
@ -309,26 +453,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
if isURL(filename):
|
if isURL(filename):
|
||||||
self._syncplayClient._player.openFile(filename)
|
self._syncplayClient._player.openFile(filename)
|
||||||
else:
|
else:
|
||||||
currentPath = self._syncplayClient.userlist.currentUser.file["path"] if self._syncplayClient.userlist.currentUser.file else None
|
pathFound = MainWindow.FileSwitchManager.findFilepath(filename)
|
||||||
if self.folderSearchEnabled:
|
|
||||||
try:
|
|
||||||
pathFound = utils.findFilenameInDirectories(filename, self.config["mediaSearchDirectories"])
|
|
||||||
except IOError as errorMessage:
|
|
||||||
self.showErrorMessage(errorMessage)
|
|
||||||
pathFound = None
|
|
||||||
self.folderSearchEnabled = False
|
|
||||||
else:
|
|
||||||
pathFound = None
|
|
||||||
if pathFound:
|
if pathFound:
|
||||||
self._syncplayClient._player.openFile(pathFound)
|
self._syncplayClient._player.openFile(pathFound)
|
||||||
elif currentPath:
|
|
||||||
currentDirectory = os.path.dirname(currentPath)
|
|
||||||
newPath = os.path.join(currentDirectory, filename)
|
|
||||||
if os.path.isfile(newPath):
|
|
||||||
self._syncplayClient._player.openFile(newPath)
|
|
||||||
else:
|
|
||||||
self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename, currentDirectory))
|
|
||||||
else:
|
else:
|
||||||
|
MainWindow.FileSwitchManager.updateInfo()
|
||||||
self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename))
|
self.showErrorMessage(getMessage("switch-file-not-found-error").format(filename))
|
||||||
|
|
||||||
@needsClient
|
@needsClient
|
||||||
@ -455,6 +584,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
fileName = fileName.replace("/", "\\")
|
fileName = fileName.replace("/", "\\")
|
||||||
self.mediadirectory = os.path.dirname(fileName)
|
self.mediadirectory = os.path.dirname(fileName)
|
||||||
|
self.FileSwitchManager.setCurrentDirectory(self.mediadirectory)
|
||||||
self.saveMediaBrowseSettings()
|
self.saveMediaBrowseSettings()
|
||||||
self._syncplayClient._player.openFile(fileName)
|
self._syncplayClient._player.openFile(fileName)
|
||||||
|
|
||||||
@ -785,7 +915,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self._syncplayClient.changeReadyState(self.readyPushButton.isChecked())
|
self._syncplayClient.changeReadyState(self.readyPushButton.isChecked())
|
||||||
else:
|
else:
|
||||||
self.showDebugMessage("Tried to change ready state too soon.")
|
self.showDebugMessage("Tried to change ready state too soon.")
|
||||||
|
|
||||||
@needsClient
|
@needsClient
|
||||||
def changeAutoplayThreshold(self, source=None):
|
def changeAutoplayThreshold(self, source=None):
|
||||||
self._syncplayClient.changeAutoPlayThrehsold(self.autoplayThresholdSpinbox.value())
|
self._syncplayClient.changeAutoPlayThrehsold(self.autoplayThresholdSpinbox.value())
|
||||||
@ -931,6 +1061,9 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
|
FileSwitchManager = self.FileSwitchManager()
|
||||||
|
FileSwitchManager.setWindow(self)
|
||||||
|
self.newWatchlist = []
|
||||||
self.publicServerList = []
|
self.publicServerList = []
|
||||||
self.lastCheckedForUpdates = None
|
self.lastCheckedForUpdates = None
|
||||||
self._syncplayClient = None
|
self._syncplayClient = None
|
||||||
|
|||||||
@ -250,17 +250,6 @@ def getListAsMultilineString(pathArray):
|
|||||||
def convertMultilineStringToList(multilineString):
|
def convertMultilineStringToList(multilineString):
|
||||||
return unicode.split(multilineString,u"\n") if multilineString else ""
|
return unicode.split(multilineString,u"\n") if multilineString else ""
|
||||||
|
|
||||||
def findFilenameInDirectories(filename, directoryList):
|
|
||||||
if filename and directoryList:
|
|
||||||
startTime = time.time()
|
|
||||||
for directory in directoryList:
|
|
||||||
for root, dirs, files in os.walk(directory):
|
|
||||||
if filename in files:
|
|
||||||
return os.path.join(root,filename)
|
|
||||||
if time.time() - startTime > constants.FOLDER_SEARCH_TIMEOUT:
|
|
||||||
raise IOError(getMessage("folder-search-timeout-error").format(directory))
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getListOfPublicServers():
|
def getListOfPublicServers():
|
||||||
try:
|
try:
|
||||||
import urllib, syncplay, sys, messages, json
|
import urllib, syncplay, sys, messages, json
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user