@unavatar/core 3.43.0 → 3.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/package.json +1 -1
- package/src/providers/bilibili.js +27 -0
- package/src/providers/deezer.js +32 -0
- package/src/providers/douban.js +15 -0
- package/src/providers/gitee.js +22 -0
- package/src/providers/index.js +21 -2
- package/src/providers/juejin.js +26 -0
- package/src/providers/netease-music.js +26 -0
- package/src/providers/qq.js +13 -0
- package/src/providers/tieba.js +36 -0
- package/src/providers/weibo.js +36 -0
- package/src/providers/zhihu.js +28 -0
package/README.md
CHANGED
|
@@ -19,21 +19,26 @@
|
|
|
19
19
|
- [Providers](#providers)
|
|
20
20
|
- [Apple Music](#apple-music)
|
|
21
21
|
- [Apple Store](#apple-store)
|
|
22
|
+
- [Baidu Tieba](#baidu-tieba)
|
|
22
23
|
- [Behance](#behance)
|
|
24
|
+
- [Bilibili](#bilibili)
|
|
23
25
|
- [Bluesky](#bluesky)
|
|
24
26
|
- [Buy Me a Coffee](#buy-me-a-coffee)
|
|
25
27
|
- [CodePen](#codepen)
|
|
26
28
|
- [Cults3D](#cults3d)
|
|
27
29
|
- [Cursor](#cursor)
|
|
30
|
+
- [Deezer](#deezer)
|
|
28
31
|
- [DeviantArt](#deviantart)
|
|
29
32
|
- [Discord](#discord)
|
|
30
33
|
- [Docker Hub](#docker-hub)
|
|
31
34
|
- [Domain](#domain)
|
|
35
|
+
- [Douban](#douban)
|
|
32
36
|
- [Dribbble](#dribbble)
|
|
33
37
|
- [DuckDuckGo](#duckduckgo)
|
|
34
38
|
- [Email](#email)
|
|
35
39
|
- [Facebook](#facebook)
|
|
36
40
|
- [Flickr](#flickr)
|
|
41
|
+
- [Gitee](#gitee)
|
|
37
42
|
- [GitHub](#github)
|
|
38
43
|
- [GitLab](#gitlab)
|
|
39
44
|
- [Google](#google)
|
|
@@ -42,11 +47,13 @@
|
|
|
42
47
|
- [Hevy](#hevy)
|
|
43
48
|
- [Hugging Face](#hugging-face)
|
|
44
49
|
- [Instagram](#instagram)
|
|
50
|
+
- [Juejin](#juejin)
|
|
45
51
|
- [Ko-fi](#ko-fi)
|
|
46
52
|
- [LinkedIn](#linkedin)
|
|
47
53
|
- [Mastodon](#mastodon)
|
|
48
54
|
- [Medium](#medium)
|
|
49
55
|
- [Microlink](#microlink)
|
|
56
|
+
- [NetEase Music](#netease-music)
|
|
50
57
|
- [npm](#npm)
|
|
51
58
|
- [OnlyFans](#onlyfans)
|
|
52
59
|
- [OpenStreetMap](#openstreetmap)
|
|
@@ -56,6 +63,7 @@
|
|
|
56
63
|
- [Printables](#printables)
|
|
57
64
|
- [Product Hunt](#product-hunt)
|
|
58
65
|
- [PSN Profiles](#psn-profiles)
|
|
66
|
+
- [QQ](#qq)
|
|
59
67
|
- [Raycast](#raycast)
|
|
60
68
|
- [Reddit](#reddit)
|
|
61
69
|
- [Snapchat](#snapchat)
|
|
@@ -73,10 +81,12 @@
|
|
|
73
81
|
- [Tumblr](#tumblr)
|
|
74
82
|
- [Twitch](#twitch)
|
|
75
83
|
- [Vimeo](#vimeo)
|
|
84
|
+
- [Weibo](#weibo)
|
|
76
85
|
- [WhatsApp](#whatsapp)
|
|
77
86
|
- [X/Twitter](#xtwitter)
|
|
78
87
|
- [Xbox Gamertag](#xbox-gamertag)
|
|
79
88
|
- [YouTube](#youtube)
|
|
89
|
+
- [Zhihu](#zhihu)
|
|
80
90
|
- [Response Format](#response-format)
|
|
81
91
|
- [Response Headers](#response-headers)
|
|
82
92
|
- [Response Errors](#response-errors)
|
|
@@ -557,12 +567,24 @@ Available inputs:
|
|
|
557
567
|
- `id` (default): [unavatar.io/apple-store/id:529479190](https://unavatar.io/apple-store/id:529479190) or [unavatar.io/apple-store/id:6474323148@es](https://unavatar.io/apple-store/id:6474323148@es)
|
|
558
568
|
- `name`: [unavatar.io/apple-store/name:pokemon%20go](https://unavatar.io/apple-store/name:pokemon%20go)
|
|
559
569
|
|
|
570
|
+
### Baidu Tieba
|
|
571
|
+
|
|
572
|
+
Get any Baidu Tieba user's profile picture by their username.
|
|
573
|
+
|
|
574
|
+
e.g., [unavatar.io/tieba/Mojang](https://unavatar.io/tieba/Mojang)
|
|
575
|
+
|
|
560
576
|
### Behance
|
|
561
577
|
|
|
562
578
|
Get any Behance user's profile picture by their username.
|
|
563
579
|
|
|
564
580
|
e.g., [unavatar.io/behance/vitormatosinhos](https://unavatar.io/behance/vitormatosinhos)
|
|
565
581
|
|
|
582
|
+
### Bilibili
|
|
583
|
+
|
|
584
|
+
Get any Bilibili user's profile picture by their numeric uid.
|
|
585
|
+
|
|
586
|
+
e.g., [unavatar.io/bilibili/2](https://unavatar.io/bilibili/2)
|
|
587
|
+
|
|
566
588
|
### Bluesky
|
|
567
589
|
|
|
568
590
|
Get any Bluesky user's profile picture by their handle. Domain-style handles are supported.
|
|
@@ -596,6 +618,21 @@ Get any Cursor user's profile picture by their username.
|
|
|
596
618
|
|
|
597
619
|
e.g., [unavatar.io/cursor/leerob](https://unavatar.io/cursor/leerob)
|
|
598
620
|
|
|
621
|
+
### Deezer
|
|
622
|
+
|
|
623
|
+
Get artwork for any Deezer entity — artists, albums, playlists, or tracks. Look up by Deezer ID.
|
|
624
|
+
|
|
625
|
+
By default the input is treated as an artist:
|
|
626
|
+
|
|
627
|
+
e.g., [unavatar.io/deezer/27](https://unavatar.io/deezer/27)
|
|
628
|
+
|
|
629
|
+
The input also supports a URI format `type:id`:
|
|
630
|
+
|
|
631
|
+
- `artist` (default): [unavatar.io/deezer/artist:27](https://unavatar.io/deezer/artist:27)
|
|
632
|
+
- `album`: [unavatar.io/deezer/album:302127](https://unavatar.io/deezer/album:302127)
|
|
633
|
+
- `playlist`: [unavatar.io/deezer/playlist:908622995](https://unavatar.io/deezer/playlist:908622995)
|
|
634
|
+
- `track`: [unavatar.io/deezer/track:3135556](https://unavatar.io/deezer/track:3135556)
|
|
635
|
+
|
|
599
636
|
### DeviantArt
|
|
600
637
|
|
|
601
638
|
Get any DeviantArt user's profile picture by their username.
|
|
@@ -623,6 +660,12 @@ Retrieve logos and favicons from any hostname with a single URL. Domain-shaped p
|
|
|
623
660
|
|
|
624
661
|
e.g., [unavatar.io/domain/microlink.io](https://unavatar.io/domain/microlink.io)
|
|
625
662
|
|
|
663
|
+
### Douban
|
|
664
|
+
|
|
665
|
+
Get any Douban user's profile picture by their username or numeric ID.
|
|
666
|
+
|
|
667
|
+
e.g., [unavatar.io/douban/ahbei](https://unavatar.io/douban/ahbei)
|
|
668
|
+
|
|
626
669
|
### Dribbble
|
|
627
670
|
|
|
628
671
|
Get any Dribbble designer's profile picture by their username.
|
|
@@ -679,6 +722,12 @@ Available inputs:
|
|
|
679
722
|
- `user` (default): [unavatar.io/flickr/user:elizabethgadd](https://unavatar.io/flickr/user:elizabethgadd)
|
|
680
723
|
- `group`: [unavatar.io/flickr/group:allthingsearthy](https://unavatar.io/flickr/group:allthingsearthy)
|
|
681
724
|
|
|
725
|
+
### Gitee
|
|
726
|
+
|
|
727
|
+
Get any Gitee user or organization's profile picture by their username.
|
|
728
|
+
|
|
729
|
+
e.g., [unavatar.io/gitee/y_project](https://unavatar.io/gitee/y_project)
|
|
730
|
+
|
|
682
731
|
### GitHub
|
|
683
732
|
|
|
684
733
|
Get any GitHub user or organization's profile picture by username, or resolve an avatar from a public email via GitHub search when the address matches a profile or commit history.
|
|
@@ -752,6 +801,12 @@ Get any Instagram user's profile picture by their username. No authentication or
|
|
|
752
801
|
|
|
753
802
|
e.g., [unavatar.io/instagram/willsmith](https://unavatar.io/instagram/willsmith)
|
|
754
803
|
|
|
804
|
+
### Juejin
|
|
805
|
+
|
|
806
|
+
Get any Juejin user's profile picture by their numeric user ID.
|
|
807
|
+
|
|
808
|
+
e.g., [unavatar.io/juejin/1556564194374926](https://unavatar.io/juejin/1556564194374926)
|
|
809
|
+
|
|
755
810
|
### Ko-fi
|
|
756
811
|
|
|
757
812
|
Get any Ko-fi page's profile picture by the creator username.
|
|
@@ -787,6 +842,12 @@ Extract the logo or representative image from any URL. The page is rendered and
|
|
|
787
842
|
|
|
788
843
|
e.g., [unavatar.io/microlink/microlink.io](https://unavatar.io/microlink/microlink.io)
|
|
789
844
|
|
|
845
|
+
### NetEase Music
|
|
846
|
+
|
|
847
|
+
Get any NetEase Cloud Music user's profile picture by their numeric user ID.
|
|
848
|
+
|
|
849
|
+
e.g., [unavatar.io/netease-music/32953014](https://unavatar.io/netease-music/32953014)
|
|
850
|
+
|
|
790
851
|
### npm
|
|
791
852
|
|
|
792
853
|
Get any npm user's profile picture by their username, including scoped handles.
|
|
@@ -851,6 +912,14 @@ Get any PlayStation Network user's profile picture by their PSN username.
|
|
|
851
912
|
|
|
852
913
|
e.g., [unavatar.io/psnprofiles/Duff85](https://unavatar.io/psnprofiles/Duff85)
|
|
853
914
|
|
|
915
|
+
### QQ
|
|
916
|
+
|
|
917
|
+
Get any QQ user's profile picture by their QQ number.
|
|
918
|
+
|
|
919
|
+
e.g., [unavatar.io/qq/10001](https://unavatar.io/qq/10001)
|
|
920
|
+
|
|
921
|
+
Note: unknown numbers resolve to QQ's default avatar, similar to Gravatar.
|
|
922
|
+
|
|
854
923
|
### Raycast
|
|
855
924
|
|
|
856
925
|
Get any Raycast user's profile picture by their username.
|
|
@@ -986,6 +1055,12 @@ Get any Vimeo user's profile picture by their username.
|
|
|
986
1055
|
|
|
987
1056
|
e.g., [unavatar.io/vimeo/ladieswithlenses](https://unavatar.io/vimeo/ladieswithlenses)
|
|
988
1057
|
|
|
1058
|
+
### Weibo
|
|
1059
|
+
|
|
1060
|
+
Get any Weibo user's profile picture by their numeric uid or nickname.
|
|
1061
|
+
|
|
1062
|
+
e.g., [unavatar.io/weibo/2803301701](https://unavatar.io/weibo/2803301701)
|
|
1063
|
+
|
|
989
1064
|
### WhatsApp
|
|
990
1065
|
|
|
991
1066
|
Get the profile picture for a WhatsApp channel or chat by ID.
|
|
@@ -1025,6 +1100,12 @@ Available inputs:
|
|
|
1025
1100
|
- `username`: [unavatar.io/youtube/casey](https://unavatar.io/youtube/casey)
|
|
1026
1101
|
- `channel`: [unavatar.io/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw](https://unavatar.io/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw)
|
|
1027
1102
|
|
|
1103
|
+
### Zhihu
|
|
1104
|
+
|
|
1105
|
+
Get any Zhihu user's profile picture by their URL token (the handle in the profile URL).
|
|
1106
|
+
|
|
1107
|
+
e.g., [unavatar.io/zhihu/kaifulee](https://unavatar.io/zhihu/kaifulee)
|
|
1108
|
+
|
|
1028
1109
|
## Response Format
|
|
1029
1110
|
|
|
1030
1111
|
A response is returning the user avatar by default.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@unavatar/core",
|
|
3
3
|
"description": "Get unified user avatar from social networks, including Instagram, SoundCloud, Telegram, Twitter, YouTube & more.",
|
|
4
4
|
"homepage": "https://unavatar.io",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.45.0",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://api.bilibili.com/x/web-interface/card'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}?mid=${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
// Users without a custom avatar get the generic `noface.jpg` placeholder.
|
|
8
|
+
const getAvatar = body => {
|
|
9
|
+
if (body?.code !== 0) return
|
|
10
|
+
const face = body.data?.card?.face
|
|
11
|
+
return face && !face.endsWith('/noface.jpg') ? face : undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = ({ got }) =>
|
|
15
|
+
async function bilibili (input) {
|
|
16
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
17
|
+
responseType: 'json',
|
|
18
|
+
throwHttpErrors: false
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (statusCode >= 400) return
|
|
22
|
+
|
|
23
|
+
return getAvatar(body)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
27
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Entities without artwork keep an empty image hash
|
|
4
|
+
// (cdn-images.dzcdn.net/images/artist//500x500.jpg), which resolves to a
|
|
5
|
+
// generic placeholder, so any empty hash segment is treated as a miss.
|
|
6
|
+
const isArtwork = url => !/\/images\/[^/]+\/\/\d/.test(url)
|
|
7
|
+
|
|
8
|
+
const getAvatarUrl = input => {
|
|
9
|
+
const [first, second] = input.split(':')
|
|
10
|
+
const type = second ? first : 'artist'
|
|
11
|
+
const id = second ?? first
|
|
12
|
+
return `https://www.deezer.com/en/${type}/${id}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Deezer server-renders og:image for missing entities as well, so the empty
|
|
16
|
+
// placeholder is filtered out and everything else is returned as-is.
|
|
17
|
+
const getAvatar = ({ $, getOgImage, NOT_FOUND }) => {
|
|
18
|
+
const image = getOgImage($)
|
|
19
|
+
return image && isArtwork(image) ? image : NOT_FOUND
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const factory = ({ createHtmlProvider, getOgImage, NOT_FOUND }) =>
|
|
23
|
+
createHtmlProvider({
|
|
24
|
+
name: 'deezer',
|
|
25
|
+
url: getAvatarUrl,
|
|
26
|
+
getter: $ => getAvatar({ $, getOgImage, NOT_FOUND })
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
factory.getAvatarUrl = getAvatarUrl
|
|
30
|
+
factory.getAvatar = getAvatar
|
|
31
|
+
|
|
32
|
+
module.exports = factory
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const getAvatarUrl = input => `https://www.douban.com/people/${input}/`
|
|
4
|
+
|
|
5
|
+
const getAvatar = $ => $('img.userface').attr('src')
|
|
6
|
+
|
|
7
|
+
module.exports = ({ createHtmlProvider }) =>
|
|
8
|
+
createHtmlProvider({
|
|
9
|
+
name: 'douban',
|
|
10
|
+
url: getAvatarUrl,
|
|
11
|
+
getter: getAvatar
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
15
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://gitee.com/api/v5/users'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}/${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
const getAvatar = body => body?.avatar_url || undefined
|
|
8
|
+
|
|
9
|
+
module.exports = ({ got }) =>
|
|
10
|
+
async function gitee (input) {
|
|
11
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
12
|
+
responseType: 'json',
|
|
13
|
+
throwHttpErrors: false
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
if (statusCode >= 400) return
|
|
17
|
+
|
|
18
|
+
return getAvatar(body)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
22
|
+
module.exports.getAvatar = getAvatar
|
package/src/providers/index.js
CHANGED
|
@@ -6,28 +6,34 @@ const providersBy = {
|
|
|
6
6
|
'apple-music',
|
|
7
7
|
'apple-store',
|
|
8
8
|
'behance',
|
|
9
|
+
'bilibili',
|
|
9
10
|
'bluesky',
|
|
10
11
|
'buymeacoffee',
|
|
11
12
|
'cashapp',
|
|
12
13
|
'codepen',
|
|
13
14
|
'cults3d',
|
|
14
15
|
'cursor',
|
|
16
|
+
'deezer',
|
|
15
17
|
'deviantart',
|
|
16
18
|
'discord',
|
|
17
19
|
'dockerhub',
|
|
20
|
+
'douban',
|
|
18
21
|
'dribbble',
|
|
19
22
|
'facebook',
|
|
20
23
|
'flickr',
|
|
24
|
+
'gitee',
|
|
21
25
|
'github',
|
|
22
26
|
'gitlab',
|
|
23
27
|
'google-play',
|
|
24
28
|
'huggingface',
|
|
25
29
|
'hevy',
|
|
26
30
|
'instagram',
|
|
31
|
+
'juejin',
|
|
27
32
|
'ko-fi',
|
|
28
33
|
'linkedin',
|
|
29
34
|
'mastodon',
|
|
30
35
|
'medium',
|
|
36
|
+
'netease-music',
|
|
31
37
|
'npm',
|
|
32
38
|
'onlyfans',
|
|
33
39
|
'openstreetmap',
|
|
@@ -52,15 +58,18 @@ const providersBy = {
|
|
|
52
58
|
'thingiverse',
|
|
53
59
|
'threads',
|
|
54
60
|
'tidal',
|
|
61
|
+
'tieba',
|
|
55
62
|
'tiktok',
|
|
56
63
|
'tumblr',
|
|
57
64
|
'twitch',
|
|
58
65
|
'venmo',
|
|
59
66
|
'vimeo',
|
|
67
|
+
'weibo',
|
|
60
68
|
'wise',
|
|
61
69
|
'x',
|
|
62
70
|
'xboxgamertag',
|
|
63
|
-
'youtube'
|
|
71
|
+
'youtube',
|
|
72
|
+
'zhihu'
|
|
64
73
|
],
|
|
65
74
|
domain: ['microlink']
|
|
66
75
|
}
|
|
@@ -70,19 +79,23 @@ module.exports = ctx => {
|
|
|
70
79
|
'apple-music': require('./apple-music')(ctx),
|
|
71
80
|
'apple-store': require('./apple-store')(ctx),
|
|
72
81
|
behance: require('./behance')(ctx),
|
|
82
|
+
bilibili: require('./bilibili')(ctx),
|
|
73
83
|
bluesky: require('./bluesky')(ctx),
|
|
74
84
|
buymeacoffee: require('./buymeacoffee')(ctx),
|
|
75
85
|
cashapp: require('./cashapp')(ctx),
|
|
76
86
|
codepen: require('./codepen')(ctx),
|
|
77
87
|
cults3d: require('./cults3d')(ctx),
|
|
78
88
|
cursor: require('./cursor')(ctx),
|
|
89
|
+
deezer: require('./deezer')(ctx),
|
|
79
90
|
deviantart: require('./deviantart')(ctx),
|
|
80
91
|
discord: require('./discord')(ctx),
|
|
81
92
|
dockerhub: require('./dockerhub')(ctx),
|
|
93
|
+
douban: require('./douban')(ctx),
|
|
82
94
|
dribbble: require('./dribbble')(ctx),
|
|
83
95
|
duckduckgo: require('./duckduckgo')(ctx),
|
|
84
96
|
facebook: require('./facebook')(ctx),
|
|
85
97
|
flickr: require('./flickr')(ctx),
|
|
98
|
+
gitee: require('./gitee')(ctx),
|
|
86
99
|
github: require('./github')(ctx),
|
|
87
100
|
gitlab: require('./gitlab')(ctx),
|
|
88
101
|
google: require('./google')(ctx),
|
|
@@ -91,11 +104,13 @@ module.exports = ctx => {
|
|
|
91
104
|
huggingface: require('./huggingface')(ctx),
|
|
92
105
|
hevy: require('./hevy')(ctx),
|
|
93
106
|
instagram: require('./instagram')(ctx),
|
|
107
|
+
juejin: require('./juejin')(ctx),
|
|
94
108
|
'ko-fi': require('./ko-fi')(ctx),
|
|
95
109
|
linkedin: require('./linkedin')(ctx),
|
|
96
110
|
mastodon: require('./mastodon')(ctx),
|
|
97
111
|
medium: require('./medium')(ctx),
|
|
98
112
|
microlink: require('./microlink')(ctx),
|
|
113
|
+
'netease-music': require('./netease-music')(ctx),
|
|
99
114
|
npm: require('./npm')(ctx),
|
|
100
115
|
onlyfans: require('./onlyfans')(ctx),
|
|
101
116
|
openstreetmap: require('./openstreetmap')(ctx),
|
|
@@ -106,6 +121,7 @@ module.exports = ctx => {
|
|
|
106
121
|
primal: require('./primal')(ctx),
|
|
107
122
|
producthunt: require('./producthunt')(ctx),
|
|
108
123
|
psnprofiles: require('./psnprofiles')(ctx),
|
|
124
|
+
qq: require('./qq')(ctx),
|
|
109
125
|
raycast: require('./raycast')(ctx),
|
|
110
126
|
reddit: require('./reddit')(ctx),
|
|
111
127
|
revolut: require('./revolut')(ctx),
|
|
@@ -120,16 +136,19 @@ module.exports = ctx => {
|
|
|
120
136
|
thingiverse: require('./thingiverse')(ctx),
|
|
121
137
|
threads: require('./threads')(ctx),
|
|
122
138
|
tidal: require('./tidal')(ctx),
|
|
139
|
+
tieba: require('./tieba')(ctx),
|
|
123
140
|
tiktok: require('./tiktok')(ctx),
|
|
124
141
|
tumblr: require('./tumblr')(ctx),
|
|
125
142
|
twitch: require('./twitch')(ctx),
|
|
126
143
|
venmo: require('./venmo')(ctx),
|
|
127
144
|
vimeo: require('./vimeo')(ctx),
|
|
145
|
+
weibo: require('./weibo')(ctx),
|
|
128
146
|
whatsapp: require('./whatsapp')(ctx),
|
|
129
147
|
wise: require('./wise')(ctx),
|
|
130
148
|
x: require('./x')(ctx),
|
|
131
149
|
xboxgamertag: require('./xboxgamertag')(ctx),
|
|
132
|
-
youtube: require('./youtube')(ctx)
|
|
150
|
+
youtube: require('./youtube')(ctx),
|
|
151
|
+
zhihu: require('./zhihu')(ctx)
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
return { providers, providersBy }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://api.juejin.cn/user_api/v1/user/get'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}?user_id=${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
// Unknown ids still respond `err_no: 0`, but with an empty profile.
|
|
8
|
+
const getAvatar = body => {
|
|
9
|
+
if (body?.err_no !== 0) return
|
|
10
|
+
return body.data?.avatar_large || undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = ({ got }) =>
|
|
14
|
+
async function juejin (input) {
|
|
15
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
16
|
+
responseType: 'json',
|
|
17
|
+
throwHttpErrors: false
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (statusCode >= 400) return
|
|
21
|
+
|
|
22
|
+
return getAvatar(body)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
26
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://music.163.com/api/v1/user/detail'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}/${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
// The API returns avatar URLs over plain HTTP; the CDN supports HTTPS.
|
|
8
|
+
const getAvatar = body => {
|
|
9
|
+
const avatarUrl = body?.profile?.avatarUrl
|
|
10
|
+
return avatarUrl ? avatarUrl.replace(/^http:/, 'https:') : undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = ({ got }) =>
|
|
14
|
+
async function neteaseMusic (input) {
|
|
15
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
16
|
+
responseType: 'json',
|
|
17
|
+
throwHttpErrors: false
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (statusCode >= 400) return
|
|
21
|
+
|
|
22
|
+
return getAvatar(body)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
26
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// qlogo.cn serves a default placeholder for unknown numbers (gravatar-like),
|
|
4
|
+
// so the URL always resolves and misses can't be detected upfront.
|
|
5
|
+
const getAvatarUrl = input =>
|
|
6
|
+
`https://q1.qlogo.cn/g?b=qq&nk=${encodeURIComponent(input)}&s=640`
|
|
7
|
+
|
|
8
|
+
module.exports = () =>
|
|
9
|
+
function qq (input) {
|
|
10
|
+
return getAvatarUrl(input)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const PORTRAIT_URL = 'https://himg.bdimg.com/sys/portrait/item'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input =>
|
|
6
|
+
`https://tieba.baidu.com/i/sys/user_json?un=${encodeURIComponent(
|
|
7
|
+
input
|
|
8
|
+
)}&ie=utf-8`
|
|
9
|
+
|
|
10
|
+
const getAvatar = body => {
|
|
11
|
+
const portrait = body?.creator?.portrait
|
|
12
|
+
return portrait ? `${PORTRAIT_URL}/${portrait}` : undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = ({ got }) =>
|
|
16
|
+
async function tieba (input) {
|
|
17
|
+
// Unknown users respond with an empty body, which is not valid JSON.
|
|
18
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
19
|
+
responseType: 'text',
|
|
20
|
+
throwHttpErrors: false
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
if (statusCode >= 400 || !body) return
|
|
24
|
+
|
|
25
|
+
let json
|
|
26
|
+
try {
|
|
27
|
+
json = JSON.parse(body)
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return getAvatar(json)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
36
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const getAvatarUrl = input =>
|
|
4
|
+
/^\d+$/.test(input)
|
|
5
|
+
? `https://m.weibo.cn/u/${input}`
|
|
6
|
+
: `https://m.weibo.cn/n/${encodeURIComponent(input)}`
|
|
7
|
+
|
|
8
|
+
// The profile avatar is served as a signed, expiring crop
|
|
9
|
+
// (`/crop.0.0.W.H.180/file?ssig=…`); rewriting it to `/large/file` yields a
|
|
10
|
+
// stable, unsigned, full-resolution URL. Default avatars live under
|
|
11
|
+
// `/default/`, which is treated as a miss.
|
|
12
|
+
const getAvatar = ({ $, NOT_FOUND }) => {
|
|
13
|
+
const src = $('.m-avatar-box img').attr('src')
|
|
14
|
+
if (!src) return
|
|
15
|
+
|
|
16
|
+
const url = new URL(src, 'https://m.weibo.cn')
|
|
17
|
+
if (url.pathname.includes('/default/')) return NOT_FOUND
|
|
18
|
+
|
|
19
|
+
const filename = url.pathname.slice(url.pathname.lastIndexOf('/'))
|
|
20
|
+
return `${url.origin}/large${filename}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The page sits behind Weibo's visitor gate (a JS cookie-setting redirect)
|
|
24
|
+
// and the avatar is injected after hydration, so it must be prerendered.
|
|
25
|
+
module.exports = ({ createHtmlProvider, NOT_FOUND }) =>
|
|
26
|
+
createHtmlProvider({
|
|
27
|
+
name: 'weibo',
|
|
28
|
+
url: getAvatarUrl,
|
|
29
|
+
getter: $ => getAvatar({ $, NOT_FOUND }),
|
|
30
|
+
htmlOpts: () => ({
|
|
31
|
+
prerender: true
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
36
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://www.zhihu.com/api/v4/members'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input =>
|
|
6
|
+
`${API_URL}/${encodeURIComponent(input)}?include=avatar_url`
|
|
7
|
+
|
|
8
|
+
// Misses (and bot challenges) come back as an `error` payload rather than
|
|
9
|
+
// a plain 404, and default avatars are flagged via `use_default_avatar`.
|
|
10
|
+
const getAvatar = body => {
|
|
11
|
+
if (!body || body.error || body.use_default_avatar) return
|
|
12
|
+
return body.avatar_url || undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = ({ got }) =>
|
|
16
|
+
async function zhihu (input) {
|
|
17
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
18
|
+
responseType: 'json',
|
|
19
|
+
throwHttpErrors: false
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
if (statusCode >= 400) return
|
|
23
|
+
|
|
24
|
+
return getAvatar(body)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
28
|
+
module.exports.getAvatar = getAvatar
|