Esempio n. 1
0
def main(argv=None):
    """Entry point for mlbv"""

    # using argparse (2.7+) https://docs.python.org/2/library/argparse.html
    parser = argparse.ArgumentParser(description=HELP_HEADER, epilog=HELP_FOOTER,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--init", action="store_true",
                        help="Generates a config file using a combination of defaults plus prompting for MLB.tv credentials.")
    parser.add_argument("--usage", action="store_true", help="Display full usage help.")
    parser.add_argument("-d", "--date", help="Display games/standings for date. Format: yyyy-mm-dd")
    parser.add_argument("--days", type=int, default=1, help="Number of days to display")
    parser.add_argument("--tomorrow", action="store_true", help="Use tomorrow's date")
    parser.add_argument("--yesterday", action="store_true", help="Use yesterday's date")
    parser.add_argument("-t", "--team",
                        help="Play selected game feed for team, one of: {}".format(mlbgamedata.TEAM_CODES))
    parser.add_argument("--info", nargs='?', const='full', metavar='full|short', choices=('full', 'short'),
                        help=("Show extended game information inline (articles/text). "
                              "Default is 'full'. Use --info=short to show only summaries (exclude full articles). "
                              "You can also set this option permanently in your config via: "
                              "info_display_articles=true|false"))
    parser.add_argument("-f", "--feed",
                        help=("Feed type, either a live/archive game feed or highlight feed "
                              "(if available). Available feeds are shown in game list,"
                              "and have a short form and long form (see 'Feed identifiers' section below)"))
    parser.add_argument("-r", "--resolution",
                        help=("Stream resolution for streamlink (overrides settting in config file). "
                              "Choices: {}. Can also be a comma-separated list of values (no spaces), "
                              "e.g 720p_alt,720p,540p").format(config.BANDWIDTH_CHOICES))
    parser.add_argument("-i", "--inning",
                        help=("Start live/archive stream from inning. Format: {t|b}{inning_num}. "
                              "t|b: (optional) top or bottom, inning_num: inning number. "
                              "e.g.  '5' - start at 5th inning. 't5' start at top 5th. "
                              "'b5' start at bottom 5th."))
    parser.add_argument("--inning-offset", type=int, metavar='SECS',
                        help="Override the inning offset time in seconds. Default=240 (4 minutes)")
    # TODO remove --from-start, in favour of --inning
    parser.add_argument("--from-start", action="store_true", help="Start live/archive stream from beginning")
    parser.add_argument("--favs", help=argparse.SUPPRESS)
                        # help=("Favourite teams, a comma-separated list of favourite teams " "(normally specified in config file)"))
    parser.add_argument("-o", "--filter", nargs='?', const='favs', metavar='filtername|teams',
                        help=("Filter output. Either a filter name (see --list-filters) or a comma-separated "
                              "list of team codes, eg: 'tor.bos,wsh'. Default: favs"))
    parser.add_argument("--list-filters", action='store_true', help="List the built-in filters")
    parser.add_argument("-g", "--game", default='1', choices=('1', '2'),
                        help="Select game number of double-header")
    parser.add_argument("-s", "--scores", action="store_true",
                        help="Show scores (default off; overrides config file)")
    parser.add_argument("-n", "--no-scores", action="store_true",
                        help="Do not show scores (default on; overrides config file)")
    parser.add_argument("-l", "--linescore", nargs='?', const='all', metavar='filter',
                        help="Show linescores. Optional: specify a filter as per --filter option.")
    parser.add_argument("--boxscore", nargs='?', const='all', metavar='filter',
                        help="Show boxscores. Optional: specify a filter as per --filter option.")
    parser.add_argument("--username", help=argparse.SUPPRESS)  # help="MLB.tv username. Required for live/archived games.")
    parser.add_argument("--password", help=argparse.SUPPRESS)  # help="MLB.tv password. Required for live/archived games.")
    parser.add_argument("--fetch", "--record", action="store_true", help="Save stream to file instead of playing")
    parser.add_argument("--wait", action="store_true",
                        help=("Wait for game to start (live games only). Will block launching the player until game time. "
                              "Useful when combined with the --fetch option."))
    parser.add_argument("--standings", nargs='?', const='division', metavar='[category]',
                        help=("Display standings. This option will display the selected standings category, then exit. "
                              "'[category]' is one of: '" + ', '.join(standings.STANDINGS_OPTIONS) + "' [default: %(default)s]. "
                              "The standings category can be shortened down to one character (all matching "
                              "categories will be included), e.g. 'div'. "
                              "Can be combined with -d/--date option to show standings for any given date.")
                        )
    parser.add_argument("--stats", nargs='?', const='team', metavar='[teamcode]',
                        help=("Display stats. This option will display the selected stats category, then exit. "
                              "Can be combined with -d/--date option to show stats for any given date.")
                        )
    parser.add_argument("--recaps", nargs='?', const='all', metavar='FILTER',
                        help=("Play recaps for given teams. "
                              "[FILTER] is an optional filter as per --filter option"))
    parser.add_argument("-v", "--verbose", action="store_true", help=argparse.SUPPRESS)  # help="Increase output verbosity")
    parser.add_argument("-D", "--debug", action="store_true", help=argparse.SUPPRESS)    # help="Turn on debug output")
    args = parser.parse_args()

    if args.usage:
        return display_usage()

    team_to_play = None
    feedtype = None

    if args.init:
        return config.Config.generate_config(args.username, args.password, "MLB.tv")

    # get our config
    config.CONFIG = config.Config(mlbconfig.DEFAULTS, args)

    # append log files if DEBUG is set (from top of file)
    util.init_logging(os.path.join(config.CONFIG.dir,
                                   os.path.splitext(os.path.basename(sys.argv[0]))[0] + '.log'), True)

    global LOG
    LOG = logging.getLogger(__name__)

    if args.info and args.info == 'short':
        config.CONFIG.parser['info_display_articles'] = 'false'

    if args.list_filters:
        print('List of built filters: ' + ', '.join(sorted(mlbgamedata.FILTERS.keys())))
        return 0
    if args.debug:
        config.CONFIG.parser['debug'] = 'true'
    if args.verbose:
        config.CONFIG.parser['verbose'] = 'true'
    if args.username:
        config.CONFIG.parser['username'] = args.username
    if args.password:
        config.CONFIG.parser['password'] = args.password
    if args.inning_offset is not None:
        config.CONFIG.parser['stream_start_offset_secs'] = str(args.inning_offset)
    if args.team:
        team_to_play = args.team.lower()
        if team_to_play not in mlbgamedata.TEAM_CODES:
            # Issue #4 all-star game has funky team codes
            LOG.warning('Unexpected team code: %s', team_to_play)
    if args.feed:
        feedtype = gamedata.convert_to_long_feedtype(args.feed.lower(), mlbgamedata.FEEDTYPE_MAP)
    if args.resolution:
        config.CONFIG.parser['resolution'] = args.resolution
    if args.scores:
        config.CONFIG.parser['scores'] = 'true'
    elif args.no_scores:
        config.CONFIG.parser['scores'] = 'false'
    if args.linescore:
        if args.linescore != 'all':
            config.CONFIG.parser['filter'] = args.linescore
        config.CONFIG.parser['linescore'] = 'true'
    if args.boxscore:
        if args.boxscore != 'all':
            config.CONFIG.parser['filter'] = args.boxscore
        config.CONFIG.parser['boxscore'] = 'true'
    if args.favs:
        config.CONFIG.parser['favs'] = args.favs
    if args.filter:
        config.CONFIG.parser['filter'] = args.filter

    if config.DEBUG:
        LOG.info(str(config.CONFIG))
    else:
        LOG.debug(str(config.CONFIG))

    if args.yesterday:
        args.date = datetime.strftime(datetime.today() - timedelta(days=1), "%Y-%m-%d")
    elif args.tomorrow:
        args.date = datetime.strftime(datetime.today() + timedelta(days=1), "%Y-%m-%d")
    elif args.date is None:
        args.date = datetime.strftime(datetime.today(), "%Y-%m-%d")

    if args.standings:
        standings.get_standings(args.standings, args.date, args.filter)
        return 0
    if args.stats:
        # def get_team_stats(team_code, team_code_id_map, stats_option='all', date_str=None):
        stats.get_team_stats(args.stats, None, 'all', None)
        return 0

    gamedata_retriever = mlbgamedata.GameDataRetriever()

    # retrieve all games for the dates given
    game_day_tuple_list = gamedata_retriever.process_game_data(args.date, args.days)

    if not team_to_play and not args.recaps:
        # nothing to play; display the games
        presenter = mlbgamedata.GameDatePresenter()
        displayed_count = 0
        for game_date, game_records in game_day_tuple_list:
            presenter.display_game_data(game_date, game_records, args.filter, args.info)
            displayed_count += 1
            if displayed_count < len(game_day_tuple_list):
                print('')
        return 0

    # from this point we only care about first day in list
    if len(game_day_tuple_list) > 0:
        game_date, game_data = game_day_tuple_list[0]
    else:
        # nothing to stream
        return 0

    if args.recaps:
        recap_teams = list()
        if args.recaps == 'all':
            for game_pk in game_data:
                # add the home team
                recap_teams.append(game_data[game_pk]['home']['abbrev'])
        else:
            for team in args.recaps.split(','):
                recap_teams.append(team.strip())
        for game_pk in game_data:
            game_rec = gamedata.apply_filter(game_data[game_pk], args.filter)
            if game_rec and (game_rec['home']['abbrev'] in recap_teams or game_rec['away']['abbrev'] in recap_teams):
                if 'recap' in game_rec['feed']:
                    LOG.info("Playing recap for %s at %s", game_rec['away']['abbrev'].upper(), game_rec['home']['abbrev'].upper())
                    game_num = 1
                    if game_rec['doubleHeader'] != 'N':
                        game_num = game_rec['gameNumber']
                    stream_game_rec = mlbstream.get_game_rec(game_data, game_rec['home']['abbrev'], game_num)
                    mlbstream.play_stream(stream_game_rec, game_rec['home']['abbrev'], 'recap', game_date,
                                          args.fetch, None, None, is_multi_highlight=True)
                else:
                    LOG.info("No recap available for %s at %s", game_rec['away']['abbrev'].upper(), game_rec['home']['abbrev'].upper())
        return 0

    # LOG.info('Sorry, MLB.tv stream support is broken. You should switch to https://github.com/tonycpsu/streamglob')
    # return -1

    game_rec = mlbstream.get_game_rec(game_data, team_to_play, args.game)

    if args.wait and not util.has_reached_time(game_rec['mlbdate']):
        LOG.info('Waiting for game to start. Local start time is %s', util.convert_time_to_local(game_rec['mlbdate']))
        print('Use Ctrl-c to quit .', end='', flush=True)
        count = 0
        while not util.has_reached_time(game_rec['mlbdate']):
            time.sleep(10)
            count += 1
            if count % 6 == 0:
                print('.', end='', flush=True)

        # refresh the game data
        LOG.info('Game time. Refreshing game data after wait...')
        game_day_tuple_list = gamedata_retriever.process_game_data(args.date, 1)
        if len(game_day_tuple_list) > 0:
            game_date, game_data = game_day_tuple_list[0]
        else:
            LOG.error('Unexpected error: no game data found after refresh on wait')
            return 0

        game_rec = mlbstream.get_game_rec(game_data, team_to_play, args.game)

    return mlbstream.play_stream(game_rec, team_to_play, feedtype, args.date, args.fetch, args.from_start, args.inning)
Esempio n. 2
0
    def _display_game_details(self, header, game_pk, game_rec, show_linescore, show_boxscore, show_info, games_displayed_count):
        show_scores = config.CONFIG.parser.getboolean('scores')
        outl = list()
        if games_displayed_count > 1:
            if show_info or show_linescore or show_boxscore:
                outl.append('')
            if show_info:
                outl.extend(header)
        border = displayutil.Border(use_unicode=config.UNICODE)
        color_on = ''
        color_off = ''
        # odd_even = games_displayed_count % 2
        # if odd_even:
        #     color_on = ANSI.fg('yellow')
        # else:
        #     color_on = display'reset'.ANSI.fg('lightblue')
        color_off = ANSI.reset()
        if gamedata.is_fav(game_rec):
            if config.CONFIG.parser['fav_colour'] != '':
                color_on = ANSI.fg(config.CONFIG.parser['fav_colour'])
                color_off = ANSI.reset()
        if game_rec['abstractGameState'] == 'Live':
            color_on += ANSI.control_code('bold')
            color_off = ANSI.reset()
        if game_rec['doubleHeader'] == 'N':
            series_info = "{sgn}/{gis}".format(sgn=game_rec['seriesGameNumber'], gis=game_rec['gamesInSeries'])
        else:
            # series_info = "DH{gn} {sgn}/{gis}".format(sgn=game_rec['seriesGameNumber'],
            #                                           gis=game_rec['gamesInSeries'],
            #                                           gn=game_rec['gameNumber'])
            series_info = "DH-{gn}".format(gn=game_rec['gameNumber'])
        game_info_str = "{time}: {a1} ({a2}) at {h1} ({h2})"\
            .format(time=util.convert_time_to_local(game_rec['mlbdate']),
                    a1=game_rec['away']['display'], a2=game_rec['away']['abbrev'].upper(),
                    h1=game_rec['home']['display'], h2=game_rec['home']['abbrev'].upper())
        game_state = ''
        game_state_color_on = color_on
        game_state_color_off = color_off

        if game_rec['abstractGameState'] in ('Preview', ):
            if game_rec['detailedState'] != 'Scheduled':
                if 'Delayed' in game_rec['detailedState']:
                    game_state = 'Delayed'
                else:
                    game_state = game_rec['detailedState']
        else:
            if show_scores:
                if game_rec['detailedState'] in ('Critical', ):
                    game_state_color_on = ANSI.fg(config.CONFIG.parser['game_critical_colour'])
                    game_state_color_off = ANSI.reset()
                if game_rec['detailedState'] in ('Final', ):
                    game_state = game_rec['detailedState']
                    if 'currentInning' in game_rec['linescore'] and int(game_rec['linescore']['currentInning']) != 9:
                        game_state += '({})'.format(game_rec['linescore']['currentInning'])
                else:
                    if game_rec['linescore']['inningState'] != '':
                        game_state = '{} {}'.format(game_rec['linescore']['inningState'].title(),
                                                    game_rec['linescore']['currentInningOrdinal'])
                    else:
                        game_state = game_rec['linescore']['currentInningOrdinal']
            else:
                game_state = game_rec['abstractGameState']
                if 'In Progress - ' in game_rec['detailedState']:
                    game_state = game_rec['detailedState'].split('In Progress - ')[-1]
                elif game_rec['detailedState'] not in ('Live', 'Final', 'Scheduled', 'In Progress'):
                    game_state = game_rec['detailedState']

        if show_scores:
            score = ''
            if game_rec['abstractGameState'] not in ('Preview', ) and game_rec['detailedState'] not in ('Postponed', ):
                score = '{}-{}'.format(game_rec['linescore']['away']['runs'], game_rec['linescore']['home']['runs'])

            # linescore
            if show_linescore:
                if games_displayed_count > 1 and not show_info:
                    outl.append('{coloron}{dash}{coloroff}'.format(coloron=ANSI.fg('darkgrey'),
                                                                   dash=border.dash*92,
                                                                   coloroff=ANSI.reset()))
                linescore_dict = self._format_linescore(game_rec)
                """
                18:05: LA Dodgers (LAD) at San Francisco (SF)            1  2  3  4  5  6  7  8  9 10 11 12 13 14  R  H  E
                1/2    Final(14): 5-7                              LAD   0  0  1  0  0  2  1  0  0  0  0  0  0  1  5 12  0
                       Feeds: a,h / cnd,rcp                        SF    1  0  0  2  0  1  0  0  0  0  0  0  0  3  7 17  1
                """
                line_fmt = "{coloron}{ginfo:<50} {lscore}{coloroff}"
                # if game_rec['abstractGameState'] not in ('Live', 'Final'):  # or game_rec['detailedState'] in ('Postponed', ):
                #     outl.append(line_fmt.format(coloron=color_on, coloroff=color_off, ginfo=game_info_str, lscore=''))
                #     return outl

                outl.append(line_fmt.format(coloron=color_on, coloroff=color_off,
                                            ginfo=game_info_str, lscore=linescore_dict['header'], pipe=border.pipe))
                if game_state == '':
                    # game_info_str = '{series:7}Not Started'.format(series=series_info)
                    game_info_str = '{series:7}'.format(series=series_info)
                else:
                    if game_rec['abstractGameState'] in ('Live',) \
                            and game_rec['linescore']['inningState'] != 'Mid' and 'raw' in game_rec['linescore']:
                        outs_info = ', {} out'.format(game_rec['linescore']['raw']['outs'])
                    else:
                        outs_info = ''
                    if score:
                        game_info_str = '{series:7}{gstate}: {score}{outs}'.format(series=series_info,
                                                                                   gstate=game_state, score=score, outs=outs_info)
                    else:
                        game_info_str = '{series:7}{gstate}'.format(series=series_info, gstate=game_state)
                outl.append(line_fmt.format(coloron=color_on, coloroff=color_off,
                                            ginfo=game_info_str, lscore=linescore_dict['away']))

                feeds = self.__get_feeds_for_display(game_rec)
                if feeds:
                    game_info_str = '{:7}Feeds: {feeds}'.format('', feeds=self.__get_feeds_for_display(game_rec))
                    outl.append(line_fmt.format(coloron=color_on, coloroff=color_off,
                                                ginfo=game_info_str, lscore=linescore_dict['home']))
                else:
                    outl.append(line_fmt.format(coloron=color_on, coloroff=color_off,
                                                ginfo='', lscore=linescore_dict['home']))
            else:
                # single-line game score
                outl.append(("{coloron}{ginfo:<48} {series:^7}{coloroff} "
                             "{pipe} {coloron}{score:^5}{coloroff} {pipe} "
                             "{gscoloron}{gstate:<9}{gscoloroff} {pipe} {coloron}{feeds}{coloroff}")
                            .format(coloron=color_on, coloroff=color_off,
                                    ginfo=game_info_str, series=series_info, score=score,
                                    gscoloron=game_state_color_on, gstate=game_state, gscoloroff=game_state_color_off,
                                    feeds=self.__get_feeds_for_display(game_rec), pipe=border.pipe))
        else:  # no scores
            outl.append(("{coloron}{ginfo:<48} {series:^7}{coloroff} {pipe} "
                         "{coloron}{gstate:^9}{coloroff} {pipe} {coloron}{feeds}{coloroff}")
                        .format(coloron=color_on, coloroff=color_off,
                                ginfo=game_info_str, series=series_info, gstate=game_state,
                                feeds=self.__get_feeds_for_display(game_rec), pipe=border.pipe))

        if show_info:
            # found_info = False
            for text_type in ('summary', 'preview'):
                if text_type in game_rec and game_rec[text_type]:
                    # if text_type == 'summary':
                    #     outl.append('')
                    outl.append('')
                    for line in game_rec[text_type]:
                        outl.append('{coloron}{text}{coloroff}'.format(coloron=color_on,
                                                                       text=util.strip_html_tags(line, True),
                                                                       coloroff=color_off))
                    # outl.append('')
                    # only show one of summary or preview
                    break

        if show_boxscore:
            outl.extend(self._get_formatted_boxscore(game_rec, color_on, color_off))

        return outl