Skip to content

Commit fd8f303

Browse files
committed
feat:mp4转码
1 parent 55a9c34 commit fd8f303

File tree

5 files changed

+112
-18
lines changed

5 files changed

+112
-18
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"dependencies": {
2020
"cropperjs": "^1.5.12",
2121
"dayjs": "^1.10.8",
22+
"mux.js": "^6.2.0",
2223
"naive-ui": "^2.33.2",
2324
"normalize.css": "^8.0.1",
2425
"nprogress": "^0.2.0",

src/env.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,5 @@ declare let electronAPI: {
2929
};
3030

3131
declare module "qrcode-decoder";
32+
33+
declare module "mux.js";

src/page/video/m3u8.tsx

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DownloadStatus, downloadStatusList, IM3u8Item, ITsItem } from "@/servic
66
import { NAlert, NButton, NCard, NInput, NInputGroup, NInputGroupLabel, NProgress, NTag, NText } from "naive-ui";
77
import { computed, defineComponent, onActivated, onMounted, reactive, ref } from "vue";
88
import { useRoute } from "vue-router";
9+
import muxjs from "mux.js";
910

1011
export default defineComponent({
1112
props: {},
@@ -30,6 +31,7 @@ export default defineComponent({
3031
const total = list.length;
3132
const doneNum = list.filter(li => li.status === DownloadStatus.FINISHED).length;
3233
const percentage = Number(((doneNum / total) * 100).toFixed(2));
34+
const duration = list.reduce((t, v) => t + v.duration, 0);
3335
if (tsList.value.some(t => t.status === DownloadStatus.DOWNLOADING)) {
3436
status = DownloadStatus.DOWNLOADING;
3537
} else if (tsList.value.some(t => t.status === DownloadStatus.ERROR)) {
@@ -45,6 +47,7 @@ export default defineComponent({
4547
percentage,
4648
total,
4749
doneNum,
50+
duration,
4851
};
4952
});
5053
return data;
@@ -69,8 +72,16 @@ export default defineComponent({
6972
}
7073

7174
const tempArr: ITsItem[] = [];
72-
data.split(/\s/).forEach(item => {
75+
const lines = data.split(/\s/);
76+
lines.forEach((item, index) => {
7377
if (/((\.ts)|(\.jpg)|(\.png)|(\.gif)|(\.image))(\?.+)?$/i.test(item)) {
78+
// 计算持续时间
79+
let duration = 0;
80+
const durationItem = lines[index - 1];
81+
const extinf = "#EXTINF:";
82+
if (durationItem.includes(extinf)) {
83+
duration = parseFloat(durationItem.split(extinf)[1]) || 0;
84+
}
7485
const src = getFullUrl(origin, item);
7586
tempArr.push({
7687
status: DownloadStatus.WAITING,
@@ -79,6 +90,7 @@ export default defineComponent({
7990
filePath: form.filePath,
8091
src,
8192
file: undefined,
93+
duration,
8294
});
8395
}
8496
});
@@ -106,27 +118,50 @@ export default defineComponent({
106118
downloadTs();
107119
}
108120
}
109-
function downloadTs() {
121+
async function downloadTs() {
110122
const index = tsList.value.findIndex(v => v.status === DownloadStatus.WAITING);
111123
if (index === -1) {
112124
return;
113125
}
126+
const item = tsList.value[index];
114127
tsList.value[index].status = DownloadStatus.DOWNLOADING;
115-
ajax<Buffer>(tsList.value[index].src, "arraybuffer")
116-
.then(data => {
117-
tsList.value[index].status = DownloadStatus.FINISHED;
118-
tsList.value[index].file = data;
119-
const m3u8 = m3u8List.value.find(v => v.src === tsList.value[index].m3u8Src);
120-
if (m3u8?.status === DownloadStatus.FINISHED) {
121-
downloadFile(m3u8);
122-
}
123-
})
124-
.catch(() => {
125-
tsList.value[index].status = DownloadStatus.ERROR;
126-
})
127-
.finally(() => {
128-
downloadTs();
128+
try {
129+
const data = await ajax<Buffer>(item.src, "arraybuffer");
130+
// 转码mp4
131+
const file: Uint8Array = await new Promise(resolve => {
132+
const { duration } = m3u8List.value.find(v => v.src === item.m3u8Src)!;
133+
const opts = duration
134+
? {
135+
keepOriginalTimestamps: true,
136+
duration,
137+
}
138+
: undefined;
139+
const transmuxer = new muxjs.mp4.Transmuxer(opts);
140+
const timer = setTimeout(() => {
141+
resolve(data);
142+
}, 2000);
143+
transmuxer.on("data", (segment: any) => {
144+
clearTimeout(timer);
145+
const data = new Uint8Array(segment.initSegment.byteLength + segment.data.byteLength);
146+
data.set(segment.initSegment, 0);
147+
data.set(segment.data, segment.initSegment.byteLength);
148+
resolve(data);
149+
});
150+
transmuxer.push(new Uint8Array(data));
151+
transmuxer.flush();
129152
});
153+
154+
tsList.value[index].status = DownloadStatus.FINISHED;
155+
tsList.value[index].file = file;
156+
const m3u8 = m3u8List.value.find(v => v.src === item.m3u8Src);
157+
if (m3u8?.status === DownloadStatus.FINISHED) {
158+
downloadFile(m3u8);
159+
}
160+
} catch (e) {
161+
tsList.value[index].status = DownloadStatus.ERROR;
162+
} finally {
163+
downloadTs();
164+
}
130165
}
131166

132167
// 文件下载
@@ -332,7 +367,16 @@ export default defineComponent({
332367
downloadFile(item);
333368
}}
334369
>
335-
保存视频
370+
下载视频
371+
</NButton>
372+
) : item.percentage > 0 ? (
373+
<NButton
374+
class="mar-r-3-item"
375+
onClick={() => {
376+
downloadFile(item);
377+
}}
378+
>
379+
强制下载
336380
</NButton>
337381
) : null}
338382
{config.isElectron ? (

src/service/video.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export interface ITsItem {
3636
m3u8Src: string;
3737
name: string;
3838
filePath: string;
39-
file?: Buffer;
39+
file?: Uint8Array;
40+
duration: number;
4041
}
4142
export interface IM3u8Item {
4243
status: DownloadStatus;
@@ -46,6 +47,7 @@ export interface IM3u8Item {
4647
percentage: number;
4748
total: number;
4849
doneNum: number;
50+
duration: number;
4951
}
5052

5153
export const circuits: { label: string; value: string }[] = [

yarn.lock

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@
240240
"@babel/helper-plugin-utils" "^7.19.0"
241241
"@babel/plugin-syntax-typescript" "^7.18.6"
242242

243+
"@babel/runtime@^7.11.2":
244+
version "7.19.0"
245+
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
246+
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
247+
dependencies:
248+
regenerator-runtime "^0.13.4"
249+
243250
"@babel/template@^7.0.0", "@babel/template@^7.18.10":
244251
version "7.18.10"
245252
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
@@ -1888,6 +1895,11 @@ dom-serializer@^1.0.1:
18881895
domhandler "^4.2.0"
18891896
entities "^2.0.0"
18901897

1898+
dom-walk@^0.1.0:
1899+
version "0.1.2"
1900+
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
1901+
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
1902+
18911903
domelementtype@^2.0.1, domelementtype@^2.2.0:
18921904
version "2.3.0"
18931905
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
@@ -2637,6 +2649,14 @@ global-tunnel-ng@^2.7.1:
26372649
npm-conf "^1.1.3"
26382650
tunnel "^0.0.6"
26392651

2652+
global@^4.4.0:
2653+
version "4.4.0"
2654+
resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
2655+
integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
2656+
dependencies:
2657+
min-document "^2.19.0"
2658+
process "^0.11.10"
2659+
26402660
globals@^11.1.0:
26412661
version "11.12.0"
26422662
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@@ -3320,6 +3340,13 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
33203340
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
33213341
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
33223342

3343+
min-document@^2.19.0:
3344+
version "2.19.0"
3345+
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
3346+
integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==
3347+
dependencies:
3348+
dom-walk "^0.1.0"
3349+
33233350
min-indent@^1.0.0:
33243351
version "1.0.1"
33253352
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
@@ -3397,6 +3424,14 @@ [email protected]:
33973424
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
33983425
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
33993426

3427+
mux.js@^6.2.0:
3428+
version "6.2.0"
3429+
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-6.2.0.tgz#158a4fcf5d83b087ab9037d325527ea993f830a3"
3430+
integrity sha512-SKuxIcbmK/aJoz78aQNuoXY8R/uEPm1gQMqWTXL6DNl7oF8UPjdt/AunXGkPQpBouGWKDgL/TzSl2VV5NuboRg==
3431+
dependencies:
3432+
"@babel/runtime" "^7.11.2"
3433+
global "^4.4.0"
3434+
34003435
naive-ui@^2.33.2:
34013436
version "2.33.2"
34023437
resolved "https://registry.yarnpkg.com/naive-ui/-/naive-ui-2.33.2.tgz#c74e8b7c944f6af18cd850bd640f6d3485a47f05"
@@ -3973,6 +4008,11 @@ process-nextick-args@~2.0.0:
39734008
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
39744009
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
39754010

4011+
process@^0.11.10:
4012+
version "0.11.10"
4013+
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
4014+
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
4015+
39764016
progress@^2.0.3:
39774017
version "2.0.3"
39784018
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
@@ -4218,6 +4258,11 @@ redent@^3.0.0:
42184258
indent-string "^4.0.0"
42194259
strip-indent "^3.0.0"
42204260

4261+
regenerator-runtime@^0.13.4:
4262+
version "0.13.9"
4263+
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
4264+
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
4265+
42214266
regexpp@^3.2.0:
42224267
version "3.2.0"
42234268
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"

0 commit comments

Comments
 (0)