Skip to content

Commit

Permalink
feat: 支持屏蔽词同步
Browse files Browse the repository at this point in the history
  • Loading branch information
yichengchen committed Dec 13, 2024
1 parent e49f52f commit 69b834c
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 3 deletions.
4 changes: 4 additions & 0 deletions BilibiliLive.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
49A441CD293F6DFD0007606C /* FollowUpsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A441CC293F6DFD0007606C /* FollowUpsViewController.swift */; };
49ADA8392D0BCD79004801EF /* MainActor+Safe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49ADA8382D0BCD71004801EF /* MainActor+Safe.swift */; };
49ADA83D2D0C0FC2004801EF /* BvidConvertor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49ADA83C2D0C0FC2004801EF /* BvidConvertor.swift */; };
49ADA83F2D0C1ADF004801EF /* VideoDanmuFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49ADA83E2D0C1ADF004801EF /* VideoDanmuFilter.swift */; };
49D250A02C118FA700173908 /* URLPlayPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D2509F2C118FA700173908 /* URLPlayPlugin.swift */; };
49D250A22C11A82B00173908 /* AVPlayerMetaUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D250A12C11A82B00173908 /* AVPlayerMetaUtils.swift */; };
49D39F28263AD40000F14497 /* WebRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D39F27263AD40000F14497 /* WebRequest.swift */; };
Expand Down Expand Up @@ -301,6 +302,7 @@
49A441CC293F6DFD0007606C /* FollowUpsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowUpsViewController.swift; sourceTree = "<group>"; };
49ADA8382D0BCD71004801EF /* MainActor+Safe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainActor+Safe.swift"; sourceTree = "<group>"; };
49ADA83C2D0C0FC2004801EF /* BvidConvertor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BvidConvertor.swift; sourceTree = "<group>"; };
49ADA83E2D0C1ADF004801EF /* VideoDanmuFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDanmuFilter.swift; sourceTree = "<group>"; };
49D2509F2C118FA700173908 /* URLPlayPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLPlayPlugin.swift; sourceTree = "<group>"; };
49D250A12C11A82B00173908 /* AVPlayerMetaUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerMetaUtils.swift; sourceTree = "<group>"; };
49D39F27263AD40000F14497 /* WebRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRequest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -421,6 +423,7 @@
2806E5212C16011B00164C10 /* ReplyCell.swift */,
2806E5222C16011B00164C10 /* ReplyCell.xib */,
49389D6128AFEA2900B9DAFD /* VideoDanmuProvider.swift */,
49ADA83E2D0C1ADF004801EF /* VideoDanmuFilter.swift */,
497CF2322C16EDAC006E1488 /* MaskProvider */,
492AD70C2BFF33DF007221C8 /* VideoPlayerViewController.swift */,
492AD70E2BFF6761007221C8 /* NewVideoPlayerViewModel.swift */,
Expand Down Expand Up @@ -918,6 +921,7 @@
AEA6AB1628FFE951007CE72E /* SettingsViewController.swift in Sources */,
49389D8928B0A1B700B9DAFD /* UIViewController+Ext.swift in Sources */,
AE2B41572914C02000BF2B0B /* Int.swift in Sources */,
49ADA83F2D0C1ADF004801EF /* VideoDanmuFilter.swift in Sources */,
498CF2932B63AABE0009793E /* memory.c in Sources */,
49ADA8392D0BCD79004801EF /* MainActor+Safe.swift in Sources */,
498CF2A72B63AABE0009793E /* decode.c in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions BilibiliLive/Component/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ enum Settings {

@UserDefaultCodable("Settings.SponsorBlockType", defaultValue: SponsorBlockType.none)
static var enableSponsorBlock: SponsorBlockType

@UserDefault("Settings.danmuFilter", defaultValue: false)
static var enableDanmuFilter: Bool
}

struct MediaQuality {
Expand Down
103 changes: 103 additions & 0 deletions BilibiliLive/Component/Video/VideoDanmuFilter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// VideoDanmuFilter.swift
// BilibiliLive
//
// Created by yicheng on 2024/12/13.
//

import UIKit

class VideoDanmuFilter {
static let shared = VideoDanmuFilter()

private var stringFilters = [String]()
private var regexFilters = [Regex<AnyRegexOutput>]()
private init() {
refreshCache(rules: VideoDanmuFilterStorage.filters)
}

func accept(_ danmu: String) -> Bool {
for filter in stringFilters {
if danmu.contains(filter) {
return false
}
}

for filter in regexFilters {
if danmu.contains(filter) {
return false
}
}
return true
}

func autoUpdate() {
if Date().timeIntervalSince(VideoDanmuFilterStorage.lastUpdate) > 60 * 60 * 24 {
Task {
await update()
}
}
}

@discardableResult
func update() async -> String {
VideoDanmuFilterStorage.lastUpdate = Date()
let data = await WebRequest.requestDanmuFilterList()
let rules = data.rule.filter({ $0.type == 0 || $0.type == 1 })
if !rules.isEmpty {
VideoDanmuFilterStorage.filters = rules
refreshCache(rules: rules)
}
return data.toast ?? ""
}

private func refreshCache(rules: [VideoDanmuFilterData.Rule]) {
stringFilters.removeAll()
regexFilters.removeAll()
rules.forEach { filter in
switch filter.type {
case 0:
stringFilters.append(filter.filter)
case 1:
if let regex = try? Regex(filter.filter) {
regexFilters.append(regex)
}
default:
break
}
}
}
}

private enum VideoDanmuFilterStorage {
@UserDefaultCodable("VideoDanmuFilter.filters", defaultValue: [])
static var filters: [VideoDanmuFilterData.Rule]

@UserDefault("VideoDanmuFilter.lastUpdate", defaultValue: Date(timeIntervalSince1970: 0))
static var lastUpdate: Date
}

private extension WebRequest.EndPoint {
static let danmuFilter = "https://api.bilibili.com/x/dm/filter/user"
}

private struct VideoDanmuFilterData: Codable {
struct Rule: Codable {
let filter: String
let type: Int
}

let rule: [Rule]
let toast: String?
}

private extension WebRequest {
static func requestDanmuFilterList() async -> VideoDanmuFilterData {
do {
let resp: VideoDanmuFilterData = try await request(url: EndPoint.danmuFilter)
return resp
} catch let err {
return VideoDanmuFilterData(rule: [], toast: "\(err)")
}
}
}
12 changes: 10 additions & 2 deletions BilibiliLive/Component/Video/VideoDanmuProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,17 @@ class VideoDanmuProvider: DanmuProviderProtocol {

var dms = reply.elems
.filter { $0.mode <= 5 }

if Settings.enableDanmuFilter {
dms = dms.filter {
VideoDanmuFilter.shared.accept($0.content)
}
}

var models = dms
.map { Danmu(dm: $0) }
dms.sort { $0.time < $1.time }
segmentDanmus[idx] = dms
models.sort { $0.time < $1.time }
segmentDanmus[idx] = models

Logger.debug("[dm] cid:\(cid!) sidx:\(idx) danmu cnt: \(dms.count)")
}
Expand Down
11 changes: 11 additions & 0 deletions BilibiliLive/Module/Personal/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,17 @@ class SettingsViewController: UIViewController {
}

SectionModel(title: "弹幕") {
Toggle(title: "用户自定义弹幕屏蔽", setting: Settings.enableDanmuFilter, onChange: Settings.enableDanmuFilter.toggle()) {
enable in
if enable {
Task {
let toast = await VideoDanmuFilter.shared.update()
let alert = UIAlertController(title: "同步结果", message: toast, preferredStyle: .alert)
alert.addAction(.init(title: "Ok", style: .cancel))
self.present(alert, animated: true)
}
}
}
Actions(title: "弹幕大小", message: "默认为36", current: Settings.danmuSize.title, options: DanmuSize.allCases, optionString: DanmuSize.allCases.map({ $0.title })) {
Settings.danmuSize = $0
}
Expand Down
3 changes: 2 additions & 1 deletion BilibiliLive/Request/WebRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,11 @@ enum WebRequest {
let object = try (decoder ?? JSONDecoder()).decode(T.self, from: data)
complete?(.success(object))
} catch let err {
print("decode fail:", err)
Logger.warn("decode fail: \(err)")
complete?(.failure(.decodeFail(message: err.localizedDescription + String(describing: err))))
}
case let .failure(err):
Logger.warn("request fail: \(err)")
complete?(.failure(err))
}
}
Expand Down

0 comments on commit 69b834c

Please sign in to comment.