├── .gitignore ├── package.json ├── LICENSE ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexo-oembed", 3 | "version": "0.1.8", 4 | "description": "embed oEmbed item on your Hexo article.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:hinastory/hexo-oembed.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/hinastory/hexo-oembed/issues" 15 | }, 16 | "keywords": [ 17 | "hexo", 18 | "blog", 19 | "plugin", 20 | "helper", 21 | "tag", 22 | "oembed", 23 | "youtube", 24 | "slideshare", 25 | "speakerdeck", 26 | "twitter", 27 | "vimeo", 28 | "codepen", 29 | "pixiv", 30 | "instagram", 31 | "flickr", 32 | "gyazo" 33 | ], 34 | "author": "hinastory", 35 | "license": "MIT", 36 | "dependencies": { 37 | "hexo-util": "^0.6.3", 38 | "oembed": "^0.1.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 hinastory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hexo-oembed 2 | 3 | [![NPM](https://nodei.co/npm/hexo-oembed.png)](https://nodei.co/npm/hexo-oembed/) 4 | [![licence](https://img.shields.io/npm/l/hexo-oembed.svg?style=flat)](LICENSE) 5 | [![hexo](https://img.shields.io/badge/Hexo-%3E%3D3.0-blue.svg?style=flat-square)](https://hexo.io) 6 | [![Maintainability](https://api.codeclimate.com/v1/badges/ddfce94fa04983a9c7c7/maintainability)](https://codeclimate.com/github/hinastory/hexo-oembed/maintainability) 7 | 8 | Embed [oEmbed](https://oembed.com/) item on your [Hexo](https://hexo.io/) article. 9 | 10 | Features 11 | -------- 12 | 13 | - Supports oEmbed Discovery 14 | - You can embed an oEmbed Discovery compatible site 15 | - You can check a permalink with [oEmbed Tester](http://oembed.frdnspnzr.de/) 16 | - Vimeo, SlideShare, Speaker Deck, CodePen, TED, pixiv and more! 17 | - Supports oEmded endpoint configuration 18 | - You can embed an oEmbed compatible site(not support oEmbed Discovery) if you configure endpoint settings 19 | - You can find an oEmbed endpoint at [oEmbed site](https://oembed.com/#section7) 20 | - Twitter, Instagram, Gyazo, Flickr and more! 21 | - Automatic [Embed.ly](http://embed.ly/) fallback when an API key is provided 22 | 23 | ## Installation 24 | 25 | `npm install hexo-oembed --save` 26 | 27 | ## Usage 28 | 29 | `{% oembed permlink [maxwidth] [maxheight] %}` 30 | 31 | ## Demo 32 | 33 | See [Demo page](https://hinastory.github.io/cats-cats-cats/hexo-oembed-demo/). 34 | 35 | ## Configuration 36 | 37 | ### className 38 | 39 | You can provide a base CSS class name of this embeded HTML. 40 | (Default: `oembed`) 41 | 42 | ### endpoints 43 | 44 | You can provide endpoints of oEmbed provider. 45 | (Default: None) 46 | 47 | You can get oEmbed provider endpoint from below link. 48 | 49 | https://oembed.com/#section7 50 | 51 | You can define `match` and `url` of a oEmbed endpoint and `url` of that endpoint will be used if a hostname of `permlink` contains the `match` value. 52 | 53 | Fallback by [oEmbed Discovery](https://oembed.com/#section4) if a `permlink` page provide a oEmbed discovery link when a suitable endpoint is not found in the `endpoints`. 54 | 55 | For example youtube is compatible with oEmbed Discovery, so you do not need to define endpoint. 56 | 57 | ### embedlyKey 58 | 59 | Fallback Embed.ly if `embedlyKey` is provided. 60 | 61 | The Embed.ly service can deliver oEmbed information even for resources 62 | that don't provide oEmbed links. Go 63 | [sign up](https://app.embed.ly/pricing/free) with them and configure your API key. 64 | 65 | ### Example 66 | 67 | _config.yml: 68 | 69 | ```yaml 70 | oembed: 71 | className: oembed 72 | embedlyKey: 73 | endpoints: 74 | youtube: 75 | match: youtube 76 | url: https://www.youtube.com/oembed 77 | gyazo: 78 | match: gyazo 79 | url: https://api.gyazo.com/api/oembed/ 80 | flickr: 81 | match: flickr 82 | url: http://www.flickr.com/services/oembed/ 83 | twitter: 84 | match: twitter 85 | url: https://publish.twitter.com/oembed 86 | ``` 87 | 88 | ## Thanks 89 | 90 | This plugin refers to the following three OSS resources: 91 | 92 | - https://github.com/astro/node-oembed 93 | - https://github.com/monsier-oui/hexo-tag-oembed 94 | - https://github.com/minamo173/hexo-tag-link-preview 95 | 96 | Thanks to them. 97 | 98 | ## License 99 | 100 | MIT 101 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * hexo-oembed 3 | * https://github.com/hinastory/hexo-oembed.git 4 | * Copyright (c) 2019, hinastory 5 | * Licensed under the MIT license. 6 | * Syntax: 7 | * {% oembed permlink [maxwidth] [maxheight] %} 8 | **/ 9 | 10 | 'use strict'; 11 | const util = require('hexo-util'); 12 | const oembed = require('oembed'); 13 | const querystring = require('querystring'); 14 | const mainClassName = (hexo.config.oembed && hexo.config.oembed.className) ? hexo.config.oembed.className : "oembed"; 15 | 16 | hexo.extend.tag.register('oembed', function (args) { 17 | return getTag(args[0], args[1], args[2]); 18 | }, { async: true }); 19 | 20 | function getTag(url, maxwidth, maxheight) { 21 | let options = {}; 22 | if (hexo.config.oembed && hexo.config.oembed.embedlyKey) { 23 | oembed.EMBEDLY_KEY = hexo.config.oembed.embedlyKey; 24 | } 25 | 26 | if (maxwidth && maxwidth !== '-') options.maxwidth = maxwidth; 27 | if (maxheight) options.maxheight = maxheight; 28 | 29 | return fetchOembed(url, options) 30 | .then(result => makeEmbedTag(url, result)) 31 | .catch(_ => fallbackOembed(url, options)); 32 | } 33 | 34 | function errorTag(msg, url) { 35 | 36 | const errMsg = util.htmlTag('span', {}, `${msg}(url=${url})`); 37 | return util.htmlTag('div', {class: `${mainClassName}-error`, style: 'color: red;'}, errMsg); 38 | } 39 | 40 | function fallbackOembed(url, options) { 41 | return new Promise(function (resolve) { 42 | oembed.fetch(url, options, (error, result) => { 43 | if (error) { 44 | console.log(url, error); 45 | resolve(errorTag('failed getting oembed item.',url)); 46 | } else { 47 | resolve(makeEmbedTag(url, result)); 48 | } 49 | }); 50 | }); 51 | } 52 | 53 | function fetchOembed(url, options) { 54 | const endpoints = hexo.config.oembed && hexo.config.oembed.endpoints; 55 | 56 | return new Promise(function (resolve, reject) { 57 | if (!endpoints) reject(); 58 | 59 | let name, endpoint; 60 | let hostname = new URL(url).hostname; 61 | for (let [key, value] of Object.entries(endpoints)) { 62 | if (hostname.match(value.match)) { 63 | name = key; 64 | endpoint = new URL(value.url); 65 | } 66 | } 67 | 68 | if (name) { 69 | options.url = url; 70 | options.format = 'json'; 71 | endpoint.search = '?' + querystring.stringify(options); 72 | 73 | oembed.fetchJSON(endpoint.toString(), (error, result) => { 74 | error ? reject() : resolve(result); 75 | }); 76 | } else { 77 | reject(); 78 | } 79 | }); 80 | } 81 | 82 | function makeEmbedTag(url, result) { 83 | const subClassName = result.provider_name ? result.provider_name.toLowerCase().replace(/[ .]/g, '-') : "default" 84 | 85 | switch (result.type) { 86 | case 'photo': 87 | const imgOpt = { src: result.url, alt: result.title ? result.title : null }; 88 | const img = util.htmlTag('img', imgOpt, null); 89 | const link = util.htmlTag('a', { href: url, class: `${mainClassName}-inner` }, img); 90 | return util.htmlTag('div', { class: `${mainClassName}-outer ${mainClassName}-${subClassName}` }, link); 91 | case 'video': 92 | case 'rich': 93 | const inner = util.htmlTag('div', { class: `${mainClassName}-inner` }, result.html); 94 | return util.htmlTag('div', { class: `${mainClassName}-outer ${mainClassName}-${subClassName}` }, inner); 95 | default: 96 | const errMsg = 'unsupported oembed type.'; 97 | console.log(url, errMsg); 98 | return errorTag(errMsg, url); 99 | } 100 | } --------------------------------------------------------------------------------