mirror of
https://github.com/leoherzog/TorrentParts.git
synced 2026-01-24 04:08:04 -08:00
Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a086867ff7 | ||
|
|
74d74f8432 | ||
|
|
570df30bcc | ||
|
|
615fe5092c | ||
|
|
dc93c4722e | ||
|
|
b2496a7719 | ||
|
|
5791f8f749 | ||
|
|
c4af414e16 | ||
|
|
d22fdf219c | ||
|
|
d60f8ffcf1 | ||
|
|
0c5f522459 | ||
|
|
f81243896c | ||
|
|
41f408d828 | ||
|
|
e17b51276b | ||
|
|
c54f496dcc | ||
|
|
2e584fe04f | ||
|
|
307af881ad | ||
|
|
09f885238e | ||
|
|
fdcf397f64 | ||
|
|
6d6f53d288 | ||
|
|
b477e9c8c9 | ||
|
|
1ad87324d2 | ||
|
|
418cb0c6b7 | ||
|
|
2d908ecf02 | ||
|
|
a1296b456d | ||
|
|
ddb4a7da06 | ||
|
|
8d81259d1a | ||
|
|
1440ac69a1 | ||
|
|
0651975b13 | ||
|
|
a94258296c | ||
|
|
66cb2914a1 | ||
|
|
de3c73f3c1 | ||
|
|
b1794f000b | ||
|
|
013318d258 | ||
|
|
93ef3186f2 | ||
|
|
7d2871abcb | ||
|
|
6e748cde77 | ||
|
|
2c728a61c2 | ||
|
|
7a9aa3bb10 | ||
|
|
aac24cfdd3 | ||
|
|
25eb970141 | ||
|
|
5244cd3710 | ||
|
|
28c2d03f55 | ||
|
|
cda94dd48a | ||
|
|
03c6ae0e17 | ||
|
|
006c8db5e6 | ||
|
|
43f63d8f22 | ||
|
|
9f26fb2384 | ||
|
|
92f5abdb96 | ||
|
|
8813e819c4 | ||
|
|
fa23c0ff3f | ||
|
|
a4152d0542 | ||
|
|
855e4140a0 | ||
|
|
c512101c4e | ||
|
|
e754ee965f | ||
|
|
5298bab66c | ||
|
|
acc6c5ba8c | ||
|
|
b150a2770a | ||
|
|
397d398f36 | ||
|
|
3ad1488a21 | ||
|
|
a47bd05d12 | ||
|
|
3daf51acca | ||
|
|
ad09499db9 | ||
|
|
b14ef98a23 | ||
|
|
85ffd7a9a0 | ||
|
|
73e99937b9 | ||
|
|
be414070b2 | ||
|
|
ffb1e250e4 | ||
|
|
6863553b66 | ||
|
|
f79c280de2 | ||
|
|
c654968116 | ||
|
|
a6a60a0722 | ||
|
|
753afdf70f | ||
|
|
6406bae502 | ||
|
|
26bb58ff05 | ||
|
|
233140a64e | ||
|
|
9ea7ac5a38 | ||
|
|
58f559ff78 | ||
|
|
9e53b06dd0 | ||
|
|
1687f59a71 | ||
|
|
69d9f807ed | ||
|
|
3d8efd6245 | ||
|
|
096919ec30 |
65
README.md
65
README.md
@@ -2,19 +2,21 @@
|
|||||||
|
|
||||||
## What is this?
|
## What is this?
|
||||||
|
|
||||||
[BitTorrent](https://bittorrent.com/) is a ubiquitus and powerful way to transfer files peer-to-peer. To specify what file(s) to download with your client, you need to input either a Torrent file or Magnet link. [Torrent Parts](https://torrent.parts/) is a client-side static web app to read and edit the metadata of a Torrent file or Magnet link so you know what you're downloading, before you add it to your Torrent client.
|
[BitTorrent](https://bittorrent.com/) is a ubiquitous and powerful way to transfer files peer-to-peer. To specify what file(s) to download with your client, you need to input either a Torrent file or Magnet URL. [Torrent Parts](https://torrent.parts/) is a client-side static web app to read and edit the metadata of a Torrent file or Magnet URL so you know what you're downloading, before you add it to your Torrent client.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- 📑 Display metadata of a Torrent file, Magnet link, or URL to a Torrent file ([CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) required)
|
- 📑 Display metadata of a Torrent file, Magnet URL, or URL to a Torrent file ([CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) required)
|
||||||
- 📝 Edit title, comment, Tracker URLs, and Webseeds
|
- 📝 Edit title, comment, Tracker URLs, and Webseeds
|
||||||
- ↔️ Save and convert between Torrent file and Magnet link
|
- ↔️ Save and convert between Torrent file and Magnet URL
|
||||||
- 🔗 Generate link directly to [Torrent Parts](https://torrent.parts/) with prefilled info
|
- 🔗 Generate a link directly to [Torrent Parts](https://torrent.parts/) with prefilled info<sup>[1]</sup>
|
||||||
- 🌐 Add currently known working trackers from [newTrackon](https://newtrackon.com/)
|
- 🌐 Add currently known working trackers from [newTrackon](https://newtrackon.com/)
|
||||||
- 👥 Fetch files metadata for a Magnet link via [WebTorrent](https://webtorrent.io/)
|
- 👥 Fetch files metadata for a Magnet URL via [WebTorrent](https://webtorrent.io/)
|
||||||
- ℹ️ Learn the basic parts of Torrent metadata and what they mean
|
- ℹ️ Learn the basic parts of Torrent metadata and what they mean
|
||||||
- 🔒 Fully client-side, no files leave your computer
|
- 🔒 Fully client-side, no files leave your computer
|
||||||
|
|
||||||
|
<sup>1. Just include the Magnet URL after `https://torrent.parts/#`, [like so](https://torrent.parts#magnet:?xt=urn:btih:9fc20b9e98ea98b4a35e6223041a5ef94ea27809&dn=ubuntu-20.04-desktop-amd64.iso&tr=https://torrent.ubuntu.com/announce&tr=https://ipv6.torrent.ubuntu.com/announce).</sup>
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
|
|
||||||
This project wouldn't be possible without the fantastic work of:
|
This project wouldn't be possible without the fantastic work of:
|
||||||
@@ -22,8 +24,7 @@ This project wouldn't be possible without the fantastic work of:
|
|||||||
- [@feross](https://github.com/feross) and contributors, for [`parse-torrent`](https://github.com/webtorrent/parse-torrent) and [`WebTorrent`](https://github.com/webtorrent/webtorrent)
|
- [@feross](https://github.com/feross) and contributors, for [`parse-torrent`](https://github.com/webtorrent/parse-torrent) and [`WebTorrent`](https://github.com/webtorrent/webtorrent)
|
||||||
- [@cvisuri](https://github.com/cvisuri), for design work
|
- [@cvisuri](https://github.com/cvisuri), for design work
|
||||||
- [@CorralPeltzer](https://github.com/CorralPeltzer), for [`newTrackon`](https://github.com/CorralPeltzer/newTrackon)
|
- [@CorralPeltzer](https://github.com/CorralPeltzer), for [`newTrackon`](https://github.com/CorralPeltzer/newTrackon)
|
||||||
- [@substack](https://github.com/substack) and contributors, for [`Browserify`](https://github.com/browserify/browserify)
|
- [Cloudflare Pages](https://pages.cloudflare.com/) hosting
|
||||||
- [Github Pages](https://pages.github.com/) hosting
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -40,27 +41,53 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|||||||
## About Me
|
## About Me
|
||||||
|
|
||||||
<a href="https://herzog.tech/" target="_blank">
|
<a href="https://herzog.tech/" target="_blank">
|
||||||
<img src="https://herzog.tech/signature/link.svg.png" width="32px" />
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/link-light.svg.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/link.svg.png">
|
||||||
|
<img src="https://herzog.tech/signature/link.svg.png" width="32px">
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://twitter.com/xd1936" target="_blank">
|
<a href="https://mastodon.social/@herzog" target="_blank">
|
||||||
<img src="https://herzog.tech/signature/twitter.svg.png" width="32px" />
|
<picture>
|
||||||
</a>
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/mastodon-light.svg.png">
|
||||||
<a href="https://facebook.com/xd1936" target="_blank">
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/mastodon.svg.png">
|
||||||
<img src="https://herzog.tech/signature/facebook.svg.png" width="32px" />
|
<img src="https://herzog.tech/signature/mastodon.svg.png" width="32px">
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/leoherzog" target="_blank">
|
<a href="https://github.com/leoherzog" target="_blank">
|
||||||
<img src="https://herzog.tech/signature/github.svg.png" width="32px" />
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/github-light.svg.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/github.svg.png">
|
||||||
|
<img src="https://herzog.tech/signature/github.svg.png" width="32px">
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://keybase.io/leoherzog" target="_blank">
|
<a href="https://keybase.io/leoherzog" target="_blank">
|
||||||
<img src="https://herzog.tech/signature/keybase.svg.png" width="32px" />
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/keybase-light.svg.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/keybase.svg.png">
|
||||||
|
<img src="https://herzog.tech/signature/keybase.svg.png" width="32px">
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.linkedin.com/in/leoherzog" target="_blank">
|
<a href="https://www.linkedin.com/in/leoherzog" target="_blank">
|
||||||
<img src="https://herzog.tech/signature/linkedin.svg.png" width="32px" />
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/linkedin-light.svg.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/linkedin.svg.png">
|
||||||
|
<img src="https://herzog.tech/signature/linkedin.svg.png" width="32px">
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://hope.edu/directory/people/herzog-leo/" target="_blank">
|
<a href="https://hope.edu/directory/people/herzog-leo/" target="_blank">
|
||||||
<img src="https://herzog.tech/signature/anchor.svg.png" width="32px" />
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/anchor-light.svg.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/anchor.svg.png">
|
||||||
|
<img src="https://herzog.tech/signature/anchor.svg.png" width="32px">
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<br />
|
<br />
|
||||||
<a href="https://www.buymeacoffee.com/leoherzog" target="_blank">
|
<a href="https://herzog.tech/$" target="_blank">
|
||||||
<img src="https://cdn.buymeacoffee.com/buttons/lato-black.png" alt="Buy Me A Coffee" width="217px" />
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://herzog.tech/signature/mug-tea-saucer-solid-light.svg.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://herzog.tech/signature/mug-tea-saucer-solid.svg.png">
|
||||||
|
<img src="https://herzog.tech/signature/mug-tea-saucer-solid.svg.png" alt="Buy Me A Tea" width="32px">
|
||||||
|
</picture>
|
||||||
|
Found this helpful? Buy me a tea!
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
87
bundle.min.js
vendored
87
bundle.min.js
vendored
File diff suppressed because one or more lines are too long
53
ext/TorrentPartsFASubset.yaml
Normal file
53
ext/TorrentPartsFASubset.yaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
projectName: 'C:\Users\Leo\Desktop\TorrentPartsFASubset'
|
||||||
|
version: 6.2.1
|
||||||
|
icons:
|
||||||
|
- magnet:
|
||||||
|
- light
|
||||||
|
- solid
|
||||||
|
- duotone
|
||||||
|
- file-lines:
|
||||||
|
- light
|
||||||
|
- regular
|
||||||
|
- duotone
|
||||||
|
- cloud-arrow-up:
|
||||||
|
- solid
|
||||||
|
- link:
|
||||||
|
- light
|
||||||
|
- solid
|
||||||
|
- duotone
|
||||||
|
- file-arrow-down:
|
||||||
|
- solid
|
||||||
|
- arrow-up-right-from-square:
|
||||||
|
- solid
|
||||||
|
- file:
|
||||||
|
- regular
|
||||||
|
- file-word:
|
||||||
|
- regular
|
||||||
|
- file-powerpoint:
|
||||||
|
- regular
|
||||||
|
- file-excel:
|
||||||
|
- regular
|
||||||
|
- file-zipper:
|
||||||
|
- regular
|
||||||
|
- file-csv:
|
||||||
|
- regular
|
||||||
|
- file-pdf:
|
||||||
|
- regular
|
||||||
|
- file-contract:
|
||||||
|
- regular
|
||||||
|
- file-audio:
|
||||||
|
- regular
|
||||||
|
- file-video:
|
||||||
|
- regular
|
||||||
|
- file-image:
|
||||||
|
- regular
|
||||||
|
- folder-tree:
|
||||||
|
- regular
|
||||||
|
- trash:
|
||||||
|
- regular
|
||||||
|
- xmark:
|
||||||
|
- regular
|
||||||
|
- circle-info:
|
||||||
|
- regular
|
||||||
|
- circle-plus:
|
||||||
|
- regular
|
||||||
BIN
ext/alata-v9-latin-regular.ttf
Normal file
BIN
ext/alata-v9-latin-regular.ttf
Normal file
Binary file not shown.
BIN
ext/alata-v9-latin-regular.woff2
Normal file
BIN
ext/alata-v9-latin-regular.woff2
Normal file
Binary file not shown.
6
ext/fa.min.js
vendored
Normal file
6
ext/fa.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
img/TorrentParts-Icon-3x.png
Normal file
BIN
img/TorrentParts-Icon-3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
158
index.html
158
index.html
@@ -14,8 +14,10 @@
|
|||||||
<link href="/favicon.ico" rel="icon" />
|
<link href="/favicon.ico" rel="icon" />
|
||||||
<link href="/img/TorrentParts-Icon-1x.png" rel="icon" type="image/png" sizes="128x128" />
|
<link href="/img/TorrentParts-Icon-1x.png" rel="icon" type="image/png" sizes="128x128" />
|
||||||
<link href="/img/TorrentParts-Icon-2x.png" rel="icon" type="image/png" sizes="256x256" />
|
<link href="/img/TorrentParts-Icon-2x.png" rel="icon" type="image/png" sizes="256x256" />
|
||||||
|
<link href="/img/TorrentParts-Icon-3x.png" rel="icon" type="image/png" sizes="512x512" />
|
||||||
<link href="/img/TorrentParts-Icon-1x.png" rel="apple-touch-icon" type="image/png" sizes="128x128" />
|
<link href="/img/TorrentParts-Icon-1x.png" rel="apple-touch-icon" type="image/png" sizes="128x128" />
|
||||||
<link href="/img/TorrentParts-Icon-2x.png" rel="apple-touch-icon" type="image/png" sizes="256x256" />
|
<link href="/img/TorrentParts-Icon-2x.png" rel="apple-touch-icon" type="image/png" sizes="256x256" />
|
||||||
|
<link href="/img/TorrentParts-Icon-3x.png" rel="apple-touch-icon" type="image/png" sizes="512x512" />
|
||||||
<meta name="msapplication-TileColor" content="#102030" />
|
<meta name="msapplication-TileColor" content="#102030" />
|
||||||
<meta name="msapplication-TileImage" content="/img/TorrentParts-Icon-2x.png" />
|
<meta name="msapplication-TileImage" content="/img/TorrentParts-Icon-2x.png" />
|
||||||
<meta property="og:image" content="/img/TorrentParts-Social.png" />
|
<meta property="og:image" content="/img/TorrentParts-Social.png" />
|
||||||
@@ -34,20 +36,14 @@
|
|||||||
|
|
||||||
<title>Torrent Parts | Inspect and edit what's in your Torrent file or Magnet link</title>
|
<title>Torrent Parts | Inspect and edit what's in your Torrent file or Magnet link</title>
|
||||||
|
|
||||||
<link href="style.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/tippy.js@6/dist/tippy.css" rel="stylesheet" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Alata&display=swap" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/tippy.js@6/animations/shift-away-subtle.css" rel="stylesheet" />
|
||||||
<link href="notyf.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css" rel="stylesheet" />
|
||||||
<script async src="https://kit.fontawesome.com/9ca49f101f.js"></script>
|
<link href="/src/style.css" rel="stylesheet" />
|
||||||
|
<script async src="/ext/fa.min.js"></script>
|
||||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||||
<script src="notyf.min.js"></script>
|
|
||||||
|
|
||||||
<script async defer src="https://www.googletagmanager.com/gtag/js?id=G-VT4953Z89H"></script>
|
<script async defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "6f97f49b4c384ee197a2f319cebec274"}'></script>
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
gtag('config', 'G-VT4953Z89H');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -55,9 +51,10 @@
|
|||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1 id="logo">Torrent<span id="originalSourceIcon"><span class="fad fa-magnet fa-fw" aria-hidden="true"></span></span>Parts</h1>
|
<h1 id="logo">Torrent<span id="originalSourceIcon"><span class="fad fa-magnet fa-fw" aria-hidden="true"></span></span>Parts</h1>
|
||||||
<a class="github-button" href="https://github.com/leoherzog/TorrentParts" data-icon="octicon-star" data-show-count="true" aria-label="Star leoherzog/TorrentParts on GitHub">Star</a>
|
<a class="github-button" href="https://github.com/leoherzog/TorrentParts" data-icon="octicon-star" data-show-count="true" aria-label="Star TorrentParts on GitHub">Star</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<form id="startForm">
|
||||||
<div id="startButtons">
|
<div id="startButtons">
|
||||||
|
|
||||||
<input id="magnet" type="text" placeholder="Enter URL" aria-label="Enter URL and press enter" />
|
<input id="magnet" type="text" placeholder="Enter URL" aria-label="Enter URL and press enter" />
|
||||||
@@ -65,30 +62,36 @@
|
|||||||
Enter URL and press enter
|
Enter URL and press enter
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input id="torrent" type="file" aria-label="Select Torrent file" />
|
<input id="torrent" type="file" accept=".torrent" aria-label="Select Torrent file" />
|
||||||
<label for="torrent">
|
<label for="torrent">
|
||||||
<span class="fas fa-cloud-upload" aria-hidden="true"></span> Select Torrent File
|
<span class="fas fa-cloud-upload" aria-hidden="true"></span> Select Torrent File
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<div id="examples">
|
<section id="examples">
|
||||||
<div>...or, try some examples!</div>
|
<div>...or, try some examples!</div>
|
||||||
<button id="example1" aria-label="Load Magnet Example"><span class="fal fa-magnet" aria-hidden="true"></span> Magnet URL</button>
|
<button id="example1" aria-label="Load Magnet Example"><span class="fal fa-magnet" aria-hidden="true"></span> Magnet URL</button>
|
||||||
<button id="example2" aria-label="Load URL to Torrent File Example"><span class="fal fa-link" aria-hidden="true"></span> URL to Torrent File</button>
|
<button id="example2" aria-label="Load URL to Torrent File Example"><span class="fal fa-link" aria-hidden="true"></span> URL to Torrent File</button>
|
||||||
<button id="example3" aria-label="Load Torrent File Example"><span class="fal fa-file-alt" aria-hidden="true"></span> Torrent File</button>
|
<button id="example3" aria-label="Load Torrent File Example"><span class="fal fa-file-alt" aria-hidden="true"></span> Torrent File</button>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<div id="properties" style="display:none">
|
<main id="properties" style="display:none">
|
||||||
|
|
||||||
<button id="reset" aria-label="Reset the page">
|
<button id="reset" aria-label="Reset the page">
|
||||||
<span class="far fa-times"></span>
|
<span class="far fa-times"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div id="share">
|
<nav id="share">
|
||||||
|
<a id="openURLWrapper" target="_blank">
|
||||||
|
<button id="openURL" aria-label="Open this Magnet URL in your Torrent client">
|
||||||
|
<span class="fas fa-arrow-up-right-from-square fa-2x"></span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
<div>
|
<div>
|
||||||
<button id="copyURL" aria-label="Copy this torrent.parts link to the clipboard">
|
<button id="copyURL" aria-label="Copy this torrent.parts link to the clipboard">
|
||||||
<span class="fas fa-share-alt fa-2x" data-fa-transform="left-1"></span>
|
<span class="fas fa-link fa-2x"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -101,112 +104,151 @@
|
|||||||
<span class="fas fa-file-download fa-2x"></span>
|
<span class="fas fa-file-download fa-2x"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<span class="info" data-tippy-content="This is the unique identifier that makes Torrents work. All of the files contained in this Torrent are run through an algorithm that generates a unique string, or “hash”."><span class="far fa-info-circle"></span></span>
|
||||||
|
Unique Hash
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div>
|
||||||
<span class="info" data-tippy-content="This is the unique identifier that makes Torrents work. All of the files contained in this Torrent are run through an algorithm that generates a unique string, or “hash”."><span class="far fa-info-circle"></span></span>
|
<label for="hash" class="sr-only">Unique Hash</label>
|
||||||
<label for="hash">Unique Hash</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<input id="hash" type="text" placeholder="" disabled/>
|
<input id="hash" type="text" placeholder="" disabled/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<span class="info" data-tippy-content="An optional title specified by the creator"><span class="far fa-info-circle"></span></span>
|
||||||
|
Torrent Name
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div>
|
||||||
<span class="info" data-tippy-content="An optional title specified by the creator"><span class="far fa-info-circle"></span></span>
|
<label for="name" class="sr-only">Torrent Name</label>
|
||||||
<label for="name">Torrent Name</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<input id="name" type="text" placeholder="Unnamed" />
|
<input id="name" type="text" placeholder="Unnamed" dir="auto" />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<span class="info" data-tippy-content="Data embedded into a Torrent file that says what program created it and when. Not included in Magnet links."><span class="far fa-info-circle"></span></span>
|
||||||
|
Created
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div>
|
||||||
<span class="info" data-tippy-content="Data embedded into a Torrent file that says what program created it and when. Not included in Magnet links."><span class="far fa-info-circle"></span></span>
|
<label for="created" class="sr-only">Created</label>
|
||||||
<label for="created">Created</label>
|
|
||||||
</div>
|
</div>
|
||||||
<label for="createdBy" style="display:none">Created By</label>
|
<label for="createdBy" style="display:none">Created By</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<input id="created" type="text" placeholder="Creation time unspecified" aria-label="Creation time" disabled />
|
<input id="created" type="text" placeholder="Creation time unspecified" aria-label="Creation time" disabled required />
|
||||||
<br />
|
<br />
|
||||||
<input id="createdBy" type="text" placeholder="Creation client unspecified" aria-label="Creation client" disabled />
|
<input id="createdBy" type="text" placeholder="Creation client unspecified" aria-label="Creation client" disabled />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<span class="info" data-tippy-content="An optional description specified by the creator"><span class="far fa-info-circle"></span></span>
|
||||||
|
Comment
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div>
|
||||||
<span class="info" data-tippy-content="An optional description specified by the creator"><span class="far fa-info-circle"></span></span>
|
<label for="comment" class="sr-only">Comment</label>
|
||||||
<label for="comment">Comment</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<input id="comment" type="text" placeholder="Not included in the URL/File provided" />
|
<input id="comment" type="text" placeholder="Not included in the URL/File provided" dir="auto" />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
<div class="labels">
|
<legend>
|
||||||
<div>
|
|
||||||
<span class="info" data-tippy-content="Servers that keep track of other users who are actively downloading this Torrent, called “peers”. Your client will contact these servers first to find out who else is available to download the files from."><span class="far fa-info-circle"></span></span>
|
<span class="info" data-tippy-content="Servers that keep track of other users who are actively downloading this Torrent, called “peers”. Your client will contact these servers first to find out who else is available to download the files from."><span class="far fa-info-circle"></span></span>
|
||||||
<label for="announce">Tracker URLs</label>
|
Tracker URLs
|
||||||
</div>
|
</legend>
|
||||||
|
<div class="labels">
|
||||||
|
<div class="label-actions">
|
||||||
<a id="addTrackers">Add Known Working Trackers</a>
|
<a id="addTrackers">Add Known Working Trackers</a>
|
||||||
<a id="removeTrackers">Remove All</a>
|
<a id="removeTrackers">Remove All</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button id="addTracker" data-type="announce">
|
<button id="addTracker" data-type="announce">
|
||||||
<span class="far fa-plus-circle"></span> Add Tracker
|
<span class="far fa-plus-circle"></span> Add Tracker
|
||||||
</button>
|
</button>
|
||||||
<div id="announce"></div>
|
<div id="announce"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
<div class="labels">
|
<legend>
|
||||||
<div>
|
|
||||||
<span class="info" data-tippy-content="A list of webservers on the internet that also have a copy of the file(s) in this Torrent, to use in case no peers are available"><span class="far fa-info-circle"></span></span>
|
<span class="info" data-tippy-content="A list of webservers on the internet that also have a copy of the file(s) in this Torrent, to use in case no peers are available"><span class="far fa-info-circle"></span></span>
|
||||||
<label for="urlList">Webseed URLs</label>
|
Webseed URLs
|
||||||
</div>
|
</legend>
|
||||||
|
<div class="labels">
|
||||||
|
<div class="label-actions">
|
||||||
<a id="removeWebseeds">Remove All</a>
|
<a id="removeWebseeds">Remove All</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button id="addWebseed" data-type="urlList">
|
<button id="addWebseed" data-type="urlList">
|
||||||
<span class="far fa-plus-circle"></span> Add Webseed
|
<span class="far fa-plus-circle"></span> Add Webseed
|
||||||
</button>
|
</button>
|
||||||
<div id="urlList"></div>
|
<div id="urlList"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<span class="info" data-tippy-content="Torrents take files and split them into equal size pieces to make them easy to share. Piece size is configurable when a Torrent file is made."><span class="far fa-info-circle"></span></span>
|
||||||
|
Pieces
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div>
|
||||||
<span class="info" data-tippy-content="The files listed in this Torrent file. Not included in Magnet links."><span class="far fa-info-circle"></span></span>
|
<label for="pieces" class="sr-only">Pieces</label>
|
||||||
<label for="files">Files</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<input id="pieces" type="text" placeholder="Not included in the URL/File provided" aria-label="Piece size and length" disabled required />
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<span class="info" data-tippy-content="The files listed in this Torrent file. Not included in Magnet links."><span class="far fa-info-circle"></span></span>
|
||||||
|
Files
|
||||||
|
</legend>
|
||||||
|
<div class="labels">
|
||||||
|
<div class="label-actions">
|
||||||
<a id="getFiles">Fetch Files List from WebTorrent</a>
|
<a id="getFiles">Fetch Files List from WebTorrent</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<table id="files">
|
<table id="files">
|
||||||
<tbody id="filesBody"></tbody>
|
<tbody id="filesBody"></tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<a href="https://github.com/leoherzog/TorrentParts/releases" target="_blank">v1.0</a>
|
<a href="https://github.com/leoherzog/TorrentParts/releases" target="_blank" rel="noopener">v2.0.2</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="bundle.min.js"></script>
|
<script type="module" src="/src/parse.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', () => navigator.serviceWorker.register('/src/sw.js'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
{
|
{
|
||||||
"src": "/img/TorrentParts-Icon-2x.png",
|
"src": "/img/TorrentParts-Icon-2x.png",
|
||||||
"sizes": "256x256",
|
"sizes": "256x256",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/img/TorrentParts-Icon-3x.png",
|
||||||
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "any maskable"
|
||||||
}
|
}
|
||||||
|
|||||||
2
notyf.min.css
vendored
2
notyf.min.css
vendored
File diff suppressed because one or more lines are too long
2
notyf.min.js
vendored
2
notyf.min.js
vendored
File diff suppressed because one or more lines are too long
38
package.json
38
package.json
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "torrentparts",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "📑 A website to inspect and edit Torrent files and Magnet URLs",
|
|
||||||
"main": "bundle.js",
|
|
||||||
"dependencies": {
|
|
||||||
"browserify": "latest",
|
|
||||||
"Buffer": "latest",
|
|
||||||
"bytes": "latest",
|
|
||||||
"clipboard": "latest",
|
|
||||||
"dropzone": "latest",
|
|
||||||
"mime-types": "latest",
|
|
||||||
"parse-torrent": "latest",
|
|
||||||
"tippy.js": "latest",
|
|
||||||
"webtorrent": "latest"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"notyf": "latest",
|
|
||||||
"terser": "latest",
|
|
||||||
"watchify": "latest"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"watch": "watchify parse.js -o bundle.js",
|
|
||||||
"compile": "browserify parse.js -o bundle.js",
|
|
||||||
"minify": "terser bundle.js -c -m -o bundle.min.js",
|
|
||||||
"build": "npm run compile && npm run minify"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/leoherzog/TorrentParts.git"
|
|
||||||
},
|
|
||||||
"author": "Leo Herzog",
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/leoherzog/TorrentParts/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/leoherzog/TorrentParts"
|
|
||||||
}
|
|
||||||
512
parse.js
512
parse.js
@@ -1,512 +0,0 @@
|
|||||||
const clipboard = require('clipboard');
|
|
||||||
const parser = require('parse-torrent');
|
|
||||||
const Buffer = require('Buffer');
|
|
||||||
const bytes = require('bytes');
|
|
||||||
const mime = require('mime-types');
|
|
||||||
const WebTorrent = require('webtorrent');
|
|
||||||
const tippy = require('tippy.js').default;
|
|
||||||
|
|
||||||
var examples = document.getElementById('examples');
|
|
||||||
var example1 = document.getElementById('example1');
|
|
||||||
var example2 = document.getElementById('example2');
|
|
||||||
var example3 = document.getElementById('example3');
|
|
||||||
var properties = document.getElementById('properties');
|
|
||||||
var originalSourceIcon = document.getElementById('originalSourceIcon');
|
|
||||||
var source;
|
|
||||||
var sourceTooltip = tippy(originalSourceIcon, {"theme": "torrent-parts", "animation": "shift-away-subtle"});
|
|
||||||
var name = document.getElementById('name');
|
|
||||||
var reset = document.getElementById('reset');
|
|
||||||
var created = document.getElementById('created');
|
|
||||||
var createdBy = document.getElementById('createdBy');
|
|
||||||
var comment = document.getElementById('comment');
|
|
||||||
var hash = document.getElementById('hash');
|
|
||||||
var addTrackers = document.getElementById('addTrackers');
|
|
||||||
var addTracker = document.getElementById('addTracker');
|
|
||||||
var removeTrackers = document.getElementById('removeTrackers');
|
|
||||||
var announce = document.getElementById('announce');
|
|
||||||
var urlList = document.getElementById('urlList');
|
|
||||||
var addWebseed = document.getElementById('addWebseed');
|
|
||||||
var removeWebseeds = document.getElementById('removeWebseeds');
|
|
||||||
var files = document.getElementById('filesBody');
|
|
||||||
var getFiles = document.getElementById('getFiles');
|
|
||||||
var copyURL = document.getElementById('copyURL');
|
|
||||||
var copyMagnet = document.getElementById('copyMagnet');
|
|
||||||
var downloadTorrentWrapper = document.getElementById('downloadTorrentWrapper');
|
|
||||||
var downloadTorrent = document.getElementById('downloadTorrent');
|
|
||||||
var copyURLTooltip = tippy(copyURL, {"theme": "torrent-parts", "animation": "shift-away-subtle", "content": "Copy torrent.parts link to clipboard"});
|
|
||||||
var copyMagnetTooltip = tippy(copyMagnet, {"theme": "torrent-parts", "animation": "shift-away-subtle", "content": "Copy Magnet link to clipboard"});
|
|
||||||
var downloadTorrentTooltip = tippy(downloadTorrentWrapper, {"theme": "torrent-parts", "animation": "shift-away-subtle", "content": "Download Torrent file"});
|
|
||||||
var parsed;
|
|
||||||
var client = new WebTorrent();
|
|
||||||
var notyf = new Notyf({
|
|
||||||
"duration": 8000,
|
|
||||||
"dismissible": true,
|
|
||||||
"ripple": false,
|
|
||||||
"position": {
|
|
||||||
"x": "right",
|
|
||||||
"y": "top",
|
|
||||||
},
|
|
||||||
"types": [
|
|
||||||
{
|
|
||||||
"type": "success",
|
|
||||||
"background": "#46835C",
|
|
||||||
"icon": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "error",
|
|
||||||
"background": "#A60A0A",
|
|
||||||
"icon": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
function placeDownloadTooltips(e) {
|
|
||||||
if (window.innerWidth > 1080) {
|
|
||||||
copyURLTooltip.setProps({"placement": "right"});
|
|
||||||
copyMagnetTooltip.setProps({"placement": "right"});
|
|
||||||
downloadTorrentTooltip.setProps({"placement": "right"});
|
|
||||||
} else {
|
|
||||||
copyURLTooltip.setProps({"placement": "top"});
|
|
||||||
copyMagnetTooltip.setProps({"placement": "top"});
|
|
||||||
downloadTorrentTooltip.setProps({"placement": "top"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', placeDownloadTooltips);
|
|
||||||
placeDownloadTooltips();
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', start);
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
|
|
||||||
document.getElementById('magnet').addEventListener('keyup', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
if (event.key === "Enter") {
|
|
||||||
source = "magnet";
|
|
||||||
originalSourceIcon.innerHTML = '<span class="fad fa-magnet fa-fw"></span>';
|
|
||||||
sourceTooltip.setContent("Currently loaded information sourced from Magnet URL");
|
|
||||||
parse(magnet.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('torrent').addEventListener('change', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.target.files[0].arrayBuffer().then(function(arrayBuffer) {
|
|
||||||
source = "torrent-file";
|
|
||||||
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
|
||||||
sourceTooltip.setContent("Currently loaded information sourced from Torrent file");
|
|
||||||
parse(Buffer.from(arrayBuffer));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
example1.addEventListener('click', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
notyf.success("Parsing Ubuntu 20.04 Magnet URL");
|
|
||||||
parse("magnet:?xt=urn:btih:9fc20b9e98ea98b4a35e6223041a5ef94ea27809&dn=ubuntu-20.04-desktop-amd64.iso&tr=https%3A%2F%2Ftorrent.ubuntu.com%2Fannounce&tr=https%3A%2F%2Fipv6.torrent.ubuntu.com%2Fannounce");
|
|
||||||
});
|
|
||||||
|
|
||||||
example2.addEventListener('click', async function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
notyf.success("Fetching and Parsing “The WIRED CD” Torrent File...");
|
|
||||||
parseRemote("https://webtorrent.io/torrents/wired-cd.torrent");
|
|
||||||
});
|
|
||||||
|
|
||||||
example3.addEventListener('click', async function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
notyf.success("Parsing Jack Johnson Archive.org Torrent File");
|
|
||||||
let response = await fetch("jj2008-06-14.mk4_archive.torrent");
|
|
||||||
let arrayBuffer = await response.arrayBuffer();
|
|
||||||
parse(Buffer.from(arrayBuffer));
|
|
||||||
});
|
|
||||||
|
|
||||||
let copyurl = new clipboard('#copyURL');
|
|
||||||
copyurl.on('success', function(e) {
|
|
||||||
notyf.success('Copied site URL to clipboard!');
|
|
||||||
console.info(e);
|
|
||||||
gtag('event', 'share', {
|
|
||||||
"method": "Copy URL",
|
|
||||||
"content_id": e.text,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
copyurl.on('failure', function(e) {
|
|
||||||
notyf.error('Problem copying to clipboard');
|
|
||||||
console.warn(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
let copymagnet = new clipboard('#copyMagnet');
|
|
||||||
copymagnet.on('success', function(e) {
|
|
||||||
notyf.success('Copied Magnet URL to clipboard!');
|
|
||||||
gtag('event', 'share', {
|
|
||||||
"method": "Copy Magnet",
|
|
||||||
"content_id": e.text,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
copymagnet.on('failure', function(e) {
|
|
||||||
notyf.error('Problem copying to clipboard');
|
|
||||||
console.warn(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
name.addEventListener('input', propertyChange);
|
|
||||||
name.addEventListener('change', propertyChange);
|
|
||||||
name.addEventListener('reset', propertyChange);
|
|
||||||
name.addEventListener('paste', propertyChange);
|
|
||||||
reset.addEventListener('click', resetProperties);
|
|
||||||
comment.addEventListener('input', propertyChange);
|
|
||||||
comment.addEventListener('change', propertyChange);
|
|
||||||
comment.addEventListener('reset', propertyChange);
|
|
||||||
comment.addEventListener('paste', propertyChange);
|
|
||||||
addTrackers.addEventListener('click', addCurrentTrackers);
|
|
||||||
addTracker.addEventListener('click', addRow);
|
|
||||||
removeTrackers.addEventListener('click', () => removeAllRows('announce'));
|
|
||||||
addWebseed.addEventListener('click', addRow);
|
|
||||||
removeWebseeds.addEventListener('click', () => removeAllRows('urlList'));
|
|
||||||
getFiles.addEventListener('click', getFilesFromPeers);
|
|
||||||
|
|
||||||
tippy('[data-tippy-content]', {"theme": "torrent-parts", "animation": "shift-away-subtle"}); // all element-defined tooltips
|
|
||||||
sourceTooltip.disable();
|
|
||||||
|
|
||||||
if (window.location.hash) {
|
|
||||||
source = "shared-url";
|
|
||||||
originalSourceIcon.innerHTML = '<span class="fad fa-link fa-fw"></span>';
|
|
||||||
sourceTooltip.setContent("Currently loaded information sourced from shared torrent.parts link");
|
|
||||||
parse(window.location.hash.split('#')[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse(toLoad) {
|
|
||||||
resetProperties();
|
|
||||||
try {
|
|
||||||
console.info("Attempting parse");
|
|
||||||
parsed = parser(toLoad);
|
|
||||||
display();
|
|
||||||
if (parsed.xs) {
|
|
||||||
console.info("Magnet includes xs, attempting remote parse");
|
|
||||||
parseRemote(parsed.xs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) { // maybe they put a URL to a torrent file in the magnet box?
|
|
||||||
console.warn(e);
|
|
||||||
if (source == "magnet") {
|
|
||||||
console.info("Attempting remote parse");
|
|
||||||
parseRemote(toLoad);
|
|
||||||
} else { // probably not. Just a bad file.
|
|
||||||
notyf.error('Problem parsing input. Is this a .torrent file?');
|
|
||||||
console.error('Problem parsing input');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseRemote(toLoad) {
|
|
||||||
parser.remote(toLoad, function(err, result) {
|
|
||||||
if (err) {
|
|
||||||
notyf.error('Problem remotely fetching that file or parsing result');
|
|
||||||
console.warn(err);
|
|
||||||
resetProperties();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
source = "remote-torrent-file";
|
|
||||||
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
|
||||||
sourceTooltip.setContent("Currently loaded information sourced from remotely fetched Torrent file");
|
|
||||||
parsed = result;
|
|
||||||
display();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function display() {
|
|
||||||
|
|
||||||
console.log(parsed);
|
|
||||||
|
|
||||||
hash.value = parsed.infoHash;
|
|
||||||
name.value = parsed.name ? parsed.name : "";
|
|
||||||
if (parsed.created) {
|
|
||||||
created.value = parsed.created.toISOString().slice(0, 19);
|
|
||||||
created.type = "datetime-local";
|
|
||||||
} else {
|
|
||||||
created.type = "text";
|
|
||||||
}
|
|
||||||
createdBy.value = parsed.createdBy ? "by " + parsed.createdBy : "";
|
|
||||||
comment.value = parsed.comment ? parsed.comment : "";
|
|
||||||
|
|
||||||
announce.innerHTML = "";
|
|
||||||
if (parsed.announce && parsed.announce.length) {
|
|
||||||
for (let i = 0; i < parsed.announce.length; i++) {
|
|
||||||
let row = document.createElement('div');
|
|
||||||
row.className = 'announce';
|
|
||||||
row.dataset.index = i;
|
|
||||||
let tracker = document.createElement('input');
|
|
||||||
tracker.type = 'text';
|
|
||||||
tracker.value = parsed.announce[i];
|
|
||||||
tracker.dataset.index = i;
|
|
||||||
tracker.dataset.group = 'announce';
|
|
||||||
tracker.setAttribute('aria-label', 'Tracker URL #' + i);
|
|
||||||
tracker.addEventListener('input', propertyChange);
|
|
||||||
row.appendChild(tracker);
|
|
||||||
let remove = document.createElement('a');
|
|
||||||
remove.className = 'remove';
|
|
||||||
remove.dataset.index = i;
|
|
||||||
remove.innerHTML = '<span class="far fa-trash"></span>';
|
|
||||||
remove.addEventListener('click', removeRow);
|
|
||||||
row.appendChild(remove);
|
|
||||||
announce.appendChild(row);
|
|
||||||
}
|
|
||||||
// } else {
|
|
||||||
// announce.innerHTML = "<em>No trackers specified in the URL/File provided</em>";
|
|
||||||
}
|
|
||||||
|
|
||||||
urlList.innerHTML = "";
|
|
||||||
if (parsed.urlList && parsed.urlList.length) {
|
|
||||||
for (let i = 0; i < parsed.urlList.length; i++) {
|
|
||||||
let row = document.createElement('div');
|
|
||||||
row.className = 'urlList';
|
|
||||||
row.dataset.index = i;
|
|
||||||
let webseed = document.createElement('input');
|
|
||||||
webseed.type = 'text';
|
|
||||||
webseed.value = parsed.urlList[i];
|
|
||||||
webseed.dataset.index = i;
|
|
||||||
webseed.dataset.group = 'urlList';
|
|
||||||
webseed.setAttribute('aria-label', 'Webseed URL #' + i);
|
|
||||||
webseed.addEventListener('input', propertyChange);
|
|
||||||
row.appendChild(webseed);
|
|
||||||
let remove = document.createElement('a');
|
|
||||||
remove.className = 'remove';
|
|
||||||
remove.dataset.index = i;
|
|
||||||
remove.innerHTML = '<span class="far fa-trash"></span>';
|
|
||||||
remove.addEventListener('click', removeRow);
|
|
||||||
row.appendChild(remove);
|
|
||||||
urlList.appendChild(row);
|
|
||||||
}
|
|
||||||
// } else {
|
|
||||||
// urlList.innerHTML = "<em>No webseed URLs in the URL/File provided</em>";
|
|
||||||
}
|
|
||||||
|
|
||||||
files.innerHTML = "";
|
|
||||||
if (parsed.files && parsed.files.length) {
|
|
||||||
getFiles.style.display = "none";
|
|
||||||
for (let file of parsed.files) {
|
|
||||||
let icon = getFontAwesomeIconForMimetype(mime.lookup(file.name));
|
|
||||||
files.appendChild(createFileRow(icon, file.name, file.length));
|
|
||||||
}
|
|
||||||
files.appendChild(createFileRow('folder-tree', '', parsed.length));
|
|
||||||
downloadTorrentTooltip.setContent('Download Torrent file');
|
|
||||||
downloadTorrent.addEventListener('click', saveTorrent);
|
|
||||||
downloadTorrent.disabled = false;
|
|
||||||
} else {
|
|
||||||
if (client.torrents.length > 0) {
|
|
||||||
getFiles.style.display = "none";
|
|
||||||
files.innerHTML = '<input type="text" placeholder="Attempting fetching of files from Webtorrent..." aria-label="Attempting fetching of files from Webtorrent..." disabled>';
|
|
||||||
} else {
|
|
||||||
getFiles.style.display = "block";
|
|
||||||
files.innerHTML = '<input type="text" placeholder="Not included in the URL/File provided" aria-label="Files information not included in the URL/File provided" disabled>';
|
|
||||||
}
|
|
||||||
downloadTorrentTooltip.setContent('Files metadata is required to generate a Torrent file. Try fetching files list from WebTorrent.');
|
|
||||||
downloadTorrent.removeEventListener('click', saveTorrent);
|
|
||||||
downloadTorrent.disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
copyURL.setAttribute('data-clipboard-text', window.location.origin + "#" + parser.toMagnetURI(parsed));
|
|
||||||
copyMagnet.setAttribute('data-clipboard-text', parser.toMagnetURI(parsed));
|
|
||||||
|
|
||||||
examples.style.display = 'none';
|
|
||||||
properties.style.display = 'flex';
|
|
||||||
|
|
||||||
window.location.hash = parser.toMagnetURI(parsed);
|
|
||||||
|
|
||||||
if (parsed.name) {
|
|
||||||
document.title = "Torrent Parts | " + parsed.name;
|
|
||||||
} else {
|
|
||||||
document.title = "Torrent Parts | Inspect and edit what's in your Torrent file or Magnet link";
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceTooltip.enable();
|
|
||||||
|
|
||||||
gtag('event', 'view_item', {
|
|
||||||
items: [{
|
|
||||||
"item_id": parsed.infoHash,
|
|
||||||
"item_name": parsed.name,
|
|
||||||
"item_category": source
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFileRow(icon, name, size) {
|
|
||||||
let row = document.createElement('tr');
|
|
||||||
let iconcell = document.createElement('td');
|
|
||||||
iconcell.innerHTML = '<span class="far fa-' + icon + '"></span>';
|
|
||||||
row.appendChild(iconcell);
|
|
||||||
let namecell = document.createElement('td');
|
|
||||||
namecell.innerHTML = name;
|
|
||||||
row.appendChild(namecell);
|
|
||||||
let totalcell = document.createElement('td');
|
|
||||||
totalcell.innerHTML = bytes.format(size, {"decimalPlaces": 1, "unitSeparator": " "});
|
|
||||||
row.appendChild(totalcell);
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFontAwesomeIconForMimetype(mimetype) {
|
|
||||||
if (!mimetype) return 'file';
|
|
||||||
switch (true) {
|
|
||||||
case mimetype.includes("msword"):
|
|
||||||
case mimetype.includes("wordprocessingml"):
|
|
||||||
case mimetype.includes("opendocument.text"):
|
|
||||||
case mimetype.includes("abiword"):
|
|
||||||
return 'file-word';
|
|
||||||
case mimetype.includes("ms-excel"):
|
|
||||||
case mimetype.includes("spreadsheet"):
|
|
||||||
return 'file-powerpoint';
|
|
||||||
case mimetype.includes("powerpoint"):
|
|
||||||
case mimetype.includes("presentation"):
|
|
||||||
return 'file-powerpoint';
|
|
||||||
case mimetype.includes("7z-"):
|
|
||||||
case mimetype.includes("iso9660"):
|
|
||||||
case mimetype.includes("zip"):
|
|
||||||
case mimetype.includes("octet-stream"):
|
|
||||||
return 'file-archive';
|
|
||||||
case mimetype.includes("csv"):
|
|
||||||
return 'file-csv';
|
|
||||||
case mimetype.includes("pdf"):
|
|
||||||
return 'file-pdf';
|
|
||||||
case mimetype.includes("font"):
|
|
||||||
return 'file-contract';
|
|
||||||
case mimetype.includes("text"):
|
|
||||||
case mimetype.includes("subrip"):
|
|
||||||
case mimetype.includes("vtt"):
|
|
||||||
return 'file-alt';
|
|
||||||
case mimetype.includes("audio"):
|
|
||||||
return 'file-audio';
|
|
||||||
case mimetype.includes("image"):
|
|
||||||
return 'file-image';
|
|
||||||
case mimetype.includes("video"):
|
|
||||||
return 'file-video';
|
|
||||||
default:
|
|
||||||
return 'file';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function propertyChange(e) {
|
|
||||||
if (this.dataset.group) {
|
|
||||||
parsed[this.dataset.group][this.dataset.index] = this.value ? this.value : "";
|
|
||||||
} else {
|
|
||||||
parsed[this.id] = this.value ? this.value : "";
|
|
||||||
}
|
|
||||||
window.location.hash = parser.toMagnetURI(parsed);
|
|
||||||
updateModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetProperties() {
|
|
||||||
document.getElementById('magnet').value = "";
|
|
||||||
document.getElementById('torrent').value = "";
|
|
||||||
examples.style.display = 'flex';
|
|
||||||
properties.style.display = 'none';
|
|
||||||
name.value = "";
|
|
||||||
created.value = "";
|
|
||||||
createdBy.value = "";
|
|
||||||
comment.value = "";
|
|
||||||
hash.value = "";
|
|
||||||
announce.innerHTML = "";
|
|
||||||
urlList.innerHTML = "";
|
|
||||||
client.torrents.forEach(torrent => torrent.destroy());
|
|
||||||
getFiles.style.display = "block";
|
|
||||||
files.innerHTML = "";
|
|
||||||
window.location.hash = "";
|
|
||||||
copyURL.setAttribute('data-clipboard-text', "");
|
|
||||||
copyMagnet.setAttribute('data-clipboard-text', "");
|
|
||||||
document.title = "Torrent Parts | Inspect and edit what's in your Torrent file or Magnet link";
|
|
||||||
sourceTooltip.disable();
|
|
||||||
gtag('event', 'reset');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addCurrentTrackers() {
|
|
||||||
addTrackers.className = 'disabled';
|
|
||||||
addTrackers.innerHTML = 'Adding...';
|
|
||||||
try {
|
|
||||||
let response = await fetch("https://newtrackon.com/api/100"); // get trackers with 100% uptime
|
|
||||||
let trackers = await response.text();
|
|
||||||
parsed.announce = parsed.announce.concat(trackers.split('\n\n'));
|
|
||||||
parsed.announce.push("http://bt1.archive.org:6969/announce");
|
|
||||||
parsed.announce.push("http://bt2.archive.org:6969/announce");
|
|
||||||
parsed.announce = parsed.announce.filter((v,i) => v && parsed.announce.indexOf(v) === i); // remove duplicates and empties
|
|
||||||
notyf.success('Added known working trackers from newTrackon');
|
|
||||||
updateModified();
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
notyf.error('Problem fetching trackers from newTrackon');
|
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
addTrackers.className = '';
|
|
||||||
addTrackers.innerHTML = 'Add Known Working Trackers';
|
|
||||||
display();
|
|
||||||
gtag('event', 'add_trackers');
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRow() {
|
|
||||||
parsed[this.dataset.type].unshift("");
|
|
||||||
display();
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeRow() {
|
|
||||||
parsed[this.parentElement.className].splice(this.parentElement.dataset.index, 1);
|
|
||||||
display();
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeAllRows(type) {
|
|
||||||
parsed[type] = [];
|
|
||||||
updateModified();
|
|
||||||
display();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateModified() {
|
|
||||||
parsed.created = new Date();
|
|
||||||
parsed.createdBy = "Torrent Parts <https://torrent.parts/>";
|
|
||||||
if (parsed.created) {
|
|
||||||
created.value = parsed.created.toISOString().slice(0, 19);
|
|
||||||
created.type = "datetime-local";
|
|
||||||
} else {
|
|
||||||
created.type = "text";
|
|
||||||
}
|
|
||||||
createdBy.value = parsed.createdBy ? "by " + parsed.createdBy : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFilesFromPeers() {
|
|
||||||
console.info("Attempting fetching files from Webtorrent...");
|
|
||||||
getFiles.style.display = "none";
|
|
||||||
parsed.announce.push("wss://tracker.webtorrent.io");
|
|
||||||
parsed.announce.push("wss://tracker.openwebtorrent.com");
|
|
||||||
parsed.announce.push("wss://tracker.btorrent.xyz");
|
|
||||||
parsed.announce.push("wss://tracker.fastcast.nz");
|
|
||||||
parsed.announce = parsed.announce.filter((v,i) => v && parsed.announce.indexOf(v) === i); // remove duplicates and empties
|
|
||||||
client.add(parser.toMagnetURI(parsed), (torrent) => {
|
|
||||||
parsed.info = Object.assign({}, torrent.info); // clone object
|
|
||||||
parsed.files = torrent.files;
|
|
||||||
parsed.infoBuffer = torrent.infoBuffer;
|
|
||||||
parsed.length = torrent.length;
|
|
||||||
parsed.lastPieceLength = torrent.lastPieceLength;
|
|
||||||
updateModified();
|
|
||||||
display();
|
|
||||||
notyf.success('Fetched file details from Webtorrent peers');
|
|
||||||
torrent.destroy();
|
|
||||||
});
|
|
||||||
display();
|
|
||||||
gtag('event', 'attempt_webtorrent_fetch');
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/36899900/2700296
|
|
||||||
function saveTorrent() {
|
|
||||||
let data = parser.toTorrentFile(parsed);
|
|
||||||
if (data !== null && navigator.msSaveBlob)
|
|
||||||
return navigator.msSaveBlob(new Blob([data], { "type": "application/x-bittorrent" }), parsed.name + '.torrent');
|
|
||||||
let a = document.createElement('a');
|
|
||||||
a.style.display = 'none';
|
|
||||||
let url = window.URL.createObjectURL(new Blob([data], { "type": "application/x-bittorrent" }));
|
|
||||||
a.setAttribute("href", url);
|
|
||||||
a.setAttribute("download", parsed.name + '.torrent');
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
a.remove();
|
|
||||||
gtag('event', 'share', {
|
|
||||||
"method": "Torrent Download",
|
|
||||||
"content_id": parsed.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
2
robots.txt
Normal file
2
robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
547
src/parse.js
Normal file
547
src/parse.js
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
import { Buffer } from 'https://cdn.jsdelivr.net/npm/buffer@6/+esm';
|
||||||
|
import clipboard from 'https://cdn.jsdelivr.net/npm/clipboard@2/+esm';
|
||||||
|
import parseTorrent, { toMagnetURI, toTorrentFile, remote as parseTorrentRemote } from 'https://cdn.jsdelivr.net/npm/parse-torrent@11/+esm';
|
||||||
|
import bytes from 'https://cdn.jsdelivr.net/npm/bytes@3/+esm';
|
||||||
|
import mime from 'https://cdn.jsdelivr.net/npm/mime-types@2/+esm';
|
||||||
|
import WebTorrent from 'https://cdn.jsdelivr.net/npm/webtorrent@2/dist/webtorrent.min.js';
|
||||||
|
import tippy from 'https://cdn.jsdelivr.net/npm/tippy.js@6/+esm';
|
||||||
|
import { Notyf } from 'https://cdn.jsdelivr.net/npm/notyf@3/+esm';
|
||||||
|
|
||||||
|
var examples = document.getElementById('examples');
|
||||||
|
var example1 = document.getElementById('example1');
|
||||||
|
var example2 = document.getElementById('example2');
|
||||||
|
var example3 = document.getElementById('example3');
|
||||||
|
var properties = document.getElementById('properties');
|
||||||
|
var originalSourceIcon = document.getElementById('originalSourceIcon');
|
||||||
|
var source;
|
||||||
|
var sourceTooltip = tippy(originalSourceIcon, { theme: 'torrent-parts', animation: 'shift-away-subtle' });
|
||||||
|
var name = document.getElementById('name');
|
||||||
|
var reset = document.getElementById('reset');
|
||||||
|
var created = document.getElementById('created');
|
||||||
|
var createdBy = document.getElementById('createdBy');
|
||||||
|
var comment = document.getElementById('comment');
|
||||||
|
var hash = document.getElementById('hash');
|
||||||
|
var addTrackers = document.getElementById('addTrackers');
|
||||||
|
var addTracker = document.getElementById('addTracker');
|
||||||
|
var removeTrackers = document.getElementById('removeTrackers');
|
||||||
|
var announce = document.getElementById('announce');
|
||||||
|
var urlList = document.getElementById('urlList');
|
||||||
|
var addWebseed = document.getElementById('addWebseed');
|
||||||
|
var removeWebseeds = document.getElementById('removeWebseeds');
|
||||||
|
var pieces = document.getElementById('pieces');
|
||||||
|
var files = document.getElementById('filesBody');
|
||||||
|
var getFiles = document.getElementById('getFiles');
|
||||||
|
var openURLWrapper = document.getElementById('openURLWrapper');
|
||||||
|
var openURL = document.getElementById('openURL');
|
||||||
|
var copyURL = document.getElementById('copyURL');
|
||||||
|
var copyMagnet = document.getElementById('copyMagnet');
|
||||||
|
var downloadTorrentWrapper = document.getElementById('downloadTorrentWrapper');
|
||||||
|
var downloadTorrent = document.getElementById('downloadTorrent');
|
||||||
|
var openURLTooltip = tippy(openURL, { theme: 'torrent-parts', animation: 'shift-away-subtle', content: 'Open this Magnet URL in your Torrent client' });
|
||||||
|
var copyURLTooltip = tippy(copyURL, { theme: 'torrent-parts', animation: 'shift-away-subtle', content: 'Copy torrent.parts link to clipboard' });
|
||||||
|
var copyMagnetTooltip = tippy(copyMagnet, { theme: 'torrent-parts', animation: 'shift-away-subtle', content: 'Copy Magnet link to clipboard' });
|
||||||
|
var downloadTorrentTooltip = tippy(downloadTorrentWrapper, { theme: 'torrent-parts', animation: 'shift-away-subtle', content: 'Download Torrent file' });
|
||||||
|
var parsed;
|
||||||
|
var client = new WebTorrent();
|
||||||
|
var notyf = new Notyf({
|
||||||
|
duration: 8000,
|
||||||
|
dismissible: true,
|
||||||
|
ripple: false,
|
||||||
|
position: {
|
||||||
|
x: 'right',
|
||||||
|
y: 'top',
|
||||||
|
},
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
type: 'success',
|
||||||
|
background: '#46835C',
|
||||||
|
icon: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'error',
|
||||||
|
background: '#A60A0A',
|
||||||
|
icon: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
function placeDownloadTooltips(e) {
|
||||||
|
if (window.innerWidth > 1080) {
|
||||||
|
openURLTooltip.setProps({ placement: 'right' });
|
||||||
|
copyURLTooltip.setProps({ placement: 'right' });
|
||||||
|
copyMagnetTooltip.setProps({ placement: 'right' });
|
||||||
|
downloadTorrentTooltip.setProps({ placement: 'right' });
|
||||||
|
} else {
|
||||||
|
openURLTooltip.setProps({ placement: 'top' });
|
||||||
|
copyURLTooltip.setProps({ placement: 'top' });
|
||||||
|
copyMagnetTooltip.setProps({ placement: 'top' });
|
||||||
|
downloadTorrentTooltip.setProps({ placement: 'top' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', placeDownloadTooltips);
|
||||||
|
placeDownloadTooltips();
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', start);
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
// form submission prevention
|
||||||
|
document.getElementById('startForm').addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// magnet input
|
||||||
|
document.getElementById('magnet').addEventListener('keyup', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
source = 'magnet';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-magnet fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from Magnet URL');
|
||||||
|
parse(this.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// torrent select button
|
||||||
|
document.getElementById('torrent').addEventListener('change', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.target.files[0].arrayBuffer().then(function (arrayBuffer) {
|
||||||
|
source = 'torrent-file';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from Torrent file');
|
||||||
|
parse(Buffer.from(arrayBuffer));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// body drag-and-drop torrent file support
|
||||||
|
document.addEventListener('dragover', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('drop', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (event.dataTransfer.items.length === 0) return;
|
||||||
|
if (event.dataTransfer.items[0].kind !== 'file') return;
|
||||||
|
const file = event.dataTransfer.items[0].getAsFile();
|
||||||
|
if (!file) return;
|
||||||
|
file.arrayBuffer().then(function (arrayBuffer) {
|
||||||
|
source = 'torrent-file';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from Torrent file');
|
||||||
|
parse(Buffer.from(arrayBuffer));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// example buttons
|
||||||
|
example1.addEventListener('click', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
notyf.success('Parsing Ubuntu 24.04 Magnet URL');
|
||||||
|
source = 'magnet';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-magnet fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from Magnet URL');
|
||||||
|
parse('magnet:?xt=urn:btih:2aa4f5a7e209e54b32803d43670971c4c8caaa05&dn=ubuntu-24.04-desktop-amd64.iso&tr=https%3A%2F%2Ftorrent.ubuntu.com%2Fannounce&tr=https%3A%2F%2Fipv6.torrent.ubuntu.com%2Fannounce');
|
||||||
|
});
|
||||||
|
|
||||||
|
example2.addEventListener('click', async function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
notyf.success('Fetching and Parsing “The WIRED CD” Torrent File...');
|
||||||
|
source = 'remote-torrent-file';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from remotely fetched Torrent file');
|
||||||
|
parseRemote('https://webtorrent.io/torrents/wired-cd.torrent');
|
||||||
|
});
|
||||||
|
|
||||||
|
example3.addEventListener('click', async function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
notyf.success('Parsing Jack Johnson Archive.org Torrent File');
|
||||||
|
let response = await fetch('/ext/jj2008-06-14.mk4_archive.torrent');
|
||||||
|
let arrayBuffer = await response.arrayBuffer();
|
||||||
|
source = 'torrent-file';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from Torrent file');
|
||||||
|
parse(Buffer.from(arrayBuffer));
|
||||||
|
});
|
||||||
|
|
||||||
|
// share buttons
|
||||||
|
let copyurl = new clipboard('#copyURL');
|
||||||
|
copyurl.on('success', function (e) {
|
||||||
|
notyf.success('Copied site URL to clipboard!');
|
||||||
|
console.info(e);
|
||||||
|
});
|
||||||
|
copyurl.on('failure', function (e) {
|
||||||
|
notyf.error('Problem copying to clipboard');
|
||||||
|
console.warn(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
let copymagnet = new clipboard('#copyMagnet');
|
||||||
|
copymagnet.on('success', function (e) {
|
||||||
|
notyf.success('Copied Magnet URL to clipboard!');
|
||||||
|
});
|
||||||
|
copymagnet.on('failure', function (e) {
|
||||||
|
notyf.error('Problem copying to clipboard');
|
||||||
|
console.warn(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
// details field listeners
|
||||||
|
name.addEventListener('input', propertyChange);
|
||||||
|
name.addEventListener('change', propertyChange);
|
||||||
|
name.addEventListener('reset', propertyChange);
|
||||||
|
name.addEventListener('paste', propertyChange);
|
||||||
|
reset.addEventListener('click', resetProperties);
|
||||||
|
comment.addEventListener('input', propertyChange);
|
||||||
|
comment.addEventListener('change', propertyChange);
|
||||||
|
comment.addEventListener('reset', propertyChange);
|
||||||
|
comment.addEventListener('paste', propertyChange);
|
||||||
|
addTrackers.addEventListener('click', addCurrentTrackers);
|
||||||
|
addTracker.addEventListener('click', addRow);
|
||||||
|
removeTrackers.addEventListener('click', () => removeAllRows('announce'));
|
||||||
|
addWebseed.addEventListener('click', addRow);
|
||||||
|
removeWebseeds.addEventListener('click', () => removeAllRows('urlList'));
|
||||||
|
getFiles.addEventListener('click', getFilesFromPeers);
|
||||||
|
|
||||||
|
tippy('[data-tippy-content]', { theme: 'torrent-parts', animation: 'shift-away-subtle' }); // all element-defined tooltips
|
||||||
|
sourceTooltip.disable();
|
||||||
|
|
||||||
|
if (window.location.hash) {
|
||||||
|
source = 'shared-url';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-link fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from shared torrent.parts link');
|
||||||
|
parse(window.location.hash.split('#')[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parse(toLoad) {
|
||||||
|
resetProperties();
|
||||||
|
try {
|
||||||
|
console.info('Attempting parse');
|
||||||
|
parsed = await parseTorrent(toLoad);
|
||||||
|
display();
|
||||||
|
if (parsed.xs) {
|
||||||
|
console.info('Magnet includes xs, attempting remote parse');
|
||||||
|
parseRemote(parsed.xs);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// maybe they put a URL to a torrent file in the magnet box?
|
||||||
|
console.warn(e);
|
||||||
|
if (source == 'magnet') {
|
||||||
|
console.info('Attempting remote parse');
|
||||||
|
parseRemote(toLoad);
|
||||||
|
} else {
|
||||||
|
// probably not. Just a bad file.
|
||||||
|
notyf.error('Problem parsing input. Is this a .torrent file?');
|
||||||
|
console.error('Problem parsing input');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseRemote(toLoad) {
|
||||||
|
try {
|
||||||
|
parsed = await new Promise((resolve, reject) => {
|
||||||
|
parseTorrentRemote(toLoad, (err, result) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
source = 'remote-torrent-file';
|
||||||
|
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
||||||
|
sourceTooltip.setContent('Currently loaded information sourced from remotely fetched Torrent file');
|
||||||
|
display();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(err);
|
||||||
|
notyf.error('Problem remotely fetching that file or parsing result');
|
||||||
|
resetProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function display() {
|
||||||
|
console.log(parsed);
|
||||||
|
|
||||||
|
hash.value = parsed.infoHash;
|
||||||
|
name.value = parsed.name ? parsed.name : '';
|
||||||
|
if (parsed.created) {
|
||||||
|
created.value = parsed.created.toISOString().slice(0, 19);
|
||||||
|
created.type = 'datetime-local';
|
||||||
|
} else {
|
||||||
|
created.type = 'text';
|
||||||
|
}
|
||||||
|
createdBy.value = parsed.createdBy ? ' by ' + parsed.createdBy : '';
|
||||||
|
comment.value = parsed.comment ? parsed.comment : '';
|
||||||
|
pieces.value = parsed.pieces ? parsed.pieces.length.toLocaleString() + ' ' + bytes.format(parsed.pieceLength, { decimalPlaces: 1, unitSeparator: ' ' }) + ' pieces (last piece ' + bytes.format(parsed.lastPieceLength, { decimalPlaces: 1, unitSeparator: ' ' }) + ')' : '';
|
||||||
|
|
||||||
|
announce.innerHTML = '';
|
||||||
|
if (parsed.announce && parsed.announce.length) {
|
||||||
|
for (let i = 0; i < parsed.announce.length; i++) {
|
||||||
|
let row = document.createElement('div');
|
||||||
|
row.className = 'announce';
|
||||||
|
row.dataset.index = i;
|
||||||
|
let tracker = document.createElement('input');
|
||||||
|
tracker.type = 'text';
|
||||||
|
tracker.value = parsed.announce[i];
|
||||||
|
tracker.dataset.index = i;
|
||||||
|
tracker.dataset.group = 'announce';
|
||||||
|
tracker.setAttribute('aria-label', 'Tracker URL #' + i);
|
||||||
|
tracker.addEventListener('input', propertyChange);
|
||||||
|
row.appendChild(tracker);
|
||||||
|
let remove = document.createElement('button');
|
||||||
|
remove.type = 'button';
|
||||||
|
remove.className = 'remove';
|
||||||
|
remove.dataset.index = i;
|
||||||
|
remove.innerHTML = '<span class="far fa-trash"></span>';
|
||||||
|
remove.setAttribute('aria-label', 'Remove tracker #' + i);
|
||||||
|
remove.addEventListener('click', removeRow);
|
||||||
|
row.appendChild(remove);
|
||||||
|
announce.appendChild(row);
|
||||||
|
}
|
||||||
|
// } else {
|
||||||
|
// announce.innerHTML = '<em>No trackers specified in the URL/File provided</em>';
|
||||||
|
}
|
||||||
|
|
||||||
|
urlList.innerHTML = '';
|
||||||
|
if (parsed.urlList && parsed.urlList.length) {
|
||||||
|
for (let i = 0; i < parsed.urlList.length; i++) {
|
||||||
|
let row = document.createElement('div');
|
||||||
|
row.className = 'urlList';
|
||||||
|
row.dataset.index = i;
|
||||||
|
let webseed = document.createElement('input');
|
||||||
|
webseed.type = 'text';
|
||||||
|
webseed.value = parsed.urlList[i];
|
||||||
|
webseed.dataset.index = i;
|
||||||
|
webseed.dataset.group = 'urlList';
|
||||||
|
webseed.setAttribute('aria-label', 'Webseed URL #' + i);
|
||||||
|
webseed.addEventListener('input', propertyChange);
|
||||||
|
row.appendChild(webseed);
|
||||||
|
let remove = document.createElement('button');
|
||||||
|
remove.type = 'button';
|
||||||
|
remove.className = 'remove';
|
||||||
|
remove.dataset.index = i;
|
||||||
|
remove.innerHTML = '<span class="far fa-trash"></span>';
|
||||||
|
remove.setAttribute('aria-label', 'Remove webseed #' + i);
|
||||||
|
remove.addEventListener('click', removeRow);
|
||||||
|
row.appendChild(remove);
|
||||||
|
urlList.appendChild(row);
|
||||||
|
}
|
||||||
|
// } else {
|
||||||
|
// urlList.innerHTML = '<em>No webseed URLs in the URL/File provided</em>';
|
||||||
|
}
|
||||||
|
|
||||||
|
files.innerHTML = '';
|
||||||
|
if (parsed.files && parsed.files.length) {
|
||||||
|
getFiles.style.display = 'none';
|
||||||
|
if (parsed.files.length < 100) {
|
||||||
|
for (let file of parsed.files) {
|
||||||
|
let icon = getFontAwesomeIconForMimetype(mime.lookup(file.name));
|
||||||
|
files.appendChild(createFileRow(icon, file.name, file.length));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
let icon = getFontAwesomeIconForMimetype(mime.lookup(parsed.files[i].name));
|
||||||
|
files.appendChild(createFileRow(icon, parsed.files[i].name, parsed.files[i].length));
|
||||||
|
}
|
||||||
|
files.appendChild(createFileRow('', '...and another ' + (parsed.files.length - 100) + ' more files', ''));
|
||||||
|
}
|
||||||
|
files.appendChild(createFileRow('folder-tree', '', parsed.length));
|
||||||
|
openURLWrapper.href = toMagnetURI(parsed);
|
||||||
|
downloadTorrentTooltip.setContent('Download Torrent file');
|
||||||
|
downloadTorrent.addEventListener('click', saveTorrent);
|
||||||
|
downloadTorrent.disabled = false;
|
||||||
|
} else {
|
||||||
|
if (client.torrents.length > 0) {
|
||||||
|
getFiles.style.display = 'none';
|
||||||
|
files.innerHTML = '<output>Attempting fetching of files from Webtorrent...</output>';
|
||||||
|
} else {
|
||||||
|
getFiles.style.display = 'block';
|
||||||
|
files.innerHTML = '<output>Not included in the URL/File provided</output>';
|
||||||
|
}
|
||||||
|
downloadTorrentTooltip.setContent('Files metadata is required to generate a Torrent file. Try fetching files list from WebTorrent.');
|
||||||
|
downloadTorrent.removeEventListener('click', saveTorrent);
|
||||||
|
downloadTorrent.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
copyURL.setAttribute('data-clipboard-text', window.location.origin + '#' + toMagnetURI(parsed));
|
||||||
|
copyMagnet.setAttribute('data-clipboard-text', toMagnetURI(parsed));
|
||||||
|
|
||||||
|
examples.style.display = 'none';
|
||||||
|
properties.style.display = 'flex';
|
||||||
|
|
||||||
|
window.location.hash = toMagnetURI(parsed);
|
||||||
|
|
||||||
|
if (parsed.name) {
|
||||||
|
document.title = 'Torrent Parts | ' + parsed.name;
|
||||||
|
} else {
|
||||||
|
document.title = "Torrent Parts | Inspect and edit what's in your Torrent file or Magnet link";
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceTooltip.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFileRow(icon, name, size) {
|
||||||
|
let row = document.createElement('tr');
|
||||||
|
let iconcell = document.createElement('td');
|
||||||
|
if (icon) iconcell.innerHTML = '<span class="far fa-' + icon + '"></span>';
|
||||||
|
row.appendChild(iconcell);
|
||||||
|
let namecell = document.createElement('td');
|
||||||
|
namecell.textContent = name;
|
||||||
|
row.appendChild(namecell);
|
||||||
|
let totalcell = document.createElement('td');
|
||||||
|
totalcell.innerHTML = bytes.format(size, { decimalPlaces: 1, unitSeparator: ' ' });
|
||||||
|
row.appendChild(totalcell);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFontAwesomeIconForMimetype(mimetype) {
|
||||||
|
if (!mimetype) return 'file';
|
||||||
|
switch (true) {
|
||||||
|
case mimetype.includes('msword'):
|
||||||
|
case mimetype.includes('wordprocessingml'):
|
||||||
|
case mimetype.includes('opendocument.text'):
|
||||||
|
case mimetype.includes('abiword'):
|
||||||
|
return 'file-word';
|
||||||
|
case mimetype.includes('ms-excel'):
|
||||||
|
case mimetype.includes('spreadsheet'):
|
||||||
|
return 'file-spreadsheet';
|
||||||
|
case mimetype.includes('powerpoint'):
|
||||||
|
case mimetype.includes('presentation'):
|
||||||
|
return 'file-powerpoint';
|
||||||
|
case mimetype.includes('7z-'):
|
||||||
|
case mimetype.includes('iso9660'):
|
||||||
|
case mimetype.includes('zip'):
|
||||||
|
case mimetype.includes('octet-stream'):
|
||||||
|
return 'file-archive';
|
||||||
|
case mimetype.includes('csv'):
|
||||||
|
return 'file-csv';
|
||||||
|
case mimetype.includes('pdf'):
|
||||||
|
return 'file-pdf';
|
||||||
|
case mimetype.includes('font'):
|
||||||
|
return 'file-contract';
|
||||||
|
case mimetype.includes('text'):
|
||||||
|
case mimetype.includes('subrip'):
|
||||||
|
case mimetype.includes('vtt'):
|
||||||
|
return 'file-alt';
|
||||||
|
case mimetype.includes('audio'):
|
||||||
|
return 'file-audio';
|
||||||
|
case mimetype.includes('image'):
|
||||||
|
return 'file-image';
|
||||||
|
case mimetype.includes('video'):
|
||||||
|
return 'file-video';
|
||||||
|
default:
|
||||||
|
return 'file';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function propertyChange(e) {
|
||||||
|
if (this.dataset.group) {
|
||||||
|
parsed[this.dataset.group][this.dataset.index] = this.value ? this.value : '';
|
||||||
|
} else {
|
||||||
|
parsed[this.id] = this.value ? this.value : '';
|
||||||
|
}
|
||||||
|
window.location.hash = toMagnetURI(parsed);
|
||||||
|
updateModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetProperties() {
|
||||||
|
document.getElementById('magnet').value = '';
|
||||||
|
document.getElementById('torrent').value = '';
|
||||||
|
examples.style.display = 'flex';
|
||||||
|
properties.style.display = 'none';
|
||||||
|
name.value = '';
|
||||||
|
created.value = '';
|
||||||
|
createdBy.value = '';
|
||||||
|
comment.value = '';
|
||||||
|
hash.value = '';
|
||||||
|
announce.innerHTML = '';
|
||||||
|
urlList.innerHTML = '';
|
||||||
|
client.torrents.forEach((torrent) => torrent.destroy());
|
||||||
|
getFiles.style.display = 'block';
|
||||||
|
files.innerHTML = '';
|
||||||
|
window.location.hash = '';
|
||||||
|
copyURL.setAttribute('data-clipboard-text', '');
|
||||||
|
copyMagnet.setAttribute('data-clipboard-text', '');
|
||||||
|
document.title = "Torrent Parts | Inspect and edit what's in your Torrent file or Magnet link";
|
||||||
|
sourceTooltip.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addCurrentTrackers() {
|
||||||
|
addTrackers.className = 'disabled';
|
||||||
|
addTrackers.innerHTML = 'Adding...';
|
||||||
|
try {
|
||||||
|
let response = await fetch('https://newtrackon.com/api/stable'); // get trackers with 95% uptime
|
||||||
|
let trackers = await response.text();
|
||||||
|
parsed.announce = (parsed.announce || []).concat(trackers.split('\n\n'));
|
||||||
|
parsed.announce.push('http://bt1.archive.org:6969/announce');
|
||||||
|
parsed.announce.push('http://bt2.archive.org:6969/announce');
|
||||||
|
parsed.announce = parsed.announce.filter((v, i) => v && parsed.announce.indexOf(v) === i); // remove duplicates and empties
|
||||||
|
notyf.success('Added known working trackers from newTrackon');
|
||||||
|
updateModified();
|
||||||
|
} catch (e) {
|
||||||
|
notyf.error('Problem fetching trackers from newTrackon');
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
addTrackers.className = '';
|
||||||
|
addTrackers.innerHTML = 'Add Known Working Trackers';
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRow() {
|
||||||
|
parsed[this.dataset.type].unshift('');
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRow() {
|
||||||
|
parsed[this.parentElement.className].splice(this.parentElement.dataset.index, 1);
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAllRows(type) {
|
||||||
|
parsed[type] = [];
|
||||||
|
updateModified();
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateModified() {
|
||||||
|
parsed.created = new Date();
|
||||||
|
parsed.createdBy = 'Torrent Parts <https://torrent.parts/>';
|
||||||
|
if (parsed.created) {
|
||||||
|
created.value = parsed.created.toISOString().slice(0, 19);
|
||||||
|
created.type = 'datetime-local';
|
||||||
|
} else {
|
||||||
|
created.type = 'text';
|
||||||
|
}
|
||||||
|
createdBy.value = parsed.createdBy ? ' by ' + parsed.createdBy : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilesFromPeers() {
|
||||||
|
console.info('Attempting fetching files from Webtorrent...');
|
||||||
|
getFiles.style.display = 'none';
|
||||||
|
parsed.announce = parsed.announce || [];
|
||||||
|
parsed.announce.push('wss://tracker.webtorrent.dev');
|
||||||
|
parsed.announce.push('wss://tracker.openwebtorrent.com');
|
||||||
|
parsed.announce.push('wss://tracker.btorrent.xyz');
|
||||||
|
parsed.announce.push('wss://tracker.fastcast.nz');
|
||||||
|
parsed.announce.push('wss://tracker.files.fm:7073/announce');
|
||||||
|
parsed.announce = parsed.announce.filter((v, i) => v && parsed.announce.indexOf(v) === i); // remove duplicates and empties
|
||||||
|
client.add(toMagnetURI(parsed), (torrent) => {
|
||||||
|
parsed.info = Object.assign({}, torrent.info); // clone object
|
||||||
|
parsed.files = torrent.files;
|
||||||
|
parsed.infoBuffer = torrent.infoBuffer;
|
||||||
|
parsed.length = torrent.length;
|
||||||
|
parsed.lastPieceLength = torrent.lastPieceLength;
|
||||||
|
updateModified();
|
||||||
|
display();
|
||||||
|
notyf.success('Fetched file details from Webtorrent peers');
|
||||||
|
torrent.destroy();
|
||||||
|
});
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveTorrent() {
|
||||||
|
const data = toTorrentFile(parsed);
|
||||||
|
const blob = new Blob([data], { type: 'application/x-bittorrent' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = parsed.name + '.torrent';
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
@@ -1,6 +1,47 @@
|
|||||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
|
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
|
||||||
|
|
||||||
|
/* Custom Tippy Theme */
|
||||||
|
.tippy-box[data-theme~="torrent-parts"] {
|
||||||
|
background-color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tippy-box[data-theme~="torrent-parts"][data-placement^="top"] > .tippy-arrow:before {
|
||||||
|
border-top-color: var(--accent);
|
||||||
|
}
|
||||||
|
.tippy-box[data-theme~="torrent-parts"][data-placement^="bottom"] > .tippy-arrow:before {
|
||||||
|
border-bottom-color: var(--accent);
|
||||||
|
}
|
||||||
|
.tippy-box[data-theme~="torrent-parts"][data-placement^="left"] > .tippy-arrow:before {
|
||||||
|
border-left-color: var(--accent);
|
||||||
|
}
|
||||||
|
.tippy-box[data-theme~="torrent-parts"][data-placement^="right"] > .tippy-arrow:before {
|
||||||
|
border-right-color: var(--accent);
|
||||||
|
}
|
||||||
|
.tippy-box[data-theme~="torrent-parts"] > .tippy-backdrop {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
.tippy-box[data-theme~="torrent-parts"] > .tippy-svg-arrow {
|
||||||
|
fill: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix Notyf dismiss button */
|
||||||
|
.notyf__dismiss-btn{border-radius: 0;}
|
||||||
|
|
||||||
|
/*! Alata | OFL-1.1 License | github.com/SorkinType/Alata */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Alata';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/ext/alata-v9-latin-regular.woff2') format('woff2'),
|
||||||
|
url('/ext/alata-v9-latin-regular.ttf') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* begin custom stuff */
|
||||||
|
|
||||||
.sr-only {
|
.sr-only {
|
||||||
border: 0;
|
border: 0;
|
||||||
clip: rect(0 0 0 0);
|
clip: rect(0 0 0 0);
|
||||||
@@ -21,32 +62,18 @@ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:b
|
|||||||
--grey: #BDBDBD;
|
--grey: #BDBDBD;
|
||||||
--white: #FFF;
|
--white: #FFF;
|
||||||
background: #24384D;
|
background: #24384D;
|
||||||
overflow-y: scroll;
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root:-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
background: var(--gradient);
|
background: var(--gradient);
|
||||||
font-family: 'Alata', sans-serif;
|
font-family: 'Alata', Seravek, 'Gill Sans Nova', Ubuntu, Calibri, 'DejaVu Sans', source-sans-pro, sans-serif;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
overflow-y: scroll;
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body:-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
background: var(--dark-blue);
|
background: var(--dark-blue);
|
||||||
@@ -214,7 +241,7 @@ label[for="torrent"] {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#share > div > button {
|
#share > * > button {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -228,12 +255,29 @@ label[for="torrent"] {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.property:first-child {
|
.property:first-child {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.property > legend {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 0;
|
||||||
|
width: 280px;
|
||||||
|
text-align: right;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.labels {
|
.labels {
|
||||||
width: 280px;
|
width: 280px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@@ -242,7 +286,14 @@ label[for="torrent"] {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.labels > a {
|
.label-actions {
|
||||||
|
margin-top: 26px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labels > a, .label-actions > a {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
@@ -279,10 +330,35 @@ input {
|
|||||||
|
|
||||||
#announce > *, #urlList > * {
|
#announce > *, #urlList > * {
|
||||||
margin: 0 0 16px 0;
|
margin: 0 0 16px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove {
|
.remove {
|
||||||
margin-left: 6px;
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline;
|
||||||
|
color: var(--grey);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
color: rgb(120, 126, 133) !important;
|
||||||
|
-webkit-text-fill-color: rgb(120, 126, 133) !important;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
padding: 8px 0;
|
||||||
|
font-style: normal;
|
||||||
|
display: block;
|
||||||
|
width: 440px;
|
||||||
|
height: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1080px) {
|
@media (max-width: 1080px) {
|
||||||
@@ -330,8 +406,12 @@ input {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.property > label {
|
.property > legend {
|
||||||
margin: 12px 0;
|
position: static;
|
||||||
|
left: auto;
|
||||||
|
width: auto;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, button {
|
input, button {
|
||||||
@@ -344,6 +424,13 @@ input {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.labels, .content, #files {
|
.labels, .content, #files {
|
||||||
width: auto;
|
width: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -351,8 +438,28 @@ input {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
#announce, #urlList {
|
#announce, #urlList {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#announce > *, #urlList > * {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 80vw;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#announce > * > input, #urlList > * > input {
|
||||||
|
width: calc(80vw - 40px);
|
||||||
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#announce > * > *, #urlList > * > *, #addTracker, #addWebseed, #examples > button {
|
#announce > * > *, #urlList > * > *, #addTracker, #addWebseed, #examples > button {
|
||||||
@@ -372,37 +479,3 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Tippy 6.2.7 | MIT License | github.com/atomiks/tippyjs */
|
|
||||||
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
|
||||||
|
|
||||||
/* Tippy shift-away-subtle theme */
|
|
||||||
.tippy-box[data-animation=shift-away-subtle][data-state=hidden]{opacity:0}.tippy-box[data-animation=shift-away-subtle][data-state=hidden][data-placement^=top]{transform:translateY(5px)}.tippy-box[data-animation=shift-away-subtle][data-state=hidden][data-placement^=bottom]{transform:translateY(-5px)}.tippy-box[data-animation=shift-away-subtle][data-state=hidden][data-placement^=left]{transform:translateX(5px)}.tippy-box[data-animation=shift-away-subtle][data-state=hidden][data-placement^=right]{transform:translateX(-5px)}
|
|
||||||
|
|
||||||
/* Custom Tippy Theme */
|
|
||||||
.tippy-box[data-theme~="torrent-parts"] {
|
|
||||||
background-color: var(--accent);
|
|
||||||
font-weight: 600;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.tippy-box[data-theme~="torrent-parts"][data-placement^="top"] > .tippy-arrow:before {
|
|
||||||
border-top-color: var(--accent);
|
|
||||||
}
|
|
||||||
.tippy-box[data-theme~="torrent-parts"][data-placement^="bottom"] > .tippy-arrow:before {
|
|
||||||
border-bottom-color: var(--accent);
|
|
||||||
}
|
|
||||||
.tippy-box[data-theme~="torrent-parts"][data-placement^="left"] > .tippy-arrow:before {
|
|
||||||
border-left-color: var(--accent);
|
|
||||||
}
|
|
||||||
.tippy-box[data-theme~="torrent-parts"][data-placement^="right"] > .tippy-arrow:before {
|
|
||||||
border-right-color: var(--accent);
|
|
||||||
}
|
|
||||||
.tippy-box[data-theme~="torrent-parts"] > .tippy-backdrop {
|
|
||||||
background-color: var(--accent);
|
|
||||||
}
|
|
||||||
.tippy-box[data-theme~="torrent-parts"] > .tippy-svg-arrow {
|
|
||||||
fill: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Fix Notyf dismiss button */
|
|
||||||
.notyf__dismiss-btn{border-radius: 0;}
|
|
||||||
38
src/sw.js
Normal file
38
src/sw.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const CACHE_NAME = 'torrent-parts-v2';
|
||||||
|
const ASSETS = [
|
||||||
|
'/',
|
||||||
|
'/index.html',
|
||||||
|
'/src/parse.js',
|
||||||
|
'/src/style.css',
|
||||||
|
'/ext/alata-v9-latin-regular.woff2',
|
||||||
|
'/ext/fa.min.js',
|
||||||
|
'/ext/jj2008-06-14.mk4_archive.torrent',
|
||||||
|
'/favicon.ico',
|
||||||
|
'/manifest.webmanifest',
|
||||||
|
];
|
||||||
|
|
||||||
|
self.addEventListener('install', (e) => {
|
||||||
|
self.skipWaiting();
|
||||||
|
e.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS)));
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('activate', (e) => {
|
||||||
|
e.waitUntil(
|
||||||
|
Promise.all([
|
||||||
|
self.clients.claim(),
|
||||||
|
caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (e) => {
|
||||||
|
if (e.request.method !== 'GET') return;
|
||||||
|
|
||||||
|
const url = new URL(e.request.url);
|
||||||
|
|
||||||
|
// Only cache same-origin requests
|
||||||
|
if (url.origin !== self.location.origin) return;
|
||||||
|
|
||||||
|
// Cache-first for local assets
|
||||||
|
e.respondWith(caches.match(e.request).then((cached) => cached || fetch(e.request)));
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user