struct VideoCommand: CommandProcessor { static let commandType: CommandType = .video let content: Content let results: PageGenerationResults init(content: Content, results: PageGenerationResults, language: ContentLanguage) { self.content = content self.results = results } /** Format: `![video](;]` */ func process(_ arguments: [String], markdown: Substring) -> String { guard arguments.count >= 1 else { invalid(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 "" } results.require(file: file) guard let videoType = file.type.htmlType else { invalid(markdown) return "" } return ContentPageVideo( filePath: file.absoluteUrl, videoType: videoType, options: options) .content } private func convertVideoOption(_ videoOption: String, markdown: Substring) -> Option? { guard let optionText = videoOption.trimmed.nonEmpty else { return nil } guard let option = Option(rawValue: optionText) else { invalid(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 } } }