Allow custom thumbnail paths in metadata

This commit is contained in:
Christoph Hagen 2022-09-29 21:23:41 +02:00
parent 9d2f1e4c90
commit c82080db82
7 changed files with 97 additions and 60 deletions

View File

@ -233,27 +233,3 @@ extension Element.LocalizedMetadata {
}
}
}
// MARK: Thumbnails
extension Element {
static let defaultThumbnailName = "thumbnail.jpg"
static func localizedThumbnailName(for language: String) -> String {
"thumbnail-\(language).jpg"
}
static func findThumbnail(for language: String, in folder: URL) -> String? {
let localizedThumbnail = localizedThumbnailName(for: language)
let localizedThumbnailUrl = folder.appendingPathComponent(localizedThumbnail)
if localizedThumbnailUrl.exists {
return localizedThumbnail
}
let defaultThumbnailUrl = folder.appendingPathComponent(defaultThumbnailName)
if defaultThumbnailUrl.exists {
return defaultThumbnailName
}
return nil
}
}

View File

@ -85,6 +85,15 @@ struct Element {
*/
let images: [ManualImage]
/**
The path to the thumbnail file.
This property is optional, and defaults to ``GenericMetadata.defaultThumbnailName``.
Note: The generator first looks for localized versions of the thumbnail by appending `-[lang]` to the file name,
e.g. `customThumb-en.jpg`. If no file is found, then the specified file is tried.
*/
let thumbnailPath: String
/**
The style of thumbnail to use when generating overviews.
@ -168,6 +177,7 @@ struct Element {
self.externalFiles = metadata.externalFiles ?? []
self.requiredFiles = metadata.requiredFiles ?? [] // Paths are already relative to root
self.images = metadata.images?.compactMap { ManualImage(input: $0, path: "") } ?? []
self.thumbnailPath = metadata.thumbnailPath ?? Element.defaultThumbnailName
self.thumbnailStyle = log.unused(metadata.thumbnailStyle, "thumbnailStyle", source: source) ?? .large
self.useManualSorting = log.unused(metadata.useManualSorting, "useManualSorting", source: source) ?? true
self.overviewItemCount = metadata.overviewItemCount ?? Element.overviewItemCountDefault
@ -237,6 +247,7 @@ struct Element {
self.externalFiles = Element.rootPaths(for: metadata.externalFiles, path: path)
self.requiredFiles = Element.rootPaths(for: metadata.requiredFiles, path: path)
self.images = metadata.images?.compactMap { ManualImage(input: $0, path: path) } ?? []
self.thumbnailPath = metadata.thumbnailPath ?? Element.defaultThumbnailName
self.thumbnailStyle = log.thumbnailStyle(metadata.thumbnailStyle, source: source)
self.useManualSorting = metadata.useManualSorting ?? false
self.overviewItemCount = metadata.overviewItemCount ?? parent.overviewItemCount
@ -420,17 +431,6 @@ extension Element {
extension Element {
/**
Get the full path of the thumbnail image for the language (relative to the root folder).
*/
func thumbnailFilePath(for language: String) -> String {
guard let thumbnailFile = Element.findThumbnail(for: language, in: inputFolder) else {
log.add(error: "Missing thumbnail", source: path)
return Element.defaultThumbnailName
}
return pathRelativeToRootForContainedInputFile(thumbnailFile)
}
/**
The full url (relative to root) for the localized page
- Parameter language: The language of the page where the url should point
@ -497,7 +497,7 @@ extension Element {
}
func linkPreviewImage(for language: String) -> String? {
localized(for: language).linkPreviewImage
localized(for: language).linkPreviewImage ?? thumbnailFileName(for: language)
}
}
@ -663,3 +663,57 @@ extension Element {
return node
}
}
// MARK: Thumbnails
extension Element {
static let defaultThumbnailName = "thumbnail.jpg"
/**
Find the thumbnail for the element.
This function uses either the custom thumbnail path from the metadata or the default name
to find a thumbnail. It first checks if a localized version of the thumbnail exists, or returns the
generic version. If no thumbnail image could be found on disk, then an error is logged and the
generic path is returned.
- Parameter language: The language of the thumbnail
- Returns: The thumbnail (either the localized or the generic version)
*/
func thumbnailFilePath(for language: String) -> (source: String, destination: String) {
let localizedThumbnail = thumbnailPath.insert("-\(language)", beforeLast: ".")
let localizedThumbnailUrl = inputFolder.appendingPathComponent(localizedThumbnail)
if localizedThumbnailUrl.exists {
let source = pathRelativeToRootForContainedInputFile(localizedThumbnail)
let ext = thumbnailPath.lastComponentAfter(".")
let destination = pathRelativeToRootForContainedInputFile("thumbnail-\(language).\(ext)")
return (source, destination)
}
let thumbnailUrl = inputFolder.appendingPathComponent(thumbnailPath)
if !thumbnailUrl.exists {
log.add(error: "Missing thumbnail", source: path)
}
let source = pathRelativeToRootForContainedInputFile(thumbnailPath)
let ext = thumbnailPath.lastComponentAfter(".")
let destination = pathRelativeToRootForContainedInputFile("thumbnail.\(ext)")
return (source, destination)
}
private func thumbnailFileName(for language: String) -> String? {
let localizedThumbnailName = thumbnailPath.insert("-\(language)", beforeLast: ".")
let localizedThumbnail = pathRelativeToRootForContainedInputFile(localizedThumbnailName)
let localizedThumbnailUrl = inputFolder.appendingPathComponent(localizedThumbnail)
if localizedThumbnailUrl.exists {
return localizedThumbnailName
}
let thumbnailUrl = inputFolder.appendingPathComponent(thumbnailPath)
if !thumbnailUrl.exists {
return nil
}
return thumbnailPath
}
}

View File

@ -84,6 +84,15 @@ struct GenericMetadata {
*/
let images: Set<String>?
/**
The path to the thumbnail file.
This property is optional, and defaults to ``Element.defaultThumbnailName``.
Note: The generator first looks for localized versions of the thumbnail by appending `-[lang]` to the file name,
e.g. `customThumb-en.jpg`. If no file is found, then the specified file is tried.
*/
let thumbnailPath: String?
/**
The style of thumbnail to use when generating overviews.
@ -136,6 +145,7 @@ extension GenericMetadata: Codable {
.externalFiles,
.requiredFiles,
.images,
.thumbnailPath,
.thumbnailStyle,
.useManualSorting,
.overviewItemCount,
@ -207,6 +217,7 @@ extension GenericMetadata {
externalFiles: [],
requiredFiles: [],
images: [],
thumbnailPath: "",
thumbnailStyle: "",
useManualSorting: false,
overviewItemCount: 6,

View File

@ -116,24 +116,20 @@ final class ValidationLog {
}
func linkPreviewThumbnail(customFile: String?, for language: String, in folder: URL, source: String) -> String? {
if let customFile = customFile {
let customFileUrl: URL
if customFile.starts(with: "/") {
customFileUrl = URL(fileURLWithPath: customFile)
} else {
customFileUrl = folder.appendingPathComponent(customFile)
}
guard customFileUrl.exists else {
missing(customFile, requiredBy: "property 'linkPreviewImage' in metadata of \(source)")
return nil
}
return customFile
}
guard let thumbnail = Element.findThumbnail(for: language, in: folder) else {
// Link preview images are not necessarily required
guard let customFile = customFile else {
return nil
}
return thumbnail
let customFileUrl: URL
if customFile.starts(with: "/") {
customFileUrl = URL(fileURLWithPath: customFile)
} else {
customFileUrl = folder.appendingPathComponent(customFile)
}
guard customFileUrl.exists else {
missing(customFile, requiredBy: "property 'linkPreviewImage' in metadata of \(source)")
return nil
}
return customFile
}
func moreLinkText(_ elementText: String?, parent parentText: String?, source: String) -> String {

View File

@ -265,7 +265,7 @@ struct PageContentGenerator {
content[.title] = linkedPage.title(for: language)
let fullThumbnailPath = linkedPage.thumbnailFilePath(for: language)
let fullThumbnailPath = linkedPage.thumbnailFilePath(for: language).destination
let relativeImageUrl = page.relativePathToOtherSiteElement(file: fullThumbnailPath)
let metadata = linkedPage.localized(for: language)

View File

@ -21,7 +21,7 @@ struct PageHeadGenerator {
// Note: Generate separate destination link for the image,
// since we don't want a single large image for thumbnails.
// Warning: Link preview source path must be relative to root
let linkPreviewImageName = image.insert("-link", beforeLast: ".")
let linkPreviewImageName = "thumbnail-link.\(image.lastComponentAfter("."))"
let sourceImagePath = page.pathRelativeToRootForContainedInputFile(image)
let destinationImagePath = page.pathRelativeToRootForContainedInputFile(linkPreviewImageName)
files.requireImage(

View File

@ -14,8 +14,8 @@ struct ThumbnailListGenerator {
}
private func itemContent(_ item: Element, parent: Element, language: String, style: ThumbnailStyle) -> String {
let fullThumbnailPath = item.thumbnailFilePath(for: language)
let relativeImageUrl = parent.relativePathToFileWithPath(fullThumbnailPath)
let (thumbnailSourcePath, thumbnailDestPath) = item.thumbnailFilePath(for: language)
let relativeImageUrl = parent.relativePathToFileWithPath(thumbnailDestPath)
let metadata = item.localized(for: language)
var content = [ThumbnailKey : String]()
@ -37,16 +37,16 @@ struct ThumbnailListGenerator {
}
files.requireImage(
source: fullThumbnailPath,
destination: fullThumbnailPath,
source: thumbnailSourcePath,
destination: thumbnailDestPath,
requiredBy: item.path,
width: style.width,
desiredHeight: style.height)
// Create image version for high-resolution screens
files.requireImage(
source: fullThumbnailPath,
destination: fullThumbnailPath.insert("@2x", beforeLast: "."),
source: thumbnailSourcePath,
destination: thumbnailDestPath.insert("@2x", beforeLast: "."),
requiredBy: item.path,
width: style.width * 2,
desiredHeight: style.height * 2)