Skip to content

Commit

Permalink
feat: add youtube support
Browse files Browse the repository at this point in the history
Closes #13 

Based on #21

Changes compared with the original PR:

- Used `got` instead of `request`.
- Resolve YouTube usernames, no need to be prefixed with `/user`.


In addition, I setup `domain: false` it's oriented for getting a generic url domain avatar and in this case you are using it for getting specific channel avatar based on url.

I feel I can refactor `domain` into a more generic field called `url` for adding this behavior.
  • Loading branch information
Kikobeats authored Nov 7, 2018
1 parent be8f964 commit 3706ce5
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 9 deletions.
5 changes: 4 additions & 1 deletion now.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"name": "unavatar",
"public": true,
"alias": "unavatar.now.sh"
"alias": "unavatar.now.sh",
"env": {
"YOUTUBE_API_KEY": "@youtube_api_key"
}
}
21 changes: 15 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@
"name": "Kiko Beats",
"url": "https://kikobeats.com"
},
"contributors": [
{
"name": "Travis CI",
"email": "travis@travis-ci.org"
},
{
"name": "Angel M De Miguel",
"email": "angel@bitnami.com"
}
],
"repository": {
"type": "git",
"url": "git+https://github.com/Kikobeats/unavatar.git"
Expand All @@ -18,10 +28,12 @@
},
"keywords": [],
"dependencies": {
"aigle": "~1.12.0-alpha.6",
"aigle": "~1.12.0-alpha.10",
"beauty-error": "~1.0.1",
"cheerio": "~1.0.0-rc.2",
"compression": "~1.7.2",
"cors": "~2.8.4",
"debug": "~4.1.0",
"express": "~4.16.3",
"got": "~9.3.0",
"helmet": "~3.14.0",
Expand All @@ -33,6 +45,7 @@
"lodash": "~4.17.10",
"memoize-one": "~4.0.2",
"morgan": "~1.9.0",
"p-any": "~1.1.0",
"p-timeout": "~2.0.1",
"url-regex": "~4.1.1"
},
Expand Down Expand Up @@ -119,9 +132,5 @@
"scripts": {
"prechangelog": "git-authors-cli"
}
},
"contributors": [
"Travis CI <travis@travis-ci.org>",
"Angel M De Miguel <angel@bitnami.com>"
]
}
}
4 changes: 3 additions & 1 deletion src/avatar-url.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict'

const isAbsoluteUrl = require('is-absolute-url')
const beautyError = require('beauty-error')
const debug = require('debug')('unavatar')
const memoizeOne = require('memoize-one')
const isUrlHttp = require('is-url-http')
const { get, isNil } = require('lodash')
Expand Down Expand Up @@ -62,7 +64,7 @@ module.exports = (fn = getAvatarUrl) => async (req, res) => {
try {
url = await pTimeout(fn(username, fallbackUrl), avatarTimeout)
} catch (err) {
console.log('err', err)
debug(beautyError(err))
url = fallbackUrl
}

Expand Down
3 changes: 2 additions & 1 deletion src/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ module.exports = {
cacheTTL: process.env.CACHE_TTL || TWENTY_FOUR_HOURS,
logLevel: process.env.LOGLEVEL || isProduction ? 'combined' : 'dev',
avatarSize: process.env.AVATAR_SIZE || 400,
avatarTimeout: process.env.AVATAR_TIMEOUT || 3000
avatarTimeout: process.env.AVATAR_TIMEOUT || 3000,
youtubeApiKey: process.env.YOUTUBE_API_KEY
}
1 change: 1 addition & 0 deletions src/providers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const providers = {
clearbit: require('./clearbit'),
github: require('./github'),
facebook: require('./facebook'),
youtube: require('./youtube'),
// gravatar returns a default avatar, so use it as fallback
gravatar: require('./gravatar')
}
Expand Down
50 changes: 50 additions & 0 deletions src/providers/youtube.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict'

const { isNil, chain, get } = require('lodash')
const pAny = require('p-any')
const url = require('url')
const got = require('got')

const { youtubeApiKey } = require('../constant')
const { URLSearchParams } = url

const getUrl = async (username, { slugProp }) => {
const parts = url.parse(username).pathname.split('/')
const slug = chain(parts)
.filter(p => p !== '')
.last()
.value()

const query = new URLSearchParams([
['part', 'id,snippet'],
[slugProp, slug],
['key', youtubeApiKey]
]).toString()

const { body } = await got(
'https://content.googleapis.com/youtube/v3/channels',
{
json: true,
query
}
)

const avatarUrl = get(body, 'items[0].snippet.thumbnails.medium.url')
if (isNil(avatarUrl)) {
throw new Error(`YouTube avatar not detected for '${slugProp}'`)
}
return avatarUrl
}

module.exports = async username => {
return pAny([
getUrl(username, { slugProp: 'forUsername' }),
getUrl(username, { slugProp: 'id' })
])
}

module.exports.supported = {
email: false,
username: true,
domain: false
}
3 changes: 3 additions & 0 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ <h3>Instagram</h3>
<h3>Twitter</h3>
<p><code>&lt;img src="https://unavatar.now.sh/twitter/:username" /&gt;</code></p>
<p>i.e <a target="_blank" href="https://unavatar.now.sh/twitter/kikobeats">https://unavatar.now.sh/twitter/kikobeats</a></p>
<h3>YouTube</h3>
<p><code>&lt;img src="https://unavatar.now.sh/youtube/:username" /&gt;</code></p>
<p>i.e <a target="_blank" href="https://unavatar.now.sh/youtube/caseyneistat">https://unavatar.now.sh/youtube/caseyneistat</a></p>
<h3>Other domain</h3>
<p><code>&lt;img src="https://unavatar.now.sh/domain/:domain" /&gt;</code></p>
<p>i.e <a target="_blank" href="https://unavatar.now.sh/domain/reddit.com">https://unavatar.now.sh/domain/reddit.com</a></p>
Expand Down

0 comments on commit 3706ce5

Please sign in to comment.