Esempio n. 1
0
 def __init__(self):
     super(ArtworkService, self).__init__()
     self.serviceenabled = addon.get_setting("enableservice")
     self.only_filesystem = addon.get_setting("only_filesystem")
     self.abort = False
     self.processor = ArtworkProcessor(self)
     self.processed = ProcessedItems(addon.datapath)
     self.signal = None
     self.processaftersettings = False
     self.recentitems = {"movie": [], "tvshow": [], "episode": []}
     self.stoppeditems = set()
     self.set_status(STATUS_IDLE)
Esempio n. 2
0
class ArtworkService(xbmc.Monitor):
    def __init__(self):
        super(ArtworkService, self).__init__()
        self.serviceenabled = addon.get_setting("enableservice")
        self.only_filesystem = addon.get_setting("only_filesystem")
        self.abort = False
        self.processor = ArtworkProcessor(self)
        self.processed = ProcessedItems(addon.datapath)
        self.signal = None
        self.processaftersettings = False
        self.recentitems = {"movie": [], "tvshow": [], "episode": []}
        self.stoppeditems = set()
        self.set_status(STATUS_IDLE)

    @property
    def toomany_recentitems(self):
        return len(self.recentitems["movie"]) + len(self.recentitems["tvshow"]) + len(self.recentitems["episode"]) > 100

    @property
    def scanning(self):
        return pykodi.get_conditional("Library.IsScanningVideo | Library.IsScanningMusic")

    def set_status(self, value):
        self.abort = False
        self.status = value
        pykodi.execute_builtin("SetProperty(ArtworkBeef.Status, {0}, Home)".format(value))

    def set_signal(self, value):
        self.set_status(STATUS_SIGNALLED if value else STATUS_IDLE)
        self.signal = value

    def reset_recent(self):
        self.recentitems = {"movie": [], "tvshow": [], "episode": []}

    def run(self):
        while not self.really_waitforabort(5):
            if self.scanning:
                continue
            if self.signal:
                signal = self.signal
                self.signal = None
                if signal == "something":
                    # Add a delay to catch rapid fire library updates
                    self.signal = "something_really"
                    continue
                self.set_status(STATUS_PROCESSING)
                if signal == "allitems":
                    self.process_allitems()
                elif signal == "unprocesseditems":
                    self.process_allitems(True)
                elif signal == "something_really":
                    self.process_something()
                self.set_status(STATUS_IDLE)

    def abortRequested(self):
        return self.abort or super(ArtworkService, self).abortRequested()

    def waitForAbort(self, timeout=None):
        return self.abort or super(ArtworkService, self).waitForAbort(timeout)

    def really_waitforabort(self, timeout=None):
        return super(ArtworkService, self).waitForAbort(timeout)

    def onNotification(self, sender, method, data):
        if method.startswith("Other."):
            if sender == "script.artwork.beef":
                message = 'Received notification "{0}"'.format(method)
                if data != "null":
                    message += " - data:\n" + data
                log(message, xbmc.LOGINFO)
            else:
                return
        if method == "Other.CancelCurrent":
            if self.status == STATUS_PROCESSING:
                self.abort = True
            elif self.signal:
                self.set_signal(None)
                self.processor.close_progress()
        elif method == "Other.ProcessNewItems":
            self.processor.create_progress()
            if addon.get_setting("lastalldate") == "0":
                self.set_signal("allitems")
            else:
                self.set_signal("unprocesseditems")
        elif method == "Other.ProcessAllItems":
            self.processor.create_progress()
            self.set_signal("allitems")
        elif method == "Other.ProcessAfterSettings":
            self.processaftersettings = True
        elif method == "Player.OnStop":
            if self.serviceenabled:
                data = json.loads(data)
                if self.watchitem(data):
                    self.stoppeditems.add((data["item"]["type"], data["item"]["id"]))
        elif method == "VideoLibrary.OnScanStarted":
            if self.serviceenabled and self.status == STATUS_PROCESSING:
                self.abort = True
        elif method == "VideoLibrary.OnScanFinished":
            if self.serviceenabled:
                self.set_signal("something")
        elif method == "VideoLibrary.OnUpdate":
            if not self.serviceenabled:
                return
            data = json.loads(data)
            if not self.watchitem(data):
                return
            if (data["item"]["type"], data["item"]["id"]) in self.stoppeditems:
                self.stoppeditems.remove((data["item"]["type"], data["item"]["id"]))
                return
            if not self.toomany_recentitems:
                self.recentitems[data["item"]["type"]].append(data["item"]["id"])
            if not self.scanning:
                self.set_signal("something")

    def watchitem(self, data):
        return (
            "item" in data
            and data["item"].get("id")
            and "playcount" not in data
            and data["item"].get("type") in self.recentitems
        )

    def process_something(self):
        # Three possible loops:
        # 1. Recent items, can run several times per day, after every library update, and only for small updates
        # 2. Unprocessed items, at least once every UNPROCESSED_DAYS and after larger library updates
        #  - this contains only new items that were missed in the recent loops, maybe in a shared database
        # 3. All items, once every ALLITEMS_DAYS ( or / 4, if filesystem only)
        #  - adds new artwork for old items missing artwork
        lastdate = addon.get_setting("lastalldate")
        if lastdate == "0":
            self.process_allitems()
            self.reset_recent()
            return
        days = ALLITEMS_DAYS
        if self.only_filesystem:
            days /= 4
        needall = lastdate < str(datetime_now() - timedelta(days=days))
        if needall:
            needunprocessed = False
        else:
            needunprocessed = self.toomany_recentitems
            if not needunprocessed:
                lastdate = addon.get_setting("lastunprocesseddate")
                needunprocessed = lastdate < str(datetime_now() - timedelta(days=UNPROCESSED_DAYS))
        if needunprocessed:
            self.process_allitems(True)
            self.reset_recent()
            return

        self.process_recentitems()
        if not self.abortRequested() and needall:
            self.process_allitems()

    def process_allitems(self, excludeprocessed=False):
        currentdate = str(datetime_now())
        items = quickjson.get_movies(properties=movie_properties)
        if excludeprocessed:
            items = [movie for movie in items if movie["movieid"] not in self.processed.movie]
        serieslist = quickjson.get_tvshows(properties=tvshow_properties)
        if self.abortRequested():
            return
        autoaddepisodes = addon.get_setting("autoaddepisodes_list") if addon.get_setting("episode.fanart") else ()
        for series in serieslist:
            if not excludeprocessed or series["season"] > self.processed.tvshow.get(series["tvshowid"]):
                items.append(series)
            if series["imdbnumber"] in autoaddepisodes:
                episodes = quickjson.get_episodes(series["tvshowid"], "dateadded", properties=episode_properties)
                for episode in episodes:
                    if not excludeprocessed or episode["episodeid"] not in self.processed.episode:
                        items.append(episode)
            if self.abortRequested():
                return

        processed = self.processor.process_medialist(items)
        if excludeprocessed:
            self.processed.extend(processed)
        else:
            self.processed.set(processed)
            addon.set_setting("lastalldate", currentdate)
        self.processed.save()
        if not self.abortRequested():
            addon.set_setting("lastunprocesseddate", currentdate)

    def process_recentitems(self):
        ignoreepisodesfrom = set()
        addepisodesfrom = set()
        seriesadded = set()
        newitems = []
        for movieid in self.recentitems["movie"]:
            newitems.append(quickjson.get_movie_details(movieid, movie_properties))
            if self.abortRequested():
                return
        for seriesid in self.recentitems["tvshow"]:
            seriesadded.add(seriesid)
            newitems.append(quickjson.get_tvshow_details(seriesid, tvshow_properties))
            if self.abortRequested():
                return
        autoaddepisodes = addon.get_setting("autoaddepisodes_list") if addon.get_setting("episode.fanart") else ()
        for episodeid in self.recentitems["episode"]:
            episode = quickjson.get_episode_details(episodeid, episode_properties)
            series = None
            if (
                episode["season"] > self.processed.tvshow.get(episode["tvshowid"])
                and episode["tvshowid"] not in seriesadded
            ):
                seriesadded.add(episode["tvshowid"])
                series = quickjson.get_tvshow_details(episode["tvshowid"], tvshow_properties)
                newitems.append(series)
            if episode["tvshowid"] in addepisodesfrom:
                newitems.append(episode)
            elif episode["tvshowid"] not in ignoreepisodesfrom:
                if not series:
                    series = quickjson.get_tvshow_details(episode["tvshowid"], tvshow_properties)
                if series["imdbnumber"] in autoaddepisodes:
                    addepisodesfrom.add(episode["tvshowid"])
                    newitems.append(episode)
                else:
                    ignoreepisodesfrom.add(episode["tvshowid"])
            if self.abortRequested():
                return

        self.reset_recent()
        processed = self.processor.process_medialist(newitems)
        self.processed.extend(processed)
        self.processed.save()

    def onSettingsChanged(self):
        self.serviceenabled = addon.get_setting("enableservice")
        self.only_filesystem = addon.get_setting("only_filesystem")
        mediatypes.update_settings()
        self.processor.update_settings()
        if self.processaftersettings:
            self.processor.create_progress()
            xbmc.sleep(200)
            self.processaftersettings = False
            self.set_signal("unprocesseditems")