mirror of
https://github.com/leoherzog/TorrentParts.git
synced 2026-01-24 04:08:04 -08:00
Compare commits
7 Commits
c4af414e16
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a086867ff7 | ||
|
|
74d74f8432 | ||
|
|
570df30bcc | ||
|
|
615fe5092c | ||
|
|
dc93c4722e | ||
|
|
b2496a7719 | ||
|
|
5791f8f749 |
135
index.html
135
index.html
@@ -54,34 +54,36 @@
|
|||||||
<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>
|
<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>
|
||||||
|
|
||||||
<div id="startButtons">
|
<form id="startForm">
|
||||||
|
<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" />
|
||||||
<label for="magnet" class="sr-only">
|
<label for="magnet" class="sr-only">
|
||||||
Enter URL and press enter
|
Enter URL and press enter
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input id="torrent" type="file" accept=".torrent" 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">
|
<a id="openURLWrapper" target="_blank">
|
||||||
<button id="openURL" aria-label="Open this Magnet URL in your Torrent client">
|
<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>
|
<span class="fas fa-arrow-up-right-from-square fa-2x"></span>
|
||||||
@@ -102,37 +104,46 @@
|
|||||||
<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" dir="auto" />
|
<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>
|
||||||
@@ -141,28 +152,33 @@
|
|||||||
<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" dir="auto" />
|
<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">
|
||||||
|
<legend>
|
||||||
|
<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>
|
||||||
|
Tracker URLs
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div class="label-actions">
|
||||||
<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>
|
<a id="addTrackers">Add Known Working Trackers</a>
|
||||||
<label for="announce">Tracker URLs</label>
|
<a id="removeTrackers">Remove All</a>
|
||||||
</div>
|
</div>
|
||||||
<a id="addTrackers">Add Known Working Trackers</a>
|
|
||||||
<a id="removeTrackers">Remove All</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button id="addTracker" data-type="announce">
|
<button id="addTracker" data-type="announce">
|
||||||
@@ -170,15 +186,17 @@
|
|||||||
</button>
|
</button>
|
||||||
<div id="announce"></div>
|
<div id="announce"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<fieldset class="property">
|
||||||
|
<legend>
|
||||||
|
<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>
|
||||||
|
Webseed URLs
|
||||||
|
</legend>
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<div>
|
<div class="label-actions">
|
||||||
<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>
|
<a id="removeWebseeds">Remove All</a>
|
||||||
<label for="urlList">Webseed URLs</label>
|
|
||||||
</div>
|
</div>
|
||||||
<a id="removeWebseeds">Remove All</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button id="addWebseed" data-type="urlList">
|
<button id="addWebseed" data-type="urlList">
|
||||||
@@ -186,37 +204,42 @@
|
|||||||
</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="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>
|
<label for="pieces" class="sr-only">Pieces</label>
|
||||||
<label for="pieces">Pieces</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<input id="pieces" type="text" placeholder="Not included in the URL/File provided" aria-label="Piece size and length" disabled required />
|
<input id="pieces" type="text" placeholder="Not included in the URL/File provided" aria-label="Piece size and length" disabled required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div class="property">
|
<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="labels">
|
||||||
<div>
|
<div class="label-actions">
|
||||||
<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>
|
<a id="getFiles">Fetch Files List from WebTorrent</a>
|
||||||
<label for="files">Files</label>
|
|
||||||
</div>
|
</div>
|
||||||
<a id="getFiles">Fetch Files List from WebTorrent</a>
|
|
||||||
</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" rel="noopener">v2.0.1</a>
|
<a href="https://github.com/leoherzog/TorrentParts/releases" target="_blank" rel="noopener">v2.0.2</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script type="module" src="/src/parse.js"></script>
|
<script type="module" src="/src/parse.js"></script>
|
||||||
|
|||||||
62
src/parse.js
62
src/parse.js
@@ -85,6 +85,11 @@ placeDownloadTooltips();
|
|||||||
document.addEventListener('DOMContentLoaded', start);
|
document.addEventListener('DOMContentLoaded', start);
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
|
// form submission prevention
|
||||||
|
document.getElementById('startForm').addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
// magnet input
|
// magnet input
|
||||||
document.getElementById('magnet').addEventListener('keyup', function (event) {
|
document.getElementById('magnet').addEventListener('keyup', function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -114,15 +119,16 @@ function start() {
|
|||||||
|
|
||||||
document.addEventListener('drop', function (event) {
|
document.addEventListener('drop', function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.dataTransfer.items[0]
|
if (event.dataTransfer.items.length === 0) return;
|
||||||
.getAsFile()
|
if (event.dataTransfer.items[0].kind !== 'file') return;
|
||||||
.arrayBuffer()
|
const file = event.dataTransfer.items[0].getAsFile();
|
||||||
.then(function (arrayBuffer) {
|
if (!file) return;
|
||||||
source = 'torrent-file';
|
file.arrayBuffer().then(function (arrayBuffer) {
|
||||||
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
source = 'torrent-file';
|
||||||
sourceTooltip.setContent('Currently loaded information sourced from Torrent file');
|
originalSourceIcon.innerHTML = '<span class="fad fa-file-alt fa-fw"></span>';
|
||||||
parse(Buffer.from(arrayBuffer));
|
sourceTooltip.setContent('Currently loaded information sourced from Torrent file');
|
||||||
});
|
parse(Buffer.from(arrayBuffer));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// example buttons
|
// example buttons
|
||||||
@@ -278,10 +284,12 @@ function display() {
|
|||||||
tracker.setAttribute('aria-label', 'Tracker URL #' + i);
|
tracker.setAttribute('aria-label', 'Tracker URL #' + i);
|
||||||
tracker.addEventListener('input', propertyChange);
|
tracker.addEventListener('input', propertyChange);
|
||||||
row.appendChild(tracker);
|
row.appendChild(tracker);
|
||||||
let remove = document.createElement('a');
|
let remove = document.createElement('button');
|
||||||
|
remove.type = 'button';
|
||||||
remove.className = 'remove';
|
remove.className = 'remove';
|
||||||
remove.dataset.index = i;
|
remove.dataset.index = i;
|
||||||
remove.innerHTML = '<span class="far fa-trash"></span>';
|
remove.innerHTML = '<span class="far fa-trash"></span>';
|
||||||
|
remove.setAttribute('aria-label', 'Remove tracker #' + i);
|
||||||
remove.addEventListener('click', removeRow);
|
remove.addEventListener('click', removeRow);
|
||||||
row.appendChild(remove);
|
row.appendChild(remove);
|
||||||
announce.appendChild(row);
|
announce.appendChild(row);
|
||||||
@@ -304,10 +312,12 @@ function display() {
|
|||||||
webseed.setAttribute('aria-label', 'Webseed URL #' + i);
|
webseed.setAttribute('aria-label', 'Webseed URL #' + i);
|
||||||
webseed.addEventListener('input', propertyChange);
|
webseed.addEventListener('input', propertyChange);
|
||||||
row.appendChild(webseed);
|
row.appendChild(webseed);
|
||||||
let remove = document.createElement('a');
|
let remove = document.createElement('button');
|
||||||
|
remove.type = 'button';
|
||||||
remove.className = 'remove';
|
remove.className = 'remove';
|
||||||
remove.dataset.index = i;
|
remove.dataset.index = i;
|
||||||
remove.innerHTML = '<span class="far fa-trash"></span>';
|
remove.innerHTML = '<span class="far fa-trash"></span>';
|
||||||
|
remove.setAttribute('aria-label', 'Remove webseed #' + i);
|
||||||
remove.addEventListener('click', removeRow);
|
remove.addEventListener('click', removeRow);
|
||||||
row.appendChild(remove);
|
row.appendChild(remove);
|
||||||
urlList.appendChild(row);
|
urlList.appendChild(row);
|
||||||
@@ -339,10 +349,10 @@ function display() {
|
|||||||
} else {
|
} else {
|
||||||
if (client.torrents.length > 0) {
|
if (client.torrents.length > 0) {
|
||||||
getFiles.style.display = 'none';
|
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>';
|
files.innerHTML = '<output>Attempting fetching of files from Webtorrent...</output>';
|
||||||
} else {
|
} else {
|
||||||
getFiles.style.display = 'block';
|
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>';
|
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.');
|
downloadTorrentTooltip.setContent('Files metadata is required to generate a Torrent file. Try fetching files list from WebTorrent.');
|
||||||
downloadTorrent.removeEventListener('click', saveTorrent);
|
downloadTorrent.removeEventListener('click', saveTorrent);
|
||||||
@@ -372,7 +382,7 @@ function createFileRow(icon, name, size) {
|
|||||||
if (icon) iconcell.innerHTML = '<span class="far fa-' + icon + '"></span>';
|
if (icon) iconcell.innerHTML = '<span class="far fa-' + icon + '"></span>';
|
||||||
row.appendChild(iconcell);
|
row.appendChild(iconcell);
|
||||||
let namecell = document.createElement('td');
|
let namecell = document.createElement('td');
|
||||||
namecell.innerHTML = name;
|
namecell.textContent = name;
|
||||||
row.appendChild(namecell);
|
row.appendChild(namecell);
|
||||||
let totalcell = document.createElement('td');
|
let totalcell = document.createElement('td');
|
||||||
totalcell.innerHTML = bytes.format(size, { decimalPlaces: 1, unitSeparator: ' ' });
|
totalcell.innerHTML = bytes.format(size, { decimalPlaces: 1, unitSeparator: ' ' });
|
||||||
@@ -458,7 +468,7 @@ async function addCurrentTrackers() {
|
|||||||
try {
|
try {
|
||||||
let response = await fetch('https://newtrackon.com/api/stable'); // get trackers with 95% uptime
|
let response = await fetch('https://newtrackon.com/api/stable'); // get trackers with 95% uptime
|
||||||
let trackers = await response.text();
|
let trackers = await response.text();
|
||||||
parsed.announce = parsed.announce.concat(trackers.split('\n\n'));
|
parsed.announce = (parsed.announce || []).concat(trackers.split('\n\n'));
|
||||||
parsed.announce.push('http://bt1.archive.org:6969/announce');
|
parsed.announce.push('http://bt1.archive.org:6969/announce');
|
||||||
parsed.announce.push('http://bt2.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
|
parsed.announce = parsed.announce.filter((v, i) => v && parsed.announce.indexOf(v) === i); // remove duplicates and empties
|
||||||
@@ -504,10 +514,12 @@ function updateModified() {
|
|||||||
function getFilesFromPeers() {
|
function getFilesFromPeers() {
|
||||||
console.info('Attempting fetching files from Webtorrent...');
|
console.info('Attempting fetching files from Webtorrent...');
|
||||||
getFiles.style.display = 'none';
|
getFiles.style.display = 'none';
|
||||||
parsed.announce.push('wss://tracker.webtorrent.io');
|
parsed.announce = parsed.announce || [];
|
||||||
|
parsed.announce.push('wss://tracker.webtorrent.dev');
|
||||||
parsed.announce.push('wss://tracker.openwebtorrent.com');
|
parsed.announce.push('wss://tracker.openwebtorrent.com');
|
||||||
parsed.announce.push('wss://tracker.btorrent.xyz');
|
parsed.announce.push('wss://tracker.btorrent.xyz');
|
||||||
parsed.announce.push('wss://tracker.fastcast.nz');
|
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
|
parsed.announce = parsed.announce.filter((v, i) => v && parsed.announce.indexOf(v) === i); // remove duplicates and empties
|
||||||
client.add(toMagnetURI(parsed), (torrent) => {
|
client.add(toMagnetURI(parsed), (torrent) => {
|
||||||
parsed.info = Object.assign({}, torrent.info); // clone object
|
parsed.info = Object.assign({}, torrent.info); // clone object
|
||||||
@@ -523,17 +535,13 @@ function getFilesFromPeers() {
|
|||||||
display();
|
display();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/a/36899900/2700296
|
|
||||||
function saveTorrent() {
|
function saveTorrent() {
|
||||||
let data = toTorrentFile(parsed);
|
const data = toTorrentFile(parsed);
|
||||||
if (data !== null && navigator.msSaveBlob) return navigator.msSaveBlob(new Blob([data], { type: 'application/x-bittorrent' }), parsed.name + '.torrent');
|
const blob = new Blob([data], { type: 'application/x-bittorrent' });
|
||||||
let a = document.createElement('a');
|
const url = URL.createObjectURL(blob);
|
||||||
a.style.display = 'none';
|
const a = document.createElement('a');
|
||||||
let url = window.URL.createObjectURL(new Blob([data], { type: 'application/x-bittorrent' }));
|
a.href = url;
|
||||||
a.setAttribute('href', url);
|
a.download = parsed.name + '.torrent';
|
||||||
a.setAttribute('download', parsed.name + '.torrent');
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
a.remove();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,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;
|
||||||
@@ -269,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;
|
||||||
}
|
}
|
||||||
@@ -306,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) {
|
||||||
@@ -357,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 {
|
||||||
@@ -371,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;
|
||||||
@@ -378,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 {
|
||||||
|
|||||||
98
src/sw.js
98
src/sw.js
@@ -1,84 +1,38 @@
|
|||||||
const cache_name = 'torrent-parts-v1';
|
const CACHE_NAME = 'torrent-parts-v2';
|
||||||
const assets = ['/', '/index.html', '/src/parse.js', '/src/style.css', '/ext/alata-v9-latin-regular.woff2', '/ext/alata-v9-latin-regular.ttf', '/ext/fa.min.js', '/ext/jj2008-06-14.mk4_archive.torrent', '/favicon.ico', '/manifest.webmanifest'];
|
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', function (event) {
|
self.addEventListener('install', (e) => {
|
||||||
self.skipWaiting(); // Force activate new SW immediately
|
self.skipWaiting();
|
||||||
event.waitUntil(
|
e.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS)));
|
||||||
caches
|
|
||||||
.open(cache_name)
|
|
||||||
.then(function (cache) {
|
|
||||||
return cache.addAll(assets);
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
console.error('Service worker install failed:', error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('activate', function (event) {
|
self.addEventListener('activate', (e) => {
|
||||||
event.waitUntil(
|
e.waitUntil(
|
||||||
Promise.all([
|
Promise.all([
|
||||||
// Take control of all clients immediately
|
|
||||||
self.clients.claim(),
|
self.clients.claim(),
|
||||||
// Clean up old caches
|
caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))),
|
||||||
caches.keys().then(function (cacheNames) {
|
|
||||||
return Promise.all(
|
|
||||||
cacheNames.map(function (cacheName) {
|
|
||||||
if (cacheName !== cache_name) {
|
|
||||||
console.log('Deleting old cache:', cacheName);
|
|
||||||
return caches.delete(cacheName);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('fetch', function (event) {
|
self.addEventListener('fetch', (e) => {
|
||||||
// Only cache GET requests
|
if (e.request.method !== 'GET') return;
|
||||||
if (event.request.method !== 'GET') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Network-first strategy for external requests
|
const url = new URL(e.request.url);
|
||||||
const requestUrl = new URL(event.request.url);
|
|
||||||
if (requestUrl.origin !== self.location.origin) {
|
|
||||||
event.respondWith(
|
|
||||||
fetch(event.request)
|
|
||||||
.then(function (response) {
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch(function () {
|
|
||||||
console.log('Network request failed, trying cache:', event.request.url);
|
|
||||||
return caches.match(event.request);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache-first strategy for app assets
|
// Only cache same-origin requests
|
||||||
event.respondWith(
|
if (url.origin !== self.location.origin) return;
|
||||||
caches
|
|
||||||
.match(event.request)
|
// Cache-first for local assets
|
||||||
.then(function (response) {
|
e.respondWith(caches.match(e.request).then((cached) => cached || fetch(e.request)));
|
||||||
if (response) {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
return fetch(event.request).then(function (response) {
|
|
||||||
// Cache successful responses for future use
|
|
||||||
if (response && response.status === 200) {
|
|
||||||
const responseClone = response.clone();
|
|
||||||
caches.open(cache_name).then(function (cache) {
|
|
||||||
cache.put(event.request, responseClone);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
console.error('Service worker fetch failed:', error);
|
|
||||||
// Could return offline fallback page here if needed
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user