mirror of
https://github.com/onionshare/onionshare.git
synced 2025-08-07 13:52:49 -04:00
Fix conflicts
This commit is contained in:
commit
2040555134
17 changed files with 457 additions and 652 deletions
|
@ -27,8 +27,6 @@ from datetime import datetime
|
|||
from datetime import timedelta
|
||||
|
||||
from .common import Common, CannotFindTor
|
||||
from .censorship import CensorshipCircumvention
|
||||
from .meek import Meek, MeekNotRunning
|
||||
from .web import Web
|
||||
from .onion import TorErrorProtocolError, TorTooOldEphemeral, TorTooOldStealth, Onion
|
||||
from .onionshare import OnionShare
|
||||
|
@ -285,20 +283,6 @@ def main(cwd=None):
|
|||
# Create the Web object
|
||||
web = Web(common, False, mode_settings, mode)
|
||||
|
||||
# Create the Meek object and start the meek client
|
||||
# meek = Meek(common)
|
||||
# meek.start()
|
||||
|
||||
# Create the CensorshipCircumvention object to make
|
||||
# API calls to Tor over Meek
|
||||
censorship = CensorshipCircumvention(common, meek)
|
||||
# Example: request recommended bridges, pretending to be from China, using
|
||||
# domain fronting.
|
||||
# censorship_recommended_settings = censorship.request_settings(country="cn")
|
||||
# print(censorship_recommended_settings)
|
||||
# Clean up the meek subprocess once we're done working with the censorship circumvention API
|
||||
# meek.cleanup()
|
||||
|
||||
# Start the Onion object
|
||||
try:
|
||||
onion = Onion(common, use_tmp_dir=True)
|
||||
|
@ -474,13 +458,13 @@ def main(cwd=None):
|
|||
if app.autostop_timer > 0:
|
||||
# if the auto-stop timer was set and has run out, stop the server
|
||||
if not app.autostop_timer_thread.is_alive():
|
||||
if mode == "share" or (mode == "website"):
|
||||
if mode == "share":
|
||||
# If there were no attempts to download the share, or all downloads are done, we can stop
|
||||
if web.share_mode.cur_history_id == 0 or web.done:
|
||||
print("Stopped because auto-stop timer ran out")
|
||||
web.stop(app.port)
|
||||
break
|
||||
if mode == "receive":
|
||||
elif mode == "receive":
|
||||
if (
|
||||
web.receive_mode.cur_history_id == 0
|
||||
or not web.receive_mode.uploads_in_progress
|
||||
|
@ -489,6 +473,11 @@ def main(cwd=None):
|
|||
web.stop(app.port)
|
||||
break
|
||||
web.receive_mode.can_upload = False
|
||||
else:
|
||||
# website or chat mode
|
||||
print("Stopped because auto-stop timer ran out")
|
||||
web.stop(app.port)
|
||||
break
|
||||
# Allow KeyboardInterrupt exception to be handled with threads
|
||||
# https://stackoverflow.com/questions/3788208/python-threading-ignores-keyboardinterrupt-exception
|
||||
time.sleep(0.2)
|
||||
|
|
|
@ -88,197 +88,206 @@ class Common:
|
|||
╰───────────────────────────────────────────╯
|
||||
"""
|
||||
|
||||
if self.platform == "Windows":
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "╭───────────────────────────────────────────╮"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄▄█████▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄████▀▀▀████▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█▀ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄█▄ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄█████▄ ███"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ███ ▀█████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀██▄ ▀█▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀██▄ ▄█▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀████▄▄▄████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█████▀▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄▀▄ ▄▀▀ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ ▀ ▀▄ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "│ │"
|
||||
)
|
||||
left_spaces = (43 - len(self.version) - 1) // 2
|
||||
right_spaces = left_spaces
|
||||
if left_spaces + len(self.version) + 1 + right_spaces < 43:
|
||||
right_spaces += 1
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ f"{' '*left_spaces}v{self.version}{' '*right_spaces}"
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "│ │"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " https://onionshare.org/ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "╰───────────────────────────────────────────╯"
|
||||
)
|
||||
print()
|
||||
try:
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "╭───────────────────────────────────────────╮"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄▄█████▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄████▀▀▀████▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█▀ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄█▄ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄█████▄ ███"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ███ ▀█████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀██▄ ▀█▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀██▄ ▄█▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀████▄▄▄████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█████▀▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄▀▄ ▄▀▀ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ ▀ ▀▄ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│ │"
|
||||
)
|
||||
left_spaces = (43 - len(self.version) - 1) // 2
|
||||
right_spaces = left_spaces
|
||||
if left_spaces + len(self.version) + 1 + right_spaces < 43:
|
||||
right_spaces += 1
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ f"{' '*left_spaces}v{self.version}{' '*right_spaces}"
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│ │"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " https://onionshare.org/ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "╰───────────────────────────────────────────╯"
|
||||
)
|
||||
print()
|
||||
except:
|
||||
# If anything fails, print a boring banner
|
||||
print(f"OnionShare v{self.version}")
|
||||
print("https://onionshare.org/")
|
||||
print()
|
||||
|
||||
def load_settings(self, config=None):
|
||||
"""
|
||||
|
@ -467,6 +476,19 @@ class Common:
|
|||
else:
|
||||
return False
|
||||
|
||||
def is_flatpak(self):
|
||||
"""
|
||||
Returns True if OnionShare is running in a Flatpak sandbox
|
||||
"""
|
||||
return os.environ.get("FLATPAK_ID") == "org.onionshare.OnionShare"
|
||||
|
||||
def is_snapcraft(self):
|
||||
"""
|
||||
Returns True if OnionShare is running in a Snapcraft sandbox
|
||||
"""
|
||||
return os.environ.get("SNAP_INSTANCE_NAME") == "onionshare"
|
||||
|
||||
|
||||
@staticmethod
|
||||
def random_string(num_bytes, output_len=None):
|
||||
"""
|
||||
|
|
|
@ -120,7 +120,8 @@ class Meek(object):
|
|||
stderr=subprocess.PIPE,
|
||||
bufsize=1,
|
||||
env=self.meek_env,
|
||||
text=True,
|
||||
# Using universal_newlines instead of text because the snap package using python < 3.7
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
# Queue up the stdout from the subprocess for polling later
|
||||
|
|
|
@ -29,6 +29,7 @@ import subprocess
|
|||
import time
|
||||
import shlex
|
||||
import psutil
|
||||
import traceback
|
||||
|
||||
from distutils.version import LooseVersion as Version
|
||||
|
||||
|
@ -354,6 +355,11 @@ class Onion(object):
|
|||
f.write("\nUseBridges 1\n")
|
||||
|
||||
# Execute a tor subprocess
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
f"starting {self.tor_path} subprocess",
|
||||
)
|
||||
start_ts = time.time()
|
||||
if self.common.platform == "Windows":
|
||||
# In Windows, hide console window when opening tor.exe subprocess
|
||||
|
@ -366,17 +372,32 @@ class Onion(object):
|
|||
startupinfo=startupinfo,
|
||||
)
|
||||
else:
|
||||
if self.common.is_snapcraft():
|
||||
env = None
|
||||
else:
|
||||
env = {"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)}
|
||||
|
||||
self.tor_proc = subprocess.Popen(
|
||||
[self.tor_path, "-f", self.tor_torrc],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env={"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)},
|
||||
env=env,
|
||||
)
|
||||
|
||||
# Wait for the tor controller to start
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
f"tor pid: {self.tor_proc.pid}",
|
||||
)
|
||||
time.sleep(2)
|
||||
|
||||
# Connect to the controller
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
"authenticating to tor controller",
|
||||
)
|
||||
try:
|
||||
if (
|
||||
self.common.platform == "Windows"
|
||||
|
@ -389,6 +410,7 @@ class Onion(object):
|
|||
self.c.authenticate()
|
||||
except Exception as e:
|
||||
print("OnionShare could not connect to Tor:\n{}".format(e.args[0]))
|
||||
print(traceback.format_exc())
|
||||
raise BundledTorBroken(e.args[0])
|
||||
|
||||
while True:
|
||||
|
|
|
@ -11,7 +11,7 @@ function unhumanize(text) {
|
|||
}
|
||||
}
|
||||
function sortTable(n) {
|
||||
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||
var table, rows, switching, i, x, y, valX, valY, shouldSwitch, dir, switchcount = 0;
|
||||
table = document.getElementById("file-list");
|
||||
switching = true;
|
||||
// Set the sorting direction to ascending:
|
||||
|
@ -21,7 +21,7 @@ function sortTable(n) {
|
|||
while (switching) {
|
||||
// Start by saying: no switching is done:
|
||||
switching = false;
|
||||
rows = table.getElementsByTagName("TR");
|
||||
rows = table.getElementsByClassName("row");
|
||||
/* Loop through all table rows (except the
|
||||
first, which contains table headers): */
|
||||
for (i = 1; i < (rows.length - 1); i++) {
|
||||
|
@ -29,18 +29,22 @@ function sortTable(n) {
|
|||
shouldSwitch = false;
|
||||
/* Get the two elements you want to compare,
|
||||
one from current row and one from the next: */
|
||||
x = rows[i].getElementsByTagName("TD")[n];
|
||||
y = rows[i + 1].getElementsByTagName("TD")[n];
|
||||
x = rows[i].getElementsByClassName("cell-data")[n];
|
||||
y = rows[i + 1].getElementsByClassName("cell-data")[n];
|
||||
|
||||
valX = x.classList.contains("size") ? unhumanize(x.innerHTML.toLowerCase()) : x.innerHTML;
|
||||
valY = y.classList.contains("size") ? unhumanize(y.innerHTML.toLowerCase()) : y.innerHTML;
|
||||
|
||||
/* Check if the two rows should switch place,
|
||||
based on the direction, asc or desc: */
|
||||
if (dir == "asc") {
|
||||
if (unhumanize(x.innerHTML.toLowerCase()) > unhumanize(y.innerHTML.toLowerCase())) {
|
||||
// If so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
}
|
||||
if (valX > valY) {
|
||||
// If so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
}
|
||||
} else if (dir == "desc") {
|
||||
if (unhumanize(x.innerHTML.toLowerCase()) < unhumanize(y.innerHTML.toLowerCase())) {
|
||||
if (valX < valY) {
|
||||
// If so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
{% endif %}
|
||||
|
||||
<div class="file-list" id="file-list">
|
||||
<div class="d-flex">
|
||||
<div class="d-flex row">
|
||||
<div id="filename-header" class="heading">Filename</div>
|
||||
<div id="size-header" class="heading">Size</div>
|
||||
</div>
|
||||
|
@ -41,26 +41,26 @@
|
|||
<div>
|
||||
<img width="30" height="30" title="" alt="" src="{{ static_url_path }}/img/web_folder.png" />
|
||||
<a href="{{ info.link }}">
|
||||
<span>{{ info.basename }}</span>
|
||||
<span class="cell-data">{{ info.basename }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>—</div>
|
||||
<div class="cell-data">—</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for info in files %}
|
||||
<div class="d-flex">
|
||||
<div class="d-flex row">
|
||||
<div>
|
||||
<img width="30" height="30" title="" alt="" src="{{ static_url_path }}/img/web_file.png" />
|
||||
{% if download_individual_files %}
|
||||
<a href="{{ info.link }}">
|
||||
<span>{{ info.basename }}</span>
|
||||
<span class="cell-data">{{ info.basename }}</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<span>{{ info.basename }}</span>
|
||||
<span class="cell-data">{{ info.basename }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{{ info.size_human }}</div>
|
||||
<div class="cell-data size">{{ info.size_human }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue