Export-OneNote.ps1


<#PSScriptInfo

.VERSION 1.3.0

.GUID b9742b5d-5b71-4a08-bbfe-635827e34076

.AUTHOR Jan-Hendrik Peters

.COMPANYNAME Shiftavenue GmbH

.COPYRIGHT Jan-Hendrik Peters, 2023

.TAGS OneNote Markdown Graph

.LICENSEURI https://raw.githubusercontent.com/nyanhp/freeing-onenote/main/LICENSE

.PROJECTURI https://github.com/nyanhp/freeing-onenote

.ICONURI

.EXTERNALMODULEDEPENDENCIES MiniGraph,MarkdownPrince

.REQUIREDSCRIPTS

.EXTERNALSCRIPTDEPENDENCIES

.RELEASENOTES


.PRIVATEDATA

#>
 

#Requires -Module MiniGraph
#Requires -Module MarkdownPrince


<#
.SYNOPSIS
 This script exports one or more OneNote notebooks to markdown

.DESCRIPTION
 This script exports one or more OneNote notebooks to markdown
 using the Graph API and an Entra App Registration.

 The script requires the following modules to be installed:
    - MiniGraph
    - MarkdownPrince

.PARAMETER TenantId
    The tenant id to use for the Graph API. Defaults to 'Common'.

.PARAMETER OneNoteAppClientId
    The client id of the app registration to use for the Graph API. Defaults to '812899b7-584c-4812-8aee-11d3e164d58b', which is managed by the author.

.PARAMETER User
    The user to use for the Graph API. Defaults to 'me'. Use string like user/GUID to request notebooks from other users if the permissions are set correctly.

.PARAMETER Notebook
    The name of the notebook to export. Can be specified multiple times. If not specified, all notebooks will be exported.

.PARAMETER All
    If specified, all notebooks will be exported.

.PARAMETER Path
    The path to export the notebooks to. If the path does not exist, it will be created.

.EXAMPLE
    ./Export-OneNote.ps1 -Notebook 'My Notebook' -Path "$home/freedomfromproprietaryformats"

#>


[CmdletBinding(DefaultParameterSetName = 'Notebook')]
param
(
    [Parameter(ParameterSetName = 'Notebook')]
    [Parameter(ParameterSetName = 'All')]
    [string]
    $TenantId = 'Common',

    # Use mine or create your own
    [Parameter(ParameterSetName = 'Notebook')]
    [Parameter(ParameterSetName = 'All')]
    [string]
    $OneNoteAppClientId = '812899b7-584c-4812-8aee-11d3e164d58b',

    [string]
    $User = 'me',

    [Parameter(Mandatory = $true, ParameterSetName = 'Notebook')]
    [string[]]
    $Notebook,

    [Parameter(Mandatory = $true, ParameterSetName = 'All')]
    [switch]
    $All,

    [Parameter(Mandatory = $true, ParameterSetName = 'Notebook')]
    [Parameter(Mandatory = $true, ParameterSetName = 'All')]
    $Path
)


Connect-GraphDeviceCode -TenantId $TenantId -ClientId $OneNoteAppClientId
Set-GraphEndpoint -Type beta

$notebooks = if ($All.IsPresent)
{
    Invoke-GraphRequest -Query "$($User)/onenote/notebooks"
}
else
{
    $Notebook | ForEach-Object {
        Invoke-GraphRequest -Query "$($User)/onenote/notebooks?`$filter=displayName eq '$($_ -replace "'", "''")'"
    }
}

if (-not (Test-Path $Path))
{
    $null = New-Item -Path $Path -ItemType Directory -Force
}

$mg = Get-Module -Name MiniGraph
$token = & $mg { $script:token }

Write-Verbose -Message "Exporting $($notebooks.count) notebooks to $Path"

foreach ($book in $notebooks)
{
    Write-Verbose -Message "Exporting notebook $($book.displayName)"
    $bookPath = Join-Path -Path $Path -ChildPath $book.displayName
    $sections = Invoke-GraphRequest -Query "$($User)/onenote/notebooks/$($book.id)/sections"
    if (-not (Test-Path -Path $bookPath))
    {
        $null = New-Item -Path $bookPath -ItemType Directory -Force
    }

    foreach ($section in $sections)
    {
        Write-Verbose -Message "Exporting section $($section.displayName)"
        $sectionPath = Join-Path -Path $bookPath -ChildPath $section.displayName
        if (-not (Test-Path -Path $sectionPath))
        {
            $null = New-Item -Path $sectionPath -ItemType Directory -Force
        }
        $pages = Invoke-GraphRequest -Query "$($User)/onenote/sections/$($section.id)/pages"

        foreach ($page in $pages)
        {
            Write-Verbose -Message "Exporting page $($page.title)"
            $sanitizedTitle = $page.title -replace '[\\\/\:\*\?\"\<\>\|]', '_'
            $pagePath = Join-Path -Path $sectionPath -ChildPath "$($page.createdDateTime.ToString('yyyy-MM-dd'))_$($sanitizedTitle).md"
            $content = Invoke-GraphRequest -Query "$($User)/onenote/pages/$($page.id)/content"
            $imgCount = 0
            foreach ($image in $content.SelectNodes("//img"))
            {
                $header = @{
                    Authorization = "Bearer $token"
                }
                $imgName = '{0}_{1:d10}.png' -f $sanitizedTitle, $imgCount
                $imgPath = Join-Path -Path $sectionPath -ChildPath resources
                if (-not (Test-Path -Path $imgPath))
                {
                    $null = New-Item -Path $imgPath -ItemType Directory -Force
                }

                Invoke-RestMethod -Method Get -Uri $image.'data-fullres-src' -Headers $header -OutFile (Join-Path $imgPath $imgName)

                $image.src = [uri]::EscapeUriString(('./resources/{1}' -f $section.displayName, $imgName))
                $imgCount++
            }

            $content.OuterXml | ConvertFrom-HTMLToMarkdown -DestinationPath $pagePath -Format
        }
    }
}