百因必有果
說一下我為什麼要做個抖音視訊去水印工具,其實是因為我的沙雕女友,她居然剛我~
有天晚上她在抖音看見一個非常具有 教育意義
的視訊,“男人疼媳婦就該承包全部家務活”,然後它就想把視訊下載下來,分享到她的姐妹群交流 馭夫
心得。
可是大家都知道抖音下載的視訊是帶水印,作為一個重度強迫症選手這是不被允許的,沒辦法那就找找有沒有去水印工具吧,找了一圈要不就是收費,要麼下載不下來,主上臉上的笑容也在逐漸消失。
我在邊上調侃了一句:也沒多難,要不我給你做一個!“你行嗎?” 然後投來了一個不屑的眼神。
哎呀!本來就開個玩笑,居然說我不行,這就不能忍了,我得證明給你看看!男人嘛,就受不了這話
先看下我做的去水印工具線上預覽效果: 47.93.6.5:8888/index
下邊和大家一起分析下做這個去水印工具的思路,很多人乍一聽 去水印
,下意識的覺得是一種什麼牛比的演算法,其實這是一種假象~
刨根問底
雖說要爭口氣,可剛開始做的時候我也真是一臉懵逼,因為根本不知道該從哪入手,去水印什麼原理啊?難不成我還要寫個演算法?
找了一個抖音視訊的分享連結,一點點分析,不難發現這是個經過處理的短連結,那這個短連結一定會重定向到真實的視訊地址 URL
。
https://v.douyin.com/JSkuhE4/
瀏覽器中輸入短連結得到了下邊這個 URL
,以我的經驗判斷URL
中的 6820792802394262795
很有可能是視訊的唯一ID,而唯一ID通常用來作為獲取詳情介面的入參,哎嘿~ 好像有點頭緒了。
https://www.iesdouyin.com/share/video/6820792802394262795/
趕緊祭出 F12
大法開啟控制檯,在眾多請求中發現這麼一個介面,它居然用到了上邊的唯一ID。
https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=6820792802394262795
更驚喜的是介面返回的資料那叫一個詳細,作者資訊、音訊地址、視訊地址、平面圖都有。但唯獨沒有無水印的視訊 URL
。
只找到一個有水印的視訊 URL
,有點小失落,我又看了看這個地址,發現 wm
和我專案名有點像啊,不就是watermark
水印的縮寫嗎?
https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f030000bqk54kg2saj3lso3oh20&ratio=720p&line=0
好像又看到了一絲希望,我趕緊修改URL
在瀏覽器中又試了一下,果然真的沒水印了。
https://aweme.snssdk.com/aweme/v1/play/?video_id=v0200f030000bqk54kg2saj3lso3oh20&ratio=720p&line=0
到這才發現抖音去水印
簡單的讓人感動,哈哈哈~
身體力行
既然原理都清晰了,剩下的就是一步一步實現功能了,原理看著挺簡單的,但實現中還是遇到一點點小坑,浪費了不少時間。
實現過程只有簡單的三步:
- 1、從輸入框中過濾取出視訊短連線
- 2、短連線傳到後端解析出無水印的視訊
URL
- 3、視訊
URL
傳遞給前端預覽、下載
後端並沒有什麼難度,一步一步按照上邊分析的流程解析真實視訊 URL
就可以了。
注意 :我們想得到的地址
URL
,都是當前短連線URL
經過重定向後的URL
。而抖音有些連結是不支援瀏覽器訪問的,所以要手動修改User-agent
屬性模擬移動端訪問才可以。
/**
* @param url
* @author xiaofu
* @description 獲取當前連結重定向後的url
* @date 2020/9/15 12:43
*/
public static String getLocation(String url) {
try {
URL serverUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
conn.setRequestMethod("GET");
conn.setInstanceFollowRedirects(false);
conn.setRequestProperty("User-agent", "ua");//模擬手機連線
conn.connect();
String location = conn.getHeaderField("Location");
return location;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
下邊是完整的後端實現,可以看到程式碼量非常的少。
/**
* @author xiaofu-公眾號:程式設計師內點事
* @description 抖音無水印視訊下載
* @date 2020/9/15 18:44
*/
@Slf4j
@Controller
public class DYController {
public static String DOU_YIN_BASE_URL = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=";
/**
* @param url
* @author xiaofu
* @description 解析抖音無水印視訊
* @date 2020/9/15 12:43
*/
@RequestMapping("/parseVideoUrl")
@ResponseBody
public String parseVideoUrl(@RequestBody String url) throws Exception {
DYDto dyDto = new DYDto();
try {
url = URLDecoder.decode(url).replace("url=", "");
/**
* 1、短連線重定向後的 URL
*/
String redirectUrl = CommonUtils.getLocation(url);
/**
* 2、拿到視訊對應的 ItemId
*/
String videoUrl = "";
String musicUrl = "";
String videoPic = "";
String desc = "";
if (!StringUtils.isEmpty(redirectUrl)) {
/**
* 3、用 ItemId 拿視訊的詳細資訊,包括無水印視訊url
*/
String itemId = CommonUtils.matchNo(redirectUrl);
StringBuilder sb = new StringBuilder();
sb.append(DOU_YIN_BASE_URL).append(itemId);
String videoResult = CommonUtils.httpGet(sb.toString());
DYResult dyResult = JSON.parseObject(videoResult, DYResult.class);
/**
* 4、無水印視訊 url
*/
videoUrl = dyResult.getItem_list().get(0)
.getVideo().getPlay_addr().getUrl_list().get(0)
.replace("playwm", "play");
String videoRedirectUrl = CommonUtils.getLocation(videoUrl);
dyDto.setVideoUrl(videoRedirectUrl);
/**
* 5、音訊 url
*/
musicUrl = dyResult.getItem_list().get(0).getMusic().getPlay_url().getUri();
dyDto.setMusicUrl(musicUrl);
/**
* 6、封面
*/
videoPic = dyResult.getItem_list().get(0).getVideo().getDynamic_cover().getUrl_list().get(0);
dyDto.setVideoPic(videoPic);
/**
* 7、視訊文案
*/
desc = dyResult.getItem_list().get(0).getDesc();
dyDto.setDesc(desc);
}
} catch (Exception e) {
log.error("去水印異常 {}", e);
}
return JSON.toJSONString(dyDto);
}
}
前端實現也比較簡單,拿到後端解析出來的視訊URL
預覽播放、下載就OK了。
為快速實現我用了老古董JQuery
,我這個年紀的人對它感情還是很深厚的,UI
框架用的 layer.js
。原始碼後邊會分享給大家,就不全貼出來了。
$.ajax({
url: '/parseVideoUrl',
type: 'POST',
data: {"url": link},
success: function (data) {
$('.qsy-submit').attr('disabled', false);
try {
var rows = JSON.parse(data);
layer.close(index);
layer.open({
type: 1,
title: false,
closeBtn: 1,
shadeClose: true,
skin: 'yourclass',
content: `<div style="overflow:hidden;height: 580px;width: 350px;"><div><div class="popButton"><a href="###" rel="noopener nofollow noreferrer" onclick="downloadVideo('${rows['videoUrl']}','${rows['desc']}')"><button class="layui-bg-red layui-btn-sm layui-btn">下載視訊</button></a></div><div class="popButton"><textarea id="videourl" cols="1" rows="1" style="height:0;width:0;position: absolute;">${rows['videoUrl']}</textarea><button class="layui-btn-sm layui-bg-blue layui-btn" onclick="copy('videourl')">複製連結</button></div><div class="popButton"><a href="###" rel="noopener nofollow noreferrer" onclick="downloadVideo('${rows['musicUrl']}','${rows['desc']}')"><button class="layui-btn-sm layui-btn">下載音訊</button></a></div><video id="video" width="360px" height="500px" src="${rows['videoUrl']}" controls = "true" poster="${rows['videoPic']}" preload="auto" webkit-playsinline="true" playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" style="object-fit:fill"><source src="${rows['videoUrl']}" type="video/mp4"> </video></div></div>`
//content: `<video id="video" src="${rows['videoUrl']}" controls = "true" poster="${rows['videoPic']}" preload="auto" webkit-playsinline="true" playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" style="object-fit:fill"><source src="${rows['videoUrl']}" type="video/mp4"> </video>`
});
} catch (error) {
layer.alert('錯誤資訊:' + error, {
title: '異常',
skin: 'layui-layer-lan',
closeBtn: 0,
anim: 4 //動畫型別
});
return false;
}
},
error: function (err) {
console.log(err);
layer.close(index);
$('.qsy-submit').attr('disabled', false);
},
done: function () {
layer.close(index);
}
})
})
注意:我們在自己的網站中引用其它網站的資源
URL
,由於不在同一個域名下referrer
不同,通常會遇到三方網站的防盜鏈攔截,所以要想正常訪問三方資源,必須要隱藏請求的referrer
,頁面中設定如下引數。
<!-- 解決訪問視訊url 請求403異常 -->
<meta name="referrer" content="no-referrer"/>
還簡單做了下移動端適配,樣式看著還可以,但是功能使用起來有點差強人意,後邊在做優化了。
總結
很多東西就是這樣,沒認真研究之前總感覺深不可測,可一旦接觸到技術的本質,又開始笑自己之前好蠢,懂與不懂有時就查那麼一層窗戶紙。
好了今天就到這,本文原始碼在 本人公眾號【程式設計師內點事】回覆【原始碼】自取
整理了幾百本各類技術電子書,送給小夥伴們。關注公號回覆【666】自行領取。和一些小夥伴們建了一個技術交流群,一起探討技術、分享技術資料,旨在共同學習進步,如果感興趣就加入我們吧!
本作品採用《CC 協議》,轉載必須註明作者和本文連結