OctoSpaccHub/public/HashyMagnet/index.html

358 lines
9.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>🧲 HashyMagnet</title>
<meta name="description" content="Generate full BitTorrent Magnet Links from Info Hashes!"/>
<meta property="og:title" content="🧲 HashyMagnet"/>
<meta property="og:description" content="Generate full BitTorrent Magnet Links from Info Hashes!"/>
<meta property="og:url" content="https://hub.octt.eu.org/HashyMagnet/"/>
<link rel="canonical" href="https://hub.octt.eu.org/HashyMagnet/"/>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link href="Bubbles.css" rel="stylesheet"/>
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png"/>
<link rel="manifest" href="./manifest.json"/>
<script src="../../shared/OctoHub-Global.js"></script>
<style>
Body {
Color: #FFFFFF;
Background-Color: #254070;
Margin: 0px;
Padding: 16px;
Overflow: Hidden;
Text-Align: Center;
Font-Family: Sans-Serif;
}
Body:Not(Input, TextArea) {
User-Select: None;
}
A {
Color: #17C3EA;
}
A:Active {
Color: #0B69BC;
}
A:Hover {
Background: #101060;
Color: #E9FFEE;
}
NoScript, NoScript P, .NoScript {
Font-Size: XX-Large;
}
Input {
Height: 2em;
}
Button {
Height: 2.25em;
}
Input, Button {
Font-Size: Initial;
}
Input, TextArea {
Width: Calc(90% - 8px);
Margin-Top: 8px;
Margin-Bottom: 8px;
}
Input:Disabled, TextArea:Disabled {
Color: #000000;
Background-Color: #EEEEEE;
}
Input, TextArea, Button {
Color: #000000;
Background-Color: #EEFFFF;
Border: Solid #808080 1px;
Border-Radius: 4px;
}
Button {
Color: #202020;
Padding-Left: 0.5em;
Padding-Right: 0.5em;
}
Button:Hover {
Color: #101010;
}
Button:Active {
Color: #000000;
Background-Color: #DDEEEE;
}
Button:Disabled {
Color: #808080;
Background-Color: RGBA(16,16,48,0.5);
}
.Section { Margin: 4px; }
.Smaller { Font-Size: Smaller; }
.NoWrap { White-Space: NoWrap; }
.LeftAlign {
Text-Align: Left;
Margin-Left: Calc(5% + 8px);
}
.ClickPointer, #TitleTitle { Cursor: Pointer; }
#Main {
Overflow-X: Hidden;
/*Overflow-Y: Scroll;*/
Max-Height: 100vh;
Position: Absolute;
Top: 50%;
Transform: TranslateY(-50%);
Left: 0px;
Right: 0px;
Z-Index: 4;
}
#LicenseText {
Overflow-Y: Auto;
}
#HomeBtn {
Position: Absolute;
Top: 0px;
Left: 0px;
Margin: 0px;
Padding: 8px;
}
#HomeBtn A {
Color: #19D5FF;
}
#ResetBtn {
Color: #FF0000;
Padding-Bottom: 4px;
Width: 2em;
}
#ResetBtn:Active, #ResetBtn:Hover {
Color: #FFFFFF;
Background-Color: #FF0000;
}
.ocean { Overflow: Hidden; }
</style>
</head>
<body>
<div id="Main">
<h5 id="HomeBtn">[<a href="..">🔼 Home</a>]</h5>
<div id="Title" class="Section"><div>
<div id="TitleTitle" title="Click for more info!">
<h3>HashyMagnet</h3>
<p>Generate a full Bit<span style="Color:#AAFFFF;">Torrent</span> Magnet Link from an Info Hash...</p>
</div>
<noscript><p class="NoScript">
This is an actual app, not a badly-made website.
<br/>
It needs JavaScript to work, so you need to enable it.
<br/>
The code is fully open, and you can review it with "View Page Source".
</p></noscript>
<div id="Info">
<h4>Info</h4>
<p>With this app you can generate full <a href="https://en.m.wikipedia.org/wiki/Magnet_URI_scheme" target="_blank" rel="noopener nofollow">BitTorrent Magnet links</a>, complete with trackers, from a simple Info Hash.</p>
<p>You can input your own list of trackers, or you can use the one that's autogenerated by the app from up-to-date lists.</p>
<p class="Smaller ClickPointer" onclick="alert('{{ Changelog }}\n\n' + Changelog);">Last updated on <span id="LastUpdatedDate"></span>.</p>
<h4>Special Thanks and Credits</h4>
<p>
Tracker lists providers:
<ul id="ListsProviders">
<li><a href="https://github.com/ngosang/trackerslist" target="_blank" rel="noopener nofollow">https://github.com/ngosang/trackerslist</a></li>
<li><a href="https://newtrackon.com" target="_blank" rel="noopener nofollow">https://newtrackon.com</a></li>
</ul>
</p>
<p>
Background animations:
<br/>
<a href="https://codinhood.com/micro/animate-octocat-sprite-css" target="_blank" rel="noopener nofollow">https://codinhood.com/micro/animate-octocat-sprite-css</a></p>
<h4>License</h4>
<pre id="LicenseText">HashyMagnet
Copyright (C) 2022-2023, <a href="https://hub.octt.eu.org">OctoSpacc</a>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <a href="https://www.gnu.org/licenses" target="_blank" rel="noopener nofollow">https://www.gnu.org/licenses</a>.
</pre>
<br/>
</div>
</div></div>
<br/>
<div class="Section">
<div class="NoWrap">
<button id="ResetBtn"><big><b>x</b></big></button>
<input id="TextBox" placeholder="📝 Paste Hash here...">
</div>
<div class="NoWrap">
<button id="GenerateBtn">🧲 Generate Magnet!</button>
<button id="CopyBtn">📋 Copy</button>
<button id="OpenBtn">🔗 Open</button>
</div>
</div>
<br/>
<div class="Section">
<div class="NoWrap LeftAlign">
<label for="TrackersBtn">Trackers list:</label>
<button id="TrackersBtn"></button>
</div>
<textarea id="TrackersArea" placeholder="📜 Paste Trackers here..." rows="8" cols="60"></textarea>
</div>
<br/>
</div>
<div class="ocean"><div class="bubble bubble-1"></div><div class="bubble bubble-2"></div><div class="bubble bubble-3"></div><div class="bubble bubble-4"></div><div class="bubble bubble-5"></div><div class="bubble bubble-6"></div><div class="bubble bubble-7"></div><div class="bubble bubble-8"></div><div class="bubble bubble-9"></div><div class="bubble bubble-10"></div><div class="bubble bubble-11"></div></div>
<script>
// https://stackoverflow.com/a/63958411
String.prototype.replaceAllTxt = function replaceAll(search, replace) {
return this.split(search).join(replace);
};
var OnInputChangePaste = ['onchange', 'oninput', 'onpaste'];
var Generated = false;
var Trackers = [],
FetchTrackers = [];
var MaxFetchTrackers = 40;
var TrackersSources = [
"https://ngosang.github.io/trackerslist/trackers_best.txt",
"https://newtrackon.com/api/stable",
];
function SplitLines(Text) {
if (Text == "") {
return [];
} else {
return Text.trim().replaceAllTxt('\r', '').replace(/\n\s*\n/g, '\n').split('\n');
};
}
async function DoFetchTrackers() {
for (var i = 0; i < TrackersSources.length; i++) {
var Req = await fetch(TrackersSources[i]);
var Data = await Req.text();
FetchTrackers = FetchTrackers.concat(SplitLines(Data));
}
FetchTrackers = FetchTrackers.slice(0,MaxFetchTrackers);
}
function WriteFetchTrackers() {
for (var i = 0; i < FetchTrackers.length; i++) {
TrackersArea.value += FetchTrackers[i] + '\n\n';
}
StoreTrackers();
DoRedraw();
}
function StoreTrackers() {
Trackers = SplitLines(TrackersArea.value);
}
async function InitialFill() {
await DoFetchTrackers();
WriteFetchTrackers();
if (new URLSearchParams(window.location.hash).get('#Hash')) {
GenerateBtn.onclick();
};
}
GenerateBtn.onclick = function() {
['magnet:', '?xt=urn:', 'btih:'].forEach(function(i) {
if (TextBox.value.toLowerCase().startsWith(i)) {
TextBox.value = TextBox.value.substring(i.length);
};
});
TextBox.value = 'magnet:?xt=urn:btih:' + TextBox.value;
for (var i = 0; i < Trackers.length; i++) {
TextBox.value += '&tr=' + encodeURIComponent(Trackers[i]);
};
Generated = true;
// Do it two times because of a strange bug with the ResetBtn
DoRedraw(); DoRedraw();
};
function DoRedraw() {
TextBox.style = '';
var TextBoxWidth = TextBox.offsetWidth;
if (Generated) {
TextBox.disabled = GenerateBtn.disabled = GenerateBtn.hidden = true;
TextBox.style = 'Width:' + (TextBoxWidth - ResetBtn.offsetWidth - 12) + 'px;'
ResetBtn.hidden = CopyBtn.hidden = OpenBtn.hidden = false;
} else {
TextBox.disabled = GenerateBtn.disabled = GenerateBtn.hidden = false;
ResetBtn.hidden = CopyBtn.hidden = OpenBtn.hidden = true;
};
GenerateBtn.disabled = !TextBox.value;
TrackersBtn.textContent = (TrackersArea.value ? '🗑️ Clear' : '🔄 Restore');
}
ResetBtn.onclick = function() {
TextBox.value = '';
Generated = false;
DoRedraw();
};
CopyBtn.onclick = function() {
navigator.clipboard.writeText(TextBox.value);
};
OpenBtn.onclick = function() {
window.open(TextBox.value);
};
TrackersBtn.onclick = function() {
if (TrackersArea.value) {
TrackersArea.value = '';
StoreTrackers();
} else {
WriteFetchTrackers();
};
DoRedraw();
};
OnInputChangePaste.forEach(function(i) {
TrackersArea[i] = function TrackersAreaOnChange() {
StoreTrackers();
DoRedraw();
};
});
TextBox.onkeydown = function(e) {
if (e.keyCode == 13 && !Generated) {
GenerateBtn.click();
};
};
OnInputChangePaste.forEach(function(i) {
TextBox[i] = DoRedraw;
});
window.onresize = DoRedraw;
Info.hidden = true;
TitleTitle.onclick = function() {
Info.hidden = !Info.hidden;
};
TextBox.value = new URLSearchParams(window.location.hash).get('#Hash');
DoRedraw();
InitialFill();
var Changelog = `\
[ 2023-04-21 ]
- Various UX fixes
[ 2023-01-18 ]
- Fix handling of torrent hash input
- Improve support for older browsers
- Improve minor UI/UX aspects
`;
LastUpdatedDate.innerHTML = Changelog.split('\n')[0].split('[')[1].split(']')[0].trim();
</script>
</body>
</html>