@unavatar/core 3.23.0 → 3.24.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/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.23.0",
5
+ "version": "3.24.0",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { $jsonld } = require('@metascraper/helpers')
4
- const memoize = require('@keyvhq/memoize')
4
+ const { createGetSearchResults } = require('../util/itunes-search')
5
5
 
6
6
  const APPLE_MUSIC_ID_REGEX = /^\d+$/
7
7
  const APPLE_MUSIC_STOREFRONT = 'us'
@@ -36,26 +36,19 @@ const parseInput = input => {
36
36
  }
37
37
 
38
38
  module.exports = ({ createHtmlProvider, itunesSearchCache, got }) => {
39
- const searchEntityId = memoize(
40
- async ({ query, type }) => {
41
- const entityConfig = APPLE_MUSIC_ENTITY_TYPES[type]
42
- if (!entityConfig) return
43
- const { entity, idKey } = entityConfig
44
-
45
- const url = `https://itunes.apple.com/search?term=${encodeURIComponent(
46
- query
47
- )}&entity=${entity}&limit=1`
48
- const body = await got(url, {
49
- responseType: 'json',
50
- resolveBodyOnly: true
51
- })
52
-
53
- const entityId = body?.results?.[0]?.[idKey]
54
- return isNumericId(entityId) ? String(entityId) : null
55
- },
56
- itunesSearchCache,
57
- { key: ({ query, type }) => `${type}:${query.trim().toLowerCase()}` }
58
- )
39
+ const getSearchResults = createGetSearchResults({ got, itunesSearchCache })
40
+
41
+ const searchEntityId = async ({ query, type }) => {
42
+ const entityConfig = APPLE_MUSIC_ENTITY_TYPES[type]
43
+ if (!entityConfig) return
44
+ const { entity, idKey } = entityConfig
45
+
46
+ const [result] = await getSearchResults(
47
+ `term=${encodeURIComponent(query)}&entity=${entity}&limit=1`
48
+ )
49
+ const entityId = result?.[idKey]
50
+ return isNumericId(entityId) ? String(entityId) : null
51
+ }
59
52
 
60
53
  const appleMusicURI = async input => {
61
54
  const { hasExplicitType, type, id } = parseInput(input)
@@ -0,0 +1,126 @@
1
+ 'use strict'
2
+
3
+ const { createGetSearchResults } = require('../util/itunes-search')
4
+
5
+ const ITUNES_LOOKUP_URL = 'https://itunes.apple.com/lookup'
6
+ const COUNTRY_CODE_REGEX = /^[a-z]{2}$/i
7
+
8
+ const getArtworkUrl = result =>
9
+ result?.artworkUrl512 || result?.artworkUrl100 || result?.artworkUrl60
10
+
11
+ const withCountry = ({ query, country }) =>
12
+ country ? `${query}&country=${encodeURIComponent(country)}` : query
13
+
14
+ const parseInput = input => {
15
+ const separatorIndex = input.indexOf(':')
16
+ if (separatorIndex === -1) return { type: 'app', value: input }
17
+
18
+ return {
19
+ type: input.slice(0, separatorIndex),
20
+ value: input.slice(separatorIndex + 1)
21
+ }
22
+ }
23
+
24
+ const parseCountry = value => {
25
+ const separatorIndex = value.lastIndexOf('@')
26
+ if (separatorIndex === -1) return { value, country: undefined }
27
+
28
+ const country = value.slice(separatorIndex + 1)
29
+ if (!COUNTRY_CODE_REGEX.test(country)) return { value, country: undefined }
30
+
31
+ const nextValue = value.slice(0, separatorIndex)
32
+ if (!nextValue) return { value, country: undefined }
33
+
34
+ return {
35
+ value: nextValue,
36
+ country: country.toLowerCase()
37
+ }
38
+ }
39
+
40
+ const getLookupResults = async ({ got, query }) => {
41
+ const body = await got(`${ITUNES_LOOKUP_URL}?${query}`, {
42
+ responseType: 'json',
43
+ resolveBodyOnly: true
44
+ })
45
+ return body?.results ?? []
46
+ }
47
+
48
+ const getAppAvatar = async ({ got, id, country }) => {
49
+ const query = withCountry({
50
+ query: `id=${encodeURIComponent(id)}&entity=software&limit=1`,
51
+ country
52
+ })
53
+ const [result] = await getLookupResults({ got, query })
54
+ return getArtworkUrl(result)
55
+ }
56
+
57
+ const getDeveloperAvatar = async ({ got, id, country }) => {
58
+ const query = withCountry({
59
+ query: `id=${encodeURIComponent(id)}&entity=software&limit=200`,
60
+ country
61
+ })
62
+ const results = await getLookupResults({ got, query })
63
+ const softwareResult = results.find(result => result?.kind === 'software')
64
+ return getArtworkUrl(softwareResult)
65
+ }
66
+
67
+ const getBundleAvatar = async ({ got, bundleId, country }) => {
68
+ const query = withCountry({
69
+ query: `bundleId=${encodeURIComponent(bundleId)}&entity=software&limit=1`,
70
+ country
71
+ })
72
+ const [result] = await getLookupResults({ got, query })
73
+ return getArtworkUrl(result)
74
+ }
75
+
76
+ const getAppNameAvatar = async ({ got, name, country, searchResults }) => {
77
+ const getSearchResults = searchResults || createGetSearchResults({ got })
78
+ const query = withCountry({
79
+ query: `term=${encodeURIComponent(name)}&entity=software&limit=1`,
80
+ country
81
+ })
82
+ const [result] = await getSearchResults(query)
83
+ return getArtworkUrl(result)
84
+ }
85
+
86
+ const getDeveloperNameAvatar = async ({ got, name, country, searchResults }) => {
87
+ const getSearchResults = searchResults || createGetSearchResults({ got })
88
+ const query = withCountry({
89
+ query: `term=${encodeURIComponent(
90
+ name
91
+ )}&entity=software&attribute=softwareDeveloper&limit=1`,
92
+ country
93
+ })
94
+ const [result] = await getSearchResults(query)
95
+ return getArtworkUrl(result)
96
+ }
97
+
98
+ module.exports = ({ got, itunesSearchCache }) => {
99
+ const searchResults = createGetSearchResults({ got, itunesSearchCache })
100
+
101
+ return async function appleStore (input) {
102
+ const { type, value: rawValue } = parseInput(input)
103
+ const { value, country } = parseCountry(rawValue)
104
+
105
+ switch (type) {
106
+ case 'app':
107
+ return getAppAvatar({ got, id: value, country })
108
+ case 'dev':
109
+ return getDeveloperAvatar({ got, id: value, country })
110
+ case 'bundle':
111
+ return getBundleAvatar({ got, bundleId: value, country })
112
+ case 'app-name':
113
+ return getAppNameAvatar({ got, name: value, country, searchResults })
114
+ case 'dev-name':
115
+ return getDeveloperNameAvatar({ got, name: value, country, searchResults })
116
+ default:
117
+ throw new Error(`Unsupported Apple Store type: ${type}`)
118
+ }
119
+ }
120
+ }
121
+
122
+ module.exports.getAppAvatar = getAppAvatar
123
+ module.exports.getDeveloperAvatar = getDeveloperAvatar
124
+ module.exports.getBundleAvatar = getBundleAvatar
125
+ module.exports.getAppNameAvatar = getAppNameAvatar
126
+ module.exports.getDeveloperNameAvatar = getDeveloperNameAvatar
@@ -4,24 +4,33 @@ const BUDDY_ICON_URL_PATTERN = /(?:https?:)?\/\/[^"'()\s]*\/buddyicons\/[^"'()\s
4
4
  const CSS_URL_PATTERN = /url\((['"]?)([^)'"]+)\1\)/i
5
5
  const unescapePathSlashes = value => value?.replace(/\\\//g, '/')
6
6
 
7
+ const parseInput = input => {
8
+ const [first, second] = input.split(':')
9
+ return {
10
+ type: second ? first : 'user',
11
+ id: second ?? first
12
+ }
13
+ }
14
+
7
15
  const getBuddyIconFromText = value => {
8
16
  const normalizedValue = unescapePathSlashes(value)
9
17
  if (typeof normalizedValue !== 'string') return
10
18
  return normalizedValue.match(BUDDY_ICON_URL_PATTERN)?.[0]
11
19
  }
12
20
 
21
+ const getUserProfileUrl = userId => `https://www.flickr.com/photos/${userId}/`
22
+ const getGroupProfileUrl = groupId => `https://www.flickr.com/groups/${groupId}/`
23
+
13
24
  const getAvatarUrl = input => {
14
- const [first, second] = input.split(':')
15
- const type = second ? first : 'user'
16
- const id = second ?? first
25
+ const { type, id } = parseInput(input)
17
26
 
18
27
  switch (type) {
19
28
  case 'user':
20
29
  case 'photos':
21
- return `https://www.flickr.com/photos/${id}/`
30
+ return getUserProfileUrl(id)
22
31
  case 'group':
23
32
  case 'groups':
24
- return `https://www.flickr.com/groups/${id}/`
33
+ return getGroupProfileUrl(id)
25
34
  default:
26
35
  throw new Error(`Unsupported Flickr type: ${type}`)
27
36
  }
@@ -4,6 +4,7 @@ const providersBy = {
4
4
  email: ['gravatar', 'github'],
5
5
  username: [
6
6
  'apple-music',
7
+ 'apple-store',
7
8
  'behance',
8
9
  'bluesky',
9
10
  'buymeacoffee',
@@ -49,6 +50,7 @@ const providersBy = {
49
50
  module.exports = ctx => {
50
51
  const providers = {
51
52
  'apple-music': require('./apple-music')(ctx),
53
+ 'apple-store': require('./apple-store')(ctx),
52
54
  behance: require('./behance')(ctx),
53
55
  bluesky: require('./bluesky')(ctx),
54
56
  buymeacoffee: require('./buymeacoffee')(ctx),
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+
3
+ const memoize = require('@keyvhq/memoize')
4
+
5
+ const ITUNES_SEARCH_URL = 'https://itunes.apple.com/search'
6
+
7
+ const getSearchResults = async ({ got, query }) => {
8
+ const body = await got(`${ITUNES_SEARCH_URL}?${query}`, {
9
+ responseType: 'json',
10
+ resolveBodyOnly: true
11
+ })
12
+ return body?.results ?? []
13
+ }
14
+
15
+ const createGetSearchResults = ({ got, itunesSearchCache }) => {
16
+ const searchResults = query => getSearchResults({ got, query })
17
+ if (!itunesSearchCache) return searchResults
18
+
19
+ return memoize(searchResults, itunesSearchCache, {
20
+ key: query => query.trim().toLowerCase()
21
+ })
22
+ }
23
+
24
+ module.exports = { createGetSearchResults }