Skip to content

Commit

Permalink
Fix crash parsing incomplete youtu.be links.
Browse files Browse the repository at this point in the history
  • Loading branch information
nolanw committed Oct 12, 2023
1 parent 3d96d73 commit 0a59079
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 67 deletions.
128 changes: 61 additions & 67 deletions App/Misc/HTMLRenderingHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -286,19 +286,18 @@ extension HTMLDocument {

func embedVideos() {
for a in nodes(matchingSelector: "a") {
if
let href = a["href"],
let url = URL(string: href),
let host = url.host
{
// don't expand if the post has an NWS smilie
if (containingPostIsNWS(node: a)) {
continue;
}
if let href = a["href"],
let url = URL(string: href),
let host = url.host
{
// don't expand if the post has an NWS smilie
if containingPostIsNWS(node: a) {
continue
}

if let ext = href.range(of: #"(\.gifv|\.webm|\.mp4)$"#, options: .regularExpression) {
if(host.lowercased().hasSuffix("imgur.com")) {
let videoElement = HTMLElement(tagName: "video", attributes: [
if let ext = href.range(of: #"(\.gifv|\.webm|\.mp4)$"#, options: .regularExpression) {
if host.lowercased().hasSuffix("imgur.com") {
let videoElement = HTMLElement(tagName: "video", attributes: [
"width": "300",
"preload":"metadata",
"controls":"",
Expand All @@ -308,11 +307,12 @@ extension HTMLDocument {
"src":href.replacingCharacters(in: ext, with: ".mp4"),
"type":"video/mp4"])

a.parent?.replace(child: a, with: videoElement)
}
if (host.lowercased().hasSuffix(".discordapp.com") &&
href.hasSuffix(".mp4")) {
let videoElement = HTMLElement(tagName: "video", attributes: [
a.parent?.replace(child: a, with: videoElement)
}
if host.lowercased().hasSuffix(".discordapp.com") &&
href.hasSuffix(".mp4")
{
let videoElement = HTMLElement(tagName: "video", attributes: [
"width": "300",
"preload":"metadata",
"controls":"",
Expand All @@ -321,41 +321,36 @@ extension HTMLDocument {
"src":href,
"type":"video/mp4"])

a.parent?.replace(child: a, with: videoElement)
}
//todo gifcat mp4 files
a.parent?.replace(child: a, with: videoElement)
}
//todo gifcat mp4 files
}

// only replace youtube links that are raw URLs
else if((href == a.textContent) &&
href.range(of: #"((https).+youtu)"#, options: .regularExpression) != nil) {
guard
let youtubeUri = getYoutubeEmbeddedUri(uri: href)
else {
continue
}
// only replace youtube links that are raw URLs
else if href == a.textContent &&
href.range(of: #"https.+youtu"#, options: .regularExpression) != nil
{
if let youtubeUri = getYoutubeEmbeddedUri(href: href) {
let embedElement = HTMLElement(tagName: "iframe", attributes: [
"width":"500",
"height":"315",
"src": youtubeUri,
"frameborder":"0",
"allow":"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",
"allowfullscreen":""
"width": "500",
"height": "315",
"src": youtubeUri.absoluteString,
"frameborder": "0",
"allow": "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",
"allowfullscreen": "",
])
a.parent?.replace(child: a, with: embedElement)
}

}

}

}
}
}

extension URL {
func valueForFirstQueryItem(named name: String) -> String? {
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true) else { return nil }
return (components.queryItems ?? []).first { $0.name == name }?.value
let components = URLComponents(url: self, resolvingAgainstBaseURL: true)
return components?.queryItems?.first { $0.name == name }?.value
}
}

Expand Down Expand Up @@ -424,37 +419,36 @@ private func randomwaffleURLForWaffleimagesURL(_ url: URL) -> URL? {
return components.url
}

private func getYoutubeEmbeddedUri(uri: String) -> String? {
guard
let sourceUrl = URLComponents(string: uri)
else{
return nil
}
var seconds: Int?
var id: String?
if(sourceUrl.host == "youtu.be") {
let t = sourceUrl.queryItems?.filter({$0.name == "t"}).first
if(t != nil && t?.value != nil){
seconds = Int(t!.value!)
}
let str: String = sourceUrl.path
id = String(str[str.index(str.startIndex, offsetBy: 1)...])
private func getYoutubeEmbeddedUri(href: String) -> URL? {
guard let source = URLComponents(string: href) else { return nil }
let seconds: Int?
let id: String
if source.host == "youtu.be" {
id = String(source.path.dropFirst())

seconds = source.queryItems?
.first { $0.name == "t" }?
.value
.flatMap { Int($0) }
} else {
let pair = sourceUrl.fragment?.split(separator: "=")
if(pair?[0] == "t" && pair?[1] != nil){
seconds = Int(pair![1])
}
let v = sourceUrl.queryItems?.filter({$0.name == "v"}).first
if(v != nil && v?.value != nil) {
id = v!.value
id = source.queryItems?.first(where: { $0.name == "v" })?.value ?? ""

if let fragment = source.fragment,
case let pair = fragment.split(separator: "=", maxSplits: 1),
pair.count == 2,
pair[0] == "t"
{
seconds = Int(pair[1])
} else {
seconds = nil
}
}

let secondsString = seconds != nil ? "?start=\(seconds!)" : ""
if(id == nil) {
return nil
guard !id.isEmpty,
var embed = URLComponents(string: "https://www.youtube.com/embed/\(id)")
else { return nil }
if let seconds {
embed.queryItems = [.init(name: "start", value: "\(seconds)")]
}
return """
https://www.youtube.com/embed/\(id!)\(secondsString)
"""
return embed.url
}
9 changes: 9 additions & 0 deletions App/Tests/HTMLRenderingHelperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,13 @@ final class HTMLRenderingHelperTests: XCTestCase {
hello there <span class="mention">^cool&gt;$user\\\"</span>
""")
}

func testEmbedVideosForIncompleteYouTubeURL() {
let doc = HTMLDocument(string: """
<a href="https://youtu.be" target="_blank" rel="nofollow">https://youtu.be</a>
""")
doc.embedVideos()
XCTAssertNil(doc.firstNode(matchingSelector: "iframe"))
XCTAssertNotNil(doc.firstNode(matchingSelector: "a"))
}
}

0 comments on commit 0a59079

Please sign in to comment.