├── .gitignore
├── .travis.yml
├── README.md
├── appveyor.yml
├── header.html
├── index.js
├── logo.svg
├── netlify.toml
├── package.json
└── test
├── cli-test.js
└── mocha.opts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | yarn.lock
4 | web.gif
5 | website/
6 | .nyc_output/
7 | coverage/
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 |
5 | language: node_js
6 |
7 | node_js:
8 | - 7
9 | - 8
10 | - 9
11 | - 10
12 |
13 | after_script:
14 | - sleep 10
15 | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_NODE_VERSION" == "8" ]]; then cat ./coverage/lcov.info | npx coveralls; fi
16 |
17 | matrix:
18 | exclude:
19 | - os: osx
20 | node_js: 7
21 | - os: osx
22 | node_js: 9
23 |
24 | jobs:
25 | include:
26 | - stage: npm release
27 | node_js: "8"
28 | script: echo "Deploying to npm ..." && npm version
29 | deploy:
30 | provider: npm
31 | email: $NPM_EMAIL
32 | api_key: $NPM_TOKEN
33 | skip_cleanup: true
34 | on:
35 | branch: master
36 | tags: true
37 | repo: anishkny/webgif
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | ## *Easily generate animated GIFs from websites*
4 |
5 | [](https://travis-ci.org/anishkny/webgif)
6 | [](https://ci.appveyor.com/project/anishkny/webgif/branch/master)
7 | [](https://greenkeeper.io/)
8 | [](https://coveralls.io/github/anishkny/webgif)
9 | 
10 | [](https://www.npmjs.com/package/webgif)
11 |
12 | * **Easy** 👉 *Just point it to a URL and get an animated GIF!*
13 | * **Cross-platform** 👉 *Works on Windows, Mac, Linux, without Docker!*
14 | * **Headless** 👉 *Uses [GoogleChrome/puppeteer](https://github.com/GoogleChrome/puppeteer)*
15 | * **Inspired** 👉 *By [asciicast2gif](https://github.com/asciinema/asciicast2gif) and wanting to make it easier to use*
16 |
17 | ### Installation
18 | ```bash
19 | yarn global add webgif || npm i -g webgif
20 | ```
21 |
22 | ### Usage
23 |
24 | To navigate to `https://giphy.com/search/lol` and make an animated GIF of duration `10` seconds, execute:
25 |
26 | ```bash
27 | webgif -u https://giphy.com/search/lol -d 10
28 |
29 | Navigating to URL: https://giphy.com/search/lol
30 | Taking screenshots: .............
31 | Encoding GIF: /home/user/web.gif
32 | ```
33 |
34 | ### Options
35 |
36 | ```bash
37 | webgif -u URL -d DURATION [-o OUTFILE]
38 |
39 | Options:
40 | --url, -u URL to generate GIF from
41 | [default: "https://giphy.com/search/lol"]
42 | --duration, -d GIF duration in seconds [default: 10]
43 | --output, -o Output file name
44 | [default: "web.gif"]
45 | -h, --help Show help [boolean]
46 | -V, --version Show version number [boolean]
47 | ```
48 |
49 | ### Sample GIF
50 |
51 | 
52 |
53 | ### How it works
54 |
55 | 1. Use [Puppeteer](https://github.com/GoogleChrome/puppeteer) to launch a headless Chrome window
56 | 1. Use `setInterval` to take screenshots and save them to disk
57 | 1. Navigate to target URL and wait for specified duration
58 | 1. Use [gifencoder](https://github.com/eugeneware/gifencoder) and [png-file-stream](https://github.com/eugeneware/png-file-stream) to create animated GIF out of saved screenshots
59 |
60 | See code: [`index.js`](https://github.com/anishkny/webgif/blob/master/index.js)
61 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # Test against the latest version of this Node.js version
2 | environment:
3 | matrix:
4 | - nodejs_version: "8"
5 | - nodejs_version: "10"
6 |
7 | # Install scripts. (runs after repo cloning)
8 | install:
9 | # Get the latest stable version of Node.js or io.js
10 | - ps: Install-Product node $env:nodejs_version
11 | # install modules
12 | - npm install
13 |
14 | # Post-install test scripts.
15 | test_script:
16 | # Output useful info for debugging.
17 | - node --version
18 | - npm --version
19 | # run tests
20 | - npm test
21 |
22 | # Don't actually build.
23 | build: off
24 |
--------------------------------------------------------------------------------
/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | webgif - Easily generate animated GIFs from websites
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const GIFEncoder = require('gifencoder');
5 | const path = require('path');
6 | const pngFileStream = require('png-file-stream');
7 | const puppeteer = require('puppeteer');
8 | const tempdir = require('tempdir');
9 |
10 | const argv = require('yargs')
11 | .alias('url', 'u').default('url', 'https://giphy.com/search/lol')
12 | .describe('url', 'URL to generate GIF from')
13 | .alias('duration', 'd').default('duration', 10)
14 | .describe('duration', 'GIF duration in seconds')
15 | .alias('output', 'o').default('output', `${process.cwd()}${require('path').sep}web.gif`)
16 | .describe('output', 'Output file name')
17 | .alias('h', 'help')
18 | .alias('V', 'version')
19 | .usage('webgif -u URL -d DURATION [-o OUTFILE]')
20 | .version()
21 | .argv;
22 |
23 | (async () => {
24 | const browser = await puppeteer.launch({
25 | ignoreHTTPSErrors: true,
26 | args: ['--allow-running-insecure-content', '--disable-setuid-sandbox', '--no-sandbox', ],
27 | });
28 | const page = await browser.newPage();
29 | const workdir = await tempdir();
30 |
31 | page.setViewport({
32 | width: 1024,
33 | height: 768,
34 | });
35 |
36 | console.log(`Navigating to URL: ${argv.url}`);
37 | await page.goto(argv.url);
38 |
39 | process.stdout.write('Taking screenshots: .');
40 | const screenshotPromises = [];
41 | for (let i = 1; i <= argv.duration; ++i) {
42 | filename = `${workdir}/T${new Date().getTime()}.png`;
43 | process.stdout.write('.');
44 | screenshotPromises.push(page.screenshot({ path: filename, }));
45 | await delay(1000);
46 | }
47 |
48 | await delay(1000);
49 | await Promise.all(screenshotPromises);
50 | console.log(`\nEncoding GIF: ${argv.output}`);
51 | const encoder = new GIFEncoder(1024, 768);
52 | await pngFileStream(`${workdir}/T*png`)
53 | .pipe(encoder.createWriteStream({ repeat: 0, delay: 200, quality: 20 }))
54 | .pipe(fs.createWriteStream(`${argv.output}`));
55 | await page.close();
56 | await browser.close();
57 |
58 | })();
59 |
60 | /* istanbul ignore next */
61 | process.on('unhandledRejection', function(reason, p) {
62 | throw new Error(reason);
63 | });
64 |
65 | function delay(ms) {
66 | return new Promise((resolve) => setTimeout(resolve, ms));
67 | }
68 |
--------------------------------------------------------------------------------
/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [Settings]
2 | ID = "webgif"
3 |
4 | [build]
5 | publish = "website/"
6 | command = "npm run website"
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webgif",
3 | "version": "1.1.0",
4 | "description": "Easily generate animated GIFs from websites",
5 | "main": "index.js",
6 | "bin": {
7 | "webgif": "./index.js"
8 | },
9 | "scripts": {
10 | "test": "nyc -r text -r html -r lcov mocha",
11 | "website": "mkdir -p website; (cat header.html; showdown makehtml -m -i README.md) > website/index.html"
12 | },
13 | "engines": {
14 | "node": ">=7.6.0"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/anishkny/webgif.git"
19 | },
20 | "keywords": [
21 | "gif",
22 | "puppeteer",
23 | "web"
24 | ],
25 | "author": "Anish Karandikar ",
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/anishkny/webgif/issues"
29 | },
30 | "homepage": "https://github.com/anishkny/webgif#readme",
31 | "dependencies": {
32 | "gifencoder": "^1.1.0",
33 | "png-file-stream": "^1.0.0",
34 | "puppeteer": "^1.2.0",
35 | "tempdir": "^2.0.0",
36 | "yargs": "^12.0.1"
37 | },
38 | "devDependencies": {
39 | "mocha": "^5.2.0",
40 | "nyc": "^12.0.1",
41 | "shelljs": "^0.8.1",
42 | "showdown": "^1.8.6"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/cli-test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const shell = require('shelljs');
3 | const tempdir = require('tempdir');
4 |
5 | shell.config.verbose = true;
6 |
7 | describe('CLI tests', async () => {
8 |
9 | it('default options', async () => {
10 | assertShellOutput('node index.js');
11 | });
12 |
13 | it('help, version', async () => {
14 | assertShellOutput('node index.js -h');
15 | assertShellOutput('node index.js -V');
16 | });
17 |
18 | it('other options', async () => {
19 | assertShellOutput('node index.js -u http://google.com');
20 | assertShellOutput('node index.js -d 5');
21 | });
22 |
23 | });
24 |
25 | function assertShellOutput(cmd) {
26 | const runoutput = shell.exec(cmd);
27 | assert(runoutput.code === 0, runoutput);
28 | }
29 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --timeout 120000
2 |
--------------------------------------------------------------------------------