diff --git a/README.md b/README.md index ccd5dc3d5f..b0a1b8b052 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,9 @@ RSSHub 是一个轻量、易于扩展的 RSS 生成器,可以给任何奇奇 - 用户 - Instagram - 用户 +- Youtube + - 用户 + - 频道 ## 参与我们 diff --git a/config.js b/config.js index 2bf16e0105..472a546c1d 100644 --- a/config.js +++ b/config.js @@ -17,5 +17,8 @@ module.exports = { consumer_secret: process.env.TWITTER_CONSUMER_SECRET, access_token: process.env.TWITTER_ACCESS_TOKEN, access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET, + }, + youtube: { + key: process.env.YOUTUBE_KEY } }; \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 61cbd3bc1a..cf8aa50669 100644 --- a/docs/README.md +++ b/docs/README.md @@ -569,4 +569,22 @@ key: 产品密钥 路由: `/instagram/user/:id` -参数: id,用户 id \ No newline at end of file +参数: id,用户 id + +## Youtube + +### 用户 + +举例: [https://rss.now.sh/youtube/user/JFlaMusic](https://rss.now.sh/youtube/user/JFlaMusic) + +路由: `/youtube/user/:username` + +参数: username,用户名 + +### 频道 + +举例: [https://rss.now.sh/youtube/channel/UCDwDMPOZfxVV0x_dz0eQ8KQ](https://rss.now.sh/youtube/channel/UCDwDMPOZfxVV0x_dz0eQ8KQ) + +路由: `/youtube/channel/:id` + +参数: id,频道 id \ No newline at end of file diff --git a/package.json b/package.json index 0da3a3ef43..ce58716384 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "crypto": "1.0.1", "eslint": "4.19.1", "form-data": "^2.3.2", + "googleapis": "30.0.0", "json-bigint": "0.2.3", "koa": "2.5.0", "koa-redis-cache": "3.0.0", diff --git a/router.js b/router.js index 7c880ac28b..c270de7700 100644 --- a/router.js +++ b/router.js @@ -103,4 +103,8 @@ router.get('/twitter/user/:id', require('./routes/twitter/user')); // Instagram router.get('/instagram/user/:id', require('./routes/instagram/user')); +// Youtube +router.get('/youtube/user/:username', require('./routes/youtube/user')); +router.get('/youtube/channel/:id', require('./routes/youtube/channel')); + module.exports = router; diff --git a/routes/youtube/channel.js b/routes/youtube/channel.js new file mode 100644 index 0000000000..c5e78bd951 --- /dev/null +++ b/routes/youtube/channel.js @@ -0,0 +1,40 @@ +const { google } = require('googleapis'); +const config = require('../../config'); +const template = require('../../utils/template'); + +const youtube = google.youtube({ + version: 'v3', + auth: config.youtube.key +}); + +module.exports = async (ctx) => { + const id = ctx.params.id; + + const playlistIdResponst = await youtube.channels.list({ + part: 'contentDetails', + id: id, + }); + const playlistId = playlistIdResponst.data.items[0].contentDetails.relatedPlaylists.uploads; + + const responst = await youtube.playlistItems.list({ + part: 'snippet,contentDetails,status', + playlistId: playlistId + }); + const data = responst.data.items; + + ctx.body = template({ + title: `${data[0].snippet.channelTitle} 的 Youtube 视频`, + link: `https://www.youtube.com/channel/${id}`, + description: `${data[0].snippet.channelTitle} 的 Youtube 视频`, + item: data.map((item) => { + const snippet = item.snippet; + const img = snippet.thumbnails.maxres || snippet.thumbnails.standard || snippet.thumbnails.high || snippet.thumbnails.medium || snippet.thumbnails.default; + return { + title: snippet.title, + description: `${snippet.description}`, + pubDate: new Date(snippet.publishedAt).toUTCString(), + link: `https://www.youtube.com/watch?v=${snippet.resourceId.videoId}` + }; + }), + }); +}; \ No newline at end of file diff --git a/routes/youtube/user.js b/routes/youtube/user.js new file mode 100644 index 0000000000..eb5eee1f64 --- /dev/null +++ b/routes/youtube/user.js @@ -0,0 +1,40 @@ +const { google } = require('googleapis'); +const config = require('../../config'); +const template = require('../../utils/template'); + +const youtube = google.youtube({ + version: 'v3', + auth: config.youtube.key +}); + +module.exports = async (ctx) => { + const username = ctx.params.username; + + const playlistIdResponst = await youtube.channels.list({ + part: 'contentDetails', + forUsername: username, + }); + const playlistId = playlistIdResponst.data.items[0].contentDetails.relatedPlaylists.uploads; + + const responst = await youtube.playlistItems.list({ + part: 'snippet,contentDetails,status', + playlistId: playlistId + }); + const data = responst.data.items; + + ctx.body = template({ + title: `${username} 的 Youtube 视频`, + link: `https://www.youtube.com/user/${username}`, + description: `${username} 的 Youtube 视频`, + item: data.map((item) => { + const snippet = item.snippet; + const img = snippet.thumbnails.maxres || snippet.thumbnails.standard || snippet.thumbnails.high || snippet.thumbnails.medium || snippet.thumbnails.default; + return { + title: snippet.title, + description: `${snippet.description}`, + pubDate: new Date(snippet.publishedAt).toUTCString(), + link: `https://www.youtube.com/watch?v=${snippet.resourceId.videoId}` + }; + }), + }); +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b0249cdd15..2b69672c72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -338,7 +338,7 @@ aws4@^1.6.0: version "1.7.0" resolved "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" -axios@0.18.0: +axios@0.18.0, axios@^0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" dependencies: @@ -372,6 +372,10 @@ base64-js@^1.0.2: version "1.3.0" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" +base64url@2.0.0, base64url@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb" + base@^0.11.1: version "0.11.2" resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -549,6 +553,10 @@ buble@^0.19.3: os-homedir "^1.0.1" vlq "^1.0.0" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + buffer-from@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" @@ -1503,6 +1511,13 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +ecdsa-sig-formatter@1.0.9: + version "1.0.9" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz#4bc926274ec3b5abb5016e7e1d60921ac262b2a1" + dependencies: + base64url "^2.0.0" + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -1805,7 +1820,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.1: +extend@^3.0.1, extend@~3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -2037,6 +2052,14 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +gcp-metadata@^0.6.2: + version "0.6.3" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz#4550c08859c528b370459bd77a7187ea0bdbc4ab" + dependencies: + axios "^0.18.0" + extend "^3.0.1" + retry-axios "0.3.2" + get-port@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" @@ -2145,6 +2168,35 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +google-auth-library@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.4.0.tgz#27dae3b6aceadcc115a9910b4f1ed27b68b1eb0b" + dependencies: + axios "^0.18.0" + gcp-metadata "^0.6.2" + gtoken "^2.2.0" + jws "^3.1.4" + lodash.isstring "^4.0.1" + lru-cache "^4.1.2" + retry-axios "^0.3.2" + +google-p12-pem@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.2.tgz#c8a3843504012283a0dbffc7430b7c753ecd4b07" + dependencies: + node-forge "^0.7.4" + pify "^3.0.0" + +googleapis@30.0.0: + version "30.0.0" + resolved "https://registry.npmjs.org/googleapis/-/googleapis-30.0.0.tgz#4673ba34878217539ca5aa4216fef4db6c247649" + dependencies: + google-auth-library "^1.4.0" + pify "^3.0.0" + qs "^6.5.1" + url-template "^2.0.8" + uuid "^3.2.1" + got@^6.7.1: version "6.7.1" resolved "https://registry.npmjs.org/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -2174,6 +2226,16 @@ gray-matter@^4.0.1: section-matter "^1.0.0" strip-bom-string "^1.0.0" +gtoken@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-2.3.0.tgz#4e0ffc16432d7041a1b3dbc1d97aac17a5dc964a" + dependencies: + axios "^0.18.0" + google-p12-pem "^1.0.0" + jws "^3.1.4" + mime "^2.2.0" + pify "^3.0.0" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -2835,6 +2897,23 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jwa@^1.1.4: + version "1.1.5" + resolved "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5" + dependencies: + base64url "2.0.0" + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.9" + safe-buffer "^5.0.1" + +jws@^3.1.4: + version "3.1.4" + resolved "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2" + dependencies: + base64url "^2.0.0" + jwa "^1.1.4" + safe-buffer "^5.0.1" + keygrip@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" @@ -3088,6 +3167,10 @@ lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -3331,7 +3414,7 @@ mime@^1.3.4: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" -mime@^2.0.3, mime@^2.1.0: +mime@^2.0.3, mime@^2.1.0, mime@^2.2.0: version "2.3.1" resolved "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" @@ -3532,6 +3615,10 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-forge@^0.7.4: + version "0.7.5" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" + node-libs-browser@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" @@ -4350,7 +4437,7 @@ q@^1.1.2: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" -qs@~6.5.1: +qs@^6.5.1, qs@~6.5.1: version "6.5.2" resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -4661,6 +4748,10 @@ ret@~0.1.10: version "0.1.15" resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" +retry-axios@0.3.2, retry-axios@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz#5757c80f585b4cc4c4986aa2ffd47a60c6d35e13" + rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" @@ -5459,6 +5550,10 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" +url-template@^2.0.8: + version "2.0.8" + resolved "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + url@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -5497,7 +5592,7 @@ utila@~0.4: version "0.4.0" resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" -uuid@^3.1.0: +uuid@^3.1.0, uuid@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"