Fix video option

This commit is contained in:
Christoph Hagen 2025-01-05 10:38:09 +01:00
parent 01baf560ee
commit 5684196ef3
5 changed files with 89 additions and 67 deletions

View File

@ -212,6 +212,7 @@
E2FE0F1B2D274FDF002963B7 /* LinkPreviewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */; };
E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */; };
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */; };
E2FE0F222D2A84A0002963B7 /* VideoCommandProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -415,6 +416,7 @@
E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewItem.swift; sourceTree = "<group>"; };
E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewGenerator.swift; sourceTree = "<group>"; };
E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = "<group>"; };
E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCommandProcessor.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -578,6 +580,7 @@
E29D31B62D0DAC030051B7F4 /* Page Content */ = {
isa = PBXGroup;
children = (
E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */,
E2FE0F142D269188002963B7 /* PageHtmlProcessor.swift */,
E2FE0F102D268E78002963B7 /* PageCodeProcessor.swift */,
E2FE0F0E2D268D4B002963B7 /* BoxCommandProcessor.swift */,
@ -1140,6 +1143,7 @@
E229903C2D0F8A7B009F8D77 /* OptionalTextFieldPropertyView.swift in Sources */,
E25DA5932D023B3C00AEF16D /* PageSettingsFile.swift in Sources */,
E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */,
E2FE0F222D2A84A0002963B7 /* VideoCommandProcessor.swift in Sources */,
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */,
E2A21C362CB9A3D70060935B /* PathSettingsView.swift in Sources */,
E29D31362D0435430051B7F4 /* TabSelection.swift in Sources */,

View File

@ -37,7 +37,7 @@ struct ImageVersion {
"\(maximumWidth)-\(maximumHeight)-\(type.fileExtension!)"
}
/// The path of the generated image version in the output folder (without leading slash)
/// The path of the generated image version in the output folder (including leading slash)
var outputPath: String {
image.outputPath(width: maximumWidth, height: maximumHeight, type: type)
}

View File

@ -0,0 +1,79 @@
struct VideoCommandProcessor: CommandProcessor {
let commandType: ShorthandMarkdownKey = .video
let content: Content
let results: PageGenerationResults
init(content: Content, results: PageGenerationResults) {
self.content = content
self.results = results
}
/**
Format: `![video](<fileId>;<option1...>]`
*/
func process(_ arguments: [String], markdown: Substring) -> String {
guard arguments.count >= 1 else {
results.invalid(command: .video, markdown)
return ""
}
let fileId = arguments[0].trimmed
let options = arguments.dropFirst().compactMap { convertVideoOption($0, markdown: markdown) }
guard let file = content.file(fileId) else {
results.missing(file: fileId, source: "Video command")
return ""
}
#warning("Create/specify video alternatives")
results.require(file: file)
guard let videoType = file.type.htmlType else {
results.invalid(command: .video, markdown)
return ""
}
return ContentPageVideo(
filePath: file.absoluteUrl,
videoType: videoType,
options: options)
.content
}
private func convertVideoOption(_ videoOption: String, markdown: Substring) -> VideoOption? {
guard let optionText = videoOption.trimmed.nonEmpty else {
return nil
}
guard let option = VideoOption(rawValue: optionText) else {
results.invalid(command: .video, markdown)
return nil
}
switch option {
case .poster(let imageId):
if let image = content.image(imageId) {
let width = 2 * content.settings.pages.contentWidth
let version = image.imageVersion(width: width, height: width, type: .jpg)
results.require(image: version)
return .poster(image: version.outputPath)
} else {
results.missing(file: imageId, source: "Video command poster")
return nil // Image file not present, so skip the option
}
case .src(let videoId):
if let video = content.video(videoId) {
results.used(file: video)
let link = video.absoluteUrl
return .src(link)
} else {
results.missing(file: videoId, source: "Video command source")
return nil // Video file not present, so skip the option
}
default:
return option
}
}
}

View File

@ -23,6 +23,8 @@ final class PageContentParser {
private let html: PageHtmlProcessor
private let video: VideoCommandProcessor
// MARK: Other handlers
private let inlineLink: InlineLinkProcessor
@ -47,6 +49,7 @@ final class PageContentParser {
self.icons = .init(content: content, results: results)
self.box = .init(content: content, results: results)
self.html = .init(content: content, results: results)
self.video = .init(content: content, results: results)
self.inlineLink = .init(content: content, results: results, language: language)
self.code = .init(results: results)
@ -116,7 +119,7 @@ final class PageContentParser {
case .buttons:
return buttonHandler.process(arguments, markdown: markdown)
case .video:
return handleVideo(arguments, markdown: markdown)
return video.process(arguments, markdown: markdown)
case .pageLink:
return handlePageLink(arguments, markdown: markdown)
case .includedHtml:
@ -173,70 +176,6 @@ final class PageContentParser {
caption: caption).content
}
/**
Format: `![video](<fileId>;<option1...>]`
*/
private func handleVideo(_ arguments: [String], markdown: Substring) -> String {
guard arguments.count >= 1 else {
results.invalid(command: .video, markdown)
return ""
}
let fileId = arguments[0].trimmed
let options = arguments.dropFirst().compactMap { convertVideoOption($0, markdown: markdown) }
guard let file = content.file(fileId) else {
results.missing(file: fileId, source: "Video command")
return ""
}
#warning("Create/specify video alternatives")
results.require(file: file)
guard let videoType = file.type.htmlType else {
results.invalid(command: .video, markdown)
return ""
}
return ContentPageVideo(
filePath: file.absoluteUrl,
videoType: videoType,
options: options)
.content
}
private func convertVideoOption(_ videoOption: String, markdown: Substring) -> VideoOption? {
guard let optionText = videoOption.trimmed.nonEmpty else {
return nil
}
guard let option = VideoOption(rawValue: optionText) else {
results.invalid(command: .video, markdown)
return nil
}
switch option {
case .poster(let imageId):
if let image = content.image(imageId) {
let width = 2*thumbnailWidth
let version = image.imageVersion(width: width, height: width, type: .jpg)
results.require(image: version)
return .poster(image: version.outputPath)
} else {
results.missing(file: imageId, source: "Video command poster")
return nil // Image file not present, so skip the option
}
case .src(let videoId):
if let video = content.video(videoId) {
results.used(file: video)
let link = video.absoluteUrl
return .src(link)
} else {
results.missing(file: videoId, source: "Video command source")
return nil // Video file not present, so skip the option
}
default:
return option
}
}
/**
Format: `![page](<pageId>)`
*/

View File

@ -96,7 +96,7 @@ enum VideoOption {
case .height(let height): return "height='\(height)'"
case .width(let width): return "width='\(width)'"
case .preload(let option): return "preload='\(option)'"
case .poster(let image): return "poster='/\(image)'"
case .poster(let image): return "poster='\(image)'"
case .src(let url): return "src='\(url)'"
}
}