Allow custom thumbnail paths in metadata
This commit is contained in:
parent
9d2f1e4c90
commit
c82080db82
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -85,6 +85,15 @@ struct Element {
|
|||||||
*/
|
*/
|
||||||
let images: [ManualImage]
|
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.
|
The style of thumbnail to use when generating overviews.
|
||||||
|
|
||||||
@ -168,6 +177,7 @@ struct Element {
|
|||||||
self.externalFiles = metadata.externalFiles ?? []
|
self.externalFiles = metadata.externalFiles ?? []
|
||||||
self.requiredFiles = metadata.requiredFiles ?? [] // Paths are already relative to root
|
self.requiredFiles = metadata.requiredFiles ?? [] // Paths are already relative to root
|
||||||
self.images = metadata.images?.compactMap { ManualImage(input: $0, path: "") } ?? []
|
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.thumbnailStyle = log.unused(metadata.thumbnailStyle, "thumbnailStyle", source: source) ?? .large
|
||||||
self.useManualSorting = log.unused(metadata.useManualSorting, "useManualSorting", source: source) ?? true
|
self.useManualSorting = log.unused(metadata.useManualSorting, "useManualSorting", source: source) ?? true
|
||||||
self.overviewItemCount = metadata.overviewItemCount ?? Element.overviewItemCountDefault
|
self.overviewItemCount = metadata.overviewItemCount ?? Element.overviewItemCountDefault
|
||||||
@ -237,6 +247,7 @@ struct Element {
|
|||||||
self.externalFiles = Element.rootPaths(for: metadata.externalFiles, path: path)
|
self.externalFiles = Element.rootPaths(for: metadata.externalFiles, path: path)
|
||||||
self.requiredFiles = Element.rootPaths(for: metadata.requiredFiles, path: path)
|
self.requiredFiles = Element.rootPaths(for: metadata.requiredFiles, path: path)
|
||||||
self.images = metadata.images?.compactMap { ManualImage(input: $0, 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.thumbnailStyle = log.thumbnailStyle(metadata.thumbnailStyle, source: source)
|
||||||
self.useManualSorting = metadata.useManualSorting ?? false
|
self.useManualSorting = metadata.useManualSorting ?? false
|
||||||
self.overviewItemCount = metadata.overviewItemCount ?? parent.overviewItemCount
|
self.overviewItemCount = metadata.overviewItemCount ?? parent.overviewItemCount
|
||||||
@ -420,17 +431,6 @@ extension Element {
|
|||||||
|
|
||||||
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
|
The full url (relative to root) for the localized page
|
||||||
- Parameter language: The language of the page where the url should point
|
- Parameter language: The language of the page where the url should point
|
||||||
@ -497,7 +497,7 @@ extension Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func linkPreviewImage(for language: String) -> String? {
|
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
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -84,6 +84,15 @@ struct GenericMetadata {
|
|||||||
*/
|
*/
|
||||||
let images: Set<String>?
|
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.
|
The style of thumbnail to use when generating overviews.
|
||||||
|
|
||||||
@ -136,6 +145,7 @@ extension GenericMetadata: Codable {
|
|||||||
.externalFiles,
|
.externalFiles,
|
||||||
.requiredFiles,
|
.requiredFiles,
|
||||||
.images,
|
.images,
|
||||||
|
.thumbnailPath,
|
||||||
.thumbnailStyle,
|
.thumbnailStyle,
|
||||||
.useManualSorting,
|
.useManualSorting,
|
||||||
.overviewItemCount,
|
.overviewItemCount,
|
||||||
@ -207,6 +217,7 @@ extension GenericMetadata {
|
|||||||
externalFiles: [],
|
externalFiles: [],
|
||||||
requiredFiles: [],
|
requiredFiles: [],
|
||||||
images: [],
|
images: [],
|
||||||
|
thumbnailPath: "",
|
||||||
thumbnailStyle: "",
|
thumbnailStyle: "",
|
||||||
useManualSorting: false,
|
useManualSorting: false,
|
||||||
overviewItemCount: 6,
|
overviewItemCount: 6,
|
||||||
|
@ -116,24 +116,20 @@ final class ValidationLog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func linkPreviewThumbnail(customFile: String?, for language: String, in folder: URL, source: String) -> String? {
|
func linkPreviewThumbnail(customFile: String?, for language: String, in folder: URL, source: String) -> String? {
|
||||||
if let customFile = customFile {
|
guard let customFile = customFile else {
|
||||||
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
|
|
||||||
return nil
|
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 {
|
func moreLinkText(_ elementText: String?, parent parentText: String?, source: String) -> String {
|
||||||
|
@ -265,7 +265,7 @@ struct PageContentGenerator {
|
|||||||
|
|
||||||
content[.title] = linkedPage.title(for: language)
|
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 relativeImageUrl = page.relativePathToOtherSiteElement(file: fullThumbnailPath)
|
||||||
let metadata = linkedPage.localized(for: language)
|
let metadata = linkedPage.localized(for: language)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ struct PageHeadGenerator {
|
|||||||
// Note: Generate separate destination link for the image,
|
// Note: Generate separate destination link for the image,
|
||||||
// since we don't want a single large image for thumbnails.
|
// since we don't want a single large image for thumbnails.
|
||||||
// Warning: Link preview source path must be relative to root
|
// 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 sourceImagePath = page.pathRelativeToRootForContainedInputFile(image)
|
||||||
let destinationImagePath = page.pathRelativeToRootForContainedInputFile(linkPreviewImageName)
|
let destinationImagePath = page.pathRelativeToRootForContainedInputFile(linkPreviewImageName)
|
||||||
files.requireImage(
|
files.requireImage(
|
||||||
|
@ -14,8 +14,8 @@ struct ThumbnailListGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func itemContent(_ item: Element, parent: Element, language: String, style: ThumbnailStyle) -> String {
|
private func itemContent(_ item: Element, parent: Element, language: String, style: ThumbnailStyle) -> String {
|
||||||
let fullThumbnailPath = item.thumbnailFilePath(for: language)
|
let (thumbnailSourcePath, thumbnailDestPath) = item.thumbnailFilePath(for: language)
|
||||||
let relativeImageUrl = parent.relativePathToFileWithPath(fullThumbnailPath)
|
let relativeImageUrl = parent.relativePathToFileWithPath(thumbnailDestPath)
|
||||||
let metadata = item.localized(for: language)
|
let metadata = item.localized(for: language)
|
||||||
var content = [ThumbnailKey : String]()
|
var content = [ThumbnailKey : String]()
|
||||||
|
|
||||||
@ -37,16 +37,16 @@ struct ThumbnailListGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
files.requireImage(
|
files.requireImage(
|
||||||
source: fullThumbnailPath,
|
source: thumbnailSourcePath,
|
||||||
destination: fullThumbnailPath,
|
destination: thumbnailDestPath,
|
||||||
requiredBy: item.path,
|
requiredBy: item.path,
|
||||||
width: style.width,
|
width: style.width,
|
||||||
desiredHeight: style.height)
|
desiredHeight: style.height)
|
||||||
|
|
||||||
// Create image version for high-resolution screens
|
// Create image version for high-resolution screens
|
||||||
files.requireImage(
|
files.requireImage(
|
||||||
source: fullThumbnailPath,
|
source: thumbnailSourcePath,
|
||||||
destination: fullThumbnailPath.insert("@2x", beforeLast: "."),
|
destination: thumbnailDestPath.insert("@2x", beforeLast: "."),
|
||||||
requiredBy: item.path,
|
requiredBy: item.path,
|
||||||
width: style.width * 2,
|
width: style.width * 2,
|
||||||
desiredHeight: style.height * 2)
|
desiredHeight: style.height * 2)
|
||||||
|
Loading…
Reference in New Issue
Block a user