guidePrograms

Which program blocks the TV guide (Schedule) should hold, fetch next, and drop.

The guide loads every channel row but programs only near the viewport: every channel's programs at once do not fit a large lineup on a 512 MB device. Programs are fetched in BLOCKS of BLOCK_SIZE consecutive channel rows (one request per block), and at most CAP blocks hold programs at once. They hold them only for a TIME WINDOW around the grid's left edge (timeWindow), not from the guide's start: a block holding every program up to a horizon that only grows ran a 512 MB device out of memory once the user looked a few days ahead. So program memory is bounded by CAP blocks x the window, whatever the lineup size and the day.

Pure decisions only: the component does the fetching and the node writes. A block's state is in held (an AA keyed by the block index as a string), absent when it holds nothing, else { from, until, stale }: it holds every program airing in [from, until] (seconds since the epoch): those ending at or after from and starting at or before until, as one /LiveTv/Programs query with MinEndDate and MaxStartDate returns them. stale marks programs that must be replaced (a series was recorded). A block whose last fetch failed also has an entry in failures (same keys): its backoff state (source/utils/backoff.bs), so it is not fetched again until its retryAt. Times in failures are on the component's own millisecond clock, not the epoch.

Members

(static, constant) BLOCK_SIZE

Channels per program request. 25 channels x 24 h is ~460 programs on the fixture server.

Default Value
  • 25

(static, constant) BUFFER_ROWS

Rows kept near either side of the visible rows: one whole block, so the next block is already loading by the time the user reaches it.

Default Value
  • 25

(static, constant) CAP

Blocks that may hold programs at once. At most four are ever wanted (the visible rows plus BUFFER_ROWS either side can touch four blocks), so this keeps a couple of recently visited blocks for scrolling back without refetching.

Default Value
  • 6

(static, constant) NEED_AFTER

3 h

Default Value
  • 43200

(static, constant) NEED_BEFORE

A block is fetched again only once it covers less than NEED_BEFORE / NEED_AFTER around the left edge, so pressing Left or Right does not start a fetch each time.

Default Value
  • 10800

(static, constant) TRIM_SLACK

Programs more than this outside the window are trimmed.

Default Value
  • 43200

(static, constant) WINDOW_AFTER

6 h

Default Value
  • 108000

(static, constant) WINDOW_BEFORE

The time window blocks load programs for: WINDOW_BEFORE before the left edge to WINDOW_AFTER after it (seconds).

Default Value
  • 21600

Methods

(static) blockOf(row) → {integer}

The block a row belongs to.

Parameters:
NameTypeDescription
rowinteger
Returns:
Type: 
integer

(static) blockRows(block, rowCount) → {object}

The rows of block present in the grid, as [first, last] inclusive.

Parameters:
NameTypeDescription
blockinteger

a block index

rowCountinteger

channel rows in the grid

Returns:
Type: 
object

(static) evictions(held, wanted, focusedRow, cap) → {object}

Blocks to drop so at most cap hold programs: never a wanted one, farthest from the focused block first. The guide passes CAP.

Parameters:
NameTypeDescription
heldobject

block states

wantedobject

from wantedBlocks

focusedRowinteger

the focused channel row

capinteger

blocks that may hold programs at once

Returns:

block indices to drop

Type: 
object

(static) fetchFor(block, state, window) → {object}

The fetch that brings a block holding state up to the window: only the side it is missing when what it holds overlaps the window, else the whole window in place of what it holds.

Parameters:
NameTypeDescription
blockinteger

a block index

statedynamic

the block's entry in held, or invalid

windowobject

from timeWindow

Returns:
Type: 
object

(static) isBlockReady(block, rowCount, rowsComplete) → {boolean}

Whether every row of block has arrived.

Parameters:
NameTypeDescription
blockinteger
rowCountinteger
rowsCompleteboolean
Returns:
Type: 
boolean

(static) isCurrent(key, held, window) → {boolean}

Whether key's block covers enough of the window to need no fetch. A stale block never does.

Parameters:
NameTypeDescription
keystring
heldobject
windowobject
Returns:
Type: 
boolean

(static) isWanted(block, wanted) → {boolean}

Whether block is one of wanted.

Parameters:
NameTypeDescription
blockinteger
wantedobject
Returns:
Type: 
boolean

(static) nextFetch(wanted, held, window, failures, nowMs) → {dynamic}

The next fetch to make, or invalid when every wanted block covers the window or is waiting to retry. Fetches are made one at a time; call again after each lands.

Parameters:
NameTypeDescription
wantedobject

from wantedBlocks

heldobject

block states (see the namespace comment)

windowobject

from timeWindow

failuresobject

backoff states of blocks whose last fetch failed

nowMsinteger

the clock failures is on

Returns:

an AA with block, kind (a GuideFetchKind) and the from / until to request

Type: 
dynamic

(static) nextRetryAt(wanted, held, window, failures) → {dynamic}

When the earliest wanted block waiting to retry may be fetched, or invalid when none is waiting: the time to come back if nextFetch has nothing to do now.

Parameters:
NameTypeDescription
wantedobject

from wantedBlocks

heldobject

block states

windowobject

from timeWindow

failuresobject

backoff states of blocks whose last fetch failed

Returns:

a time on the failures clock, or invalid

Type: 
dynamic

(static) pruneFailures(failures, wanted) → {object}

failures with only the wanted blocks' entries, so a block the focus comes back to later starts again from the first, shortest delay.

Parameters:
NameTypeDescription
failuresobject

backoff states of blocks whose last fetch failed

wantedobject

from wantedBlocks

Returns:

a new AA; failures is left untouched

Type: 
object

(static) spanState(firstRow, lastRow, held, failures, window) → {string}

What rows in [firstRow, lastRow] without programs should say. TimeGrid has one text for every such row. FAILED wins over LOADING: a block waiting up to 30 s to retry must not read as loading, while a block still loading next to it reads as unavailable only until it lands.

Parameters:
NameTypeDescription
firstRowinteger

first row, inclusive

lastRowinteger

last row, inclusive

heldobject

block states

failuresobject

backoff states of blocks whose last fetch failed

windowobject

from timeWindow

Returns:
Type: 
string

(static) timeWindow(leftEdge, guideStart) → {object}

The time window blocks should hold programs for, from the grid's left edge.

need no fetch; keepFrom / keepUntil, outside which what a block holds is trimmed

Parameters:
NameTypeDescription
leftEdgeinteger

the time at the grid's left edge (seconds since the epoch)

guideStartinteger

the earliest time the guide shows programs for

Returns:

an AA: from / until to fetch; needFrom / needUntil, what a block must cover to

Type: 
object

(static) trims(held, wanted, window, focusedRow) → {object}

What to cut back so blocks hold programs only near the window, farthest from the focus first: a block no longer wanted that holds anything outside [keepFrom, keepUntil] is dropped whole (coming back to it loads its own window anyway), as is a wanted one that no longer overlaps the window at all; any other wanted one is cut back to the window on the side that ran past it. The drops come before the cuts.

until to keep

Parameters:
NameTypeDescription
heldobject

block states

wantedobject

from wantedBlocks

windowobject

from timeWindow

focusedRowinteger

the focused channel row

Returns:

AAs with block and drop: true to drop the block, else false with the from /

Type: 
object

(static) wantedBlocks(focusedRow, visibleRows, rowCount, rowsComplete) → {object}

Blocks to hold programs for, nearest the focused row first: the focused block, then alternately below and above it. Only blocks whose rows have all arrived are listed: a block fetched while its channels are still paging in would miss the late ones.

Parameters:
NameTypeDescription
focusedRowdynamic

the focused channel row

visibleRowsinteger

rows the grid shows at once

rowCountinteger

channel rows in the grid

rowsCompleteboolean

whether every channel row has arrived

Returns:

block indices, in the order to fetch them

Type: 
object