Add Sponsor Block and settings

This commit is contained in:
Arkadiusz Fal
2021-10-23 18:49:45 +02:00
parent e64a520d5e
commit 8e0af22b94
21 changed files with 362 additions and 114 deletions

View File

@@ -19,6 +19,7 @@ extension Defaults.Keys {
static let lastInstanceID = Key<Instance.ID?>("lastInstanceID")
static let sponsorBlockInstance = Key<String>("sponsorBlockInstance", default: "https://sponsor.ajay.app")
static let sponsorBlockCategories = Key<Set<String>>("sponsorBlockCategories", default: Set(SponsorBlockAPI.categories))
static let quality = Key<Stream.ResolutionSetting>("quality", default: .hd720pFirstThenBest)

View File

@@ -21,6 +21,9 @@ struct PlaybackBar: View {
Spacer()
HStack(spacing: 4) {
if !player.lastSkipped.isNil {
restoreLastSkippedSegmentButton
}
if player.currentVideo!.live {
Image(systemName: "dot.radiowaves.left.and.right")
} else if player.isLoadingAvailableStreams || player.isLoadingStream {
@@ -30,7 +33,6 @@ struct PlaybackBar: View {
streamControl
.disabled(player.isLoadingAvailableStreams)
.frame(alignment: .trailing)
.environment(\.colorScheme, .dark)
.onChange(of: player.streamSelection) { selection in
guard !selection.isNil else {
return
@@ -42,6 +44,7 @@ struct PlaybackBar: View {
.frame(maxWidth: 180)
#endif
}
.environment(\.colorScheme, .dark)
.transaction { t in t.animation = .none }
.foregroundColor(.gray)
.font(.caption2)
@@ -91,6 +94,19 @@ struct PlaybackBar: View {
return "ends at \(timeFinishAtString)"
}
private var restoreLastSkippedSegmentButton: some View {
Button {
player.restoreLastSkippedSegment()
} label: {
HStack(spacing: 4) {
Image(systemName: "arrow.uturn.left.circle")
Text(player.lastSkipped!.category)
Text("")
}
}
.buttonStyle(.plain)
}
private var streamControl: some View {
#if os(macOS)
Picker("", selection: $player.streamSelection) {

View File

@@ -17,8 +17,6 @@ struct InstanceForm: View {
@Environment(\.dismiss) private var dismiss
@EnvironmentObject<InstancesModel> private var instancesModel
var body: some View {
VStack(alignment: .leading) {
Group {

View File

@@ -2,12 +2,14 @@ import Defaults
import SwiftUI
struct ServicesSettings: View {
@Default(.sponsorBlockInstance) private var sponsorBlock
@Default(.sponsorBlockInstance) private var sponsorBlockInstance
@Default(.sponsorBlockCategories) private var sponsorBlockCategories
var body: some View {
Section(header: Text("SponsorBlock API")) {
TextField(
"SponsorBlock API Instance",
text: $sponsorBlock,
text: $sponsorBlockInstance,
prompt: Text("SponsorBlock API URL, leave blank to disable")
)
.labelsHidden()
@@ -15,9 +17,77 @@ struct ServicesSettings: View {
.autocapitalization(.none)
.keyboardType(.URL)
#endif
}
Section(header: Text("SponsorBlock Categories to Skip")) {
#if os(macOS)
List(SponsorBlockAPI.categories, id: \.self) { category in
SponsorBlockCategorySelectionRow(
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
selected: sponsorBlockCategories.contains(category)
) { value in
toggleCategory(category, value: value)
}
}
.listStyle(.inset(alternatesRowBackgrounds: true))
Spacer()
#else
ForEach(SponsorBlockAPI.categories, id: \.self) { category in
SponsorBlockCategorySelectionRow(
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
selected: sponsorBlockCategories.contains(category)
) { value in
toggleCategory(category, value: value)
}
}
#endif
}
}
func toggleCategory(_ category: String, value: Bool) {
if let index = sponsorBlockCategories.firstIndex(where: { $0 == category }), !value {
sponsorBlockCategories.remove(at: index)
} else if value {
sponsorBlockCategories.insert(category)
}
}
struct SponsorBlockCategorySelectionRow: View {
let title: String
let selected: Bool
var action: (Bool) -> Void
@State private var toggleChecked = false
var body: some View {
Button(action: { action(!selected) }) {
HStack {
#if os(macOS)
Toggle(isOn: $toggleChecked) {
Text(self.title)
Spacer()
}
.onAppear {
toggleChecked = selected
}
.onChange(of: toggleChecked) { new in
action(new)
}
#else
Text(self.title)
Spacer()
if selected {
Image(systemName: "checkmark")
#if os(iOS)
.foregroundColor(.accentColor)
#endif
}
#endif
}
.contentShape(Rectangle())
}
#if !os(tvOS)
.buttonStyle(.plain)
#endif
}
}

View File

@@ -31,7 +31,7 @@ struct HorizontalCells: View {
.padding(.vertical, 10)
#endif
}
.id(UUID())
.id(items.map(\.id).joined())
#if os(tvOS)
.frame(height: 560)
#else

View File

@@ -17,7 +17,7 @@ struct VerticalCells: View {
}
.padding()
}
.id(UUID())
.id(items.map(\.id).joined())
.edgesIgnoringSafeArea(.horizontal)
#if os(macOS)
.background()
@@ -56,7 +56,7 @@ struct VerticalCells: View {
}
}
struct VideoCellsVertical_Previews: PreviewProvider {
struct VeticalCells_Previews: PreviewProvider {
static var previews: some View {
VerticalCells(items: ContentItem.array(of: Video.allFixtures))
.injectFixtureEnvironmentObjects()

View File

@@ -5,7 +5,6 @@ struct PlayerControlsView<Content: View>: View {
@Environment(\.navigationStyle) private var navigationStyle
@EnvironmentObject<PlayerModel> private var model
@EnvironmentObject<NavigationModel> private var navigation
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content()