lfss 0.7.13__py3-none-any.whl → 0.7.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- frontend/index.html +1 -1
- frontend/scripts.js +31 -46
- frontend/state.js +57 -0
- frontend/styles.css +0 -1
- frontend/thumb.css +4 -10
- frontend/thumb.js +42 -24
- lfss/src/server.py +14 -3
- lfss/src/thumb.py +18 -12
- {lfss-0.7.13.dist-info → lfss-0.7.15.dist-info}/METADATA +1 -1
- {lfss-0.7.13.dist-info → lfss-0.7.15.dist-info}/RECORD +12 -11
- {lfss-0.7.13.dist-info → lfss-0.7.15.dist-info}/WHEEL +0 -0
- {lfss-0.7.13.dist-info → lfss-0.7.15.dist-info}/entry_points.txt +0 -0
frontend/index.html
CHANGED
frontend/scripts.js
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
import Connector from './api.js';
|
2
1
|
import { permMap } from './api.js';
|
3
2
|
import { showFloatingWindowLineInput, showPopup } from './popup.js';
|
4
3
|
import { formatSize, decodePathURI, ensurePathURI, getRandomString, cvtGMT2Local, debounce, encodePathURI, asHtmlText } from './utils.js';
|
5
4
|
import { showInfoPanel, showDirInfoPanel } from './info.js';
|
6
5
|
import { makeThumbHtml } from './thumb.js';
|
6
|
+
import { store } from './state.js';
|
7
7
|
|
8
|
-
|
8
|
+
/** @type {import('./api.js').UserRecord}*/
|
9
9
|
let userRecord = null;
|
10
|
+
|
10
11
|
const ensureSlashEnd = (path) => {
|
11
12
|
return path.endsWith('/') ? path : path + '/';
|
12
13
|
}
|
@@ -26,25 +27,16 @@ const randomizeFnameButton = document.querySelector('#randomize-fname-btn');
|
|
26
27
|
const sortBySelect = document.querySelector('#sort-by-sel');
|
27
28
|
const sortOrderSelect = document.querySelector('#sort-order-sel');
|
28
29
|
|
29
|
-
conn
|
30
|
-
|
30
|
+
const conn = store.conn;
|
31
|
+
store.init();
|
31
32
|
|
32
33
|
{
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
conn.config.endpoint = endpoint;
|
37
|
-
}
|
38
|
-
const token = window.localStorage.getItem('token');
|
39
|
-
if (token){
|
40
|
-
tokenInput.value = token;
|
41
|
-
conn.config.token = token;
|
42
|
-
}
|
43
|
-
const path = window.localStorage.getItem('path');
|
44
|
-
if (path){
|
45
|
-
pathInput.value = path;
|
46
|
-
}
|
34
|
+
tokenInput.value = store.token;
|
35
|
+
endpointInput.value = store.endpoint;
|
36
|
+
pathInput.value = store.dirpath;
|
47
37
|
uploadFilePrefixLabel.textContent = pathInput.value;
|
38
|
+
sortBySelect.value = store.sortby;
|
39
|
+
sortOrderSelect.value = store.sortorder;
|
48
40
|
maybeRefreshUserRecord().then(
|
49
41
|
() => maybeRefreshFileList()
|
50
42
|
);
|
@@ -52,20 +44,18 @@ conn.config.token = tokenInput.value;
|
|
52
44
|
|
53
45
|
function onPathChange(){
|
54
46
|
uploadFilePrefixLabel.textContent = pathInput.value;
|
55
|
-
|
47
|
+
store.dirpath = pathInput.value;
|
56
48
|
maybeRefreshFileList();
|
57
49
|
}
|
58
50
|
|
59
51
|
endpointInput.addEventListener('blur', () => {
|
60
|
-
|
61
|
-
window.localStorage.setItem('endpoint', endpointInput.value);
|
52
|
+
store.endpoint = endpointInput.value;
|
62
53
|
maybeRefreshUserRecord().then(
|
63
54
|
() => maybeRefreshFileList()
|
64
55
|
);
|
65
56
|
});
|
66
57
|
tokenInput.addEventListener('blur', () => {
|
67
|
-
|
68
|
-
window.localStorage.setItem('token', tokenInput.value);
|
58
|
+
store.token = tokenInput.value;
|
69
59
|
maybeRefreshUserRecord().then(
|
70
60
|
() => maybeRefreshFileList()
|
71
61
|
);
|
@@ -94,7 +84,7 @@ function onFileNameInpuChange(){
|
|
94
84
|
uploadFileNameInput.classList.remove('duplicate');
|
95
85
|
}
|
96
86
|
else {
|
97
|
-
const p = ensurePathURI(
|
87
|
+
const p = ensurePathURI(store.dirpath + fileName);
|
98
88
|
conn.getMetadata(p).then(
|
99
89
|
(data) => {
|
100
90
|
console.log("Got file meta", data);
|
@@ -126,7 +116,7 @@ uploadFileSelector.addEventListener('change', () => {
|
|
126
116
|
});
|
127
117
|
uploadButton.addEventListener('click', () => {
|
128
118
|
const file = uploadFileSelector.files[0];
|
129
|
-
let path =
|
119
|
+
let path = store.dirpath;
|
130
120
|
let fileName = uploadFileNameInput.value;
|
131
121
|
if (fileName.length === 0){
|
132
122
|
throw new Error('File name cannot be empty');
|
@@ -172,7 +162,7 @@ uploadFileNameInput.addEventListener('input', debounce(onFileNameInpuChange, 500
|
|
172
162
|
uploadFileNameInput.focus();
|
173
163
|
}
|
174
164
|
else if (files.length > 1){
|
175
|
-
let dstPath =
|
165
|
+
let dstPath = store.dirpath + uploadFileNameInput.value;
|
176
166
|
if (!dstPath.endsWith('/')){ dstPath += '/'; }
|
177
167
|
if (!confirm(`
|
178
168
|
You are trying to upload multiple files at once.
|
@@ -216,52 +206,50 @@ Are you sure you want to proceed?
|
|
216
206
|
|
217
207
|
function maybeRefreshFileList(){
|
218
208
|
if (
|
219
|
-
|
209
|
+
store.dirpath && store.dirpath.length > 0 && store.dirpath.endsWith('/')
|
220
210
|
){
|
221
211
|
refreshFileList();
|
222
212
|
}
|
223
213
|
}
|
224
214
|
|
225
|
-
let sortBy = sortBySelect.value;
|
226
|
-
let sortOrder = sortOrderSelect.value;
|
227
215
|
/** @param {import('./api.js').DirectoryRecord} dirs */
|
228
216
|
function sortDirList(dirs){
|
229
|
-
if (
|
217
|
+
if (store.sortby === 'name'){
|
230
218
|
dirs.sort((a, b) => { return a.url.localeCompare(b.url); });
|
231
219
|
}
|
232
|
-
if (
|
220
|
+
if (store.sortorder === 'desc'){ dirs.reverse(); }
|
233
221
|
}
|
234
222
|
/** @param {import('./api.js').FileRecord} files */
|
235
223
|
function sortFileList(files){
|
236
224
|
function timestr2num(timestr){
|
237
225
|
return new Date(timestr).getTime();
|
238
226
|
}
|
239
|
-
if (
|
227
|
+
if (store.sortby === 'name'){
|
240
228
|
files.sort((a, b) => { return a.url.localeCompare(b.url); });
|
241
229
|
}
|
242
|
-
if (
|
230
|
+
if (store.sortby === 'size'){
|
243
231
|
files.sort((a, b) => { return a.file_size - b.file_size; });
|
244
232
|
}
|
245
|
-
if (
|
233
|
+
if (store.sortby === 'access'){
|
246
234
|
files.sort((a, b) => { return timestr2num(a.access_time) - timestr2num(b.access_time); });
|
247
235
|
}
|
248
|
-
if (
|
236
|
+
if (store.sortby === 'create'){
|
249
237
|
files.sort((a, b) => { return timestr2num(a.create_time) - timestr2num(b.create_time); });
|
250
238
|
}
|
251
|
-
if (
|
239
|
+
if (store.sortby === 'mime'){
|
252
240
|
files.sort((a, b) => { return a.mime_type.localeCompare(b.mime_type); });
|
253
241
|
}
|
254
|
-
if (
|
242
|
+
if (store.sortorder === 'desc'){ files.reverse(); }
|
255
243
|
}
|
256
|
-
sortBySelect.addEventListener('change', (elem) => {
|
257
|
-
sortOrderSelect.addEventListener('change', (elem) => {
|
244
|
+
sortBySelect.addEventListener('change', (elem) => {store.sortby = elem.target.value; refreshFileList();});
|
245
|
+
sortOrderSelect.addEventListener('change', (elem) => {store.sortorder = elem.target.value; refreshFileList();});
|
258
246
|
|
259
247
|
function refreshFileList(){
|
260
|
-
conn.listPath(
|
248
|
+
conn.listPath(store.dirpath)
|
261
249
|
.then(data => {
|
262
250
|
pathHintDiv.classList.remove('disconnected');
|
263
251
|
pathHintDiv.classList.add('connected');
|
264
|
-
pathHintLabel.textContent =
|
252
|
+
pathHintLabel.textContent = store.dirpath;
|
265
253
|
tbody.innerHTML = '';
|
266
254
|
|
267
255
|
console.log("Got data", data);
|
@@ -520,7 +508,7 @@ function refreshFileList(){
|
|
520
508
|
(err) => {
|
521
509
|
pathHintDiv.classList.remove('connected');
|
522
510
|
pathHintDiv.classList.add('disconnected');
|
523
|
-
pathHintLabel.textContent =
|
511
|
+
pathHintLabel.textContent = store.dirpath;
|
524
512
|
tbody.innerHTML = '';
|
525
513
|
console.log("Error");
|
526
514
|
console.error(err);
|
@@ -530,7 +518,7 @@ function refreshFileList(){
|
|
530
518
|
|
531
519
|
|
532
520
|
async function maybeRefreshUserRecord(){
|
533
|
-
if (
|
521
|
+
if (store.endpoint && store.token){
|
534
522
|
await refreshUserRecord();
|
535
523
|
}
|
536
524
|
}
|
@@ -545,9 +533,6 @@ async function refreshUserRecord(){
|
|
545
533
|
console.error("Failed to get user record");
|
546
534
|
return false;
|
547
535
|
}
|
548
|
-
|
549
|
-
// UI updates.
|
550
|
-
|
551
536
|
return true;
|
552
537
|
}
|
553
538
|
|
frontend/state.js
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
import Connector from './api.js';
|
3
|
+
|
4
|
+
function loadPersistedState(key, defaultValue) {
|
5
|
+
const persistedValue = window.localStorage.getItem(key);
|
6
|
+
return persistedValue ? persistedValue : defaultValue;
|
7
|
+
}
|
8
|
+
|
9
|
+
function setPersistedState(key, value) {
|
10
|
+
window.localStorage.setItem(key, value);
|
11
|
+
}
|
12
|
+
|
13
|
+
export const store = {
|
14
|
+
conn: new Connector(),
|
15
|
+
|
16
|
+
init() {
|
17
|
+
this.conn.config.token = this.token;
|
18
|
+
this.conn.config.endpoint = this.endpoint;
|
19
|
+
},
|
20
|
+
|
21
|
+
get token() {
|
22
|
+
return loadPersistedState('token', '');
|
23
|
+
},
|
24
|
+
set token(t) {
|
25
|
+
setPersistedState('token', t);
|
26
|
+
this.conn.config.token = t;
|
27
|
+
},
|
28
|
+
|
29
|
+
get endpoint() {
|
30
|
+
return loadPersistedState('endpoint', 'http://localhost:8000');
|
31
|
+
},
|
32
|
+
set endpoint(url) {
|
33
|
+
setPersistedState('endpoint', url);
|
34
|
+
this.conn.config.endpoint = url;
|
35
|
+
},
|
36
|
+
|
37
|
+
get dirpath() {
|
38
|
+
return loadPersistedState('dirpath', '/');
|
39
|
+
},
|
40
|
+
set dirpath(pth) {
|
41
|
+
setPersistedState('dirpath', pth);
|
42
|
+
},
|
43
|
+
|
44
|
+
get sortby () {
|
45
|
+
return loadPersistedState('sortby', 'none');
|
46
|
+
},
|
47
|
+
set sortby (sb) {
|
48
|
+
setPersistedState('sortby', sb);
|
49
|
+
},
|
50
|
+
|
51
|
+
get sortorder () {
|
52
|
+
return loadPersistedState('sortorder', 'asc');
|
53
|
+
},
|
54
|
+
set sortorder (so) {
|
55
|
+
setPersistedState('sortorder', so);
|
56
|
+
},
|
57
|
+
};
|
frontend/styles.css
CHANGED
frontend/thumb.css
CHANGED
@@ -1,16 +1,10 @@
|
|
1
1
|
|
2
2
|
div.thumb{
|
3
|
-
height: 32px;
|
4
|
-
width: 32px;
|
5
|
-
border-radius: 0.25rem;
|
6
3
|
display: contents;
|
7
4
|
}
|
8
5
|
img.thumb{
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
img.thumb-svg{
|
14
|
-
/* dark blue */
|
15
|
-
filter: invert(30%) sepia(100%) saturate(80%) hue-rotate(180deg);
|
6
|
+
height: 32px; /* smaller than backend */
|
7
|
+
width: 32px;
|
8
|
+
object-fit: contain;
|
9
|
+
border-radius: 0.1rem;
|
16
10
|
}
|
frontend/thumb.js
CHANGED
@@ -24,28 +24,34 @@ function getIconSVGFromMimeType(mimeType){
|
|
24
24
|
if (mimeType.startsWith('image/')){
|
25
25
|
return ICON_IMAGE;
|
26
26
|
}
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
return ICON_FILE;
|
27
|
+
|
28
|
+
if (['application/pdf', 'application/x-pdf'].includes(mimeType)){
|
29
|
+
return ICON_PDF;
|
30
|
+
}
|
31
|
+
if (['application/x-msdownload', 'application/x-msdos-program', 'application/x-msi', 'application/x-ms-wim', 'application/octet-stream', 'application/x-apple-diskimage'].includes(mimeType)){
|
32
|
+
return ICON_EXE;
|
33
|
+
}
|
34
|
+
if (['application/zip', 'application/x-zip-compressed', 'application/x-7z-compressed', 'application/x-rar-compressed', 'application/x-tar', 'application/x-gzip'].includes(mimeType)){
|
35
|
+
return ICON_ZIP;
|
36
|
+
}
|
37
|
+
if ([
|
38
|
+
"text/html", "application/xhtml+xml", "application/xml", "text/css", "application/javascript", "text/javascript", "application/json", "text/x-python", "text/x-java-source",
|
39
|
+
"application/x-httpd-php", "text/x-ruby", "text/x-perl", "application/x-sh", "application/sql", "text/x-c", "text/x-c++", "text/x-csharp", "text/x-go", "text/x-haskell",
|
40
|
+
"text/x-lua", "text/x-markdown", "application/wasm", "application/x-tcl", "text/x-yaml", "application/x-latex", "application/x-tex", "text/x-scss", "application/x-lisp",
|
41
|
+
"application/x-rust", "application/x-ruby", "text/x-asm"
|
42
|
+
].includes(mimeType)){
|
43
|
+
return ICON_CODE;
|
45
44
|
}
|
45
|
+
|
46
|
+
return ICON_FILE;
|
46
47
|
}
|
47
48
|
|
48
49
|
function getSafeIconUrl(icon_str){
|
50
|
+
// change icon color
|
51
|
+
const color = '#345';
|
52
|
+
icon_str = icon_str
|
53
|
+
.replace(/<svg/, `<svg fill="${color}"`)
|
54
|
+
.replace(/<path/, `<path fill="${color}"`);
|
49
55
|
return 'data:image/svg+xml,' + encodeURIComponent(icon_str);
|
50
56
|
}
|
51
57
|
|
@@ -56,13 +62,25 @@ let thumb_counter = 0;
|
|
56
62
|
* @returns {string}
|
57
63
|
*/
|
58
64
|
export function makeThumbHtml(c, r){
|
65
|
+
function ensureSlashEnd(url){ return url.endsWith('/')? url : url + '/'; }
|
59
66
|
const token = c.config.token;
|
60
67
|
const mtype = r.mime_type? r.mime_type : 'directory';
|
61
68
|
const thumb_id = `thumb-${thumb_counter++}`;
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
69
|
+
const url = mtype == 'directory'? ensureSlashEnd(r.url) : r.url;
|
70
|
+
|
71
|
+
if (mtype.startsWith('image/')){
|
72
|
+
return `
|
73
|
+
<div class="thumb" id="${thumb_id}"> \
|
74
|
+
<img src="${c.config.endpoint}/${url}?token=${token}&thumb=true" alt="${r.url}" class="thumb" \
|
75
|
+
onerror="this.src='${getSafeIconUrl(getIconSVGFromMimeType(mtype))}';this.classList.add('thumb-svg');" /> \
|
76
|
+
</div>
|
77
|
+
`;
|
78
|
+
}
|
79
|
+
else{
|
80
|
+
return `
|
81
|
+
<div class="thumb" id="${thumb_id}"> \
|
82
|
+
<img src="${getSafeIconUrl(getIconSVGFromMimeType(mtype))}" alt="${r.url}" class="thumb thumb-svg"/ >
|
83
|
+
</div>
|
84
|
+
`;
|
85
|
+
}
|
68
86
|
}
|
lfss/src/server.py
CHANGED
@@ -101,9 +101,11 @@ async def log_requests(request: Request, call_next):
|
|
101
101
|
response_time = end_time - start_time
|
102
102
|
response.headers["X-Response-Time"] = str(response_time)
|
103
103
|
|
104
|
+
if response.headers.get("X-Skip-Log", None) is not None:
|
105
|
+
return response
|
106
|
+
|
104
107
|
if response.status_code >= 400:
|
105
108
|
logger_failed_request.error(f"{request.method} {request.url.path} {response.status_code}")
|
106
|
-
|
107
109
|
await req_conn.log_request(
|
108
110
|
request_time_stamp,
|
109
111
|
request.method, request.url.path, response.status_code, response_time,
|
@@ -114,11 +116,20 @@ async def log_requests(request: Request, call_next):
|
|
114
116
|
response_size = int(response.headers.get("Content-Length", 0))
|
115
117
|
)
|
116
118
|
await req_conn.ensure_commit_once()
|
117
|
-
|
118
119
|
return response
|
119
120
|
|
121
|
+
def skip_request_log(fn):
|
122
|
+
@wraps(fn)
|
123
|
+
async def wrapper(*args, **kwargs):
|
124
|
+
response = await fn(*args, **kwargs)
|
125
|
+
assert isinstance(response, Response), "Response expected"
|
126
|
+
response.headers["X-Skip-Log"] = "1"
|
127
|
+
return response
|
128
|
+
return wrapper
|
129
|
+
|
120
130
|
router_fs = APIRouter(prefix="")
|
121
131
|
|
132
|
+
@skip_request_log
|
122
133
|
async def emit_thumbnail(
|
123
134
|
path: str, download: bool,
|
124
135
|
create_time: Optional[str] = None
|
@@ -128,7 +139,7 @@ async def emit_thumbnail(
|
|
128
139
|
else:
|
129
140
|
fname = path.split("/")[-1]
|
130
141
|
if (thumb_res := await get_thumb(path)) is None:
|
131
|
-
|
142
|
+
return Response(status_code=415, content="Thumbnail not supported")
|
132
143
|
thumb_blob, mime_type = thumb_res
|
133
144
|
disp = "inline" if not download else "attachment"
|
134
145
|
headers = {
|
lfss/src/thumb.py
CHANGED
@@ -5,6 +5,7 @@ from typing import Optional
|
|
5
5
|
from PIL import Image
|
6
6
|
from io import BytesIO
|
7
7
|
import aiosqlite
|
8
|
+
from contextlib import asynccontextmanager
|
8
9
|
|
9
10
|
async def _maybe_init_thumb(c: aiosqlite.Cursor):
|
10
11
|
await c.execute('''
|
@@ -14,6 +15,7 @@ async def _maybe_init_thumb(c: aiosqlite.Cursor):
|
|
14
15
|
thumb BLOB
|
15
16
|
)
|
16
17
|
''')
|
18
|
+
await c.execute('CREATE INDEX IF NOT EXISTS thumbs_path_idx ON thumbs (path)')
|
17
19
|
|
18
20
|
async def _get_cache_thumb(c: aiosqlite.Cursor, path: str, ctime: str) -> Optional[bytes]:
|
19
21
|
res = await c.execute('''
|
@@ -48,6 +50,13 @@ async def _delete_cache_thumb(c: aiosqlite.Cursor, path: str):
|
|
48
50
|
''', (path, ))
|
49
51
|
await c.execute('COMMIT')
|
50
52
|
|
53
|
+
@asynccontextmanager
|
54
|
+
async def cache_cursor():
|
55
|
+
async with aiosqlite.connect(THUMB_DB) as conn:
|
56
|
+
cur = await conn.cursor()
|
57
|
+
await _maybe_init_thumb(cur)
|
58
|
+
yield cur
|
59
|
+
|
51
60
|
async def get_thumb(path: str) -> Optional[tuple[bytes, str]]:
|
52
61
|
"""
|
53
62
|
returns [image bytes of thumbnail, mime type] if supported,
|
@@ -57,20 +66,17 @@ async def get_thumb(path: str) -> Optional[tuple[bytes, str]]:
|
|
57
66
|
if path.endswith('/'):
|
58
67
|
return None
|
59
68
|
|
60
|
-
async with
|
61
|
-
|
62
|
-
await
|
63
|
-
|
64
|
-
|
65
|
-
fconn = FileConn(main_c)
|
66
|
-
r = await fconn.get_file_record(path)
|
67
|
-
if r is None:
|
69
|
+
async with unique_cursor() as main_c:
|
70
|
+
fconn = FileConn(main_c)
|
71
|
+
r = await fconn.get_file_record(path)
|
72
|
+
if r is None:
|
73
|
+
async with cache_cursor() as cur:
|
68
74
|
await _delete_cache_thumb(cur, path)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
return None
|
75
|
+
raise FileNotFoundError(f'File not found: {path}')
|
76
|
+
if not r.mime_type.startswith('image/'):
|
77
|
+
return None
|
73
78
|
|
79
|
+
async with cache_cursor() as cur:
|
74
80
|
c_time = r.create_time
|
75
81
|
thumb_blob = await _get_cache_thumb(cur, path, c_time)
|
76
82
|
if thumb_blob is not None:
|
@@ -2,15 +2,16 @@ Readme.md,sha256=vsPotlwPAaHI5plh4aaszpi3rr7ZGDn7-wLdEYTWQ0k,1275
|
|
2
2
|
docs/Known_issues.md,sha256=rfdG3j1OJF-59S9E06VPyn0nZKbW-ybPxkoZ7MEZWp8,81
|
3
3
|
docs/Permission.md,sha256=X0VNfBKU52f93QYqcVyiBFJ3yURiSkhIo9S_5fdSgzM,2265
|
4
4
|
frontend/api.js,sha256=o1sP4rKxxnM-rebxnlMlPkhPHzKaVW4kZC7B4ufbOK4,8026
|
5
|
-
frontend/index.html,sha256=
|
5
|
+
frontend/index.html,sha256=i45ilkRCorqXP0bTfMiwT3QmmEF23V34lZJC1nODHLo,2862
|
6
6
|
frontend/info.css,sha256=Ny0N3GywQ3a9q1_Qph_QFEKB4fEnTe_2DJ1Y5OsLLmQ,595
|
7
7
|
frontend/info.js,sha256=WhOGaeqMoezEAfg4nIpK26hvejC7AZ-ZDLiJmRj0kDk,5758
|
8
8
|
frontend/popup.css,sha256=TJZYFW1ZcdD1IVTlNPYNtMWKPbN6XDbQ4hKBOFK8uLg,1284
|
9
9
|
frontend/popup.js,sha256=3PgaGZmxSdV1E-D_MWgcR7aHWkcsHA1BNKSOkmP66tA,5191
|
10
|
-
frontend/scripts.js,sha256=
|
11
|
-
frontend/
|
12
|
-
frontend/
|
13
|
-
frontend/thumb.
|
10
|
+
frontend/scripts.js,sha256=tmA_tG3bLEEqn0jTgZ-DkcEl6egxdrFYJmstb9eZMr0,21862
|
11
|
+
frontend/state.js,sha256=Dda-2G4QzyqdxffjJa3Lb7rgJOrg2LvJ3TCRcB8YCrU,1327
|
12
|
+
frontend/styles.css,sha256=krMo6Ulroi8pqEq1exQsFEU-FJqT9GzI8vyARiNF11k,4484
|
13
|
+
frontend/thumb.css,sha256=1i8wudiMWGwdrnTqs6yrS8YaiPeHPR-A2YqUNJN20Ok,165
|
14
|
+
frontend/thumb.js,sha256=6m8bscQpi2sYaLRir2ZeY1H_1ZRKFVss5P28AnLvIVQ,5486
|
14
15
|
frontend/utils.js,sha256=IYUZl77ugiXKcLxSNOWC4NSS0CdD5yRgUsDb665j0xM,2556
|
15
16
|
lfss/cli/balance.py,sha256=R2rbO2tg9TVnnQIVeU0GJVeMS-5LDhEdk4mbOE9qGq0,4121
|
16
17
|
lfss/cli/cli.py,sha256=LH1nx5wI1K2DZ3hvHz7oq5HcXVDoW2V6sr7q9gJ8gqo,4621
|
@@ -30,11 +31,11 @@ lfss/src/database.py,sha256=w2QPE3h1Lx0D0fUmdtu9s1XHpNp9p27zqm8AVeP2UVg,32476
|
|
30
31
|
lfss/src/datatype.py,sha256=WfrLALU_7wei5-i_b0TxY8xWI5mwxLUHFepHSps49zA,1767
|
31
32
|
lfss/src/error.py,sha256=imbhwnbhnI3HLhkbfICROe3F0gleKrOk4XnqHJDOtuI,285
|
32
33
|
lfss/src/log.py,sha256=u6WRZZsE7iOx6_CV2NHh1ugea26p408FI4WstZh896A,5139
|
33
|
-
lfss/src/server.py,sha256=
|
34
|
+
lfss/src/server.py,sha256=igkPC3gdJoIqcVTKBAKkVPRrclXR2ZNBdRIAEci4xMo,17717
|
34
35
|
lfss/src/stat.py,sha256=Wr-ug_JqtbSIf3XwQnv1xheGhsDTEOlLWuYoKO_26Jo,3201
|
35
|
-
lfss/src/thumb.py,sha256=
|
36
|
+
lfss/src/thumb.py,sha256=qjCNMpnCozMuzkhm-2uAYy1eAuYTeWG6xqs-13HX-7k,3266
|
36
37
|
lfss/src/utils.py,sha256=TBGYvgt6xMP8UC5wTGHAr9fmdhu0_gjOtxcSeyvGyVM,3918
|
37
|
-
lfss-0.7.
|
38
|
-
lfss-0.7.
|
39
|
-
lfss-0.7.
|
40
|
-
lfss-0.7.
|
38
|
+
lfss-0.7.15.dist-info/METADATA,sha256=s6jCttzD9bRauwKiKGNgrBouK5UDNEUMTgDHZBY_VfY,2021
|
39
|
+
lfss-0.7.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
40
|
+
lfss-0.7.15.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
|
41
|
+
lfss-0.7.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|