关于java:手写一个抖音视频去水印工具千万别刚一个程序员

33次阅读

共计 5815 个字符,预计需要花费 15 分钟才能阅读完成。

百因必有果

说一下我为什么要做个抖音视频去水印工具,其实是因为我的沙雕女友,她竟然刚我~

有天早晨她在抖音看见一个十分具备 教育意义 的视频,“男人疼媳妇就该承包全副家务活”,而后它就想把视频下载下来,分享到她的姐妹群交换 驭夫 心得。

可是大家都晓得抖音下载的视频是带水印,作为一个重度强迫症选手这是不被容许的,没方法那就找找有没有去水印工具吧,找了一圈要不就是免费,要么下载不下来,主上脸上的笑容也在逐步隐没。

我在边上调侃了一句:也没多难,要不我给你做一个!“你行吗?”而后投来了一个不屑的眼神。

哎呀!原本就开个玩笑,竟然说我不行,这就不能忍了,我得证实给你看看!男人嘛,就受不了这话

先看下我做的去水印工具线上预览成果:http://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"/>

还简略做了下挪动端适配,款式看着还能够,然而性能应用起来有点差强人意,后边在做优化了。

总结

很多货色就是这样,没认真钻研之前总感觉深不可测,可一旦接触到技术的实质,又开始笑本人之前好蠢,懂与不懂有时就查那么一层窗户纸。

好了明天就到这,本文源码在 自己公众号【程序员内点事】回复【 源码】自取

正文完
 0