diff --git a/addon.xml b/addon.xml index 17dbf36..e5d3684 100644 --- a/addon.xml +++ b/addon.xml @@ -2,21 +2,21 @@ - + - + - - + all diff --git a/default.py b/default.py index 70f6c87..082d58a 100644 --- a/default.py +++ b/default.py @@ -32,8 +32,8 @@ import xbmcaddon import xbmcgui -from resources.lib.footballscores import League from resources.lib.notificationqueue import NotificationQueue +from resources.lib.footballscores import FootballMatch # Set the addon environment _A_ = xbmcaddon.Addon("service.bbclivefootballscores") @@ -41,6 +41,9 @@ _SET_ = _A_.setSetting pluginPath = _A_.getAddonInfo("path") +SETTING_TEAMS = "watchedteams" +SETTING_LEAGUES = "watchedleagues" + # Set some constants # Define our images @@ -52,17 +55,10 @@ IMG_YELLOW = os.path.join(pluginPath, "resources", "media", "yellow-card.png") IMG_RED = os.path.join(pluginPath, "resources", "media", "red-card.png") -# Notification display time -n = int(_GET_("DisplayTime")) -NOTIFY_TIME = n * 1000 - -# Additional detail -d = _GET_("AdditionalDetail") == "true" -SHOW_GOALSCORER = _GET_("ShowGoalscorer") == "true" -SHOW_BOOKINGS = int(_GET_("ShowBookings")) -SHOW_YELLOW = bool(SHOW_BOOKINGS == 2) -SHOW_RED = bool(SHOW_BOOKINGS != 0) -DETAILED = all([d, any([SHOW_GOALSCORER, SHOW_BOOKINGS])]) +# What bookings to show +BOOKINGS = {0: "OFF", + 1: "RED ONLY", + 2: "ALL"} # STATUS_DICT object # Format is {status: [status text, image path]} @@ -71,6 +67,9 @@ "L": ["Latest", IMG_LATEST], "Fixture": ["Fixture", IMG_FIXTURE]} + +# Define core generic funcitons + def localise(id): '''Gets localised string. @@ -85,306 +84,458 @@ def debug(msg): Takes one argument: msg: debug message to send to XBMC log ''' - msg = u"bbclivefootballscores: {0}".format(msg).encode("ascii", "ignore") - xbmc.log(msg,xbmc.LOGDEBUG) - -def getSelectedLeagues(): - '''Returns list of leagues selected by user in settings file.''' - - # Try to get list of selected leagues from settings file - try: - - # Get the settings value and convert to a list - watchedleagues = json.loads(str(_GET_("watchedleagues"))) - - # if there's a problem - except: - - # Create an empty list (stops service from crashing) - watchedleagues = [] - - # Return this list - return watchedleagues + xbmc.log(msg, xbmc.LOGDEBUG) -def checkAlerts(): - '''Setting is "True" when alerts are disabled. - Returns boolean: - True: Service should display alerts - False: Alerts are disabled +class SettingsMonitor(xbmc.Monitor): + '''Handler to checking when settings are updated and triggering an + appropriate callback. ''' - return _GET_("Alerts") != "true" + def __init__(self, *args, **kwargs): + xbmc.Monitor.__init__(self) + self.action = kwargs['action'] -def checkNotificationDetailLevel(): - '''Sets certain constants to determine how much detail is required for - notifications. + def onSettingsChanged(self): + debug("Detected change in settings (NB may not be this addon)") + self.action() - Returns a tuple which should set the following variables: - SHOW_GOALSCORER - SHOW_BOOKINGS - SHOW_YELLOW - SHOW_RED - DETAILED - ''' - d = _GET_("AdditionalDetail") == "true" - SHOW_GOALSCORER = _GET_("ShowGoalscorer") == "true" - SHOW_BOOKINGS = int(_GET_("ShowBookings")) - SHOW_YELLOW = bool(SHOW_BOOKINGS == 2) - SHOW_RED = bool(SHOW_BOOKINGS != 0) - DETAILED = all([d, any([SHOW_GOALSCORER, SHOW_BOOKINGS])]) - - return SHOW_GOALSCORER, SHOW_BOOKINGS, SHOW_YELLOW, SHOW_RED, DETAILED - -def serviceRunning(): - '''User should be able to deactivate alerts (rather than deactivating - the service) via setting. - - Returns a boolean as to whether we should provide alerts or not. - ''' - return True +class FootballScoresService(object): + '''Class definition for the football scoress service. -def updateWatchedLeagues(matchdict, selectedleagues): - '''Updates our active leagues to make sure that we're just looking at the - leagues that the user wants. + Service will run on starting Kodi and will stop on exit. - Takes 2 arguments: - matchdict: dictionary of leagues being watched - selectedleagues: list of league IDs chosen by user - - Returns updated dictionary object. + Settings are read on starting the service. A separate monitor instance is + needed to update settings if a user changes them while the script is + running. ''' - # Build a list of leagues selected by user that are not in - # our current dictionary - newleagues = [l for l in selectedleagues if l not in matchdict] + def __init__(self): + '''Initialises the service object but does not start it. - # Build a list of leagues in our dictionary that are no longer in - # list of leagues selected by users - removedleagues = [l for l in matchdict if l not in selectedleagues] + Reads settings and defines the necessary variables and objects. + ''' + debug("Initialisings service...") - # Loop through new leagues - for l in newleagues: + # Create a notification queue object for handling notifications + debug("Creating queue") + self.queue = NotificationQueue() - # Add a League object to the dictioanary + # Define required constants and objects + self.leaguedict = {} + self.teamdict = {} + self.ticker = "" + self.SHOW_ALERTS = True + # self.SHOW_GOALSCORER = -1 + # self.SHOW_BOOKINGS = -1 + # self.SHOW_YELLOW = -1 + # self.SHOW_RED = -1 + # self.DETAILED = -1 + self.NOTIFY_TIME = 5000 + + # Read the addon settings + self.getSettings() + + + + # Create a settings monitor + debug("Starting settings monitor...") + self.monitor = SettingsMonitor(action=self.getSettings) + + # Clear old tickers + # debug("Clearing tickers") + # self.checkTickers() + + def Notify(self, subject, message, image=None, timeout=2000): + '''Displays match notification. + + Take 4 arguments: + subject: subject line + message: message line + image: path to icon + timeoute: display time in milliseconds + ''' + self.queue.add(subject, message, image, timeout) + + def _goal(self, event): + + subject = u"GOAL! ({})".format(event.Scorer.AbbreviatedName) + message = u"{}".format(unicode(event.match)) + + self.Notify(subject, message, IMG_GOAL, timeout=self.NOTIFY_TIME) + + def _status(self, event): + + subject = u"{}".format(event.match.LongStatus) + message = u"{}".format(unicode(event.match)) + + self.Notify(subject, message, IMG_FT, timeout=self.NOTIFY_TIME) + + def _fixture(self, event): + + subject = u"New Match" + message = u"{}".format(unicode(event.match)) + + self.Notify(subject, message, IMG_FIXTURE, timeout=self.NOTIFY_TIME) + + def _red(self, event): + + subject = u"Red Card! ({})".format(event.RedCard.AbbreviatedName) + message = u"{}".format(unicode(event.match)) + + self.Notify(subject, message, IMG_RED, timeout=self.NOTIFY_TIME) + + def getSettings(self): + '''Reads the addon settings and updates the scipt settings accordingly. + + This method should be handled by a monitor instance so that any + changes made to settings while the service is running are also + updated. + ''' + debug("Checking settings...") + self.updateWatchedTeams() + # self.checkAlerts() + # self.checkNotificationDetailLevel() + # self.updateWatchedLeagues() + # self.checkNotificationTime() + + def checkNotificationTime(self): + '''Sets the length of time for which notifications should be + displayed. + ''' + # try: + # n = int(_GET_("DisplayTime")) + # except ValueError: + # # Default is 2 seconds + # n = 2 + + n = 2 + + if n != (self.NOTIFY_TIME / 1000): + debug("Notification time now {0} seconds".format(n)) + self.NOTIFY_TIME = n * 1000 + + # def checkAlerts(self): + # '''Setting is "True" when alerts are disabled. + # ''' + # alerts = _GET_("Alerts") != "true" + # + # if alerts != self.SHOW_ALERTS: + # debug("Alerts now {0}.".format("ON" if alerts else "OFF")) + # self.SHOW_ALERTS = alerts + # + # def checkNotificationDetailLevel(self): + # '''Sets certain constants to determine how much detail is required for + # notifications. + # ''' + # d = _GET_("AdditionalDetail") == "true" + # + # gs = _GET_("ShowGoalscorer") == "true" + # if gs != self.SHOW_GOALSCORER: + # debug("Goal scorer alerts now {0}.".format("ON" if gs else "OFF")) + # self.SHOW_GOALSCORER = gs + # + # try: + # bk = int(_GET_("ShowBookings")) + # except ValueError: + # bk = 0 + # + # if bk != self.SHOW_BOOKINGS: + # debug("Bookings are now {0}.".format(BOOKINGS[bk])) + # self.SHOW_YELLOW = bool(bk == 2) + # self.SHOW_RED = bool(bk != 0) + # self.SHOW_BOOKINGS = bk + # + # dt = all([d, any([self.SHOW_GOALSCORER, self.SHOW_BOOKINGS])]) + # + # if dt != self.DETAILED: + # level = "ON" if dt else "OFF" + # debug("Showing additional detail is now {0}.".format(level)) + # self.DETAILED = dt + + def updateWatchedTeams(self): + + selected_teams = self.getUserSelected(SETTING_TEAMS) + + newteams = [t for t in selected_teams if t not in self.teamdict] + removedteams = [t for t in self.teamdict if t not in selectedteams] + + for team in newteams: + + obj = FootballMatch(team, + on_goal=self._goal, + on_red=self._red, + on_status_change=self._status, + on_new_match=self._fixture) + + self.teamdict[team] = obj + + # Loop through leaues to be removed + for l in removedteams: + + # Remove league from the dictionary + self.teamdict.pop(l) + + + # def updateWatchedLeagues(self): + # '''Updates our active leagues to make sure that we're just looking at + # the leagues that the user wants. + # ''' + # selectedleagues = self.getSelectedLeagues() + # + # # Build a list of leagues selected by user that are not in + # # our current dictionary + # newleagues = [l for l in selectedleagues if l not in self.matchdict] + # + # # Build a list of leagues in our dictionary that are no longer in + # # list of leagues selected by users + # removedleagues = [l for l in self.matchdict if l not in selectedleagues] + # + # # Loop through new leagues + # for l in newleagues: + # + # # Add a League object to the dictioanary + # try: + # self.matchdict[l] = League(l, detailed=self.DETAILED) + # except TypeError: + # pass + # + # # Loop through leaues to be removed + # for l in removedleagues: + # + # # Remove league from the dictionary + # self.matchdict.pop(l) + # + # if newleagues: + # debug("Added new leagues: {0}".format(newleagues)) + # + # if removedleagues: + # debug("Removed leagues: {0}".format(removedleagues)) + # + # if newleagues or removedleagues: + # debug(u"LeagueList - {0}".format(self.matchdict)) + + def getUserSelected(self, setting): + '''Returns list of leagues selected by user in settings file.''' + + # Try to get list of selected leagues from settings file try: - matchdict[l] = League(l, detailed=DETAILED) - except TypeError: - pass - - # Loop through leaues to be removed - for l in removedleagues: - - # Remove league from the dictionary - matchdict.pop(l) - - # Return the dictionary - return matchdict - -def Notify(subject, message, image=None, timeout=2000): - '''Displays match notification. - - Take 4 arguments: - subject: subject line - message: message line - image: path to icon - timeoute: display time in milliseconds - ''' - queue.add(subject, message, image, timeout) - -def checkMatch(match): - '''Look at the match and work out what notification we want to show. - - Takes one argument: - match: footballscores.FootballMatch object - ''' - - if match.booking: - - # Should we show notification? - if (SHOW_YELLOW and DETAILED): - yellow = u" {1} ({0})".format(*match.LastYellowCard) - else: - yellow = None - - Notify(u"YELLOW!{0}".format(yellow if yellow else u""), - str(match), - IMG_YELLOW, - timeout=NOTIFY_TIME) - debug(u"Yellow Card: %s" % (unicode(match))) - - if match.redcard: - - # Should we show notification? - if (SHOW_RED and DETAILED): - red = u" {1} ({0})".format(*match.LastRedCard) - else: - red = None - - Notify(u"RED!{0}".format(red if red else u""), - str(match), - IMG_RED, - timeout=NOTIFY_TIME) - debug(u"Red Card: %s" % (unicode(match))) - - # Has there been a goal? - if match.Goal: - - # Gooooooooooooooooooooooooooooollllllllllllllll! - - # Should we show goalscorer? - if (SHOW_GOALSCORER and DETAILED): - scorer = u" {0}".format(match.LastGoalScorer[1]) - else: - scorer = None - - Notify(u"GOAL!{0}".format(scorer if scorer else u""), - str(match), - IMG_GOAL, - timeout=NOTIFY_TIME) - debug(u"GOAL: %s" % (unicode(match))) - - - # Has the status changed? e.g. kick-off, half-time, full-time? - if match.StatusChanged: - - # Get the relevant status info - info = STATUS_DICT.get(match.status, STATUS_DICT["Fixture"]) - - # Send the notification - Notify(info[0], unicode(match), info[1], timeout=NOTIFY_TIME) - debug(u"STATUS: {0}".format(unicode(match))) - -def checkTickers(): - - try: - tickers = json.loads(_GET_("currenttickers")) - except ValueError: - tickers = {} - - d = [] - - for k in tickers: - - w = xbmcgui.Window(int(k)) - try: - c = w.getControl(tickers[k]) - except RuntimeError: - d.append(k) - - for k in d: - tickers.pop(k) - - _SET_("currenttickers", json.dumps(tickers)) - - -def updateTickers(text): - - try: - tickers = json.loads(_GET_("currenttickers")) - except ValueError: - tickers = {} - - for k in tickers: - - w = xbmcgui.Window(int(k)) - c = w.getControl(tickers[k]) - c.reset() - c.addLabel(text) - - - _SET_("ticker", text) - - -def doUpdates(matchdict): - '''Main function to updated leagues and check matches for updates. - - Takes one argument: - matchdict: dictionary of leagues being watchedleagues - - Returns updated dictionary - ''' - - ticker = u"" - - # Loop through each league that we're following - for league in matchdict: - - # Make sure we only get additional information if we need it. - for m in matchdict[league].LeagueMatches: - m.detailed = DETAILED - - # Get the league to update each match - matchdict[league].Update() - - if matchdict[league]: - if ticker: - ticker += " " - ticker += u"[B]{0}[/B]: ".format(matchdict[league].LeagueName) - ticker += u", ".join(unicode(m) for m in matchdict[league].LeagueMatches) - - # Loop through the matches - for match in matchdict[league].LeagueMatches: - - # and check it for updates - checkMatch(match) - - debug(ticker) - updateTickers(ticker) - # xbmc.executebuiltin(u"skin.setstring(tickertext,{0})".format(ticker)) - - # Return the updated dicitonary object - return matchdict - - -# Script starts here. -# Let's get some initial data before we enter main service loop - -# Build dictionary of leagues we want to follow -matchdict = updateWatchedLeagues({}, getSelectedLeagues()) -debug(u"LeagueList - {0}".format(matchdict)) - -# Check if we need to show alerts or not. -alerts = checkAlerts() - -# Clear old tickers -checkTickers() - -# Variable for counting loop iterations -i = 0 - -# Create a notification queue object -queue = NotificationQueue() - -# Main service loop - need to exit script cleanly if XBMC is shutting down -debug("Entering main loop...") -while not xbmc.abortRequested: - - # 5 seconds before we do main update, let's check and see if there are any - # new leagues that we need to follow. - # Also, check whether the user has enabled/disabled alerts - if i == 11: - matchdict = updateWatchedLeagues(matchdict, getSelectedLeagues()) - alerts = checkAlerts() - (SHOW_GOALSCORER, - SHOW_BOOKINGS, - SHOW_YELLOW, - SHOW_RED, - DETAILED) = checkNotificationDetailLevel() - - # If user wants alerts and we've reached ou desired loop number... - if alerts and not i: - - # Update our match dictionary and check for updates. - debug("Checking scores...") - matchdict = doUpdates(matchdict) - - # Sleep for 5 seconds (if this is longer, XBMC may not shut down cleanly.) - xbmc.sleep(5000) - # Increment our counter - # 12 x 5000 = 60,000 i.e. scores update every 1 minute - # Currently hard-coded - may look to change this. - i = (i + 1) % 12 + # Get the settings value and convert to a list + selection = json.loads(str(_GET_(setting))) + + # if there's a problem + except: + + # Create an empty list (stops service from crashing) + selection = [] + + # Return this list + return selection + + # def checkMatch(self, match): + # '''Look at the match and work out what notification we want to show. + # + # Takes one argument: + # match: footballscores.FootballMatch object + # ''' + # + # if match.booking: + # + # # Should we show notification? + # if (self.SHOW_YELLOW and self.DETAILED): + # debug(u"yellow card: {0}".format(match.LastYellowCard)) + # try: + # yellow = u" {1} ({0})".format(*match.LastYellowCard) + # except AttributeError: + # yellow = None + # else: + # yellow = None + # + # if self.SHOW_YELLOW: + # + # self.Notify(u"YELLOW!{0}".format(yellow if yellow else u""), + # unicode(match), + # IMG_YELLOW, + # timeout=self.NOTIFY_TIME) + # debug(u"Yellow Card: {0}, {1}".format(match, yellow)) + # + # if match.redcard: + # + # # Should we show notification? + # if (self.SHOW_RED and self.DETAILED): + # debug(u"red card: {0}".format(match.LastRedCard)) + # try: + # red = u" {1} ({0})".format(*match.LastRedCard) + # except AttributeError: + # red = None + # else: + # red = None + # + # if self.SHOW_RED: + # + # self.Notify(u"RED!{0}".format(red if red else u""), + # unicode(match), + # IMG_RED, + # timeout=self.NOTIFY_TIME) + # debug(u"Red Card: {0}, {1}".format(match, red)) + # + # # Has there been a goal? + # if match.Goal: + # + # # Gooooooooooooooooooooooooooooollllllllllllllll! + # + # # Should we show goalscorer? + # if (self.SHOW_GOALSCORER and self.DETAILED): + # debug(u"goalscorer: {0}".format(match.LastGoalScorer)) + # try: + # scorer = u" {0}".format(match.LastGoalScorer[1]) + # except AttributeError: + # scorer = None + # else: + # scorer = None + # + # self.Notify(u"GOAL!{0}".format(scorer if scorer else u""), + # unicode(match), + # IMG_GOAL, + # timeout=self.NOTIFY_TIME) + # debug(u"GOAL: {0}, {1}".format(match, scorer)) + # + # # Has the status changed? e.g. kick-off, half-time, full-time? + # if match.StatusChanged: + # + # # Get the relevant status info + # info = STATUS_DICT.get(match.status, STATUS_DICT["Fixture"]) + # + # # Send the notification + # self.Notify(info[0], unicode(match), info[1], + # timeout=self.NOTIFY_TIME) + # debug(u"STATUS: {0}".format(unicode(match))) + + # def checkTickers(self): + # '''Tickers are not a class property because they are implemented by a + # separate script. + # + # We therefore need to manually maintain a list of which windows have + # tickers and check to see it's correct. + # + # If a ticker cannot be found (i.e. it was implemented in a separate + # Kodi session) then it needs to be removed from the list. + # ''' + # try: + # tickers = json.loads(_GET_("currenttickers")) + # except ValueError: + # tickers = {} + # + # d = [] + # + # for k in tickers: + # + # w = xbmcgui.Window(int(k)) + # try: + # c = w.getControl(tickers[k]) + # except RuntimeError: + # d.append(k) + # + # for k in d: + # tickers.pop(k) + # + # _SET_("currenttickers", json.dumps(tickers)) + # + # def updateTickers(self): + # '''Updates the ticker text on all known tickers.''' + # + # try: + # tickers = json.loads(_GET_("currenttickers")) + # except ValueError: + # tickers = {} + # + # for k in tickers: + # + # w = xbmcgui.Window(int(k)) + # c = w.getControl(tickers[k]) + # c.reset() + # c.addLabel(self.ticker.decode("utf-8").replace("|", ",")) + + def doUpdates(self): + + for team in self.teamdict: + self.teamdict[team].update() + + # + # ticker = u"" + # + # # Loop through each league that we're following + # for league in self.matchdict: + # + # # Make sure we only get additional information if we need it. + # for m in self.matchdict[league].LeagueMatches: + # m.detailed = self.DETAILED + # + # # Get the league to update each match + # self.matchdict[league].Update() + # + # if self.matchdict[league]: + # if ticker: + # ticker += u" " + # lgn = u"[B]{0}[/B]: ".format(self.matchdict[league].LeagueName) + # mtc = u", ".join(unicode(m) for m + # in self.matchdict[league].LeagueMatches) + # ticker += lgn + # ticker += mtc + # + # # If we're showing alerts then let's check each match for updates + # if self.SHOW_ALERTS: + # + # # Loop through the matches + # for match in self.matchdict[league].LeagueMatches: + # + # # and check it for updates + # self.checkMatch(match) + # + # # If there have been any changes then we need to update the tickers + # if ticker != self.ticker: + # debug(u"Ticker: {0}".format(ticker)) + # self.ticker = ticker.replace(",", "|").encode("utf-8") + # xbmc.executebuiltin("Skin.SetString(bbcscorestickertext, {0})".format(self.ticker)) + # self.updateTickers() + + def run(self): + '''Method to start the service. + + Service runs in a loop which is terminated when the user exits Kodi. + ''' + + # Variable for counting loop iterations + i = 0 + + # Main service loop - need to exit script cleanly if XBMC is shutting + # down + debug("Entering main loop...") + while not xbmc.abortRequested: + + # If user wants alerts and we've reached our desired loop number... + if self.SHOW_ALERTS and not i: + + # Update our match dictionary and check for updates. + debug("Checking scores...") + self.doUpdates() + + # Sleep for 5 seconds + # (if this is longer, XBMC may not shut down cleanly.) + xbmc.sleep(5000) + + # Increment our counter + # 12 x 5000 = 60,000 i.e. scores update every 1 minute + # Currently hard-coded - may look to change this. + i = (i + 1) % 12 + + +if __name__ == "__main__": + scores_service = FootballScoresService() + scores_service.run() + + # Clean exit + scores_service = None diff --git a/fanart.jpg b/fanart.jpg deleted file mode 100644 index 9f12427..0000000 Binary files a/fanart.jpg and /dev/null differ diff --git a/helper.py b/helper.py index bc7137c..abefbec 100644 --- a/helper.py +++ b/helper.py @@ -38,138 +38,152 @@ import xbmcaddon # Import service specific objects -from resources.lib.settings import selectLeagues, toggleNotification -from resources.lib.league_tables import XBMCLeagueTable -from resources.lib.live_scores_detail import XBMCLiveScoresDetail -from resources.lib.results import XBMCResults -from resources.lib.fixtures import XBMCFixtures -from resources.lib.utils import closeAddonSettings -from resources.lib.menu import FootballHelperMenu -from resources.lib.ticker import TickerOverlay - -# Import PyXBMCt module. -from pyxbmct.addonwindow import * +from resources.lib.settings import selectTeams +# from resources.lib.league_tables import XBMCLeagueTable +# from resources.lib.live_scores_detail import XBMCLiveScoresDetail +# from resources.lib.results import XBMCResults +# from resources.lib.fixtures import XBMCFixtures +# from resources.lib.utils import closeAddonSettings +# from resources.lib.menu import FootballHelperMenu +# from resources.lib.ticker import TickerOverlay +# +# # Import PyXBMCt module. +# from pyxbmct.addonwindow import * _A_ = xbmcaddon.Addon("service.bbclivefootballscores") _GET_ = _A_.getSetting _SET_ = _A_.setSetting -getwin = {"jsonrpc":"2.0", - "id":1, - "method":"GUI.GetProperties", - "params": - {"properties":["currentwindow"]} - } - -def ToggleTicker(): - - try: - tickers = json.loads(_GET_("currenttickers")) - except ValueError: - tickers = {} - - if not tickers: - tickers = {} - - # Get the current window ID - current_window = xbmc.executeJSONRPC(json.dumps(getwin)) - window_id = json.loads(current_window)["result"]["currentwindow"]["id"] - - # json doesn't like integer keys so we need to look for a unicode object - key = unicode(window_id) - - if key in tickers: - - # There's already a ticker on this window - # Remove it from our list but get the ID of the ticker first - tickerid = tickers.pop(key) - - # Get the window - w = xbmcgui.Window(window_id) - - # Find the ticker - t = w.getControl(tickerid) - - # and remove it - w.removeControl(t) - - else: - - # No ticker, so create one - ScoreTicker = TickerOverlay(window_id) - - # Show it - ScoreTicker.show() - - # Give it current text - tickertext = _GET_("ticker") - ScoreTicker.update(tickertext) - - # Add to our list of current active tickers - tickers[ScoreTicker.windowid] = ScoreTicker.id - - # Save our list - _SET_("currenttickers", json.dumps(tickers)) - +# getwin = {"jsonrpc":"2.0", +# "id":1, +# "method":"GUI.GetProperties", +# "params": +# {"properties":["currentwindow"]} +# } +# +# def ToggleTicker(): +# +# try: +# tickers = json.loads(_GET_("currenttickers")) +# except ValueError: +# tickers = {} +# +# if not tickers: +# tickers = {} +# +# # Get the current window ID +# current_window = xbmc.executeJSONRPC(json.dumps(getwin)) +# window_id = json.loads(current_window)["result"]["currentwindow"]["id"] +# +# # json doesn't like integer keys so we need to look for a unicode object +# key = unicode(window_id) +# +# if key in tickers: +# +# # There's already a ticker on this window +# # Remove it from our list but get the ID of the ticker first +# tickerid = tickers.pop(key) +# +# # Get the window +# w = xbmcgui.Window(window_id) +# +# # Find the ticker +# t = w.getControl(tickerid) +# +# # and remove it +# w.removeControl(t) +# +# else: +# +# # No ticker, so create one +# ScoreTicker = TickerOverlay(window_id) +# +# # Show it +# ScoreTicker.show() +# +# # Give it current text +# tickertext = xbmc.getInfoLabel("Skin.String(bbcscorestickertext)") +# tickertext = tickertext.decode("utf-8").replace("|", ",") +# ScoreTicker.update(unicode(tickertext)) +# +# # Add to our list of current active tickers +# tickers[ScoreTicker.windowid] = ScoreTicker.id +# +# # Save our list +# _SET_("currenttickers", json.dumps(tickers)) +# try: params = dict((x.split("=") for x in sys.argv[1].lower().split(";"))) except (ValueError, AttributeError, IndexError): params = {} - -# If no parameters are passed then we show default menu -if not params: - - menu = FootballHelperMenu() - menu.show() - - +# +# # If no parameters are passed then we show default menu +# if not params: +# +# menu = FootballHelperMenu() +# menu.show() +# menu = None +# +# # If there are parameters, let's see what we want to do... -if params.get("mode") == "selectleague": - - selectLeagues() - -elif params.get("mode") == "leaguetable": - - # Close addon setting window (if open) - closeAddonSettings() - - # Create an instance of the XBMC League Table - xlt = XBMCLeagueTable() - - # and display it! - xlt.start() - -elif params.get("mode") == "matchdetail": - - # Close addon setting window (if open) - closeAddonSettings() - - # Create an instance of the XBMC League Table - xlsd = XBMCLiveScoresDetail() - - # and display it! - xlsd.start() - -elif params.get("mode") == "results": - # Close addon setting window (if open) - closeAddonSettings() - - # Create an instance of the XBMC Results - xr = XBMCResults() - - # and display it! - xr.start() - -elif params.get("mode") == "fixtures": - # Close addon setting window (if open) - closeAddonSettings() - - # Create an instance of the XBMC Fixtures - xf = XBMCFixtures() - - # and display it! - xf.start() - -elif params.get("mode") == "toggleticker": - - ToggleTicker() +if params.get("mode") == "selectteams": + + selectTeams() +# +# elif params.get("mode") == "leaguetable": +# +# # Close addon setting window (if open) +# closeAddonSettings() +# +# # Create an instance of the XBMC League Table +# xlt = XBMCLeagueTable() +# +# # and display it! +# xlt.start() +# +# # Get rid of it when we're finished +# xlt = None +# +# elif params.get("mode") == "matchdetail": +# +# # Close addon setting window (if open) +# closeAddonSettings() +# +# # Create an instance of the XBMC League Table +# xlsd = XBMCLiveScoresDetail() +# +# # and display it! +# xlsd.start() +# +# # Get rid of it when we're finished +# xlsd = None +# +# elif params.get("mode") == "results": +# # Close addon setting window (if open) +# closeAddonSettings() +# +# # Create an instance of the XBMC Results +# xr = XBMCResults() +# +# # and display it! +# xr.start() +# +# # Get rid of it when we're finished +# xr = None +# +# elif params.get("mode") == "fixtures": +# # Close addon setting window (if open) +# closeAddonSettings() +# +# # Create an instance of the XBMC Fixtures +# xf = XBMCFixtures() +# +# # and display it! +# xf.start() +# +# # Get rid of it when we're finished +# xf = None +# +# elif params.get("mode") == "toggleticker": +# +# ToggleTicker() diff --git a/resources/language/English/strings.po b/resources/language/English/strings.po index 6e01a5e..772e58c 100644 --- a/resources/language/English/strings.po +++ b/resources/language/English/strings.po @@ -34,6 +34,14 @@ msgctxt "#32003" msgid "Notification display time (secs)" msgstr "" +msgctxt "#32004" +msgid "Select teams..." +msgstr "" + +msgctxt "#32005" +msgid "You must press 'ok' after selecting teams to save the selection" +msgstr "" + #empty ids from 32003 through 32019 #settings script messages diff --git a/resources/lib/api/footballscoresapi.py b/resources/lib/api/footballscoresapi.py deleted file mode 100644 index 3eaefeb..0000000 --- a/resources/lib/api/footballscoresapi.py +++ /dev/null @@ -1,84 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -''' This script is part of the BBC Football Scores service by elParaguayo - -''' -import sys - -if sys.version_info >= (2, 7): - import json as json -else: - import simplejson as json - -import xbmc -import xbmcgui -import xbmcaddon - -class LiveScoresAPI(object): - - def __init__(self): - - self.__addon = xbmcaddon.Addon("service.bbclivefootballscores") - self._getS = self.__addon.getSetting - - def __loadLeagues(self): - '''See if there are any previously selected leagues. - - Returns list of league IDs. - ''' - - try: - watchedleagues = json.loads(str(self._getS("watchedleagues"))) - except: - watchedleagues = [] - - return watchedleagues - - def __saveLeagues(self, leagues): - '''Converts list to JSON compatible string and saves it to our - user's settings. - ''' - - rawdata = json.dumps(leagues) - self.__addon.setSetting(id="watchedleagues",value=rawdata) - - def isFollowing(self, leagueID): - - return leagueID in self.__loadLeagues() - - def addLeague(self, leagueID): - - all = self.__loadLeagues() - - if not leagueID in all: - all.append(leagueID) - self.__saveLeagues(all) - return True - - else: - return False - - def removeLeague(self, leagueID): - - all = self.__loadLeagues() - - if leagueID in all: - all.remove(leagueID) - self.__saveLeagues(all) - return True - - else: - return False diff --git a/resources/lib/fixtures.py b/resources/lib/fixtures.py deleted file mode 100644 index 1db95ca..0000000 --- a/resources/lib/fixtures.py +++ /dev/null @@ -1,322 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -''' This script is part of the BBC Football Scores service by elParaguayo - - It allows users to select which leagues they wish to receive updates - for. - - It is called via the script configuration screen or by passing - parameters to trigger specific functions. - - The script accepts the following parameters: - toggle: Turns score notifications on and off - reset: Resets watched league data - - NB only one parameter should be passed at a time. -''' -import sys - -if sys.version_info >= (2, 7): - import json as json - from collections import OrderedDict -else: - import simplejson as json - from resources.lib.ordereddict import OrderedDict - -import xbmc -import xbmcgui -import xbmcaddon - -from resources.lib.footballscores import Fixtures -from resources.lib.utils import closeAddonSettings - -# Import PyXBMCt module. -from pyxbmct.addonwindow import * - -_A_ = xbmcaddon.Addon("service.bbclivefootballscores") -_S_ = _A_.getSetting - -def localise(id): - '''Gets localised string. - - Shamelessly copied from service.xbmc.versioncheck - ''' - string = _A_.getLocalizedString(id).encode( 'utf-8', 'ignore' ) - return string - -class XBMCFixtures(object): - - def __init__(self): - - # It may take a bit of time to display menus/fixtures so let's - # make sure the user knows what's going on - self.prog = xbmcgui.DialogProgressBG() - self.prog.create(localise(32114), localise(32107)) - - # variables for league fixtures display - self.redraw = False - self.offset = 0 - self.menu = True - self.leagueid = 0 - - # variables for root menu - self.showall = True - self.active = True - - # Get our favourite leagues - self.watchedleagues = json.loads(str(_S_("watchedleagues"))) - - # Create a Fixtures instance - self.fixtures = Fixtures() - - self.prog.update(25, localise(32108)) - - allcomps = self.fixtures.getCompetitions() - - allcomps = [x for x in allcomps if x["id"][:11] == "competition"] - - # Get all of the available leagues, store it in an Ordered Dict - # key=League name - # value=League ID - self.allleagues = OrderedDict((x["name"], - x["id"][-9:]) - for x in allcomps) - - self.prog.update(75, localise(32109)) - - # Create a similar Ordered Dict for just those leagues that we're - # currently followin - lgid = x["id"][-9:] - self.watchedleagues = OrderedDict((x["name"], x["id"][-9:]) - for x in allcomps - if (unicode(lgid).isnumeric() and - int(lgid) in self.watchedleagues)) - - self.prog.close() - - - def showMenu(self, all_leagues=False): - - # Setting this to False means that the menu won't display if - # we hit escape - self.active = False - - # Set the title and menu size - window = AddonDialogWindow(localise(32100)) - window.setGeometry(450,300,5,4) - - # Create a List object - self.leaguelist = List() - - # Get the appropriate list of leagues depending on what mode we're in - displaylist = self.allleagues if self.showall else self.watchedleagues - - #self.prog.update(92) - - # Add the List to the menu - window.placeControl(self.leaguelist, 0, 0, rowspan=4, columnspan=4) - self.leaguelist.addItems(displaylist.keys()) - - # Bind the list action - p = self.leaguelist.getSelectedPosition - window.connect(self.leaguelist, - lambda w = window: - self.setID(self.leaguelist.getListItem(p()).getLabel(), - w)) - - # Don't think these are needed, but what the hell... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(94) - - # Create the button to toggle mode - leaguetext = localise(32101) if self.showall else localise(32102) - self.leaguebutton = Button(leaguetext) - window.placeControl(self.leaguebutton, 4, 0, columnspan=2) - - # Bind the button - window.connect(self.leaguebutton, lambda w=window: self.toggleMode(w)) - - #self.prog.update(96) - - # Add the close button - self.closebutton = Button(localise(32103)) - window.placeControl(self.closebutton, 4, 2, columnspan=2) - window.setFocus(self.leaguelist) - # Connect the button to a function. - window.connect(self.closebutton, lambda w=window:self.finish(w)) - - #self.prog.update(98) - - # Handle navigation to make user experience better - self.leaguelist.controlLeft(self.leaguebutton) - self.leaguelist.controlRight(self.closebutton) - self.closebutton.controlUp(self.leaguelist) - self.closebutton.controlLeft(self.leaguebutton) - self.leaguebutton.controlRight(self.closebutton) - self.leaguebutton.controlUp(self.leaguelist) - - # Ready to go... - window.doModal() - - def showFixtures(self): - - # Basic variables - self.redraw = False - - # If there are multiple fixture dates for a competition - # Let's just get the required one - fixtures = self.rawdata[self.offset] - - #self.prog.update(92) - - # How many fixtures are there on this date? - # We'll need this to set the size of the display - n = len(fixtures["fixtures"]) - - # Create a window instance and size it - window = AddonDialogWindow(fixtures["date"]) - window.setGeometry(450, (n + 4) * 30, n + 3, 11) - - #self.prog.update(94) - - # Add the teams - for i,f in enumerate(fixtures["fixtures"]): - - home = Label(u"{hometeam}".format(**f), alignment=1) - vlab = Label("v", alignment=2) - away = Label(u"{awayteam}".format(**f)) - - window.placeControl(home, i+1, 0, columnspan=5) - window.placeControl(vlab, i+1, 5) - window.placeControl(away, i+1, 6, columnspan=5) - - - #self.prog.update(94) - - # Add the close button - closebutton = Button(localise(32103)) - window.placeControl(closebutton, n+2, 4, columnspan=3) - window.setFocus(closebutton) - # Connect the button to a function. - window.connect(closebutton, lambda w=window: self.finish(w)) - - # Not sure we need these... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(96) - - # We may need some extra buttons - nextbutton = Button(localise(32104)) - prevbutton = Button(localise(32105)) - - # There are more fixtures after the ones we're showing - if self.offset < (len(self.rawdata) - 1): - window.placeControl(nextbutton, n+2, 7, columnspan=4) - window.connect(nextbutton, lambda w=window: self.next(w)) - nextbutton.controlLeft(closebutton) - closebutton.controlRight(nextbutton) - - # There are more fixtures before the ones we're showing - if self.offset > 0: - window.placeControl(prevbutton, n+2, 0, columnspan=4) - window.connect(prevbutton, lambda w=window: self.previous(w)) - prevbutton.controlRight(closebutton) - closebutton.controlLeft(prevbutton) - - #self.prog.close() - - # Ready to go... - window.doModal() - - def getFixturesData(self, ID): - - self.prog.create(localise(32114), localise(32115)) - try: - raw = self.fixtures.getFixtures("competition-%s" - % (self.leagueid)) - - except: - print "ERROR" - raw = None - - self.prog.close() - - return raw - - def setID(self, ID, w): - # Gets the ID of the selected league - ID = self.allleagues[ID] - self.setleague(ID,w) - - def next(self,w): - # Display the next fixture day in the competion - self.offset += 1 - self.redraw = True - w.close() - - def previous(self,w): - # Display the previous fixture day the competition - self.offset -= 1 - self.redraw = True - w.close() - - def finish(self,w): - # We're done. Gracefully close down menu. - self.redraw = False - self.menu = False - self.active = False - w.close() - - def setleague(self,lg, w): - # Set up the variables to display the league fixtures - self.leagueid = lg - self.offset = 0 - self.redraw = True - w.close() - self.rawdata = self.getFixturesData(self.leagueid) - self.prog.update(90) - - def toggleMode(self,w): - # Toggle between showing all competitions and just our favourites - self.showall = not self.showall - self.active = True - w.close() - - def start(self): - - # Let's begin - while self.active: - # Show the main menu - self.showMenu() - - while self.redraw: - - # Show fixtures - self.showFixtures() - -if __name__ == "__main__": - - # Close addon setting window (if open) - closeAddonSettings() - - # Create an instance of the XBMC Fixtures - xf = XBMCFixtures() - - # and display it! - xf.start() diff --git a/resources/lib/footballscores.py b/resources/lib/footballscores.py deleted file mode 100755 index 70bfc08..0000000 --- a/resources/lib/footballscores.py +++ /dev/null @@ -1,1289 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -import urllib2 -import string -from BeautifulSoup import BeautifulSoup -import re -from datetime import datetime, time -import json -import codecs - -__version__ = "0.3.0" - - -class matchcommon(object): - '''class for common functions for match classes.''' - - livescoreslink = ("http://www.bbc.co.uk/sport/shared/football/" - "live-scores/matches/{comp}/today") - - def getPage(self, url, sendresponse = False): - page = None - try: - user_agent = ('Mozilla/5.0 (Windows; U; Windows NT 6.1; ' - 'en-US; rv:1.9.1.5) Gecko/20091102 Firefox') - headers = { 'User-Agent' : user_agent } - request = urllib2.Request(url) - response = urllib2.urlopen(request) - page = response.read() - except: - pass - - if sendresponse: - return response - else: - # Fixed this line to handle accented team namess - return codecs.decode(page, "utf-8") if page else None - -class FootballMatch(matchcommon): - '''Class for getting details of individual football matches. - Data is pulled from BBC live scores page. - ''' - # self.accordionlink = "http://polling.bbc.co.uk/sport/shared/football/accordion/partial/collated" - - detailprefix = ("http://www.bbc.co.uk/sport/football/live/" - "partial/{id}") - - def __init__(self, team, detailed = False, data = None): - '''Creates an instance of the Match object. - Must be created by passing the name of one team. - - data - User can also send data to the class e.g. if multiple instances - of class are being run thereby saving http requests. Otherwise class - can handle request on its own. - - detailed - Do we want additional data (e.g. goal scorers, bookings)? - ''' - self.detailed = detailed - - # Set the relevant urls - self.detailedmatchpage = None - self.scorelink = None - - # Boolean to notify user if there is a valid match - self.matchfound = False - - # Which team am I following? - self.myteam = team - - self.__resetMatch() - - # Let's try and load some data - data = self.__loadData(data) - - # If our team is found or we have data - if data: - - # Update the class properties - self.__update(data) - # No notifications for now - self.goal = False - self.statuschange = False - self.newmatch = False - - def __getUKTime(self): - #api.geonames.org/timezoneJSON?formatted=true&lat=51.51&lng=0.13&username=demo&style=full - rawbbctime = self.getPage("http://api.geonames.org/timezoneJSON" - "?formatted=true&lat=51.51&lng=0.13&" - "username=elParaguayo&style=full") - - bbctime = json.loads(rawbbctime).get("time") if rawbbctime else None - - if bbctime: - servertime = datetime.strptime(bbctime, - "%Y-%m-%d %H:%M") - return servertime - - else: - - return None - - def __resetMatch(self): - '''Clear all variables''' - self.hometeam = None - self.awayteam = None - self.homescore = None - self.awayscore = None - self.scorelink = None - self.homescorers = None - self.awayscorers = None - self.homeyellowcards = [] - self.awayyellowcards = [] - self.homeredcards = [] - self.awayredcards = [] - self.competition = None - self.matchtime = None - self.status = None - self.goal = False - self.statuschange = False - self.newmatch = False - self.homebadge = None - self.awaybadge = None - self.matchid = None - self.matchlink = None - self.rawincidents = [] - self.booking = False - self.redcard = False - self.leagueid = None - - - def __findMatch(self): - leaguepage = self.getPage(self.livescoreslink.format(comp="")) - data = None - teamfound = False - - if leaguepage: - - # Start with the default page so we can get list of active leagues - raw = BeautifulSoup(leaguepage) - - # Find the list of active leagues - selection = raw.find("div", {"class": - "drop-down-filter live-scores-fixtures"}) - - - # Loop throught the active leagues - for option in selection.findAll("option"): - - # Build the link for that competition - league = option.get("value")[12:] - - if league: - scorelink = self.livescoreslink.format(comp=league) - - scorepage = self.getPage(scorelink) - - if scorepage: - # Prepare to process page - optionhtml = BeautifulSoup(scorepage) - - # We just want the live games... - live = optionhtml.find("div", {"id": "matches-wrapper"}) - - # Let's look for our team - if live.find(text=self.myteam): - teamfound = True - self.scorelink = scorelink - self.competition = option.text.split("(")[0].strip() - self.leagueid = league - data = live - break - - self.matchfound = teamfound - - return data - - def __getScores(self, data, update = False): - - for match in data.findAll("tr", {"id": re.compile(r'^match-row')}): - if match.find(text=self.myteam): - - self.hometeam = match.find("span", {"class": "team-home"}).text ## ENCODE - - self.awayteam = match.find("span", {"class": "team-away"}).text - - linkrow = match.find("td", {"class": "match-link"}) - try: - link = linkrow.find("a").get("href") - self.matchlink = "http://www.bbc.co.uk%s" % (link) - except AttributeError: - self.matchlink = None - - if match.get("class") == "fixture": - status = "Fixture" - matchtime = match.find("span", - {"class": - "elapsed-time"}).text.strip()[:5] - - elif match.get("class") == "report": - status = "FT" - matchtime = None - - elif ("%s" % - (match.find("span", - {"class": "elapsed-time"}).text.strip()) == "Half Time"): - status = "HT" - matchtime = None - - else: - status = "L" - matchtime = match.find("span", - {"class": "elapsed-time"}).text.strip() - - matchid = match.get("id")[10:] - - score = match.find("span", - {"class": "score"}).text.strip().split(" - ") - - try: - homescore = int(score[0].strip()) - awayscore = int(score[1].strip()) - - except: - homescore = 0 - awayscore = 0 - - self.statuschange = False - self.newmatch = False - self.goal=False - - if update: - - if not status == self.status: - self.statuschange = True - - if not matchid == self.matchid: - self.newmatch = True - - if not (homescore == self.homescore and - awayscore == self.awayscore): - # Gooooooooooooaaaaaaaaaaaaaaaaallllllllllllllllll! - self.goal = True - - self.status = status if status else None ## ENCODE - self.matchtime = matchtime if matchtime else None ## ENCODE - self.matchid = matchid if matchid else None ## ENCODE - self.homescore = homescore - self.awayscore = awayscore - - - def __update(self, data = None): - - self.__getScores(data) - - if self.detailed: - self.__getDetails() - - def __loadData(self, data = None): - - self.matchfound = False - - if data: - if data.find(text=self.myteam): - self.matchfound = True - else: - data = None - - if not data and self.scorelink: - scorepage = self.getPage(self.scorelink) - if scorepage: - scorehtml = BeautifulSoup(scorepage) - data = scorehtml.find("div", {"id": "matches-wrapper"}) - if data.find(text=self.myteam): - self.matchfound = True - else: - data = None - else: - data = None - - if not data: - data = self.__findMatch() - - if not data: - self.__resetMatch() - - return data - - def Update(self, data = None): - - data = self.__loadData(data) - - if data: - self.__getScores(data, update = True) - - if self.detailed: - self.__getDetails() - - def __getDetails(self): - - if self.matchid: - # Prepare bautiful soup to scrape match page - - - # Let's get the home and away team detail sections - try: - bs = BeautifulSoup(self.getPage(self.detailprefix.format( - id=self.matchid))) - incidents = bs.find("table", - {"class": "incidents-table"}).findAll("tr") - except: - incidents = None - - # Get incidents - # This populates variables with details of scorers and bookings - # Incidents are stored in a list of tuples: format is: - # [(Player Name, [times of incidents])] - hsc = [] - asc = [] - hyc = [] - ayc = [] - hrc = [] - arc = [] - - if incidents: - - self.__goalscorers = [] - self.__yellowcards = [] - self.__redcards = [] - - for incident in incidents: - i = incident.find("td", - {"class": - re.compile(r"\bincident-type \b")}) - if i: - h = incident.find("td", - {"class": - "incident-player-home"}).text.strip() - - a = incident.find("td", - {"class": - "incident-player-away"}).text.strip() - - t = incident.find("td", - {"class": - "incident-time"}).text.strip() - - if "goal" in i.get("class"): - if h: - hsc = self.__addIncident(hsc, h, t) ## ENCODE - self.__goalscorers.append((self.hometeam, h, t)) - self.__addRawIncident("home", "goal", h, t) - else: - asc = self.__addIncident(asc, a, t) - self.__goalscorers.append((self.awayteam, a, t)) - self.__addRawIncident("away", "goal", a, t) - - elif "yellow-card" in i.get("class"): - if h: - hyc = self.__addIncident(hyc, h, t) - self.__yellowcards.append((self.hometeam, h, t)) - self.__addRawIncident("home", "yellow", h, t) - else: - ayc = self.__addIncident(ayc, a, t) - self.__yellowcards.append((self.awayteam, a, t)) - self.__addRawIncident("away", "yellow", a, t) - - elif "red-card" in i.get("class"): - if h: - hrc = self.__addIncident(hrc, h, t) - self.__redcards.append((self.hometeam, h, t)) - self.__addRawIncident("home", "red", h, t) - else: - arc = self.__addIncident(arc, a, t) - self.__redcards.append((self.awayteam, a, t)) - self.__addRawIncident("away", "red", a, t) - - self.booking = not (self.homeyellowcards == hyc and - self.awayyellowcards == ayc) - - self.redcard = not (self.homeredcards == hrc and - self.awayredcards == arc) - - self.homescorers = hsc - self.awayscorers = asc - self.homeyellowcards = hyc - self.awayyellowcards = ayc - self.homeredcards = hrc - self.awayredcards = arc - - def __addIncident(self, incidentlist, player, incidenttime): - '''method to add incident to list variable''' - found = False - for incident in incidentlist: - if incident[0] == player: - incident[1].append(incidenttime) - found = True - break - - if not found: - incidentlist.append((player, [incidenttime])) - - return incidentlist - - def __addRawIncident(self, team, incidenttype, player, incidenttime): - - incident = (team, incidenttype, player, incidenttime) - - if not incident in self.rawincidents: - self.rawincidents.append(incident) - - def formatIncidents(self, incidentlist, newline = False): - '''Incidents are in the following format: - List: - [Tuple: - (Player name, [list of times of incidents])] - - This function converts the list into a string. - ''' - temp = [] - incidentjoin = "\n" if newline else ", " - - for incident in incidentlist: - temp.append("%s (%s)" % (incident[0], - ", ".join(incident[1]))) - - return incidentjoin.join(temp) - - def getTeamBadges(self): - found = False - - if self.matchlink: - badgepage = self.getPage(self.matchlink) - if badgepage: - linkpage = BeautifulSoup(badgepage) - badges = linkpage.findAll("div", {"class": "team-badge"}) - if badges: - self.homebadge = badges[0].find("img").get("src") - self.awaybadge = badges[1].find("img").get("src") - found = True - - return found - - - def __nonzero__(self): - - return self.matchfound - - def __repr__(self): - - return "FootballMatch(\'%s\', detailed=%s)" % (self.myteam, - self.detailed) - - def __eq__(self, other): - if isinstance(other, self.__class__): - if not self.matchid is None: - return self.matchid == other.matchid - else: - return self.myteam == other.myteam - else: - return False - - # Neater functions to return data: - - @property - def HomeTeam(self): - """Returns string of the home team's name - - """ - return self.hometeam - - @property - def AwayTeam(self): - """Returns string of the away team's name - - """ - return self.awayteam - - @property - def HomeScore(self): - """Returns the number of goals scored by the home team - - """ - return self.homescore - - @property - def AwayScore(self): - """Returns the number of goals scored by the away team - - """ - return self.awayscore - - @property - def Competition(self): - """Returns the name of the competition to which the match belongs - - e.g. "Premier League", "FA Cup" etc - - """ - return self.competition - - @property - def Status(self): - """Returns the status of the match - - e.g. "L", "HT", "FT" - - """ - if self.status == "Fixture": - return self.matchtime - else: - return self.status - - @property - def Goal(self): - """Boolean. Returns True if score has changed since last update - - """ - return self.goal - - @property - def StatusChanged(self): - """Boolean. Returns True if status has changed since last update - - e.g. Match started, half-time started etc - - """ - return self.statuschange - - @property - def NewMatch(self): - """Boolean. Returns True if the match found since last update - - """ - return self.newmatch - - @property - def MatchFound(self): - """Boolean. Returns True if a match is found in JSON feed - - """ - return self.matchfound - - @property - def HomeBadge(self): - """Returns link to image for home team's badge - - """ - return self.homebadge - - @property - def AwayBadge(self): - """Returns link to image for away team's badge - - """ - return self.awaybadge - - @property - def HomeScorers(self): - """Returns list of goalscorers for home team - - """ - return self.homescorers - - @property - def AwayScorers(self): - """Returns list of goalscorers for away team - - """ - return self.awayscorers - - @property - def HomeYellowCards(self): - """Returns list of players receiving yellow cards for home team - - """ - return self.homeyellowcards - - @property - def AwayYellowCards(self): - """Returns list of players receiving yellow cards for away team - - """ - return self.awayyellowcards - - @property - def HomeRedCards(self): - """Returns list of players sent off for home team - - """ - return self.homeredcards - - @property - def AwayRedCards(self): - """Returns list of players sent off for away team - - """ - return self.awayredcards - - @property - def LastGoalScorer(self): - if self.detailed: - if self.__goalscorers: - return self.__goalscorers[-1] - else: - return None - else: - return None - - @property - def LastYellowCard(self): - if self.detailed: - if self.__yellowcards: - return self.__yellowcards[-1] - else: - return None - else: - return None - - @property - def LastRedCard(self): - if self.detailed: - if self.__redcards: - return self.__redcards[-1] - else: - return None - else: - return None - - @property - def MatchDate(self): - """Returns date of match i.e. today's date - - """ - d = datetime.now() - datestring = "%s %d %s" % ( - d.strftime("%A"), - d.day, - d.strftime("%B %Y") - ) - return datestring - - @property - def MatchTime(self): - """If detailed info available, returns match time in minutes. - - If not, returns Status. - - """ - if self.status=="L" and self.matchtime is not None: - return self.matchtime - else: - return self.Status - - def abbreviate(self, cut): - """Returns short formatted summary of match but team names are - truncated according to the cut parameter. - - e.g. abbreviate(3): - "Ars 1-1 Che (L)" - - Should handle accented characters. - - """ - return u"%s %s-%s %s (%s)" % ( - self.hometeam[:cut], - self.homescore, - self.awayscore, - self.awayteam[:cut], - self.Status - ) - - def __unicode__(self): - """Returns short formatted summary of match. - - e.g. "Arsenal 1-1 Chelsea (L)" - - Should handle accented characters. - - """ - if self.matchfound: - - return u"%s %s-%s %s (%s)" % ( - self.hometeam, - self.homescore, - self.awayscore, - self.awayteam, - self.Status - ) - - else: - - return u"%s are not playing today." % (self.myteam) - - def __str__(self): - """Returns short formatted summary of match. - - e.g. "Arsenal 1-1 Chelsea (L)" - - """ - return unicode(self).encode('utf-8') - - @property - def PrintDetail(self): - """Returns detailed summary of match (if available). - - e.g. "(L) Arsenal 1-1 Chelsea (Arsenal: Wilshere 10', - Chelsea: Lampard 48')" - """ - if self.detailed: - hscore = False - scorerstring = "" - - if self.homescorers or self.awayscorers: - scorerstring = " (" - if self.homescorers: - hscore = True - scorerstring += "%s: %s" % (self.hometeam, - self.formatIncidents(self.homescorers)) - - - if self.awayscorers: - if hscore: - scorerstring += " - " - scorerstring += "%s: %s" % (self.awayteam, - self.formatIncidents(self.awayscorers)) - - scorerstring += ")" - - return "(%s) %s %s-%s %s%s" % ( - self.MatchTime, - self.hometeam, - self.homescore, - self.awayscore, - self.awayteam, - scorerstring - ) - else: - return self.__str__() - - @property - def TimeToKickOff(self): - '''Returns a timedelta object for the time until the match kicks off. - - Returns None if unable to parse match time or if match in progress. - - Should be unaffected by timezones as it gets current time from bbc - server which *should* be the same timezone as matches shown. - ''' - if self.status == "Fixture": - try: - koh = int(self.matchtime[:2]) - kom = int(self.matchtime[3:5]) - kickoff = datetime.combine( - datetime.now().date(), - time(koh, kom, 0)) - timetokickoff = kickoff - self.__getUKTime() - except Exception, e: - timetokickoff = None - finally: - pass - else: - timetokickoff = None - - return timetokickoff - - @property - def matchdict(self): - return {"hometeam": self.hometeam, - "awayteam": self.awayteam, - "status": self.status, - "matchtime": self.MatchTime, - "homescore": self.homescore, - "awayscore": self.awayscore, - "homescorers": self.homescorers, - "awayscorers": self.awayscorers, - "homeyellow": self.homeyellowcards, - "awayyellow": self.awayyellowcards, - "homered": self.homeredcards, - "awayred": self.awayredcards, - "incidentlist": self.rawincidents} - - - -class League(matchcommon): - '''Get summary of matches for a given league. - - NOTE: this may need to be updated as currently uses the accordion - source data whereas main Match module uses more complete source. - ''' - - accordionlink = ("http://polling.bbc.co.uk/sport/shared/football/" - "accordion/partial/collated") - - def __init__(self, league, detailed=False): - - self.__leaguematches = self.__getMatches(league,detailed=detailed) - self.__leagueid = league - self.__leaguename = self.__getLeagueName(league) - self.__detailed = detailed - - def __getData(self, league): - - scorelink = self.livescoreslink.format(comp=league) - data = None - # Prepare to process page - optionpage = self.getPage(scorelink) - if optionpage: - optionhtml = BeautifulSoup(optionpage) - - # We just want the live games... - data = optionhtml.find("div", {"id": "matches-wrapper"}) - - return data - - def __getLeagueName(self, league): - - leaguename = None - rawpage = self.getPage(self.livescoreslink.format(comp=league)) - - if rawpage: - raw = BeautifulSoup(rawpage) - - # Find the list of active leagues - selection = raw.find("div", - {"class": - "drop-down-filter live-scores-fixtures"}) - - if selection: - - selectedleague = selection.find("option", - {"selected": "selected"}) - - if selectedleague: - leaguename = selectedleague.text.split("(")[0].strip() - - return leaguename - - - @staticmethod - def getLeagues(): - leagues = [] - # raw = BeautifulSoup(self.getPage(self.accordionlink)) - # # Loop through all the competitions being played today - # for option in raw.findAll("option"): - # league = {} - # league["name"] = option.text - # league["id"] = option.get("value") - # leagues.append(league) - - # return leagues - livescoreslink = matchcommon().livescoreslink - - # Start with the default page so we can get list of active leagues - rawpage = matchcommon().getPage(livescoreslink.format(comp="")) - if rawpage: - raw = BeautifulSoup(rawpage) - - # Find the list of active leagues - selection = raw.find("div", - {"class": - "drop-down-filter live-scores-fixtures"}) - - # Loop throught the active leagues - for option in selection.findAll("option"): - - # Build the link for that competition - # league = option.get("value")[12:] - league = {} - league["name"] = option.text.split("(")[0].strip() - league["id"] = option.get("value")[12:].strip() - if league["id"]: - leagues.append(league) - - return leagues - - def __getMatches(self, league, detailed=False, data = None): - - if data is None: - data = self.__getData(league) - - matches = [] - if data: - rawmatches = data.findAll("tr", {"id": re.compile(r'^match-row')}) - else: - rawmatches = None - - if rawmatches: - - for match in rawmatches: - team = match.find("span", {"class": "team-home"}).text - m = FootballMatch(team, detailed=detailed, data=data) - matches.append(m) - - return matches - - def __repr__(self): - return "League(\'%s\', detailed=%s)" % (self.__leagueid, - self.__detailed) - - def __str__(self): - if self.__leaguematches: - if len(self.__leaguematches) == 1: - matches = "(1 match)" - else: - matches = "(%d matches)" % (len(self.__leaguematches)) - return "%s %s" % (self.__leaguename, matches) - else: - return None - - def __nonzero__(self): - return bool(self.__leaguematches) - - def Update(self): - '''Updates all matches in the league. - - If there are no games (e.g. a new day) then the old macthes are removed. - - If there are new games, these are added. - ''' - - # Get the data for league - data = self.__getData(self.__leagueid) - - # We've found some data so let's process - if data: - # Get a list of the current matches from the new data - currentmatches = self.__getMatches(self.__leagueid, data=data) - - # If the match is already in our league, then we keep it - self.__leaguematches = [m for m in self.__leaguematches if m in currentmatches] - - # Check if there are any matches in the new data which aren't in our list - newmatches = [m for m in currentmatches if m not in self.__leaguematches] - - # If so... - if newmatches: - # If we want detailed info on each match - if self.__detailed: - for m in newmatches: - - # then we need to update the flag for that match - m.detailed = True - - # and add it to our list - self.__leaguematches.append(m) - else: - # If not, then we can just add the new matches to our list - self.__leaguematches += newmatches - - # If we've got matches in our list - if self.__leaguematches: - for match in self.__leaguematches: - - # Update the matches - # NB we need to update each match to ensure the "Goal" - # flag is updated appropriately, rather than just adding a new match - # object. - match.Update(data=data) - - else: - # If there's no data, there are no matches... - self.__leaguematches = [] - - # If we haven't managed to set the league name yet - # then we should be able to find it if there are some matches - if self.__leaguematches and self.LeagueName is None: - self.LeagueName = self.__getLeagueName(self.__leagueid) - - @property - def LeagueMatches(self): - return self.__leaguematches - - @property - def LeagueName(self): - return self.__leaguename - - @property - def LeagueID(self): - return self.__leagueid - -class LeagueTable(matchcommon): - '''class to convert BBC league table format into python list/dict.''' - - leaguebase = "http://www.bbc.co.uk/sport/football/tables" - leaguemethod = "filter" - - def __init__(self): - #self.availableLeague = self.getLeagues() - pass - - def getLeagues(self): - '''method for getting list of available leagues''' - - leaguelist = [] - raw = BeautifulSoup(self.getPage(self.leaguebase)) - form = raw.find("div", {"class": "drop-down-filter", - "id": "filter-fixtures-no-js"}) - self.leaguemethod = form.find("select").get("name") - leagues = form.findAll("option") - for league in leagues: - l = {} - if league.get("value") != "" and not league.get("value").endswith("competition-"): - l["name"] = league.text - l["id"] = league.get("value") - leaguelist.append(l) - return leaguelist - - def getLeagueTable(self, leagueid): - '''method for creating league table of selected league.''' - - result = [] - - class LeagueTableTeam(object): - - def __init__(self, team): - - f = team.find - mov = re.compile(r"no-movement|moving-up|moving-down") - movmap = {"No movement": "same", - "Moving up": "up", - "Moving down": "down"} - self.name = f("td", {"class": "team-name"}).text - self.movement = movmap.get(f("span", {"class": mov}).text) - self.position = int(f("span", - {"class": "position-number"}).text) - self.played = int(f("td", {"class": "played"}).text) - self.won = int(f("td", {"class": "won"}).text) - self.drawn = int(f("td", {"class": "drawn"}).text) - self.lost = int(f("td", {"class": "lost"}).text) - self.goalsfor = int(f("td", {"class": "for"}).text) - self.goalsagainst = int(f("td", {"class": "against"}).text) - self.goaldifference = int(f("td", - {"class": "goal-difference"}).text) - self.points = int(f("td", {"class": "points"}).text) - - try: - lastgames = f("td", {"class": "last-10-games"}) - lg = [] - for game in lastgames.findAll("li"): - g = {} - g["result"] = game.get("class") - g["score"] = game.get("data-result") - g["opponent"] = game.get("data-against") - g["date"] = game.get("data-date") - g["summary"] = game.get("title") - lg.append(g) - self.lasttengames = lg - - except: - self.lasttengames = [] - - def __repr__(self): - return "" % self.name - - def __str__(self): - return "%d %s %d" % (self.position, - self.name, - self.points) - - leaguepage = "%s?%s=%s" % (self.leaguebase, - self.leaguemethod, - leagueid) - - raw = BeautifulSoup(self.getPage(leaguepage)) - - for table in raw.findAll("div", {"class": "league-table full-table-wide"}): - - lg = {} - teamlist = [] - - leaguename = table.find("h2", {"class": "table-header"}) - - for tag in ["div", "script"]: - for nest in leaguename.findAll(tag): - nest.extract() - - lg["name"] = leaguename.text.strip() - - for team in table.findAll("tr", {"id": re.compile(r'team')}): - t = LeagueTableTeam(team) - teamlist.append(t) - - lg["table"] = teamlist - result.append(lg) - - return result - -class Teams(matchcommon): - - def getTeams(self): - # Start with the default page so we can get list of active leagues - rawpage = self.getPage(self.livescoreslink.format(comp="")) - teamlist = [] - - if rawpage: - raw = BeautifulSoup(rawpage) - - # Find the list of active leagues - selection = raw.find("div", {"class": - "drop-down-filter live-scores-fixtures"}) - - # Loop throught the active leagues - for option in selection.findAll("option"): - - # Build the link for that competition - league = option.get("value")[12:] - - if league: - scorelink = self.livescoreslink.format(comp=league) - - # Prepare to process page - scorepage = self.getPage(scorelink) - if scorepage: - optionhtml = BeautifulSoup(scorepage) - - # We just want the live games... - live = optionhtml.find("div", - {"id": "matches-wrapper"}) - - for match in live.findAll("tr", - {"id": re.compile(r'^match-row')}): - - teamlist.append(match.find("span", - {"class": "team-home"}).text) - - teamlist.append(match.find("span", - {"class": "team-away"}).text) - - - teamlist = sorted(teamlist) - - return teamlist - -class Results(matchcommon): - - '''class to convert BBC league table format into python list/dict.''' - - resultbase = "http://www.bbc.co.uk/sport/football/results" - resultmethod = "filter" - - def __init__(self): - pass - - def getCompetitions(self): - '''method for getting list of available results pages''' - - complist = [] - raw = BeautifulSoup(self.getPage(self.resultbase)) - form = raw.find("div", {"class": "drop-down-filter", - "id": "filter-fixtures-no-js"}) - self.resultmethod = form.find("select").get("name") - comps = form.findAll("option") - for comp in comps: - l = {} - if comp.get("value") <> "": - l["name"] = comp.text - l["id"] = comp.get("value") - complist.append(l) - return complist - - def getResults(self, compid): - '''method for creating league table of selected league.''' - - result = [] - - leaguepage = "%s?%s=%s" % (self.resultbase, - self.resultmethod, - compid) - - raw = BeautifulSoup(self.getPage(leaguepage)) - - raw = raw.find("div", {"class": re.compile(r"\bfixtures-table\b")}) - - while raw.find("h2", {"class": "table-header"}) is not None: - - - resultdate = raw.find("h2", {"class": "table-header"}) - matchdate = resultdate.text.strip() - resultdate.extract() - - results = raw.find("table", {"class": "table-stats"}) - - matches = [] - - for matchresult in results.findAll("tr", {"id": re.compile(r'^match-row')}): - hometeam = matchresult.find("span", {"class": re.compile(r'^team-home')}).text.strip() - awayteam = matchresult.find("span", {"class": re.compile(r'^team-away')}).text.strip() - score = matchresult.find("span", {"class": "score"}).text.strip() - matches.append({"hometeam": hometeam, - "awayteam": awayteam, - "score": score}) - - resultday = {"date": matchdate, - "results": matches} - - result.append(resultday) - - results.extract() - - return result - - -class Fixtures(matchcommon): - - '''class to convert BBC league table format into python list/dict.''' - - fixturebase = "http://www.bbc.co.uk/sport/football/fixtures" - fixturemethod = "filter" - - def __init__(self): - pass - - def getCompetitions(self): - '''method for getting list of available results pages''' - - complist = [] - raw = BeautifulSoup(self.getPage(self.fixturebase)) - form = raw.find("div", {"class": "drop-down-filter", - "id": "filter-fixtures-no-js"}) - self.fixturemethod = form.find("select").get("name") - comps = form.findAll("option") - for comp in comps: - l = {} - if comp.get("value") <> "": - l["name"] = comp.text - l["id"] = comp.get("value") - complist.append(l) - return complist - - def getFixtures(self, compid): - '''method for creating league table of selected league.''' - - result = [] - - leaguepage = "%s?%s=%s" % (self.fixturebase, - self.fixturemethod, - compid) - - raw = BeautifulSoup(self.getPage(leaguepage)) - - raw = raw.find("div", {"class": re.compile(r"\bfixtures-table\b")}) - - while raw.find("h2", {"class": "table-header"}) is not None: - - - fixturedate = raw.find("h2", {"class": "table-header"}) - matchdate = fixturedate.text.strip() - fixturedate.extract() - - results = raw.find("table", {"class": "table-stats"}) - - matches = [] - - for matchresult in results.findAll("tr", {"id": re.compile(r'^match-row')}): - hometeam = matchresult.find("span", {"class": re.compile(r'^team-home')}).text.strip() - awayteam = matchresult.find("span", {"class": re.compile(r'^team-away')}).text.strip() - matches.append({"hometeam": hometeam, - "awayteam": awayteam}) - - resultday = {"date": matchdate, - "fixtures": matches} - - result.append(resultday) - - results.extract() - - return result - -def getAllLeagues(): - - tableleagues = LeagueTable().getLeagues() - tableleagues = [{"name": x["name"], "id": x["id"][12:]} for x in tableleagues] - matchleagues = League.getLeagues() - - tableleagues += [x for x in matchleagues if x not in tableleagues] - - return tableleagues diff --git a/resources/lib/league_tables.py b/resources/lib/league_tables.py deleted file mode 100644 index 0eefe62..0000000 --- a/resources/lib/league_tables.py +++ /dev/null @@ -1,293 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -''' This script is part of the BBC Football Scores service by elParaguayo - -''' -import sys - -if sys.version_info >= (2, 7): - import json as json - from collections import OrderedDict -else: - import simplejson as json - from resources.lib.ordereddict import OrderedDict - -import xbmc -import xbmcgui -import xbmcaddon - -from resources.lib.footballscores import LeagueTable -from resources.lib.utils import closeAddonSettings - -# Import PyXBMCt module. -from pyxbmct.addonwindow import * - -_A_ = xbmcaddon.Addon("service.bbclivefootballscores") -_S_ = _A_.getSetting - -def localise(id): - '''Gets localised string. - - Shamelessly copied from service.xbmc.versioncheck - ''' - string = _A_.getLocalizedString(id).encode( 'utf-8', 'ignore' ) - return string - -class XBMCLeagueTable(object): - - def __init__(self): - - # It may take a bit of time to display menus/tables so let's - # make sure the user knows what's going on - self.prog = xbmcgui.DialogProgressBG() - self.prog.create(localise(32106), localise(32107)) - - # variables for league table display - self.redraw = False - self.offset = 0 - self.menu = True - self.leagueid = 0 - - # variables for root menu - self.showall = True - self.active = True - - # Get our favourite leagues - self.watchedleagues = json.loads(str(_S_("watchedleagues"))) - - # Create a Leaue Table instance - self.league = LeagueTable() - - self.prog.update(25, localise(32108)) - - # Get all of the available leagues, store it in an Ordered Dict - # key=League name - # value=League ID - self.allleagues = OrderedDict((x["name"], - x["id"][-9:]) - for x in self.league.getLeagues()) - - self.prog.update(75, localise(32109)) - - # Create a similar Ordered Dict for just those leagues that we're - # currently followin - self.watchedleagues = OrderedDict((x["name"], x["id"][-9:]) - for x in self.league.getLeagues() - if int(x["id"][-9:]) - in self.watchedleagues) - - self.prog.close() - - - def showMenu(self, all_leagues=False): - - - - # Setting this to False means that the menu won't display if - # we hit escape - self.active = False - - # Set the title and menu size - window = AddonDialogWindow(localise(32100)) - window.setGeometry(450,300,5,4) - - # Create a List object - self.leaguelist = List() - - # Get the appropriate list of leagues depending on what mode we're in - displaylist = self.allleagues if self.showall else self.watchedleagues - - #self.prog.update(92) - - # Add the List to the menu - window.placeControl(self.leaguelist, 0, 0, rowspan=4, columnspan=4) - self.leaguelist.addItems(displaylist.keys()) - - # Bind the list action - p = self.leaguelist.getSelectedPosition - window.connect(self.leaguelist, - lambda w = window: - self.setID(self.leaguelist.getListItem(p()).getLabel(), - w)) - - # Don't think these are needed, but what the hell... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(94) - - # Create the button to toggle mode - leaguetext = localise(32101) if self.showall else localise(32102) - self.leaguebutton = Button(leaguetext) - window.placeControl(self.leaguebutton, 4, 0, columnspan=2) - - # Bind the button - window.connect(self.leaguebutton, lambda w=window: self.toggleMode(w)) - - #self.prog.update(96) - - # Add the close button - self.closebutton = Button(localise(32103)) - window.placeControl(self.closebutton, 4, 2, columnspan=2) - window.setFocus(self.leaguelist) - # Connect the button to a function. - window.connect(self.closebutton, lambda w=window:self.finish(w)) - - #self.prog.update(98) - - # Handle navigation to make user experience better - self.leaguelist.controlLeft(self.leaguebutton) - self.leaguelist.controlRight(self.closebutton) - self.closebutton.controlUp(self.leaguelist) - self.closebutton.controlLeft(self.leaguebutton) - self.leaguebutton.controlRight(self.closebutton) - self.leaguebutton.controlUp(self.leaguelist) - - # Ready to go... - window.doModal() - - def showLeagueTable(self): - - # Basic variables - self.redraw = False - - # If there are multiple tables for a competition (e.g. World Cup) - # Let's just get the required one - table = self.rawleaguedata[self.offset] - - #self.prog.update(92) - - # How many rows are in the table? - # We'll need this to set the size of the display - n = len(table["table"]) - - # Create a window instance and size it - window = AddonDialogWindow(table["name"]) - window.setGeometry(450, (n + 4) * 25, n + 3, 4) - - #self.prog.update(94) - - # Add the teams - for i, t in enumerate(table["table"]): - pos = Label(str(t.position)) - team = Label(t.name) - points = Label(str(t.points), alignment=ALIGN_RIGHT) - window.placeControl(pos,i+1,0) - window.placeControl(team,i+1,1, columnspan=2) - window.placeControl(points,i+1,3) - - #self.prog.update(94) - - # Add the close button - closebutton = Button(localise(32103)) - window.placeControl(closebutton, n+2, 1, columnspan=2) - window.setFocus(closebutton) - # Connect the button to a function. - window.connect(closebutton, lambda w=window: self.finish(w)) - - # Not sure we need these... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(96) - - # We may need some extra buttons (for multiple table competitions) - nextbutton = Button(localise(32104)) - prevbutton = Button(localise(32105)) - - # There are more leagues after the one we're showing - if self.offset < (len(self.rawleaguedata) - 1): - window.placeControl(nextbutton, n+2,3) - window.connect(nextbutton, lambda w=window: self.next(w)) - nextbutton.controlLeft(closebutton) - closebutton.controlRight(nextbutton) - - # There are more leagues before the one we're showing - if self.offset > 0: - window.placeControl(prevbutton, n+2,0) - window.connect(prevbutton, lambda w=window: self.previous(w)) - prevbutton.controlRight(closebutton) - closebutton.controlLeft(prevbutton) - - #self.prog.close() - - # Ready to go... - window.doModal() - - def getLeagueTableData(self, ID): - - self.prog.create(localise(32106), localise(32111)) - try: - raw = self.league.getLeagueTable("competition-%s" - % (self.leagueid)) - - except: - raw = None - - self.prog.close() - - return raw - - def setID(self, ID, w): - # Gets the ID of the selected league - ID = self.allleagues[ID] - self.setleague(ID,w) - - def next(self,w): - # Display the next table in the competion - self.offset += 1 - self.redraw = True - w.close() - - def previous(self,w): - # Display the previous tablein the competition - self.offset -= 1 - self.redraw = True - w.close() - - def finish(self,w): - # We're done. Gracefully close down menu. - self.redraw = False - self.menu = False - self.active = False - w.close() - - def setleague(self,lg, w): - # Set up the variables to display the league table - self.leagueid = lg - self.offset = 0 - self.redraw = True - w.close() - self.rawleaguedata = self.getLeagueTableData(self.leagueid) - self.prog.update(90) - - def toggleMode(self,w): - # Toggle between showing all competitions and just our favourites - self.showall = not self.showall - self.active = True - w.close() - - def start(self): - - # Let's begin - while self.active: - # Show the main menu - self.showMenu() - - while self.redraw: - - # Show a league table - self.showLeagueTable() diff --git a/resources/lib/live_scores_detail.py b/resources/lib/live_scores_detail.py deleted file mode 100644 index fdc44e9..0000000 --- a/resources/lib/live_scores_detail.py +++ /dev/null @@ -1,383 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -''' This script is part of the BBC Football Scores service by elParaguayo - -''' -import sys -import os - -if sys.version_info >= (2, 7): - import json as json - from collections import OrderedDict -else: - import simplejson as json - from resources.lib.ordereddict import OrderedDict - -import xbmc -import xbmcgui -import xbmcaddon - -from resources.lib.footballscores import League -from resources.lib.utils import closeAddonSettings - -# Import PyXBMCt module. -from pyxbmct.addonwindow import * - -_A_ = xbmcaddon.Addon("service.bbclivefootballscores") -_S_ = _A_.getSetting -pluginPath = _A_.getAddonInfo("path") - -def imgloc(img): - return os.path.join(pluginPath, "resources", "media" , img) - -imagedict = {"goal": imgloc("ball-white.png"), - "yellow": imgloc("yellow-card.png"), - "red": imgloc("red-card.png")} - -def localise(id): - '''Gets localised string. - - Shamelessly copied from service.xbmc.versioncheck - ''' - string = _A_.getLocalizedString(id).encode( 'utf-8', 'ignore' ) - return string - -class XBMCLiveScoresDetail(object): - - def __init__(self): - - # It may take a bit of time to display menus/tables so let's - # make sure the user knows what's going on - self.prog = xbmcgui.DialogProgressBG() - self.prog.create(localise(32106), localise(32107)) - - # variables for league table display - self.redraw = False - self.menu = True - self.leagueid = 0 - - # variables for root menu - self.showall = True - self.active = True - - # Get our favourite leagues - self.watchedleagues = json.loads(str(_S_("watchedleagues"))) - - self.prog.update(25, localise(32108)) - self.activeleagues = League.getLeagues() - self.favouriteleagues = [x for x in self.activeleagues if int(x["id"]) in self.watchedleagues] - - # Get all of the available leagues, store it in an Ordered Dict - # key=League name - # value=League ID - self.allleagues = OrderedDict((x["name"], - x["id"]) - for x in self.activeleagues) - - self.prog.update(75, localise(32109)) - - # Create a similar Ordered Dict for just those leagues that we're - # currently followin - self.watchedleagues = OrderedDict((x["name"], x["id"]) - for x in self.favouriteleagues) - - self.prog.close() - - - def showMenu(self, all_leagues=False): - - - - # Setting this to False means that the menu won't display if - # we hit escape - self.active = False - - # Set the title and menu size - window = AddonDialogWindow(localise(32100)) - window.setGeometry(450,300,5,4) - - # Create a List object - self.leaguelist = List() - - # Get the appropriate list of leagues depending on what mode we're in - displaylist = self.allleagues if self.showall else self.watchedleagues - - #self.prog.update(92) - - # Add the List to the menu - window.placeControl(self.leaguelist, 0, 0, rowspan=4, columnspan=4) - self.leaguelist.addItems(displaylist.keys()) - - # Bind the list action - p = self.leaguelist.getSelectedPosition - window.connect(self.leaguelist, - lambda w = window: - self.setID(self.leaguelist.getListItem(p()).getLabel(), - w)) - - # Don't think these are needed, but what the hell... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(94) - - # Create the button to toggle mode - leaguetext = localise(32101) if self.showall else localise(32102) - self.leaguebutton = Button(leaguetext) - window.placeControl(self.leaguebutton, 4, 0, columnspan=2) - - # Bind the button - window.connect(self.leaguebutton, lambda w=window: self.toggleMode(w)) - - #self.prog.update(96) - - # Add the close button - self.closebutton = Button(localise(32103)) - window.placeControl(self.closebutton, 4, 2, columnspan=2) - window.setFocus(self.leaguelist) - # Connect the button to a function. - window.connect(self.closebutton, lambda w=window:self.finish(w)) - - #self.prog.update(98) - - # Handle navigation to make user experience better - self.leaguelist.controlLeft(self.leaguebutton) - self.leaguelist.controlRight(self.closebutton) - self.closebutton.controlUp(self.leaguelist) - self.closebutton.controlLeft(self.leaguebutton) - self.leaguebutton.controlRight(self.closebutton) - self.leaguebutton.controlUp(self.leaguelist) - - # self.prog.close() - - # Ready to go... - window.doModal() - - def showLiveMatches(self): - - # Basic variables - self.redraw = False - - # If there are multiple tables for a competition (e.g. World Cup) - # Let's just get the required one - matches = self.rawdata.LeagueMatches - - #self.prog.update(92) - - # How many rows are in the table? - # We'll need this to set the size of the display - n = min(len(matches),8) - - # Create a window instance and size it - window = AddonDialogWindow("Select Match") - window.setGeometry(450, 450, 8, 4) - - #self.prog.update(94) - - self.matchlist = List() - - window.placeControl(self.matchlist, 0, 0, rowspan=7, columnspan=4) - self.matchlist.addItems([unicode(x) for x in matches]) - - # Bind the list action - p = self.matchlist.getSelectedPosition - window.connect(self.matchlist, - lambda w = window: - self.setMatch(p(), - w)) - #self.matchlist.addItems(["2","3"]) - - # Add the teams - # for i, m in enumerate(matches): - # match = Button(unicode(m)) - # window.placeControl(match,i+1,0, columnspan=4) - - #self.prog.update(94) - - # Add the close button - closebutton = Button(localise(32103)) - window.placeControl(closebutton, 7, 1, columnspan=2) - window.setFocus(self.matchlist) - # Connect the button to a function. - window.connect(closebutton, lambda w=window: self.finish(w)) - - # Not sure we need these... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(96) - - self.matchlist.controlLeft(closebutton) - self.matchlist.controlRight(closebutton) - closebutton.controlUp(self.matchlist) - - # We may need some extra buttons (for multiple table competitions) - nextbutton = Button(localise(32104)) - prevbutton = Button(localise(32105)) - - # Ready to go... - window.doModal() - - def showMatchDetail(self, match): - - # Basic variables - self.redraw = False - - match.detailed = True - match.Update() - - homeincidents = [x for x in match.rawincidents if x[0]=="home"] - awayincidents = [x for x in match.rawincidents if x[0]=="away"] - - n = max(len(homeincidents), len(awayincidents)) - - # Create a window instance and size it - window = AddonDialogWindow("Match Detail") - window.setGeometry(700, 190 + (n * 30), n + 4, 11) - - #self.prog.update(94) - - homelabel = Label(u"[B]{0}[/B]".format(match.HomeTeam), alignment=ALIGN_CENTER) - awaylabel = Label(u"[B]{0}[/B]".format(match.AwayTeam), alignment=ALIGN_CENTER) - scorelabel = Label("[B]{homescore} - {awayscore}[/B]".format(**match.matchdict), alignment=ALIGN_CENTER) - - window.placeControl(homelabel, 0, 0, columnspan=5) - window.placeControl(awaylabel, 0, 6, columnspan=5) - window.placeControl(scorelabel, 1, 4, columnspan=3) - - # Add the incidents - for i, m in enumerate(homeincidents): - #t = Label(m[1][0], alignment=ALIGN_CENTER_X) - t = Image(imagedict[m[1]], aspectRatio=2) - p = Label(m[2], alignment=ALIGN_RIGHT) - c = Label(m[3], alignment=ALIGN_CENTER_X) - - window.placeControl(t,i+2,0) - window.placeControl(p,i+2,1, columnspan=3) - window.placeControl(c,i+2,4) - - for i, m in enumerate(awayincidents): - t = Image(imagedict[m[1]], aspectRatio=2) - p = Label(m[2], alignment=ALIGN_RIGHT) - c = Label(m[3], alignment=ALIGN_CENTER_X) - - window.placeControl(t,i+2,6) - window.placeControl(p,i+2,7, columnspan=3) - window.placeControl(c,i+2,10) - - #self.prog.update(94) - - # Add the close button - closebutton = Button(localise(32103)) - window.placeControl(closebutton, n+3, 6, columnspan=4) - window.setFocus(closebutton) - # Connect the button to a function. - window.connect(closebutton, lambda w=window: self.finish(w)) - - # Choose another competition - compbutton = Button("Different Game") - window.placeControl(compbutton, n+3, 1, columnspan=4) - # Connect the button to a function. - window.connect(compbutton, lambda w=window: self.back(w)) - - # Not sure we need these... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(96) - - # We may need some extra buttons (for multiple table competitions) - nextbutton = Button(localise(32104)) - prevbutton = Button(localise(32105)) - - closebutton.controlLeft(compbutton) - compbutton.controlRight(closebutton) - - # Ready to go... - window.doModal() - - def getLiveMatches(self, ID): - - self.prog.create(localise(32106), localise(32111)) - try: - raw = League(self.leagueid) - - except: - raw = None - - self.prog.close() - - return raw - - def back(self, w): - self.redraw = True - w.close() - - def setID(self, ID, w): - # Gets the ID of the selected league - ID = self.allleagues[ID] - self.setleague(ID,w) - - def setMatch(self, ID, w): - m = self.rawdata.LeagueMatches[ID] - w.close() - self.showMatchDetail(m) - - def next(self,w): - # Display the next table in the competion - self.offset += 1 - self.redraw = True - w.close() - - def previous(self,w): - # Display the previous tablein the competition - self.offset -= 1 - self.redraw = True - w.close() - - def finish(self,w): - # We're done. Gracefully close down menu. - self.redraw = False - self.menu = False - self.active = False - w.close() - - def setleague(self,lg, w): - # Set up the variables to display the league table - self.leagueid = lg - self.offset = 0 - self.redraw = True - w.close() - self.rawdata = self.getLiveMatches(self.leagueid) - self.prog.update(90) - - def toggleMode(self,w): - # Toggle between showing all competitions and just our favourites - self.showall = not self.showall - self.active = True - w.close() - - def start(self): - - # Let's begin - while self.active: - # Show the main menu - self.showMenu() - - while self.redraw: - - # Show a league table - self.showLiveMatches() diff --git a/resources/lib/menu.py b/resources/lib/menu.py deleted file mode 100644 index 04da85c..0000000 --- a/resources/lib/menu.py +++ /dev/null @@ -1,108 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -''' This script is part of the BBC Football Scores service by elParaguayo - -''' -import xbmc -import xbmcaddon - -# Import PyXBMCt module. -from pyxbmct.addonwindow import * - -_A_ = xbmcaddon.Addon("service.bbclivefootballscores") -_S_ = _A_.getSetting - -def localise(id): - '''Gets localised string. - - Shamelessly copied from service.xbmc.versioncheck - ''' - string = _A_.getLocalizedString(id).encode( 'utf-8', 'ignore' ) - return string - -class FootballHelperMenu(object): - - def __init__(self): - - pass - - def show(self): - - self.control_list = [] - - # Set the title and menu size - self.window = AddonDialogWindow("BBC Football Scores") - self.window.setGeometry(450,300,5,2) - self.control_list.append(self.window) - - # ITEM 1 - LEAGUE TABLES - self.ltbutton = Button("Show League Tables") - self.window.placeControl(self.ltbutton, 0, 0, columnspan = 2) - - # ITEM 2 - MATCH DETAIL - self.mdbutton = Button("Show Match Detail") - self.window.placeControl(self.mdbutton, 1, 0, columnspan = 2) - - # ITEM 3 - MATCH DETAIL - self.resbutton = Button("Show Results") - self.window.placeControl(self.resbutton, 2, 0, columnspan = 2) - - # ITEM 4 - MATCH DETAIL - self.fixbutton = Button("Show Fixtures") - self.window.placeControl(self.fixbutton, 3, 0, columnspan = 2) - - # CLOSE BUTTON - - self.clbutton = Button("Close") - self.window.placeControl(self.clbutton, 4, 0, columnspan = 2) - - # add buttons to control_list - self.control_list += [self.ltbutton, self.mdbutton, - self.resbutton, self.fixbutton] - - # Bind actions - self.window.connect(ACTION_PREVIOUS_MENU, lambda: self.window.close()) - self.window.connect(ACTION_NAV_BACK, lambda: self.window.close()) - self.window.connect(self.clbutton, lambda: self.window.close()) - self.window.connect(self.ltbutton, lambda: self.open("leaguetable")) - self.window.connect(self.mdbutton, lambda: self.open("matchdetail")) - self.window.connect(self.resbutton, lambda: self.open("results")) - self.window.connect(self.fixbutton, lambda: self.open("fixtures")) - - self.window.setFocus(self.ltbutton) - - # Handle navigation to make user experience better - self.ltbutton.controlDown(self.mdbutton) - self.mdbutton.controlUp(self.ltbutton) - self.mdbutton.controlDown(self.resbutton) - self.resbutton.controlDown(self.fixbutton) - self.resbutton.controlUp(self.mdbutton) - self.fixbutton.controlDown(self.clbutton) - self.fixbutton.controlUp(self.resbutton) - self.clbutton.controlUp(self.fixbutton) - - - # Ready to go... - self.window.doModal() - - # clean up - for control in self.control_list: - del control - - def open(self, mode): - - self.window.close() - xbmc.executebuiltin("RunScript(service.bbclivefootballscores, mode={0})".format(mode)) diff --git a/resources/lib/results.py b/resources/lib/results.py deleted file mode 100644 index a9266c7..0000000 --- a/resources/lib/results.py +++ /dev/null @@ -1,332 +0,0 @@ -''' - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - -''' This script is part of the BBC Football Scores service by elParaguayo - - It allows users to select which leagues they wish to receive updates - for. - - It is called via the script configuration screen or by passing - parameters to trigger specific functions. - - The script accepts the following parameters: - toggle: Turns score notifications on and off - reset: Resets watched league data - - NB only one parameter should be passed at a time. -''' -import sys - -if sys.version_info >= (2, 7): - import json as json - from collections import OrderedDict -else: - import simplejson as json - from resources.lib.ordereddict import OrderedDict - -import xbmc -import xbmcgui -import xbmcaddon - -from resources.lib.footballscores import Results -from resources.lib.utils import closeAddonSettings - -# Import PyXBMCt module. -from pyxbmct.addonwindow import * - -_A_ = xbmcaddon.Addon("service.bbclivefootballscores") -_S_ = _A_.getSetting - -def localise(id): - '''Gets localised string. - - Shamelessly copied from service.xbmc.versioncheck - ''' - string = _A_.getLocalizedString(id).encode( 'utf-8', 'ignore' ) - return string - -class XBMCResults(object): - - def __init__(self): - - # It may take a bit of time to display menus/tables so let's - # make sure the user knows what's going on - self.prog = xbmcgui.DialogProgressBG() - self.prog.create(localise(32112), localise(32107)) - - # variables for league table display - self.redraw = False - self.offset = 0 - self.menu = True - self.leagueid = 0 - - # variables for root menu - self.showall = True - self.active = True - - # Get our favourite leagues - self.watchedleagues = json.loads(str(_S_("watchedleagues"))) - - # Create a Leaue Table instance - self.results = Results() - - self.prog.update(25, localise(32108)) - - allcomps = self.results.getCompetitions() - - allcomps = [x for x in allcomps if x["id"][:11] == "competition"] - - # Get all of the available leagues, store it in an Ordered Dict - # key=League name - # value=League ID - self.allleagues = OrderedDict((x["name"], - x["id"][-9:]) - for x in allcomps) - - xbmc.log(str(self.allleagues)) - - self.prog.update(75, localise(32109)) - - # Create a similar Ordered Dict for just those leagues that we're - # currently followin - lgid = x["id"][-9:] - self.watchedleagues = OrderedDict((x["name"], x["id"][-9:]) - for x in allcomps - if (unicode(lgid).isnumeric() and - int(lgid) in self.watchedleagues)) - - self.prog.close() - - - def showMenu(self, all_leagues=False): - - - - # Setting this to False means that the menu won't display if - # we hit escape - self.active = False - - # Set the title and menu size - window = AddonDialogWindow(localise(32100)) - window.setGeometry(450,300,5,4) - - # Create a List object - self.leaguelist = List() - - # Get the appropriate list of leagues depending on what mode we're in - displaylist = self.allleagues if self.showall else self.watchedleagues - - #self.prog.update(92) - - # Add the List to the menu - window.placeControl(self.leaguelist, 0, 0, rowspan=4, columnspan=4) - self.leaguelist.addItems(displaylist.keys()) - - # Bind the list action - p = self.leaguelist.getSelectedPosition - window.connect(self.leaguelist, - lambda w = window: - self.setID(self.leaguelist.getListItem(p()).getLabel(), - w)) - - # Don't think these are needed, but what the hell... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(94) - - # Create the button to toggle mode - leaguetext = localise(32101) if self.showall else localise(32102) - self.leaguebutton = Button(leaguetext) - window.placeControl(self.leaguebutton, 4, 0, columnspan=2) - - # Bind the button - window.connect(self.leaguebutton, lambda w=window: self.toggleMode(w)) - - #self.prog.update(96) - - # Add the close button - self.closebutton = Button(localise(32103)) - window.placeControl(self.closebutton, 4, 2, columnspan=2) - window.setFocus(self.leaguelist) - # Connect the button to a function. - window.connect(self.closebutton, lambda w=window:self.finish(w)) - - #self.prog.update(98) - - # Handle navigation to make user experience better - self.leaguelist.controlLeft(self.leaguebutton) - self.leaguelist.controlRight(self.closebutton) - self.closebutton.controlUp(self.leaguelist) - self.closebutton.controlLeft(self.leaguebutton) - self.leaguebutton.controlRight(self.closebutton) - self.leaguebutton.controlUp(self.leaguelist) - - # Ready to go... - window.doModal() - - def showResults(self): - - # Basic variables - self.redraw = False - - # If there are multiple tables for a competition (e.g. World Cup) - # Let's just get the required one - table = self.rawdata[self.offset] - - #self.prog.update(92) - - # How many rows are in the table? - # We'll need this to set the size of the display - n = len(table["results"]) - - # Create a window instance and size it - window = AddonDialogWindow(table["date"]) - window.setGeometry(450, (n + 4) * 30, n + 3, 15) - - #self.prog.update(94) - - # Add the teams - for i,r in enumerate(table["results"]): - try: - r["score"] = r["score"].replace("-", " - ") - except: - pass - - # result = Label(u"{hometeam} {score} {awayteam}".format(**r), - # alignment=2) - home = Label(u"{hometeam}".format(**r), alignment=1) - score = Label("{score}".format(**r), alignment=2) - away = Label(u"{awayteam}".format(**r)) - - window.placeControl(home, i+1, 0, columnspan=6) - window.placeControl(score, i+1, 6, columnspan=3) - window.placeControl(away, i+1, 9, columnspan=6) - - - #self.prog.update(94) - - # Add the close button - closebutton = Button(localise(32103)) - window.placeControl(closebutton, n+2, 5, columnspan=5) - window.setFocus(closebutton) - # Connect the button to a function. - window.connect(closebutton, lambda w=window: self.finish(w)) - - # Not sure we need these... - window.connect(ACTION_PREVIOUS_MENU, lambda w=window: self.finish(w)) - window.connect(ACTION_NAV_BACK, lambda w=window: self.finish(w)) - - #self.prog.update(96) - - # We may need some extra buttons (for multiple table competitions) - nextbutton = Button(localise(32104)) - prevbutton = Button(localise(32105)) - - # There are more leagues after the one we're showing - if self.offset < (len(self.rawdata) - 1): - window.placeControl(nextbutton, n+2, 10, columnspan=5) - window.connect(nextbutton, lambda w=window: self.next(w)) - nextbutton.controlLeft(closebutton) - closebutton.controlRight(nextbutton) - - # There are more leagues before the one we're showing - if self.offset > 0: - window.placeControl(prevbutton, n+2, 0, columnspan=5) - window.connect(prevbutton, lambda w=window: self.previous(w)) - prevbutton.controlRight(closebutton) - closebutton.controlLeft(prevbutton) - - #self.prog.close() - - # Ready to go... - window.doModal() - - def getResultsData(self, ID): - - self.prog.create(localise(32112), localise(32113)) - try: - raw = self.results.getResults("competition-%s" - % (self.leagueid)) - - except: - print "ERROR" - raw = None - - self.prog.close() - - return raw - - def setID(self, ID, w): - # Gets the ID of the selected league - ID = self.allleagues[ID] - self.setleague(ID,w) - - def next(self,w): - # Display the next table in the competion - self.offset += 1 - self.redraw = True - w.close() - - def previous(self,w): - # Display the previous tablein the competition - self.offset -= 1 - self.redraw = True - w.close() - - def finish(self,w): - # We're done. Gracefully close down menu. - self.redraw = False - self.menu = False - self.active = False - w.close() - - def setleague(self,lg, w): - # Set up the variables to display the league table - self.leagueid = lg - self.offset = 0 - self.redraw = True - w.close() - self.rawdata = self.getResultsData(self.leagueid) - self.prog.update(90) - - def toggleMode(self,w): - # Toggle between showing all competitions and just our favourites - self.showall = not self.showall - self.active = True - w.close() - - def start(self): - - # Let's begin - while self.active: - # Show the main menu - self.showMenu() - - while self.redraw: - - # Show a league table - self.showResults() - -if __name__ == "__main__": - - # Close addon setting window (if open) - closeAddonSettings() - - # Create an instance of the XBMC League Table - xlr = XBMCResults() - - # and display it! - xlr.start() diff --git a/resources/lib/settings.py b/resources/lib/settings.py index 221342f..23508c5 100644 --- a/resources/lib/settings.py +++ b/resources/lib/settings.py @@ -23,11 +23,12 @@ else: import simplejson as json + import xbmc import xbmcgui import xbmcaddon -from resources.lib.footballscores import getAllLeagues +from resources.lib.footballscores import getAllTeams _A_ = xbmcaddon.Addon("service.bbclivefootballscores") _S_ = _A_.getSetting @@ -37,6 +38,9 @@ RESET = 1 TOGGLE_NOTIFICATIONS = 2 +SETTING_TEAMS = "watchedteams" +SETTING_LEAGUES = "watchedleagues" + modes= {"standard": STANDARD, "reset": RESET, "toggle": TOGGLE_NOTIFICATIONS} @@ -59,159 +63,216 @@ def Notify(subject, message, image=None): ''' xbmcgui.Dialog().notification(subject, message, image, 2000) -def selectLeagues(): - '''Get list of available leagues and allow user to select those - leagues from which they want to receive updates. - ''' - - # Get list of leagues - # Format is [{"name": "Name of League", - # "id": "competition-xxxxxxx"}] - leagues = getMasterLeagueList() - - # Set a flag to keep displaying select dialog until flag changes - finishedSelection = False - - # Load the list of leagues currently selected by user - watchedleagues = loadLeagues() - - # Start loop - will be exited once user confirms selection or - # cancels - while not finishedSelection: - - # Empty list for select dialog - userchoice = [] - - # Loop through leagues - for league in leagues: - - try: - # Add league details to our list - # leagues.append([league["name"],int(league["id"][12:])]) - - # Check whether leagues is one we're following - if int(league["id"]) in watchedleagues: - - # Mark the league if it's one the user has previously - # selected and add it to the select dialog - userchoice.append("*" + league["name"]) - - else: - - # If not previously selected, we still need to add to - # select dialog - userchoice.append(league["name"]) - - # Hopefully we don't end up here... - except: - - # Tell the user there's a problem - userchoice.append(localise(32020)) - - # We only need to tell the user once! - break - - # Add an option to say we've finished selecting leagues - userchoice.append(localise(32022)) - - - # Display the list - inputchoice = xbmcgui.Dialog().select(localise(32021), - userchoice) - - - # Check whether the user has clicked on a league... - if (inputchoice >=0 and not userchoice[inputchoice] == localise(32022) - and not userchoice[inputchoice] == localise(32021)): - - # If it's one that's already in our watched league list... - if int(leagues[inputchoice]["id"]) in watchedleagues: - - # ...then we need to remove it - watchedleagues.remove(int(leagues[inputchoice]["id"])) - - # if not... - else: - - # ... then we need to add it - watchedleagues.append(int(leagues[inputchoice]["id"])) - - # If we're done - elif userchoice[inputchoice] == localise(32022): - - # Save our new list - saveLeagues(watchedleagues) - - # Set the flag to leave the select dialog loop - finishedSelection = True - - # If there's an error or we hit cancel - elif (inputchoice == -1 or - userchoice[inputchoice] == localise(32020)): - - - # end the selection (but don't save new settings) - finishedSelection = True - -def getMasterLeagueList(): - '''Returns master list of leagues/competitions for which we - can obtain data. - - Some competitions are only visible when matches are being - played so any new competitions are added to the master list - whenever this script is run. - - Returns: masterLeagueList - list of competitions in dict - format {"name": xx, "id", xx} - ''' - - currentleagues = getAllLeagues() - - try: - masterLeagueList = json.loads(_S_("masterlist")) - - except: - masterLeagueList = [] - - masterLeagueList += [x for x in currentleagues - if x not in masterLeagueList] - - _A_.setSetting(id="masterlist",value=json.dumps(masterLeagueList)) - - return masterLeagueList - -def loadLeagues(): - '''See if there are any previously selected leagues. - - Returns list of league IDs. - ''' - - try: - watchedleagues = json.loads(str(_S_("watchedleagues"))) - except: - watchedleagues = [] - - return watchedleagues - -def saveLeagues(leagues): - '''Converts list to JSON compatible string and saves it to our - user's settings. - ''' - - rawdata = json.dumps(leagues) - _A_.setSetting(id="watchedleagues",value=rawdata) - -def resetLeagues(): - '''Clears all saved league IDs. - - Useful if IDs change leading to duplicate menu entries. - ''' - _A_.setSetting(id="watchedleagues",value="[]") - _A_.setSetting(id="masterlist",value="[]") - ok = xbmcgui.Dialog().ok(localise(32023), localise(32027)) - -def toggleNotification(): - '''Toggles score notifications on or off.''' - state = not (_S_("Alerts") == "true") - Notify("BBC Football Scores", localise(32024) % (localise(32025) if state else localise(32026))) - _A_.setSetting(id="Alerts", value=str(state).lower()) +def getSetting(setting, is_json=True): + + setting = _S_(setting) + + if is_json: + try: + return json.loads(setting) + except: + return setting + + else: + return setting + +def doMultiselect(heading, items, preselect=None): + + ms = xbmcgui.Dialog().multiselect + + code = ms(heading, items, preselect=preselect) + + return code + + +def selectTeams(): + + teams = getAllTeams() + retry = 5 + + while teams is None: + xbmc.sleep(500) + teams = getAllTeams() + + retry -= 1 + + if retry == 0: + break + + if teams is None: + Notify("BBC Live Football Scores", localise(32020)) + return False + + teams = [x["name"] for x in teams] + + myteams = getSetting(SETTING_TEAMS) + + if type(myteams) != list: + myteams = [] + + preselect = [teams.index(x) for x in myteams if x in teams] + + selected = doMultiselect(localise(32004), teams, preselect=preselect) + + if selected is not None: + + selected_teams = [teams[x] for x in selected] + + saveSetting(SETTING_TEAMS, selected_teams) + +# def selectLeagues(): +# '''Get list of available leagues and allow user to select those +# leagues from which they want to receive updates. +# ''' +# +# # Get list of leagues +# # Format is [{"name": "Name of League", +# # "id": "competition-xxxxxxx"}] +# leagues = getMasterLeagueList() +# +# # Set a flag to keep displaying select dialog until flag changes +# finishedSelection = False +# +# # Load the list of leagues currently selected by user +# watchedleagues = loadLeagues() +# +# # Start loop - will be exited once user confirms selection or +# # cancels +# while not finishedSelection: +# +# # Empty list for select dialog +# userchoice = [] +# +# # Loop through leagues +# for league in leagues: +# +# try: +# # Add league details to our list +# # leagues.append([league["name"],int(league["id"][12:])]) +# +# # Check whether leagues is one we're following +# if int(league["id"]) in watchedleagues: +# +# # Mark the league if it's one the user has previously +# # selected and add it to the select dialog +# userchoice.append("*" + league["name"]) +# +# else: +# +# # If not previously selected, we still need to add to +# # select dialog +# userchoice.append(league["name"]) +# +# # Hopefully we don't end up here... +# except: +# +# # Tell the user there's a problem +# userchoice.append(localise(32020)) +# +# # We only need to tell the user once! +# break +# +# # Add an option to say we've finished selecting leagues +# userchoice.append(localise(32022)) +# +# +# # Display the list +# inputchoice = xbmcgui.Dialog().select(localise(32021), +# userchoice) +# +# +# # Check whether the user has clicked on a league... +# if (inputchoice >=0 and not userchoice[inputchoice] == localise(32022) +# and not userchoice[inputchoice] == localise(32021)): +# +# # If it's one that's already in our watched league list... +# if int(leagues[inputchoice]["id"]) in watchedleagues: +# +# # ...then we need to remove it +# watchedleagues.remove(int(leagues[inputchoice]["id"])) +# +# # if not... +# else: +# +# # ... then we need to add it +# watchedleagues.append(int(leagues[inputchoice]["id"])) +# +# # If we're done +# elif userchoice[inputchoice] == localise(32022): +# +# # Save our new list +# saveLeagues(watchedleagues) +# +# # Set the flag to leave the select dialog loop +# finishedSelection = True +# +# # If there's an error or we hit cancel +# elif (inputchoice == -1 or +# userchoice[inputchoice] == localise(32020)): +# +# +# # end the selection (but don't save new settings) +# finishedSelection = True +# +# def getMasterLeagueList(): +# '''Returns master list of leagues/competitions for which we +# can obtain data. +# +# Some competitions are only visible when matches are being +# played so any new competitions are added to the master list +# whenever this script is run. +# +# Returns: masterLeagueList - list of competitions in dict +# format {"name": xx, "id", xx} +# ''' +# +# currentleagues = getAllLeagues() +# +# try: +# masterLeagueList = json.loads(_S_("masterlist")) +# +# except: +# masterLeagueList = [] +# +# masterLeagueList += [x for x in currentleagues +# if x not in masterLeagueList] +# +# _A_.setSetting(id="masterlist",value=json.dumps(masterLeagueList)) +# +# return masterLeagueList +# +# def loadLeagues(): +# '''See if there are any previously selected leagues. +# +# Returns list of league IDs. +# ''' +# +# try: +# watchedleagues = json.loads(str(_S_("watchedleagues"))) +# except: +# watchedleagues = [] +# +# return watchedleagues +# +def saveSetting(setting, value, is_json=True): + + if is_json: + value = json.dumps(value) + + _A_.setSetting(id=setting,value=value) + +# +# def resetLeagues(): +# '''Clears all saved league IDs. +# +# Useful if IDs change leading to duplicate menu entries. +# ''' +# _A_.setSetting(id="watchedleagues",value="[]") +# _A_.setSetting(id="masterlist",value="[]") +# ok = xbmcgui.Dialog().ok(localise(32023), localise(32027)) +# +# def toggleNotification(): +# '''Toggles score notifications on or off.''' +# state = not (_S_("Alerts") == "true") +# Notify("BBC Football Scores", localise(32024) % (localise(32025) if state else localise(32026))) +# _A_.setSetting(id="Alerts", value=str(state).lower()) diff --git a/resources/settings.xml b/resources/settings.xml index 1f2128e..ec188d6 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -4,7 +4,9 @@ - + + + + - + - +