@unavatar/core 3.46.17 → 3.48.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 +9 -2
- package/package.json +1 -1
- package/src/providers/bluesky.js +23 -8
- package/src/providers/deezer.js +30 -18
- package/src/providers/gitlab.js +40 -5
- package/src/providers/index.js +2 -0
- package/src/providers/kick.js +25 -0
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
- [Hugging Face](#hugging-face)
|
|
50
50
|
- [Instagram](#instagram)
|
|
51
51
|
- [Juejin](#juejin)
|
|
52
|
+
- [Kick](#kick)
|
|
52
53
|
- [Ko-fi](#ko-fi)
|
|
53
54
|
- [LinkedIn](#linkedin)
|
|
54
55
|
- [Mastodon](#mastodon)
|
|
@@ -106,13 +107,13 @@ Last updated on August 18, 2026
|
|
|
106
107
|
|
|
107
108
|
## Introduction
|
|
108
109
|
|
|
109
|
-
**unavatar.io** retrieves anyone's avatar from a single URL — by username, email, or domain — across
|
|
110
|
+
**unavatar.io** retrieves anyone's avatar from a single URL — by username, email, or domain — across 73 platforms including [Instagram](https://unavatar.io/docs#instagram), [GitHub](https://unavatar.io/docs#github), [YouTube](https://unavatar.io/docs#youtube), [X/Twitter](https://unavatar.io/docs#xtwitter), and [Gravatar](https://unavatar.io/docs#gravatar). No API keys, no SDKs, free to start.
|
|
110
111
|
|
|
111
112
|
Try it: [unavatar.io/github/kikobeats](https://unavatar.io/github/kikobeats) · [unavatar.io/x/josebaseba](https://unavatar.io/x/josebaseba) · [unavatar.io/domain/reddit.com](https://unavatar.io/domain/reddit.com)
|
|
112
113
|
|
|
113
114
|
Everything you need to serve avatars at scale:
|
|
114
115
|
|
|
115
|
-
- **Versatile**: One endpoint rules
|
|
116
|
+
- **Versatile**: One endpoint rules 73 platforms and services — [TikTok](https://unavatar.io/docs#tiktok), [Instagram](https://unavatar.io/docs#instagram), [YouTube](https://unavatar.io/docs#youtube), [X/Twitter](https://unavatar.io/docs#xtwitter), [Gravatar](https://unavatar.io/docs#gravatar), and more — all queried the same way.
|
|
116
117
|
|
|
117
118
|
- **Battle-tested**: Already serving 2.77 TB of avatars across 189.4M requests, with a 70% cache hit rate.
|
|
118
119
|
|
|
@@ -811,6 +812,12 @@ Get any Juejin user's profile picture by their numeric user ID.
|
|
|
811
812
|
|
|
812
813
|
e.g., [unavatar.io/juejin/1556564194374926](https://unavatar.io/juejin/1556564194374926)
|
|
813
814
|
|
|
815
|
+
### Kick
|
|
816
|
+
|
|
817
|
+
Get any Kick streamer's profile picture by their username.
|
|
818
|
+
|
|
819
|
+
e.g., [unavatar.io/kick/xqc](https://unavatar.io/kick/xqc)
|
|
820
|
+
|
|
814
821
|
### Ko-fi
|
|
815
822
|
|
|
816
823
|
Get any Ko-fi page's profile picture by the creator username.
|
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.48.0",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js",
|
package/src/providers/bluesky.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const API_URL = 'https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}?actor=${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
const getAvatar = body => {
|
|
8
|
+
const pic = body?.avatar
|
|
9
|
+
return typeof pic === 'string' && pic ? pic : undefined
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = ({ got }) =>
|
|
13
|
+
async function bluesky (input) {
|
|
14
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
15
|
+
responseType: 'json',
|
|
16
|
+
throwHttpErrors: false
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
if (statusCode >= 400) return
|
|
20
|
+
|
|
21
|
+
return getAvatar(body)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
25
|
+
module.exports.getAvatar = getAvatar
|
package/src/providers/deezer.js
CHANGED
|
@@ -1,32 +1,44 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const API_URL = 'https://api.deezer.com'
|
|
4
|
+
|
|
3
5
|
// Entities without artwork keep an empty image hash
|
|
4
6
|
// (cdn-images.dzcdn.net/images/artist//500x500.jpg), which resolves to a
|
|
5
7
|
// generic placeholder, so any empty hash segment is treated as a miss.
|
|
6
8
|
const isArtwork = url => !/\/images\/[^/]+\/\/\d/.test(url)
|
|
7
9
|
|
|
8
|
-
const
|
|
10
|
+
const parseInput = input => {
|
|
9
11
|
const [first, second] = input.split(':')
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
return {
|
|
13
|
+
type: second ? first : 'artist',
|
|
14
|
+
id: second ?? first
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const image = getOgImage($)
|
|
19
|
-
return image && isArtwork(image) ? image : NOT_FOUND
|
|
18
|
+
const getAvatarUrl = input => {
|
|
19
|
+
const { type, id } = parseInput(input)
|
|
20
|
+
return `${API_URL}/${encodeURIComponent(type)}/${encodeURIComponent(id)}`
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const getAvatar = body => {
|
|
24
|
+
if (body?.error) return
|
|
25
|
+
|
|
26
|
+
const pic = body?.picture_xl || body?.cover_xl || body?.album?.cover_xl
|
|
27
|
+
return typeof pic === 'string' && pic && isArtwork(pic) ? pic : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = ({ got }) =>
|
|
31
|
+
async function deezer (input) {
|
|
32
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
33
|
+
responseType: 'json',
|
|
34
|
+
throwHttpErrors: false
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
if (statusCode >= 400) return
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
return getAvatar(body)
|
|
40
|
+
}
|
|
31
41
|
|
|
32
|
-
module.exports =
|
|
42
|
+
module.exports.parseInput = parseInput
|
|
43
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
44
|
+
module.exports.getAvatar = getAvatar
|
package/src/providers/gitlab.js
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const GITLAB_API_URL = 'https://gitlab.com/api/v4'
|
|
4
|
+
|
|
5
|
+
const getUserAvatarUrl = input =>
|
|
6
|
+
`${GITLAB_API_URL}/users?username=${encodeURIComponent(input)}`
|
|
7
|
+
|
|
8
|
+
const getGroupAvatarUrl = input =>
|
|
9
|
+
`${GITLAB_API_URL}/groups/${encodeURIComponent(input)}`
|
|
10
|
+
|
|
11
|
+
const getAvatar = body => {
|
|
12
|
+
const pic = Array.isArray(body) ? body[0]?.avatar_url : body?.avatar_url
|
|
13
|
+
return typeof pic === 'string' && pic ? pic : undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const fetchAvatarUrl = async ({ got, url }) => {
|
|
17
|
+
const { statusCode, body } = await got(url, {
|
|
18
|
+
responseType: 'json',
|
|
19
|
+
throwHttpErrors: false
|
|
8
20
|
})
|
|
21
|
+
|
|
22
|
+
if (statusCode >= 400) return
|
|
23
|
+
|
|
24
|
+
return getAvatar(body)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = ({ got }) =>
|
|
28
|
+
async function gitlab (input) {
|
|
29
|
+
const userAvatarUrl = await fetchAvatarUrl({
|
|
30
|
+
got,
|
|
31
|
+
url: getUserAvatarUrl(input)
|
|
32
|
+
})
|
|
33
|
+
if (userAvatarUrl) return userAvatarUrl
|
|
34
|
+
|
|
35
|
+
return fetchAvatarUrl({
|
|
36
|
+
got,
|
|
37
|
+
url: getGroupAvatarUrl(input)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports.getUserAvatarUrl = getUserAvatarUrl
|
|
42
|
+
module.exports.getGroupAvatarUrl = getGroupAvatarUrl
|
|
43
|
+
module.exports.getAvatar = getAvatar
|
package/src/providers/index.js
CHANGED
|
@@ -30,6 +30,7 @@ const providerTiers = {
|
|
|
30
30
|
'hevy',
|
|
31
31
|
'instagram',
|
|
32
32
|
'juejin',
|
|
33
|
+
'kick',
|
|
33
34
|
'ko-fi',
|
|
34
35
|
'linkedin',
|
|
35
36
|
'mastodon',
|
|
@@ -118,6 +119,7 @@ module.exports = ctx => {
|
|
|
118
119
|
hevy: require('./hevy')(ctx),
|
|
119
120
|
instagram: require('./instagram')(ctx),
|
|
120
121
|
juejin: require('./juejin')(ctx),
|
|
122
|
+
kick: require('./kick')(ctx),
|
|
121
123
|
'ko-fi': require('./ko-fi')(ctx),
|
|
122
124
|
linkedin: require('./linkedin')(ctx),
|
|
123
125
|
mastodon: require('./mastodon')(ctx),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://kick.com/api/v2/channels'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}/${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
const getAvatar = body => {
|
|
8
|
+
const pic = body?.user?.profile_pic
|
|
9
|
+
return typeof pic === 'string' && pic ? pic : undefined
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = ({ got }) =>
|
|
13
|
+
async function kick (input) {
|
|
14
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
15
|
+
responseType: 'json',
|
|
16
|
+
throwHttpErrors: false
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
if (statusCode >= 400) return
|
|
20
|
+
|
|
21
|
+
return getAvatar(body)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
25
|
+
module.exports.getAvatar = getAvatar
|