From 998dcf62e46a055716802ee4df4dbed178e308aa Mon Sep 17 00:00:00 2001 From: SarmonFish <98707554+SarmonFish@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:38:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=92=AD=E6=94=BE=E5=99=A8):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=A7=86=E9=A2=91=E9=9F=B3=E9=A2=91=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=99=A8=E5=8F=8A=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.新增视频音频播放器页面及样式 2.添加视频音频缓存管理 3.在视频详情页添加播放入口 4.更新临时文件清理功能支持音频缓存 5.优化临时文件清理功能 --- package.json | 2 +- src/bilibiliclient/video/video.ts | 64 +- src/common/icon/download.png | Bin 0 -> 678 bytes src/common/icon/exit.png | Bin 0 -> 469 bytes src/common/icon/next.png | Bin 0 -> 801 bytes src/common/icon/pause.png | Bin 0 -> 253 bytes src/common/icon/play-list.png | Bin 0 -> 938 bytes src/common/icon/play.png | Bin 0 -> 633 bytes src/common/icon/prev.png | Bin 0 -> 744 bytes src/common/icon/progress.png | Bin 0 -> 2764 bytes src/common/icon/repeat-once.png | Bin 0 -> 1227 bytes src/common/icon/repeat.png | Bin 0 -> 1121 bytes src/common/icon/shuffle.png | Bin 0 -> 1382 bytes src/common/icon/volume.png | Bin 0 -> 1253 bytes src/manifest.json | 10 +- .../features/savedcontent/savedcontent.less | 44 + .../app/features/savedcontent/savedcontent.ux | 24 +- .../features/settings/cleartmp/cleartmp.ux | 76 +- src/pages/video/player/player.less | 601 +++++++ src/pages/video/player/player.ux | 1533 +++++++++++++++++ src/pages/video/videodetail/videodetail.ux | 33 +- src/savedcontent.ts | 51 +- 22 files changed, 2412 insertions(+), 26 deletions(-) create mode 100644 src/common/icon/download.png create mode 100644 src/common/icon/exit.png create mode 100644 src/common/icon/next.png create mode 100644 src/common/icon/pause.png create mode 100644 src/common/icon/play-list.png create mode 100644 src/common/icon/play.png create mode 100644 src/common/icon/prev.png create mode 100644 src/common/icon/progress.png create mode 100644 src/common/icon/repeat-once.png create mode 100644 src/common/icon/repeat.png create mode 100644 src/common/icon/shuffle.png create mode 100644 src/common/icon/volume.png create mode 100644 src/pages/app/features/savedcontent/savedcontent.less create mode 100644 src/pages/video/player/player.less create mode 100644 src/pages/video/player/player.ux diff --git a/package.json b/package.json index 27ffdd9..0c4d0c2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "start": "aiot server --watch --open-nuttx", "build": "aiot build --enable-jsc --enable-protobuf", - "release": "aiot release --enable-jsc --enable-protobuf --enable-image-png8", + "release": "aiot release --enable-jsc --enable-protobuf", "watch": "aiot watch --open-nuttx", "lint": "eslint --format codeframe --fix --ext .ux,.js src/", "postinstall": "node -e \"if (process.env.npm_execpath && !process.env.npm_execpath.includes('yarn')) { console.error('Please use Yarn to install dependencies!'); process.exit(1); }\"" diff --git a/src/bilibiliclient/video/video.ts b/src/bilibiliclient/video/video.ts index cdc4bf9..49739de 100644 --- a/src/bilibiliclient/video/video.ts +++ b/src/bilibiliclient/video/video.ts @@ -55,4 +55,66 @@ export const BilibiliClientVideoMethods = { global.logger.log(response); return response.data.data; }, -}; \ No newline at end of file + + async getVideoBestAudioUrlByBVID(this: any, bvid: string): Promise { + const info = await this.getVideoInfoByBVID(bvid); + const cid = info.cid || info.pages?.[0]?.cid; + if (!cid) { + throw new Error("cid not found"); + } + + const url = `https://api.bilibili.com/x/player/wbi/playurl`; + const response = await this.getRequestWbi(url, { + bvid, + cid, + fnval: 4048, + fourk: 1, + platform: "pc" + }); + + const payload = response && response.data ? response.data : response; + if (!payload) { + global.logger.error("[getVideoBestAudioUrlByBVID] empty response"); + throw new Error("empty response"); + } + global.logger.log("[getVideoBestAudioUrlByBVID] response code:", payload.code, "message:", payload.message); + + const dash = payload.data?.dash || payload.dash; + const audioTracks = dash?.audio || []; + global.logger.log("[getVideoBestAudioUrlByBVID] audioTracks length:", audioTracks.length); + if (!audioTracks.length) { + const keys = Object.keys(payload.data || payload || {}); + global.logger.error("[getVideoBestAudioUrlByBVID] no audio tracks, payload keys:", keys); + throw new Error("audio stream not found"); + } + + audioTracks.sort((a: any, b: any) => (b.bandwidth || 0) - (a.bandwidth || 0)); + const best = audioTracks[0] || {}; + + const candidates: Array = []; + const baseUrl = best.baseUrl || best.base_url; + if (baseUrl) candidates.push(baseUrl); + + const backupUrls = best.backupUrl || best.backup_url; + if (Array.isArray(backupUrls)) { + backupUrls.forEach((u: any) => { + if (typeof u === "string" && u.length > 0) candidates.push(u); + }); + } + + const uniqueCandidates = candidates.filter((u, idx) => candidates.indexOf(u) === idx); + uniqueCandidates.sort((a, b) => { + const score = (u: string) => { + let s = 0; + if (u.includes("mcdn")) s -= 10; + if (u.includes("upos")) s += 2; + return s; + }; + return score(b) - score(a); + }); + + const selected = uniqueCandidates[0]; + global.logger.log("[getVideoBestAudioUrlByBVID] selected url:", selected); + return selected; + }, +}; diff --git a/src/common/icon/download.png b/src/common/icon/download.png new file mode 100644 index 0000000000000000000000000000000000000000..326b4ef3a3ed72726868e524a5b965dc87c2656e GIT binary patch literal 678 zcmV;X0$KfuP)*h$n$+D=k8shiOE6#O*iKSvVb{Bj~O+}-=-K!`7!mA@3g$_rql z6`&PRi9p}?yZ-X}!)8}0jXI~mAb3BBe#Lf=zAtQxw_4YLtwrg(rEm7GS^zlfrZ`)l zIYxZH765S}xegUTF1e)~rxmc2natIp6_88rS&uXG)(S9*tZiQ_U~MyQ>eUJ`iL7m3 zD?ob2v9KY!(j)E9)GJy6OcnveR(jy?SJEGRJv31O@NQe_*+NM2au<%Fc-tzGtq-B| znlu1c7D*61;^|*>&(OBqY<(Piwh(~xAgQ+i zgsI}Q(ku#q8CoFl=4p?9@tYjR3t4dhObB96umg&rXTlv;s;{ZaK!{Dj*fM0!Sss@pw#bvN5+*(+Wr>wzTn~3gDC4(lp=c2?6l^ z({tyj;hk}b%$i~dKDE5000I_L_t&o02wS>kOO4fBme*a M07*qoM6N<$g2lWpmH+?% literal 0 HcmV?d00001 diff --git a/src/common/icon/exit.png b/src/common/icon/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..68733d713554ead7b38fef2a7e350053aff730b3 GIT binary patch literal 469 zcmV;`0V@89P)l=&!rlg-F{xJND@g8ro2!L#R{BD*4Sc{*z$*sh$>$(%;O=bcha?H5k zn#2`s0ceh^y8%$l+DYb4aRESF+fzG9J7quJCs)!10HyWzR1s$(TmUFq?G6dUGMd^+ z2p0fJ0&(P5!u*V;b_B5rfOo0_vySS+C>z3exLgrLH2~ssfhk#uYyJfx1jY1Wx~c() zTY_mfTQvahRt2_0Sx-{pOue$xlw<6y22d0U&Uo&KUiz7PyIK-|r+=|c088{A4QfS) z&>A2TSGWrR;wn5-+zo)lMdHj0E-OHN%!R6NhO2ij(Y3n(NB{^Gs`<^}1|WzMw*d5i zo;z|ireZ76$<8qWh|C=}x7&=V^_BwosZX;#A_Aaz<_gX|`P{Dx*WB&uL`DGg1t3(x z%aO``s%=)da#@}Pu!8<`+#SGz{RIF3|NmT&)K>ri00v1!K~w_(LTb@LTgNbV00000 LNkvXXu0mjf4R6Tw literal 0 HcmV?d00001 diff --git a/src/common/icon/next.png b/src/common/icon/next.png new file mode 100644 index 0000000000000000000000000000000000000000..8dee02f0474d4224772901f5edf5d9204b4c40e3 GIT binary patch literal 801 zcmV++1K#|JP)%~W7}%fW-KQu5jYgx#bUrYK*Alxlm8F@?Qjl|(5;X2 z6CWKCx%X){C;)dn#k0gOXmLWkc^XI>zNVl1|DTW-M{%GZa0MH>$YD@aPSqoK@Rb^6hq=(hmZyi6_P{zKygX&H*w!lJk^*6S*AU&@+htmW#45;|^V9)rgjp~on($)ip_VkrUht>`j+pKMhtL5%3Zqi(B@MnJod!Jhcctrq zrqzvUutInOjKOuFSQ@B`XQ%_VbXg*u1{Vl+cmZS7#WV8E&mHh5(ZnIt0n;=v5zkNu zyioKwZHGH6J2VqNB6VOY4SE>ap}Ba5I^Yq(4j&Uu96|@EZjuxc&xjrJ44AURjbp>AVy$Sz)NjD@F0dw_wB%^~Zwm&Z>oY$@SpHa3SWBQG|_quot3Gqtff}A`h^E!uij7q%oYAm47JFS{(ehSuk34TUEDePZ zkdPV@H?qoZY_WYTjrx f8jVJyahCi8tUA&Ah~Q5?00000NkvXXu0mjf5bj(b literal 0 HcmV?d00001 diff --git a/src/common/icon/pause.png b/src/common/icon/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..1520e857c7258d3f9a424ceef9b6873ec4d3695a GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xZ3?!EyURMI7R04cLT!Hj|LSPGL?@ORM?vfzC zV1|Z|MXsh7x2_l9RpQ_G3n*Oc>EamTaXeXqwP3@MfPfpqbGz9hW|zIcw^pvtx!p__s}C-+7E34+Y;cv<*>#d}ayP@7-HHbofOJnlAfw5<6^jC07k9ad_G(Fm zntbRg2u)oQ-W78<;o@823pfqSRP@*R_gDFS1Vqvj6BM$?E&L%tMRSe(u Q03E{M>FVdQ&MBb@01PT?rvLx| literal 0 HcmV?d00001 diff --git a/src/common/icon/play-list.png b/src/common/icon/play-list.png new file mode 100644 index 0000000000000000000000000000000000000000..ebf53b751c63492dadeb182e01e5453f79a12362 GIT binary patch literal 938 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 literal 0 HcmV?d00001 diff --git a/src/common/icon/shuffle.png b/src/common/icon/shuffle.png new file mode 100644 index 0000000000000000000000000000000000000000..988215418398ef15d3fd2a7b3235ae328e79e88f GIT binary patch literal 1382 zcmV-s1)2JZP);#;7 z0vz@X*fZeF8Q8DDnJ2(r1UGX9x~{wLy6&}m7seMY*}Vw@1o~cJA0&WA7~g1~v8MgD z2?7KU!0NKbT$O~a5b%~k|9Gg9uoVKHJ}^&4MG`haKnUTlV0&4hrGra?OMnUpm6WQg zq1jk_)(V2|mB!VgbC}^rer_d?QHbMJW^2BWBxp~jXA&T#VO5oFvtj5;D+tD)H6|DW zV@{9rHN})bce45@xs5^`h#sqrIB$kpCIM1FQQb+6z{m=uH89tafRkB}n?Sp|jSCqz zkATvBYV>m;3&;qr$4Nv8JfH!Ot;0+|88(lAQdRwI95BwcXeL3xFC+jbn?CEWrk`Om z2?+016~&CLn&rYeC)ji)1RSC`dG-`cp0xB5vGtox0$7#eo#awtHtAeSuA{V&1+XFs z5j+*W1w20Q_SP_D&{XVo4JUhEu>1r=uCO5hwv$i>z7m%mG3!f@PaJgN`NaBPMHY z{_g>bkjx-8OF=y--K_%0XgYY*21!ZZx{bajUdixUu?aT*>8)C1lQQFYyJ6#~Nh ztBMr$&=r3_@HWjczX|~aDiTObFAK5lHPSSg*#o^|Y?GYgGc21!O?! zDp;?^8ip^euSx)siUh7g^Q)0POUmj5049<^Ht;wW^VK+3n(O&n2#CSg>H&o@sxH@k z8v)7!GN4osR(Un9LI8jGNcgswovKKn9zcY3#OOA^8fBkPZ+wD77_;B9o6RjsfPw;a z`ijdZF00Ddt^!}Ev!KTh@`DbA70?Ld>c7RXvlsy}C_v*U$)vz33&?=dRp3`;045t& z58&}Ub5#-+Awb{(P5|Sec!mV}UPJ;{!P7?%60GcD4S_`Zv0$@E!mI=cIRCt;ebY1l zyp1GyKmf*Xi4;bz6=}{~l41r)$tB@S1PCy}0GiVQIA3$e!O4H$_2j~uRovH+bL328q*@sB`0 z&uPu-&Ff$iAh;AAHq<~_j;uLOo{tq~`VTjJl7!*bmNA&JSF1^YETO1wmd2MIK65o2 zg9HhCh=Z(X-rJyv&0ftTU=F98HXk&LY2Qa;3`7F;g8g&ElRRX$JOWAuB^mO1_5vJ% z1W&JNUL^2+kZhnfU^@daHq!8njb6YZ!%Gen&=H7w{3DP85hb_mA&W8{GXpY> zU+@s$TKqoXMQegbc@qQ(2-iWK*i2$qqRKahC02MpuVh o0RR6r_`(7J000I_L_t&o047N&pK+ZHyZ`_I07*qoM6N<$f>A73ApigX literal 0 HcmV?d00001 diff --git a/src/common/icon/volume.png b/src/common/icon/volume.png new file mode 100644 index 0000000000000000000000000000000000000000..83c45432a2093ace84836db2740c734f31fc1f63 GIT binary patch literal 1253 zcmVvVpkHolGv3e^CIe zJc&-1>y||c5O{#UuM64Ez6b#VULA3v3C%(qcNb28zyo^BeqSXSh7us~Lq}Y-OwY&T z@vXXvtn;zM2oQMo`_QU>D4^%#@kp)9<;&~@2>93|?nA&sz*jM-rd;&LJ#l6wKogBU z;sE{}vEGUSu&9rkDab)C37rIJ5LWSB^;I^yXttpv?o(|5A1V1CnjR@``c7x;}CHqM5MC8Sc5?bUK0tBAUL#xOnx7u02LnPl32&V~3>v(kF=QSAX zXNCZIBfukhg3|EuMMMMsXU|MV1vvr8q^IxAj{qMk`BJqeFp&V^-n>QyIRR#%sW%G5 z3u5Zf2LUIu7aE8}&9#xX{I4?9psH1B6^tT4Kzd7S_+W>?pEN~Ntz6@i`(IJOfcLfn z2|hI(ML=q~tv@w@Mp!Q*Cinl+bBIu73O~-0V)jaaLDRV5i*f>nuT()70X#-EyZw(5 zPuhS_z(811l}*3^SK9Bg&MU~;tpWf;mZ#1w0c$utqEfqEDxd`C z0W(`bYVq3bo`8~vR%HO|WJo|;txCt972p9g1e7?ts?=`J1eh0}2Esh+_Y$>B0wil* zc+6gJ5RhNyr-FIDO9F(zTm4TZJ&s5aTsy3O?!qpV|1%h~=^Oecej(SO3-)$Ooa#6yswB8GFhYZeJ0B>{~I5 z_>($$d!O%nq)H#Vjl3m~Kn08M4;|#`j0h@W;B7mnNfyR8%CAsK! zTv1+bK z2uLIAkC?w{OQ_4@C6oZrhy?Z^TY~+F%d;W1c>N~%!wDcdk-(PV)fF;uZN?TMph7Dm z$i}SnkQ%Bk;q{^ffJP*Y%?mUSx5Wtnkx1ZJA}>(mTFq7=0JI{3yZ{Mo30$k#Y6Mhh zMa0mS2+bF(695q+fztssbb;j=%|dJw1b|E=uq8Mu*%A +
+ +
+ {{$item.title}} + {{$item.author}} +
+
- + @@ -49,6 +58,18 @@ export default { } }) }, + PlayVideoAudio(item) { + router.push({ + uri: "pages/video/player", + params: { + bvid: item.bvid, + cachedAudioUri: item.fileUri, + cachedTitle: item.title, + cachedCoverUrl: item.coverUrl, + cachedAuthor: item.author + } + }) + }, async LoadContent() { const origin_content = await global.savedcontent.SavedContentManager.listAllContent() const patched = this.PatchContent(origin_content) @@ -62,4 +83,5 @@ export default { \ No newline at end of file diff --git a/src/pages/app/features/settings/cleartmp/cleartmp.ux b/src/pages/app/features/settings/cleartmp/cleartmp.ux index 3543f07..9677d84 100644 --- a/src/pages/app/features/settings/cleartmp/cleartmp.ux +++ b/src/pages/app/features/settings/cleartmp/cleartmp.ux @@ -36,12 +36,14 @@ export default { clearControl: { imgs: false, logs: false, - sc: false + sc: false, + videoAudio: false }, typeColors: { imgs: "#434343", logs: "#434343", sc: "#434343", + videoAudio: "#434343", others: "#222222" }, scrclass: "" @@ -51,12 +53,14 @@ export default { this.clearControl = { imgs: false, logs: false, - sc: false + sc: false, + videoAudio: false } this.typeColors = { imgs: "#434343", logs: "#434343", sc: "#434343", + videoAudio: "#434343", others: "#222222" } }, @@ -88,6 +92,14 @@ export default { }) } + if (this.clearControl.videoAudio) { + const contents = await global.savedcontent.SavedContentManager.listAllContent() + const videoAudioContents = contents.filter((ct) => ct.type === 'videoAudio') + videoAudioContents.forEach((ct) => { + tasks.push(global.savedcontent.SavedContentManager.deleteContent(ct.id)) + }) + } + await Promise.all(tasks) prompt.showToast({ @@ -115,11 +127,11 @@ export default { success: (data) => { this.clearControl.sc = true this.typeColors.sc = this.clearControl.sc ? "#FF7DA8" : "#434343" - // 由于这是异步函数,因此需要重新做cantClear检测 this.cantClear = !( this.clearControl.imgs || this.clearControl.logs || - this.clearControl.sc + this.clearControl.sc || + this.clearControl.videoAudio ) } }) @@ -128,9 +140,30 @@ export default { this.typeColors.sc = this.clearControl.sc ? "#FF7DA8" : "#434343" } break + case "videoAudio": + if (!this.clearControl.videoAudio) { + prompt.showDialog({ + message: + "清除视频音频将删除所有已缓存的视频音频文件,且该操作不可逆。确定要继续吗?", + success: (data) => { + this.clearControl.videoAudio = true + this.typeColors.videoAudio = this.clearControl.videoAudio ? "#FF7DA8" : "#434343" + this.cantClear = !( + this.clearControl.imgs || + this.clearControl.logs || + this.clearControl.sc || + this.clearControl.videoAudio + ) + } + }) + } else { + this.clearControl.videoAudio = false + this.typeColors.videoAudio = this.clearControl.videoAudio ? "#FF7DA8" : "#434343" + } + break } - this.cantClear = !(this.clearControl.imgs || this.clearControl.logs || this.clearControl.sc) + this.cantClear = !(this.clearControl.imgs || this.clearControl.logs || this.clearControl.sc || this.clearControl.videoAudio) global.logger.log("[ClearTmp.SelectHandler] cantClear: ", this.cantClear) }, BytesToMB(bytes) { @@ -141,17 +174,27 @@ export default { const contents = await global.savedcontent.SavedContentManager.listAllContent() const ret = await Promise.all( contents.map(async (content) => { - const data = await asyncFile.get({uri: content.fileUri}) - return { - uri: content.fileUri, - length: data.length, - _labelIsSavedContent: true + try { + const fileExists = await asyncFile.access({uri: content.fileUri}) + if (!fileExists) { + return null + } + const data = await asyncFile.get({uri: content.fileUri}) + return { + uri: content.fileUri, + length: data.length, + _labelIsSavedContent: true + } + } catch (e) { + global.logger.log("缓存文件不存在,跳过:" + content.fileUri) + return null } }) ) - return ret + return ret.filter(item => item !== null) } catch (e) { global.logger.error("在扫描临时文件(统计缓存内容)时出错:", e.toString()) + return [] } }, async ScanTemps() { @@ -161,6 +204,7 @@ export default { images: {count: 0, size: 0, list: []}, logs: {count: 0, size: 0, list: []}, savedContent: {count: 0, size: 0}, + videoAudio: {count: 0, size: 0, list: []}, others: {count: 0, size: 0} } @@ -184,6 +228,10 @@ export default { totalStats.logs.count++ totalStats.logs.size += length totalStats.logs.list.push(uri) + } else if (/\.m4s$/i.test(uri)) { + totalStats.videoAudio.count++ + totalStats.videoAudio.size += length + totalStats.videoAudio.list.push(uri) } else if (_labelIsSavedContent) { totalStats.savedContent.count++ totalStats.savedContent.size += length @@ -211,6 +259,12 @@ export default { alias: "sc", size: `${this.BytesToMB(totalStats.savedContent.size)} MB` }, + { + name: "视频音频", + alias: "videoAudio", + size: `${this.BytesToMB(totalStats.videoAudio.size)} MB`, + list: totalStats.videoAudio.list + }, { name: "其它", alias: "others", diff --git a/src/pages/video/player/player.less b/src/pages/video/player/player.less new file mode 100644 index 0000000..2d9cf2e --- /dev/null +++ b/src/pages/video/player/player.less @@ -0,0 +1,601 @@ +@import '@less/defs.less'; +@import '@less/color.less'; + +.player-container { + width: 100%; + height: 100%; +} + +.demo-page { + width: 100%; + height: 100%; + flex-direction: column; + justify-content: space-around; + align-items: center; + background-color: #000; + padding: 0; + position: relative; +} + +.background-image { + width: 432px; + height: 514px; + position: absolute; + top: 0; + left: 0; + object-fit: cover; + opacity: 0.5; + background-size: cover; +} + +.song { + width: 320px; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.song-name { + width: 320px; + font-size: 36px; + color: #ffffff; + lines: 1; + text-overflow: ellipsis; + text-align: center; +} + +.singer-name { + width: 300px; + font-size: 24px; + color: rgba(255, 255, 255, 0.7); + lines: 1; + text-overflow: ellipsis; + text-align: center; +} + +.controls { + width: 360px; + height: 160px; + margin-top: 30px; + justify-content: space-between; + align-items: center; +} + +.play-button { + width: 200px; + height: 200px; + justify-content: center; + align-items: center; +} + +.progress-container { + position: absolute; + left: 0; + top: 0; + width: 200px; + height: 200px; + justify-content: center; + align-items: center; +} + +.play-progress { + radius: 74px; + stroke-width: 14px; + start-angle: 4deg; + total-angle: 360deg; + color: #ffffff; + layer-color: rgba(255, 255, 255, 0.1); +} + +.icon { + width: 48px; + height: 48px; +} + +.control-icon { + width: 64px; + height: 64px; +} + +.footer { + width: 360px; + height: 100px; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0 20px; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 30px; + margin-bottom: 20px; +} + +.new-player-page { + width: 100%; + height: 100%; + flex-direction: column; + justify-content: space-between; + align-items: center; + background-color: #000; + padding: 0; + position: relative; +} + +.new-player-center { + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 40px; +} + +.new-player-cover { + width: 230px; + height: 230px; + border-radius: 20px; + margin-bottom: 10px; +} + +.new-player-song-name { + width: 380px; + font-size: 32px; + color: #ffffff; + font-weight: bold; + text-align: center; + lines: 2; + text-overflow: ellipsis; +} + +.new-player-artist-name { + width: 380px; + font-size: 28px; + color: rgba(255, 255, 255, 0.7); + text-align: center; + lines: 1; + text-overflow: ellipsis; + margin-top: 8px; +} + +.new-player-controls { + width: 360px; + height: 160px; + margin-bottom: 20px; + justify-content: space-between; + align-items: center; +} + +.new-player-progress-container { + position: absolute; + left: 0; + top: 0; + width: 120px; + height: 120px; + justify-content: center; + align-items: center; +} + +.new-player-play-progress { + radius: 44px; + stroke-width: 8px; + start-angle: 4deg; + total-angle: 360deg; + color: #ffffff; + layer-color: rgba(255, 255, 255, 0.1); +} + +.new-player-sidebar { + position: absolute; + right: 20px; + top: 50%; + transform: translateY(-50%); + width: 60px; + height: 400px; + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.features-page { + width: 100%; + height: 100%; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #000; + padding: 0; + position: relative; +} + +.features-sidebar { + position: absolute; + left: 20px; + top: 50%; + transform: translateY(-50%); + width: 60px; + height: 400px; + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.feature-icon { + width: 48px; + height: 48px; +} + +.swiper-container { + width: 100%; + height: 100%; +} + +.swiper-page { + width: 100%; + height: 100%; +} + +.lyric-page { + width: 100%; + height: 100%; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #000; +} + +.lyric-scroll-container { + width: 100%; + height: 100%; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.adjust-progress-overlay { + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + justify-content: center; + align-items: center; + position: absolute; + top: 0; + left: 0; +} + +.adjust-progress-panel { + width: 85%; + background-color: rgba(26, 26, 26, 0.9); + border-radius: 20px; + padding: 30px 20px; + flex-direction: column; + align-items: center; +} + +.adjust-progress-title { + font-size: 36px; + font-weight: 600; + color: #ffffff; + margin-bottom: 20px; +} + +.adjust-progress-time { + font-size: 28px; + color: #cccccc; + margin-bottom: 25px; +} + +.adjust-progress-slider { + width: 100%; + height: 60px; + margin-bottom: 25px; + block-color: #FF7DA8; + selected-color: #FF7DA8; +} + +.adjust-progress-buttons { + width: 100%; + flex-direction: row; + justify-content: space-around; + margin-top: 10px; +} + +.adjust-btn { + width: 140px; + height: 60px; + border-radius: 30px; + background-color: #333333; + justify-content: center; + align-items: center; +} + +.adjust-btn-primary { + background-color: #FF7DA8; +} + +.adjust-btn-text { + font-size: 28px; + font-weight: 600; + color: #ffffff; +} + +.adjust-volume-overlay { + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + justify-content: flex-end; + align-items: center; + position: absolute; + top: 0; + left: 0; +} + +.adjust-volume-panel { + width: 100%; + background-color: rgba(26, 26, 26, 0.9); + border-radius: 20px 20px 0 0; + padding: 30px 20px 50px 20px; + flex-direction: column; + align-items: center; +} + +.adjust-volume-title { + font-size: 32px; + font-weight: 600; + color: #ffffff; + margin-bottom: 15px; +} + +.adjust-volume-value { + font-size: 48px; + font-weight: 700; + color: #FF7DA8; + margin-bottom: 20px; +} + +.adjust-volume-slider { + width: 90%; + height: 60px; + block-color: #FF7DA8; + selected-color: #FF7DA8; +} + +.cache-tips-page { + width: 100%; + height: 100%; + background-color: #000; + position: absolute; + top: 0; + left: 0; +} + +.cache-tips-scroll { + width: 100%; + height: 100%; + flex-direction: column; + align-items: center; +} + +.cache-tips-content { + width: 90%; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 80px 20px; +} + +.cache-tips-title { + font-size: 48px; + font-weight: 600; + color: #ffffff; + margin-bottom: 30px; + text-align: center; +} + +.cache-tips-description { + font-size: 28px; + color: #cccccc; + margin-bottom: 15px; + text-align: center; +} + +.cache-tips-checkbox { + flex-direction: row; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.cache-remember-checkbox { + width: 42px; + height: 42px; + background-color: #666666; +} + +.cache-remember-text { + font-size: 24px; + color: #999999; + margin-left: 10px; +} + +.cache-tips-buttons { + width: 100%; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 40px; +} + +.cache-btn { + width: 322px; + height: 80px; + border-radius: 90px; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.cache-btn-primary { + background-color: #FF7DA8; +} + +.cache-btn-secondary { + background-color: #262626; +} + +.cache-btn-text { + font-size: 40px; + font-weight: 600; + color: #ffffff; +} + +.applemusic-lyric-container { + width: 100%; + height: 100%; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.applemusic-lyric-scroll { + flex-direction: column; + flex: 1; + padding-top: 0; + width: 100%; +} + +.applemusic-lyric-list { + flex-direction: column; + padding: 150px 40px; +} + +.applemusic-lyric-normal { + width: 100%; + font-size: 32px; + color: #ffffff; + text-align: left; + padding: 16px 0; + height: auto; + min-height: 50px; + lines: 3; + text-overflow: ellipsis; + transition: all 0.3s ease; + font-weight: bold; +} + +.applemusic-lyric-active { + width: 100%; + font-size: 38px; + color: #ffffff; + text-align: left; + padding: 16px 0; + height: auto; + min-height: 60px; + lines: 3; + text-overflow: ellipsis; + font-weight: bold; + transition: all 0.3s ease; +} + +@media (min-width: 200px) and (max-width: 300px) { + .background-image { + width: 466px; + height: 466px; + } + + .song { + width: 300px; + } + + .song-name { + width: 300px; + font-size: 32px; + } + + .singer-name { + width: 280px; + font-size: 22px; + } + + .controls { + width: 340px; + height: 150px; + margin-top: 20px; + } + + .play-button { + width: 180px; + height: 180px; + } + + .progress-container { + width: 180px; + height: 180px; + } + + .play-progress { + radius: 66px; + stroke-width: 12px; + } + + .new-player-progress-container { + width: 120px; + height: 120px; + } + + .new-player-play-progress { + radius: 44px; + stroke-width: 8px; + } + + .footer { + width: 340px; + height: 90px; + margin-bottom: 15px; + } + + .icon { + width: 44px; + height: 44px; + } + + .control-icon { + width: 58px; + height: 58px; + } + + .applemusic-lyric-list { + padding: 120px 40px; + } + + .applemusic-lyric-normal { + font-size: 30px; + padding: 14px 0; + min-height: 48px; + } + + .applemusic-lyric-active { + font-size: 36px; + padding: 14px 0; + min-height: 55px; + } +} + +@media (min-width: 210px) and (max-width: 230px) { + .background-image { + width: 432px; + height: 514px; + } + + .applemusic-lyric-list { + padding: 150px 40px; + } + + .applemusic-lyric-normal { + font-size: 32px; + padding: 16px 0; + min-height: 50px; + } + + .applemusic-lyric-active { + font-size: 38px; + padding: 16px 0; + min-height: 60px; + } +} diff --git a/src/pages/video/player/player.ux b/src/pages/video/player/player.ux new file mode 100644 index 0000000..a180f99 --- /dev/null +++ b/src/pages/video/player/player.ux @@ -0,0 +1,1533 @@ + + + \ No newline at end of file diff --git a/src/pages/video/videodetail/videodetail.ux b/src/pages/video/videodetail/videodetail.ux index 908e518..ae1bcd9 100644 --- a/src/pages/video/videodetail/videodetail.ux +++ b/src/pages/video/videodetail/videodetail.ux @@ -22,6 +22,10 @@ +
+ + 听视频 +
{{ stat_like }} @@ -99,6 +103,33 @@ export default { message: "该功能仍在开发中" }) }, + async OpenPlayer() { + const exists = await global.savedcontent.SavedContentManager.checkVideoAudioExists(this.bvid) + if (exists) { + const contents = await global.savedcontent.SavedContentManager.listAllContent() + const cachedItem = contents.find((ct) => ct.bvid === this.bvid && ct.type === 'videoAudio') + if (cachedItem) { + prompt.showToast({ message: '正在加载缓存...', duration: 1000 }) + router.push({ + uri: "pages/video/player", + params: { + bvid: this.bvid, + cachedAudioUri: cachedItem.fileUri, + cachedTitle: cachedItem.title, + cachedCoverUrl: cachedItem.coverUrl, + cachedAuthor: cachedItem.author + } + }) + return + } + } + router.push({ + uri: "pages/video/player", + params: { + bvid: this.bvid + } + }) + }, OpenOwnerHome() { router.push({ uri: "pages/user", @@ -217,4 +248,4 @@ export default { \ No newline at end of file + diff --git a/src/savedcontent.ts b/src/savedcontent.ts index af21c30..e79f3b4 100644 --- a/src/savedcontent.ts +++ b/src/savedcontent.ts @@ -1,19 +1,19 @@ import { asyncFile } from "./asyncapi/file"; -// 定义文件存储的基本结构 interface StoredContent { id: string; title: string; type: string; fileUri: string; + coverUrl?: string; + author?: string; + bvid?: string; } -// 模拟存储的内容列表 let storageIndex: StoredContent[] = []; -// 定义文件存储的基础路径 const baseUri = 'internal://files/bilisavedcontent/'; -const indexFileUri = `${baseUri}index.json`; // 存储 storageIndex 的文件 +const indexFileUri = `${baseUri}index.json`; // 同步 storageIndex 到本地文件 async function saveStorageIndex(): Promise { @@ -66,10 +66,8 @@ export class SavedContentManager { const id = generateUUID(); const fileUri = `${baseUri}${id}.txt`; - // 写入文件 await asyncFile.writeText({ uri: fileUri, text: data }); - // 更新 storageIndex 并保存 storageIndex.push({ id, title, type, fileUri }); await saveStorageIndex(); @@ -79,6 +77,35 @@ export class SavedContentManager { } } + // 存储视频音频(直接引用已存在的文件) + static async storeVideoAudio(title: string, audioUri: string, coverUrl: string, author: string, bvid: string): Promise { + try { + const id = generateUUID(); + + storageIndex.push({ + id, + title, + type: 'videoAudio', + fileUri: audioUri, + coverUrl, + author, + bvid + }); + await saveStorageIndex(); + + global.logger.log(`[SavedContentManager] 视频音频已保存: ${title}, id: ${id}`); + return id; + } catch (e) { + global.logger.error(`[SavedContentManager] storeVideoAudio Error: ${e.toString()}`); + } + } + + // 检查视频音频是否已存在 + static async checkVideoAudioExists(bvid: string): Promise { + const exists = storageIndex.some(item => item.bvid === bvid && item.type === 'videoAudio'); + return exists; + } + // 根据 id 或 title 读取内容 static async getContent(identifier: string): Promise { try { @@ -87,7 +114,10 @@ export class SavedContentManager { // 读取文件内容 const fileExists = await asyncFile.access({ uri: content.fileUri }); - if (!fileExists) throw new Error(`File does not exist: ${content.fileUri}`); + if (!fileExists) { + global.logger.log(`[SavedContentManager] 文件不存在:${content.fileUri}`); + return null; + } return await asyncFile.readText({ uri: content.fileUri }); } catch (e) { @@ -107,9 +137,12 @@ export class SavedContentManager { const contentIndex = storageIndex.findIndex(item => item.id === identifier || item.title === identifier); if (contentIndex === -1) throw new Error('Content not found'); - // 删除文件 + // 删除文件(如果文件存在) const fileUri = storageIndex[contentIndex].fileUri; - await asyncFile.delete({ uri: fileUri }); + const fileExists = await asyncFile.access({ uri: fileUri }); + if (fileExists) { + await asyncFile.delete({ uri: fileUri }); + } // 从 storageIndex 中删除记录并保存 storageIndex.splice(contentIndex, 1);