lfss 0.7.12__py3-none-any.whl → 0.7.13__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 CHANGED
@@ -24,9 +24,27 @@
24
24
  </div>
25
25
 
26
26
  <div class="container content" id="view">
27
- <div id="position-hint" class='disconnected'>
28
- <span></span>
29
- <label></label>
27
+ <div id="top-bar">
28
+ <div id="position-hint" class='disconnected'>
29
+ <span></span>
30
+ <label></label>
31
+ </div>
32
+ <div>
33
+ <span style="margin-right: 0.25rem; color: #999;">
34
+ Sort by</span>
35
+ <select id="sort-by-sel">
36
+ <option value="none">None</option>
37
+ <option value="name">Name</option>
38
+ <option value="size">Size</option>
39
+ <option value="access">Accessed</option>
40
+ <option value="create">Created</option>
41
+ <option value="mime">Type</option>
42
+ </select>
43
+ <select id="sort-order-sel">
44
+ <option value="asc">Ascending</option>
45
+ <option value="desc">Descending</option>
46
+ </select>
47
+ </div>
30
48
  </div>
31
49
  <table id="files">
32
50
  <thead>
frontend/scripts.js CHANGED
@@ -3,6 +3,7 @@ import { permMap } from './api.js';
3
3
  import { showFloatingWindowLineInput, showPopup } from './popup.js';
4
4
  import { formatSize, decodePathURI, ensurePathURI, getRandomString, cvtGMT2Local, debounce, encodePathURI, asHtmlText } from './utils.js';
5
5
  import { showInfoPanel, showDirInfoPanel } from './info.js';
6
+ import { makeThumbHtml } from './thumb.js';
6
7
 
7
8
  const conn = new Connector();
8
9
  let userRecord = null;
@@ -22,6 +23,8 @@ const uploadFileSelector = document.querySelector('#file-selector');
22
23
  const uploadFileNameInput = document.querySelector('#file-name');
23
24
  const uploadButton = document.querySelector('#upload-btn');
24
25
  const randomizeFnameButton = document.querySelector('#randomize-fname-btn');
26
+ const sortBySelect = document.querySelector('#sort-by-sel');
27
+ const sortOrderSelect = document.querySelector('#sort-order-sel');
25
28
 
26
29
  conn.config.endpoint = endpointInput.value;
27
30
  conn.config.token = tokenInput.value;
@@ -219,6 +222,40 @@ function maybeRefreshFileList(){
219
222
  }
220
223
  }
221
224
 
225
+ let sortBy = sortBySelect.value;
226
+ let sortOrder = sortOrderSelect.value;
227
+ /** @param {import('./api.js').DirectoryRecord} dirs */
228
+ function sortDirList(dirs){
229
+ if (sortBy === 'name'){
230
+ dirs.sort((a, b) => { return a.url.localeCompare(b.url); });
231
+ }
232
+ if (sortOrder === 'desc'){ dirs.reverse(); }
233
+ }
234
+ /** @param {import('./api.js').FileRecord} files */
235
+ function sortFileList(files){
236
+ function timestr2num(timestr){
237
+ return new Date(timestr).getTime();
238
+ }
239
+ if (sortBy === 'name'){
240
+ files.sort((a, b) => { return a.url.localeCompare(b.url); });
241
+ }
242
+ if (sortBy === 'size'){
243
+ files.sort((a, b) => { return a.file_size - b.file_size; });
244
+ }
245
+ if (sortBy === 'access'){
246
+ files.sort((a, b) => { return timestr2num(a.access_time) - timestr2num(b.access_time); });
247
+ }
248
+ if (sortBy === 'create'){
249
+ files.sort((a, b) => { return timestr2num(a.create_time) - timestr2num(b.create_time); });
250
+ }
251
+ if (sortBy === 'mime'){
252
+ files.sort((a, b) => { return a.mime_type.localeCompare(b.mime_type); });
253
+ }
254
+ if (sortOrder === 'desc'){ files.reverse(); }
255
+ }
256
+ sortBySelect.addEventListener('change', (elem) => {sortBy = elem.target.value; refreshFileList();});
257
+ sortOrderSelect.addEventListener('change', (elem) => {sortOrder = elem.target.value; refreshFileList();});
258
+
222
259
  function refreshFileList(){
223
260
  conn.listPath(pathInput.value)
224
261
  .then(data => {
@@ -232,6 +269,9 @@ function refreshFileList(){
232
269
  if (!data.dirs){ data.dirs = []; }
233
270
  if (!data.files){ data.files = []; }
234
271
 
272
+ sortDirList(data.dirs);
273
+ sortFileList(data.files);
274
+
235
275
  data.dirs.forEach(dir => {
236
276
  const tr = document.createElement('tr');
237
277
  const sizeTd = document.createElement('td');
@@ -253,10 +293,7 @@ function refreshFileList(){
253
293
  dirLink.href = '#';
254
294
  const nameDiv = document.createElement('div');
255
295
  nameDiv.classList.add('filename-container');
256
- const thumbImg = document.createElement('img');
257
- thumbImg.classList.add('thumb');
258
- thumbImg.src = conn.config.endpoint + '/' + ensureSlashEnd(dir.url) + '?token=' + conn.config.token + '&thumb=true';
259
- nameDiv.appendChild(thumbImg);
296
+ nameDiv.innerHTML = makeThumbHtml(conn, dir);
260
297
  nameDiv.appendChild(dirLink);
261
298
  nameTd.appendChild(nameDiv);
262
299
 
@@ -353,7 +390,7 @@ function refreshFileList(){
353
390
 
354
391
  nameTd.innerHTML = `
355
392
  <div class="filename-container">
356
- <img class="thumb" src="${conn.config.endpoint}/${file.url}?token=${conn.config.token}&thumb=true" />
393
+ ${makeThumbHtml(conn, file)}
357
394
  <span>${asHtmlText(fileName)}</span>
358
395
  </div>
359
396
  `
frontend/styles.css CHANGED
@@ -1,5 +1,6 @@
1
1
  @import "./popup.css";
2
2
  @import "./info.css";
3
+ @import "./thumb.css";
3
4
 
4
5
  body{
5
6
  font-family: Arial, sans-serif;
@@ -120,6 +121,13 @@ input#path{
120
121
  cursor: pointer;
121
122
  }
122
123
 
124
+ div#top-bar{
125
+ display: flex;
126
+ flex-direction: row;
127
+ justify-content: space-between;
128
+ align-items: center;
129
+ gap: 1rem;
130
+ }
123
131
  div#position-hint{
124
132
  color: rgb(138, 138, 138);
125
133
  border-radius: 0.5rem;
@@ -186,14 +194,8 @@ div.filename-container{
186
194
  flex-direction: row;
187
195
  align-items: center;
188
196
  gap: 0.5rem;
189
- height: 32px; /* same as thumbnail */
197
+ height: 32px;
190
198
  }
191
- img.thumb{
192
- max-height: 32px; /* smaller than backend */
193
- max-width: 32px;
194
- border-radius: 0.25rem;
195
- }
196
-
197
199
  label#upload-file-prefix{
198
200
  width: 800px;
199
201
  text-align: right;
frontend/thumb.css ADDED
@@ -0,0 +1,16 @@
1
+
2
+ div.thumb{
3
+ height: 32px;
4
+ width: 32px;
5
+ border-radius: 0.25rem;
6
+ display: contents;
7
+ }
8
+ img.thumb{
9
+ max-height: 32px; /* smaller than backend */
10
+ max-width: 32px;
11
+ border-radius: 0.25rem;
12
+ }
13
+ img.thumb-svg{
14
+ /* dark blue */
15
+ filter: invert(30%) sepia(100%) saturate(80%) hue-rotate(180deg);
16
+ }
frontend/thumb.js ADDED
@@ -0,0 +1,68 @@
1
+
2
+ /**
3
+ * @import { FileRecord, DirectoryRecord } from './api.js'
4
+ */
5
+ import Connector from './api.js';
6
+
7
+ // from: https://pictogrammers.com/library/mdi/
8
+ const ICON_FOLDER = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>folder-outline</title><path d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /></svg>';
9
+ const ICON_FILE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>file-document-outline</title><path d="M6,2A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6M6,4H13V9H18V20H6V4M8,12V14H16V12H8M8,16V18H13V16H8Z" /></svg>'
10
+ const ICON_PDF = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>file-pdf-box</title><path d="M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3M9.5 11.5C9.5 12.3 8.8 13 8 13H7V15H5.5V9H8C8.8 9 9.5 9.7 9.5 10.5V11.5M14.5 13.5C14.5 14.3 13.8 15 13 15H10.5V9H13C13.8 9 14.5 9.7 14.5 10.5V13.5M18.5 10.5H17V11.5H18.5V13H17V15H15.5V9H18.5V10.5M12 10.5H13V13.5H12V10.5M7 10.5H8V11.5H7V10.5Z" /></svg>'
11
+ const ICON_EXE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>application-brackets-outline</title><path d="M9.5,8.5L11,10L8,13L11,16L9.5,17.5L5,13L9.5,8.5M14.5,17.5L13,16L16,13L13,10L14.5,8.5L19,13L14.5,17.5M21,2H3A2,2 0 0,0 1,4V20A2,2 0 0,0 3,22H21A2,2 0 0,0 23,20V4A2,2 0 0,0 21,2M21,20H3V6H21V20Z" /></svg>'
12
+ const ICON_ZIP = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>zip-box-outline</title><path d="M12 17V15H14V17H12M14 13V11H12V13H14M14 9V7H12V9H14M10 11H12V9H10V11M10 15H12V13H10V15M21 5V19C21 20.1 20.1 21 19 21H5C3.9 21 3 20.1 3 19V5C3 3.9 3.9 3 5 3H19C20.1 3 21 3.9 21 5M19 5H12V7H10V5H5V19H19V5Z" /></svg>'
13
+ const ICON_CODE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>file-code-outline</title><path d="M14 2H6C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H18C19.11 22 20 21.11 20 20V8L14 2M18 20H6V4H13V9H18V20M9.54 15.65L11.63 17.74L10.35 19L7 15.65L10.35 12.3L11.63 13.56L9.54 15.65M17 15.65L13.65 19L12.38 17.74L14.47 15.65L12.38 13.56L13.65 12.3L17 15.65Z" /></svg>'
14
+ const ICON_VIDEO = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>television-play</title><path d="M21,3H3C1.89,3 1,3.89 1,5V17A2,2 0 0,0 3,19H8V21H16V19H21A2,2 0 0,0 23,17V5C23,3.89 22.1,3 21,3M21,17H3V5H21M16,11L9,15V7" /></svg>'
15
+ const ICON_IMAGE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>image-outline</title><path d="M19,19H5V5H19M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M13.96,12.29L11.21,15.83L9.25,13.47L6.5,17H17.5L13.96,12.29Z" /></svg>'
16
+
17
+ function getIconSVGFromMimeType(mimeType){
18
+ if (mimeType == 'directory'){
19
+ return ICON_FOLDER;
20
+ }
21
+ if (mimeType.startsWith('video/')){
22
+ return ICON_VIDEO;
23
+ }
24
+ if (mimeType.startsWith('image/')){
25
+ return ICON_IMAGE;
26
+ }
27
+ switch (mimeType){
28
+ case
29
+ 'application/pdf' || 'application/x-pdf':
30
+ return ICON_PDF;
31
+ case
32
+ 'application/x-msdownload' || 'application/x-msdos-program' || 'application/x-msi' || 'application/x-ms-wim' || 'application/octet-stream' || 'application/x-apple-diskimage':
33
+ return ICON_EXE;
34
+ case
35
+ 'application/zip' || 'application/x-zip-compressed' || 'application/x-7z-compressed' || 'application/x-rar-compressed' || 'application/x-tar' || 'application/x-gzip':
36
+ return ICON_ZIP;
37
+ case
38
+ 'text/x-python' || 'text/x-c' || 'text/x-c++' || 'text/x-java' || 'text/x-javascript' || 'text/x-php' || 'text/x-rust' || 'text/x-go' || 'text/x-csharp' || 'text/x-typescript' ||
39
+ 'text/x-html' || 'text/x-css' || 'text/x-sql' || 'text/x-xml' || 'text/x-yaml' || 'text/x-json' || 'text/x-markdown' || 'text/x-shellscript' ||
40
+ 'text/x-bat' || 'text/x-powershell' || 'text/x-bash' || 'text/x-perl' || 'text/x-ruby' || 'text/x-lua' || 'text/x-tcl' || 'text/x-lisp' ||
41
+ 'text/x-haskell' || 'text/x-elm' || 'text/x-crystal' || 'text/x-nim' || 'text/x-zig':
42
+ return ICON_CODE;
43
+ default:
44
+ return ICON_FILE;
45
+ }
46
+ }
47
+
48
+ function getSafeIconUrl(icon_str){
49
+ return 'data:image/svg+xml,' + encodeURIComponent(icon_str);
50
+ }
51
+
52
+ let thumb_counter = 0;
53
+ /**
54
+ * @param {Connector} c
55
+ * @param {FileRecord | DirectoryRecord} r
56
+ * @returns {string}
57
+ */
58
+ export function makeThumbHtml(c, r){
59
+ const token = c.config.token;
60
+ const mtype = r.mime_type? r.mime_type : 'directory';
61
+ const thumb_id = `thumb-${thumb_counter++}`;
62
+ return `
63
+ <div class="thumb" id="${thumb_id}"> \
64
+ <img src="${c.config.endpoint}/${r.url}?token=${token}&thumb=true" alt="${r.url}" class="thumb" \
65
+ onerror="this.src='${getSafeIconUrl(getIconSVGFromMimeType(mtype))}';this.classList.add('thumb-svg');" \
66
+ </div>
67
+ `;
68
+ }
lfss/src/config.py CHANGED
@@ -21,3 +21,6 @@ else:
21
21
  MAX_FILE_BYTES = 512 * 1024 * 1024 # 512MB
22
22
  MAX_BUNDLE_BYTES = 512 * 1024 * 1024 # 512MB
23
23
  CHUNK_SIZE = 1024 * 1024 # 1MB chunks for streaming (on large files)
24
+
25
+ THUMB_DB = DATA_HOME / 'thumbs.db'
26
+ THUMB_SIZE = (48, 48)
lfss/src/server.py CHANGED
@@ -127,7 +127,9 @@ async def emit_thumbnail(
127
127
  fname = path.split("/")[-2]
128
128
  else:
129
129
  fname = path.split("/")[-1]
130
- thumb_blob, mime_type = await get_thumb(path)
130
+ if (thumb_res := await get_thumb(path)) is None:
131
+ raise HTTPException(status_code=415, detail="Thumbnail not supported")
132
+ thumb_blob, mime_type = thumb_res
131
133
  disp = "inline" if not download else "attachment"
132
134
  headers = {
133
135
  "Content-Disposition": f"{disp}; filename={fname}.thumb.jpg",
lfss/src/thumb.py CHANGED
@@ -1,4 +1,4 @@
1
- from lfss.src.config import DATA_HOME
1
+ from lfss.src.config import THUMB_DB, THUMB_SIZE
2
2
  from lfss.src.database import FileConn
3
3
  from lfss.src.connection_pool import unique_cursor
4
4
  from typing import Optional
@@ -6,26 +6,6 @@ from PIL import Image
6
6
  from io import BytesIO
7
7
  import aiosqlite
8
8
 
9
- def prepare_svg(svg_str: str):
10
- """
11
- Injects grey color to the svg string, and encodes it to bytes
12
- """
13
- color = "#666"
14
- x = svg_str.replace('<path', '<path fill="{}"'.format(color))
15
- x = x.replace('<svg', '<svg fill="{}"'.format(color))
16
- return x.encode()
17
-
18
- # from: https://pictogrammers.com/library/mdi/
19
- ICON_FOLER = prepare_svg('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>folder-outline</title><path d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /></svg>')
20
- ICON_FILE = prepare_svg('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>file-document-outline</title><path d="M6,2A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6M6,4H13V9H18V20H6V4M8,12V14H16V12H8M8,16V18H13V16H8Z" /></svg>')
21
- ICON_PDF = prepare_svg('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>file-pdf-box</title><path d="M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3M9.5 11.5C9.5 12.3 8.8 13 8 13H7V15H5.5V9H8C8.8 9 9.5 9.7 9.5 10.5V11.5M14.5 13.5C14.5 14.3 13.8 15 13 15H10.5V9H13C13.8 9 14.5 9.7 14.5 10.5V13.5M18.5 10.5H17V11.5H18.5V13H17V15H15.5V9H18.5V10.5M12 10.5H13V13.5H12V10.5M7 10.5H8V11.5H7V10.5Z" /></svg>')
22
- ICON_EXE = prepare_svg('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>application-brackets-outline</title><path d="M9.5,8.5L11,10L8,13L11,16L9.5,17.5L5,13L9.5,8.5M14.5,17.5L13,16L16,13L13,10L14.5,8.5L19,13L14.5,17.5M21,2H3A2,2 0 0,0 1,4V20A2,2 0 0,0 3,22H21A2,2 0 0,0 23,20V4A2,2 0 0,0 21,2M21,20H3V6H21V20Z" /></svg>')
23
- ICON_ZIP = prepare_svg('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>zip-box-outline</title><path d="M12 17V15H14V17H12M14 13V11H12V13H14M14 9V7H12V9H14M10 11H12V9H10V11M10 15H12V13H10V15M21 5V19C21 20.1 20.1 21 19 21H5C3.9 21 3 20.1 3 19V5C3 3.9 3.9 3 5 3H19C20.1 3 21 3.9 21 5M19 5H12V7H10V5H5V19H19V5Z" /></svg>')
24
- ICON_CODE = prepare_svg('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>file-code-outline</title><path d="M14 2H6C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H18C19.11 22 20 21.11 20 20V8L14 2M18 20H6V4H13V9H18V20M9.54 15.65L11.63 17.74L10.35 19L7 15.65L10.35 12.3L11.63 13.56L9.54 15.65M17 15.65L13.65 19L12.38 17.74L14.47 15.65L12.38 13.56L13.65 12.3L17 15.65Z" /></svg>')
25
-
26
- THUMB_DB = DATA_HOME / 'thumbs.db'
27
- THUMB_SIZE = (48, 48)
28
-
29
9
  async def _maybe_init_thumb(c: aiosqlite.Cursor):
30
10
  await c.execute('''
31
11
  CREATE TABLE IF NOT EXISTS thumbs (
@@ -68,14 +48,14 @@ async def _delete_cache_thumb(c: aiosqlite.Cursor, path: str):
68
48
  ''', (path, ))
69
49
  await c.execute('COMMIT')
70
50
 
71
- async def get_thumb(path: str) -> tuple[bytes, str]:
51
+ async def get_thumb(path: str) -> Optional[tuple[bytes, str]]:
72
52
  """
73
53
  returns [image bytes of thumbnail, mime type] if supported,
74
54
  or None if not supported.
75
55
  Raises FileNotFoundError if file does not exist
76
56
  """
77
57
  if path.endswith('/'):
78
- return ICON_FOLER, 'image/svg+xml'
58
+ return None
79
59
 
80
60
  async with aiosqlite.connect(THUMB_DB) as conn:
81
61
  cur = await conn.cursor()
@@ -89,17 +69,7 @@ async def get_thumb(path: str) -> tuple[bytes, str]:
89
69
  raise FileNotFoundError(f'File not found: {path}')
90
70
 
91
71
  if not r.mime_type.startswith('image/'):
92
- match r.mime_type:
93
- case 'application/pdf' | 'application/x-pdf':
94
- return ICON_PDF, 'image/svg+xml'
95
- case 'application/x-msdownload' | 'application/x-msdos-program' | 'application/x-msi' | 'application/octet-stream':
96
- return ICON_EXE, 'image/svg+xml'
97
- case 'application/zip' | 'application/x-zip-compressed' | 'application/x-zip' | 'application/x-compressed' | 'application/x-compress':
98
- return ICON_ZIP, 'image/svg+xml'
99
- case 'text/plain' | 'text/x-python' | 'text/x-c' | 'text/x-c++' | 'text/x-java' | 'text/x-php' | 'text/x-shellscript' | 'text/x-perl' | 'text/x-ruby' | 'text/x-go' | 'text/x-rust' | 'text/x-haskell' | 'text/x-lisp' | 'text/x-lua' | 'text/x-tcl' | 'text/x-sql' | 'text/x-yaml' | 'text/x-xml' | 'text/x-markdown' | 'text/x-tex' | 'text/x-asm' | 'text/x-fortran' | 'text/x-pascal' | 'text/x-erlang' | 'text/x-ocaml' | 'text/x-matlab' | 'text/x-csharp' | 'text/x-swift' | 'text/x-kotlin' | 'text/x-dart' | 'text/x-julia' | 'text/x-scala' | 'text/x-clojure' | 'text/x-elm' | 'text/x-crystal' | 'text/x-nim' | 'text/x-zig':
100
- return ICON_CODE, 'image/svg+xml'
101
- case _:
102
- return ICON_FILE, 'image/svg+xml'
72
+ return None
103
73
 
104
74
  c_time = r.create_time
105
75
  thumb_blob = await _get_cache_thumb(cur, path, c_time)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.7.12
3
+ Version: 0.7.13
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: li, mengxun
@@ -2,13 +2,15 @@ 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=Mem8de9vwmZoe4x1DKqpu_aFgIBURqT3mIGdeOOTbIs,2051
5
+ frontend/index.html,sha256=29btC2xxSaCBhZ4ozAlK-lC7Q4FcsuRgfAop3S9nBQ8,2844
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=4LHc1M2QwxtCJsbShxXu0uKYBY8auv84JuYcd1zwpQM,21069
11
- frontend/styles.css,sha256=ghtQLSi7-zS8Yuac49TNiGVxNgggh-R3SM7HDYQM97c,4482
10
+ frontend/scripts.js,sha256=hzX5kX2txIS3x2OLuYjrI3j-T5Na1M-_ktz4Rkxara4,22331
11
+ frontend/styles.css,sha256=Xl8FTDS32DQ_dLVX-UIgvAVJlKx8csYEyeNFX5o1eUY,4502
12
+ frontend/thumb.css,sha256=uVTt-8gDdgNiNmqJnsTVK1gvixEUabM2SRd3Ed_0Kag,319
13
+ frontend/thumb.js,sha256=m0-Nip5bgtipdBz1GtRBk-0aaJ-4wKhlT9rEN_0oBh8,4881
12
14
  frontend/utils.js,sha256=IYUZl77ugiXKcLxSNOWC4NSS0CdD5yRgUsDb665j0xM,2556
13
15
  lfss/cli/balance.py,sha256=R2rbO2tg9TVnnQIVeU0GJVeMS-5LDhEdk4mbOE9qGq0,4121
14
16
  lfss/cli/cli.py,sha256=LH1nx5wI1K2DZ3hvHz7oq5HcXVDoW2V6sr7q9gJ8gqo,4621
@@ -22,17 +24,17 @@ lfss/sql/init.sql,sha256=C-JtQAlaOjESI8uoF1Y_9dKukEVSw5Ll-7yA3gG-XHU,1210
22
24
  lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
23
25
  lfss/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
26
  lfss/src/bounded_pool.py,sha256=BI1dU-MBf82TMwJBYbjhEty7w1jIUKc5Bn9SnZ_-hoY,1288
25
- lfss/src/config.py,sha256=2qN2nu3onP1M7U6ENC0ZbVVRE90aDomTcQ1GsBLLHF8,800
27
+ lfss/src/config.py,sha256=K1b5clNRO4EzxDG-p6U5aRLDaq-Up0tDHfn_D79D0ns,857
26
28
  lfss/src/connection_pool.py,sha256=r4Ho5d_Gd4S_KbT7515UJoiyfIgS6xyttqMsKqOfaIg,5190
27
29
  lfss/src/database.py,sha256=w2QPE3h1Lx0D0fUmdtu9s1XHpNp9p27zqm8AVeP2UVg,32476
28
30
  lfss/src/datatype.py,sha256=WfrLALU_7wei5-i_b0TxY8xWI5mwxLUHFepHSps49zA,1767
29
31
  lfss/src/error.py,sha256=imbhwnbhnI3HLhkbfICROe3F0gleKrOk4XnqHJDOtuI,285
30
32
  lfss/src/log.py,sha256=u6WRZZsE7iOx6_CV2NHh1ugea26p408FI4WstZh896A,5139
31
- lfss/src/server.py,sha256=hFUeg14-G2nIfY2sD9fL_3YvRv7TuuGQk932HCT_NPs,17224
33
+ lfss/src/server.py,sha256=d5PdRXR2LefvbVpBk7qC-MCBXP_Z3hGL_vBceSznpw4,17344
32
34
  lfss/src/stat.py,sha256=Wr-ug_JqtbSIf3XwQnv1xheGhsDTEOlLWuYoKO_26Jo,3201
33
- lfss/src/thumb.py,sha256=KnKvjP3RwhjqFZxZabdUTuk5l9fv3AXO2YUgu0Xzvps,6770
35
+ lfss/src/thumb.py,sha256=QhFmPCBZ5fDwtNC07kt-PKttDVNtx2x0HFXphORlkes,3028
34
36
  lfss/src/utils.py,sha256=TBGYvgt6xMP8UC5wTGHAr9fmdhu0_gjOtxcSeyvGyVM,3918
35
- lfss-0.7.12.dist-info/METADATA,sha256=bFSR41dbc7Y_Ui4ALbYEqbCjvmpvh4WNBmonJ3Pn868,2021
36
- lfss-0.7.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
37
- lfss-0.7.12.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
38
- lfss-0.7.12.dist-info/RECORD,,
37
+ lfss-0.7.13.dist-info/METADATA,sha256=M5e-IMIsEuQaVHXwm3osFRM-yzdk6PDod6uTZnImyVI,2021
38
+ lfss-0.7.13.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
39
+ lfss-0.7.13.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
40
+ lfss-0.7.13.dist-info/RECORD,,
File without changes