Android手擼一個今日頭條視訊下載器
前言
今日頭條是我最喜歡的app之一,當然喜歡並不是因為內容精彩,而是逗比的評論,而且看視訊的沒有廣告,我這個人喜歡收藏,尤其是小視訊(手動滑稽),可是卻沒有下載的按鈕,之後在仿今日頭條專案裡也需要用到視訊,進入網頁右鍵另存為也比較麻煩,作為程式猿,這可不是我們的辦事風格。於是動手擼了一個視訊下載器,喜歡的記得給個Star,當作是給我的鼓勵和動力吧。
成果圖
原始碼下載
分析視訊地址
這裡我摘出來視訊的獲取流程
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的朋友可以去看下這篇文章
擼程式碼
首先得先獲取到播放視訊的網頁地址,這裡我使用的是分享來接收,今日頭條有分享功能,分享的內容裡面肯定會有地址,所以我們來配置下接收分享
<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,整個下載的邏輯就寫完了
參考
宣告
這個屬於個人開發作品,僅做學習交流使用,切勿使用於商業用途,如用本程式做非法用途後果自負,與作者無關!!
相關推薦
Android手擼一個今日頭條視訊下載器
前言 今日頭條是我最喜歡的app之一,當然喜歡並不是因為內容精彩,而是逗比的評論,而且看視訊的沒有廣告,我這個人喜歡收藏,尤其是小視訊(手動滑稽),可是卻沒有下載的按鈕,之後在仿今日頭條專案裡也需要用到視訊,進入網頁右鍵另存為也比較麻煩,作為程式猿,這
今日頭條視訊下載器[android下載原始碼]
在家無聊,看到趙四大神 寫的一個python指令碼下載今日頭條的工具,最後他還給出了移動端的樣子,可惜沒有原始碼,在他的虛心教導下,看完了他的文章,我決定自己擼一個,見笑了: CSDN圖片最大值只能傳2m,所以用了兩張gif圖,大家見笑了。 下面說一
Python指令碼下載今日頭條視訊 附加Android版本輔助下載器
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Python指令碼下載今日頭條視訊(附加Android版本輔助下載器)
一、前言今日頭條有毒,這句話不是虛的,現在資訊類app中也就大黃易和今日頭條可以博取使用者一點喜好了,我所說的喜好不是指內容精彩,而是評論,玩過這兩個app的人都知道,看的不是新聞本生內容,而是他逗逼的
如何下載今日頭條視訊
隨著《今日頭條》使用者數量越來越多,很多人都願意到《今日頭條》上去看視訊,但是有很多人並不知道《今日頭條》的視訊如何下載到電腦的,現在我介紹PC端的《今日頭條》視訊如何簡單的儲存到電腦上。 1.開啟瀏覽器,並進入今日頭條 2.找到你喜歡並想下載的視訊 3.
手擼一個Android柱形圖和線型圖的組合圖表
專案開發中經常用到統計圖表,網上也有很多的圖表類庫,比如 :MPAndroidChart,XCL-chart,hellochart,AChartEngine等等,以前我最常用的就是MPAndroidChart,這個庫做的非常細緻用起來也簡單。 但是用別人的東
手擼一個Vue滾動加載自定義指令
請求 tel document javascrip 決定 tlist win 滾動加載 pos 用Vue在移動端做滾動加載,使用mint-ui框架, InfiniteScroll指令loadmore組件,在uc瀏覽器和qq瀏覽器都無法觸發。無奈我只能自己寫了。 決定用vu
LRU原理和Redis實現——一個今日頭條的面試題
滿了 存儲空間 當前 node 硬盤 java 原理 remove http 看了評論,發現有些地方有問題,更新了圖和一些描述,希望可以更清晰一些,也歡迎關註,還會有幹貨文章 -------- 很久前參加過今日頭條的面試,遇到一個題,目前半部分是如何實現 LRU,後半部
分享一個抖音視訊下載程式
在網上呼叫別人的介面來實現的功能 import requests import execjs # 生成引數s def generateStr(a): js = ''' test = function(a) { var
使用Java Socket手擼一個http服務器
con body buffered run value nal uic news ble 原文連接:使用Java Socket手擼一個http服務器 作為一個java後端,提供http服務可以說是基本技能之一了,但是你真的了解http協議麽?你知道知道如何手擼一個htt
使用Java Socket手擼一個http伺服器
原文連線:使用Java Socket手擼一個http伺服器 作為一個java後端,提供http服務可以說是基本技能之一了,但是你真的瞭解http協議麼?你知道知道如何手擼一個http伺服器麼?tomcat的底層是怎麼支援http服務的呢?大名鼎鼎的Servlet又是什麼東西呢,該怎麼使用呢? 在
仿今日頭條視訊播放
轉自:https://www.jianshu.com/p/34d378bffb00 gitbug:https://github.com/lipangit/JiaoZiVideoPlayer 參考:http://blog.csdn.net/androidstarjack/ar
Android使用SVG實現今日頭條下拉重新整理動畫
1 SVG的全稱是Scalable Vector Graphics,叫可縮放向量圖形。它和點陣圖(Bitmap)相對,SVG不會像點陣圖一樣因為縮放而讓圖片質量下降。 2 Android
手擼一個預載入頁面,酷炫環形進度條
高仿格瓦拉生活預載入頁面環形進度條——我稱之為二龍戲珠。話不多說先上圖。 實現思路: 1、自定義一個view,畫兩個從點變換到半圓的弧形。 需要拓展的功能點: 1、需要展現出一個動畫效果。 2、進度條走滿的時候需要觸發介面的跳轉。 3、繪製的控制
(android高仿系列)今日頭條 --新聞閱讀器 (三) 完結 、總結 篇
從寫第一篇今日頭條高仿系列開始,到現在已經過去了1個多月了,其實大體都做好了,就是遲遲沒有放出來,因為我覺得,做這個東西也是有個過程的,我想把這個模仿中一步一步學習的過程,按照自己的思路寫下來,在根據碰到的知識點和問題,並且羅列出這些東西的知識點和使用方法。如果你單
Android之高仿今日頭條、網易新聞首頁動態改變tab
前言: 專案需要一個類似今日頭條或者網易新聞首頁動態改變tab(頻道欄目)的功能,進過一番折騰,目前已實現該功能。 先看看效果圖: 思路: 1,關於tab欄目橫著滑動功能控制元件的選擇,這裡我採用的Horizontal
手擼一個自定義日曆控制元件
引言 日曆控制元件在android開發中也是比較常見的一個控制元件,並且目前大部分開源的日曆控制元件也已經做得很漂亮,很完善了,功能也相當豐富; 今天這個日曆控制元件就是我在別人的基礎上進行修改了的,首先很感謝這個開源庫(https://github.com/c
【手擼一個ORM】第四步、Expression(表達式目錄樹)擴展
clas access stat ber expr req exc nodetype 支持 一、表達式目錄樹解析時需要的擴展方法 表達式操作符轉SQL操作符 獲取MemberExpression的根類型,後面需要根據該類型進行不同的操作 獲取表達式目錄路的值
【手擼一個ORM】第六步、對象表達式解析和Select表達式解析
member ++ access over def 大於 () 表達式 cti 對象表達式用於解析 Expression<Func<Student, object>> expr = s => new { s.Id, s.Name } 這種形式的
【手擼一個ORM】第八步、實體查詢和按需查詢
index desc cond ldb 主表 open 導航 sql handler 一、實體查詢 using MyOrm.Commons; using MyOrm.DbParameters; using MyOrm.Expressions; using MyO