destreamer icon indicating copy to clipboard operation
destreamer copied to clipboard

Microsoft teams?

Open alessandrotodisco opened this issue 4 years ago • 19 comments

Are you going to implement in the near future the option to download videos from Microsoft Teams too? (Is it somewhat possible?) Thanks in advance!

alessandrotodisco avatar May 15 '20 15:05 alessandrotodisco

Aren't those recordings already on MS Stream? image

snobu avatar May 15 '20 20:05 snobu

Aren't those recordings already on MS Stream? image

No, it's actually not. Some videos can't be played in Microsoft Stream. I've tried a couple of times before, no success. I only have the Microsoft Team Link to the video, and even changing it to a Microsoft Stream URL (with the ID) won't let me download the video aka gives me a 404 error.

How can I download videos from Microsoft Teams? (if this is already possible)

teddylandclub avatar May 15 '20 22:05 teddylandclub

Exactly, not all videos are available in Microsoft Stream. https://teams.microsoft.com/l/meetup-join/19%3Ameeting_YWJiZGY1ODYtNDE1Ny00MDM5LWJmMTYtNDA3Y2FiZTgxYTMz%40thread.v2/0?context=%7B%22Tid%22%3A%224f0132f7-dd79-424c-9089-b22764c40ebd%22%2C%22Oid%22%3A%2264979a43-443e-4edc-87b2-74dd8ff11f2d%22%2C%22IsBroadcastMeeting%22%3Atrue%7D This video is an example.

alessandrotodisco avatar May 16 '20 07:05 alessandrotodisco

Sorry, i don't know how that works, could be MS Teams hosting property. This is out of scope for destreamer.

snobu avatar May 16 '20 16:05 snobu

@alessandrotodisco Did you by any chance find a solution on how to download those kinds of videos? I'm having the same problem now and I can't figure anything out other than using a screen recorder :/

OlgabrezelPrivate avatar Aug 12 '20 18:08 OlgabrezelPrivate

@Olgabrezel Unfortunately no. The only boring solution I found is by using a screen recorder..

alessandrotodisco avatar Aug 13 '20 09:08 alessandrotodisco

@Olgabrezel @alessandrotodisco I'm sorry that I can't help you but I have no such video at my disposal to do some reverse engineering and tell you if and how to download them :/

lukaarma avatar Aug 13 '20 10:08 lukaarma

@lukaarma It would be so awesome if you could try that! For such a video - the link that alessandro posted above works for me. I just logged in on Microsoft and chose to participate anonymously and the video loaded. So perhaps you could use that for testing?

Edit: I just tried again and for that video it seems logging in is not even required. In my case, however, it is only available for members of my organization after a login.

OlgabrezelPrivate avatar Aug 14 '20 13:08 OlgabrezelPrivate

@Olgabrezel @alessandrotodisco did the deed, I reversed the platform logic and lo and behold I have the url of the video and the auth token

BUT

the video is streamed using SmoothStreamingMedia, proprietary format of Microsoft (@snobu I know you said it was out of scope, and I wont implement this in destreamer, but if you have any idea how to do this without a custom program please let me know if you have time) I have to do some more digging to find a good and working downloader for that but in the meantime I leave these here

SOURCE => videojs.getPlayers().teamsDefaultPlayer.options_.sourceList.slice(-1).pop().src

AUTH => document.cookie.match(/TSAUTHCOOKIE=(.*?);/)[1]

leave me more time to find a suitable downloader, or in the worst case scenario I have to write something custom for you but that would require a lot of time

lukaarma avatar Aug 14 '20 14:08 lukaarma

FFmpeg may be able to download SmoothStreaming as is. If it's not i believe we can form an HLS URL out of it, i highly doubt they only publish a SS stream. Can you post the manifest URL (video GUID redacted) so i can have a look?

snobu avatar Aug 15 '20 17:08 snobu

@snobu

Ffmpeg doesn't parse SS playlists

I tried getting the hls as we do in Destreamer with (format= hls.codeword.thatIdontRemember) at the end of the URL instead of (format=SS.thatIdontRemember) but no dice the server doesn't have the encoding

As soon as I'm on my pc I'll send you an email with links and the SS playlist I was able to download

As per converting to HLS it should be quite easy with a smal script so I'll look into that

lukaarma avatar Aug 15 '20 17:08 lukaarma

Keeping this open so that maybe we don't get duplicates

lukaarma avatar Oct 09 '20 11:10 lukaarma

@snobu You said that this is out of scope for Destreamer, so if Iwanted to implement this I should do it in another tool? Or can I try and implement it here?

lukaarma avatar Feb 03 '21 17:02 lukaarma

@lukaarma I would never say no to a PR if you implement that. However i would probably wait until we learn more about the SharePoint/OneDrive MSStream vNext thing, since it may affect how Teams saves recordings as well. I know how the house operates :)

snobu avatar Feb 04 '21 15:02 snobu

@lukaarma Have you abandoned this idea?

Franek-Antoniak avatar Mar 11 '23 10:03 Franek-Antoniak

Responding to the musings above about someone muddling through this process by hand and describing what they found:

I played one of these public Microsoft-Teams-hosted videos in a web browser while capturing packets and eventually figured out how to get a plain .mp4 of the stream contents (no screen-recording or re-encoding). Steps:

  1. The root of most of this is the 'manifest' request. It had a URL like https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/manifest(format=mpd-time-csf)
  2. Per Den Delimarsky's advice, you can change the format=mpd-time-csf part to format=m3u8-aapl-v3 to get the same stream metadata in HLS/m3u8 format instead of the MPEG-DASH XML format. I don't think this is necessary, but this is the path I took, so I mention it here in case it was important. ffmpeg supports both formats. This change also had the effect of giving me one mp4 stream with both audio and video instead of separate audio and video streams.
  3. The top-level manifest offers multiple URLs for different quality levels. They're something like https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/QualityLevels(128000)/Manifest(audio,format=m3u8-aapl-v3) and they're full of entries like Fragments(audio=12345678999,format=m3u8-aapl-v3). Here's where the problems start:
    1. Problem: Tools that read this fragment-list combine the fragment names and the base URL in a different way than the server expects. ffmpeg looks for the fragments at the URL
      • https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/QualityLevels(128000)/Manifest(audio,format=m3u8-aapl-v3)/Fragments(audio=12345678999,format=m3u8-aapl-v3) but the server serves the file at the URL
      • https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/QualityLevels(128000)/Fragments(audio=12345678999,format=m3u8-aapl-v3) Note the missing Manifest(...) penultimate path component.
    2. Problem: The line breaks in the fragments list are \r\n and sometimes ffmpeg breaks lines by \n, leaving the \r on the end of the URL, which the server rejects as 400.
    3. Problem: The server responses with the fragments of video data have a content-type: video/mp2t (or audio/mp4 and video/mp4 for the mpd-time-csf/MPEG-DASH streams) header and no Content-Encoding header. But the contents are actually AES-128 encrypted. ffmpeg attempts to support this! Invoking ffmpeg on the manifest URL directly results in it muttering about crypto+https://... URLs, so it knows it needs to decrypt something, but it isn't able to find the key. Even after I separately extracted the key and was able to provide it to ffmpeg directly on the command line, I was unable to get ffmpeg to play the encrypted fragments, sometimes with silly results like ffmpeg trying to aes-decrypt the text file that lists the fragment names and then failing to parse it.
  4. Anyway, I just fetched and then concatenated all the Fragments. (Concatenate first. Concatenate-then-decrypt worked. Decrypt-then-concatenate did not work.)
  5. From the packet capture, I observe that the browser got the decryption key from a no-body POST to https://blah.keydelivery.blah.media.azure.net/?KID=bbbbbbbb-cccc-dddd-eeee-ffffffffffff, which returns a 16-byte body that is the decryption key. This link is in the MPEG-DASH manifest as keyUriTemplate. The browser sends a HMACSHA256 Bearer Authorization header, and I get 401 if I don't send one.
    • It looks like this token comes from an earlier request to https://prd.attend.teams.microsoft.com/teams/66666666-7777-8888-9999-aaaaaaaaaaaa/bbbbbbbb-cccc-dddd-eeee-ffffffffffff/19:meeting_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxxxxx@thread.v2/0?role=Attendee, and all those fields were in the stream-viewing/'meeting-invite' link.
  6. The MPEG-DASH manifest also has the decryption IV.
  7. With the key, iv, and concatenated fragments, I can now decrypt the stream: openssl enc -d -aes128 -iv 00000000111111112222222233333333 -K 44444444555555556666666677777777 -in allthefragments -out stream.mp4

Again, these are public videos—nothing here bypasses any access controls. These are just the details of how Microsoft Teams implements public videos: By serving them encrypted and then offering the key to anyone that asks.

chkno avatar Dec 08 '23 00:12 chkno

Responding to the musings above about someone muddling through this process by hand and describing what they found:

I played one of these public Microsoft-Teams-hosted videos in a web browser while capturing packets and eventually figured out how to get a plain .mp4 of the stream contents (no screen-recording or re-encoding). Steps:

  1. The root of most of this is the 'manifest' request. It had a URL like https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/manifest(format=mpd-time-csf)

  2. Per Den Delimarsky's advice, you can change the format=mpd-time-csf part to format=m3u8-aapl-v3 to get the same stream metadata in HLS/m3u8 format instead of the MPEG-DASH XML format. I don't think this is necessary, but this is the path I took, so I mention it here in case it was important. ffmpeg supports both formats. This change also had the effect of giving me one mp4 stream with both audio and video instead of separate audio and video streams.

  3. The top-level manifest offers multiple URLs for different quality levels. They're something like https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/QualityLevels(128000)/Manifest(audio,format=m3u8-aapl-v3) and they're full of entries like Fragments(audio=12345678999,format=m3u8-aapl-v3). Here's where the problems start:

    1. Problem: Tools that read this fragment-list combine the fragment names and the base URL in a different way than the server expects. ffmpeg looks for the fragments at the URL

      • https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/QualityLevels(128000)/Manifest(audio,format=m3u8-aapl-v3)/Fragments(audio=12345678999,format=m3u8-aapl-v3) but the server serves the file at the URL
      • https://endpoint1-blah.blah.blah.blah.office.net/11111111-2222-3333-4444-555555555555/66666666-7777-8888-9999-aaaaaaaaaaaa.ism/QualityLevels(128000)/Fragments(audio=12345678999,format=m3u8-aapl-v3) Note the missing Manifest(...) penultimate path component.
    2. Problem: The line breaks in the fragments list are \r\n and sometimes ffmpeg breaks lines by \n, leaving the \r on the end of the URL, which the server rejects as 400.

    3. Problem: The server responses with the fragments of video data have a content-type: video/mp2t (or audio/mp4 and video/mp4 for the mpd-time-csf/MPEG-DASH streams) header and no Content-Encoding header. But the contents are actually AES-128 encrypted. ffmpeg attempts to support this! Invoking ffmpeg on the manifest URL directly results in it muttering about crypto+https://... URLs, so it knows it needs to decrypt something, but it isn't able to find the key. Even after I separately extracted the key and was able to provide it to ffmpeg directly on the command line, I was unable to get ffmpeg to play the encrypted fragments, sometimes with silly results like ffmpeg trying to aes-decrypt the text file that lists the fragment names and then failing to parse it.

  4. Anyway, I just fetched and then concatenated all the Fragments. (Concatenate first. Concatenate-then-decrypt worked. Decrypt-then-concatenate did not work.)

  5. From the packet capture, I observe that the browser got the decryption key from a no-body POST to https://blah.keydelivery.blah.media.azure.net/?KID=bbbbbbbb-cccc-dddd-eeee-ffffffffffff, which returns a 16-byte body that is the decryption key. This link is in the MPEG-DASH manifest as keyUriTemplate. The browser sends a HMACSHA256 Bearer Authorization header, and I get 401 if I don't send one.

    • It looks like this token comes from an earlier request to https://prd.attend.teams.microsoft.com/teams/66666666-7777-8888-9999-aaaaaaaaaaaa/bbbbbbbb-cccc-dddd-eeee-ffffffffffff/19:meeting_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxxxxx@thread.v2/0?role=Attendee, and all those fields were in the stream-viewing/'meeting-invite' link.
  6. The MPEG-DASH manifest also has the decryption IV.

  7. With the key, iv, and concatenated fragments, I can now decrypt the stream: openssl enc -d -aes128 -iv 00000000111111112222222233333333 -K 44444444555555556666666677777777 -in allthefragments -out stream.mp4

Again, these are public videos—nothing here bypasses any access controls. These are just the details of how Microsoft Teams implements public videos: By serving them encrypted and then offering the key to anyone that asks.

Could you please elaborate what you did from step 3 and forward? I have the KID, Bearer, Issuer, HMACSHA256, response from the keydelivery and I think all the components but I can't get it to work; how do I concatenate the streams for example? ffmpeg refuses to do anything with the crypto+https-URLs and Google is worthless when trying to find anything with a plus sign :/

zejjnt avatar Feb 21 '24 18:02 zejjnt

how do I concatenate the streams for example?

cat * > allthefragments

chkno avatar Feb 25 '24 00:02 chkno

@chkno thanks, what worked for me is changing the format of manifest, then i downloaded encrypted parts via yt-dlp and aria2c, then used openssl with AES and IV on each encrypted part, then used ffmpeg to combine decrypted parts them into one file

I was trying to automate it, but I couldn't figure out the way how to obtain the HMACSHA256 in automated way to be used for auth... any ideas?

elkolorado avatar Feb 29 '24 15:02 elkolorado