Skip to content
RobSpectrePublic

About

A Python package and command line interface to control video players for MultiViewer For F1, the best way to watch Formula 1.

Resources

Stars

20 stars

Watchers

2 watching

Forks

Repository files navigation

mvf1

A Python package, command line interface and MCP server to control video players for MultiViewer, the best way to watch Formula 1.

https://dl.circleci.com/status-badge/img/gh/RobSpectre/mvf1/tree/main.svg?style=svg https://codecov.io/gh/RobSpectre/mvf1/branch/main/graph/badge.svg?token=L5N96KXN2V Documentation Status

Table of Contents

Features

Installation

The latest version can be installed via pip.

$ pip install mvf1

Quickstart

Command Line

$ mvf1-cli --help

Model Context Protocol (MCP) Server

$ mvf1-cli mcp

To use in an AI agent, use your framework's affordance for a stdio MCP server.

Here is an example with PydanticAI.

from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerStdio

server = MCPServerStdio(
    'mvf1-cli',
    args=[
        'mcp'
    ]
)
agent = Agent('openai:gpt-4o', mcp_servers=[server])

Library

Displaying all players

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> remote.players
[6: INTERNATIONAL, 7: PER]

Pause all players

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> for player in remote.players:
>>>     player.mute()
{'data': {'playerSetMuted': True}}
{'data': {'playerSetMuted': True}}

Retrieve specific player

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> player = remote.player(6)
>>> player.state
{'ts': 1677168293.21, 'paused': False, 'muted': True, 'volume': 100, 'live': False, 'currentTime': 10.002025, 'interpolatedCurrentTime': 363.656025}

Switch stream of player to data channel

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> player = remote.player(6)
>>> player.switch_stream('DATA')
{'data': {'playerCreate': '12'}}

Synchronize all players to specific player

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> player = remote.player(6)
>>> player.sync()
{'data': {'playerSync': True}}

Synchornize all players to player streaming broadcast commentary

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> remote.player_sync_to_commentary()
{'data': {'playerSync': True}}

Synchronize open players to live timing

>>> from mvf1 import MultiViewerForF1
>>> remote = MultiViewerForF1()
>>> remote.player_sync_to_live_timing()
{'data': {'meeting_id': '1279', 'session_id': '11234', 'synced_players': [...]}}

The same operation is available to MCP clients as player_sync_to_live_timing. It uses the current live-timing clock, session start, and each feed's signed offset to seek matching open players to the first open player's session.

Content IDs and additional controls

The library can retrieve a content ID from an open player, or look up session content IDs using a meeting key:

from mvf1 import MultiViewerForF1

remote = MultiViewerForF1()
player = remote.players[0]  # Requires an open player.
content_id = player.content_id

state = remote.f1_live_timing_state["data"]["f1LiveTimingState"]
meeting_key = state["SessionInfo"]["Meeting"]["Key"]
session_ids = remote.content_ids(meeting_key)  # Session title -> content ID.

content_ids() uses MultiViewer's public meetings API and needs internet access. The live-timing example requires an active F1 timing session. MCP exposes content_ids(meeting_key) and player_get_content_id() (the first open player's content ID).

Additional controls are available in both Python and MCP:

subscriptions = remote.active_subscriptions["data"]["activeSubscriptions"]
remote.player_create(content_id, channel_id=player.channel_id)
player.set_always_on_top(True, level="FLOATING")
player.seek(relative=-0.25)
player.set_volume(37.5)

The corresponding MCP tools are active_subscriptions, player_create, player_set_always_on_top, player_seek_to, and player_set_volume. The last three take a player id. Supported levels are FLOATING, MAIN_MENU, MODAL_PANEL, NORMAL, POP_UP_MENU, SCREEN_SAVER, STATUS, and TORN_OFF_MENU. Omitting the level uses MultiViewer's default. Volume is normally 0-100, with up to 200 allowed for F1 TV v2 players when volume boost is enabled in MultiViewer.

Development

Hacking

To hack on the project, clone the GitHub repo:

$ git clone https://github.com/RobSpectre/mvf1

Then install in a virtualenv.

$ pip install -e ./

Test

The project uses tox for tests. Simply run from project root

$ tox

GraphQL compatibility

The generated schema was refreshed from MultiViewer 2.15.2. WEC live timing now returns positions, session, and raceControl; applications using the previous WEC fields must migrate. See the dependency and API audit for the full comparison, remaining coverage gaps, and schema regeneration commands.

Meta

  • Written by Rob Spectre.
  • Released under MIT License.
  • Software is as is - no warranty expressed or implied, diggity.
  • This package is not developed or maintained by MultiViewer or Formula 1 TV.
  • Shout out to the excellent MultiViewer team! This app absolutely changed how I enjoy Formula 1.
  • 🏎️ Go Weeyums! 🏎️

About

A Python package and command line interface to control video players for MultiViewer For F1, the best way to watch Formula 1.

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages