Skip to content

Instantly share code, notes, and snippets.

@spajak
Last active April 30, 2023 19:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spajak/a6699005d9648696fbdda1d545153a38 to your computer and use it in GitHub Desktop.
Save spajak/a6699005d9648696fbdda1d545153a38 to your computer and use it in GitHub Desktop.
Creates EPUB e-book package from a given directory containing ebook source files.
[CmdletBinding(DefaultParameterSetName = "Default")]
param(
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
[Alias("i", "Path", "Source")]
[String] $SourceDirectory,
[Parameter(Mandatory=$true, ParameterSetName="DestinationAlongSource")]
[Alias("ds", "os")]
[Switch] $DestinationAlongSource,
[Parameter(Mandatory=$true, ParameterSetName="DestinationDirectory")]
[Alias("dd", "od")]
[String] $DestinationDirectory,
[Parameter(Position=1, Mandatory=$true, ParameterSetName="DestinationFile")]
[Alias("o", "df", "of", "Destination", "File")]
[String] $DestinationFile,
[Parameter(Mandatory=$false, ParameterSetName="DestinationFile")]
[ValidateSet("Custom", "Append", "Correct")]
[Alias("e")]
[String] $Extension = "Append",
[Parameter(Mandatory=$false)]
[Switch] $Force
)
function Main
{
$parameterSet = $PSCmdlet.ParameterSetName
Write-Verbose "ParameterSetName: $parameterSet"
$SourceDirectory = (Resolve-Path $SourceDirectory -ErrorAction Stop).Path
$DestinationFile = Get-DestinationFilePath
Write-Verbose "SourceDirectory: $SourceDirectory"
Write-Verbose "DestinationFile: $DestinationFile"
Build-Epub $SourceDirectory $DestinationFile
Show-EpubFileSummary $DestinationFile
}
function Get-DestinationFilePath
{
if (($parameterSet -eq "DestinationFile") -and $DestinationFile) {
if (($Extension -ne "Custom") -and ($DestinationFile -notlike "*.epub")) {
if ($Extension -eq "Append") {
$DestinationFile = $DestinationFile + ".epub"
} else {
$directory = Split-Path $DestinationFile -Parent
$name = (Split-Path $DestinationFile -LeafBase) + ".epub"
if ($directory) {
$DestinationFile = Join-Path $directory $name
} else {
$DestinationFile = $name
}
}
}
} else {
$DestinationFile = (Split-Path $SourceDirectory -Leaf) + ".epub"
if (($parameterSet -eq "DestinationDirectory") -and $DestinationDirectory) {
$DestinationFile = Join-Path $DestinationDirectory $DestinationFile -ErrorAction Stop
} elseif ($DestinationAlongSource.IsPresent) {
$DestinationFile = Join-Path (Split-Path $SourceDirectory -Parent) $DestinationFile
}
}
if (!(Test-Path -LiteralPath $DestinationFile -IsValid)) {
throw "Destination file `"$DestinationFile`" is not valid."
}
if (-not $Force.IsPresent -and (Test-Path -LiteralPath $DestinationFile -PathType Leaf)) {
throw "Destination file `"$DestinationFile`" exists. Use -Force to overwrite."
}
if (Test-Path -LiteralPath $DestinationFile -PathType Container) {
throw "Destination file `"$DestinationFile`" already exists and it is a directory."
}
$directory = Split-Path $DestinationFile -Parent
if ($directory -and -not (Test-Path $directory -PathType Container)) {
throw "Destination directory `"$directory`" does not exist."
}
return $DestinationFile
}
$epubMimetypeFile = "mimetype"
$epubMimetypeName = "application/epub+zip"
$epubContainerFile = Join-Path "META-INF" "container.xml"
$systemTempPath = [System.IO.Path]::GetTempPath()
function Assert-EpubSource([String] $inputDirPath)
{
if (!(Test-Path -LiteralPath $inputDirPath -PathType Container)) {
throw "Path `"${inputDirPath}`" is not a directory."
}
$containerPath = Join-Path $inputDirPath $epubContainerFile
if (!(Test-Path -LiteralPath $containerPath -PathType Leaf)) {
throw "File `"${epubContainerFile}`" not found. Not an e-book?"
}
$mimeTypePath = Join-Path $inputDirPath $epubMimetypeFile
if (Test-Path -LiteralPath $mimeTypePath -PathType Leaf) {
$content = (Get-Content -LiteralPath $mimeTypePath).Trim()
if ($content -ne $epubMimetypeName) {
throw "MIME type `"$content`" is not a valid type for epub (should be `"$epubMimetypeName`")"
}
}
}
function Get-EpubTempMimetype
{
$tempPath = Join-Path $systemTempPath "epub"
if (!(Test-Path -LiteralPath $tempPath -PathType Container)) {
New-Item -LiteralPath $tempPath -ItemType Directory
}
$mimetypeTempFile = Join-Path $tempPath $epubMimetypeFile
Set-Content -LiteralPath $mimetypeTempFile -Value $epubMimetypeName -NoNewline
return $mimetypeTempFile
}
function Show-EpubFileSummary([String] $outputFile)
{
$size = (Get-ItemPropertyValue -LiteralPath $outputFile -Name Length).ToString('N0')
Write-Output "File: $outputFile"
Write-Output "Size: ${size} bytes"
}
function Build-Epub([String] $inputDirPath, [String] $outputFile = $null)
{
$inputDirPath = (Resolve-Path -LiteralPath $inputDirPath -ErrorAction Stop).Path
Assert-EpubSource $inputDirPath
if (!$outputFile) {
$name = (Split-Path $inputDirPath -Leaf) + ".epub"
$outputFile = Join-Path (Split-Path $inputDirPath -Parent) $name
} elseif ($outputFile -notmatch ".+\.(epub|zip)$") {
$outputFile = $outputFile + ".epub"
}
$mimeTypePath = Join-Path $inputDirPath $epubMimetypeFile
if (!(Test-Path -LiteralPath $mimeTypePath -PathType Leaf)) {
$mimeTypePath = Get-EpubTempMimetype
}
$items = Get-ChildItem -LiteralPath $inputDirPath
$items = $items.Where({$_.Name -ne $epubMimetypeFile}).ForEach({$_.FullName})
Remove-Item -LiteralPath $outputFile -ErrorAction Ignore
Compress-Archive -LiteralPath $mimeTypePath $outputFile -CompressionLevel NoCompression
Compress-Archive -LiteralPath $items $outputFile -Update -CompressionLevel Fastest -ErrorAction Ignore
}
Main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment