Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches: [main, agent/community-api]
pull_request:

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: server/package-lock.json
- name: Validate extension
run: |
node --check content.js
node --check popup.js
node --check options.js
node scripts/validate-extension.mjs
- name: Install Worker dependencies
working-directory: server
run: npm ci
- name: Test Worker
working-directory: server
run: npm test
- name: Type-check Worker
working-directory: server
run: npm run check
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Package release

on:
push:
tags: ['v*']

permissions:
contents: write

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: server/package-lock.json
- name: Verify tag and extension
run: |
node scripts/validate-extension.mjs
node -e "const v=require('./manifest.json').version;if(process.env.GITHUB_REF_NAME!=='v'+v)throw new Error('Tag 与 manifest 版本不一致')"
- name: Test Worker
working-directory: server
run: npm ci && npm test && npm run check
- name: Build Chrome ZIP
run: |
mkdir -p dist/douyin-ad-skipper
cp manifest.json content.js content.css popup.html popup.js popup.css options.html options.js options.css LICENSE PRIVACY.md SECURITY.md dist/douyin-ad-skipper/
mkdir -p dist/douyin-ad-skipper/icons
cp icons/icon-16.png icons/icon-32.png icons/icon-48.png icons/icon-128.png dist/douyin-ad-skipper/icons/
cd dist/douyin-ad-skipper
zip -r ../douyin-ad-skipper-${GITHUB_REF_NAME}.zip .
- name: Publish GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "$GITHUB_REF_NAME" "dist/douyin-ad-skipper-${GITHUB_REF_NAME}.zip" --generate-notes --verify-tag
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# 更新日志

## 0.6.2 - 2026-08-12

- 新增 GitHub Actions 持续集成,自动检查扩展脚本、清单、Worker 测试与类型。
- 新增基于 `v版本号` 标签的 Chrome ZIP 自动打包与 GitHub Release 发布流程。
- 更新隐私、安全和服务端文档,明确匿名身份、IP 限流和数据保留边界。

## 0.6.1 - 2026-08-12

- 修复 D1 分类约束,使自我推广和互动提醒可正常提交。
- 社区写接口限制 JSON 请求体大小,并增加身份与 IP 双层限流。
- 禁止投稿者给自己的片段投票或举报,并自动清理过期限流记录。
- 导入备份时限制文件、作品和片段数量,严格校验时间、分类、设置与文本长度。
- 完成旧版全局社区策略到分类策略的数据迁移,并移除废弃设置。

## 0.6.0 - 2026-08-09

- 提交成功后立即把服务器确认片段写入当前页面缓存,进度条颜色无需刷新即可连续显示。
- 提交社区前必须逐段完成播放器预览。
- 跳过片段后可撤销;社区片段可直接赞成或反对。
- 撤销后短时间暂停该片段自动跳过,避免立即重复触发。
- 社区服务器记录去重后的有效跳过,展示实际帮助人数、跳过次数和节省时间。
- 自动合并重叠或几乎相邻的本地草稿,并要求重新预览合并结果。
- 社区片段仅保留最近 100 个作品的页面内存缓存,不写入本地磁盘。
- 片段管理支持修改本地草稿时间,编辑后必须重新预览。
- 跳过社区片段后可分别赞成、反对或按原因举报。
- 社区片段支持自动跳过、手动提示和不处理三种行为模式。
- 新增播放器快捷键总开关及开始/结束、取消、提交快捷操作。
- 进度条片段提供状态与起止时间说明;不足 1 秒的片段保存前会二次确认。
- 播放查询改用作品 ID 的 SHA-256 哈希,不再发送原始作品 ID。
- 快捷键支持在选项页重新录制;撤销跳过后可一键重新跳过。
- 新增赞助、自我推广、互动提醒三种片段分类及进度条颜色。
- 片段管理支持起点和终点 ±0.1 秒精细调整。
- 按分类分别设置跳过行为:赞助默认自动跳过,自我推广和互动提醒默认显示提示。
- 关于页版本号改为从扩展清单自动读取,发布更新时同步升级至 0.6.0。

## 0.4.0 - 2026-08-05

- 修复切换视频后播放器标记按钮延迟或缺失的问题。
Expand Down
19 changes: 13 additions & 6 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# 隐私说明

最后更新:2026-08-05
最后更新:2026-08-12

## 当前版本(v0.5.0)
## 当前版本(v0.6.2)

扩展只在 `https://www.douyin.com/*` 页面运行,并使用 Chrome `storage` 权限保存:

- 功能开关;
- 累计跳过次数;
- 用户手动创建的作品 ID、片段起止时间、标题、作者、作品链接和创建时间。
- 尚未提交的本地草稿(作品 ID、片段起止时间、标题、作者、作品链接和创建时间)。

以上数据保存在 Chrome 扩展本地存储中。社区共享功能默认启用并连接项目公共 API,可随时在选项页关闭。关闭时:
功能开关、累计跳过次数和未提交草稿保存在 Chrome 扩展本地存储中。匿名贡献者 ID 保存在 Chrome 同步存储中,以便同一 Chrome 账号在其他电脑识别自己的社区贡献。已提交片段存放在社区服务器,不再重复保存在本地。社区共享功能可随时在选项页关闭。关闭时:

- 不向项目维护者或第三方服务器发送数据;
- 不收集 Cookie、登录凭据或账号私信;
Expand All @@ -23,11 +23,18 @@

社区功能启用时:

- 浏览视频时会用作品 ID 查询该作品的可用片段,不上传完整观看历史;
- 浏览视频时会在本地计算作品 ID 的 SHA-256 哈希,并使用哈希查询片段;不发送原始作品 ID,也不上传完整观看历史;
- 只有用户在播放器提交菜单中明确点击“提交到社区”时,才上传作品 ID、片段起止时间、视频时长、分类和随机请求 ID;
- 公共数据不包含抖音用户名或账号标识;
- 请求使用扩展随机生成的匿名客户端标识;服务端示例会将其与服务端盐、来源 IP 一起散列后用于限流,不保存原始客户端标识;
- 请求使用扩展随机生成、由 Chrome 同步的匿名贡献者 ID;服务端只保存加盐哈希,不接收 Chrome、QQ 或抖音账号信息;
- 来源 IP 仅参与限流和基础设施安全日志,不再作为贡献者身份的一部分;
- 选项页会通过匿名贡献者 ID 拉取该用户已提交的片段和累计贡献时长;
- 社区片段成功跳过且未立即撤销时,会上传片段 ID;服务器按匿名贡献者哈希、片段和日期去重,用于统计实际帮助人数、跳过次数和节省时间;
- 提供完全关闭远程查询与提交的选项;
- 实际服务器运营者仍需自行公布基础设施日志的保留期限和删除方式。

作品 ID 的 SHA-256 哈希用于减少查询时直接暴露原始 ID,但作品 ID 的取值空间有限,哈希不应被理解为完全匿名或不可反查。公共社区 API 会对写入请求同时按匿名身份哈希与来源 IP 哈希限流;限流记录仅用于防滥用,应用数据库会抽样清理超过 48 小时的限流桶。Cloudflare 基础设施日志的保存与处理还受 Cloudflare 服务配置和政策约束。

匿名贡献者 ID 不是登录账号或安全凭证。能够读取 Chrome 同步数据的人可能以同一匿名身份查询贡献记录,因此扩展不会使用该 ID 保存 Cookie、密码、支付资料或其他敏感信息。

如发现隐私问题,请按照 [SECURITY.md](SECURITY.md) 私下报告。
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
- 识别“广告 / 商业推广 / 广告推广 / 推广”等明确页面标识并切换到下一条。
- 在抖音播放器控制栏内创建广告片段起止时间。
- 在播放器进度条中显示已标记片段:待提交为深绿色,已提交/可信片段为亮绿色。
- 再次播放同一作品时自动跳过本地标记片段。
- 再次播放同一作品时自动跳过本地草稿或社区片段。
- 可从播放器内的提交菜单把待提交片段发送到自建社区服务。
- 在选项页查看、搜索、打开和删除本地片段。
- 在选项页查看本地草稿、云端片段和累计贡献时长。
- 导入、导出本地数据。
- 完全本地运行,不上传浏览记录。
- 未提交草稿留在本地;已提交片段以社区服务器为准,不上传完整浏览记录。

## 安装本地测试版

Expand All @@ -25,20 +25,35 @@
5. 选择本仓库根目录。
6. 打开或刷新 `https://www.douyin.com/`。

也可以从 GitHub Releases 下载自动生成的 Chrome ZIP,解压后按上述方式加载。Release 标签必须与 `manifest.json` 版本一致,例如版本 `0.6.2` 使用标签 `v0.6.2`。

## 创建片段

播放器底部清晰度按钮左侧会显示片段标记图标:

1. 广告开始时点击“片段从当前开始”。
2. 图标切换为红色取消按钮和青色结束按钮。
3. 广告结束时点击“片段现在结束”,片段立即保存在本地,并以深绿色显示在进度条上。
3. 广告结束时点击“片段现在结束”,片段先作为本地草稿保存,并以深绿色显示在进度条上。
4. 此时控制栏会出现“提交”和“删除最后一个未提交片段”按钮;点击提交会打开播放器内菜单。
5. 提交成功后片段改为亮绿色;点击扩展图标 →“片段管理”也可查看记录。
5. 在提交菜单逐段点击“预览”;播放器实际越过片段末尾后才允许提交。
6. 提交成功后本地草稿会删除,亮绿色片段和管理列表从社区服务读取。

自动跳过时间片段后,提示条会短暂提供“撤销”按钮;社区片段还可直接赞成、反对或举报。

片段管理页可以修改本地草稿的开始和结束时间。修改会清除预览状态,必须返回对应视频重新预览;社区片段跳过后可赞成、反对或按“时间错误、不是广告、视频不符、滥用”举报。

社区片段支持“自动跳过、显示提示手动跳过、不处理”三种模式。播放器快捷键默认为:`Alt+Z` 开始/结束片段、`Alt+X` 取消创建、`Alt+Enter` 打开提交菜单;可在选项页的“快捷键”中重新录制,在输入框中不会触发。

片段可标记为“赞助/广告”“自我推广”或“互动提醒”,进度条会使用不同颜色。片段管理页支持对起点和终点进行 ±0.1 秒微调;时间变化后需要重新预览才能上传。

和 BilibiliSponsorBlock 的默认行为一致:赞助/广告自动跳过,自我推广与互动提醒先显示提示,也可以在“社区共享”页分别改成自动跳过或不处理。

## 社区共享

扩展默认连接项目公共社区 API,普通用户安装后即可查询和提交。新片段提交成功后立即供其他用户使用,投票与举报用于事后纠错:

扩展会把随机匿名贡献者 ID 保存到 Chrome 同步存储;使用同一 Chrome 账号安装扩展后,可在另一台电脑看到自己的云端片段与贡献统计。服务器只保存该 ID 的加盐哈希。

```text
用户提交片段 → API 校验 → 立即共享并自动跳过
↓
Expand Down Expand Up @@ -70,6 +85,8 @@

欢迎提交问题、页面适配、交互改进和共享服务设计。开始前请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 和 [SECURITY.md](SECURITY.md)。

每次推送和 Pull Request 都会运行扩展语法、清单约束、Worker 测试和 TypeScript 检查;推送 `v版本号` 标签后会自动生成不含服务器配置的 Chrome ZIP,并发布到 GitHub Releases。

## 许可证

扩展代码使用 [GNU GPL v3](LICENSE) 发布。共享服务端使用 [AGPL-3.0](server/LICENSE)。社区片段数据的开放许可将在服务上线前确定。
Expand Down
11 changes: 10 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 支持范围

项目仍处于早期阶段,安全修复优先应用于最新版本。
项目仍处于早期阶段,安全修复只保证优先应用于最新版本。报告问题前请先在 `chrome://extensions` 确认版本号,并避免在生产社区 API 上进行批量或破坏性测试。

## 报告漏洞

Expand All @@ -14,4 +14,13 @@

在 GitHub 仓库启用 Private vulnerability reporting 后,请使用仓库 Security 页面私下报告。在该功能启用前,请联系仓库维护者,并只提供最少必要信息。

私密报告入口:https://github.com/bbnotcode/douyin-ad-skipper/security/advisories/new

报告中请包含受影响版本、复现步骤、影响和建议修复方式。请勿访问他人数据或进行破坏性测试。

## 安全边界

- `X-Client-ID` 是匿名贡献标识,不是认证令牌;不得用于保存或授权敏感数据。
- 社区 API 是公开服务,主要通过输入校验、请求体限制、身份/IP 限流和社区纠错降低滥用风险。
- `.dev.vars`、Cloudflare 密钥、数据库导出和浏览器 Cookie 不得提交到仓库或 Issue。
- 扩展发布包只包含运行所需文件,不包含 Worker 配置、数据库迁移或本地开发文件。
19 changes: 16 additions & 3 deletions content.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,14 @@
min-width: 1px;
background: #00d400;
opacity: .82;
pointer-events: auto;
}

.das-preview-segment.das-pending { background: #007800; }
.das-preview-segment.das-candidate { background: #b8a900; }
.das-preview-segment.das-category-sponsor { background:#00d400; }
.das-preview-segment.das-category-selfpromo { background:#f4d03f; }
.das-preview-segment.das-category-interaction { background:#d15cff; }
.das-preview-segment.das-pending { opacity:.5; }
.das-preview-segment.das-candidate { opacity:.68; background-image:repeating-linear-gradient(135deg,transparent 0 4px,rgba(0,0,0,.28) 4px 8px); }

.das-submission-menu {
position: absolute;
Expand All @@ -122,9 +126,18 @@
.das-submission-menu header button { border: 0; color: #aaa; background: transparent; font-size: 24px; cursor: pointer; }
.das-submission-menu p { margin: 10px 0; color: #b8bac1; }
.das-submission-menu ol { max-height: 180px; margin: 0; padding: 0; overflow: auto; list-style: none; }
.das-submission-menu li { display: flex; justify-content: space-between; padding: 8px 0; border-top: 1px solid rgba(255,255,255,.08); }
.das-submission-menu li { display: flex; align-items:center; justify-content: space-between; gap:12px; padding: 8px 0; border-top: 1px solid rgba(255,255,255,.08); }
.das-submission-menu li span { display:flex; flex-direction:column; }
.das-submission-menu li small { margin-top:2px; color:#8f939d; }
.das-submission-menu li button { padding:5px 10px; border:1px solid rgba(37,244,238,.3); border-radius:6px; color:#25f4ee; background:rgba(37,244,238,.08); cursor:pointer; }
.das-submission-menu li select { max-width:105px; padding:5px 6px; border:1px solid rgba(255,255,255,.18); border-radius:6px; color:#fff; background:#292c33; }
.das-submission-menu li em { color: #9da0a9; font-style: normal; }
.das-submission-actions { display: flex; gap: 8px; margin-top: 14px; }
.das-submission-actions button { flex: 1; padding: 9px 10px; border: 0; border-radius: 7px; color: #fff; background: #34363d; cursor: pointer; }
.das-submission-actions button:first-child { color: #161823; background: #25f4ee; font-weight: 700; }
.das-submission-actions button:disabled { opacity: .55; cursor: wait; }

.das-toast-actions { display:flex; gap:6px; margin-left:12px; pointer-events:auto; }
.das-toast-actions button { padding:4px 8px; border:1px solid rgba(255,255,255,.24); border-radius:6px; color:#fff; background:rgba(255,255,255,.1); cursor:pointer; }
.das-toast-actions button:hover { border-color:#25f4ee; color:#25f4ee; }
.das-toast-actions button:disabled { opacity:.55; cursor:default; }
Loading
Loading