A simple library to download, slice and search NFL game footage on a play-by-play basis.
This library comes with preloaded play-by-play meta data, which describes the start time of each play in the game footage. However, the actual footage does not come with this library and is not released by me. This package therefore provides utilities to batch download NFL Game Footage from the original source.
Once game footage is downloaded, you can use this library to search plays and construct a playlist to play in any video player.
def artificial_slice( | footage_play_dir, gobj, gobj_play) |
Creates a video file that contains a single static image with a textual description of the play. The purpose is to provide some representation of a play even if its video form doesn't exist. (Or more likely, the play-by-play meta data for that play is corrupt.)
This function requires the use of ImageMagick's convert with pango support.
Note that gobj_play is an nflgame.game.Play object and not an nflvid.Play object.
def broadcast_url( | gobj, quality='1600') |
Returns the HTTP Live Stream URL (an m3u8 file) for the given game and quality.
Note that this does not work with every game (yet). In particular, URLs vary unpredictably (to me) from game to game.
def coach_url( | gobj) |
Returns the rtmp URL as a triple for the coach footage of the given game. The elemtns of the triple are::
(rtmp server, rtmp app name, rtmp playpath)
Coach video only comes in 1600 quality.
def download_broadcast( | footage_dir, gobj, quality='1600', dry_run=False) |
Starts an ffmpeg process to download the full broadcast of the given game with the quality provided. The qualities available are: 400, 800, 1200, 1600, 2400, 3000, 4500 with 4500 being the best.
The footage will be saved to the following path::
footage_dir/{eid}-{gamekey}.mp4
If footage is already at that path, then a LookupError is raised.
A full game's worth of footage at a quality of 1600 is about 2GB.
def download_coach( | footage_dir, gobj, dry_run=False) |
Starts an rtmpdump process to download the full coach footage of the given game. Currently, the only quality available is 1600.
The footage will be saved to the following path::
footage_dir/{eid}-{gamekey}.mp4
If footage is already at that path, then a LookupError is raised.
A full game's worth of footage at a quality of 1600 is about 1GB.
def footage_full( | footage_dir, gobj) |
Returns the path to the full video for a given game inside an nflvid footage directory.
If the full footage doesn't exist, then None is returned.
def footage_play( | footage_play_dir, gobj, playid) |
Returns a file path to an existing play slice in the footage play directory for the game and play given.
If the file for the play is not readable, then None is returned.
def footage_plays( | footage_play_dir, gobj) |
Returns a list of all footage broken down by play inside an nflvid footage directory. The list is sorted numerically by play id.
If no footage breakdown exists for the game provided, then an empty list is returned.
def play( | gobj, playid, coach=True) |
Returns a Play object given a game and a play id with timings for the coach footage. If coach is False, then the timings will be for the broadcast footage.
The game must be a nflgame.game.Game object.
If a play with the given id does not exist, None is returned.
def plays( | gobj, coach=True) |
Returns an ordered dictionary of all plays for a particular game with timings for the coach footage. If coach is False, then the timings will be for the broadcast footage.
The game must be a nflgame.game.Game object.
If there is a problem retrieving the data, None is returned.
If the game is over, then the XML data is saved to disk.
def slice( | footage_play_dir, full_footage_file, gobj, coach=True, threads=4, dry_run=False) |
Uses ffmpeg to slice the given footage file into play-by-play pieces. The full_footage_file should point to a full game downloaded with nflvid-footage and gobj should be the corresponding nflgame.game.Game object.
The footage_play_dir is where the pieces will be saved::
{footage_play_dir}/{eid}-{gamekey}/{playid}.mp4
This function will not duplicate work. If a video file exists for a particular play, then slice will not regenerate it.
Note that this function uses an eventlet green pool to run multiple ffmpeg instances simultaneously. The maximum number of threads to use is specified by threads. This function only terminates when all threads have finished processing.
If coach is False, then play timings for broadcast footage will be used instead of coach timings.
If dry_run is true, then only the first 10 plays of the game are sliced.
def slice_play( | footage_play_dir, full_footage_file, gobj, play, max_duration=0, cut_scoreboard=True) |
This is just like slice, but it only slices the play provided. In typical cases, slice should be used since it makes sure not to duplicate work.
This function will not check if the play-by-play directory for gobj has been created.
max_duration is used to cap the length of a play. This drastically cuts down on the time required to slice a game and the storage requirements of a game at the cost of potentially missing bigger plays. This is particularly useful if you are slicing broadcast footage, where imposing a cap at about 15 seconds can decrease storage and CPU requirements by more than half without missing much.
When cut_scoreboard is True, the first 3.0 seconds of the play will be clipped to remove the scoreboard view.
def unsliced_plays( | footage_play_dir, gobj, coach=True, dry_run=False) |
Scans the game directory inside footage_play_dir and returns a list of plays that haven't been sliced yet. In particular, a play is only considered sliced if the following file is readable, assuming {playid} is its play id::
{footage_play_dir}/{eid}-{gamekey}/{playid}.mp4
All plays for the game given that don't fit this criteria will be returned in the list.
If the list is empty, then all plays for the game have been sliced. Alternatively, None can be returned if there was a problem retrieving the play-by-play meta data.
If coach is False, then play timings for broadcast footage will be used instead of coach timings.
If dry_run is True, then only the first 10 plays of the game are sliced.
class Play
Represents a single play with meta data that ties it to game footage. The footage_start corresponds to the 'ArchiveTCIN' or 'CATIN', which is when the play starts. Since there is no record of when a play stops, the end is computed by using the start time of the next play. If it's the last play recorded, then the end time is None.
The play id is the foreign key that maps to play data stored in nflgame.
def __init__( | self, start, end, playid) |
def idstr( | self) |
Returns a string play id padded with zeroes.
class PlayTime
Represents a footage time point, in the format HH:MM:SS:MMM where MMM can be either 2 or 3 digits.
def __init__( | self, point=None, seconds=None) |
Construct a PlayTime object given a point in time in the format HH:MM:SS:MMM where MMM can be either 2 or 3 digits.
Alternatively, seconds can be provided (which may be a float).
def add_seconds( | self, seconds) |
Returns a new PlayTime with seconds (int or float) added to self.
def fractional( | self) |
Returns this time point as fractional seconds based on milliseconds.
def seconds( | self) |
Returns this time point rounded to the nearest second.