Skip notifications for upcoming/premiere videos

Videos that haven't premiered yet were triggering repeated notifications
on every background refresh cycle. Filter them out by checking isUpcoming
flag and rejecting videos with future publish dates.

Also decode isUpcoming/premiereTimestamp from Yattee Server feed responses
instead of hardcoding false/nil.
This commit is contained in:
Arkadiusz Fal
2026-04-04 15:47:56 +02:00
parent 00ba029a92
commit 0071e1b117
2 changed files with 22 additions and 7 deletions

View File

@@ -402,6 +402,8 @@ struct ServerFeedVideo: Decodable, Sendable {
let videoThumbnails: [YatteeThumbnail]? let videoThumbnails: [YatteeThumbnail]?
let extractor: String let extractor: String
let videoUrl: String? let videoUrl: String?
let isUpcoming: Bool?
let premiereTimestamp: Int64?
func toVideo() -> Video? { func toVideo() -> Video? {
// Determine content source based on extractor // Determine content source based on extractor
@@ -427,8 +429,8 @@ struct ServerFeedVideo: Decodable, Sendable {
likeCount: nil, likeCount: nil,
thumbnails: videoThumbnails?.map { $0.toThumbnail() } ?? [], thumbnails: videoThumbnails?.map { $0.toThumbnail() } ?? [],
isLive: false, isLive: false,
isUpcoming: false, isUpcoming: isUpcoming ?? false,
scheduledStartTime: nil scheduledStartTime: premiereTimestamp.map { Date(timeIntervalSince1970: TimeInterval($0)) }
) )
} }
} }

View File

@@ -122,7 +122,8 @@ final class BackgroundFeedRefresher {
let newVideos: [(video: Video, channelName: String)] = response.videos.compactMap { serverVideo in let newVideos: [(video: Video, channelName: String)] = response.videos.compactMap { serverVideo in
guard let video = serverVideo.toVideo(), guard let video = serverVideo.toVideo(),
let publishedAt = video.publishedAt, let publishedAt = video.publishedAt,
publishedAt > lastCheckDate else { publishedAt > lastCheckDate,
publishedAt <= Date() else {
return nil return nil
} }
dateFilteredCount += 1 dateFilteredCount += 1
@@ -218,9 +219,10 @@ final class BackgroundFeedRefresher {
} }
channelFilteredCount += 1 channelFilteredCount += 1
// Only include videos published after last check // Only include videos published after last check and not in the future
guard let publishedAt = video.publishedAt, guard let publishedAt = video.publishedAt,
publishedAt > lastCheckDate else { publishedAt > lastCheckDate,
publishedAt <= Date() else {
return nil return nil
} }
dateFilteredCount += 1 dateFilteredCount += 1
@@ -318,9 +320,10 @@ final class BackgroundFeedRefresher {
} }
channelFilteredCount += 1 channelFilteredCount += 1
// Only include videos published after last check // Only include videos published after last check and not in the future
guard let publishedAt = video.publishedAt, guard let publishedAt = video.publishedAt,
publishedAt > lastCheckDate else { publishedAt > lastCheckDate,
publishedAt <= Date() else {
return nil return nil
} }
dateFilteredCount += 1 dateFilteredCount += 1
@@ -377,6 +380,16 @@ final class BackgroundFeedRefresher {
var videosToNotify = newVideos var videosToNotify = newVideos
// Filter out upcoming/premiere videos that haven't aired yet
let upcomingCount = videosToNotify.count
videosToNotify = videosToNotify.filter { !$0.video.isUpcoming }
if upcomingCount - videosToNotify.count > 0 {
LoggingService.shared.debug(
"Filtered \(upcomingCount - videosToNotify.count) upcoming/premiere videos from notifications",
category: .notifications
)
}
// Filter out videos the user has already started or finished watching // Filter out videos the user has already started or finished watching
if let appEnvironment { if let appEnvironment {
let watchEntries = appEnvironment.dataManager.watchEntriesMap() let watchEntries = appEnvironment.dataManager.watchEntriesMap()