本文收錄在 GitHub 地址 https://github.com/chengxy-nds/Springboot-Notebookjavascript
百因必有果
說一下我爲何要作個抖音視頻去水印工具,實際上是由於個人沙雕女朋友,她竟然剛我~前端
有天晚上她在抖音看見一個很是具備 教育意義
的視頻,「男人疼媳婦就該承包所有家務活」,而後它就想把視頻下載下來,分享到她的姐妹羣交流 馭夫
心得。java
但是你們都知道抖音下載的視頻是帶水印,做爲一個重度強迫症選手這是不被容許的,沒辦法那就找找有沒有去水印工具吧,找了一圈要不就是收費,要麼下載不下來,主上臉上的笑容也在逐漸消失。git
我在邊上調侃了一句:也沒多難,要不我給你作一個!「你行嗎?」 而後投來了一個不屑的眼神。程序員
哎呀!原本就開個玩笑,竟然說我不行,這就不能忍了,我得證實給你看看!男人嘛,就受不了這話github
先看下我作的去水印工具線上預覽效果: http://47.93.6.5:8888/indexweb
下邊和你們一塊兒分析下作這個去水印工具的思路,不少人乍一聽 去水印
,下意識的以爲是一種什麼牛比的算法,其實這是一種假象~面試
刨根問底
雖然說要爭口氣,可剛開始作的時候我也真是一臉懵逼,由於根本不知道該從哪入手,去水印什麼原理啊?難不成我還要寫個算法?ajax
找了一個抖音視頻的分享連接,一點點分析,不難發現這是個通過處理的短連接,那這個短連接必定會重定向到真實的視頻地址 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
到這才發現抖音
去水印
簡單的讓人感動,哈哈哈~
身體力行
既然原理都清晰了,剩下的就是一步一步實現功能了,原理看着挺簡單的,但實現中仍是遇到一點點小坑,浪費了很多時間。
實現過程只有簡單的三步:
- 一、從輸入框中過濾取出視頻短鏈接
- 二、短鏈接傳到後端解析出無水印的視頻
URL
- 三、視頻
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=", ""); /** * 一、短鏈接重定向後的 URL */ String redirectUrl = CommonUtils.getLocation(url); /** * 二、拿到視頻對應的 ItemId */ String videoUrl = ""; String musicUrl = ""; String videoPic = ""; String desc = ""; if (!StringUtils.isEmpty(redirectUrl)) { /** * 三、用 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); /** * 四、無水印視頻 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); /** * 五、音頻 url */ musicUrl = dyResult.getItem_list().get(0).getMusic().getPlay_url().getUri(); dyDto.setMusicUrl(musicUrl); /** * 六、封面 */ videoPic = dyResult.getItem_list().get(0).getVideo().getDynamic_cover().getUrl_list().get(0); dyDto.setVideoPic(videoPic); /** * 七、視頻文案 */ 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】自行領取。和一些小夥伴們建了一個技術交流羣,一塊兒探討技術、分享技術資料,旨在共同窗習進步,若是感興趣就加入咱們吧!
不管你是剛入行、仍是已經有幾年經驗的程序員,相信這份面試提綱都會給你很多助力,長按二維碼關注 『 程序員內點事 』 ,回覆 『 offer 』 自行領取,祝你們 offer 拿到手軟