tv-anarchy/Sources/TVAnarchyCore/Library/RegistryIngest.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
}
}