Skip to content

Commit c75ebd8

Browse files
committed
添加网易云歌单小部件示例,修复help.ts通知api没声的bug
1 parent 7a07f5d commit c75ebd8

File tree

4 files changed

+1138
-1
lines changed

4 files changed

+1138
-1
lines changed

src/lib/help.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ export async function showNotification(args: ShowNotificationParams): Promise<vo
589589
notification.subtitle = subtitle
590590
notification.body = body
591591
openURL && (notification.openURL = openURL)
592-
sound && notification.sound
592+
sound && (notification.sound = sound)
593593
notification = Object.assign(notification, others)
594594
return await notification.schedule()
595595
}
@@ -663,6 +663,17 @@ export function hash(string: string): string {
663663
return `hash_${hash}`
664664
}
665665

666+
/**
667+
* 获取范围内随机整数
668+
* @param min 最小值
669+
* @param max 最大值
670+
*/
671+
export function getRandomInt(min: number, max: number): number {
672+
min = Math.ceil(min)
673+
max = Math.floor(max)
674+
return Math.floor(Math.random() * (max - min + 1)) + min
675+
}
676+
666677
/**
667678
* 是否在 app 内启动脚本
668679
*/

src/scripts/funds.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* 基金小部件
3+
*/
4+
15
import {
26
isLaunchInsideApp,
37
request,

src/scripts/music163.tsx

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
/**
2+
* 网易云歌单小部件
3+
*/
4+
5+
import {
6+
getRandomInt,
7+
isLaunchInsideApp,
8+
request,
9+
showActionSheet,
10+
showModal,
11+
showNotification,
12+
showPreviewOptions,
13+
useStorage,
14+
} from '@app/lib/help'
15+
import {WstackProps} from '@app/types/widget'
16+
import {FC} from 'react'
17+
18+
/**网易云歌单数据格式*/
19+
interface NetmusicListData {
20+
code: number
21+
playlist: Playlist
22+
}
23+
24+
interface Playlist {
25+
/**歌曲信息存放*/
26+
tracks: Track[]
27+
}
28+
29+
interface Track {
30+
name: string
31+
id: number
32+
pst: number
33+
t: number
34+
alia: string[]
35+
pop: number
36+
st: number
37+
rt?: string
38+
fee: number
39+
v: number
40+
crbt?: unknown
41+
cf: string
42+
43+
/**该首歌曲信息*/
44+
al: MusicInfo
45+
dt: number
46+
a?: unknown
47+
cd: string
48+
no: number
49+
rtUrl?: unknown
50+
ftype: number
51+
rtUrls: unknown[]
52+
djId: number
53+
copyright: number
54+
s_id: number
55+
mark: number
56+
originCoverType: number
57+
noCopyrightRcmd?: unknown
58+
rtype: number
59+
rurl?: unknown
60+
mst: number
61+
cp: number
62+
mv: number
63+
publishTime: number
64+
alg: string
65+
}
66+
67+
/**歌曲信息*/
68+
interface MusicInfo {
69+
/**歌曲 id */
70+
id: number
71+
72+
/**歌曲名字*/
73+
name: string
74+
75+
/**歌曲封面图*/
76+
picUrl: string
77+
78+
tns: unknown[]
79+
pic_str: string
80+
pic: number
81+
}
82+
83+
const {setStorage, getStorage} = useStorage('music163-grid')
84+
85+
// Favorite 歌单ID
86+
const favoriteListId = getStorage<number>('favoriteListId') || 3136952023
87+
88+
// Like 歌单ID
89+
const likeListId = getStorage<number>('likeListId') || 310970433
90+
91+
// Cloud 歌单ID
92+
const cloudListId = getStorage<number>('cloudListId') || 2463071445
93+
94+
// 文字颜色
95+
const textColor = '#ffffff'
96+
97+
/**横向居中组件*/
98+
const RowCenter: FC<WstackProps> = ({children, ...props}) => {
99+
return (
100+
<wstack {...props}>
101+
<wspacer></wspacer>
102+
{children}
103+
<wspacer></wspacer>
104+
</wstack>
105+
)
106+
}
107+
108+
/**纵向居中组件*/
109+
const ColCenter: FC<WstackProps> = ({children, ...props}) => {
110+
return (
111+
<wstack {...props} flexDirection="column">
112+
<wspacer></wspacer>
113+
{children}
114+
<wspacer></wspacer>
115+
</wstack>
116+
)
117+
}
118+
119+
/**纵横都居中的组件*/
120+
const Center: FC<WstackProps> = ({children, ...props}) => {
121+
return (
122+
<RowCenter {...props}>
123+
<ColCenter>{children}</ColCenter>
124+
</RowCenter>
125+
)
126+
}
127+
128+
/**格子组件参数*/
129+
interface GridProps {
130+
/** icon 名字*/
131+
iconName: string
132+
133+
/**格子背景*/
134+
background: WstackProps['background']
135+
136+
/**文字*/
137+
text: string
138+
139+
/**点击格子跳转的链接*/
140+
href: string
141+
}
142+
143+
/**格子组件*/
144+
const Grid: FC<GridProps> = ({...props}) => {
145+
const {iconName, background, text, href} = props
146+
return (
147+
<wstack background={background} href={href}>
148+
<Center background="#00000033">
149+
<wstack flexDirection="column">
150+
<RowCenter>
151+
<wimage src={iconName} filter={textColor} width={20} height={20}></wimage>
152+
</RowCenter>
153+
<RowCenter>
154+
<wtext font={new Font('heavymenlo', 12.5)} textColor={textColor}>
155+
{text}
156+
</wtext>
157+
</RowCenter>
158+
</wstack>
159+
</Center>
160+
</wstack>
161+
)
162+
}
163+
164+
class Music163 {
165+
async init() {
166+
if (isLaunchInsideApp()) {
167+
return await this.showMenu()
168+
}
169+
const widget = (await this.render()) as ListWidget
170+
Script.setWidget(widget)
171+
Script.complete()
172+
}
173+
174+
//渲染组件
175+
async render(): Promise<unknown> {
176+
if (isLaunchInsideApp()) {
177+
await showNotification({title: '稍等片刻', body: '小部件渲染中...', sound: 'alert'})
178+
}
179+
const favoriteImageUrl = ((await this.getRandomMusic(favoriteListId)) || {}).picUrl
180+
const likeImageUrl = ((await this.getRandomMusic(likeListId)) || {}).picUrl
181+
const cloudImageUrl = ((await this.getRandomMusic(cloudListId)) || {}).picUrl
182+
// 多久(毫秒)更新一次小部件(默认3小时)
183+
const updateInterval = 3 * 60 * 60 * 1000
184+
return (
185+
<wbox padding={[0, 0, 0, 0]} updateDate={new Date(Date.now() + updateInterval)}>
186+
<wstack>
187+
<Grid
188+
iconName="heart.fill"
189+
text="Favorite"
190+
background={favoriteImageUrl || '#d65151'}
191+
href={'orpheus://playlist/' + favoriteListId + '?autoplay=1'}
192+
></Grid>
193+
<wstack flexDirection="column">
194+
<wstack>
195+
<Grid
196+
iconName="star.fill"
197+
text="Like"
198+
background={likeImageUrl || '#5ebb07'}
199+
href={'orpheus://playlist/' + likeListId + '?autoplay=1'}
200+
></Grid>
201+
<Grid
202+
iconName="person.icloud.fill"
203+
text="Cloud"
204+
background={cloudImageUrl || '#0fb196'}
205+
href={'orpheus://playlist/' + cloudListId + '?autoplay=1'}
206+
></Grid>
207+
</wstack>
208+
<wstack>
209+
<Grid iconName="calendar" text="Daily" background="#fe9500" href="orpheus://songrcmd?autoplay=1"></Grid>
210+
<Grid iconName="radio.fill" text="FM" background="#000000" href="orpheuswidget://radio"></Grid>
211+
</wstack>
212+
</wstack>
213+
</wstack>
214+
</wbox>
215+
)
216+
}
217+
218+
// 显示菜单
219+
async showMenu() {
220+
const selectIndex = await showActionSheet({
221+
title: '菜单',
222+
itemList: ['自定义歌单', '预览组件'],
223+
})
224+
let musicListId: number | null
225+
switch (selectIndex) {
226+
case 0:
227+
const {texts} = await showModal({
228+
title: '设置歌单',
229+
content: '去网易云歌单 -> 分享 -> 复制链接, 然后粘贴到此',
230+
inputItems: [
231+
{
232+
placeholder: '这里填 Favorite 的歌单链接',
233+
},
234+
{
235+
placeholder: '这里填 Like 的歌单链接',
236+
},
237+
{
238+
placeholder: '这里填 Cloud 的歌单链接',
239+
},
240+
],
241+
})
242+
if (texts[0]) {
243+
musicListId = this.getListIdFromLink(texts[0])
244+
musicListId && setStorage('favoriteListId', musicListId)
245+
!musicListId &&
246+
(await showNotification({
247+
title: '歌单链接错误',
248+
body: 'Favorite 的歌单链接检测不到歌单 id ',
249+
sound: 'failure',
250+
}))
251+
}
252+
if (texts[1]) {
253+
musicListId = this.getListIdFromLink(texts[1])
254+
musicListId && setStorage('likeListId', musicListId)
255+
!musicListId &&
256+
(await showNotification({title: '歌单链接错误', body: 'Like 的歌单链接检测不到歌单 id ', sound: 'failure'}))
257+
}
258+
if (texts[2]) {
259+
musicListId = this.getListIdFromLink(texts[2])
260+
musicListId && setStorage('cloudListId', musicListId)
261+
!musicListId &&
262+
(await showNotification({
263+
title: '歌单链接错误',
264+
body: 'cloud 的歌单链接检测不到歌单 id ',
265+
sound: 'failure',
266+
}))
267+
}
268+
await showNotification({title: '设置完成', sound: 'default'})
269+
break
270+
case 1:
271+
await showPreviewOptions(this.render.bind(this))
272+
break
273+
}
274+
}
275+
/**
276+
* 根据歌单链接获取歌单 id
277+
* @param musicListLink 歌单链接
278+
*/
279+
getListIdFromLink(musicListLink: string): number | null {
280+
return Number((musicListLink.match(/\&id\=([\d]+)/) || [])[1]) || null
281+
}
282+
283+
/**
284+
* 根据歌单id获取歌单数据
285+
* @param musicListId 歌单 id
286+
**/
287+
async getMusicListData(musicListId: number): Promise<Track[]> {
288+
let tracks: Track[] = []
289+
try {
290+
tracks =
291+
(
292+
await request<NetmusicListData>({
293+
url: `https://api.imjad.cn/cloudmusic/?type=playlist&id=${musicListId}`,
294+
dataType: 'json',
295+
})
296+
).data?.playlist.tracks || []
297+
} catch (err) {
298+
console.warn(`获取歌单数据失败:${err}`)
299+
}
300+
return tracks
301+
}
302+
303+
/**
304+
* 从歌曲列表里随机选一首歌曲,返回该首歌曲信息
305+
* @param musicListId 歌单id
306+
*/
307+
async getRandomMusic(musicListId: number): Promise<MusicInfo | null> {
308+
const tracks = await this.getMusicListData(musicListId)
309+
if (tracks.length <= 0) {
310+
await showNotification({title: `歌单ID${musicListId}获取出错`, body: '该歌单没有歌曲或获取歌曲失败'})
311+
return null
312+
}
313+
return tracks[getRandomInt(0, tracks.length - 1)].al
314+
}
315+
}
316+
317+
new Music163().init()

0 commit comments

Comments
 (0)