OneNote.ps1

# Speaks out the given text.
# Feb 22nd 2022
function Start-Speech
{
<#
    .SYNOPSIS
    Gets mp3 stream of the given text and plays it with Media player.
 
    .DESCRIPTION
    Gets mp3 stream of the given text using learning tools API and plays it with Media player.
 
    .Parameter AccessToken
    The access token used to get the speech.
 
    .Parameter Language
    The language code. Defaults to "en-US"
 
    .Parameter PreferredVoice
    Male or Female voice, defaults to Female.
 
    .Parameter Text
    The text to speak.
 
    .Example
    PS C:\>Get-AADIntAccessTokenForOneNote -SaveToCache
    PS C:\>Start-AADIntSpeech -Text "Three Swedish switched witches watch three Swiss Swatch watch switches. Which Swedish switched witch watch which Swiss Swatch watch switch?" -Language "en-GB" -PreferredVoice Male
 
    .Example
    PS C:\>Get-AADIntAccessTokenForOneNote -SaveToCache
    PS C:\>Start-AADIntSpeech -Text "Mustan kissan paksut posket" -PreferredVoice Female -Language fi-FI
 
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$Text,
        [Parameter(Mandatory=$False)]
        [String]$Language = "en-US",
        [Parameter(Mandatory=$False)]
        [ValidateSet("Female","Male")]
        [String]$PreferredVoice = "Female"
    )
    Process
    {
        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -Resource "https://onenote.com" -ClientId "1fec8e78-bce4-4aaf-ab1b-5451cc387264"

        # Construct the body
        $body = @{
            "data" = [ordered]@{
                "title" = "The King's Speech"
                "chunks" = @(
                    [ordered]@{
                        "content"  = $Text
                        "mimeType" = "text/plain"
                    }
                )
                "startingChunkIndex" = 0
                "startingCharIndex"  = 0
            }
        }

        # Set the headers
        $headers=@{
            "Authorization" = "Bearer $AccessToken"
            "Accept-Language" = $Language
            "MS-Int-AppId" = "Teams"
        }

        # Invoke the command
        $contentModel = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://learningtools.onenote.com/learningtoolsapi/v2.0/getcontentmodelforreader" -Headers $headers -Body ($body | ConvertTo-Json -Depth 5) -ContentType "application/json; charset=utf-8"

        # Construct the body
        $body = [ordered]@{
            "data" = [ordered]@{
                "sentenceModels" = @(
                    [ordered]@{
                        "t"  = $Text
                        "lang" = $Language
                        "se" = $contentModel.data[0].formatting.b.r[0].i[0]
                        "wo" = @()
                    }
                )
            }
            "options" = [ordered]@{
                "preferredVoice" = $PreferredVoice
                "extractWordMarkers" = $True
                "encoding" = "Wav"
                "clientLabel" = "ReadAloudFirstPrefetch"
                "useBrowserSpecifiedDialect" = $True
            }
        }

        # Set the headers
        $headers=@{
            "Authorization" = "MS-SessionToken $($contentModel.meta.sessionToken)"
            "X-UserSessionId" = $contentModel.meta.sessionId
            "Accept-Language" = $Language
            "MS-Int-AppId" = "Teams"
        }

        # Invoke the command
        $response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://learningtools.onenote.com/learningtoolsapi/v2.0/GetSpeech" -Headers $headers -Body ($body | ConvertTo-Json -Depth 5) -ContentType "application/json"

        $mp3B64 = $response.data.sb[0].ad.Split(",")[1]

        # Create a temporary file
        $tmp = New-TemporaryFile
        Rename-Item -Path $tmp.FullName -NewName ($tmp.Name+".mp3")
        $mp3 = ($tmp.FullName+".mp3")
        
        try
        {
            [byte[]](Convert-B64ToByteArray -B64 $mp3B64) | Set-Content $mp3 -Encoding byte

            $player = [System.Windows.Media.MediaPlayer]::new()
            $player.Open($mp3)
            # Pause for a while to populate the duration
            Start-Sleep -Milliseconds 100
            $player.Play()

            # Wait till completed
            while($player.Position -lt $player.NaturalDuration.TimeSpan)
            {
                Start-Sleep -Milliseconds 10
            }
            $player.Close()
        }
        catch
        {
        }
        finally
        {
            # Remove the temp file
            Remove-Item $mp3
        }
    }
}