SponsorBlock set colors for each category

Default colors are defined in alignment to upstream. These can be changed by the user.
The colors are also used in the Timeline and the seek view.
This commit is contained in:
Toni Förster 2024-04-23 17:16:31 +02:00
parent 321eaecd21
commit b5ac760af2
No known key found for this signature in database
GPG Key ID: 292F3E5086C83FC7
4 changed files with 142 additions and 36 deletions

View File

@ -243,6 +243,7 @@ extension Defaults.Keys {
static let sponsorBlockInstance = Key<String>("sponsorBlockInstance", default: "https://sponsor.ajay.app") static let sponsorBlockInstance = Key<String>("sponsorBlockInstance", default: "https://sponsor.ajay.app")
static let sponsorBlockCategories = Key<Set<String>>("sponsorBlockCategories", default: Set(SponsorBlockAPI.categories)) static let sponsorBlockCategories = Key<Set<String>>("sponsorBlockCategories", default: Set(SponsorBlockAPI.categories))
static let sponsorBlockColors = Key<[String: String]>("sponsorBlockColors", default: SponsorBlockColors.dictionary)
// MARK: GROUP - Locations // MARK: GROUP - Locations
@ -580,3 +581,26 @@ enum WidgetListingStyle: String, CaseIterable, Defaults.Serializable {
case horizontalCells case horizontalCells
case list case list
} }
enum SponsorBlockColors: String {
case sponsor = "#00D400" // Green
case selfpromo = "#FFFF00" // Yellow
case interaction = "#CC00FF" // Purple
case intro = "#00FFFF" // Cyan
case outro = "#0202ED" // Dark Blue
case preview = "#008FD6" // Light Blue
case filler = "#7300FF" // Violet
case music_offtopic = "#FF9900" // Orange
// Define all cases, can be used to iterate over the colors
static let allCases: [SponsorBlockColors] = [.sponsor, .selfpromo, .interaction, .intro, .outro, .preview, .filler, .music_offtopic]
// Create a dictionary with the category names as keys and colors as values
static let dictionary: [String: String] = {
var dict = [String: String]()
for item in allCases {
dict[String(describing: item)] = item.rawValue
}
return dict
}()
}

View File

@ -13,6 +13,17 @@ struct Seek: View {
@Default(.playerControlsLayout) private var regularPlayerControlsLayout @Default(.playerControlsLayout) private var regularPlayerControlsLayout
@Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout @Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout
@Default(.sponsorBlockColors) private var sponsorBlockColors
private func getColor(for category: String) -> Color {
if let hexString = sponsorBlockColors[category], let rgbValue = Int(hexString.dropFirst(), radix: 16) {
let r = Double((rgbValue >> 16) & 0xFF) / 255.0
let g = Double((rgbValue >> 8) & 0xFF) / 255.0
let b = Double(rgbValue & 0xFF) / 255.0
return Color(red: r, green: g, blue: b)
}
return Color("AppRedColor") // Fallback color if no match found
}
var body: some View { var body: some View {
Group { Group {
@ -51,7 +62,8 @@ struct Seek: View {
if let segment = projectedSegment { if let segment = projectedSegment {
Text(SponsorBlockAPI.categoryDescription(segment.category) ?? "Sponsor") Text(SponsorBlockAPI.categoryDescription(segment.category) ?? "Sponsor")
.font(.system(size: playerControlsLayout.segmentFontSize)) .font(.system(size: playerControlsLayout.segmentFontSize))
.foregroundColor(Color("AppRedColor")) .foregroundColor(getColor(for: segment.category))
.padding(.bottom, 3)
} }
} else { } else {
#if !os(tvOS) #if !os(tvOS)
@ -69,7 +81,8 @@ struct Seek: View {
Divider() Divider()
Text(SponsorBlockAPI.categoryDescription(category) ?? "Sponsor") Text(SponsorBlockAPI.categoryDescription(category) ?? "Sponsor")
.font(.system(size: playerControlsLayout.segmentFontSize)) .font(.system(size: playerControlsLayout.segmentFontSize))
.foregroundColor(Color("AppRedColor")) .foregroundColor(getColor(for: category))
.padding(.bottom, 3)
default: default:
EmptyView() EmptyView()
} }

View File

@ -51,11 +51,22 @@ struct TimelineView: View {
@Default(.playerControlsLayout) private var regularPlayerControlsLayout @Default(.playerControlsLayout) private var regularPlayerControlsLayout
@Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout @Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout
@Default(.sponsorBlockColors) private var sponsorBlockColors
var playerControlsLayout: PlayerControlsLayout { var playerControlsLayout: PlayerControlsLayout {
player.playingFullScreen ? fullScreenPlayerControlsLayout : regularPlayerControlsLayout player.playingFullScreen ? fullScreenPlayerControlsLayout : regularPlayerControlsLayout
} }
private func getColor(for category: String) -> Color {
if let hexString = sponsorBlockColors[category], let rgbValue = Int(hexString.dropFirst(), radix: 16) {
let r = Double((rgbValue >> 16) & 0xFF) / 255.0
let g = Double((rgbValue >> 8) & 0xFF) / 255.0
let b = Double(rgbValue & 0xFF) / 255.0
return Color(red: r, green: g, blue: b)
}
return Color("AppRedColor") // Fallback color if no match found
}
var chapters: [Chapter] { var chapters: [Chapter] {
player.currentVideo?.chapters ?? [] player.currentVideo?.chapters ?? []
} }
@ -79,7 +90,7 @@ struct TimelineView: View {
Text(description) Text(description)
.font(.system(size: playerControlsLayout.segmentFontSize)) .font(.system(size: playerControlsLayout.segmentFontSize))
.fixedSize() .fixedSize()
.foregroundColor(Color("AppRedColor")) .foregroundColor(getColor(for: segment.category))
} }
if let chapter = projectedChapter { if let chapter = projectedChapter {
Text(chapter.title) Text(chapter.title)
@ -299,7 +310,7 @@ struct TimelineView: View {
ForEach(segments, id: \.uuid) { segment in ForEach(segments, id: \.uuid) { segment in
Rectangle() Rectangle()
.offset(x: segmentLayerHorizontalOffset(segment)) .offset(x: segmentLayerHorizontalOffset(segment))
.foregroundColor(Color("AppRedColor")) .foregroundColor(getColor(for: segment.category))
.frame(maxHeight: height) .frame(maxHeight: height)
.frame(width: segmentLayerWidth(segment)) .frame(width: segmentLayerWidth(segment))
} }

View File

@ -1,15 +1,18 @@
import Defaults import Defaults
import SwiftUI import SwiftUI
import UIKit
struct SponsorBlockSettings: View { struct SponsorBlockSettings: View {
@ObservedObject private var settings = SettingsModel.shared
@Default(.sponsorBlockInstance) private var sponsorBlockInstance @Default(.sponsorBlockInstance) private var sponsorBlockInstance
@Default(.sponsorBlockCategories) private var sponsorBlockCategories @Default(.sponsorBlockCategories) private var sponsorBlockCategories
@Default(.sponsorBlockColors) private var sponsorBlockColors
var body: some View { var body: some View {
Group { Group {
#if os(macOS) #if os(macOS)
sections sections
Spacer() Spacer()
#else #else
List { List {
@ -35,41 +38,63 @@ struct SponsorBlockSettings: View {
.labelsHidden() .labelsHidden()
#if !os(macOS) #if !os(macOS)
.autocapitalization(.none) .autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.URL) .keyboardType(.URL)
#endif #endif
} }
Section(header: SettingsHeader(text: "Categories to Skip".localized())) {
categoryRows
}
colorSection
Section(header: SettingsHeader(text: "Categories to Skip".localized()), footer: categoriesDetails) { Button {
#if os(macOS) settings.presentAlert(
let list = ForEach(SponsorBlockAPI.categories, id: \.self) { category in Alert(
MultiselectRow( title: Text("Restore Default Colors?"),
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown", message: Text("This action will reset all custom colors back to their original defaults. " +
selected: sponsorBlockCategories.contains(category) "Any custom color changes you've made will be lost."),
) { value in primaryButton: .destructive(Text("Restore")) {
toggleCategory(category, value: value) resetColors()
} },
} secondaryButton: .cancel()
)
)
} label: {
Text("Restore Default Colors …")
.foregroundColor(.red)
}
Group { Section(footer: categoriesDetails) {
if #available(macOS 12.0, *) { EmptyView()
list }
.listStyle(.inset(alternatesRowBackgrounds: true)) }
} else { }
list
.listStyle(.inset) private var colorSection: some View {
} Section(header: SettingsHeader(text: "Colors for Categories")) {
} ForEach(SponsorBlockAPI.categories, id: \.self) { category in
Spacer() LazyVStack(alignment: .leading) {
#else ColorPicker(
ForEach(SponsorBlockAPI.categories, id: \.self) { category in SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
MultiselectRow( selection: Binding(
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown", get: { getColor(for: category) },
selected: sponsorBlockCategories.contains(category) set: { setColor($0, for: category) }
) { value in )
toggleCategory(category, value: value) )
} }
} }
#endif }
}
private var categoryRows: some View {
ForEach(SponsorBlockAPI.categories, id: \.self) { category in
LazyVStack(alignment: .leading) {
MultiselectRow(
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
selected: sponsorBlockCategories.contains(category)
) { value in
toggleCategory(category, value: value)
}
} }
} }
} }
@ -90,7 +115,6 @@ struct SponsorBlockSettings: View {
} }
} }
.foregroundColor(.secondary) .foregroundColor(.secondary)
.padding(.top, 10)
} }
func toggleCategory(_ category: String, value: Bool) { func toggleCategory(_ category: String, value: Bool) {
@ -100,6 +124,40 @@ struct SponsorBlockSettings: View {
sponsorBlockCategories.insert(category) sponsorBlockCategories.insert(category)
} }
} }
private func getColor(for category: String) -> Color {
if let hexString = sponsorBlockColors[category], let rgbValue = Int(hexString.dropFirst(), radix: 16) {
let r = Double((rgbValue >> 16) & 0xFF) / 255.0
let g = Double((rgbValue >> 8) & 0xFF) / 255.0
let b = Double(rgbValue & 0xFF) / 255.0
return Color(red: r, green: g, blue: b)
}
return Color("AppRedColor") // Fallback color if no match found
}
private func setColor(_ color: Color, for category: String) {
let uiColor = UIColor(color)
// swiftlint:disable no_cgfloat
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
// swiftlint:enable no_cgfloat
uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
let r = Int(red * 255.0)
let g = Int(green * 255.0)
let b = Int(blue * 255.0)
let rgbValue = (r << 16) | (g << 8) | b
sponsorBlockColors[category] = String(format: "#%06x", rgbValue)
}
private func resetColors() {
sponsorBlockColors = SponsorBlockColors.dictionary
}
} }
struct SponsorBlockSettings_Previews: PreviewProvider { struct SponsorBlockSettings_Previews: PreviewProvider {