mirror of
https://github.com/yattee/yattee.git
synced 2026-02-19 17:29:45 +00:00
80 lines
3.1 KiB
Swift
80 lines
3.1 KiB
Swift
//
|
|
// AvatarURLBuilder.swift
|
|
// Yattee
|
|
//
|
|
// Utility for constructing channel avatar URLs with Yattee Server fallback.
|
|
//
|
|
|
|
import Foundation
|
|
import Nuke
|
|
|
|
/// Utility for constructing channel avatar URLs with Yattee Server fallback
|
|
enum AvatarURLBuilder {
|
|
|
|
/// Available avatar sizes on Yattee Server
|
|
private static let availableSizes = [32, 48, 76, 100, 176, 512]
|
|
|
|
/// Constructs the effective avatar URL for a channel
|
|
/// - Parameters:
|
|
/// - channelID: The channel ID
|
|
/// - directURL: Direct avatar URL if available (from API response)
|
|
/// - serverURL: Yattee Server base URL
|
|
/// - size: Desired size in points (will be doubled for retina and rounded to nearest available)
|
|
/// - Returns: URL to use for avatar, or nil if unavailable
|
|
static func avatarURL(
|
|
channelID: String,
|
|
directURL: URL?,
|
|
serverURL: URL?,
|
|
size: Int
|
|
) -> URL? {
|
|
// Check if this is a YouTube channel (UC prefix or @handle)
|
|
let isYouTubeChannel = channelID.hasPrefix("UC") || channelID.hasPrefix("@")
|
|
|
|
// Priority 1: For YouTube channels, prefer Yattee Server (more reliable, avoids stale URLs from iCloud sync)
|
|
if isYouTubeChannel, let serverURL = serverURL {
|
|
return buildServerAvatarURL(serverURL: serverURL, channelID: channelID, size: size)
|
|
}
|
|
|
|
// Priority 2: Use direct URL for non-YouTube channels or when server unavailable
|
|
if let directURL = directURL {
|
|
return directURL
|
|
}
|
|
|
|
// Priority 3: Try server as last resort (for YouTube without direct URL)
|
|
if let serverURL = serverURL {
|
|
return buildServerAvatarURL(serverURL: serverURL, channelID: channelID, size: size)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/// Builds the Yattee Server avatar URL for a channel
|
|
private static func buildServerAvatarURL(serverURL: URL, channelID: String, size: Int) -> URL {
|
|
// Calculate retina size and round to nearest available
|
|
let retinaSize = size * 2
|
|
let roundedSize = availableSizes
|
|
.min { abs($0 - retinaSize) < abs($1 - retinaSize) } ?? 176
|
|
|
|
return serverURL
|
|
.appendingPathComponent("api/v1/channels")
|
|
.appendingPathComponent(channelID)
|
|
.appendingPathComponent("avatar")
|
|
.appendingPathComponent("\(roundedSize).jpg")
|
|
}
|
|
|
|
/// Creates an ImageRequest with auth header for Yattee Server avatar URLs
|
|
/// - Parameters:
|
|
/// - url: The avatar URL
|
|
/// - authHeader: Optional Basic Auth header for Yattee Server
|
|
/// - Returns: ImageRequest configured with auth if needed, or nil if URL is nil
|
|
static func imageRequest(url: URL?, authHeader: String?) -> ImageRequest? {
|
|
guard let url else { return nil }
|
|
var request = URLRequest(url: url)
|
|
// Only add auth header for Yattee Server avatar URLs
|
|
if let authHeader, url.path.contains("/api/v1/channels/") && url.path.contains("/avatar/") {
|
|
request.setValue(authHeader, forHTTPHeaderField: "Authorization")
|
|
}
|
|
return ImageRequest(urlRequest: request)
|
|
}
|
|
}
|