Metadata-Version: 2.5
Name: msteams-transcripts
Version: 1.0.1
Summary: List and download Microsoft Teams meeting transcripts from the command line, through your own signed-in browser session.
Project-URL: Homepage, https://github.com/divyavanmahajan/msteams-transcripts
Project-URL: Repository, https://github.com/divyavanmahajan/msteams-transcripts
Project-URL: Issues, https://github.com/divyavanmahajan/msteams-transcripts/issues
Author-email: Divya Van Mahajan <divyavanmahajan@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: cli,meeting,microsoft-teams,recap,sharepoint,transcript
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Conferencing
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: playwright>=1.40
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: hatch>=1.9; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Description-Content-Type: text/markdown

# msteams-transcripts

Download Microsoft Teams meeting transcripts from the command line, including
the meetings where the Download button is greyed out.

It drives a browser you are already signed in to, so it needs no admin consent,
no app registration and no Graph permissions, and it reaches exactly the
meetings you can open yourself. Anything you can read in the Recap tab, this
can save to a file.

```powershell
uvx msteams-transcripts browser
uvx msteams-transcripts list --from 2026-09-01 --to today
uvx msteams-transcripts get 3 --details
```

## Why the Download button being disabled does not matter

- **Listing meetings.** The Teams web client loads your calendar from its
  middle-tier API. Each online meeting carries a thread identifier.
- **Finding the transcript.** The Recap tab asks the meeting-content service
  about that thread. The reply lists resources; a transcript resource holds the
  OneDrive drive, item and transcript identifiers.
- **Reading the transcript.** The recap page renders it from SharePoint, at
  `/_api/v2.1/drives/<driveId>/items/<itemId>/media/transcripts/<id>/content`,
  in JSON or WebVTT. That call is authenticated by your SharePoint cookies
  alone. The whole transcript is already delivered to your browser in one
  request, so no scrolling or scraping is involved. The disabled button is a
  user-interface policy, not an access control.
- **Tokens.** The two Teams APIs want bearer tokens that the web client keeps
  encrypted in local storage. The tool captures them from the requests the
  Teams tab makes as it loads.

## Install

With [uv](https://docs.astral.sh/uv/), nothing to install:

```powershell
uvx msteams-transcripts --help
```

Or install it permanently:

```powershell
uv tool install msteams-transcripts     # or: pipx install msteams-transcripts
pip install msteams-transcripts         # into the current environment
```

Both `msteams-transcripts` and the shorter `teams-transcripts` are installed as
commands. Python 3.10 or newer is required. Playwright comes as a dependency,
but **do not run `playwright install`**: the tool attaches to the Edge or Chrome
already on your machine and downloads no browsers of its own.

## Walkthrough

A first session from a clean machine, start to finish.

**1. Start the browser.**

```powershell
uvx msteams-transcripts browser
```

An Edge window opens on Teams using a profile of its own. On a corporate,
Entra-joined PC it usually signs in by itself. If a sign-in page appears,
complete it there once. Leave the window open; you can minimise it. Recent Edge
and Chrome refuse remote debugging on your everyday profile, which is why a
separate one is used.

**2. See what is available.**

```powershell
uvx msteams-transcripts list --from 2026-09-01 --to today
```

```
  #  Start (UTC)       Subject                                            Organizer
  1  2026-09-15 05:41  Weekly one to one                                  Priya Raman
  2  2026-09-15 06:09  Platform team sync                                 Tomas Lindqvist
  3  2026-09-16 09:02  Architecture review: storage tiering               Maya Okonkwo
12 transcript(s). Use:  msteams-transcripts get <#>
```

The first run takes 30 to 60 seconds while Teams loads and issues its tokens;
later runs in the same browser session are quicker. Rows are transcripts rather
than meetings, so a recurring meeting recorded twice appears twice.

**3. Download one transcript with the meeting details.**

```powershell
uvx msteams-transcripts get 3 --details
```

This writes `transcripts\2026-09-16 0902 - Architecture review storage tiering.txt`:

```
# Architecture review: storage tiering
# Date: 2026-09-16 09:02
# Source: https://contoso-my.sharepoint.com/personal/.../Recordings/Architecture-review....mp4
# Organizer: Maya Okonkwo <maya.okonkwo@example.com>
# Location: Microsoft Teams Meeting
# Invited (13):
#   Priya Raman <priya.raman@example.com> (Required, Accepted)
#   Tomas Lindqvist <tomas.lindqvist@example.com> (Required, Tentative)
#   ...
# Speakers in transcript (5):
#   Priya Raman (84 segments)
#   Maya Okonkwo (72 segments)
#   ...
# Files shared in meeting chat (1):
#   capacity-plan.xlsx  https://contoso.sharepoint.com/sites/...
# Invitation text:
#   Agenda: ...

[00:00:03] Priya Raman:
    Good morning everyone, let's start with the architecture review.
```

A sidecar `...meta.json` holds the same details as structured data: attendees
with response status, speakers with segment counts, shared files, invitation
text and the thread identifier.

**4. Download from a link instead.** Open the meeting in Teams, go to the Recap
tab, copy the address bar, and pass it:

```powershell
uvx msteams-transcripts get "https://teams.cloud.microsoft/l/meetingrecap?driveId=...&driveItemId=...&sitePath=..." --details -f all
```

This path does not touch the calendar and finishes in a few seconds.

**5. Archive a whole period.**

```powershell
uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --details -o C:\Archive\teams
uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --only 3,7,12
```

Files with the same name are overwritten. Batch ends with a count of what was
downloaded and what failed.

**6. Skipping the "Stay better connected with the Teams desktop app" popup**
when you open recap links yourself: change `/l/meetingrecap?` in the address to
`/_#/l/meetingrecap?`. That form opens the web client directly. The tool
already uses it internally.

## Commands

```
msteams-transcripts browser
msteams-transcripts list  --from YYYY-MM-DD --to YYYY-MM-DD|today [--json]
msteams-transcripts get   <row#|recap-url|thread-id> [-d] [-f txt|json|vtt|all] [-o DIR]
msteams-transcripts batch --from D --to D [-d] [-f FMT] [-o DIR] [--only 3,7,12]
```

| Option | Meaning |
|---|---|
| `-d`, `--details` | organizer, location, invited attendees with response status, speakers, shared files, invitation text, plus a `.meta.json` sidecar |
| `-f`, `--format` | `txt` (default), `json` for the raw Stream document, `vtt` for WebVTT, or `all` |
| `-o`, `--out` | output directory, `./transcripts` by default |
| `--json` | on `list`, print the rows as JSON for scripting |
| `--only` | on `batch`, restrict to given row numbers |
| `--port`, `--profile`, `--browser` | override the debugging port, profile directory or browser executable |

The text format is:

```
[00:00:03] Speaker Name:
    what they said
    the next sentence by the same speaker
```

Timestamps are offsets from the start of the recording, not wall-clock time.
Row numbers used by `get N` come from the most recent `list`, cached in
`~/.teams_transcripts/last_list.json`.

## Using it from an agent

`.claude/skills/teams-transcripts/SKILL.md` describes the tool for Claude Code
or another agent: preconditions, commands, output format, typical flows and
error handling. Claude Code picks it up automatically when working in this
project.

## Library use

```python
import asyncio
from pathlib import Path
from teams_transcripts import TeamsSession, download_ref, refs_from_recap_url

async def main():
    async with TeamsSession() as s:
        ref = refs_from_recap_url("https://teams.cloud.microsoft/l/meetingrecap?...")
        await download_ref(s, ref, Path("out"), "txt", details=True)

asyncio.run(main())
```

Anything the user can act on raises `TranscriptError`.

## Environment variables

| Variable | Default | Purpose |
|---|---|---|
| `TT_CDP_PORT` | `9222` | browser debugging port |
| `TT_PROFILE_DIR` | platform application-data directory | browser profile |
| `TT_BROWSER_EXE` | Edge, then Chrome, from the usual locations | browser executable |
| `TT_STATE_DIR` | `~/.teams_transcripts` | where the last listing is cached |

## Troubleshooting

- **"The browser did not expose its debugging port"**: another window is
  already using the dedicated profile without the port. Close it, then run
  `browser` again.
- **"Could not capture a Teams token"**: the Teams tab in that window is not
  signed in. Sign in there, then retry.
- **"Could not capture the meeting-content token"**: open any meeting's Recap
  tab in that window, then retry.
- **HTTP 403 when listing or downloading**: the recording belongs to a tenant
  or site you cannot read. The Recap tab would be empty for you too.
- **Not available through this tool**: the attendance report of who actually
  joined and when, collaborative Loop notes, and Copilot AI notes. `--details`
  gives the invited list from the calendar and who actually spoke per the
  transcript.
- **Recurring meetings** share one thread identifier. Each occurrence's
  transcript is listed separately with its own start time.

## Development

```powershell
git clone https://github.com/divyavanmahajan/msteams-transcripts
cd msteams-transcripts
pip install -e ".[dev]"
python -m pytest
```

The tests cover the pure functions in `model.py` and `formats.py` and need
neither a browser nor the network.

Hatchling owns the version, read from `__version__` in
`src/teams_transcripts/__init__.py`. To release:

```powershell
hatch version patch            # or minor, major, or an explicit 1.2.3
git commit -am "Release 0.1.1"
git tag v0.1.1
git push --follow-tags
```

Pushing the tag runs `.github/workflows/publish.yml`, which refuses to continue
if the tag and the package version disagree, then builds, publishes to PyPI
through trusted publishing and creates a GitHub release. Trusted publishing is
configured once on PyPI under Publishing, naming this repository, the
`publish.yml` workflow and the `pypi` environment; no API token is stored.
Running the workflow by hand offers TestPyPI as the target.

## Licence

MIT. Transcripts are meeting content belonging to the people in them; this tool
only fetches what you already have access to, and what you do with it afterwards
is your responsibility.
