msteams-transcripts 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ # Downloaded meeting content - never commit this
2
+ transcripts/
3
+ transcripts_*/
4
+ *.vtt
5
+ *.meta.json
6
+
7
+ # Python
8
+ __pycache__/
9
+ *.py[cod]
10
+ *.egg-info/
11
+ .eggs/
12
+ build/
13
+ dist/
14
+ .venv/
15
+ venv/
16
+ env/
17
+ .python-version
18
+
19
+ # Tooling
20
+ .pytest_cache/
21
+ .ruff_cache/
22
+ .mypy_cache/
23
+ .coverage
24
+ htmlcov/
25
+ .hatch/
26
+
27
+ # Editors and OS
28
+ .vscode/
29
+ .idea/
30
+ *.swp
31
+ .DS_Store
32
+ Thumbs.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Divya Van Mahajan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,315 @@
1
+ Metadata-Version: 2.5
2
+ Name: msteams-transcripts
3
+ Version: 1.0.0
4
+ Summary: List and download Microsoft Teams meeting transcripts from the command line, through your own signed-in browser session.
5
+ Project-URL: Homepage, https://github.com/divyavanmahajan/msteams-transcripts
6
+ Project-URL: Repository, https://github.com/divyavanmahajan/msteams-transcripts
7
+ Project-URL: Issues, https://github.com/divyavanmahajan/msteams-transcripts/issues
8
+ Author-email: Divya Van Mahajan <divyavanmahajan@users.noreply.github.com>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Divya Van Mahajan
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: cli,meeting,microsoft-teams,recap,sharepoint,transcript
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Environment :: Console
34
+ Classifier: Intended Audience :: End Users/Desktop
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: MacOS
37
+ Classifier: Operating System :: Microsoft :: Windows
38
+ Classifier: Operating System :: POSIX :: Linux
39
+ Classifier: Programming Language :: Python :: 3
40
+ Classifier: Programming Language :: Python :: 3.10
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3.12
43
+ Classifier: Programming Language :: Python :: 3.13
44
+ Classifier: Topic :: Communications :: Conferencing
45
+ Classifier: Topic :: Utilities
46
+ Requires-Python: >=3.10
47
+ Requires-Dist: playwright>=1.40
48
+ Provides-Extra: dev
49
+ Requires-Dist: build>=1.0; extra == 'dev'
50
+ Requires-Dist: hatch>=1.9; extra == 'dev'
51
+ Requires-Dist: pytest>=7; extra == 'dev'
52
+ Requires-Dist: twine>=5.0; extra == 'dev'
53
+ Description-Content-Type: text/markdown
54
+
55
+ # msteams-transcripts
56
+
57
+ Download Microsoft Teams meeting transcripts from the command line, including
58
+ the meetings where the Download button is greyed out.
59
+
60
+ It drives a browser you are already signed in to, so it needs no admin consent,
61
+ no app registration and no Graph permissions, and it reaches exactly the
62
+ meetings you can open yourself. Anything you can read in the Recap tab, this
63
+ can save to a file.
64
+
65
+ ```powershell
66
+ uvx msteams-transcripts browser
67
+ uvx msteams-transcripts list --from 2026-09-01 --to today
68
+ uvx msteams-transcripts get 3 --details
69
+ ```
70
+
71
+ ## Why the Download button being disabled does not matter
72
+
73
+ - **Listing meetings.** The Teams web client loads your calendar from its
74
+ middle-tier API. Each online meeting carries a thread identifier.
75
+ - **Finding the transcript.** The Recap tab asks the meeting-content service
76
+ about that thread. The reply lists resources; a transcript resource holds the
77
+ OneDrive drive, item and transcript identifiers.
78
+ - **Reading the transcript.** The recap page renders it from SharePoint, at
79
+ `/_api/v2.1/drives/<driveId>/items/<itemId>/media/transcripts/<id>/content`,
80
+ in JSON or WebVTT. That call is authenticated by your SharePoint cookies
81
+ alone. The whole transcript is already delivered to your browser in one
82
+ request, so no scrolling or scraping is involved. The disabled button is a
83
+ user-interface policy, not an access control.
84
+ - **Tokens.** The two Teams APIs want bearer tokens that the web client keeps
85
+ encrypted in local storage. The tool captures them from the requests the
86
+ Teams tab makes as it loads.
87
+
88
+ ## Install
89
+
90
+ With [uv](https://docs.astral.sh/uv/), nothing to install:
91
+
92
+ ```powershell
93
+ uvx msteams-transcripts --help
94
+ ```
95
+
96
+ Or install it permanently:
97
+
98
+ ```powershell
99
+ uv tool install msteams-transcripts # or: pipx install msteams-transcripts
100
+ pip install msteams-transcripts # into the current environment
101
+ ```
102
+
103
+ Both `msteams-transcripts` and the shorter `teams-transcripts` are installed as
104
+ commands. Python 3.10 or newer is required. Playwright comes as a dependency,
105
+ but **do not run `playwright install`**: the tool attaches to the Edge or Chrome
106
+ already on your machine and downloads no browsers of its own.
107
+
108
+ ## Walkthrough
109
+
110
+ A first session from a clean machine, start to finish.
111
+
112
+ **1. Start the browser.**
113
+
114
+ ```powershell
115
+ uvx msteams-transcripts browser
116
+ ```
117
+
118
+ An Edge window opens on Teams using a profile of its own. On a corporate,
119
+ Entra-joined PC it usually signs in by itself. If a sign-in page appears,
120
+ complete it there once. Leave the window open; you can minimise it. Recent Edge
121
+ and Chrome refuse remote debugging on your everyday profile, which is why a
122
+ separate one is used.
123
+
124
+ **2. See what is available.**
125
+
126
+ ```powershell
127
+ uvx msteams-transcripts list --from 2026-09-01 --to today
128
+ ```
129
+
130
+ ```
131
+ # Start (UTC) Subject Organizer
132
+ 1 2026-09-15 05:41 Weekly one to one Priya Raman
133
+ 2 2026-09-15 06:09 Platform team sync Tomas Lindqvist
134
+ 3 2026-09-16 09:02 Architecture review: storage tiering Maya Okonkwo
135
+ 12 transcript(s). Use: msteams-transcripts get <#>
136
+ ```
137
+
138
+ The first run takes 30 to 60 seconds while Teams loads and issues its tokens;
139
+ later runs in the same browser session are quicker. Rows are transcripts rather
140
+ than meetings, so a recurring meeting recorded twice appears twice.
141
+
142
+ **3. Download one transcript with the meeting details.**
143
+
144
+ ```powershell
145
+ uvx msteams-transcripts get 3 --details
146
+ ```
147
+
148
+ This writes `transcripts\2026-09-16 0902 - Architecture review storage tiering.txt`:
149
+
150
+ ```
151
+ # Architecture review: storage tiering
152
+ # Date: 2026-09-16 09:02
153
+ # Source: https://contoso-my.sharepoint.com/personal/.../Recordings/Architecture-review....mp4
154
+ # Organizer: Maya Okonkwo <maya.okonkwo@example.com>
155
+ # Location: Microsoft Teams Meeting
156
+ # Invited (13):
157
+ # Priya Raman <priya.raman@example.com> (Required, Accepted)
158
+ # Tomas Lindqvist <tomas.lindqvist@example.com> (Required, Tentative)
159
+ # ...
160
+ # Speakers in transcript (5):
161
+ # Priya Raman (84 segments)
162
+ # Maya Okonkwo (72 segments)
163
+ # ...
164
+ # Files shared in meeting chat (1):
165
+ # capacity-plan.xlsx https://contoso.sharepoint.com/sites/...
166
+ # Invitation text:
167
+ # Agenda: ...
168
+
169
+ [00:00:03] Priya Raman:
170
+ Good morning everyone, let's start with the architecture review.
171
+ ```
172
+
173
+ A sidecar `...meta.json` holds the same details as structured data: attendees
174
+ with response status, speakers with segment counts, shared files, invitation
175
+ text and the thread identifier.
176
+
177
+ **4. Download from a link instead.** Open the meeting in Teams, go to the Recap
178
+ tab, copy the address bar, and pass it:
179
+
180
+ ```powershell
181
+ uvx msteams-transcripts get "https://teams.cloud.microsoft/l/meetingrecap?driveId=...&driveItemId=...&sitePath=..." --details -f all
182
+ ```
183
+
184
+ This path does not touch the calendar and finishes in a few seconds.
185
+
186
+ **5. Archive a whole period.**
187
+
188
+ ```powershell
189
+ uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --details -o C:\Archive\teams
190
+ uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --only 3,7,12
191
+ ```
192
+
193
+ Files with the same name are overwritten. Batch ends with a count of what was
194
+ downloaded and what failed.
195
+
196
+ **6. Skipping the "Stay better connected with the Teams desktop app" popup**
197
+ when you open recap links yourself: change `/l/meetingrecap?` in the address to
198
+ `/_#/l/meetingrecap?`. That form opens the web client directly. The tool
199
+ already uses it internally.
200
+
201
+ ## Commands
202
+
203
+ ```
204
+ msteams-transcripts browser
205
+ msteams-transcripts list --from YYYY-MM-DD --to YYYY-MM-DD|today [--json]
206
+ msteams-transcripts get <row#|recap-url|thread-id> [-d] [-f txt|json|vtt|all] [-o DIR]
207
+ msteams-transcripts batch --from D --to D [-d] [-f FMT] [-o DIR] [--only 3,7,12]
208
+ ```
209
+
210
+ | Option | Meaning |
211
+ |---|---|
212
+ | `-d`, `--details` | organizer, location, invited attendees with response status, speakers, shared files, invitation text, plus a `.meta.json` sidecar |
213
+ | `-f`, `--format` | `txt` (default), `json` for the raw Stream document, `vtt` for WebVTT, or `all` |
214
+ | `-o`, `--out` | output directory, `./transcripts` by default |
215
+ | `--json` | on `list`, print the rows as JSON for scripting |
216
+ | `--only` | on `batch`, restrict to given row numbers |
217
+ | `--port`, `--profile`, `--browser` | override the debugging port, profile directory or browser executable |
218
+
219
+ The text format is:
220
+
221
+ ```
222
+ [00:00:03] Speaker Name:
223
+ what they said
224
+ the next sentence by the same speaker
225
+ ```
226
+
227
+ Timestamps are offsets from the start of the recording, not wall-clock time.
228
+ Row numbers used by `get N` come from the most recent `list`, cached in
229
+ `~/.teams_transcripts/last_list.json`.
230
+
231
+ ## Using it from an agent
232
+
233
+ `.claude/skills/teams-transcripts/SKILL.md` describes the tool for Claude Code
234
+ or another agent: preconditions, commands, output format, typical flows and
235
+ error handling. Claude Code picks it up automatically when working in this
236
+ project.
237
+
238
+ ## Library use
239
+
240
+ ```python
241
+ import asyncio
242
+ from pathlib import Path
243
+ from teams_transcripts import TeamsSession, download_ref, refs_from_recap_url
244
+
245
+ async def main():
246
+ async with TeamsSession() as s:
247
+ ref = refs_from_recap_url("https://teams.cloud.microsoft/l/meetingrecap?...")
248
+ await download_ref(s, ref, Path("out"), "txt", details=True)
249
+
250
+ asyncio.run(main())
251
+ ```
252
+
253
+ Anything the user can act on raises `TranscriptError`.
254
+
255
+ ## Environment variables
256
+
257
+ | Variable | Default | Purpose |
258
+ |---|---|---|
259
+ | `TT_CDP_PORT` | `9222` | browser debugging port |
260
+ | `TT_PROFILE_DIR` | platform application-data directory | browser profile |
261
+ | `TT_BROWSER_EXE` | Edge, then Chrome, from the usual locations | browser executable |
262
+ | `TT_STATE_DIR` | `~/.teams_transcripts` | where the last listing is cached |
263
+
264
+ ## Troubleshooting
265
+
266
+ - **"The browser did not expose its debugging port"**: another window is
267
+ already using the dedicated profile without the port. Close it, then run
268
+ `browser` again.
269
+ - **"Could not capture a Teams token"**: the Teams tab in that window is not
270
+ signed in. Sign in there, then retry.
271
+ - **"Could not capture the meeting-content token"**: open any meeting's Recap
272
+ tab in that window, then retry.
273
+ - **HTTP 403 when listing or downloading**: the recording belongs to a tenant
274
+ or site you cannot read. The Recap tab would be empty for you too.
275
+ - **Not available through this tool**: the attendance report of who actually
276
+ joined and when, collaborative Loop notes, and Copilot AI notes. `--details`
277
+ gives the invited list from the calendar and who actually spoke per the
278
+ transcript.
279
+ - **Recurring meetings** share one thread identifier. Each occurrence's
280
+ transcript is listed separately with its own start time.
281
+
282
+ ## Development
283
+
284
+ ```powershell
285
+ git clone https://github.com/divyavanmahajan/msteams-transcripts
286
+ cd msteams-transcripts
287
+ pip install -e ".[dev]"
288
+ python -m pytest
289
+ ```
290
+
291
+ The tests cover the pure functions in `model.py` and `formats.py` and need
292
+ neither a browser nor the network.
293
+
294
+ Hatchling owns the version, read from `__version__` in
295
+ `src/teams_transcripts/__init__.py`. To release:
296
+
297
+ ```powershell
298
+ hatch version patch # or minor, major, or an explicit 1.2.3
299
+ git commit -am "Release 0.1.1"
300
+ git tag v0.1.1
301
+ git push --follow-tags
302
+ ```
303
+
304
+ Pushing the tag runs `.github/workflows/publish.yml`, which refuses to continue
305
+ if the tag and the package version disagree, then builds, publishes to PyPI
306
+ through trusted publishing and creates a GitHub release. Trusted publishing is
307
+ configured once on PyPI under Publishing, naming this repository, the
308
+ `publish.yml` workflow and the `pypi` environment; no API token is stored.
309
+ Running the workflow by hand offers TestPyPI as the target.
310
+
311
+ ## Licence
312
+
313
+ MIT. Transcripts are meeting content belonging to the people in them; this tool
314
+ only fetches what you already have access to, and what you do with it afterwards
315
+ is your responsibility.
@@ -0,0 +1,261 @@
1
+ # msteams-transcripts
2
+
3
+ Download Microsoft Teams meeting transcripts from the command line, including
4
+ the meetings where the Download button is greyed out.
5
+
6
+ It drives a browser you are already signed in to, so it needs no admin consent,
7
+ no app registration and no Graph permissions, and it reaches exactly the
8
+ meetings you can open yourself. Anything you can read in the Recap tab, this
9
+ can save to a file.
10
+
11
+ ```powershell
12
+ uvx msteams-transcripts browser
13
+ uvx msteams-transcripts list --from 2026-09-01 --to today
14
+ uvx msteams-transcripts get 3 --details
15
+ ```
16
+
17
+ ## Why the Download button being disabled does not matter
18
+
19
+ - **Listing meetings.** The Teams web client loads your calendar from its
20
+ middle-tier API. Each online meeting carries a thread identifier.
21
+ - **Finding the transcript.** The Recap tab asks the meeting-content service
22
+ about that thread. The reply lists resources; a transcript resource holds the
23
+ OneDrive drive, item and transcript identifiers.
24
+ - **Reading the transcript.** The recap page renders it from SharePoint, at
25
+ `/_api/v2.1/drives/<driveId>/items/<itemId>/media/transcripts/<id>/content`,
26
+ in JSON or WebVTT. That call is authenticated by your SharePoint cookies
27
+ alone. The whole transcript is already delivered to your browser in one
28
+ request, so no scrolling or scraping is involved. The disabled button is a
29
+ user-interface policy, not an access control.
30
+ - **Tokens.** The two Teams APIs want bearer tokens that the web client keeps
31
+ encrypted in local storage. The tool captures them from the requests the
32
+ Teams tab makes as it loads.
33
+
34
+ ## Install
35
+
36
+ With [uv](https://docs.astral.sh/uv/), nothing to install:
37
+
38
+ ```powershell
39
+ uvx msteams-transcripts --help
40
+ ```
41
+
42
+ Or install it permanently:
43
+
44
+ ```powershell
45
+ uv tool install msteams-transcripts # or: pipx install msteams-transcripts
46
+ pip install msteams-transcripts # into the current environment
47
+ ```
48
+
49
+ Both `msteams-transcripts` and the shorter `teams-transcripts` are installed as
50
+ commands. Python 3.10 or newer is required. Playwright comes as a dependency,
51
+ but **do not run `playwright install`**: the tool attaches to the Edge or Chrome
52
+ already on your machine and downloads no browsers of its own.
53
+
54
+ ## Walkthrough
55
+
56
+ A first session from a clean machine, start to finish.
57
+
58
+ **1. Start the browser.**
59
+
60
+ ```powershell
61
+ uvx msteams-transcripts browser
62
+ ```
63
+
64
+ An Edge window opens on Teams using a profile of its own. On a corporate,
65
+ Entra-joined PC it usually signs in by itself. If a sign-in page appears,
66
+ complete it there once. Leave the window open; you can minimise it. Recent Edge
67
+ and Chrome refuse remote debugging on your everyday profile, which is why a
68
+ separate one is used.
69
+
70
+ **2. See what is available.**
71
+
72
+ ```powershell
73
+ uvx msteams-transcripts list --from 2026-09-01 --to today
74
+ ```
75
+
76
+ ```
77
+ # Start (UTC) Subject Organizer
78
+ 1 2026-09-15 05:41 Weekly one to one Priya Raman
79
+ 2 2026-09-15 06:09 Platform team sync Tomas Lindqvist
80
+ 3 2026-09-16 09:02 Architecture review: storage tiering Maya Okonkwo
81
+ 12 transcript(s). Use: msteams-transcripts get <#>
82
+ ```
83
+
84
+ The first run takes 30 to 60 seconds while Teams loads and issues its tokens;
85
+ later runs in the same browser session are quicker. Rows are transcripts rather
86
+ than meetings, so a recurring meeting recorded twice appears twice.
87
+
88
+ **3. Download one transcript with the meeting details.**
89
+
90
+ ```powershell
91
+ uvx msteams-transcripts get 3 --details
92
+ ```
93
+
94
+ This writes `transcripts\2026-09-16 0902 - Architecture review storage tiering.txt`:
95
+
96
+ ```
97
+ # Architecture review: storage tiering
98
+ # Date: 2026-09-16 09:02
99
+ # Source: https://contoso-my.sharepoint.com/personal/.../Recordings/Architecture-review....mp4
100
+ # Organizer: Maya Okonkwo <maya.okonkwo@example.com>
101
+ # Location: Microsoft Teams Meeting
102
+ # Invited (13):
103
+ # Priya Raman <priya.raman@example.com> (Required, Accepted)
104
+ # Tomas Lindqvist <tomas.lindqvist@example.com> (Required, Tentative)
105
+ # ...
106
+ # Speakers in transcript (5):
107
+ # Priya Raman (84 segments)
108
+ # Maya Okonkwo (72 segments)
109
+ # ...
110
+ # Files shared in meeting chat (1):
111
+ # capacity-plan.xlsx https://contoso.sharepoint.com/sites/...
112
+ # Invitation text:
113
+ # Agenda: ...
114
+
115
+ [00:00:03] Priya Raman:
116
+ Good morning everyone, let's start with the architecture review.
117
+ ```
118
+
119
+ A sidecar `...meta.json` holds the same details as structured data: attendees
120
+ with response status, speakers with segment counts, shared files, invitation
121
+ text and the thread identifier.
122
+
123
+ **4. Download from a link instead.** Open the meeting in Teams, go to the Recap
124
+ tab, copy the address bar, and pass it:
125
+
126
+ ```powershell
127
+ uvx msteams-transcripts get "https://teams.cloud.microsoft/l/meetingrecap?driveId=...&driveItemId=...&sitePath=..." --details -f all
128
+ ```
129
+
130
+ This path does not touch the calendar and finishes in a few seconds.
131
+
132
+ **5. Archive a whole period.**
133
+
134
+ ```powershell
135
+ uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --details -o C:\Archive\teams
136
+ uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --only 3,7,12
137
+ ```
138
+
139
+ Files with the same name are overwritten. Batch ends with a count of what was
140
+ downloaded and what failed.
141
+
142
+ **6. Skipping the "Stay better connected with the Teams desktop app" popup**
143
+ when you open recap links yourself: change `/l/meetingrecap?` in the address to
144
+ `/_#/l/meetingrecap?`. That form opens the web client directly. The tool
145
+ already uses it internally.
146
+
147
+ ## Commands
148
+
149
+ ```
150
+ msteams-transcripts browser
151
+ msteams-transcripts list --from YYYY-MM-DD --to YYYY-MM-DD|today [--json]
152
+ msteams-transcripts get <row#|recap-url|thread-id> [-d] [-f txt|json|vtt|all] [-o DIR]
153
+ msteams-transcripts batch --from D --to D [-d] [-f FMT] [-o DIR] [--only 3,7,12]
154
+ ```
155
+
156
+ | Option | Meaning |
157
+ |---|---|
158
+ | `-d`, `--details` | organizer, location, invited attendees with response status, speakers, shared files, invitation text, plus a `.meta.json` sidecar |
159
+ | `-f`, `--format` | `txt` (default), `json` for the raw Stream document, `vtt` for WebVTT, or `all` |
160
+ | `-o`, `--out` | output directory, `./transcripts` by default |
161
+ | `--json` | on `list`, print the rows as JSON for scripting |
162
+ | `--only` | on `batch`, restrict to given row numbers |
163
+ | `--port`, `--profile`, `--browser` | override the debugging port, profile directory or browser executable |
164
+
165
+ The text format is:
166
+
167
+ ```
168
+ [00:00:03] Speaker Name:
169
+ what they said
170
+ the next sentence by the same speaker
171
+ ```
172
+
173
+ Timestamps are offsets from the start of the recording, not wall-clock time.
174
+ Row numbers used by `get N` come from the most recent `list`, cached in
175
+ `~/.teams_transcripts/last_list.json`.
176
+
177
+ ## Using it from an agent
178
+
179
+ `.claude/skills/teams-transcripts/SKILL.md` describes the tool for Claude Code
180
+ or another agent: preconditions, commands, output format, typical flows and
181
+ error handling. Claude Code picks it up automatically when working in this
182
+ project.
183
+
184
+ ## Library use
185
+
186
+ ```python
187
+ import asyncio
188
+ from pathlib import Path
189
+ from teams_transcripts import TeamsSession, download_ref, refs_from_recap_url
190
+
191
+ async def main():
192
+ async with TeamsSession() as s:
193
+ ref = refs_from_recap_url("https://teams.cloud.microsoft/l/meetingrecap?...")
194
+ await download_ref(s, ref, Path("out"), "txt", details=True)
195
+
196
+ asyncio.run(main())
197
+ ```
198
+
199
+ Anything the user can act on raises `TranscriptError`.
200
+
201
+ ## Environment variables
202
+
203
+ | Variable | Default | Purpose |
204
+ |---|---|---|
205
+ | `TT_CDP_PORT` | `9222` | browser debugging port |
206
+ | `TT_PROFILE_DIR` | platform application-data directory | browser profile |
207
+ | `TT_BROWSER_EXE` | Edge, then Chrome, from the usual locations | browser executable |
208
+ | `TT_STATE_DIR` | `~/.teams_transcripts` | where the last listing is cached |
209
+
210
+ ## Troubleshooting
211
+
212
+ - **"The browser did not expose its debugging port"**: another window is
213
+ already using the dedicated profile without the port. Close it, then run
214
+ `browser` again.
215
+ - **"Could not capture a Teams token"**: the Teams tab in that window is not
216
+ signed in. Sign in there, then retry.
217
+ - **"Could not capture the meeting-content token"**: open any meeting's Recap
218
+ tab in that window, then retry.
219
+ - **HTTP 403 when listing or downloading**: the recording belongs to a tenant
220
+ or site you cannot read. The Recap tab would be empty for you too.
221
+ - **Not available through this tool**: the attendance report of who actually
222
+ joined and when, collaborative Loop notes, and Copilot AI notes. `--details`
223
+ gives the invited list from the calendar and who actually spoke per the
224
+ transcript.
225
+ - **Recurring meetings** share one thread identifier. Each occurrence's
226
+ transcript is listed separately with its own start time.
227
+
228
+ ## Development
229
+
230
+ ```powershell
231
+ git clone https://github.com/divyavanmahajan/msteams-transcripts
232
+ cd msteams-transcripts
233
+ pip install -e ".[dev]"
234
+ python -m pytest
235
+ ```
236
+
237
+ The tests cover the pure functions in `model.py` and `formats.py` and need
238
+ neither a browser nor the network.
239
+
240
+ Hatchling owns the version, read from `__version__` in
241
+ `src/teams_transcripts/__init__.py`. To release:
242
+
243
+ ```powershell
244
+ hatch version patch # or minor, major, or an explicit 1.2.3
245
+ git commit -am "Release 0.1.1"
246
+ git tag v0.1.1
247
+ git push --follow-tags
248
+ ```
249
+
250
+ Pushing the tag runs `.github/workflows/publish.yml`, which refuses to continue
251
+ if the tag and the package version disagree, then builds, publishes to PyPI
252
+ through trusted publishing and creates a GitHub release. Trusted publishing is
253
+ configured once on PyPI under Publishing, naming this repository, the
254
+ `publish.yml` workflow and the `pypi` environment; no API token is stored.
255
+ Running the workflow by hand offers TestPyPI as the target.
256
+
257
+ ## Licence
258
+
259
+ MIT. Transcripts are meeting content belonging to the people in them; this tool
260
+ only fetches what you already have access to, and what you do with it afterwards
261
+ is your responsibility.
@@ -0,0 +1,54 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "msteams-transcripts"
7
+ dynamic = ["version"]
8
+ description = "List and download Microsoft Teams meeting transcripts from the command line, through your own signed-in browser session."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "Divya Van Mahajan", email = "divyavanmahajan@users.noreply.github.com" }]
13
+ keywords = ["microsoft-teams", "transcript", "meeting", "recap", "sharepoint", "cli"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Environment :: Console",
17
+ "Intended Audience :: End Users/Desktop",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: Microsoft :: Windows",
20
+ "Operating System :: MacOS",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Communications :: Conferencing",
28
+ "Topic :: Utilities",
29
+ ]
30
+ dependencies = ["playwright>=1.40"]
31
+
32
+ [project.optional-dependencies]
33
+ dev = ["pytest>=7", "build>=1.0", "twine>=5.0", "hatch>=1.9"]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/divyavanmahajan/msteams-transcripts"
37
+ Repository = "https://github.com/divyavanmahajan/msteams-transcripts"
38
+ Issues = "https://github.com/divyavanmahajan/msteams-transcripts/issues"
39
+
40
+ [project.scripts]
41
+ msteams-transcripts = "teams_transcripts.cli:main"
42
+ teams-transcripts = "teams_transcripts.cli:main"
43
+
44
+ [tool.hatch.version]
45
+ path = "src/teams_transcripts/__init__.py"
46
+
47
+ [tool.hatch.build.targets.wheel]
48
+ packages = ["src/teams_transcripts"]
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = ["/src", "/tests", "/README.md", "/LICENSE"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]