Skip to content

Commit

Permalink
fix: subtitle syncing issues (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
mta452 authored and shafqat-muneer committed Jan 24, 2025
1 parent eaacaf0 commit bfb5057
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 20 deletions.
94 changes: 74 additions & 20 deletions Course/Course/Presentation/Video/SubtitlesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ public struct SubtitlesView: View {
@State var pause: Bool = false
@State var languages: [SubtitleUrl]

@State private var isAnimating = false
@State private var syncBlock: (() -> Void)?

@State private var autoScrollPublisher = PassthroughSubject<Void, Never>()

private enum Constants {
static let autoScrollInterval: TimeInterval = 3.0
static let animationDuration: TimeInterval = 0.3
static let animationSkipInterval: TimeInterval = animationDuration + 0.05
}

public init(languages: [SubtitleUrl],
currentTime: Binding<Double>,
viewModel: VideoPlayerViewModel,
Expand Down Expand Up @@ -76,39 +87,82 @@ public struct SubtitlesView: View {
.foregroundColor(subtitle.fromTo.contains(Date(milliseconds: currentTime))
? Theme.Colors.accentButtonColor
: Theme.Colors.textPrimary)

.onChange(of: currentTime, perform: { _ in
if subtitle.fromTo.contains(Date(milliseconds: currentTime)) {
if id != subtitle.id {
withAnimation {
if !pause {
scroll.scrollTo(subtitle.id, anchor: .top)
}
}
}
self.id = subtitle.id
}
})
})
}.id(subtitle.id)
}
}
}
}.simultaneousGesture(
}
.simultaneousGesture(
DragGesture().onChanged({ _ in
pauseScrolling()
}))
pause = true
autoScrollPublisher.send()
})
)
.onChange(of: currentTime) { _ in
refreshID()
}
.onChange(of: viewModel.isPlaying) { isPlaying in
if isPlaying {
scrollTo(id, in: scroll)
}
}
.onChange(of: id) { newID in
scrollTo(newID, in: scroll)
}
.onReceive(
autoScrollPublisher.debounce(
for: .seconds(Constants.autoScrollInterval),
scheduler: DispatchQueue.main
),
perform: { _ in
if pause {
refreshID()
pause = false
}
}
)
}
}.padding(.horizontal, 24)
.padding(.top, 16)
.padding(.bottom, isHorizontal ? 100 : 16)
}
}

private func pauseScrolling() {
pause = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.pause = false
private func refreshID() {
if let subtitle = viewModel.findSubtitle(at: Date(milliseconds: currentTime)) {
id = subtitle.id
}
}

private func scrollTo(_ viewID: Int, in scrollView: ScrollViewProxy) {
if !pause {
let scrollBlock = {
if viewModel.isPlaying {
isAnimating = true

withAnimation(.linear(duration: Constants.animationDuration)) {
scrollView.scrollTo(viewID, anchor: .top)
}

DispatchQueue.main.asyncAfter(deadline: .now() + Constants.animationSkipInterval) {
isAnimating = false

if let nextBlock = syncBlock {
syncBlock = nil
nextBlock()
}
}
} else {
scrollView.scrollTo(viewID, anchor: .top)
}
}

if isAnimating {
syncBlock = scrollBlock
} else {
scrollBlock()
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Course/Course/Presentation/Video/VideoPlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import _AVKit_SwiftUI
import Combine

public class VideoPlayerViewModel: ObservableObject {
@Published private(set) var isPlaying: Bool = false
@Published var pause: Bool = false
@Published var currentTime: Double = 0
@Published var isLoading: Bool = true
Expand Down Expand Up @@ -100,6 +101,8 @@ public class VideoPlayerViewModel: ObservableObject {

playerHolder.getRatePublisher()
.sink {[weak self] rate in
self?.isPlaying = rate != 0

guard self?.isLoading == false else { return }
self?.trackVideoSpeedChange(rate: rate)
}
Expand Down

0 comments on commit bfb5057

Please sign in to comment.