草庐IT

Android初学 抖音短视频无水印下载APP的实现

蜉蝣Sakura 2023-09-24 原文

实现效果:

  1. 在抖音复制分享链接
  2. 粘贴分享链接到App, 点击解析
  3. 点击 去下载 进入浏览器下载

获取无水印视频链接

首先我们使用浏览器打开分享的链接

8.97 teO:/ 一起玩水吧 在这炽热的夏天%%蓝色治愈系 %%夏天 %%游泳 https://v.douyin.com/Y5SpHKD/ 复制此链接,打开Dou音搜索,直接观看视频!

然后打开控制台找到对应的视频, 右键检查可以找到视频的链接
https://www.douyin.com/aweme/v1/play/?video_id=v0200fg10000ca08sgbc77udtm7m06p0

然后在浏览器中打开此链接发现是没有水印的视频, 稍后下载的视频使用的是此链接.

通过测试其他分享的视频发现不同视频对应的video_id不同, 于是需要找到获取video_id的请求.
https://www.douyin.com/aweme/v1/play/?video_id=xxx
通过控制台将设备改成移动端设备, 点击分享的链接https://v.douyin.com/Y5SpHKD/, 这时有一条请求进入我们的视线:
https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=7097830584419962125

该请求的响应有我们需要的video_id
该请求有一个参数item_ids, 此参数正是点击分享链接后重定向链接的一部分

因此我们只需要解析该链接就可以拿到item_ids

至此, 就得到了下载无水印视频的全过程。

  1. 根据https://v.douyin.com/Y5SpHKD/的重定向地址https://www.iesdouyin.com/share/video/7097830584419962125获取item_ids=7097830584419962125
  2. 根据https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=7097830584419962125的响应拿到vid
  3. 根据www.douyin.com/aweme/v1/play/?video_id=v0200fg10000ca08sgbc77udtm7m06p0下载视频

实现

model层

通过传入的分享链接, 获取无水印视频链接

public class DouyinDownloadModel {
    private final OkHttpClient mOkHttpClient = new OkHttpClient();

    public void download(InputBean inputBean, DownloadListener downloadListener) {
        Request.Builder builder = new Request.Builder().get().url(inputBean.getShareUrl());
        requestInit(builder);
        Request request = builder.build();
        mOkHttpClient.newBuilder()
                .followRedirects(false).build().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                downloadListener.fail();
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) {
                if (response.code() == 302) {
                    String location = response.header("location");
                    if (location != null) {
                        System.out.println(location);
                        String itemIds = parseItemIds(location);
                        if (!TextUtils.isEmpty(itemIds)) {
                            getVidByItemIds(itemIds, new GetVidRequestListener() {
                                @Override
                                public void success(JsonRootBean bean) {
                                    Item_list item_list = bean.getItem_list().get(0);
                                    String vid = item_list.getVideo().getVid();
                                    String url = "https://www.douyin.com/aweme/v1/play/?video_id="
                                            + vid;
                                    DownLoadResult downLoadResult = new DownLoadResult();
                                    downLoadResult.setVideoUrl(url)
                                            .setNickName(item_list.getAuthor().getNickname())
                                            .setVideoDesc(item_list.getDesc());
                                    downloadListener.success(downLoadResult);
                                }

                                @Override
                                public void fail() {
                                    downloadListener.fail();
                                }
                            });
                        } else {
                            downloadListener.fail();
                        }
                    }
                }
            }
        });
    }

    public void getVidByItemIds(String itemIds, GetVidRequestListener getVidRequestListener) {
        String url = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=" + itemIds;
        System.out.println(url);
        Request.Builder builder = new Request.Builder().get().url(url);
        requestInit(builder);
        Request request = builder.build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                getVidRequestListener.fail();
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response)
                    throws IOException {
                if (response.code() == 200) {
                    response.body();
                    String jsonString = response.body().string();
                    Gson gson = new Gson();
                    System.out.println(jsonString);
                    JsonRootBean jsonRootBean = gson.fromJson(jsonString, JsonRootBean.class);
                    System.out.println(jsonRootBean.getItem_list().get(0).getVideo().getVid());
                    getVidRequestListener.success(jsonRootBean);
                }
            }
        });
    }

    public interface DownloadListener {
        void success(DownLoadResult downLoadResult);

        void fail();

        void onProgress(int progress);
    }

    interface GetVidRequestListener {

        void success(JsonRootBean bean);

        void fail();
    }

    public void requestInit(Request.Builder builder) {
        builder.addHeader("user-agent",
                "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 "
                        + "(KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/101.0.4951.41");
    }

    public String parseItemIds(String text) {
        String regex = "\\d{19}";
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(text);
        if (m.find()) {
            return m.group();
        }
        return null;
    }
}

Presenter层

View层

源代码 + Apk

参考

  1. 重定向的链接在谷歌F12中看不到
  2. 认识Android中的MVP设计模式

有关Android初学 抖音短视频无水印下载APP的实现的更多相关文章

  1. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  4. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  5. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. ruby - 使用 postgres.app 在 rvm 下要求 pg 时出错 - 2

    我正在使用Postgres.app在OSX(10.8.3)上。我已经修改了我的PATH,以便应用程序的bin文件夹位于所有其他文件夹之前。Rammy:~phrogz$whichpg_config/Applications/Postgres.app/Contents/MacOS/bin/pg_config我已经安装了rvm并且可以毫无错误地安装pggem,但是当我需要它时我得到一个错误:Rammy:~phrogz$gem-v1.8.25Rammy:~phrogz$geminstallpgFetching:pg-0.15.1.gem(100%)Buildingnativeextension

  10. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

随机推荐