Skip to content

CLI

Command-line interface for gopro-api. Run gopro-api --help to see all subcommands.

Built with Typer; the Typer application is exposed as gopro_api.cli.app for embedding or testing.

Entry point

gopro_api.cli.main(argv: Optional[list[str]] = None) -> None

CLI entrypoint: parse argv and run the selected command.

Parameters:

Name Type Description Default
argv Optional[list[str]]

Argument list (defaults to process arguments when None).

None
Source code in gopro_api/cli/app.py
def main(argv: Optional[list[str]] = None) -> None:
    """CLI entrypoint: parse ``argv`` and run the selected command.

    Args:
        argv: Argument list (defaults to process arguments when ``None``).
    """
    app(args=argv)

Application

gopro_api.cli.app

Typer application instance, root callback, and CLI entrypoint.

main(argv: Optional[list[str]] = None) -> None

CLI entrypoint: parse argv and run the selected command.

Parameters:

Name Type Description Default
argv Optional[list[str]]

Argument list (defaults to process arguments when None).

None
Source code in gopro_api/cli/app.py
def main(argv: Optional[list[str]] = None) -> None:
    """CLI entrypoint: parse ``argv`` and run the selected command.

    Args:
        argv: Argument list (defaults to process arguments when ``None``).
    """
    app(args=argv)

Commands

gopro_api.cli.search_command(ctx: typer.Context, *, start: str = typer.Option(..., '--start', help='Range start: YYYY-MM-DD or ISO datetime'), end: str = typer.Option(..., '--end', help='Range end: YYYY-MM-DD or ISO datetime (API treats range as in query string)'), page: int = typer.Option(1, '--page', help='Page number (default: 1)'), per_page: int = typer.Option(30, '--per-page', help='Page size (default: 30)'), all_pages: bool = typer.Option(False, '--all-pages', help='Keep requesting pages until a page returns no media'), tsv: bool = typer.Option(False, '--tsv', help='Print tab-separated values (header row + metadata line) for scripting'), json_out: bool = typer.Option(False, '--json', help='Print full API JSON (with --all-pages: list of page payloads)')) -> None

Run search against the cloud API and print results.

Source code in gopro_api/cli/search.py
@app.command(
    "search",
    help=(
        "List media in a capture date range (Rich table by default; "
        "--tsv for tab-separated fields; --json for raw API payloads)"
    ),
)
def search_command(  # pylint: disable=too-many-arguments
    ctx: typer.Context,
    *,
    start: str = typer.Option(
        ...,
        "--start",
        help="Range start: YYYY-MM-DD or ISO datetime",
    ),
    end: str = typer.Option(
        ...,
        "--end",
        help=(
            "Range end: YYYY-MM-DD or ISO datetime "
            "(API treats range as in query string)"
        ),
    ),
    page: int = typer.Option(1, "--page", help="Page number (default: 1)"),
    per_page: int = typer.Option(30, "--per-page", help="Page size (default: 30)"),
    all_pages: bool = typer.Option(
        False,
        "--all-pages",
        help="Keep requesting pages until a page returns no media",
    ),
    tsv: bool = typer.Option(
        False,
        "--tsv",
        help="Print tab-separated values (header row + metadata line) for scripting",
    ),
    json_out: bool = typer.Option(
        False,
        "--json",
        help="Print full API JSON (with --all-pages: list of page payloads)",
    ),
) -> None:
    """Run search against the cloud API and print results."""
    asyncio.run(
        _run_search(
            timeout=ctx.obj["timeout"],
            params=_SearchParams(
                start=start,
                end=end,
                page=page,
                per_page=per_page,
                all_pages=all_pages,
                json_out=json_out,
                tsv=tsv,
            ),
        ),
    )

gopro_api.cli.info_command(ctx: typer.Context, media_id: str = typer.Argument(..., help='Media id from search'), tsv: bool = typer.Option(False, '--tsv', help='Print tab-separated values for scripting'), json_out: bool = typer.Option(False, '--json', help='Print full API JSON')) -> None

Fetch and display download metadata for media_id.

Source code in gopro_api/cli/info.py
@app.command(
    "info",
    help=(
        "Show download metadata for one media id "
        "(Rich table by default; --tsv for tab-separated; --json for raw API)"
    ),
)
def info_command(
    ctx: typer.Context,
    media_id: str = typer.Argument(..., help="Media id from search"),
    tsv: bool = typer.Option(
        False,
        "--tsv",
        help="Print tab-separated values for scripting",
    ),
    json_out: bool = typer.Option(False, "--json", help="Print full API JSON"),
) -> None:
    """Fetch and display download metadata for ``media_id``."""
    asyncio.run(
        _run_info(
            timeout=ctx.obj["timeout"],
            media_id=media_id,
            json_out=json_out,
            tsv=tsv,
        ),
    )

gopro_api.cli.pull_command(ctx: typer.Context, media_id: str = typer.Argument(..., help='Media id from search'), destination: str = typer.Argument(..., help='Path to save the file'), height: Optional[int] = typer.Option(None, '--height', metavar='PX', help='For video: pick the variation whose height is closest to PX (default: tallest)'), width: Optional[int] = typer.Option(None, '--width', metavar='PX', help='For video: pick the variation whose width is closest to PX (default: tallest)'), tsv: bool = typer.Option(False, '--tsv', help='Print tab-separated summary instead of the Rich table')) -> None

Download all resolved files for media_id into destination.

Source code in gopro_api/cli/pull.py
@app.command(
    "pull",
    help=(
        "Download files from a media id (prints a Rich summary by default; "
        "--tsv for tab-separated)"
    ),
)
def pull_command(  # pylint: disable=too-many-positional-arguments
    ctx: typer.Context,
    media_id: str = typer.Argument(..., help="Media id from search"),
    destination: str = typer.Argument(..., help="Path to save the file"),
    height: Optional[int] = typer.Option(
        None,
        "--height",
        metavar="PX",
        help=(
            "For video: pick the variation whose height is closest to PX "
            "(default: tallest)"
        ),
    ),
    width: Optional[int] = typer.Option(
        None,
        "--width",
        metavar="PX",
        help=(
            "For video: pick the variation whose width is closest to PX "
            "(default: tallest)"
        ),
    ),
    tsv: bool = typer.Option(
        False,
        "--tsv",
        help="Print tab-separated summary instead of the Rich table",
    ),
) -> None:
    """Download all resolved files for ``media_id`` into ``destination``."""
    height = _validate_positive_px(height, "--height")
    width = _validate_positive_px(width, "--width")
    asyncio.run(
        _run_pull(
            timeout=ctx.obj["timeout"],
            media_id=media_id,
            destination=destination,
            height=height,
            width=width,
            tsv=tsv,
        ),
    )

gopro_api.cli.auth_command(ctx: typer.Context, tsv: bool = typer.Option(False, '--tsv', help='Print tab-separated values for scripting'), json_out: bool = typer.Option(False, '--json', help='Print structured JSON')) -> None

Check whether the GoPro access token is configured and accepted by the API.

Source code in gopro_api/cli/auth.py
@app.command(
    "auth",
    help=(
        "Verify GP_ACCESS_TOKEN configuration and authentication "
        "(Rich panel by default; --tsv or --json for scripting)"
    ),
)
def auth_command(
    ctx: typer.Context,
    tsv: bool = typer.Option(
        False,
        "--tsv",
        help="Print tab-separated values for scripting",
    ),
    json_out: bool = typer.Option(
        False,
        "--json",
        help="Print structured JSON",
    ),
) -> None:
    """Check whether the GoPro access token is configured and accepted by the API."""
    asyncio.run(
        _run_auth(
            timeout=ctx.obj["timeout"],
            json_out=json_out,
            tsv=tsv,
        ),
    )

Printers

gopro_api.cli.search.SearchPrinter(console: Console | None = None)

Handles all search output formatting: Rich table, TSV, and JSON key renaming.

Initialize with an optional Rich console.

Parameters:

Name Type Description Default
console Console | None

Console used for Rich output; a default soft-wrap console is created when None.

None
Source code in gopro_api/cli/search.py
def __init__(self, console: Console | None = None) -> None:
    """Initialize with an optional Rich console.

    Args:
        console: Console used for Rich output; a default soft-wrap console is
            created when ``None``.
    """
    self._console = console or Console(soft_wrap=True)

page_meta_line(page: GoProMediaSearchResponse) -> str

Format the pagination metadata comment line for a search page.

Parameters:

Name Type Description Default
page GoProMediaSearchResponse

Search response containing pagination details.

required

Returns:

Type Description
str

A # _pages: comment string with current page, per-page, total items,

str

and total pages.

Source code in gopro_api/cli/search.py
def page_meta_line(self, page: GoProMediaSearchResponse) -> str:
    """Format the pagination metadata comment line for a search page.

    Args:
        page: Search response containing pagination details.

    Returns:
        A ``# _pages:`` comment string with current page, per-page, total items,
        and total pages.
    """
    pages = page.pages
    return (
        f"# _pages: current_page={pages.current_page} per_page={pages.per_page} "
        f"total_items={pages.total_items} total_pages={pages.total_pages}"
    )

emit_embedded_errors(page: GoProMediaSearchResponse) -> None

Print any embedded API errors to stderr as yellow comment lines.

Parameters:

Name Type Description Default
page GoProMediaSearchResponse

Search response whose _embedded.errors list is checked.

required
Source code in gopro_api/cli/search.py
def emit_embedded_errors(self, page: GoProMediaSearchResponse) -> None:
    """Print any embedded API errors to stderr as yellow comment lines.

    Args:
        page: Search response whose ``_embedded.errors`` list is checked.
    """
    if page.embedded.errors:
        for err in page.embedded.errors:
            typer.secho(
                f"# _embedded.errors: {json.dumps(err, ensure_ascii=False)}",
                fg=typer.colors.YELLOW,
                err=True,
            )

cells_plain(item: GoProMediaSearchItem) -> list[str]

Build raw string cells for TSV output.

Parameters:

Name Type Description Default
item GoProMediaSearchItem

A single media item from the search response.

required

Returns:

Type Description
list[str]

Ordered list of strings for each field in DEFAULT_FIELDS;

list[str]

missing values are represented as empty strings.

Source code in gopro_api/cli/search.py
def cells_plain(self, item: GoProMediaSearchItem) -> list[str]:
    """Build raw string cells for TSV output.

    Args:
        item: A single media item from the search response.

    Returns:
        Ordered list of strings for each field in ``DEFAULT_FIELDS``;
        missing values are represented as empty strings.
    """
    row = item.model_dump(mode="json")
    return ["" if row.get(c) is None else str(row[c]) for c in DEFAULT_FIELDS]

cells_rich(item: GoProMediaSearchItem) -> list[str]

Build human-formatted cells for Rich table output.

File sizes are formatted with decimal SI units; all other values are stringified as-is.

Parameters:

Name Type Description Default
item GoProMediaSearchItem

A single media item from the search response.

required

Returns:

Type Description
list[str]

Ordered list of display strings for each field in DEFAULT_FIELDS.

Source code in gopro_api/cli/search.py
def cells_rich(self, item: GoProMediaSearchItem) -> list[str]:
    """Build human-formatted cells for Rich table output.

    File sizes are formatted with decimal SI units; all other values are
    stringified as-is.

    Args:
        item: A single media item from the search response.

    Returns:
        Ordered list of display strings for each field in ``DEFAULT_FIELDS``.
    """
    row = item.model_dump(mode="json")
    cells: list[str] = []
    for c in DEFAULT_FIELDS:
        val = row.get(c)
        if val is None:
            cells.append("")
        elif c == "file_size":
            cells.append(format_decimal_size(int(val)))
        else:
            cells.append(str(val))
    return cells

make_table() -> Table

Build an empty Rich Table with the default search columns.

Returns:

Type Description
Table

A rich.table.Table configured with per-column overflow settings,

Table

ready to receive rows via add_row.

Source code in gopro_api/cli/search.py
def make_table(self) -> Table:
    """Build an empty Rich Table with the default search columns.

    Returns:
        A ``rich.table.Table`` configured with per-column overflow settings,
        ready to receive rows via ``add_row``.
    """
    table = Table(show_header=True, header_style="bold")
    for name in DEFAULT_FIELDS:
        col_kw: dict = {}
        if name == "filename":
            col_kw["overflow"] = "ellipsis"
            col_kw["max_width"] = 40
        elif name == "captured_at":
            col_kw["overflow"] = "ellipsis"
            col_kw["max_width"] = 28
        elif name == "id":
            col_kw["overflow"] = "fold"
        elif name == "type":
            col_kw["overflow"] = "ellipsis"
            col_kw["max_width"] = 14
        table.add_column(_renamed_field(name), **col_kw)
    return table

print_table(table: Table) -> None

Print a Rich table to the console.

Parameters:

Name Type Description Default
table Table

Fully populated Rich table to render.

required
Source code in gopro_api/cli/search.py
def print_table(self, table: Table) -> None:
    """Print a Rich table to the console.

    Args:
        table: Fully populated Rich table to render.
    """
    self._console.print(table)

print_tsv_page(page: GoProMediaSearchResponse, *, header: bool = True) -> None

Print a TSV-formatted search page to stdout.

Parameters:

Name Type Description Default
page GoProMediaSearchResponse

Search response page to render.

required
header bool

When True (default), emit the column-name header row first; set to False for subsequent pages in --all-pages mode.

True
Source code in gopro_api/cli/search.py
def print_tsv_page(
    self, page: GoProMediaSearchResponse, *, header: bool = True
) -> None:
    """Print a TSV-formatted search page to stdout.

    Args:
        page: Search response page to render.
        header: When ``True`` (default), emit the column-name header row first;
            set to ``False`` for subsequent pages in ``--all-pages`` mode.
    """
    typer.echo(self.page_meta_line(page))
    self.emit_embedded_errors(page)
    if header:
        typer.echo("\t".join(_renamed_field(c) for c in DEFAULT_FIELDS))
    for item in page.embedded.media:
        typer.echo("\t".join(self.cells_plain(item)))

print_rich_page(page: GoProMediaSearchResponse) -> None

Print a single Rich-formatted search page with metadata and a table.

Parameters:

Name Type Description Default
page GoProMediaSearchResponse

Search response page to render.

required
Source code in gopro_api/cli/search.py
def print_rich_page(self, page: GoProMediaSearchResponse) -> None:
    """Print a single Rich-formatted search page with metadata and a table.

    Args:
        page: Search response page to render.
    """
    self.emit_embedded_errors(page)
    typer.echo(self.page_meta_line(page))
    table = self.make_table()
    for item in page.embedded.media:
        table.add_row(*self.cells_rich(item))
    self._console.print(table)

append_rich_rows(table: Table, page: GoProMediaSearchResponse) -> None

Append a page's media rows to an existing Rich table.

Used in --all-pages mode to accumulate rows across pages before a single final render.

Parameters:

Name Type Description Default
table Table

Rich table to append rows to.

required
page GoProMediaSearchResponse

Search response page whose media items are appended.

required
Source code in gopro_api/cli/search.py
def append_rich_rows(self, table: Table, page: GoProMediaSearchResponse) -> None:
    """Append a page's media rows to an existing Rich table.

    Used in ``--all-pages`` mode to accumulate rows across pages before a
    single final render.

    Args:
        table: Rich table to append rows to.
        page: Search response page whose media items are appended.
    """
    self.emit_embedded_errors(page)
    for item in page.embedded.media:
        table.add_row(*self.cells_rich(item))

rename_payload(payload: dict) -> dict

Apply display-name aliases to a raw API JSON payload.

Renames keys inside _embedded.media items according to _FIELD_LABELS so that JSON output uses the same names as the table headers.

Parameters:

Name Type Description Default
payload dict

Raw API payload dict (typically from model_dump).

required

Returns:

Type Description
dict

The same payload dict with _embedded.media keys renamed in-place.

Source code in gopro_api/cli/search.py
def rename_payload(self, payload: dict) -> dict:
    """Apply display-name aliases to a raw API JSON payload.

    Renames keys inside ``_embedded.media`` items according to
    ``_FIELD_LABELS`` so that JSON output uses the same names as the table
    headers.

    Args:
        payload: Raw API payload dict (typically from ``model_dump``).

    Returns:
        The same ``payload`` dict with ``_embedded.media`` keys renamed in-place.
    """
    embedded = payload.get("_embedded")
    if isinstance(embedded, dict):
        media = embedded.get("media")
        if isinstance(media, list):
            embedded["media"] = [
                (
                    {_renamed_field(k): v for k, v in it.items()}
                    if isinstance(it, dict)
                    else it
                )
                for it in media
            ]
    return payload

gopro_api.cli.info.InfoPrinter(console: Console | None = None)

Handles Rich table and TSV rendering for the info command.

Initialize with an optional Rich console.

Parameters:

Name Type Description Default
console Console | None

Console used for Rich output; a default soft-wrap console is created when None.

None
Source code in gopro_api/cli/info.py
def __init__(self, console: Console | None = None) -> None:
    """Initialize with an optional Rich console.

    Args:
        console: Console used for Rich output; a default soft-wrap console is
            created when ``None``.
    """
    self._console = console or Console(soft_wrap=True)

variation_cells(idx: int, v: GoProMediaDownloadVariation) -> list[str]

Build row cells for a video variation entry.

Parameters:

Name Type Description Default
idx int

Zero-based row index shown in the idx column.

required
v GoProMediaDownloadVariation

Variation object from the download metadata.

required

Returns:

Type Description
list[str]

Ordered list of strings matching VARIATION_HEADERS.

Source code in gopro_api/cli/info.py
def variation_cells(self, idx: int, v: GoProMediaDownloadVariation) -> list[str]:
    """Build row cells for a video variation entry.

    Args:
        idx: Zero-based row index shown in the ``idx`` column.
        v: Variation object from the download metadata.

    Returns:
        Ordered list of strings matching ``VARIATION_HEADERS``.
    """
    return [
        str(idx),
        v.label,
        v.quality,
        v.type,
        f"{v.width}x{v.height}",
        _yes_no(v.available),
        v.url,
    ]

file_cells(idx: int, f: GoProMediaDownloadFile) -> list[str]

Build row cells for a multi-lens file entry.

Parameters:

Name Type Description Default
idx int

Zero-based row index shown in the idx column.

required
f GoProMediaDownloadFile

File object from the download metadata.

required

Returns:

Type Description
list[str]

Ordered list of strings matching FILE_HEADERS.

Source code in gopro_api/cli/info.py
def file_cells(self, idx: int, f: GoProMediaDownloadFile) -> list[str]:
    """Build row cells for a multi-lens file entry.

    Args:
        idx: Zero-based row index shown in the ``idx`` column.
        f: File object from the download metadata.

    Returns:
        Ordered list of strings matching ``FILE_HEADERS``.
    """
    return [
        str(idx),
        str(f.item_number),
        f.camera_position,
        f"{f.width}x{f.height}",
        _yes_no(f.available),
        f.url,
    ]

sidecar_cells(idx: int, s: GoProMediaDownloadSidecarFile) -> list[str]

Build row cells for a sidecar file entry.

Parameters:

Name Type Description Default
idx int

Zero-based row index shown in the idx column.

required
s GoProMediaDownloadSidecarFile

Sidecar file object from the download metadata.

required

Returns:

Type Description
list[str]

Ordered list of strings matching SIDECAR_HEADERS.

Source code in gopro_api/cli/info.py
def sidecar_cells(self, idx: int, s: GoProMediaDownloadSidecarFile) -> list[str]:
    """Build row cells for a sidecar file entry.

    Args:
        idx: Zero-based row index shown in the ``idx`` column.
        s: Sidecar file object from the download metadata.

    Returns:
        Ordered list of strings matching ``SIDECAR_HEADERS``.
    """
    return [
        str(idx),
        s.label,
        s.type,
        str(s.fps),
        _yes_no(s.available),
        s.url,
    ]

print_rich(meta: GoProMediaDownloadResponse) -> None

Print a Rich table of variations or files, plus sidecars when present.

Parameters:

Name Type Description Default
meta GoProMediaDownloadResponse

Download metadata response from the GoPro API.

required
Source code in gopro_api/cli/info.py
def print_rich(self, meta: GoProMediaDownloadResponse) -> None:
    """Print a Rich table of variations or files, plus sidecars when present.

    Args:
        meta: Download metadata response from the GoPro API.
    """
    typer.secho(meta.filename, bold=True)
    if is_video_filename(meta.filename):
        table = _build_basic_table(self.VARIATION_HEADERS)
        for idx, v in enumerate(meta.embedded.variations):
            table.add_row(*self.variation_cells(idx, v))
    else:
        table = _build_basic_table(self.FILE_HEADERS)
        for idx, f in enumerate(meta.embedded.files):
            table.add_row(*self.file_cells(idx, f))
    self._console.print(table)
    if meta.embedded.sidecar_files:
        typer.secho("sidecars", bold=True)
        sidecars = _build_basic_table(self.SIDECAR_HEADERS)
        for idx, s in enumerate(meta.embedded.sidecar_files):
            sidecars.add_row(*self.sidecar_cells(idx, s))
        self._console.print(sidecars)

print_tsv(meta: GoProMediaDownloadResponse) -> None

Print tab-separated rows of variations or files, plus sidecars when present.

Parameters:

Name Type Description Default
meta GoProMediaDownloadResponse

Download metadata response from the GoPro API.

required
Source code in gopro_api/cli/info.py
def print_tsv(self, meta: GoProMediaDownloadResponse) -> None:
    """Print tab-separated rows of variations or files, plus sidecars when present.

    Args:
        meta: Download metadata response from the GoPro API.
    """
    typer.echo(f"# filename: {meta.filename}")
    if is_video_filename(meta.filename):
        typer.echo("\t".join(self.VARIATION_HEADERS))
        for idx, v in enumerate(meta.embedded.variations):
            typer.echo("\t".join(self.variation_cells(idx, v)))
    else:
        typer.echo("\t".join(self.FILE_HEADERS))
        for idx, f in enumerate(meta.embedded.files):
            typer.echo("\t".join(self.file_cells(idx, f)))
    if meta.embedded.sidecar_files:
        typer.echo("# sidecars")
        typer.echo("\t".join(self.SIDECAR_HEADERS))
        for idx, s in enumerate(meta.embedded.sidecar_files):
            typer.echo("\t".join(self.sidecar_cells(idx, s)))

gopro_api.cli.pull.PullPrinter(console: Console | None = None)

Handles Rich table and TSV rendering for the pull command.

Initialize with an optional Rich console.

Parameters:

Name Type Description Default
console Console | None

Console used for Rich output; a default soft-wrap console is created when None.

None
Source code in gopro_api/cli/pull.py
def __init__(self, console: Console | None = None) -> None:
    """Initialize with an optional Rich console.

    Args:
        console: Console used for Rich output; a default soft-wrap console is
            created when ``None``.
    """
    self._console = console or Console(soft_wrap=True)

summary_cells(filename: str, asset: DownloadAsset) -> list[str]

Build row cells for a single download asset.

Parameters:

Name Type Description Default
filename str

Local filename that the asset will be saved as.

required
asset DownloadAsset

Resolved download asset containing URL and dimensions.

required

Returns:

Type Description
list[str]

Ordered list of strings matching HEADERS.

Source code in gopro_api/cli/pull.py
def summary_cells(self, filename: str, asset: DownloadAsset) -> list[str]:
    """Build row cells for a single download asset.

    Args:
        filename: Local filename that the asset will be saved as.
        asset: Resolved download asset containing URL and dimensions.

    Returns:
        Ordered list of strings matching ``HEADERS``.
    """
    return [
        filename,
        f"{asset.width}x{asset.height}",
        _yes_no(asset.available),
        asset.url,
    ]

print_rich(assets: dict[str, DownloadAsset], destination: str) -> None

Print a Rich summary table of all assets to be downloaded.

Parameters:

Name Type Description Default
assets dict[str, DownloadAsset]

Mapping of local filename to resolved download asset.

required
destination str

Target directory path shown in the header line.

required
Source code in gopro_api/cli/pull.py
def print_rich(self, assets: dict[str, DownloadAsset], destination: str) -> None:
    """Print a Rich summary table of all assets to be downloaded.

    Args:
        assets: Mapping of local filename to resolved download asset.
        destination: Target directory path shown in the header line.
    """
    typer.secho(
        f"Pulling {len(assets)} file(s) to {destination}",
        bold=True,
    )
    table = _build_basic_table(self.HEADERS)
    for filename, asset in assets.items():
        table.add_row(*self.summary_cells(filename, asset))
    self._console.print(table)

print_tsv(assets: dict[str, DownloadAsset], destination: str) -> None

Print a tab-separated summary of all assets for scripting.

Parameters:

Name Type Description Default
assets dict[str, DownloadAsset]

Mapping of local filename to resolved download asset.

required
destination str

Target directory path shown in the comment header.

required
Source code in gopro_api/cli/pull.py
def print_tsv(self, assets: dict[str, DownloadAsset], destination: str) -> None:
    """Print a tab-separated summary of all assets for scripting.

    Args:
        assets: Mapping of local filename to resolved download asset.
        destination: Target directory path shown in the comment header.
    """
    typer.echo(f"# destination: {destination}")
    typer.echo("\t".join(self.HEADERS))
    for filename, asset in assets.items():
        typer.echo("\t".join(self.summary_cells(filename, asset)))

gopro_api.cli.auth.AuthPrinter(console: Console | None = None)

Handles Rich, TSV, and JSON rendering for the auth command.

Initialize with an optional Rich console.

Parameters:

Name Type Description Default
console Console | None

Console used for Rich output; a default soft-wrap console is created when None.

None
Source code in gopro_api/cli/auth.py
def __init__(self, console: Console | None = None) -> None:
    """Initialize with an optional Rich console.

    Args:
        console: Console used for Rich output; a default soft-wrap console is
            created when ``None``.
    """
    self._console = console or Console(soft_wrap=True)

verifying_status() -> Iterator[None]

Show a spinner while the API verification request runs.

Source code in gopro_api/cli/auth.py
@contextmanager
def verifying_status(self) -> Iterator[None]:
    """Show a spinner while the API verification request runs."""
    with self._console.status(
        "⏳ [bold cyan]Verifying access token…[/bold cyan]",
    ):
        yield

print_rich(status: GoProAuthStatus) -> None

Print authentication status as a Rich panel.

Parameters:

Name Type Description Default
status GoProAuthStatus

Authentication status from the API client.

required
Source code in gopro_api/cli/auth.py
def print_rich(self, status: GoProAuthStatus) -> None:
    """Print authentication status as a Rich panel.

    Args:
        status: Authentication status from the API client.
    """
    table = Table(show_header=False, box=box.SIMPLE)
    table.add_column("field", style="bold")
    table.add_column("value")
    table.add_row("token configured", _yes_no(status.token_configured))
    table.add_row(
        "token source",
        status.token_source or "—",
    )
    table.add_row("authenticated", self._authenticated_label(status))
    if status.http_status is not None:
        table.add_row("http status", str(status.http_status))
    table.add_row("message", status.message)

    border = "green" if status.authenticated else "red"
    if not status.token_configured:
        border = "yellow"
    elif status.authenticated is None:
        border = "yellow"

    self._console.print(
        Panel(
            table,
            title="[bold blue]Authentication[/bold blue]",
            border_style=border,
        ),
    )

print_tsv(status: GoProAuthStatus) -> None

Print authentication status as tab-separated key/value rows.

Parameters:

Name Type Description Default
status GoProAuthStatus

Authentication status from the API client.

required
Source code in gopro_api/cli/auth.py
def print_tsv(self, status: GoProAuthStatus) -> None:
    """Print authentication status as tab-separated key/value rows.

    Args:
        status: Authentication status from the API client.
    """
    rows = {
        "token_configured": _yes_no(status.token_configured),
        "token_source": status.token_source or "",
        "authenticated": self._authenticated_label(status),
        "http_status": (
            "" if status.http_status is None else str(status.http_status)
        ),
        "message": status.message,
    }
    console = Console(soft_wrap=True, highlight=False, markup=False)
    console.print("field\tvalue")
    for key, value in rows.items():
        console.print(f"{key}\t{value}")

print_json(status: GoProAuthStatus) -> None

Print authentication status as JSON on stdout.

Parameters:

Name Type Description Default
status GoProAuthStatus

Authentication status from the API client.

required
Source code in gopro_api/cli/auth.py
def print_json(self, status: GoProAuthStatus) -> None:
    """Print authentication status as JSON on stdout.

    Args:
        status: Authentication status from the API client.
    """
    sys.stdout.write(json.dumps(status.model_dump(mode="json"), indent=2))
    sys.stdout.write("\n")

exit_code(status: GoProAuthStatus) -> int

Map authentication status to a process exit code.

Parameters:

Name Type Description Default
status GoProAuthStatus

Authentication status from the API client.

required

Returns:

Type Description
int

0 when authenticated, 2 when the token is missing, 1 otherwise.

Source code in gopro_api/cli/auth.py
def exit_code(self, status: GoProAuthStatus) -> int:
    """Map authentication status to a process exit code.

    Args:
        status: Authentication status from the API client.

    Returns:
        ``0`` when authenticated, ``2`` when the token is missing, ``1`` otherwise.
    """
    if not status.token_configured:
        return 2
    if status.authenticated:
        return 0
    return 1