mirror of
https://github.com/anasty17/mirror-leech-telegram-bot.git
synced 2025-01-08 12:07:33 +08:00
New file selection page
This commit is contained in:
parent
b58b633862
commit
b58f7404f1
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
mltbenv/*
|
@ -55,16 +55,14 @@ def create_help_buttons():
|
|||||||
|
|
||||||
def bt_selection_buttons(id_):
|
def bt_selection_buttons(id_):
|
||||||
gid = id_[:12] if len(id_) > 25 else id_
|
gid = id_[:12] if len(id_) > 25 else id_
|
||||||
pincode = "".join([n for n in id_ if n.isdigit()][:4])
|
pin = "".join([n for n in id_ if n.isdigit()][:4])
|
||||||
buttons = ButtonMaker()
|
buttons = ButtonMaker()
|
||||||
BASE_URL = config_dict["BASE_URL"]
|
BASE_URL = config_dict["BASE_URL"]
|
||||||
if config_dict["WEB_PINCODE"]:
|
if config_dict["WEB_PINCODE"]:
|
||||||
buttons.url_button("Select Files", f"{BASE_URL}/app/files/{id_}")
|
buttons.url_button("Select Files", f"{BASE_URL}/app/files?gid={id_}")
|
||||||
buttons.data_button("Pincode", f"sel pin {gid} {pincode}")
|
buttons.data_button("Pincode", f"sel pin {gid} {pin}")
|
||||||
else:
|
else:
|
||||||
buttons.url_button(
|
buttons.url_button("Select Files", f"{BASE_URL}/app/files?gid={id_}&pin={pin}")
|
||||||
"Select Files", f"{BASE_URL}/app/files/{id_}?pin_code={pincode}"
|
|
||||||
)
|
|
||||||
buttons.data_button("Done Selecting", f"sel done {gid} {id_}")
|
buttons.data_button("Done Selecting", f"sel done {gid} {id_}")
|
||||||
buttons.data_button("Cancel", f"sel cancel {gid}")
|
buttons.data_button("Cancel", f"sel cancel {gid}")
|
||||||
return buttons.build_menu(2)
|
return buttons.build_menu(2)
|
||||||
|
26
web/nodes.py
26
web/nodes.py
@ -118,7 +118,7 @@ def make_tree(res, tool=False):
|
|||||||
folders[-1],
|
folders[-1],
|
||||||
is_file=True,
|
is_file=True,
|
||||||
parent=previous_node,
|
parent=previous_node,
|
||||||
size=i["length"],
|
size=float(i["length"]),
|
||||||
priority=priority,
|
priority=priority,
|
||||||
file_id=i["index"],
|
file_id=i["index"],
|
||||||
progress=round(
|
progress=round(
|
||||||
@ -130,7 +130,7 @@ def make_tree(res, tool=False):
|
|||||||
folders[-1],
|
folders[-1],
|
||||||
is_file=True,
|
is_file=True,
|
||||||
parent=parent,
|
parent=parent,
|
||||||
size=i["length"],
|
size=float(i["length"]),
|
||||||
priority=priority,
|
priority=priority,
|
||||||
file_id=i["index"],
|
file_id=i["index"],
|
||||||
progress=round(
|
progress=round(
|
||||||
@ -155,14 +155,14 @@ def create_list(parent, contents=None):
|
|||||||
contents = []
|
contents = []
|
||||||
for i in parent.children:
|
for i in parent.children:
|
||||||
if i.is_folder:
|
if i.is_folder:
|
||||||
childrens = []
|
children = []
|
||||||
create_list(i, childrens)
|
create_list(i, children)
|
||||||
contents.append(
|
contents.append(
|
||||||
{
|
{
|
||||||
"id": f"folderNode_{i.file_id}",
|
"id": f"folderNode_{i.file_id}",
|
||||||
"name": i.name,
|
"name": i.name,
|
||||||
"type": "folder",
|
"type": "folder",
|
||||||
"children": childrens,
|
"children": children,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -177,3 +177,19 @@ def create_list(parent, contents=None):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
|
|
||||||
|
def extract_file_ids(data):
|
||||||
|
selected_files = []
|
||||||
|
unselected_files = []
|
||||||
|
for item in data:
|
||||||
|
if item.get("type") == "file":
|
||||||
|
if item.get("selected"):
|
||||||
|
selected_files.append(str(item["id"]))
|
||||||
|
else:
|
||||||
|
unselected_files.append(str(item["id"]))
|
||||||
|
if item.get("children"):
|
||||||
|
child_selected, child_unselected = extract_file_ids(item["children"])
|
||||||
|
selected_files.extend(child_selected)
|
||||||
|
unselected_files.extend(child_unselected)
|
||||||
|
return selected_files, unselected_files
|
||||||
|
834
web/templates/page.html
Normal file
834
web/templates/page.html
Normal file
@ -0,0 +1,834 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Torrent Selector</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
transition: background-color 0.5s, color 0.5s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
background-color: hsl(224 71% 4%);
|
||||||
|
color: hsl(213 31% 91%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
background-color: hsl(0 0% 100%);
|
||||||
|
color: hsl(224 71% 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: hsl(224 71% 4%);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .card {
|
||||||
|
background-color: hsl(224 71% 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .card {
|
||||||
|
background-color: hsl(0 0% 100%);
|
||||||
|
color: hsl(224 71% 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: hsl(217.2 91.2% 59.8%);
|
||||||
|
color: hsl(0 0% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: hsl(215 20.2% 65.1%);
|
||||||
|
color: hsl(0 0% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: hsl(215 20.2% 65.1%);
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked+.slider {
|
||||||
|
background-color: hsl(217.2 91.2% 59.8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked+.slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-tree-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid hsla(0, 0%, 100%, 0.1);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-tree-item:hover {
|
||||||
|
background-color: hsla(0, 0%, 100%, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .file-tree-item:hover {
|
||||||
|
background-color: hsla(0, 0%, 0%, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: hsl(0 0% 100%);
|
||||||
|
color: hsl(224 71% 4%);
|
||||||
|
margin: 15% auto;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .modal-content {
|
||||||
|
background-color: hsl(224 71% 4%);
|
||||||
|
color: hsl(213 31% 91%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes day-night {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-night-animation {
|
||||||
|
animation: day-night 1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-name-input {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid hsl(217.2 91.2% 59.8%);
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin-right: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-name-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-bottom: 2px solid hsl(217.2 91.2% 59.8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pin-entry {
|
||||||
|
animation: fadeIn 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pin-entry.fadeOut {
|
||||||
|
animation: fadeOut 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeOut {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-info {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: hsl(215 20.2% 65.1%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .size-info {
|
||||||
|
color: hsl(213 31% 80%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light input[type="text"],
|
||||||
|
.light input[type="password"] {
|
||||||
|
background-color: hsl(0 0% 95%);
|
||||||
|
color: hsl(224 71% 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 2rem 0;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 2rem 0;
|
||||||
|
box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.3);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-buttons button {
|
||||||
|
padding: 2px 4px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.footer-content {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-buttons {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-tree-item .file-name,
|
||||||
|
.file-tree-item .folder-name {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pinEntry {
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pinEntry input[type="password"] {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pinEntry button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-btn:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="dark">
|
||||||
|
<header>
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<div class="flex flex-col sm:flex-row justify-between items-center">
|
||||||
|
<h1 class="text-3xl font-bold text-white mb-4 sm:mb-0">Torrent
|
||||||
|
file selector</h1>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span id="themeIcon" class="mr-2 text-white">🌙</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="themeToggle" checked>
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="flex-grow container mx-auto p-4">
|
||||||
|
<div id="pinEntry" class="card p-6 mb-4 pin-entry">
|
||||||
|
<h2 class="text-xl font-bold mb-4">Enter PIN</h2>
|
||||||
|
<input type="text" id="pinInput"
|
||||||
|
title="Enter the code that you have got from Telegram to access the Torrent"
|
||||||
|
class="bg-gray-700 text-white p-2 rounded mb-4 w-full" placeholder="Enter PIN">
|
||||||
|
<button id="submitPin" class="btn btn-primary">Submit</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="fileManager" class="card p-6 hidden">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<button id="selectAllBtn" class="btn btn-primary">Select
|
||||||
|
All</button>
|
||||||
|
<button id="submitBtn" class="btn btn-primary">Submit</button>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<p>Selected files: <span id="selectedCount">0</span> / <span id="totalCount">0</span></p>
|
||||||
|
<p>Total size: <span id="selectedSize">0 B</span> / <span id="totalSize">0 B</span></p>
|
||||||
|
</div>
|
||||||
|
<div id="fileTree" class="mb-4"></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<div class="footer-content flex flex-col sm:flex-row justify-between items-center">
|
||||||
|
<p class="text-white mb-4 sm:mb-0">© 2024 Torrent file selector.
|
||||||
|
All rights reserved.</p>
|
||||||
|
<div class="footer-buttons flex space-x-4">
|
||||||
|
<a href="https://github.com/anasty17/mirror-leech-telegram-bot" target="_blank"
|
||||||
|
class="social-button">
|
||||||
|
<i class="fab fa-github"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.instagram.com" target="_blank" class="social-button">
|
||||||
|
<i class="fab fa-instagram"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<div id="reusableModal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h2 id="modalTitle" class="text-xl font-bold mb-4"></h2>
|
||||||
|
<div id="modalBody"></div>
|
||||||
|
<div id="modalFooter" class="mt-4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const themeToggle = document.getElementById('themeToggle');
|
||||||
|
const themeIcon = document.getElementById('themeIcon');
|
||||||
|
const body = document.body;
|
||||||
|
const fileTree = document.getElementById('fileTree');
|
||||||
|
const selectedCount = document.getElementById('selectedCount');
|
||||||
|
const totalCount = document.getElementById('totalCount');
|
||||||
|
const selectedSize = document.getElementById('selectedSize');
|
||||||
|
const totalSize = document.getElementById('totalSize');
|
||||||
|
const selectAllBtn = document.getElementById('selectAllBtn');
|
||||||
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
const pinEntry = document.getElementById('pinEntry');
|
||||||
|
const pinInput = document.getElementById('pinInput');
|
||||||
|
const submitPin = document.getElementById('submitPin');
|
||||||
|
const fileManager = document.getElementById('fileManager');
|
||||||
|
const reusableModal = document.getElementById('reusableModal');
|
||||||
|
const modalTitle = document.getElementById('modalTitle');
|
||||||
|
const modalBody = document.getElementById('modalBody');
|
||||||
|
const modalFooter = document.getElementById('modalFooter');
|
||||||
|
pinInput.focus();
|
||||||
|
const urlParams = new Proxy(new URLSearchParams(window.location.search), {
|
||||||
|
get: (searchParams, prop) => searchParams.get(prop),
|
||||||
|
});
|
||||||
|
if (urlParams.pin) {
|
||||||
|
pinInput.value = urlParams.pin
|
||||||
|
setTimeout(() => submitPin.click(), 0);
|
||||||
|
}
|
||||||
|
let currentFolder = null;
|
||||||
|
let files = [];
|
||||||
|
let allowEdit = false;
|
||||||
|
|
||||||
|
function loadThemePreference() {
|
||||||
|
const savedTheme = localStorage.getItem('darkMode');
|
||||||
|
if (savedTheme !== null) {
|
||||||
|
const isDark = savedTheme === 'true';
|
||||||
|
body.classList.toggle('dark', isDark);
|
||||||
|
body.classList.toggle('light', !isDark);
|
||||||
|
themeToggle.checked = isDark;
|
||||||
|
themeIcon.textContent = isDark ? '☀️' : '🌙';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
body.classList.toggle('dark');
|
||||||
|
body.classList.toggle('light');
|
||||||
|
const isDark = body.classList.contains('dark');
|
||||||
|
themeIcon.textContent = isDark ? '☀️' : '🌙';
|
||||||
|
themeIcon.classList.add('day-night-animation');
|
||||||
|
setTimeout(() => themeIcon.classList.remove('day-night-animation'), 1000);
|
||||||
|
localStorage.setItem('darkMode', isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSize(size) {
|
||||||
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
let i = 0;
|
||||||
|
while (size >= 1024 && i < units.length - 1) {
|
||||||
|
size /= 1024;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return `${size.toFixed(2)} ${units[i]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateFolderSize(folder) {
|
||||||
|
let totalSize = 0;
|
||||||
|
const queue = [folder];
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const node = queue.pop();
|
||||||
|
if (node.type === 'file') {
|
||||||
|
totalSize += node.size;
|
||||||
|
} else if (node.children) {
|
||||||
|
queue.push(...node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFileTree(nodes) {
|
||||||
|
fileTree.innerHTML = '';
|
||||||
|
if (currentFolder) {
|
||||||
|
const backButton = document.createElement('div');
|
||||||
|
backButton.className = 'file-tree-item folder';
|
||||||
|
backButton.innerHTML = '<span class="icon">📁</span>...';
|
||||||
|
backButton.addEventListener('click', goBack);
|
||||||
|
fileTree.appendChild(backButton);
|
||||||
|
}
|
||||||
|
nodes.forEach(node => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'file-tree-item';
|
||||||
|
const checkboxWrapper = document.createElement('div');
|
||||||
|
checkboxWrapper.className = 'checkbox-wrapper';
|
||||||
|
|
||||||
|
const checkbox = document.createElement('input');
|
||||||
|
checkbox.type = 'checkbox';
|
||||||
|
checkbox.id = node.id;
|
||||||
|
checkbox.checked = node.selected;
|
||||||
|
checkbox.addEventListener('change', () => toggleFile(node));
|
||||||
|
|
||||||
|
checkboxWrapper.appendChild(checkbox);
|
||||||
|
|
||||||
|
const icon = document.createElement('span');
|
||||||
|
icon.className = 'icon';
|
||||||
|
icon.textContent = node.type === 'folder' ? '📁' : '📄';
|
||||||
|
|
||||||
|
const fileInfo = document.createElement('div');
|
||||||
|
fileInfo.className = 'file-info';
|
||||||
|
|
||||||
|
const nameElement = document.createElement('div');
|
||||||
|
nameElement.className = 'file-name cursor-pointer';
|
||||||
|
nameElement.textContent = node.name;
|
||||||
|
|
||||||
|
const sizeInfo = document.createElement('div');
|
||||||
|
sizeInfo.className = 'size-info';
|
||||||
|
const size = node.type === 'folder' ? calculateFolderSize(node) : node.size;
|
||||||
|
if (node.type === 'folder') {
|
||||||
|
sizeInfo.textContent = `${formatSize(size)}`;
|
||||||
|
} else {
|
||||||
|
sizeInfo.textContent = `${formatSize(size)}`;
|
||||||
|
if (allowEdit) {
|
||||||
|
const editBtn = document.createElement('span');
|
||||||
|
editBtn.textContent = ' | Edit ✏️';
|
||||||
|
editBtn.className = 'edit-btn';
|
||||||
|
sizeInfo.appendChild(editBtn);
|
||||||
|
}
|
||||||
|
if (node.progress !== undefined) {
|
||||||
|
const progressText = document.createElement('span');
|
||||||
|
progressText.textContent = ` | Progress: ${node.progress}%`;
|
||||||
|
sizeInfo.appendChild(progressText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.addEventListener('click', (event) => {
|
||||||
|
if (event.target.className === 'file-name cursor-pointer') {
|
||||||
|
toggleFile(node)
|
||||||
|
} else if (event.target.className === 'edit-btn') {
|
||||||
|
openEditFileNameModal(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fileInfo.appendChild(nameElement);
|
||||||
|
fileInfo.appendChild(sizeInfo);
|
||||||
|
|
||||||
|
div.appendChild(checkboxWrapper);
|
||||||
|
div.appendChild(icon);
|
||||||
|
div.appendChild(fileInfo);
|
||||||
|
|
||||||
|
if (node.type === 'folder') {
|
||||||
|
nameElement.classList.add('folder', 'folder-name');
|
||||||
|
nameElement.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
openFolder(node);
|
||||||
|
});
|
||||||
|
if (areAllChildrenSelected(node)) {
|
||||||
|
checkbox.checked = true;
|
||||||
|
} else if (areSomeChildrenSelected(node)) {
|
||||||
|
checkbox.indeterminate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileTree.appendChild(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function areAllChildrenSelected(folder) {
|
||||||
|
return folder.children.every(child => child.type === 'folder' ? areAllChildrenSelected(child) : child.selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function areSomeChildrenSelected(folder) {
|
||||||
|
return folder.children.some(child => child.type === 'folder' ? areSomeChildrenSelected(child) : child.selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFile(node) {
|
||||||
|
if (node.type === 'folder') {
|
||||||
|
const isSelected = !areAllChildrenSelected(node);
|
||||||
|
toggleFolder(node, isSelected);
|
||||||
|
} else {
|
||||||
|
node.selected = !node.selected;
|
||||||
|
}
|
||||||
|
updateParentFolders(node);
|
||||||
|
updateStats();
|
||||||
|
renderFileTree(currentFolder ? currentFolder.children : files);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFolder(folder, isSelected) {
|
||||||
|
folder.selected = isSelected;
|
||||||
|
const queue = [folder];
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const node = queue.pop();
|
||||||
|
if (node.type === 'file') {
|
||||||
|
node.selected = isSelected;
|
||||||
|
} else if (node.children) {
|
||||||
|
node.selected = isSelected;
|
||||||
|
queue.push(...node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateParentFolders(node) {
|
||||||
|
let parent = findParent(files[0], node.id);
|
||||||
|
while (parent) {
|
||||||
|
parent.selected = areAllChildrenSelected(parent);
|
||||||
|
parent = findParent(files[0], parent.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findParent(root, id) {
|
||||||
|
if (root.children) {
|
||||||
|
for (const child of root.children) {
|
||||||
|
if (child.id === id) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
const found = findParent(child, id);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStats() {
|
||||||
|
const stats = calculateStats(files);
|
||||||
|
selectedCount.textContent = stats.selectedCount;
|
||||||
|
totalCount.textContent = stats.totalCount;
|
||||||
|
selectedSize.textContent = formatSize(stats.selectedSize);
|
||||||
|
totalSize.textContent = formatSize(stats.totalSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateStats(nodes) {
|
||||||
|
let selectedCount = 0;
|
||||||
|
let totalCount = 0;
|
||||||
|
let selectedSize = 0;
|
||||||
|
let totalSize = 0;
|
||||||
|
|
||||||
|
const queue = [...nodes];
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const node = queue.pop();
|
||||||
|
if (node.type === 'file') {
|
||||||
|
totalCount++;
|
||||||
|
totalSize += node.size;
|
||||||
|
if (node.selected) {
|
||||||
|
selectedCount++;
|
||||||
|
selectedSize += node.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
queue.push(...node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { selectedCount, totalCount, selectedSize, totalSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFolder(folder) {
|
||||||
|
currentFolder = folder;
|
||||||
|
renderFileTree(folder.children);
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
if (currentFolder) {
|
||||||
|
const parent = findParent(files[0], currentFolder.id);
|
||||||
|
if (parent) {
|
||||||
|
currentFolder = parent;
|
||||||
|
renderFileTree(parent.children);
|
||||||
|
} else {
|
||||||
|
currentFolder = null;
|
||||||
|
renderFileTree(files);
|
||||||
|
}
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAll() {
|
||||||
|
const nodes = currentFolder ? currentFolder.children : files;
|
||||||
|
nodes.forEach(node => {
|
||||||
|
if (node.type === 'folder') {
|
||||||
|
toggleFolder(node, true);
|
||||||
|
} else {
|
||||||
|
node.selected = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateStats();
|
||||||
|
renderFileTree(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditFileNameModal(node) {
|
||||||
|
modalTitle.textContent = 'Edit File Name';
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<input type="text" id="editNameInput" class="edit-name-input" value="${node.name}">
|
||||||
|
`;
|
||||||
|
modalFooter.innerHTML = `
|
||||||
|
<button id="saveNameBtn" class="btn btn-primary">Save</button>
|
||||||
|
<button class="btn btn-secondary" onClick="closeModal()">Cancel</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const saveNameBtn = document.getElementById('saveNameBtn');
|
||||||
|
const editNameInput = document.getElementById('editNameInput');
|
||||||
|
|
||||||
|
saveNameBtn.addEventListener('click', () => {
|
||||||
|
const newName = editNameInput.value.trim();
|
||||||
|
if (newName && newName !== node.name) {
|
||||||
|
node.name = newName;
|
||||||
|
renderFileTree(currentFolder ? currentFolder.children : files);
|
||||||
|
}
|
||||||
|
closeModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
editNameInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
saveNameBtn.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openModal();
|
||||||
|
editNameInput.setSelectionRange(editNameInput.value.length, editNameInput.value.length)
|
||||||
|
editNameInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
reusableModal.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
reusableModal.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitData() {
|
||||||
|
if (parseInt(selectedCount.innerText) === 0) {
|
||||||
|
modalTitle.textContent = 'Error';
|
||||||
|
modalBody.innerHTML = '<p>No files selected.</p>';
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onclick="closeModal()">Okay</button>';
|
||||||
|
openModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalTitle.textContent = 'Processing...';
|
||||||
|
modalBody.innerHTML = `<p>Submitting your selection ${selectedCount.innerText} ... </p>`;
|
||||||
|
modalFooter.innerHTML = '';
|
||||||
|
openModal();
|
||||||
|
const requestUrl = `/app/files/torrent?gid=${urlParams.gid}&pin=${pinInput.value}`;
|
||||||
|
fetch(requestUrl, { 'method': 'POST', 'body': JSON.stringify(files) }).then(response => {
|
||||||
|
if (reusableModal.style.display === 'block') {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
if (response.ok) {
|
||||||
|
modalTitle.textContent = 'Success!';
|
||||||
|
modalBody.innerHTML = '<p>Your selection has been submitted successfully.</p>';
|
||||||
|
} else {
|
||||||
|
modalTitle.textContent = 'Error';
|
||||||
|
modalBody.innerHTML = '<p>There was an error submitting your selection. Please try again.</p>';
|
||||||
|
}
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onclick="closeModal()">Okay</button>';
|
||||||
|
openModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pinInput.addEventListener('keypress', ('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
submitPin.click();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
submitPin.addEventListener('click', () => {
|
||||||
|
pinInput.blur();
|
||||||
|
if (pinInput.value === '') {
|
||||||
|
modalTitle.textContent = 'Missing Pin';
|
||||||
|
modalBody.innerHTML = `<p>The <code>pin</code> is missing.</p>`;
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onClick="closeModal()">Try Again</button>';
|
||||||
|
openModal();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (urlParams.gid == null || urlParams.gid === '') {
|
||||||
|
modalTitle.textContent = 'Missing parameter';
|
||||||
|
modalBody.innerHTML = `<p>The parameter <code>gid</code> is missing.</p>`;
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onClick="closeModal()">Try Again</button>';
|
||||||
|
openModal();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const requestUrl = `/app/files/torrent?gid=${urlParams.gid}&pin=${pinInput.value}`;
|
||||||
|
fetch(requestUrl).then(function (response) {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json().then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
modalTitle.textContent = data.error;
|
||||||
|
modalBody.innerHTML = `<p>${data.message}. Please try again.</p>`;
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onclick="closeModal()">Retry</button>';
|
||||||
|
openModal();
|
||||||
|
} else {
|
||||||
|
files = data.files;
|
||||||
|
allowEdit = data.engine === "qbittorrent";
|
||||||
|
pinEntry.classList.add('fadeOut');
|
||||||
|
setTimeout(() => {
|
||||||
|
pinEntry.style.display = 'none';
|
||||||
|
fileManager.classList.remove('hidden');
|
||||||
|
renderFileTree(files);
|
||||||
|
updateStats();
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
modalTitle.textContent = 'Something Went Wrong';
|
||||||
|
modalBody.innerHTML = '<p>Please check console. Status Code: ' + response.status + '</p>';
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onclick="closeModal()">Retry</button>';
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
modalTitle.textContent = 'Server Error';
|
||||||
|
modalBody.innerHTML = '<p>There was an error connecting to the server. Please try again.</p><br>' + error.message;
|
||||||
|
modalFooter.innerHTML = '<button class="btn btn-primary" onclick="closeModal()">Retry</button>';
|
||||||
|
openModal();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
themeToggle.addEventListener('change', toggleTheme);
|
||||||
|
selectAllBtn.addEventListener('click', selectAll);
|
||||||
|
submitBtn.addEventListener('click', submitData);
|
||||||
|
reusableModal.addEventListener('click', (event) => {
|
||||||
|
if (event.target === reusableModal) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loadThemePreference();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
779
web/wserver.py
779
web/wserver.py
@ -1,11 +1,10 @@
|
|||||||
from aria2p import API as ariaAPI, Client as ariaClient
|
from aria2p import API as ariaAPI, Client as ariaClient
|
||||||
from flask import Flask, request
|
from flask import Flask, request, render_template, jsonify
|
||||||
from logging import getLogger, FileHandler, StreamHandler, INFO, basicConfig
|
from logging import getLogger, FileHandler, StreamHandler, INFO, basicConfig
|
||||||
from qbittorrentapi import NotFound404Error, Client as qbClient
|
from qbittorrentapi import NotFound404Error, Client as qbClient
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from json import dumps
|
|
||||||
|
|
||||||
from web.nodes import make_tree
|
from web.nodes import extract_file_ids, make_tree
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@ -27,640 +26,8 @@ basicConfig(
|
|||||||
|
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger(__name__)
|
||||||
|
|
||||||
page = """
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Torrent File Selector</title>
|
|
||||||
<link rel="icon" href="https://telegra.ph/file/43af672249c94053356c7.jpg" type="image/jpg">
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
|
|
||||||
integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
></script>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css"
|
|
||||||
integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
/>
|
|
||||||
<style>
|
|
||||||
|
|
||||||
*{
|
def re_verify(paused, resumed, hash_id):
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: "Ubuntu", sans-serif;
|
|
||||||
list-style: none;
|
|
||||||
text-decoration: none;
|
|
||||||
outline: none !important;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
body{
|
|
||||||
background-color: #0D1117;
|
|
||||||
}
|
|
||||||
|
|
||||||
header{
|
|
||||||
margin: 3vh 1vw;
|
|
||||||
padding: 0.5rem 1rem 0.5rem 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
border-bottom: #161B22;
|
|
||||||
border-radius: 30px;
|
|
||||||
background-color: #161B22;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
}
|
|
||||||
|
|
||||||
header:hover, section:hover{
|
|
||||||
box-shadow: 0px 0px 15px black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand{
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
img{
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
border: 2px solid black;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name{
|
|
||||||
margin-left: 1vw;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro{
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
margin-top: 1vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social a{
|
|
||||||
font-size: 1.5rem;
|
|
||||||
padding-left: 1vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social a:hover, .brand:hover{
|
|
||||||
filter: invert(0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
section{
|
|
||||||
margin: 0vh 1vw;
|
|
||||||
margin-bottom: 10vh;
|
|
||||||
padding: 1vh 3vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #161B22 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
li:nth-child(1){
|
|
||||||
padding: 1rem 1rem 0.5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
li:nth-child(n+1){
|
|
||||||
padding-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
li label{
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
li{
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
span{
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
transition: transform 200ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.active{
|
|
||||||
transform: rotate(90deg);
|
|
||||||
-ms-transform: rotate(90deg); /* for IE */
|
|
||||||
-webkit-transform: rotate(90deg);/* for browsers supporting webkit (such as chrome, firefox, safari etc.). */
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul{
|
|
||||||
margin: 1vh 1vw 1vh 1vw;
|
|
||||||
padding: 0 0 0.5rem 0;
|
|
||||||
border: 2px solid black;
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #1c2129;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]{
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="submit"] {
|
|
||||||
border-radius: 20px;
|
|
||||||
margin: 2vh auto 1vh auto;
|
|
||||||
width: 50%;
|
|
||||||
display: block;
|
|
||||||
height: 5.5vh;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
background-color: #0D1117;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="submit"]:hover, input[type="submit"]:focus{
|
|
||||||
background-color: rgba(255, 255, 255, 0.068);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px){
|
|
||||||
input[type="submit"]{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#treeview .parent {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#treeview .parent > ul {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sticks {
|
|
||||||
margin: 0vh 1vw;
|
|
||||||
margin-bottom: 1vh;
|
|
||||||
padding: 1vh 3vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #161b22;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sticks.stick {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
function s_validate() {
|
|
||||||
if ($("input[name^='filenode_']:checked").length == 0) {
|
|
||||||
alert("Select one file at least!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!--© Designed and coded by @bipuldey19-Telegram-->
|
|
||||||
<header>
|
|
||||||
<div class="brand">
|
|
||||||
<img
|
|
||||||
src="https://telegra.ph/file/43af672249c94053356c7.jpg"
|
|
||||||
alt="logo"
|
|
||||||
/>
|
|
||||||
<a href="https://t.me/anas_tayyar">
|
|
||||||
<h2 class="name">Bittorrent Selection</h2>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="social">
|
|
||||||
<a href="https://www.github.com/anasty17/mirror-leech-telegram-bot"><i class="fab fa-github"></i></a>
|
|
||||||
<a href="https://t.me/anas_tayyar"><i class="fab fa-telegram"></i></a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div id="sticks">
|
|
||||||
<h4>Selected files: <b id="checked_files">0</b> of <b id="total_files">0</b></h4>
|
|
||||||
<h4>Selected files size: <b id="checked_size">0</b> of <b id="total_size">0</b></h4>
|
|
||||||
</div>
|
|
||||||
<section>
|
|
||||||
<form action="{form_url}" onsubmit="return s_validate()" method="POST">
|
|
||||||
{My_content}
|
|
||||||
<input type="submit" name="Select these files ;)">
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function () {
|
|
||||||
docready();
|
|
||||||
var tags = $("li").filter(function () {
|
|
||||||
return $(this).find("ul").length !== 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
tags.each(function () {
|
|
||||||
$(this).addClass("parent");
|
|
||||||
});
|
|
||||||
|
|
||||||
$("body").find("ul:first-child").attr("id", "treeview");
|
|
||||||
$(".parent").prepend("<span>▶</span>");
|
|
||||||
|
|
||||||
$("span").click(function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
$(this).parent( ".parent" ).find(">ul").toggle("slow");
|
|
||||||
if ($(this).hasClass("active")) $(this).removeClass("active");
|
|
||||||
else $(this).addClass("active");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if(document.getElementsByTagName("ul").length >= 10){
|
|
||||||
var labels = document.querySelectorAll("label");
|
|
||||||
//Shorting the file/folder names
|
|
||||||
labels.forEach(function (label) {
|
|
||||||
if (label.innerText.toString().split(" ").length >= 9) {
|
|
||||||
let FirstPart = label.innerText
|
|
||||||
.toString()
|
|
||||||
.split(" ")
|
|
||||||
.slice(0, 5)
|
|
||||||
.join(" ");
|
|
||||||
let SecondPart = label.innerText
|
|
||||||
.toString()
|
|
||||||
.split(" ")
|
|
||||||
.splice(-5)
|
|
||||||
.join(" ");
|
|
||||||
label.innerText = `${FirstPart}... ${SecondPart}`;
|
|
||||||
}
|
|
||||||
if (label.innerText.toString().split(".").length >= 9) {
|
|
||||||
let first = label.innerText
|
|
||||||
.toString()
|
|
||||||
.split(".")
|
|
||||||
.slice(0, 5)
|
|
||||||
.join(" ");
|
|
||||||
let second = label.innerText
|
|
||||||
.toString()
|
|
||||||
.split(".")
|
|
||||||
.splice(-5)
|
|
||||||
.join(".");
|
|
||||||
label.innerText = `${first}... ${second}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$('input[type="checkbox"]').change(function(e) {
|
|
||||||
var checked = $(this).prop("checked"),
|
|
||||||
container = $(this).parent(),
|
|
||||||
siblings = container.siblings();
|
|
||||||
/*
|
|
||||||
$(this).attr('value', function(index, attr){
|
|
||||||
return attr == 'yes' ? 'noo' : 'yes';
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
container.find('input[type="checkbox"]').prop({
|
|
||||||
indeterminate: false,
|
|
||||||
checked: checked
|
|
||||||
});
|
|
||||||
function checkSiblings(el) {
|
|
||||||
var parent = el.parent().parent(),
|
|
||||||
all = true;
|
|
||||||
el.siblings().each(function() {
|
|
||||||
let returnValue = all = ($(this).children('input[type="checkbox"]').prop("checked") === checked);
|
|
||||||
return returnValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (all && checked) {
|
|
||||||
parent.children('input[type="checkbox"]').prop({
|
|
||||||
indeterminate: false,
|
|
||||||
checked: checked
|
|
||||||
});
|
|
||||||
checkSiblings(parent);
|
|
||||||
} else if (all && !checked) {
|
|
||||||
parent.children('input[type="checkbox"]').prop("checked", checked);
|
|
||||||
parent.children('input[type="checkbox"]').prop("indeterminate", (parent.find('input[type="checkbox"]:checked').length > 0));
|
|
||||||
checkSiblings(parent);
|
|
||||||
} else {
|
|
||||||
el.parents("li").children('input[type="checkbox"]').prop({
|
|
||||||
indeterminate: true,
|
|
||||||
checked: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkSiblings(container);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
function docready () {
|
|
||||||
$("label[for^='filenode_']").css("cursor", "pointer");
|
|
||||||
$("label[for^='filenode_']").click(function () {
|
|
||||||
$(this).prev().click();
|
|
||||||
});
|
|
||||||
checked_size();
|
|
||||||
checkingfiles();
|
|
||||||
var total_files = $("label[for^='filenode_']").length;
|
|
||||||
$("#total_files").text(total_files);
|
|
||||||
var total_size = 0;
|
|
||||||
var ffilenode = $("label[for^='filenode_']");
|
|
||||||
ffilenode.each(function () {
|
|
||||||
var size = parseFloat($(this).data("size"));
|
|
||||||
total_size += size;
|
|
||||||
$(this).append(" - " + humanFileSize(size));
|
|
||||||
});
|
|
||||||
$("#total_size").text(humanFileSize(total_size));
|
|
||||||
};
|
|
||||||
function checked_size() {
|
|
||||||
var checked_size = 0;
|
|
||||||
var checkedboxes = $("input[name^='filenode_']:checked");
|
|
||||||
checkedboxes.each(function () {
|
|
||||||
var size = parseFloat($(this).data("size"));
|
|
||||||
checked_size += size;
|
|
||||||
});
|
|
||||||
$("#checked_size").text(humanFileSize(checked_size));
|
|
||||||
}
|
|
||||||
function checkingfiles() {
|
|
||||||
var checked_files = $("input[name^='filenode_']:checked").length;
|
|
||||||
$("#checked_files").text(checked_files);
|
|
||||||
}
|
|
||||||
$("input[name^='foldernode_']").change(function () {
|
|
||||||
checkingfiles();
|
|
||||||
checked_size();
|
|
||||||
});
|
|
||||||
$("input[name^='filenode_']").change(function () {
|
|
||||||
checkingfiles();
|
|
||||||
checked_size();
|
|
||||||
});
|
|
||||||
function humanFileSize(size) {
|
|
||||||
var i = -1;
|
|
||||||
var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
||||||
do {
|
|
||||||
size = size / 1024;
|
|
||||||
i++;
|
|
||||||
} while (size > 1024);
|
|
||||||
return Math.max(size, 0).toFixed(1) + byteUnits[i];
|
|
||||||
}
|
|
||||||
function sticking() {
|
|
||||||
var window_top = $(window).scrollTop();
|
|
||||||
var div_top = $('.brand').offset().top;
|
|
||||||
if (window_top > div_top) {
|
|
||||||
$('#sticks').addClass('stick');
|
|
||||||
} else {
|
|
||||||
$('#sticks').removeClass('stick');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$(function () {
|
|
||||||
$(window).scroll(sticking);
|
|
||||||
sticking();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
code_page = """
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Torrent Code Checker</title>
|
|
||||||
<link rel="icon" href="https://telegra.ph/file/43af672249c94053356c7.jpg" type="image/jpg">
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css"
|
|
||||||
integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
/>
|
|
||||||
<style>
|
|
||||||
*{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: "Ubuntu", sans-serif;
|
|
||||||
list-style: none;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
body{
|
|
||||||
background-color: #0D1117;
|
|
||||||
}
|
|
||||||
|
|
||||||
header{
|
|
||||||
margin: 3vh 1vw;
|
|
||||||
padding: 0.5rem 1rem 0.5rem 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
border-bottom: #161B22;
|
|
||||||
border-radius: 30px;
|
|
||||||
background-color: #161B22;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
}
|
|
||||||
|
|
||||||
header:hover, section:hover{
|
|
||||||
box-shadow: 0px 0px 15px black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand{
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
img{
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
border: 2px solid black;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name{
|
|
||||||
color: white;
|
|
||||||
margin-left: 1vw;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro{
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
margin-top: 1vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social a{
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: white;
|
|
||||||
padding-left: 1vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social a:hover, .brand:hover{
|
|
||||||
filter: invert(0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
section{
|
|
||||||
margin: 0vh 1vw;
|
|
||||||
margin-bottom: 10vh;
|
|
||||||
padding: 1vh 3vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #161B22 ;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
section form{
|
|
||||||
display: flex;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
section div{
|
|
||||||
background-color: #0D1117;
|
|
||||||
border-radius: 20px;
|
|
||||||
max-width: fit-content;
|
|
||||||
padding: 0.7rem;
|
|
||||||
margin-top: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
section label{
|
|
||||||
font-size: larger;
|
|
||||||
font-weight: 500;
|
|
||||||
margin: 0 0 0.5vh 1.5vw;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
section input[type="text"]{
|
|
||||||
border-radius: 20px;
|
|
||||||
outline: none;
|
|
||||||
width: 50vw;
|
|
||||||
height: 4vh;
|
|
||||||
padding: 1rem;
|
|
||||||
margin: 0.5vh;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
background-color: #3e475531;
|
|
||||||
box-shadow: inset 0px 0px 10px black;
|
|
||||||
}
|
|
||||||
|
|
||||||
section input[type="text"]:focus{
|
|
||||||
border-color: rgba(255, 255, 255, 0.404);
|
|
||||||
}
|
|
||||||
|
|
||||||
section button{
|
|
||||||
border-radius: 20px;
|
|
||||||
margin-top: 1vh;
|
|
||||||
width: 100%;
|
|
||||||
height: 5.5vh;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.11);
|
|
||||||
background-color: #0D1117;
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
section button:hover, section button:focus{
|
|
||||||
background-color: rgba(255, 255, 255, 0.068);
|
|
||||||
}
|
|
||||||
|
|
||||||
section span{
|
|
||||||
display: block;
|
|
||||||
font-size: x-small;
|
|
||||||
margin: 1vh;
|
|
||||||
font-weight: 100;
|
|
||||||
font-style: italic;
|
|
||||||
margin-left: 23%;
|
|
||||||
margin-right: auto;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
section form{
|
|
||||||
flex-direction: column;
|
|
||||||
width: 90vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
section div{
|
|
||||||
max-width: 100%;
|
|
||||||
margin-bottom: 1vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
section label{
|
|
||||||
margin-left: 3vw;
|
|
||||||
margin-top: 1vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
section input[type="text"]{
|
|
||||||
width: calc(100% - 0.3rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
section button{
|
|
||||||
width: 100%;
|
|
||||||
height: 5vh;
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
section span{
|
|
||||||
margin-left: 5%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!--© Designed and coded by @bipuldey19-Telegram-->
|
|
||||||
<header>
|
|
||||||
<div class="brand">
|
|
||||||
<img
|
|
||||||
src="https://telegra.ph/file/43af672249c94053356c7.jpg"
|
|
||||||
alt="logo"
|
|
||||||
/>
|
|
||||||
<a href="https://t.me/anas_tayyar">
|
|
||||||
<h2 class="name">Bittorrent Selection</h2>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="social">
|
|
||||||
<a href="https://www.github.com/anasty17/mirror-leech-telegram-bot"><i class="fab fa-github"></i></a>
|
|
||||||
<a href="https://t.me/anas_tayyar"><i class="fab fa-telegram"></i></a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<section>
|
|
||||||
<form action="{form_url}">
|
|
||||||
<div>
|
|
||||||
<label for="pin_code">Pin Code :</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="pin_code"
|
|
||||||
placeholder="Enter the code that you have got from Telegram to access the Torrent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary">Submit</button>
|
|
||||||
</form>
|
|
||||||
<span
|
|
||||||
>* Dont mess around. Your download will get messed up.</
|
|
||||||
>
|
|
||||||
</section>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def re_verfiy(paused, resumed, hash_id):
|
|
||||||
paused = paused.strip()
|
paused = paused.strip()
|
||||||
resumed = resumed.strip()
|
resumed = resumed.strip()
|
||||||
if paused:
|
if paused:
|
||||||
@ -706,60 +73,90 @@ def re_verfiy(paused, resumed, hash_id):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@app.route("/app/files/<string:id_>", methods=["GET"])
|
@app.route("/app/files")
|
||||||
def list_torrent_contents(id_):
|
def files():
|
||||||
if "pin_code" not in request.args.keys():
|
return render_template("page.html")
|
||||||
return code_page.replace("{form_url}", f"/app/files/{id_}")
|
|
||||||
|
|
||||||
pincode = ""
|
|
||||||
for nbr in id_:
|
|
||||||
if nbr.isdigit():
|
|
||||||
pincode += str(nbr)
|
|
||||||
if len(pincode) == 4:
|
|
||||||
break
|
|
||||||
if request.args["pin_code"] != pincode:
|
|
||||||
return "<h1>Incorrect pin code</h1>"
|
|
||||||
|
|
||||||
if len(id_) > 20:
|
@app.route("/app/files/torrent", methods=["GET", "POST"])
|
||||||
res = qbittorrent_client.torrents_files(torrent_hash=id_)
|
def handle_torrent():
|
||||||
cont = make_tree(res, "qbittorrent")
|
if not (gid := request.args.get("gid")):
|
||||||
else:
|
return jsonify(
|
||||||
res = aria2.client.get_files(id_)
|
{
|
||||||
cont = make_tree(res, "aria2")
|
"files": [],
|
||||||
|
"engine": "",
|
||||||
try:
|
"error": "GID is missing",
|
||||||
content = dumps(cont)
|
"message": "GID not specified",
|
||||||
except Exception as e:
|
}
|
||||||
LOGGER.error(str(e))
|
|
||||||
content = dumps({"files": [], "engine": str(e)})
|
|
||||||
|
|
||||||
return page.replace("{My_content}", content).replace(
|
|
||||||
"{form_url}", f"/app/files/{id_}?pin_code={pincode}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not (pin := request.args.get("pin")):
|
||||||
@app.route("/app/files/<string:id_>", methods=["POST"])
|
return jsonify(
|
||||||
def set_priority(id_):
|
{
|
||||||
data = dict(request.form)
|
"files": [],
|
||||||
|
"engine": "",
|
||||||
resume = ""
|
"error": "Pin is missing",
|
||||||
if len(id_) > 20:
|
"message": "PIN not specified",
|
||||||
pause = ""
|
}
|
||||||
for i, value in data.items():
|
)
|
||||||
if "filenode" in i:
|
code = ""
|
||||||
node_no = i.split("_")[-1]
|
for nbr in gid:
|
||||||
|
if nbr.isdigit():
|
||||||
if value == "on":
|
code += str(nbr)
|
||||||
resume += f"{node_no}|"
|
if len(code) == 4:
|
||||||
|
break
|
||||||
|
if code != pin:
|
||||||
|
return jsonify(
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"engine": "",
|
||||||
|
"error": "Invalid pin",
|
||||||
|
"message": "The PIN you entered is incorrect",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if request.method == "POST":
|
||||||
|
data = request.get_json(cache=False, force=True)
|
||||||
|
selected_files, unselected_files = extract_file_ids(data)
|
||||||
|
if len(gid) > 20:
|
||||||
|
selected_files = "|".join(selected_files)
|
||||||
|
unselected_files = "|".join(unselected_files)
|
||||||
|
set_qbittorrent(gid, selected_files, unselected_files)
|
||||||
else:
|
else:
|
||||||
pause += f"{node_no}|"
|
selected_files = ",".join(selected_files)
|
||||||
|
set_aria2(gid, selected_files)
|
||||||
|
content = jsonify(
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"engine": "",
|
||||||
|
"error": "",
|
||||||
|
"message": "Your selection has been submitted successfully.",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if request.method == "GET":
|
||||||
|
try:
|
||||||
|
if len(gid) > 20:
|
||||||
|
res = qbittorrent_client.torrents_files(torrent_hash=gid)
|
||||||
|
content = jsonify(make_tree(res, "qbittorrent"))
|
||||||
|
else:
|
||||||
|
res = aria2.client.get_files(gid)
|
||||||
|
content = jsonify(make_tree(res, "aria2"))
|
||||||
|
except Exception as e:
|
||||||
|
LOGGER.error(str(e))
|
||||||
|
content = jsonify(
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"engine": "",
|
||||||
|
"error": "Error getting files",
|
||||||
|
"message": str(e),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return content
|
||||||
|
|
||||||
pause = pause.strip("|")
|
|
||||||
resume = resume.strip("|")
|
|
||||||
|
|
||||||
|
def set_qbittorrent(gid, selected_files, unselected_files):
|
||||||
try:
|
try:
|
||||||
qbittorrent_client.torrents_file_priority(
|
qbittorrent_client.torrents_file_priority(
|
||||||
torrent_hash=id_, file_ids=pause, priority=0
|
torrent_hash=gid, file_ids=unselected_files, priority=0
|
||||||
)
|
)
|
||||||
except NotFound404Error as e:
|
except NotFound404Error as e:
|
||||||
raise NotFound404Error from e
|
raise NotFound404Error from e
|
||||||
@ -767,29 +164,23 @@ def set_priority(id_):
|
|||||||
LOGGER.error(f"{e} Errored in paused")
|
LOGGER.error(f"{e} Errored in paused")
|
||||||
try:
|
try:
|
||||||
qbittorrent_client.torrents_file_priority(
|
qbittorrent_client.torrents_file_priority(
|
||||||
torrent_hash=id_, file_ids=resume, priority=1
|
torrent_hash=gid, file_ids=selected_files, priority=1
|
||||||
)
|
)
|
||||||
except NotFound404Error as e:
|
except NotFound404Error as e:
|
||||||
raise NotFound404Error from e
|
raise NotFound404Error from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOGGER.error(f"{e} Errored in resumed")
|
LOGGER.error(f"{e} Errored in resumed")
|
||||||
sleep(1)
|
sleep(1)
|
||||||
if not re_verfiy(pause, resume, id_):
|
if not re_verify(unselected_files, selected_files, gid):
|
||||||
LOGGER.error(f"Verification Failed! Hash: {id_}")
|
LOGGER.error(f"Verification Failed! Hash: {gid}")
|
||||||
else:
|
|
||||||
for i, value in data.items():
|
|
||||||
if "filenode" in i and value == "on":
|
|
||||||
node_no = i.split("_")[-1]
|
|
||||||
resume += f"{node_no},"
|
|
||||||
|
|
||||||
resume = resume.strip(",")
|
|
||||||
|
|
||||||
res = aria2.client.change_option(id_, {"select-file": resume})
|
def set_aria2(gid, selected_files):
|
||||||
|
res = aria2.client.change_option(gid, {"select-file": selected_files})
|
||||||
if res == "OK":
|
if res == "OK":
|
||||||
LOGGER.info(f"Verified! Gid: {id_}")
|
LOGGER.info(f"Verified! Gid: {gid}")
|
||||||
else:
|
else:
|
||||||
LOGGER.info(f"Verification Failed! Report! Gid: {id_}")
|
LOGGER.info(f"Verification Failed! Report! Gid: {gid}")
|
||||||
return list_torrent_contents(id_)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
Loading…
Reference in New Issue
Block a user