mirror of
https://github.com/dirtydishes/dreamio.git
synced 2026-06-06 13:37:24 +00:00
accept plain stremio subtitle text
This commit is contained in:
parent
128b9518a5
commit
9693f65f45
3 changed files with 234 additions and 7 deletions
|
|
@ -97,7 +97,7 @@ final class SubtitleResolver: SubtitleResolving {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cacheSubtitleDataIfNeeded(_ data: Data, original: SubtitleCandidate) -> SubtitleCandidate? {
|
private func cacheSubtitleDataIfNeeded(_ data: Data, original: SubtitleCandidate) -> SubtitleCandidate? {
|
||||||
guard let subtitleType = Self.subtitleType(in: data) else {
|
guard let subtitleType = Self.subtitleType(in: data, sourceURL: original.url) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ final class SubtitleResolver: SubtitleResolving {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func subtitleType(in data: Data) -> SubtitlePayloadType? {
|
private static func subtitleType(in data: Data, sourceURL: URL? = nil) -> SubtitlePayloadType? {
|
||||||
guard !data.isEmpty,
|
guard !data.isEmpty,
|
||||||
let text = String(data: data.prefix(4096), encoding: .utf8)?
|
let text = String(data: data.prefix(4096), encoding: .utf8)?
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
|
@ -228,9 +228,32 @@ final class SubtitleResolver: SubtitleResolving {
|
||||||
) != nil {
|
) != nil {
|
||||||
return .srt
|
return .srt
|
||||||
}
|
}
|
||||||
|
if let sourceURL,
|
||||||
|
isStremioSubtitleDownloadURL(sourceURL),
|
||||||
|
isPlausiblePlainSubtitleText(text) {
|
||||||
|
return .srt
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func isPlausiblePlainSubtitleText(_ text: String) -> Bool {
|
||||||
|
let lowercased = text.lowercased()
|
||||||
|
guard !lowercased.hasPrefix("{"),
|
||||||
|
!lowercased.hasPrefix("["),
|
||||||
|
!lowercased.hasPrefix("<!doctype"),
|
||||||
|
!lowercased.hasPrefix("<html"),
|
||||||
|
!lowercased.hasPrefix("<?xml")
|
||||||
|
else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return lowercased.contains("-->")
|
||||||
|
|| lowercased.contains("<font")
|
||||||
|
|| lowercased.contains("{\\")
|
||||||
|
|| lowercased.contains("\\n")
|
||||||
|
|| lowercased.split(whereSeparator: \.isNewline).count > 1
|
||||||
|
}
|
||||||
|
|
||||||
private static func logRejected(_ candidate: SubtitleCandidate, responseURL: URL?, data: Data) -> SubtitleCandidate? {
|
private static func logRejected(_ candidate: SubtitleCandidate, responseURL: URL?, data: Data) -> SubtitleCandidate? {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
let responseDescription = responseURL.map { URLRedactor.redactedURLString($0.absoluteString) } ?? "none"
|
let responseDescription = responseURL.map { URLRedactor.redactedURLString($0.absoluteString) } ?? "none"
|
||||||
|
|
@ -244,10 +267,21 @@ final class SubtitleResolver: SubtitleResolving {
|
||||||
} else {
|
} else {
|
||||||
bodyKind = "unreadable"
|
bodyKind = "unreadable"
|
||||||
}
|
}
|
||||||
print("[DreamioSubtitles] rejected candidate reason=\(bodyKind) url=\(URLRedactor.redactedURLString(candidate.url.absoluteString)) responseURL=\(responseDescription)")
|
print("[DreamioSubtitles] rejected candidate reason=\(bodyKind) url=\(URLRedactor.redactedURLString(candidate.url.absoluteString)) responseURL=\(responseDescription) preview=\(rejectionPreview(data))")
|
||||||
#endif
|
#endif
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private static func rejectionPreview(_ data: Data) -> String {
|
||||||
|
guard let text = String(data: data.prefix(180), encoding: .utf8) else {
|
||||||
|
return "unreadable"
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
.replacingOccurrences(of: "\n", with: "\\n")
|
||||||
|
.replacingOccurrences(of: "\r", with: "\\r")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol StreamResolving {
|
protocol StreamResolving {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ struct StreamResolverTests {
|
||||||
testOpenSubtitlesV3DownloadResponseResolution()
|
testOpenSubtitlesV3DownloadResponseResolution()
|
||||||
testOpenSubtitlesNestedDownloadResponseResolution()
|
testOpenSubtitlesNestedDownloadResponseResolution()
|
||||||
await testSubtitleResolverCachesStremioDownloadBody()
|
await testSubtitleResolverCachesStremioDownloadBody()
|
||||||
|
await testSubtitleResolverCachesPlainStremioDownloadBody()
|
||||||
await testSubtitleResolverDownloadJSONReturningLink()
|
await testSubtitleResolverDownloadJSONReturningLink()
|
||||||
await testSubtitleResolverRedirectToDirectSubtitle()
|
await testSubtitleResolverRedirectToDirectSubtitle()
|
||||||
await testSubtitleResolverRejectsNonSubtitleAPIResponse()
|
await testSubtitleResolverRejectsNonSubtitleAPIResponse()
|
||||||
|
|
@ -476,6 +477,41 @@ struct StreamResolverTests {
|
||||||
assertEqual(cachedBody, subtitleBody)
|
assertEqual(cachedBody, subtitleBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func testSubtitleResolverCachesPlainStremioDownloadBody() async {
|
||||||
|
let sourceURL = "https://subs5.strem.io/en/download/subencoding-stremio-utf8/src-api/file/1952341942"
|
||||||
|
let subtitleBody = """
|
||||||
|
00:01.000 --> 00:02.000
|
||||||
|
Plain cue text without an index
|
||||||
|
|
||||||
|
"""
|
||||||
|
MockURLProtocol.handler = nil
|
||||||
|
MockURLProtocol.handlers = [
|
||||||
|
sourceURL: (
|
||||||
|
200,
|
||||||
|
URL(string: sourceURL)!,
|
||||||
|
subtitleBody.data(using: .utf8)!
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
let cacheDirectory = FileManager.default.temporaryDirectory
|
||||||
|
.appendingPathComponent("DreamioSubtitleResolverTests-\(UUID().uuidString)", isDirectory: true)
|
||||||
|
defer {
|
||||||
|
try? FileManager.default.removeItem(at: cacheDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolver = SubtitleResolver(session: mockSession(), cacheDirectory: cacheDirectory)
|
||||||
|
let candidate = await resolver.resolve(SubtitleCandidate(
|
||||||
|
url: URL(string: sourceURL)!,
|
||||||
|
label: "English",
|
||||||
|
language: "eng"
|
||||||
|
))
|
||||||
|
|
||||||
|
assertEqual(candidate?.url.isFileURL, true)
|
||||||
|
assertEqual(candidate?.url.pathExtension, "srt")
|
||||||
|
let cachedBody = try? String(contentsOf: candidate!.url, encoding: .utf8)
|
||||||
|
assertEqual(cachedBody, subtitleBody)
|
||||||
|
}
|
||||||
|
|
||||||
private static func testSubtitleResolverDownloadJSONReturningLink() async {
|
private static func testSubtitleResolverDownloadJSONReturningLink() async {
|
||||||
MockURLProtocol.handler = nil
|
MockURLProtocol.handler = nil
|
||||||
MockURLProtocol.handlers = [
|
MockURLProtocol.handlers = [
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue