force scanfolder
#16
EDIT: I added more comments in the code example to hopefully make it clearer.

I included the sleep in my code as a demo to demonstrate how it works in a fully runnable example. I have no idea exactly how the OP intends to use the routine so I wanted to ensure that the .isupdating variable gets updated, regardless of what he is doing in his main thread. I have nothing to do in my main thread in an example. I could have written an infinite loop to keep things alive instead (in the following example I use time.sleep to keep it alive)...

And in my experience, if the main thread does not release control by sleeping with xbmc.sleep, the event will not be executed.

For example:

Code:
import threading
import time
import xbmc
dbmonitor = None


class XbmcMonitor (xbmc.Monitor):

    def __init__(self):
        xbmc.Monitor.__init__(self)
        self.isupdating = False

    def onDatabaseScanStarted(self, database):
        self.isupdating = True
        xbmc.log('######## Scan started thread: %s' % str(threading.current_thread().ident), level=xbmc.LOGNOTICE)

    def onDatabaseUpdated(self, database):
        self.isupdating = False
        xbmc.log('######## Scan ended thread: %s' % str(threading.current_thread().ident), level=xbmc.LOGNOTICE)


def threadstart():
    global dbmonitor
    dbmonitor = XbmcMonitor()
    while not xbmc.abortRequested:
        xbmc.sleep(250)


def main():

    # test_cond = 1: Use threading and xbmc.sleep - result successful
    # test_cond = 2: Use threading and time.sleep - result successful
    # test_cond = 3: No threading and xbmc.sleep - result successful
    # test_cond = 4: No threading and time.sleep - fails

    test_cond = 4  # Change this manually to test different scenarios

    if test_cond == 1 or test_cond == 2:  # Use threading
        thread = threading.Thread(target=threadstart)
        thread.start()
        xbmc.sleep(1000)  # Need to wait until thread is started before checking .isupdating below since there is some
                          # asynchronous time overhead for thread starting
        
    elif test_cond == 3 or test_cond == 4:  # No threading
        global dbmonitor
        dbmonitor = XbmcMonitor()
        
    while not xbmc.abortRequested:
        if dbmonitor.isupdating is True:
            xbmc.log('######### DB updating thread: %s' % str(threading.current_thread().ident), level=xbmc.LOGNOTICE)
        if test_cond == 1 or test_cond == 3:
            xbmc.sleep(500)
        else:
            time.sleep(0.500)  # This could be an infinite loop or a bunch of other code that doesn't release control

if __name__ == '__main__':
    main()

Using this I ran the code four times, manually changing the variable test_cond. If you use threading and do not call xbmc.sleep, the variable (.isupdating) gets updated without any issues (test_cond = 2). If you do not use threading and do not call xbmc.sleep, the variable is never updated and the method fails (test_cond=4).

If you say that the events are not reliably called within xbmc when db scans start and stop, then the whole issue is moot. If the problem hasn't been reported then perhaps you may want to open a ticket at http://trac.xbmc.org/
Reply
#17
I see. Either way I don't think this has anything to do with what OP asked for. *he only want to wait for the previous scan to finish before moving on to the next one. No need for threading. As long as you toggle the isupdating flag async, it wont tell you whether you should wait or not. (regardless of being threaded). That was my main point.
Reply
#18
Well, this 'seems' to work for me:

Code:
import threading
import time
import xbmc, xbmcgui
dbmonitor = None


class XbmcMonitor (xbmc.Monitor):

    def __init__(self):
        xbmc.Monitor.__init__(self)
        self.isupdating = False

    def onDatabaseScanStarted(self, database):
        self.isupdating = True
        xbmc.log('######## Scan started thread: %s' % str(threading.current_thread().ident), level=xbmc.LOGNOTICE)

    def onDatabaseUpdated(self, database):
        self.isupdating = False
        xbmc.log('######## Scan ended thread: %s' % str(threading.current_thread().ident), level=xbmc.LOGNOTICE)


def threadstart():
    global dbmonitor
    dbmonitor = XbmcMonitor()
    while not xbmc.abortRequested:
        xbmc.sleep(5)  # short sleep needed to catch event and force thread frequency


def main():
    thread = threading.Thread(target=threadstart)
    thread.start()
    xbmc.sleep(5000)
    if dbmonitor.isupdating:
        dialog = xbmcgui.Dialog()
        dialog.ok('Scanning..', "Scan is already running. Stop Current Scan and try again")
    else:
        lines = (r'smb://HTPC-W7/Movies/', r'smb://HTPC-W7/TV Recordings/', r'smb://VIVESWHS/Videos/TV/Game of Thrones/')
        for path in lines:
            count = 0
            xbmc.log('####### Starting scan of: %s' % path)
            xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "VideoLibrary.Scan", "params": {"directory": "' + path + '" }, "id": 1 }')
            xbmc.sleep(45)  # Latency between json call and the event occurring
            while dbmonitor.isupdating:
                xbmc.sleep(5)
                count += 1
            #xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "VideoLibrary.Scan", "id": 1 }')
            xbmc.log('####### Waited %s msec while DB was updating' % str(45 + count * 5), level=xbmc.LOGNOTICE)

if __name__ == '__main__':
    main()

Looking at the relevant portion of the log, the code seems to wait until the scan finishes before proceeding:

Code:
12:19:58 T:8352  NOTICE: ####### Starting scan of: smb://HTPC-W7/Movies/
12:19:58 T:13188  NOTICE: Thread VideoInfoScanner start, auto delete: false
12:19:58 T:13188  NOTICE: VideoInfoScanner: Starting scan ..
12:19:58 T:7108  NOTICE: ######## Scan started thread: 7108
12:19:58 T:13188  NOTICE: VideoInfoScanner: Finished scan. Scanning for video info took 00:00
12:19:58 T:7108  NOTICE: ######## Scan ended thread: 7108
12:19:58 T:8352  NOTICE: ####### Waited 65 msec while DB was updating
12:19:58 T:8352  NOTICE: ####### Starting scan of: smb://HTPC-W7/TV Recordings/
12:19:58 T:1512  NOTICE: Thread VideoInfoScanner start, auto delete: false
12:19:58 T:1512  NOTICE: VideoInfoScanner: Starting scan ..
12:19:58 T:7108  NOTICE: ######## Scan started thread: 7108
12:19:58 T:1512  NOTICE: VideoInfoScanner: Finished scan. Scanning for video info took 00:00
12:19:58 T:7108  NOTICE: ######## Scan ended thread: 7108
12:19:58 T:8352  NOTICE: ####### Waited 60 msec while DB was updating
12:19:58 T:8352  NOTICE: ####### Starting scan of: smb://VIVESWHS/Videos/TV/Game of Thrones/
12:19:58 T:4112  NOTICE: Thread VideoInfoScanner start, auto delete: false
12:19:58 T:4112  NOTICE: VideoInfoScanner: Starting scan ..
12:19:58 T:7108  NOTICE: ######## Scan started thread: 7108
12:19:58 T:4112  NOTICE: VideoInfoScanner: Finished scan. Scanning for video info took 00:00
12:19:58 T:7108  NOTICE: ######## Scan ended thread: 7108
12:19:58 T:8352  NOTICE: ####### Waited 70 msec while DB was updating
Reply
#19
"Latency between json call and the event occurring" Now you're just making stuff up :p It will probably mostly work, but I can guarantee you the scan wont reliably start at most 45ms after the call. It can take seconds. It's the same as using getCondVisibility. But why go though all this trouble? I posted a much simpler example of how to do it reliably.
Reply
#20
You're probably right that the latency will be variable and dependent on individual systems and the conditions under which the scan is running. There IS a latency. I ran that multiple times slowly increasing the amount of sleep until it caught the change.

Trouble? No trouble... I learn something from every piece of code I write. Hopefully the OP sees that there is more than one way to skin a cat as well.
Reply
#21
(2014-09-23, 14:00)takoi Wrote:
(2014-09-22, 22:39)KenV99 Wrote: Something like this:

Yes, it uses multiple inheritance Undecided. If that is worry, create a separate class that only subclasses 'threading.Thread' and then instantiate the monitor in that class when you override run() - see the links below.

Docs:
http://pymotw.com/2/threading/
https://docs.python.org/2/library/thread...ad-objects
But why do you need a thread? You don't have a run method so inheriting from Thread does absolutely nothing. Also, since you toggle the isupdating flag, you will have exactly the same issues as using getCondVisibility: you can't trust it call next scan like OP wanted. Here's a different example (pseudocode, not tested):

Code:
class MyCallbackHandler(xbmc.Monitor):
    def __init__(self):
        xbmc.Monitor.__init__(self)
        self.scan_complete = False

    def onDatabaseUpdated(self, library):
        self.scan_complete = True


monitor = MyCallbackHandler()

for path in paths:
    xbmc.executeJSONRPC('...')

    # wait for scan to complete
    while not monitor.scan_complete:
        xbmc.sleep(..)

    # important thing is to reset the flag here, before next call to scan, so you *know* it will toggled again only when it finish
    monitor.scan_complete = False

Hi takoi

You are correct that all i want to do is start a scan for a path and then only continue to scan next path once previous one has ended.

that being the case, do i put this code in the same .py file or do i put it in a different file.

i assume code from class MyCallbackHandler ...... to ...... self.scan_complete = True , would go in one file and
the rest would go in my py file. is that correct

thanks for your help. The more i see the more i learn.
Reply
#22
You can put everything in your file, if you want.
Reply

Logout Mark Read Team Forum Stats Members Help
force scanfolder0