From 0071e1b117fdcb81b901154e797ff96249cb5b91 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sat, 4 Apr 2026 15:47:56 +0200 Subject: [PATCH] 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. --- Yattee/Services/API/YatteeServerAPI.swift | 6 +++-- .../BackgroundFeedRefresher.swift | 23 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Yattee/Services/API/YatteeServerAPI.swift b/Yattee/Services/API/YatteeServerAPI.swift index 312c463c..632458ce 100644 --- a/Yattee/Services/API/YatteeServerAPI.swift +++ b/Yattee/Services/API/YatteeServerAPI.swift @@ -402,6 +402,8 @@ struct ServerFeedVideo: Decodable, Sendable { let videoThumbnails: [YatteeThumbnail]? let extractor: String let videoUrl: String? + let isUpcoming: Bool? + let premiereTimestamp: Int64? func toVideo() -> Video? { // Determine content source based on extractor @@ -427,8 +429,8 @@ struct ServerFeedVideo: Decodable, Sendable { likeCount: nil, thumbnails: videoThumbnails?.map { $0.toThumbnail() } ?? [], isLive: false, - isUpcoming: false, - scheduledStartTime: nil + isUpcoming: isUpcoming ?? false, + scheduledStartTime: premiereTimestamp.map { Date(timeIntervalSince1970: TimeInterval($0)) } ) } } diff --git a/Yattee/Services/BackgroundRefresh/BackgroundFeedRefresher.swift b/Yattee/Services/BackgroundRefresh/BackgroundFeedRefresher.swift index c1cff5f1..210a8867 100644 --- a/Yattee/Services/BackgroundRefresh/BackgroundFeedRefresher.swift +++ b/Yattee/Services/BackgroundRefresh/BackgroundFeedRefresher.swift @@ -122,7 +122,8 @@ final class BackgroundFeedRefresher { let newVideos: [(video: Video, channelName: String)] = response.videos.compactMap { serverVideo in guard let video = serverVideo.toVideo(), let publishedAt = video.publishedAt, - publishedAt > lastCheckDate else { + publishedAt > lastCheckDate, + publishedAt <= Date() else { return nil } dateFilteredCount += 1 @@ -218,9 +219,10 @@ final class BackgroundFeedRefresher { } 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, - publishedAt > lastCheckDate else { + publishedAt > lastCheckDate, + publishedAt <= Date() else { return nil } dateFilteredCount += 1 @@ -318,9 +320,10 @@ final class BackgroundFeedRefresher { } 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, - publishedAt > lastCheckDate else { + publishedAt > lastCheckDate, + publishedAt <= Date() else { return nil } dateFilteredCount += 1 @@ -377,6 +380,16 @@ final class BackgroundFeedRefresher { 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 if let appEnvironment { let watchEntries = appEnvironment.dataManager.watchEntriesMap()