ASMRManager 2.2.1__tar.gz → 2.2.3__tar.gz

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.
Files changed (70) hide show
  1. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/PKG-INFO +6 -5
  2. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/README.md +5 -4
  3. asmrmanager-2.2.3/asmrmanager/_version.py +1 -0
  4. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/dl.py +17 -3
  5. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/file.py +52 -16
  6. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/fileconverter.py +6 -4
  7. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/vtt2lrc.py +3 -3
  8. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/config.py +2 -1
  9. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/manager.py +25 -18
  10. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/config.example.toml +20 -0
  11. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/pyproject.toml +1 -1
  12. asmrmanager-2.2.1/asmrmanager/_version.py +0 -1
  13. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/LICENSE +0 -0
  14. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/__init__.py +0 -0
  15. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/__main__.py +0 -0
  16. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/core.py +0 -0
  17. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/hold.py +0 -0
  18. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/info.py +0 -0
  19. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/main.py +0 -0
  20. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/pl.py +0 -0
  21. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/play.py +0 -0
  22. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/query.py +0 -0
  23. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/review.py +0 -0
  24. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/sql.py +0 -0
  25. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/utils.py +0 -0
  26. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/view.py +0 -0
  27. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/cli/which.py +0 -0
  28. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/__init__.py +0 -0
  29. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/browse_params.py +0 -0
  30. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/download_params.py +0 -0
  31. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/output.py +0 -0
  32. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/parse_filter.py +0 -0
  33. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/rj_parse.py +0 -0
  34. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/select.py +0 -0
  35. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/common/types.py +0 -0
  36. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/__init__.py +0 -0
  37. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/database.py +0 -0
  38. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/engine.py +0 -0
  39. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/manage.py +0 -0
  40. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/orm_type.py +0 -0
  41. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/q_func.py +0 -0
  42. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/utils/__init__.py +0 -0
  43. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/utils/add2db.py +0 -0
  44. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/database/utils/uuid_sqlite.py +0 -0
  45. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/__init__.py +0 -0
  46. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/appdirs_.py +0 -0
  47. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/exceptions.py +0 -0
  48. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/file_zipper.py +0 -0
  49. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/sqls.example/circle_name.sql +0 -0
  50. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/sqls.example/score.sql +0 -0
  51. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/sqls.example/search.sql +0 -0
  52. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/sqls.example/tags.sql +0 -0
  53. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/sqls.example/test.sql +0 -0
  54. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/resources/sqls.example/vas.sql +0 -0
  55. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/filemanager/utils.py +0 -0
  56. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/logger/__init__.py +0 -0
  57. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/__init__.py +0 -0
  58. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/lrcparse.py +0 -0
  59. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/main.css +0 -0
  60. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/main.py +0 -0
  61. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/player/base.py +0 -0
  62. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/player/mpdplayer.py +0 -0
  63. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/lrcplayer/player/pygameplayer.py +0 -0
  64. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/__init__.py +0 -0
  65. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/asmrapi.py +0 -0
  66. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/downloader.py +0 -0
  67. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/interface.py +0 -0
  68. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/playlist.py +0 -0
  69. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/utils/IDMHelper.py +0 -0
  70. {asmrmanager-2.2.1 → asmrmanager-2.2.3}/asmrmanager/spider/utils/aria2_downloader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ASMRManager
3
- Version: 2.2.1
3
+ Version: 2.2.3
4
4
  Summary: download, manage and play the voices on asmr.one
5
5
  Keywords: asmr,downloader,music,player,manager,cli,tui,commandline,terminal
6
6
  Author-Email: SLQY <sqiyel@gmail.com>
@@ -48,8 +48,9 @@ Description-Content-Type: text/markdown
48
48
 
49
49
  ### 下载
50
50
 
51
- 支持网站所支持的所有索引方式(关键词,标签,会社,价格,声优,日期,年龄分级等等),以及排序方式。
51
+ 支持网站所支持的所有索引方式(关键词,标签,会社,价格,声优,日期,年龄分级等等)以及排序方式。
52
52
  目前仅支持调用 IDM 或 aria2 下载。
53
+ 支持根据根据文件名对下载的文件进行过滤,以及对音频文件的格式转换。
53
54
 
54
55
  ```
55
56
  > asmr dl search --help
@@ -124,8 +125,6 @@ Options:
124
125
 
125
126
  ## 使用方法
126
127
 
127
- <!-- **注意**:由于网站最近更新了针对BJ和VJ的支持,本项目进行了很大的重构,2.0.0 版本暂并不保证稳定运行,如果没有对BJ和VJ的需求,可以考虑继续使用1代的最后一个版本`pip install ASMRManager[依赖]==1.9.1` -->
128
-
129
128
  本工具支持 `python >= 3.10`, 安装方法如下:
130
129
 
131
130
  ```shell
@@ -158,9 +157,11 @@ pip install ASMRManager[依赖]
158
157
  常用的命令有:
159
158
  - `dl search` 搜索并下载。
160
159
  - `info` 搜索某个 RJID 的具体信息
160
+ - `file check` 检查下载目录下的文件是否按照规则被正确下载
161
+ - `file store` 将下载文件转移到存储目录(STORAGE_PATH),并执行相应文件格式转换(详情见config.toml的before_store字段)
161
162
  - `view` 将选择文件并移动到 VIEW_PATH
162
- - `review` 为某个作品评分并评论(本地)
163
163
  - `pl add` 将某个音声添加到用户的云端播放列表(配合 `pl create` 使用)
164
+ - `review` 为某个作品评分并评论(本地)
164
165
 
165
166
  > 使用命令时,如果不输入 RJID ,将会自动使用上一次命令的RJID。
166
167
 
@@ -6,8 +6,9 @@
6
6
 
7
7
  ### 下载
8
8
 
9
- 支持网站所支持的所有索引方式(关键词,标签,会社,价格,声优,日期,年龄分级等等),以及排序方式。
9
+ 支持网站所支持的所有索引方式(关键词,标签,会社,价格,声优,日期,年龄分级等等)以及排序方式。
10
10
  目前仅支持调用 IDM 或 aria2 下载。
11
+ 支持根据根据文件名对下载的文件进行过滤,以及对音频文件的格式转换。
11
12
 
12
13
  ```
13
14
  > asmr dl search --help
@@ -82,8 +83,6 @@ Options:
82
83
 
83
84
  ## 使用方法
84
85
 
85
- <!-- **注意**:由于网站最近更新了针对BJ和VJ的支持,本项目进行了很大的重构,2.0.0 版本暂并不保证稳定运行,如果没有对BJ和VJ的需求,可以考虑继续使用1代的最后一个版本`pip install ASMRManager[依赖]==1.9.1` -->
86
-
87
86
  本工具支持 `python >= 3.10`, 安装方法如下:
88
87
 
89
88
  ```shell
@@ -116,9 +115,11 @@ pip install ASMRManager[依赖]
116
115
  常用的命令有:
117
116
  - `dl search` 搜索并下载。
118
117
  - `info` 搜索某个 RJID 的具体信息
118
+ - `file check` 检查下载目录下的文件是否按照规则被正确下载
119
+ - `file store` 将下载文件转移到存储目录(STORAGE_PATH),并执行相应文件格式转换(详情见config.toml的before_store字段)
119
120
  - `view` 将选择文件并移动到 VIEW_PATH
120
- - `review` 为某个作品评分并评论(本地)
121
121
  - `pl add` 将某个音声添加到用户的云端播放列表(配合 `pl create` 使用)
122
+ - `review` 为某个作品评分并评论(本地)
122
123
 
123
124
  > 使用命令时,如果不输入 RJID ,将会自动使用上一次命令的RJID。
124
125
 
@@ -0,0 +1 @@
1
+ __version__ = "2.2.3"
@@ -93,7 +93,11 @@ def check(source_ids: List[LocalSourceID]):
93
93
 
94
94
 
95
95
  @click.command()
96
- @click.argument("text", type=str, default="")
96
+ @click.argument(
97
+ "keywords",
98
+ type=str,
99
+ nargs=-1,
100
+ )
97
101
  @click.option(
98
102
  "--tags", "-t", type=str, multiple=True, help="tags to include[multiple]"
99
103
  )
@@ -163,7 +167,7 @@ def check(source_ids: List[LocalSourceID]):
163
167
  @browse_param_options
164
168
  @download_param_options
165
169
  def search(
166
- text: str,
170
+ keywords: Tuple[str],
167
171
  tags: Tuple[str],
168
172
  vas: Tuple[str],
169
173
  circle: str | None,
@@ -182,6 +186,10 @@ def search(
182
186
  """
183
187
  search and download ASMR
184
188
 
189
+ The keywords argument is used to filter the title of an ASMR.
190
+ Specially, you can pass a keyword starts with `!`
191
+ to exclude this word, eg: `!中文版`
192
+
185
193
  the [multiple] options means you can add multiple same option such as:
186
194
 
187
195
  --tags tag1 --tags tag2 --no-tags tag3
@@ -198,7 +206,13 @@ def search(
198
206
  )
199
207
  spider.run(
200
208
  spider.search(
201
- text,
209
+ " ".join(
210
+ map(
211
+ lambda t: (t := t.strip())
212
+ and ('-' + t[1:] if t.startswith("!") else t),
213
+ keywords,
214
+ )
215
+ ),
202
216
  tags=tags,
203
217
  vas=vas,
204
218
  circle=circle,
@@ -1,9 +1,10 @@
1
1
  import re
2
2
  from pathlib import Path
3
- from typing import List, Tuple
3
+ from typing import List, Tuple, Literal
4
4
 
5
5
  import click
6
6
 
7
+ from asmrmanager.config import config
7
8
  from asmrmanager.cli.core import (
8
9
  create_database,
9
10
  fm,
@@ -39,9 +40,9 @@ def del_(source_id: LocalSourceID):
39
40
 
40
41
  folders = folder_chooser_multiple(
41
42
  rj_path,
42
- lambda p: any(
43
- [i.suffix != ".info" for i in p.iterdir() if not i.is_dir()]
44
- ),
43
+ lambda p: any([
44
+ i.suffix != ".info" for i in p.iterdir() if not i.is_dir()
45
+ ]),
45
46
  )
46
47
 
47
48
  for folder in folders:
@@ -136,6 +137,40 @@ def store(source_ids: List[LocalSourceID], replace: bool, all_: bool):
136
137
  store the downloaded files to the storage
137
138
  """
138
139
 
140
+ def before_store_hook(path: Path):
141
+ from asmrmanager.common.fileconverter import (
142
+ convert_audio_format,
143
+ convert_vtt2lrc,
144
+ )
145
+
146
+ def convert(
147
+ file: Path, to: Literal["mp3", "flac", "m4a", "wav", "lrc"]
148
+ ):
149
+ if to == "lrc":
150
+ assert file.suffix.lower() == ".vtt"
151
+ logger.debug(f"converting {file} to lrc")
152
+ convert_vtt2lrc(file)
153
+ else:
154
+ logger.debug(f"converting {file} to {to}")
155
+ convert_audio_format(file, to)
156
+
157
+ assert file.with_suffix(f".{to}").exists()
158
+ file.unlink()
159
+
160
+ def convert_all(from_: str, to: Literal["mp3", "flac", "m4a", "wav", "lrc"]):
161
+ for file in path.rglob(f"*.{from_}", case_sensitive=False):
162
+ convert(file, to)
163
+
164
+ code = config.before_store
165
+ logger.debug("executing before_store_hook code: %s", code)
166
+ if not code.strip():
167
+ return
168
+
169
+ exec(code, {"path": path, "convert": convert, "convert_all": convert_all})
170
+
171
+
172
+
173
+
139
174
  db = create_database()
140
175
  try:
141
176
  if all_:
@@ -146,12 +181,12 @@ def store(source_ids: List[LocalSourceID], replace: bool, all_: bool):
146
181
  )
147
182
  if res is None or res is False:
148
183
  return
149
- fm.store_all(replace=replace)
184
+ fm.store_all(replace=replace, hook=before_store_hook)
150
185
  id_to_store = fm.list_("download")
151
186
 
152
187
  else:
153
188
  for rj_id in source_ids:
154
- fm.store(rj_id, replace=replace)
189
+ fm.store(rj_id, replace=replace, hook=before_store_hook)
155
190
  id_to_store = source_ids
156
191
 
157
192
  for id_ in id_to_store:
@@ -162,10 +197,11 @@ def store(source_ids: List[LocalSourceID], replace: bool, all_: bool):
162
197
  )
163
198
  continue
164
199
  res.stored = True
165
- db.commit()
166
200
  logger.info("succesfully stored all files")
167
201
  except DstItemAlreadyExistsException as e:
168
202
  logger.error("storing terminated for %s", e)
203
+ finally:
204
+ db.commit()
169
205
 
170
206
 
171
207
  @click.command()
@@ -182,12 +218,12 @@ def diff(source_id: LocalSourceID):
182
218
 
183
219
  local_files = fm.get_all_files(source_id)
184
220
 
185
- remote_files_should_down = set(
186
- [Path(i["path"]) for i in recovers if i["should_download"]]
187
- )
188
- remote_files_filterd = set(
189
- [Path(i["path"]) for i in recovers if not i["should_download"]]
190
- )
221
+ remote_files_should_down = set([
222
+ Path(i["path"]) for i in recovers if i["should_download"]
223
+ ])
224
+ remote_files_filterd = set([
225
+ Path(i["path"]) for i in recovers if not i["should_download"]
226
+ ])
191
227
  filtered_but_downloaded = remote_files_filterd & local_files
192
228
  should_download_but_missing = remote_files_should_down - local_files
193
229
  added_new_files = (
@@ -252,9 +288,9 @@ def check(list_: bool):
252
288
  continue
253
289
 
254
290
  local_files = fm.get_all_files(source_id)
255
- remote_files_should_down = set(
256
- [Path(i["path"]) for i in recovers if i["should_download"]]
257
- )
291
+ remote_files_should_down = set([
292
+ Path(i["path"]) for i in recovers if i["should_download"]
293
+ ])
258
294
  should_download_but_missing = remote_files_should_down - local_files
259
295
  if len(should_download_but_missing):
260
296
  logger.error(
@@ -13,7 +13,7 @@ def convert_vtt2lrc(vtt_path: Path):
13
13
 
14
14
 
15
15
  def convert_audio_format(
16
- audio_path: Path, dst: Literal["mp3", "flac", "wav"] = "mp3"
16
+ audio_path: Path, dst: Literal["mp3", "flac","m4a", "wav"] = "mp3"
17
17
  ):
18
18
  from subprocess import run
19
19
 
@@ -23,13 +23,15 @@ def convert_audio_format(
23
23
  " add it to your path"
24
24
  )
25
25
 
26
- match dst:
26
+ match dst.lower():
27
27
  case "mp3":
28
- convert_args = ["mp3", "-ab", "256k"]
28
+ convert_args = ["mp3", "-ab", "320k"]
29
29
  case "flac":
30
- convert_args = ["flac"]
30
+ convert_args = ["flac", '-compression_level', '5']
31
31
  case "wav":
32
32
  convert_args = ["pcm_s16le"]
33
+ case "m4a":
34
+ convert_args = ["aac", '-ab', '320k']
33
35
  case _:
34
36
  assert False
35
37
 
@@ -16,7 +16,7 @@ def format_time(time):
16
16
  DEFAULT_THRESHOLD = timedelta(seconds=2)
17
17
 
18
18
 
19
- def vtt2lrc(vtt, header=True, threshold=DEFAULT_THRESHOLD):
19
+ def vtt2lrc(vtt_path: Path, header=True, threshold=DEFAULT_THRESHOLD):
20
20
  lrc = ""
21
21
 
22
22
  if header:
@@ -24,6 +24,7 @@ def vtt2lrc(vtt, header=True, threshold=DEFAULT_THRESHOLD):
24
24
 
25
25
  last_end = parse_time("23:59:59.99") # Insanely big value
26
26
 
27
+ vtt = vtt_path.read_text(encoding="utf-8")
27
28
  for chunk in vtt.split("\n\n")[1:]:
28
29
  if not chunk:
29
30
  continue
@@ -53,8 +54,7 @@ def vtt2lrc(vtt, header=True, threshold=DEFAULT_THRESHOLD):
53
54
  "vtt", type=click.Path(exists=True, dir_okay=False, path_type=Path)
54
55
  )
55
56
  def main(vtt: Path):
56
- content = vtt.read_text(encoding="utf-8")
57
- lrc = vtt2lrc(content)
57
+ lrc = vtt2lrc(vtt)
58
58
  with open(
59
59
  vtt.with_suffix("").with_suffix(".lrc"), "w", encoding="utf-8"
60
60
  ) as f:
@@ -23,7 +23,7 @@ class Config:
23
23
  playlist_aliases: Dict[str, str]
24
24
  player: Literal["mpd", "pygame"]
25
25
  mpd_config: "MPDConfig"
26
-
26
+ before_store: str = ""
27
27
 
28
28
  @dataclass
29
29
  class Filter:
@@ -89,4 +89,5 @@ config = Config(
89
89
  playlist_aliases=_config.get("playlist_aliases", {}),
90
90
  player=_config.get("player", "pygame"),
91
91
  mpd_config=MPDConfig(**_config.get("mpd_config", {})),
92
+ before_store=_config.get("before_store", ""),
92
93
  )
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import shutil
3
3
  from pathlib import Path
4
- from typing import Iterable, List, Literal, NamedTuple, Set, Tuple
4
+ from typing import Callable, Iterable, List, Literal, NamedTuple, Set, Tuple
5
5
 
6
6
  import toml
7
7
 
@@ -124,7 +124,12 @@ class FileManager:
124
124
  def could_store(self):
125
125
  return self.storage_path_exists and self.download_path_exists
126
126
 
127
- def store(self, source_id: LocalSourceID, replace=True):
127
+ def store(
128
+ self,
129
+ source_id: LocalSourceID,
130
+ replace=True,
131
+ hook: Callable[[Path], None] | None = None,
132
+ ):
128
133
  """sync download path and storage path"""
129
134
  # TODO 换用fcp实现?
130
135
  assert self.could_store()
@@ -133,6 +138,10 @@ class FileManager:
133
138
  logger.warning(f"item {rj_name} does not exists, skip it")
134
139
  return
135
140
 
141
+ if hook is not None:
142
+ logger.info(f'Execute hook function for: {rj_name}')
143
+ hook(self.download_path / rj_name)
144
+
136
145
  # if os.path.exists(self.storage_path / rj_name):
137
146
  # if not exists_ok:
138
147
  # logger.error(f"the item {rj_name} to store already exists!")
@@ -189,13 +198,15 @@ class FileManager:
189
198
  # )
190
199
  # exit(-1)
191
200
 
192
- def store_all(self, replace=True):
201
+ def store_all(
202
+ self, replace=True, hook: Callable[[Path], None] | None = None
203
+ ):
193
204
  for file in os.listdir(self.download_path):
194
205
  source_id = source_name2id(SourceName(file))
195
206
  if source_id is None:
196
207
  logger.warning(f"Ignore invalid file {file} in download path")
197
208
  continue
198
- self.store(source_id, replace=replace)
209
+ self.store(source_id, replace=replace, hook=hook)
199
210
 
200
211
  def could_view(self):
201
212
  """
@@ -374,20 +385,16 @@ class FileManager:
374
385
  def get_all_files(self, source_id: LocalSourceID) -> Set[Path]:
375
386
  """get all files of source ID both in download and storage path"""
376
387
  source_name = id2source_name(source_id)
377
- l1 = set(
378
- [
379
- i.relative_to(self.download_path / source_name)
380
- for i in (self.download_path / source_name).rglob("*")
381
- if not i.is_dir()
382
- ]
383
- )
384
- l2 = set(
385
- [
386
- i.relative_to(self.storage_path / source_name)
387
- for i in (self.storage_path / source_name).rglob("*")
388
- if not i.is_dir()
389
- ]
390
- )
388
+ l1 = set([
389
+ i.relative_to(self.download_path / source_name)
390
+ for i in (self.download_path / source_name).rglob("*")
391
+ if not i.is_dir()
392
+ ])
393
+ l2 = set([
394
+ i.relative_to(self.storage_path / source_name)
395
+ for i in (self.storage_path / source_name).rglob("*")
396
+ if not i.is_dir()
397
+ ])
391
398
  return l1 | l2
392
399
 
393
400
  @classmethod
@@ -36,6 +36,26 @@ download_method = "idm"
36
36
  # 若使用mpd请确保已安装mpd依赖并配置好mpd_config
37
37
  player = 'pygame'
38
38
 
39
+ # asmr file store 前执行的脚本,若不需要可直接删除或留空,
40
+ # 使用 Python 语法编写,运行时动态执行,并提供以下全局资源以供调用:
41
+
42
+ # - path: pathlib.Path 音声的存储路径,download_path的子目录
43
+
44
+ # - convert(file: pathlib.Path, to: Literal['mp3', 'wav', 'flac', 'm4a', 'lrc'])
45
+ # 将单个文件转化为目标格式,mp3与m4a默认采用320k码率,flac默认使用压缩等级为5
46
+ # 若使用to='lrc',请确保原文件为vtt格式。
47
+
48
+ # - convert_all(from_: Literal['mp3', 'wav', 'flac', 'm4a', 'vtt'], to: Literal['mp3', 'wav', 'flac', 'm4a', 'lrc'])
49
+ # 将path 下的所有<from_>类型的文件转化为<to>类型,
50
+ # 对convert函数的封装,其余细节与convert无异。
51
+
52
+ before_store = '''
53
+ convert_all('vtt', 'lrc')
54
+ convert_all('wav', 'mp3')
55
+ convert_all('flac', 'mp3')
56
+ ''''
57
+
58
+
39
59
  # 如使用aria2请配置此项,否则请忽略
40
60
  # 如果是在本地使用aria2,运行`aria2c --enable-rpc`,以下保持默认即可
41
61
  # 如果使用motrix,且没有更改过默认端口,请将端口(port)改为16800
@@ -33,7 +33,7 @@ dependencies = [
33
33
  ]
34
34
  requires-python = ">=3.10"
35
35
  readme = "README.md"
36
- version = "2.2.1"
36
+ version = "2.2.3"
37
37
 
38
38
  [project.license]
39
39
  text = "MIT"
@@ -1 +0,0 @@
1
- __version__ = "2.2.1"
File without changes