Repository: wmhilton/download-with-webtorrent-button
Branch: master
Commit: e1c9b0a3e4d5
Files: 13
Total size: 21.8 KB
Directory structure:
gitextract_nyt5xsd5/
├── .gitignore
├── .travis.yml
├── LICENSE.md
├── README.md
├── dist/
│ ├── index.css
│ └── index.js
├── generate_gif.js
├── index.css
├── index.html
├── index.js
├── package.json
├── postcss.config.js
└── webpack.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
screenshots
================================================
FILE: .travis.yml
================================================
language: node_js
node_js: 6
deploy:
provider: npm
email: wmhilton@gmail.com
on:
skip_cleanup: true
tags: true
branch: master
repo: wmhilton/download-with-webtorrent-button
api_key:
secure: hlb8qQQlurY9WGoyU7le3HSrxwZil6GJp2N8ZPlrJQS1fawbg5uwLxI2JkdydCEAIe3/SVD2JEKQTa2toq20z/w2cWYEGectKJxMUGzM4VOK9UgSsBM0CKTa7N3nXoZbxZRjOjDcdHtPV6q5HBy3VKM3RIcJsOst8DbQHFI9cfhrOLBPm+Y9Ke+dE7mij+P1ys+tnLRLzEBEC9Ea7fn85/+4IAmhU7tZp/lyu1doPlFSNa7bkscB8DCB82DWkY9Vw4fRJYpHPC2861RsYlXoVdcUhv6EdCCKFtQIJQsxoToCOVtCV5tHRRlMK3rmv9r1I/cPjYVkdcfRmgO+P/3IlT+5GDiYGaBvq4Og3vdyMcPhf7poQhIJDysCTLo+BpODP3zUeCprE7dqMrkTHrGlL46gSGBmRYIJkhxtC0oMLug5QEwep510jHSdidutQVoX5w5dGdy70jKyNBYJrTOOuaQ52QNWPBGy1LniEBGD50Dv/Qk71lhElhMqDpsj15JPIlwy4hE0aLQfrNE1bp3+YjspUkbSARcwBuyoFr90+lMZKDJJtYH9Kms8TQmcbycmPkwacAJj6e8/3rD5njgVwDfm/8c5fGy+qilz5tYGIrDJwaURE1MRRAP+NZc6srWglOFsGns05r09F0eSNJ19qVxY+s7LRA+7TYTeltiWpss=
================================================
FILE: LICENSE.md
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
================================================
FILE: README.md
================================================
# download-with-webtorrent-button
Transform ordinary download links into super-powered WebTorrent ones!
## Example
Check out the demo page: https://wmhilton.github.io/download-with-webtorrent-button
[](https://wmhilton.github.io/download-with-webtorrent-button)
<a href="https://app.codesponsor.io/link/tZYS6JjRUMRBU2NL7eN4t5Gg/wmhilton/download-with-webtorrent-button" rel="nofollow"><img src="https://app.codesponsor.io/embed/tZYS6JjRUMRBU2NL7eN4t5Gg/wmhilton/download-with-webtorrent-button.svg" style="width: 888px; height: 68px;" alt="Sponsor" /></a>
## Rationale
Do you have a big, popular file that lots of people download and the Internet
would grind to a halt if your website went down?
It is EASY to spread the burden of hosting a file among all the people who are
downloading it. Bittorrent (despite its association with piracy) is a fabulous
network protocol that does exactly that. It use to be that you needed to install
separate software to use Bittorrent, but with the advent of
[WebTorrent](https://webtorrent.io) it is now possible to use the Bittorrent
protocol seamlessly in the browser without visitors ever having to leave your site!
Despite how FREAKING AWESOME WebTorrent is, not enough sites are taking advantage
of it. To make taking advantage of WebTorrent as easy and
accessible as possible, I decided to make a "Download with WebTorrent" button
that turns your ordinary download link into a super-powered WebTorrent download
link! All you have to do is paste a small code snippet in your HTML.
## Installation using a CDN
Add the following stylesheet to `<head>`:
```html
<link rel="stylesheet" href="https://unpkg.com/download-with-webtorrent-button/dist/index.css">
```
And the following scripts to the bottom of `<body>`:
```html
<script src="https://unpkg.com/webtorrent/webtorrent.min.js"></script>
<script src="https://unpkg.com/download-with-webtorrent-button/dist/index.js"></script>
```
This adds a single function `registerWebtorrentLinks()` to the global scope.
It automatically initializes `a` tags. If you add additional `a` tags after the
initial page load (such as in the case of single page apps) you can rerun registerWebtorrentLinks().
If you want to override the CSS styles, take a look at `index.css`.
## Installation using a module bundler?
Somebody should fork this and make it a React component. Pull requests welcome!
## Usage
### The easiest way
To add a **Download with WebTorrent** button to your page, use a regular `<a>` link.
The link's `href` attribute will be provided as a fallback on browsers that can't run WebTorrent,
or if an error occurs. Then add a `data-webtorrent` attribute.
You can use `data-webtorrent="auto"` and these
[fabulous](https://github.com/wmhilton/webtorrentify-link)
[free](https://github.com/wmhilton/webtorrentify-server)
[services](https://github.com/wmhilton/cors-buster)
will auto-generate a WebTorrent-compatible .torrent file for your link.
```html
<a href="file.mp4" data-webtorrent="auto">Link Text</a>
```
### Bring your own torrent
If you already have a magnet URI, you can use that,
```html
<a href="file.mp4" data-webtorrent="magnet:?xt=urn:btih:...">Link Text</a>
```
or the location of a .torrent file,
```html
<a href="file.mp4" data-webtorrent="https://example.com/path/to/file.torrent">Link Text</a>
```
but know that WebTorrent is not yet compatible with the DHT and requires `ws` or `http` trackers. If your .torrent only includes `udp` trackers or is tracker-less and relies on the DHT, you are better off using `data-webtorrent="auto"`.
If your torrent is a folder torrent rather than a single file, add a `data-file` attribute with the name of the individual file you intend the link for.
```html
<a href="https://webtorrent.io/torrents/Sintel/Sintel.mp4" data-webtorrent="magnet:?xt=urn:btih:08ada5a7a6183aae1e09d831df6748d566095a10&dn=Sintel&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel.torrent" data-file="Sintel.mp4">Sintel</a>
```
## License
Copyright 2017 William Hilton.
Licensed under [The Unlicense](http://unlicense.org/).
================================================
FILE: dist/index.css
================================================
a[data-webtorrent]{display:inline-block;color:#000;text-decoration:none;font:11pt sans-serif;font-weight:700;text-align:center;padding:10px;border-radius:10px;box-shadow:2px 2px 8px rgba(0,0,0,.2);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 1334 1534' stroke-linejoin='round'%3E%3Cstyle%3E.a%7Bfill:%23ef334c;%7D%3C/style%3E%3Cpath d='M15 374l637-368c8-5 19-5 28 0l637 368c8 5 14 14 14 24l0 736c0 10-5 19-14 24l-637 368c-8 5-19 5-28 0l-637-368c-8-5-14-14-14-24l0-736c0-10 5-19 14-24Z' class='a'/%3E%3Cpath d='M98 424l556-321c7-4 17-4 24 0l556 321c7 4 12 12 12 21l0 642c0 9-5 17-12 21l-556 321c-7 4-17 4-24 0l-556-321c-7-4-12-12-12-21l0-642c0-9 4-16 12-21Z' fill='%23343b45'/%3E%3Cpath d='M666 1078l242 219c0 0 269-155 327-189 7-4 11-11 11-19 0-54 0-261 0-261l-321-298 -259 548Z' fill='%23262b33'/%3E%3Cpath d='M980 636c-8-83-78-147-163-147 -68 0-126 41-151 100 -25-59-83-100-151-100 -85 0-155 64-163 147 -1 6-1 11-1 17 0 199 244 293 315 426 71-133 315-227 315-426 0-6 0-11-1-17Z' class='a'/%3E%3C/svg%3E"),-webkit-linear-gradient(right,#4caf50,#009e9e),-webkit-linear-gradient(bottom right,#00b7b7 0,#009e9e);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 1334 1534' stroke-linejoin='round'%3E%3Cstyle%3E.a%7Bfill:%23ef334c;%7D%3C/style%3E%3Cpath d='M15 374l637-368c8-5 19-5 28 0l637 368c8 5 14 14 14 24l0 736c0 10-5 19-14 24l-637 368c-8 5-19 5-28 0l-637-368c-8-5-14-14-14-24l0-736c0-10 5-19 14-24Z' class='a'/%3E%3Cpath d='M98 424l556-321c7-4 17-4 24 0l556 321c7 4 12 12 12 21l0 642c0 9-5 17-12 21l-556 321c-7 4-17 4-24 0l-556-321c-7-4-12-12-12-21l0-642c0-9 4-16 12-21Z' fill='%23343b45'/%3E%3Cpath d='M666 1078l242 219c0 0 269-155 327-189 7-4 11-11 11-19 0-54 0-261 0-261l-321-298 -259 548Z' fill='%23262b33'/%3E%3Cpath d='M980 636c-8-83-78-147-163-147 -68 0-126 41-151 100 -25-59-83-100-151-100 -85 0-155 64-163 147 -1 6-1 11-1 17 0 199 244 293 315 426 71-133 315-227 315-426 0-6 0-11-1-17Z' class='a'/%3E%3C/svg%3E"),linear-gradient(270deg,#4caf50 0,#009e9e),linear-gradient(to top left,#00b7b7 0,#009e9e);background-repeat:no-repeat,no-repeat,no-repeat;background-position:5px 50%,left 100%,100%;background-size:28px 28px,0 100%,100%;padding-left:3em}a[data-webtorrent]:after{content:attr(title)}a[data-webtorrent].no-webrtc a,a[data-webtorrent]:after{display:block;clear:left;font-weight:400;text-align:center;font:8pt sans-serif}a[data-webtorrent].no-webrtc a{color:#000;text-decoration:none;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}a[data-webtorrent].no-webrtc a:hover{text-decoration:underline}
================================================
FILE: dist/index.js
================================================
!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=2)}([function(e,t){function n(){"use strict";function e(e){var o=e.currentTarget;return o.classList.contains("init")?t(e):o.classList.contains("downloading")?n(e):o.classList.contains("ready")?r(e):!o.classList.contains("seeding")||r(e)}function t(e){var t=e.currentTarget;t.classList.remove("init"),t.classList.add("downloading");try{var n=t.innerText,r=new WebTorrent;return r.on("error",function(e){console.error("ERROR: "+e.message)}),"auto"===t.dataset.webtorrent&&(t.dataset.webtorrent="https://webtorrentify.now.sh/?href="+t.href,t.title="Generating .torrent file..."),r.add(t.dataset.webtorrent,function(e){function r(){var r=e.numPeers;r+=1===r?" peer":" peers";var i=Math.floor(100*e.progress)+"%";t.style="background-size: 28px 28px, "+i+" 100%, 100%;",e.done?e.done&&t.classList.contains("seeding")&&(t.innerText=o.name+" - Ready",t.title="Seeding ("+r+")"):t.innerText.endsWith(" - Ready")||(t.innerText=n+" - "+i,t.title="Downloading ("+r+")")}console.log(e);var o;o=1===e.files.length||void 0===t.dataset.file?e.files[0]:e.files.find(function(e){return e.name===t.dataset.file}),r(),setInterval(r,500),o.getBlobURL(function(e,n){if(e)return void window.alert("WebTorrent error: source getBlobURL");t.classList.remove("downloading"),t.classList.add("ready"),t.innerText=o.name+" - Ready",t.title="Click to save file",t.download=o.name,t.href=n})}),e.preventDefault(),!1}catch(e){return console.log(e),!0}}function n(e){return e.preventDefault(),!1}function r(e){var t=e.currentTarget;return t.classList.remove("ready"),t.classList.add("seeding"),!0}for(var o=document.querySelectorAll("a[data-webtorrent]"),i=0;i<o.length;i++){var a=o[i];if(WebTorrent.WEBRTC_SUPPORT)a.title="Download with WebTorrent",a.addEventListener("click",e);else if(a.classList.add("no-webrtc"),"auto"!==a.dataset.webtorrent){a.title="";var s=document.createElement("a");s.href=a.dataset.webtorrent,s.innerText="alternate Bittorrent link",a.appendChild(s)}else a.title="Download";a.classList.add("init")}}window&&(window.registerWebtorrentLinks=n,n())},function(e,t){},function(e,t,n){n(0),e.exports=n(1)}]);
================================================
FILE: generate_gif.js
================================================
var Nightmare = require('nightmare')
function waitPercent (n) {
return document.querySelector('a span').innerText.endsWith(n + '%')
}
new Nightmare({waitTimeout: 300000, show: true, backgroundColor: '#FFF', transparent: false})
.viewport(300, 200)
.goto('https://wmhilton.com/download-with-webtorrent-button/')
.screenshot('screenshots/00.png', {x: 0, y: 0, width: 210, height: 64})
.click('a')
.wait('a.downloading')
.screenshot('screenshots/01.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 10)
.screenshot('screenshots/02.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 20)
.screenshot('screenshots/03.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 30)
.screenshot('screenshots/04.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 40)
.screenshot('screenshots/05.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 50)
.screenshot('screenshots/06.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 60)
.screenshot('screenshots/07.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 70)
.screenshot('screenshots/08.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 80)
.screenshot('screenshots/09.png', {x: 0, y: 0, width: 210, height: 64})
.wait(waitPercent, 90)
.screenshot('screenshots/10.png', {x: 0, y: 0, width: 210, height: 64})
.wait('a.ready')
.screenshot('screenshots/11.png', {x: 0, y: 0, width: 210, height: 64})
.click('a')
.wait('a.seeding')
.wait(1000)
.screenshot('screenshots/12.png', {x: 0, y: 0, width: 210, height: 64})
.end()
.then(() => {
console.log('Generating animated gif')
var fs = require('fs')
var GIFEncoder = require('gifencoder')
var encoder = new GIFEncoder(210, 64)
var pngFileStream = require('png-file-stream')
pngFileStream('screenshots/*.png')
.pipe(encoder.createWriteStream({ repeat: -1, delay: 1000, quality: 10 }))
.pipe(fs.createWriteStream('dist/animated.gif'))
})
.catch(err => console.log)
================================================
FILE: index.css
================================================
a[data-webtorrent] {
display: inline-block;
color: #000;
text-decoration: none;
font: 11pt sans-serif;
font-weight: bold;
text-align: center;
padding: 10px;
border-radius: 10px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.2);
background-image: url(./assets/webtorrent-v3.1.svg),
linear-gradient(to left, #4CAF50 0%, #009e9e 100%),
linear-gradient(to top left, #00b7b7 0%, #009e9e 100%);
background-repeat: no-repeat, no-repeat, no-repeat;
background-position: 5px 50%, left 100%, 100%;
background-size: 28px 28px, 0px 100%, 100%;
padding-left: 3em;
}
/* Used for displaying status messages */
a[data-webtorrent]::after {
display: block;
clear: left;
content: attr(title);
font-weight: normal;
text-align: center;
font: 8pt sans-serif;
}
/* Used for fallback Bittorrent link */
a[data-webtorrent].no-webrtc a {
display: block;
clear: left;
font-weight: normal;
text-align: center;
font: 8pt sans-serif;
color: black;
text-decoration: none;
width: fit-content;
}
a[data-webtorrent].no-webrtc a:hover {
text-decoration: underline;
}
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Button Demo - Download with WebTorrent Button</title>
<link rel="stylesheet" href="dist/index.css">
</head>
<body>
<a title="Download with WebTorrent" href="https://nodejs.org/dist/v6.10.2/node-v6.10.2-linux-x64.tar.xz" data-webtorrent="auto">node-v6.10.2-linux-x64.tar.xz</a>
<script src="https://unpkg.com/webtorrent/webtorrent.min.js"></script>
<script src="dist/index.js"></script>
</body>
</html>
================================================
FILE: index.js
================================================
/* global WebTorrent */
function registerWebtorrentLinks () {
'use strict'
// Add onclick handlers to all <a data-webtorrent="..."> elements
var links = document.querySelectorAll('a[data-webtorrent]')
for (var i = 0; i < links.length; i++) {
// Wrap the link innerHTML so its easier to update
var a = links[i]
if (WebTorrent.WEBRTC_SUPPORT) {
a.title = 'Download with WebTorrent'
a.addEventListener('click', onButtonClick)
} else {
a.classList.add('no-webrtc')
if (a.dataset.webtorrent !== 'auto') {
a.title = ''
var sublink = document.createElement('a')
sublink.href = a.dataset.webtorrent
sublink.innerText = 'alternate Bittorrent link'
a.appendChild(sublink)
} else {
a.title = 'Download'
}
}
a.classList.add('init')
}
function onButtonClick (e) {
// The button has a simple state machine that progresses from
// 'init' to 'downloading' to 'ready' to 'seeding' and clicks
// are handled differently in each case.
var a = e.currentTarget
if (a.classList.contains('init')) return downloadWithWebTorrent(e)
if (a.classList.contains('downloading')) return ignoreClicks(e)
if (a.classList.contains('ready')) return saveFile(e)
if (a.classList.contains('seeding')) return saveFile(e)
return true
}
// This is what runs when user clicks the link
function downloadWithWebTorrent (e) {
var a = e.currentTarget
a.classList.remove('init')
a.classList.add('downloading')
try {
var title = a.innerText
// Initialize WebTorrent
var client = new WebTorrent()
client.on('error', function (err) {
console.error('ERROR: ' + err.message)
})
// The 'auto' option will dynamically generate a .torrent file
// using a free service I built
if (a.dataset.webtorrent === 'auto') {
a.dataset.webtorrent = 'https://webtorrentify.now.sh/?href=' + a.href
a.title = 'Generating .torrent file...'
}
// This starts downloading the torrent
client.add(a.dataset.webtorrent, function (torrent) {
console.log(torrent)
// Torrents can contain multiple files, so we have to deal with that.
var file
if (torrent.files.length === 1 || a.dataset.file === undefined) {
file = torrent.files[0]
} else {
file = torrent.files.find(function (file) { return file.name === a.dataset.file })
}
// Show progress bar
function progress () {
var numPeers = torrent.numPeers
numPeers += (numPeers === 1 ? ' peer' : ' peers')
var percent = Math.floor(torrent.progress * 100) + '%'
// Nifty progress bar using CSS gradient backgrounds
a.style = 'background-size: 28px 28px, ' + percent + ' 100%, 100%;'
if (!torrent.done) {
// Update download percentage
if (!a.innerText.endsWith(' - Ready')) {
a.innerText = title + ' - ' + percent
a.title = 'Downloading (' + numPeers + ')'
}
} else if (torrent.done && a.classList.contains('seeding')) {
a.innerText = file.name + ' - Ready'
a.title = 'Seeding (' + numPeers + ')'
}
}
progress()
setInterval(progress, 500)
// When the file is ready, change the button text to reflect that
file.getBlobURL(function (err, url) {
if (err) {
window.alert('WebTorrent error: source getBlobURL')
return
}
a.classList.remove('downloading')
a.classList.add('ready')
a.innerText = file.name + ' - Ready'
a.title = 'Click to save file'
a.download = file.name
a.href = url
})
})
// Prevent default link behavior and don't follow it.
e.preventDefault()
return false
} catch (err) {
console.log(err)
// If something went wrong, bail and use the default link behavior
// to download the link without WebTorrent if possible.
return true
}
}
// If we're already downloading don't start another download
function ignoreClicks (e) {
e.preventDefault()
return false
}
// Once the file is downloaded, we change the href to point to a blob.
// Thus we just let the link do its default behavior.
function saveFile (e) {
var a = e.currentTarget
a.classList.remove('ready')
a.classList.add('seeding')
return true
}
}
if (window) {
window.registerWebtorrentLinks = registerWebtorrentLinks
registerWebtorrentLinks()
}
================================================
FILE: package.json
================================================
{
"name": "download-with-webtorrent-button",
"version": "1.0.5",
"description": "Transform ordinary download links into super-powered WebTorrent ones!",
"main": "./index.js",
"style": "./index.css",
"devDependencies": {
"css-loader": "^0.28.0",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.11.1",
"gifencoder": "^1.0.6",
"http-server": "^0.9.0",
"husky": "^0.13.3",
"nightmare": "^2.10.0",
"png-file-stream": "^1.0.0",
"postcss-loader": "^1.3.3",
"standard": "^10.0.1",
"svg-url-loader": "^2.0.2",
"webpack": "^2.3.3",
"webtorrent": "^0.98.15"
},
"browserlist": [
"last 2 versions"
],
"scripts": {
"start": "http-server --cors -o",
"precommit": "npm test && npm run build",
"build": "webpack -p",
"gif": "node generate_gif.js",
"test": "standard index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wmhilton/download-with-webtorrent-button.git"
},
"keywords": [],
"author": "William Hilton <wmhilton@gmail.com>",
"license": "Unlicense",
"bugs": {
"url": "https://github.com/wmhilton/download-with-webtorrent-button/issues"
},
"homepage": "https://github.com/wmhilton/download-with-webtorrent-button#readme"
}
================================================
FILE: postcss.config.js
================================================
module.exports = {
plugins: [
require('autoprefixer')
]
}
================================================
FILE: webpack.config.js
================================================
const path = require('path')
const pkg = require('./package.json')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
entry: [pkg.main, pkg.style],
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{ test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {importLoaders: 1}
},
'postcss-loader'
]
})
},
{ test: /\.svg/,
use: 'svg-url-loader'
},
],
},
plugins: [
new ExtractTextPlugin('index.css'),
]
}
gitextract_nyt5xsd5/ ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── dist/ │ ├── index.css │ └── index.js ├── generate_gif.js ├── index.css ├── index.html ├── index.js ├── package.json ├── postcss.config.js └── webpack.config.js
SYMBOL INDEX (4 symbols across 3 files)
FILE: dist/index.js
function t (line 1) | function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{...
function n (line 1) | function n(){"use strict";function e(e){var o=e.currentTarget;return o.c...
FILE: generate_gif.js
function waitPercent (line 3) | function waitPercent (n) {
FILE: index.js
function registerWebtorrentLinks (line 2) | function registerWebtorrentLinks () {
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (24K chars).
[
{
"path": ".gitignore",
"chars": 25,
"preview": "node_modules\nscreenshots\n"
},
{
"path": ".travis.yml",
"chars": 903,
"preview": "language: node_js\nnode_js: 6\ndeploy:\n provider: npm\n email: wmhilton@gmail.com\n on:\n skip_cleanup: true\n tags: "
},
{
"path": "LICENSE.md",
"chars": 1211,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "README.md",
"chars": 4593,
"preview": "# download-with-webtorrent-button\nTransform ordinary download links into super-powered WebTorrent ones!\n\n## Example\n\nChe"
},
{
"path": "dist/index.css",
"chars": 2680,
"preview": "a[data-webtorrent]{display:inline-block;color:#000;text-decoration:none;font:11pt sans-serif;font-weight:700;text-align:"
},
{
"path": "dist/index.js",
"chars": 2578,
"preview": "!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.e"
},
{
"path": "generate_gif.js",
"chars": 2053,
"preview": "var Nightmare = require('nightmare')\n\nfunction waitPercent (n) {\n return document.querySelector('a span').innerText.end"
},
{
"path": "index.css",
"chars": 1121,
"preview": "a[data-webtorrent] {\n display: inline-block;\n color: #000;\n text-decoration: none;\n font: 11pt sans-serif;\n font-we"
},
{
"path": "index.html",
"chars": 474,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Button Demo - Download with WebTorrent Button</title>\n "
},
{
"path": "index.js",
"chars": 4658,
"preview": "/* global WebTorrent */\nfunction registerWebtorrentLinks () {\n 'use strict'\n // Add onclick handlers to all <a data-we"
},
{
"path": "package.json",
"chars": 1272,
"preview": "{\n \"name\": \"download-with-webtorrent-button\",\n \"version\": \"1.0.5\",\n \"description\": \"Transform ordinary download links"
},
{
"path": "postcss.config.js",
"chars": 65,
"preview": "module.exports = {\n plugins: [\n require('autoprefixer')\n ]\n}"
},
{
"path": "webpack.config.js",
"chars": 676,
"preview": "const path = require('path')\nconst pkg = require('./package.json')\nconst ExtractTextPlugin = require('extract-text-webpa"
}
]
About this extraction
This page contains the full source code of the wmhilton/download-with-webtorrent-button GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (21.8 KB), approximately 7.3k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.