Skip to content
This repository has been archived by the owner on Mar 5, 2023. It is now read-only.

Commit

Permalink
Add use_short_feeds config option to control feed output in listings;…
Browse files Browse the repository at this point in the history
… change --record to --fetch
  • Loading branch information
kmac committed Jan 1, 2018
1 parent ffa65a1 commit 5444446
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 99 deletions.
46 changes: 33 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,25 @@ Live Games: | |
22:30: Calgary Flames (CGY) at San Jose Sharks (SJS) | | | away, home
19:30: Montréal Canadiens (MTL) at Tampa Bay Lightning (TBL) | 1-3 | Final | away, french, home
19:30: Philadelphia Flyers (PHI) at Florida Panthers (FLA) | 2-3 | Final | away, home
````

With `short_feed_names` option to reduce some clutter (some games have condensed, recap feeds available):
````
2017-12-31 | Score | State | Feeds
----------------------------------------------------------------|-------|-----------|------------
Live Games: | | |
20:00: New York Islanders (NYI) at Colorado Avalanche (COL) | 1-5 | 07:05 3rd | a/h
20:00: San Jose Sharks (SJS) at Dallas Stars (DAL) | 0-6 | 02:25 3rd | a/h
21:00: Chicago Blackhawks (CHI) at Calgary Flames (CGY) | 2-3 | 09:29 2nd | a/nat
----- | | |
15:30: Toronto Maple Leafs (TOR) at Vegas Golden Knights (VGK) | 3-6 | Final | a/fr/h cnd/rcp
16:00: Arizona Coyotes (ARI) at Anaheim Ducks (ANA) | 2-5 | Final | a/h cnd/rcp
18:00: Tampa Bay Lightning (TBL) at Columbus Blue Jackets (CBJ) | 5-0 | Final | a/h cnd/rcp
19:00: Winnipeg Jets (WPG) at Edmonton Oilers (EDM) | 5-0 | Final | nat rcp
19:00: Pittsburgh Penguins (PIT) at Detroit Red Wings (DET) | 1-4 | Final | a/h
````


This project incorporates some code modified from the following projects:

Expand Down Expand Up @@ -74,7 +92,7 @@ Some things you may want to set:

* username: NHL.tv account username
* password: NHL.tv account password
* use_rogers: set to true if your account goes through Rogers
* use_rogers: set to true if your NHL streaming account goes through Rogers
* favs: a comma-separated list of team codes which are 1) highlighted in the game data and 2) can be filtered on using the --filter option to show only the favourite team(s)
* scores: a boolean specifying whether or not you want to see scores in the game information. Spoilers.
* resolution: the stream quality (passed in to streamlink). Use 'best' for full HD at 60 frames/sec.
Expand All @@ -83,7 +101,7 @@ Some things you may want to set:

## TODO

* add `mlbv` to view baseball games
* add `mlbv` to view baseball games. This should be fairly simple since they both use the MLBAM infrastructure.


## Usage
Expand All @@ -97,7 +115,7 @@ Running `nhlv` without options shows you the status of today's games.

### Playing a Live or Archived Game

If you pass the `-t/--team TEAM` option, the stream is launched for the given team. By default the 'home' feed
If you pass the `-t/--team TEAM` option, the stream is launched for the given team. By default the local feed
for the given team is chosen - i.e., it will follow the home/away feed appropriate for the team so that you
get the local team feed. You can override the feed using the `-f/--feed` option. This works for either live
games or for archived games (e.g. if you use the `--date` option to select an earlier date).
Expand All @@ -108,19 +126,19 @@ Example:
nhlv --yesterday -t wpg # play yesterday's jets game (see below for options on specifying dates)


### Recording
### Fetching

If you pass the `-r/--record` option, instead of launching the video player, the selected stream is saved to
If you pass the `-f/--fetch` option, instead of launching the video player, the selected stream is saved to
disk. The stream is named to convention: `<date>-<away_team>-<home_team>-<feed>.mp4`.

Example: `2017-12-27-edm-wpg-national.mp4`.

You can select the stream for record and then manually launch your video player at a later time while the
stream is still recording.
You can select the stream for fetch, then manually launch your video player at a later time while the
stream is being saved to file.

Example:

nhlv --team wpg --record # record the live jets game
nhlv --team wpg --fetch # fetch the live jets game to disk. Most players let you view while downloading


### Highlights: Recap or Condensed Games
Expand Down Expand Up @@ -156,8 +174,10 @@ Note: the common options have both short and long options. Both are shown in the

#### Live Games

nhlv --team wpg # play the live jets game
nhltv -t wpg --feed national # choose the national feed
nhlv --team wpg # play the live jets game. The feed is chosen based on jets being home vs. away
nhltv -t wpg --feed national # play live game, choose the national feed
nhltv -t wpg --feed away # play live game, choose the away feed. If jets are the home team this would choose
# the opponent's feed

#### Archived Games

Expand All @@ -171,12 +191,12 @@ Use the `--feed` option to select the highlight feed (`recap` or `condensed`):
nhlv --yesterday -t wpg --feed condensed # condensed feed
nhlv --yesterday -t wpg -f recap # recap feed

#### Recording
#### Fetch

In these examples the game is save to a .mp4 file in the current directory.

nhlv --team wpg --record # record the live jets game.
nhlv --yesterday -t wpg -f recap --record # record yesterday's recap
nhlv --team wpg --fetch
nhlv --yesterday -t wpg -f recap --fetch # fetch yesterday's recap

#### Using `--days` for Schedule View

Expand Down
21 changes: 21 additions & 0 deletions config
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,22 @@
# Example: favs=wpg,ott
#favs=

# Favourite team colour, shown in game listings to highlight the favourite teams.
# Leave this blank to disable
# Available colours:
# black, red, green, orange, blue, purple, cyan, lightgrey, darkgrey,
# lightred, lightgreen, yellow, lightblue, pink, lightcyan
#fav_colour=cyan

# Colour used to highlight games in 'critical' state
#game_critical_colour=yellow

# Show scores. Scores are hidden by default. If this is set to true then scores are shown.
#scores=false

# Use short feed names in game listings
use_short_feeds=true

# Bandwidth/stream resolution for streamlink. One of 'worst', '360p', '540p', '720p_alt', '720p' 'best'
# Note: 720p (best) is the 60fps stream. The 720p_alt is a lower framerate.
# Fallback streams can be specified by using a comma-separated list, e.g.: 720p,540p,best
Expand All @@ -40,6 +53,14 @@
# Audio player for audio-only feeds (not implemented yet):
#audio_player=mpv

# Use streamlink for highlights. If false will send url direct to video_player (no resolution selection)
streamlink_highlights=true
# Passthrough the HLS stream to the player for highlights: allows seeking
streamlink_passthrough_highlights=true

# Passthrough the HLS stream to the player for live/archived games
streamlink_passthrough=false

# Turn on extra debugging information
#debug=false

1 change: 1 addition & 0 deletions mlbam/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Config:
'use_rogers': 'false',
'favs': '',
'fav_colour': 'cyan',
'use_short_feeds': 'true',
'filter': 'false',
'cdn': 'akamai',
'resolution': 'best',
Expand Down
147 changes: 84 additions & 63 deletions mlbam/gamedata.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,22 @@

# this map is used to transform the statsweb feed name to something shorter
FEEDTYPE_MAP = {
'away': 'away',
'home': 'home',
'away': 'a',
'home': 'h',
'french': 'fr',
'national': 'national',
'condensed': 'condensed',
'recap': 'recap',
'national': 'nat',
'condensed': 'cnd',
'recap': 'rcp',
}


def get_feedtype_keystring():
reverse_list = list()
for longkey in FEEDTYPE_MAP:
reverse_list.append('{}:{}'.format(FEEDTYPE_MAP[longkey], longkey))
return ', '.join(reverse_list)


def is_fav(game_rec):
if 'favourite' in game_rec:
return game_rec['favourite']
Expand Down Expand Up @@ -84,55 +91,26 @@ class NHLGameData(GameData):
def __init__(self):
GameData.__init__(self)

def show_game_data(self, game_date, num_days=1):
game_data_list = list()
for i in range(0, num_days):
game_data = self.get_game_data(game_date)
if game_data is not None:
game_data_list.append(game_data)
if len(game_data_list) > 1:
print('') # add line feed between days
live_game_pks = list()
for game_pk in game_data:
if game_data[game_pk]['abstractGameState'] == 'Live':
if filter_favs(game_data[game_pk]) is not None:
live_game_pks.append(game_pk)

# print header
date_hdr = '{:7}{}'.format('','{}'.format(game_date))
show_scores = config.CONFIG.parser.getboolean('scores')
if show_scores:
print("{:63} | {:^5} | {:^9} | {}".format(date_hdr, 'Score', 'State', 'Feeds'))
print("{}|{}|{}|{}".format('-' * 64, '-' * 7, '-' * 11, '-' * 12))
def __get_feeds_for_display(self, game_rec):
non_highlight_feeds = list()
use_short_feeds = config.CONFIG.parser.getboolean('use_short_feeds', True)
for feed in sorted(game_rec['feed'].keys()):
if feed not in config.HIGHLIGHT_FEEDTYPES:
if use_short_feeds:
non_highlight_feeds.append(self.convert_feedtype_to_short(feed))
else:
print("{:63} | {:^9} | {}".format(date_hdr, 'State', 'Feeds'))
print("{}|{}|{}".format('-' * 64, '-' * 11, '-' * 12))

if len(live_game_pks) > 0:
if show_scores:
print("{:63} |{}|{}|{}".format('Live Games:', ' ' * 7, ' ' * 11, ' ' * 12))
else:
print("{:63} |{}|{}".format('Live Games:', ' ' * 11, ' ' * 12))
for game_pk in live_game_pks:
if filter_favs(game_data[game_pk]) is not None:
self.show_game_details(game_pk, game_data[game_pk])
if show_scores:
print("{:63} |{}|{}|{}".format('-----', ' ' * 7, ' ' * 11, ' ' * 12))
else:
print("{:63} |{}|{}".format('-----', ' ' * 11, ' ' * 12))
for game_pk in game_data:
if game_data[game_pk]['abstractGameState'] != 'Live':
if filter_favs(game_data[game_pk]) is not None:
self.show_game_details(game_pk, game_data[game_pk])
else:
LOG.info("No game data for {}".format(game_date))

game_date = datetime.strftime(datetime.strptime(game_date, "%Y-%m-%d") + timedelta(days=1), "%Y-%m-%d")

return game_data_list
non_highlight_feeds.append(feed)
highlight_feeds = list()
for feed in game_rec['feed'].keys():
if feed in config.HIGHLIGHT_FEEDTYPES:
if use_short_feeds:
highlight_feeds.append(self.convert_feedtype_to_short(feed))
else:
highlight_feeds.append(feed)
return '{:7} {}'.format('/'.join(non_highlight_feeds), '/'.join(highlight_feeds))

@staticmethod
def get_game_data(date_str=None, overwrite_json=True):
def _get_game_data(date_str=None, overwrite_json=True):
if date_str is None:
date_str = time.strftime("%Y-%m-%d")
if config.SAVE_JSON_FILE_BY_TIMESTAMP:
Expand Down Expand Up @@ -161,7 +139,7 @@ def get_game_data(date_str=None, overwrite_json=True):
game_data = dict() # we return this dictionary

if json_data['dates'] is None or len(json_data['dates']) < 1:
LOG.debug("get_game_data: no game data for {}".format(date_str))
LOG.debug("_get_game_data: no game data for {}".format(date_str))
return None

for game in json_data['dates'][0]['games']:
Expand Down Expand Up @@ -225,6 +203,54 @@ def get_game_data(date_str=None, overwrite_json=True):
game_rec['feed'][feedtype]['playback_url'] = playback_item['url']
return game_data

def show_game_data(self, game_date, num_days=1):
game_data_list = list()
for i in range(0, num_days):
game_data = self._get_game_data(game_date)
if game_data is not None:
game_data_list.append(game_data)
if len(game_data_list) > 1:
print('') # add line feed between days
live_game_pks = list()
for game_pk in game_data:
if game_data[game_pk]['abstractGameState'] == 'Live':
if filter_favs(game_data[game_pk]) is not None:
live_game_pks.append(game_pk)

# print header
date_hdr = '{:7}{}'.format('','{}'.format(game_date))
show_scores = config.CONFIG.parser.getboolean('scores')
if show_scores:
print("{:63} | {:^5} | {:^9} | {}".format(date_hdr, 'Score', 'State', 'Feeds'))
print("{}|{}|{}|{}".format('-' * 64, '-' * 7, '-' * 11, '-' * 12))
else:
print("{:63} | {:^9} | {}".format(date_hdr, 'State', 'Feeds'))
print("{}|{}|{}".format('-' * 64, '-' * 11, '-' * 12))

if len(live_game_pks) > 0:
if show_scores:
print("{:63} |{}|{}|{}".format('Live Games:', ' ' * 7, ' ' * 11, ' ' * 12))
else:
print("{:63} |{}|{}".format('Live Games:', ' ' * 11, ' ' * 12))
for game_pk in live_game_pks:
if filter_favs(game_data[game_pk]) is not None:
self.show_game_details(game_pk, game_data[game_pk])
if show_scores:
print("{:63} |{}|{}|{}".format('-----', ' ' * 7, ' ' * 11, ' ' * 12))
else:
print("{:63} |{}|{}".format('-----', ' ' * 11, ' ' * 12))
for game_pk in game_data:
if game_data[game_pk]['abstractGameState'] != 'Live':
if filter_favs(game_data[game_pk]) is not None:
self.show_game_details(game_pk, game_data[game_pk])
# print(' ' * 5, get_feedtype_keystring())
else:
LOG.info("No game data for {}".format(game_date))

game_date = datetime.strftime(datetime.strptime(game_date, "%Y-%m-%d") + timedelta(days=1), "%Y-%m-%d")

return game_data_list

def show_game_details(self, game_pk, game_rec):
color_on = ''
color_off = ''
Expand Down Expand Up @@ -258,25 +284,20 @@ def show_game_details(self, game_pk, game_rec):
game_rec['linescore']['currentPeriodOrdinal'])
# else:
# game_state = 'Pending'
short_feeds = list()
for feed in game_rec['feed'].keys():
short_feeds.append(self.convert_feedtype_to_short(feed))
short_feed_str = ', '.join(sorted(short_feeds))
if config.CONFIG.parser.getboolean('scores'):
score = ''
if game_rec['abstractGameState'] not in ('Preview', ):
score = '{}-{}'.format(game_rec['away_score'], game_rec['home_score'])
print("{0}{2:<63}{1} | {0}{3:^5}{1} | {4}{5:<9}{6} | {0}{7}{1}".format(color_on, color_off,
print("{0}{2:<63}{1} | {0}{3:^5}{1} | {4}{5:>9}{6} | {0}{7}{1}".format(color_on, color_off,
game_info_str, score,
game_state_color_on,
game_state,
game_state_color_off,
short_feed_str))
#', '.join(sorted(game_rec['feed'].keys()))))
game_state_color_on,
game_state,
game_state_color_off,
self.__get_feeds_for_display(game_rec)))
else:
print("{0}{2:<63}{1} | {0}{3:^9}{1} | {0}{4}{1}".format(color_on, color_off,
game_info_str, game_state,
', '.join(sorted(game_rec['feed'].keys()))))
self.__get_feeds_for_display(game_rec)))
if config.CONFIG.parser.getboolean('debug') and config.CONFIG.parser.getboolean('verbose'):
for feedtype in game_rec['feed']:
print(' {}: {} [game_pk:{}, mediaPlaybackId:{}]'.format(feedtype,
Expand Down
Loading

0 comments on commit 5444446

Please sign in to comment.