diff --git a/BilibiliLive/Component/Settings.swift b/BilibiliLive/Component/Settings.swift index cccdd6b..491ea1a 100644 --- a/BilibiliLive/Component/Settings.swift +++ b/BilibiliLive/Component/Settings.swift @@ -110,6 +110,9 @@ enum Settings { @UserDefault("Settings.danmuFilter", defaultValue: false) static var enableDanmuFilter: Bool + + @UserDefault("Settings.danmuRemoveDup", defaultValue: false) + static var enableDanmuRemoveDup: Bool } struct MediaQuality { diff --git a/BilibiliLive/Component/Video/NewVideoPlayerViewModel.swift b/BilibiliLive/Component/Video/NewVideoPlayerViewModel.swift index 6a43f4b..9af6df5 100644 --- a/BilibiliLive/Component/Video/NewVideoPlayerViewModel.swift +++ b/BilibiliLive/Component/Video/NewVideoPlayerViewModel.swift @@ -28,7 +28,8 @@ class VideoPlayerViewModel { var nextProvider: VideoNextProvider? private var playInfo: PlayInfo - private let danmuProvider = VideoDanmuProvider() + private let danmuProvider = VideoDanmuProvider(enableDanmuFilter: Settings.enableDanmuFilter, + enableDanmuRemoveDup: Settings.enableDanmuRemoveDup) private var videoDetail: VideoDetail? private var cancellable = Set() private var playPlugin: CommonPlayerPlugin? diff --git a/BilibiliLive/Component/Video/VideoDanmuProvider.swift b/BilibiliLive/Component/Video/VideoDanmuProvider.swift index c746ed8..0e9e9a2 100644 --- a/BilibiliLive/Component/Video/VideoDanmuProvider.swift +++ b/BilibiliLive/Component/Video/VideoDanmuProvider.swift @@ -37,7 +37,9 @@ struct Danmu: Codable { } class VideoDanmuProvider: DanmuProviderProtocol { - var cid: Int! + private var cid: Int! + private let enableDanmuFilter: Bool + private let enableDanmuRemoveDup: Bool private var allDanmus = [Danmu]() private var playingDanmus = [Danmu]() @@ -59,6 +61,11 @@ class VideoDanmuProvider: DanmuProviderProtocol { private let segmentDuration = 60 * 6 private func getSegmentIdx(time: TimeInterval) -> Int { Int(time) / segmentDuration + 1 } + init(enableDanmuFilter: Bool, enableDanmuRemoveDup: Bool) { + self.enableDanmuFilter = enableDanmuFilter + self.enableDanmuRemoveDup = enableDanmuRemoveDup + } + func initVideo(cid id: Int, startPos: Int) async { cid = id upDanmus.removeAll() @@ -108,7 +115,12 @@ class VideoDanmuProvider: DanmuProviderProtocol { var dms = reply.elems .filter { $0.mode <= 5 } - if Settings.enableDanmuFilter { + if enableDanmuRemoveDup { + var set = Set() + dms = dms.filter { set.insert($0.content).inserted } + } + + if enableDanmuFilter { dms = dms.filter { VideoDanmuFilter.shared.accept($0.content) } diff --git a/BilibiliLive/Module/Live/LiveDanMuProvider.swift b/BilibiliLive/Module/Live/LiveDanMuProvider.swift index e787d0a..ab8589a 100644 --- a/BilibiliLive/Module/Live/LiveDanMuProvider.swift +++ b/BilibiliLive/Module/Live/LiveDanMuProvider.swift @@ -13,15 +13,19 @@ import SwiftyJSON class LiveDanMuProvider: DanmuProviderProtocol { let observerPlayerTime = false + let enableDanmuRemoveDup: Bool var onSendTextModel = PassthroughSubject() private var websocket: WebSocketRequest? private var heartBeatTimer: Timer? private let roomID: Int private var token = "" + private var danmuSet = Set() + private var danmuSetClearTimer: Timer? - init(roomID: Int) { + init(roomID: Int, removeDup: Bool) { self.roomID = roomID + enableDanmuRemoveDup = removeDup } deinit { @@ -31,6 +35,7 @@ class LiveDanMuProvider: DanmuProviderProtocol { func playerTimeChange(time: TimeInterval) {} func start() async throws { + stop() let info = try await WebRequest.requestDanmuServerInfo(roomID: roomID) guard let server = info.host_list.first else { Logger.info("Get room server info Fail") @@ -45,11 +50,20 @@ class LiveDanMuProvider: DanmuProviderProtocol { websocket = AF.webSocketRequest(to: "wss://\(server.host):\(server.wss_port)/sub", headers: afheaders).streamMessageEvents { [weak self] event in self?.handleWebsocketEvent(event: event) } + + if enableDanmuRemoveDup { + danmuSetClearTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { + [weak self] _ in + self?.danmuSet.removeAll(keepingCapacity: true) + } + } } func stop() { websocket?.close(sending: .normalClosure) heartBeatTimer?.invalidate() + danmuSet.removeAll() + danmuSetClearTimer?.invalidate() } private func setupHeartBeat() { @@ -122,7 +136,7 @@ extension LiveDanMuProvider { case "DANMU_MSG": if let str = json["info"][1].string { let model = DanmakuTextCellModel(str: str) - onSendTextModel.send(model) + sentDanmuModel(model) } case "DM_INTERACTION": guard let data = json["data"]["data"].string else { return } @@ -130,7 +144,7 @@ extension LiveDanMuProvider { for combo in comboArr.arrayValue { if let str = combo["content"].string { let model = DanmakuTextCellModel(str: str) - onSendTextModel.send(model) + sentDanmuModel(model) } } case "SUPER_CHAT_MESSAGE": @@ -138,6 +152,7 @@ extension LiveDanMuProvider { let model = DanmakuTextCellModel(str: str) model.type = .top model.displayTime = 60 + sentDanmuModel(model) } default: break @@ -145,12 +160,14 @@ extension LiveDanMuProvider { } } - private func getDanMu(data: [JSON]) -> [String] { - return data.filter { - $0["cmd"].stringValue == "DANMU_MSG" - }.compactMap { json in - json["info"][1].string + private func sentDanmuModel(_ model: DanmakuTextCellModel) { + if enableDanmuRemoveDup { + if danmuSet.contains(model.text) { + return + } + danmuSet.insert(model.text) } + onSendTextModel.send(model) } } diff --git a/BilibiliLive/Module/Live/LivePlayerViewModel.swift b/BilibiliLive/Module/Live/LivePlayerViewModel.swift index a91a5c4..6a6a6b3 100644 --- a/BilibiliLive/Module/Live/LivePlayerViewModel.swift +++ b/BilibiliLive/Module/Live/LivePlayerViewModel.swift @@ -167,7 +167,7 @@ class LivePlayerViewModel { } @MainActor private func initDanmu() async -> [CommonPlayerPlugin] { - danMuProvider = LiveDanMuProvider(roomID: roomID) + danMuProvider = LiveDanMuProvider(roomID: roomID, removeDup: Settings.enableDanmuRemoveDup) let danmuPlugin = DanmuViewPlugin(provider: danMuProvider!) try? await danMuProvider?.start() diff --git a/BilibiliLive/Module/Personal/SettingsViewController.swift b/BilibiliLive/Module/Personal/SettingsViewController.swift index f152dc6..82ff1fd 100644 --- a/BilibiliLive/Module/Personal/SettingsViewController.swift +++ b/BilibiliLive/Module/Personal/SettingsViewController.swift @@ -186,6 +186,7 @@ class SettingsViewController: UIViewController { } } } + Toggle(title: "移除重复弹幕", setting: Settings.enableDanmuRemoveDup, onChange: Settings.enableDanmuRemoveDup.toggle()) Actions(title: "弹幕大小", message: "默认为36", current: Settings.danmuSize.title, options: DanmuSize.allCases, optionString: DanmuSize.allCases.map({ $0.title })) { Settings.danmuSize = $0 }