@@ -11,7 +11,7 @@ actor CacheRuntime {
1111 static let shared = CacheRuntime ( )
1212
1313 private( set) var server = NIOProxyServer ( )
14- private( set) var cacheIndex = CacheIndex ( rootDirectory: CacheRuntime . defaultRootDirectory )
14+ private( set) var cacheIndex = CacheIndex ( rootDirectory: CacheRuntime . defaultRootDirectoryURL )
1515 private( set) var downloader : any CacheDownloading = URLSessionDownloader ( )
1616 private var hlsContentHandler : ( @Sendable ( String ) -> String ) ?
1717 private var hlsVariantStreamSelectionHandler : HLSVariantStreamSelectionHandler ?
@@ -95,9 +95,12 @@ actor CacheRuntime {
9595 return renditions. first { $0. isDefault } ?? renditions. first
9696 }
9797
98- private func defaultVariantStream( from variants: [ HLSVariantStreamItem ] ) -> HLSVariantStreamItem {
98+ private func defaultVariantStream( from variants: [ HLSVariantStreamItem ] ) -> HLSVariantStreamItem ? {
99+ guard let fallback = fallbackVariantStream ( from: variants) else {
100+ return nil
101+ }
99102 guard variants. contains ( where: { $0. codecs != nil || $0. videoRange != nil || $0. averageBandwidth > 0 } ) else {
100- return variants [ variants . count / 2 ]
103+ return fallback
101104 }
102105
103106 return variants. min { lhs, rhs in
@@ -107,7 +110,14 @@ actor CacheRuntime {
107110 return left < right
108111 }
109112 return effectiveBandwidth ( lhs) < effectiveBandwidth ( rhs)
110- } ?? variants [ variants. count / 2 ]
113+ } ?? fallback
114+ }
115+
116+ private func fallbackVariantStream( from variants: [ HLSVariantStreamItem ] ) -> HLSVariantStreamItem ? {
117+ guard !variants. isEmpty else {
118+ return nil
119+ }
120+ return variants [ variants. count / 2 ]
111121 }
112122
113123 private func variantCompatibilityScore( _ variant: HLSVariantStreamItem ) -> Int {
@@ -116,19 +126,19 @@ actor CacheRuntime {
116126 var score = 0
117127
118128 if videoRange == " PQ " || videoRange == " HLG " {
119- score += 100
129+ score += VariantCompatibilityScore . hdrPenalty
120130 }
121131 if codecs. contains ( " dvh " ) || codecs. contains ( " dvhe " ) {
122- score += 100
132+ score += VariantCompatibilityScore . dolbyVisionPenalty
123133 }
124134 if codecs. contains ( " ec-3 " ) || codecs. contains ( " ac-3 " ) {
125- score += 20
135+ score += VariantCompatibilityScore . surroundAudioPenalty
126136 }
127137 if codecs. contains ( " mp4a " ) {
128- score -= 10
138+ score += VariantCompatibilityScore . commonCodecBonus
129139 }
130140 if codecs. contains ( " avc1 " ) {
131- score -= 10
141+ score += VariantCompatibilityScore . commonCodecBonus
132142 }
133143
134144 return score
@@ -157,10 +167,20 @@ actor CacheRuntime {
157167 await cacheIndex. setCacheIdentifierProvider ( provider)
158168 }
159169
160- private static var defaultRootDirectory : URL {
161- let baseDirectory = FileManager . default. urls ( for: . documentDirectory, in: . userDomainMask) . first ??
162- FileManager . default. urls ( for: . cachesDirectory, in: . userDomainMask) [ 0 ]
163- return baseDirectory
164- . appendingPathComponent ( " HTTPMediaCache " , isDirectory: true )
170+ static func defaultRootDirectory( cachesDirectory: URL , documentsDirectory _: URL ) -> URL {
171+ cachesDirectory. appendingPathComponent ( " HTTPMediaCache " , isDirectory: true )
172+ }
173+
174+ private static var defaultRootDirectoryURL : URL {
175+ let cachesDirectory = FileManager . default. urls ( for: . cachesDirectory, in: . userDomainMask) [ 0 ]
176+ let documentsDirectory = FileManager . default. urls ( for: . documentDirectory, in: . userDomainMask) . first ?? cachesDirectory
177+ return defaultRootDirectory ( cachesDirectory: cachesDirectory, documentsDirectory: documentsDirectory)
165178 }
166179}
180+
181+ private enum VariantCompatibilityScore {
182+ static let hdrPenalty = 100
183+ static let dolbyVisionPenalty = 100
184+ static let surroundAudioPenalty = 20
185+ static let commonCodecBonus = - 10
186+ }
0 commit comments