Android手撸一个今日头条视频下载器
2017-02-16 16:00
246 查看
转载请标明出处: http://www.weyye.me/detail/today-news-video/
本文出自:【Wey Ye的博客】
这里我摘出来视频的获取流程
1、将/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()},进行crc32加密。
2、将上面得到的加密值拼接到上面的链接中即可,最终的链接形式是:
http://i.snssdk.com/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()}&s={crc32值}
3、访问这个链接得到一个json数据,需要解析video_list数组中的main_url值,然后用base64解码得到最终的原始视频链接。
看到上面的步骤并不复杂,但是在操作过程中还是有些地方需要注意的,主要是上面的那个随机数和crc32加密逻辑,videoid可以从视频网页的html源代码里面获取
用正则表达式取出videoid即可
看流程分析,我们需要视频所在的网页地址,在get请求访问成功的回调里面进行正则表达式匹配,然后将/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()},进行crc32加密,然后拼在一起,再get请求,再在请求成功的回调里解析json获取base64编码的地址,然后进行解码,最后获得视频源地址。这一些列操作都是需要请求成功才可以执行的,可以想到嵌套里面再嵌套,那种代码逻辑着实让人看着蛋疼,所以这个时候RxJava的优势就出来了,完美的解决了这个问题,如果还不是很懂RxJava的朋友可以去看下这篇文章给 Android 开发者的 RxJava 详解
ok,这样就具备的接收的功能,然后在
首先通过正则表达式取出分享链接,然后进行视频解析
视频解析的核心代码
ok,一套流程下来,我们就获取到视频的真实地址
视频获取出来,就可以下载了,
这里我没有使用
ok,整个下载的逻辑就写完了
Android Canvas Clear with transparency
本文出自:【Wey Ye的博客】
前言
今日头条是我最喜欢的app之一,当然喜欢并不是因为内容精彩,而是逗比的评论,而且看视频的没有广告,我这个人喜欢收藏,尤其是小视频(手动滑稽),可是却没有下载的按钮,之后在仿今日头条项目里也需要用到视频,进入网页右键另存为也比较麻烦,作为程序猿,这可不是我们的办事风格。于是动手撸了一个视频下载器,喜欢的记得给个Star,当作是给我的鼓励和动力吧。成果图
源码下载
https://github.com/yewei02538/TodayNewsVideoDownloader分析视频地址
详细的视频地址分析请看Python脚本下载今日头条视频(附加Android版本辅助下载器)这里我摘出来视频的获取流程
1、将/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()},进行crc32加密。
2、将上面得到的加密值拼接到上面的链接中即可,最终的链接形式是:
http://i.snssdk.com/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()}&s={crc32值}
3、访问这个链接得到一个json数据,需要解析video_list数组中的main_url值,然后用base64解码得到最终的原始视频链接。
看到上面的步骤并不复杂,但是在操作过程中还是有些地方需要注意的,主要是上面的那个随机数和crc32加密逻辑,videoid可以从视频网页的html源代码里面获取
用正则表达式取出videoid即可
看流程分析,我们需要视频所在的网页地址,在get请求访问成功的回调里面进行正则表达式匹配,然后将/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()},进行crc32加密,然后拼在一起,再get请求,再在请求成功的回调里解析json获取base64编码的地址,然后进行解码,最后获得视频源地址。这一些列操作都是需要请求成功才可以执行的,可以想到嵌套里面再嵌套,那种代码逻辑着实让人看着蛋疼,所以这个时候RxJava的优势就出来了,完美的解决了这个问题,如果还不是很懂RxJava的朋友可以去看下这篇文章给 Android 开发者的 RxJava 详解
撸代码
首先得先获取到播放视频的网页地址,这里我使用的是分享来接收,今日头条有分享功能,分享的内容里面肯定会有地址,所以我们来配置下接收分享<activity android:name=".ui.MainActivity" android:label="@string/app_name" android:launchMode="singleTask" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity>
ok,这样就具备的接收的功能,然后在
MainActivity里面写上接受的逻辑
protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.i("MainActivity", "onNewIntent"); String title = intent.getStringExtra(Intent.EXTRA_TEXT); parseUrl(title); } private void parseUrl(String title) { //取出网页地址 Pattern pattern = Pattern.compile("【(.+)】\\n(http.+)"); final Matcher matcher = pattern.matcher(title); if (matcher.find()) { final ProgressDialog dialog = new ProgressDialog(this); dialog.setMessage("正在获取视频地址,请稍后~"); dialog.setCanceledOnTouchOutside(false); //解析视频真实地址 VideoPathDecoder decoder = new VideoPathDecoder() { @Override public void onSuccess(Video s) { dialog.dismiss(); s.title = matcher.group(1); mDatas.add(s); mAdapter.notifyItemInserted(mDatas.size()); startDownload(s); } @Override public void onDecodeError(Throwable e) { dialog.dismiss(); Snackbar.make(mRecyclerView, "获取视频失败!", Snackbar.LENGTH_LONG).show(); } }; dialog.show(); decoder.decodePath(matcher.group(2)); } else { Snackbar.make(mRecyclerView, "不是分享的链接", Snackbar.LENGTH_LONG).show(); } }
首先通过正则表达式取出分享链接,然后进行视频解析
视频解析的核心代码
AppClient.getApiService().getVideoHtml(srcUrl) .flatMap(new Func1<String, Observable<ResultResponse<VideoModel>>>() { @Override public Observable<ResultResponse<VideoModel>> call(String response) { Pattern pattern = Pattern.compile("videoid:\'(.+)\'"); Matcher matcher = pattern.matcher(response); if (matcher.find()) { String videoId = matcher.group(1); Log.i(TAG,videoId); //1.将/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()},进行crc32加密。 String r = getRandom(); CRC32 crc32 = new CRC32(); String s = String.format(ApiService.URL_VIDEO, videoId, r); //进行crc32加密。 crc32.update(s.getBytes()); String crcString = crc32.getValue() + ""; //2.访问http://i.snssdk.com/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()}&s={crc32值} String url = ApiService.HOST_VIDEO + s + "&s=" + crcString; Log.i(TAG,url); return AppClient.getApiService().getVideoData(url); } return null; } }) .map(new Func1<ResultResponse<VideoModel>, Video>() { @Override public Video call(ResultResponse<VideoModel> videoModelResultResponse) { VideoModel.VideoListBean data = videoModelResultResponse.data.video_list; if (data.video_3 != null) { return updateVideo(data.video_3); } if (data.video_2 != null) { return updateVideo(data.video_2); } if (data.video_1 != null) { return updateVideo(data.video_1); } return null; } private String getRealPath(String base64) { return new String(Base64.decode(base64.getBytes(), Base64.DEFAULT)); } private Video updateVideo(Video video) { //base64解码 video.main_url = getRealPath(video.main_url); return video; } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Video>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { e.printStackTrace(); onDecodeError(e); } @Override public void onNext(Video s) { onSuccess(s); } });
ok,一套流程下来,我们就获取到视频的真实地址
视频获取出来,就可以下载了,
private void startDownload(final Video video) { FileDownload download = new FileDownload(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "todayNewsVideo" , UUID.randomUUID().toString() + "." + video.vtype); download.download(video.main_url, new FileDownload.Callback() { @Override public void onError(Exception e) { mAdapter.setPercent(video.main_url, -1); } @Override public void onSuccess(File file) { video.file = file; mAdapter.setPercent(video.main_url, 100); } @Override public void inProgress(float progress, long total) { mAdapter.setPercent(video.main_url, (int) (progress * 100)); } }); }
这里我没有使用
retrofit,因为下载大文件的时候容易oom(希望有大神能解决我这个问题)为了方便,我直接使用原生的okhttp来下载
public void download(String url, final Callback callback) { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new okhttp3.Callback() { @Override public void onFailure(Call call, IOException e) { callback.onError(e); } @Override public void onResponse(Call call, Response response) throws IOException { try { final File file = saveFile(response, callback); AppClient.getDelivery().post(new Runnable() { @Override public void run() { callback.onSuccess(file); } }); } catch (IOException e) { callback.onError(e); } } }); }
ok,整个下载的逻辑就写完了
参考
Python脚本下载今日头条视频(附加Android版本辅助下载器)Android Canvas Clear with transparency
声明
这个属于个人开发作品,仅做学习交流使用,切勿使用于商业用途,如用本程序做非法用途后果自负,与作者无关!!相关文章推荐
- <Android 应用 之路> 一个类似今日头条的APP
- <Android 应用 之路> 一个类似今日头条的APP
- Android应用程序开发教程 - 实现一个登录对话框
- 好久没有blog了,今日就share一个update program的经验
- Android 开发一个Activity 启动另一个Activity碰到的一个问题
- Android Donut SDK 1.6r1 今日发布
- Eclipse下新建Android项目碰到的一个小问题
- 设计并编写一个Windows Mobile 6.5今日界面
- 用android build system 编译一个最小的android平台
- 一个简单的Intent的android程序例子
- Android Donut SDK 1.6r1 今日发布
- 今日发现mysql的一个bug
- 推荐一个在线编辑Android的工具给大家用下
- 用android build system 编译一个最小的android平台
- 如何在PPC上写一个今日插件
- Android是一个应用框架,而不是类别库
- 今日建立了一个Roller的BLOG
- 今日学习LINQ,发现一个好的工具。LINQPad!!
- Android的Dalvik虚拟机的一个小BUG
- Android UI界面目前做的最好的一个:索尼爱立信Xperia X10