55 lines
2.7 KiB
Swift
55 lines
2.7 KiB
Swift
import Foundation
|
|
|
|
/// Last-resort offline title list: ingests media-recommender's `registry.md`
|
|
/// when neither a scan nor a cached snapshot is available. Produces episode-less
|
|
/// shows (titles only) so the grid still renders something useful offline.
|
|
public enum RegistryIngest {
|
|
private static func registryURL() -> URL {
|
|
if let dir = ProcessInfo.processInfo.environment["MEDIA_RECOMMENDER_DIR"] {
|
|
return URL(fileURLWithPath: dir).appendingPathComponent("registry.md")
|
|
}
|
|
return RepoPaths.recommender.appendingPathComponent("registry.md")
|
|
}
|
|
|
|
public static func shows() -> [CachedShow] {
|
|
guard let text = try? String(contentsOf: registryURL(), encoding: .utf8) else { return [] }
|
|
return parse(text)
|
|
}
|
|
|
|
static func parse(_ text: String) -> [CachedShow] {
|
|
var seen = Set<String>()
|
|
var out: [CachedShow] = []
|
|
for raw in text.split(separator: "\n") {
|
|
let line = raw.trimmingCharacters(in: .whitespaces)
|
|
// Only bullet lines are show entries (skip headings, the _Generated_
|
|
// note, table rows, blanks).
|
|
guard line.hasPrefix("- ") || line.hasPrefix("* ") else { continue }
|
|
var head = String(line.dropFirst(2))
|
|
// The registry separates title from release notes with " — " (em dash).
|
|
// The note carries a season range we can surface offline (e.g.
|
|
// "S01-S05 720p"); the title parses to the show name.
|
|
var note = ""
|
|
if let r = head.range(of: " — ") {
|
|
note = String(head[r.upperBound...])
|
|
head = String(head[..<r.lowerBound])
|
|
}
|
|
let name = LibraryScanner.normalizeShowName(head)
|
|
let key = name.lowercased()
|
|
guard !name.isEmpty, name.count > 1, !seen.contains(key) else { continue }
|
|
seen.insert(key)
|
|
out.append(CachedShow(name: name, rootDir: "registry:\(key)",
|
|
episodes: [], seasonCount: seasonCount(from: note)))
|
|
}
|
|
return out.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
|
|
}
|
|
|
|
/// Season count from a registry note: "S01-S05" → 5, a lone "S02" → 1, none → nil.
|
|
static func seasonCount(from note: String) -> Int? {
|
|
if let r = note.range(of: "S(\\d{1,2})-S(\\d{1,2})", options: [.regularExpression, .caseInsensitive]) {
|
|
let nums = note[r].split(whereSeparator: { !$0.isNumber }).compactMap { Int($0) }
|
|
if nums.count == 2, nums[1] >= nums[0] { return nums[1] - nums[0] + 1 }
|
|
}
|
|
if note.range(of: "S\\d{1,2}", options: [.regularExpression, .caseInsensitive]) != nil { return 1 }
|
|
return nil
|
|
}
|
|
}
|