lfss 0.7.14__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/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
- const conn = new Connector();
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.config.endpoint = endpointInput.value;
30
- conn.config.token = tokenInput.value;
30
+ const conn = store.conn;
31
+ store.init();
31
32
 
32
33
  {
33
- const endpoint = window.localStorage.getItem('endpoint');
34
- if (endpoint){
35
- endpointInput.value = endpoint;
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
- window.localStorage.setItem('path', pathInput.value);
47
+ store.dirpath = pathInput.value;
56
48
  maybeRefreshFileList();
57
49
  }
58
50
 
59
51
  endpointInput.addEventListener('blur', () => {
60
- conn.config.endpoint = endpointInput.value;
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
- conn.config.token = tokenInput.value;
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(pathInput.value + fileName);
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 = pathInput.value;
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 = pathInput.value + uploadFileNameInput.value;
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
- pathInput.value && pathInput.value.length > 0 && pathInput.value.endsWith('/')
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 (sortBy === 'name'){
217
+ if (store.sortby === 'name'){
230
218
  dirs.sort((a, b) => { return a.url.localeCompare(b.url); });
231
219
  }
232
- if (sortOrder === 'desc'){ dirs.reverse(); }
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 (sortBy === 'name'){
227
+ if (store.sortby === 'name'){
240
228
  files.sort((a, b) => { return a.url.localeCompare(b.url); });
241
229
  }
242
- if (sortBy === 'size'){
230
+ if (store.sortby === 'size'){
243
231
  files.sort((a, b) => { return a.file_size - b.file_size; });
244
232
  }
245
- if (sortBy === 'access'){
233
+ if (store.sortby === 'access'){
246
234
  files.sort((a, b) => { return timestr2num(a.access_time) - timestr2num(b.access_time); });
247
235
  }
248
- if (sortBy === 'create'){
236
+ if (store.sortby === 'create'){
249
237
  files.sort((a, b) => { return timestr2num(a.create_time) - timestr2num(b.create_time); });
250
238
  }
251
- if (sortBy === 'mime'){
239
+ if (store.sortby === 'mime'){
252
240
  files.sort((a, b) => { return a.mime_type.localeCompare(b.mime_type); });
253
241
  }
254
- if (sortOrder === 'desc'){ files.reverse(); }
242
+ if (store.sortorder === 'desc'){ files.reverse(); }
255
243
  }
256
- sortBySelect.addEventListener('change', (elem) => {sortBy = elem.target.value; refreshFileList();});
257
- sortOrderSelect.addEventListener('change', (elem) => {sortOrder = elem.target.value; refreshFileList();});
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(pathInput.value)
248
+ conn.listPath(store.dirpath)
261
249
  .then(data => {
262
250
  pathHintDiv.classList.remove('disconnected');
263
251
  pathHintDiv.classList.add('connected');
264
- pathHintLabel.textContent = pathInput.value;
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 = pathInput.value;
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 (endpointInput.value && tokenInput.value){
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/thumb.js CHANGED
@@ -24,25 +24,26 @@ function getIconSVGFromMimeType(mimeType){
24
24
  if (mimeType.startsWith('image/')){
25
25
  return ICON_IMAGE;
26
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/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-csrc" || "text/x-c++src" || "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-rustsrc" || "application/x-ruby" || "text/x-asm":
42
- return ICON_CODE;
43
- default:
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){
@@ -66,10 +67,20 @@ export function makeThumbHtml(c, r){
66
67
  const mtype = r.mime_type? r.mime_type : 'directory';
67
68
  const thumb_id = `thumb-${thumb_counter++}`;
68
69
  const url = mtype == 'directory'? ensureSlashEnd(r.url) : r.url;
69
- return `
70
- <div class="thumb" id="${thumb_id}"> \
71
- <img src="${c.config.endpoint}/${url}?token=${token}&thumb=true" alt="${r.url}" class="thumb" \
72
- onerror="this.src='${getSafeIconUrl(getIconSVGFromMimeType(mtype))}';this.classList.add('thumb-svg');" \
73
- </div>
74
- `;
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
+ }
75
86
  }
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('''
@@ -49,6 +50,13 @@ async def _delete_cache_thumb(c: aiosqlite.Cursor, path: str):
49
50
  ''', (path, ))
50
51
  await c.execute('COMMIT')
51
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
+
52
60
  async def get_thumb(path: str) -> Optional[tuple[bytes, str]]:
53
61
  """
54
62
  returns [image bytes of thumbnail, mime type] if supported,
@@ -58,20 +66,17 @@ async def get_thumb(path: str) -> Optional[tuple[bytes, str]]:
58
66
  if path.endswith('/'):
59
67
  return None
60
68
 
61
- async with aiosqlite.connect(THUMB_DB) as conn:
62
- cur = await conn.cursor()
63
- await _maybe_init_thumb(cur)
64
-
65
- async with unique_cursor() as main_c:
66
- fconn = FileConn(main_c)
67
- r = await fconn.get_file_record(path)
68
- 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:
69
74
  await _delete_cache_thumb(cur, path)
70
- raise FileNotFoundError(f'File not found: {path}')
71
-
72
- if not r.mime_type.startswith('image/'):
73
- return None
75
+ raise FileNotFoundError(f'File not found: {path}')
76
+ if not r.mime_type.startswith('image/'):
77
+ return None
74
78
 
79
+ async with cache_cursor() as cur:
75
80
  c_time = r.create_time
76
81
  thumb_blob = await _get_cache_thumb(cur, path, c_time)
77
82
  if thumb_blob is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.7.14
3
+ Version: 0.7.15
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: li, mengxun
@@ -7,10 +7,11 @@ 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=hzX5kX2txIS3x2OLuYjrI3j-T5Na1M-_ktz4Rkxara4,22331
10
+ frontend/scripts.js,sha256=tmA_tG3bLEEqn0jTgZ-DkcEl6egxdrFYJmstb9eZMr0,21862
11
+ frontend/state.js,sha256=Dda-2G4QzyqdxffjJa3Lb7rgJOrg2LvJ3TCRcB8YCrU,1327
11
12
  frontend/styles.css,sha256=krMo6Ulroi8pqEq1exQsFEU-FJqT9GzI8vyARiNF11k,4484
12
13
  frontend/thumb.css,sha256=1i8wudiMWGwdrnTqs6yrS8YaiPeHPR-A2YqUNJN20Ok,165
13
- frontend/thumb.js,sha256=MFAr_gOB-L4Su7JsnR8sy9TqP2oHR31AJAWi8nH4bXg,5296
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
@@ -32,9 +33,9 @@ lfss/src/error.py,sha256=imbhwnbhnI3HLhkbfICROe3F0gleKrOk4XnqHJDOtuI,285
32
33
  lfss/src/log.py,sha256=u6WRZZsE7iOx6_CV2NHh1ugea26p408FI4WstZh896A,5139
33
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=Ml4vQEWK3R-tVAAFDt6GZUteZBUM1Q0QPVtU1zn7G44,3111
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.14.dist-info/METADATA,sha256=ol6x7IVBcz2KtNZPPXULNiW_sztyuNvm3osrAkq4_Lk,2021
38
- lfss-0.7.14.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
39
- lfss-0.7.14.dist-info/entry_points.txt,sha256=VJ8svMz7RLtMCgNk99CElx7zo7M-N-z7BWDVw2HA92E,205
40
- lfss-0.7.14.dist-info/RECORD,,
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