微信小程序搜索“抖猫高清去水印” 即可使用

大家好!最近很多做小程序开发的朋友问我:“想做一个短视频去水印工具,从前端到后端该怎么落地?” 确实,市面上的第三方去水印接口要么收费贵,要么不稳定,不如自己搭一套来得踏实。

今天就带大家从 0 到 1 实现微信小程序短视频去水印功能,涵盖前端交互、后端 API、部署上线全流程,代码直接复制就能用,新手也能轻松上手!

一、先搞懂:去水印不是 “消水印”,是 “找地址”

很多人以为去水印是用算法把视频里的水印擦掉,其实这是误区!主流平台的去水印核心是 “解析无水印源地址”—— 平台会同时存储带水印和无水印的视频文件,我们只要找到无水印文件的真实链接,就能直接播放或下载。

整个流程特别简单,就 4 步:

  1. 用户在小程序粘贴带水印视频链接(比如抖音 “复制链接”)
  1. 小程序把链接传给后端 API
  1. 后端识别平台(抖音 / 快手 / 小红书),解析出无水印地址
  1. 小程序拿到地址,展示视频并提供下载

是不是比想象中简单?接下来我们分前端、后端逐步实现。

二、前端小程序:30 分钟搭好交互界面

前端核心是 “让用户能输入链接、看结果、下载视频”,用微信原生小程序开发最省心,不用额外装框架。

1. 页面布局:简洁够用就好

在 pages/index/index.wxml 写页面结构,主要分 3 块:输入区、结果区、提示区,代码注释很详细,新手也能看懂:


<!-- 顶部标题 -->

<view class="page-title">短视频去水印工具</view>

<!-- 链接输入区 -->

<view class="input-wrap">

<input

class="url-input"

placeholder="粘贴抖音/快手/小红书视频链接"

bindinput="handleUrlInput"

value="{{videoUrl}}"

placeholder-class="input-placeholder"

/>

<!-- 解析按钮:没输入或解析中时禁用 -->

<button

class="parse-btn"

bindtap="handleParse"

disabled="{{!videoUrl || isParsing}}"

loading="{{isParsing}}"

>

{{isParsing ? "解析中..." : "立即解析"}}

</button>

</view>

<!-- 解析结果区:默认隐藏 -->

<view class="result-wrap" hidden="{{!isSuccess}}">

<!-- 视频封面 -->

<image class="video-cover" src="{{videoInfo.coverUrl}}" mode="widthFix" />

<!-- 视频标题 -->

<view class="video-title">{{videoInfo.title}}</view>

<!-- 无水印视频播放 -->

<video

class="video-player"

src="{{videoInfo.noWatermarkUrl}}"

controls

autoplay="{{false}}"

poster="{{videoInfo.coverUrl}}"

></video>

<!-- 下载按钮 -->

<button

class="download-btn"

bindtap="handleDownload"

open-type="openSetting"

bindopensetting="handleSettingChange"

>

保存到相册

</button>

<!-- 有效期提示 -->

<view class="tip-text">⚠️ 视频地址24小时内有效,失效请重新解析</view>

</view>

<!-- 错误提示:比如链接无效、解析失败 -->

<view class="error-tip" hidden="{{!errorMsg}}">{{errorMsg}}</view>

2. 样式美化:适配所有手机

在 pages/index/index.wxss 写样式,重点做好适配,避免在小屏手机上变形:


/* 全局样式重置 */

page {

background-color: #f5f5f5;

padding: 20rpx;

box-sizing: border-box;

}

.page-title {

font-size: 36rpx;

font-weight: bold;

color: #333;

text-align: center;

margin-bottom: 30rpx;

}

.input-wrap {

display: flex;

flex-direction: column;

gap: 20rpx;

margin-bottom: 30rpx;

}

.url-input {

padding: 22rpx;

background-color: #fff;

border-radius: 12rpx;

font-size: 28rpx;

color: #333;

border: 1rpx solid #eee;

}

.input-placeholder {

color: #999;

}

.parse-btn {

background-color: #27ae60;

color: #fff;

font-size: 30rpx;

padding: 20rpx 0;

border-radius: 12rpx;

}

.result-wrap {

background-color: #fff;

padding: 20rpx;

border-radius: 12rpx;

display: flex;

flex-direction: column;

gap: 20rpx;

}

.video-cover {

width: 100%;

border-radius: 8rpx;

}

.video-title {

font-size: 28rpx;

color: #333;

line-height: 44rpx;

max-height: 88rpx;

overflow: hidden;

text-overflow: ellipsis;

display: -webkit-box;

-webkit-line-clamp: 2;

-webkit-box-orient: vertical;

}

.video-player {

width: 100%;

height: 400rpx;

border-radius: 8rpx;

background-color: #fafafa;

}

.download-btn {

background-color: #3498db;

color: #fff;

font-size: 30rpx;

padding: 20rpx 0;

border-radius: 12rpx;

margin-top: 10rpx;

}

.tip-text {

font-size: 24rpx;

color: #666;

text-align: center;

margin-top: 10rpx;

}

.error-tip {

color: #e74c3c;

font-size: 26rpx;

text-align: center;

background-color: #fff;

padding: 20rpx;

border-radius: 12rpx;

}

3. 核心逻辑:处理输入、解析、下载

在 pages/index/index.js 写交互逻辑,重点解决 3 个问题:输入校验、调用后端 API、保存视频到相册。关键代码都加了注释,跟着写就行:


Page({

data: {

videoUrl: "", // 用户输入的视频链接

isParsing: false, // 解析中状态(避免重复点击)

isSuccess: false, // 解析成功状态

videoInfo: {}, // 解析结果:标题/无水印地址/封面

errorMsg: "" // 错误提示

},

// 1. 监听输入框变化,保存链接

handleUrlInput(e) {

this.setData({ videoUrl: e.detail.value.trim() });

// 输入时清空之前的错误提示

if (this.data.errorMsg) {

this.setData({ errorMsg: "" });

}

},

// 2. 点击解析按钮,调用后端API

async handleParse() {

const { videoUrl } = this.data;

// 先简单校验链接:必须包含http/https

if (!/^https?:\/\/.+/.test(videoUrl)) {

this.setData({ errorMsg: "请粘贴有效的视频链接(如https://v.douyin.com/xxx)" });

return;

}

// 初始化状态:显示加载中,隐藏结果

this.setData({ isParsing: true, isSuccess: false, errorMsg: "" });

try {

// 生成签名:防止后端接口被别人滥用(和后端约定好的规则)

const sign = await this.generateSign(videoUrl);

// 调用后端解析接口(替换成你的后端域名)

const res = await wx.request({

url: "https://你的后端域名/api/remove-watermark",

method: "POST",

data: { url: videoUrl, sign },

header: { "content-type": "application/json" }

});

// 处理后端返回结果

const { code, msg, data } = res.data;

if (code === 200) {

// 解析成功:展示结果

this.setData({

isSuccess: true,

videoInfo: data,

isParsing: false

});

} else {

// 解析失败:显示错误信息

this.setData({

errorMsg: msg || "解析失败,请检查链接是否有效",

isParsing: false

});

}

} catch (err) {

// 捕获网络错误(比如没网、后端挂了)

this.setData({

errorMsg: "网络异常,请检查网络后重试",

isParsing: false

});

}

},

// 3. 生成签名:和后端用一样的加密规则(防止接口被滥用)

generateSign(url) {

const secret = "你的密钥"; // 必须和后端.env里的SECRET_KEY一致

// 微信小程序自带crypto模块,不用额外安装

const crypto = require("crypto");

return crypto.createHmac("sha256", secret)

.update(JSON.stringify({ url }))

.digest("hex");

},

// 4. 点击下载,保存视频到相册

async handleDownload() {

const { noWatermarkUrl } = this.data.videoInfo;

// 先检查是否有保存相册的权限

const authRes = await wx.getSetting();

if (!authRes.authSetting["scope.writePhotosAlbum"]) {

// 没有权限:弹框提示用户授权

wx.showModal({

title: "需要授权",

content: "请允许保存视频到你的相册",

success: (res) => {

if (res.confirm) {

// 跳转到授权设置页

wx.openSetting({ withSubscriptions: true });

}

}

});

return;

}

// 有权限:开始下载视频

wx.showLoading({ title: "正在保存..." });

try {

// 第一步:下载视频到小程序临时目录

const downloadRes = await wx.downloadFile({

url: noWatermarkUrl,

filePath: `${wx.env.USER_DATA_PATH}/watermark_free.mp4` // 临时存储路径

});

// 第二步:保存到手机相册

await wx.saveVideoToPhotosAlbum({

filePath: downloadRes.filePath

});

wx.hideLoading();

wx.showToast({ title: "保存成功!已在相册查看" });

} catch (err) {

wx.hideLoading();

wx.showToast({ title: "保存失败,请重试", icon: "none" });

}

},

// 5. 监听授权设置变化(比如用户从设置页回来)

handleSettingChange(e) {

// 如果用户开启了保存相册权限,自动触发下载

if (e.detail.authSetting["scope.writePhotosAlbum"]) {

this.handleDownload();

}

}

});

4. 权限配置:必不可少的一步

在 app.json 里声明 “保存相册” 的权限,否则小程序会报错:


{

"pages": ["pages/index/index"],

"window": {

"navigationBarTitleText": "短视频去水印",

"navigationBarBackgroundColor": "#fff",

"navigationBarTextStyle": "black"

},

"permission": {

"scope.writePhotosAlbum": {

"desc": "用于保存无水印视频到你的手机相册"

}

},

"sitemapLocation": "sitemap.json"

}

三、后端 API:Node.js 快速搭服务

后端核心是 “解析不同平台的视频链接”,用 Node.js + Express 开发最快,代码量少,部署也简单。

1. 先搭项目骨架

首先创建项目文件夹,执行 npm init -y 初始化,然后安装需要的依赖:

# 安装核心依赖

npm install express axios cheerio cors dotenv express-rate-limit

# 开发时用的依赖(热更新)

npm install nodemon --save-dev

然后在 package.json 里加脚本,方便启动服务:


{

"name": "watermark-api",

"version": "1.0.0",

"main": "server.js",

"scripts": {

"start": "node server.js", // 线上启动

"dev": "nodemon server.js" // 本地开发(热更新)

},

"dependencies": {

"axios": "^1.6.2",

"cheerio": "^1.0.0-rc.12",

"cors": "^2.8.5",

"dotenv": "^16.3.1",

"express": "^4.18.2",

"express-rate-limit": "^7.1.4"

},

"devDependencies": {

"nodemon": "^3.0.1"

}

}

创建 .env 文件存储敏感配置(比如端口、密钥),不要把这些写在代码里:


PORT=3000 # 服务端口

SECRET_KEY=your_secret_key # 和前端一致的签名密钥

TIMEOUT=5000 # 请求超时时间(毫秒)

RATE_LIMIT=30 # 每IP15分钟最多30次请求(防滥用)

RATE_LIMIT_WINDOW=900000 # 15分钟=900000毫秒

2. 入口文件:启动服务

创建 server.js 作为入口,配置跨域、请求限制,定义核心路由:

require('dotenv').config(); // 加载.env里的配置

const express = require('express');

const cors = require('cors');

const rateLimit = require('express-rate-limit');

const parserController = require('./controllers/parserController');

const app = express();

const PORT = process.env.PORT || 3000;

// 1. 跨域配置:只允许微信小程序访问(安全)

app.use(cors({

origin: ['https://servicewechat.com'],

credentials: true

}));

// 2. 请求限制:防止别人恶意刷接口

const limiter = rateLimit({

windowMs: process.env.RATE_LIMIT_WINDOW,

max: process.env.RATE_LIMIT,

message: { code: 429, msg: '请求太频繁啦,15分钟后再试~' }

});

app.use('/api', limiter); // 只对/api开头的接口生效

// 3. 解析JSON请求体(前端传的是JSON格式)

app.use(express.json());

// 4. 核心路由:短视频去水印解析

app.post('/api/remove-watermark', parserController.parseVideo);

// 5. 启动服务

app.listen(PORT, () => {

console.log(`服务已启动,访问地址:http://localhost:${PORT}`);

});

3. 关键工具:签名验证、平台识别

先写 2 个工具函数,解决 “签名验证” 和 “识别视频平台” 的问题。

(1)签名验证:防止接口被滥用

创建 utils/signVerify.js,确保只有你的小程序能调用接口:

const crypto = require('crypto');

/**

* 验证前端传的签名是否合法

* @param {Object} data - 要验证的数据(比如{url: "视频链接"})

* @param {string} sign - 前端传的签名

* @returns {boolean} 签名是否合法

*/

function verifySign(data, sign) {

const secret = process.env.SECRET_KEY;

// 和前端一样的加密逻辑:Hmac-SHA256 + hex编码

const generatedSign = crypto.createHmac('sha256', secret)

.update(JSON.stringify(data))

.digest('hex');

return generatedSign === sign;

}

module.exports = verifySign;
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐