1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
/*
This source file is part of the Swift.org open source project
Copyright (c) 2021-2024 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import Foundation
import Markdown
/// A block filled with a video.
public final class VideoMedia: Semantic, Media, AutomaticDirectiveConvertible {
public static let directiveName = "Video"
public static let introducedVersion = "5.5"
public let originalMarkup: BlockDirective
@DirectiveArgumentWrapped(
parseArgument: { bundle, argumentValue in
ResourceReference(bundleIdentifier: bundle.identifier, path: argumentValue)
}
)
public private(set) var source: ResourceReference
/// Alternate text describing the video.
@DirectiveArgumentWrapped(name: .custom("alt"))
public private(set) var altText: String? = nil
/// The name of a device frame that should wrap this video.
///
/// This is an experimental feature – any device frame specified here
/// must be defined in the `theme-settings.json` file of the containing DocC catalog.
@DirectiveArgumentWrapped(hiddenFromDocumentation: true)
public private(set) var deviceFrame: String? = nil
/// An optional caption that should be rendered alongside the video.
@ChildMarkup(numberOfParagraphs: .zeroOrOne)
public private(set) var caption: MarkupContainer
/// An image to be shown when the video isn't playing.
@DirectiveArgumentWrapped(
parseArgument: { bundle, argumentValue in
ResourceReference(bundleIdentifier: bundle.identifier, path: argumentValue)
}
)
public private(set) var poster: ResourceReference? = nil
static var keyPaths: [String : AnyKeyPath] = [
"source" : \VideoMedia._source,
"poster" : \VideoMedia._poster,
"caption" : \VideoMedia._caption,
"altText" : \VideoMedia._altText,
"deviceFrame" : \VideoMedia._deviceFrame,
]
func validate(source: URL?, for bundle: DocumentationBundle, in context: DocumentationContext, problems: inout [Problem]) -> Bool {
if !FeatureFlags.current.isExperimentalDeviceFrameSupportEnabled && deviceFrame != nil {
let diagnostic = Diagnostic(
source: source,
severity: .warning, range: originalMarkup.range,
identifier: "org.swift.docc.UnknownArgument",
summary: "Unknown argument 'deviceFrame' in \(Self.directiveName)."
)
problems.append(.init(diagnostic: diagnostic))
deviceFrame = nil
}
return true
}
@available(*, deprecated, message: "Do not call directly. Required for 'AutomaticDirectiveConvertible'.")
init(originalMarkup: BlockDirective) {
self.originalMarkup = originalMarkup
}
public override func accept<V>(_ visitor: inout V) -> V.Result where V : SemanticVisitor {
return visitor.visitVideoMedia(self)
}
}
extension VideoMedia: RenderableDirectiveConvertible {
func render(with contentCompiler: inout RenderContentCompiler) -> [RenderContent] {
var renderedCaption: [RenderInlineContent]?
if let caption = caption.first {
let blockContent = contentCompiler.visit(caption)
if case let .paragraph(paragraph) = blockContent.first as? RenderBlockContent {
renderedCaption = paragraph.inlineContent
}
}
var posterReferenceIdentifier: RenderReferenceIdentifier?
if let poster {
posterReferenceIdentifier = contentCompiler.resolveImage(source: poster.path)
}
let unescapedVideoSource = source.path.removingPercentEncoding ?? source.path
let videoIdentifier = RenderReferenceIdentifier(unescapedVideoSource)
guard let resolvedVideos = contentCompiler.context.resolveAsset(
named: unescapedVideoSource,
in: contentCompiler.identifier,
withType: .video
) else {
return []
}
contentCompiler.videoReferences[unescapedVideoSource] = VideoReference(
identifier: videoIdentifier,
altText: altText,
videoAsset: resolvedVideos,
poster: posterReferenceIdentifier
)
var metadata: RenderContentMetadata?
if renderedCaption != nil || deviceFrame != nil {
metadata = RenderContentMetadata(abstract: renderedCaption, deviceFrame: deviceFrame)
}
let video = RenderBlockContent.Video(
identifier: videoIdentifier,
metadata: metadata
)
return [RenderBlockContent.video(video)]
}
}
|