library 3.2.7__tar.gz → 4.0.0__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 (199) hide show
  1. library-4.0.0/.github/README.md +4783 -0
  2. library-4.0.0/PKG-INFO +4879 -0
  3. library-4.0.0/library/__main__.py +864 -0
  4. library-4.0.0/library/_version.py +25 -0
  5. library-4.0.0/library/archive/__init__.py +1 -0
  6. {library-3.2.7/library/mediafiles → library-4.0.0/library/archive}/unardel.py +112 -13
  7. library-4.0.0/library/captions/__init__.py +1 -0
  8. {library-3.2.7/library/mediadb → library-4.0.0/library/captions}/search.py +1 -1
  9. library-4.0.0/library/command_registry.py +153 -0
  10. {library-3.2.7 → library-4.0.0}/library/data/http_errors.py +2 -2
  11. library-4.0.0/library/database/__init__.py +1 -0
  12. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/av.py +8 -6
  13. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/computer_info.py +3 -1
  14. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/computers_add.py +6 -2
  15. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/fs_add.py +86 -42
  16. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/fs_add_metadata.py +44 -25
  17. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/gallery_add.py +11 -6
  18. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/gallery_backend.py +93 -48
  19. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/getty_add.py +7 -7
  20. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/links_add.py +44 -29
  21. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/nicotine_import.py +1 -1
  22. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/places_import.py +1 -1
  23. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/reddit_add.py +6 -5
  24. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/site_add.py +74 -29
  25. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/substack.py +1 -1
  26. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/tabs_add.py +14 -12
  27. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/tildes.py +1 -1
  28. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/torrents_add.py +9 -3
  29. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/tube_add.py +11 -5
  30. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/tube_backend.py +20 -11
  31. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/web_add.py +53 -20
  32. {library-3.2.7/library/multidb → library-4.0.0/library/database/manage}/copy_play_counts.py +2 -3
  33. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/db_history.py +6 -0
  34. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/db_media.py +36 -10
  35. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/db_playlists.py +103 -47
  36. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/dedupe_db.py +9 -2
  37. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/dedupe_media.py +50 -42
  38. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/history.py +5 -3
  39. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/history_add.py +1 -1
  40. {library-3.2.7/library/multidb → library-4.0.0/library/database/manage}/merge_dbs.py +10 -2
  41. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/merge_online_local.py +7 -5
  42. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/mpv_watchlater.py +1 -1
  43. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/playlists.py +21 -16
  44. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/pushshift.py +2 -2
  45. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/reddit_selftext.py +1 -1
  46. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/stats.py +2 -2
  47. library-4.0.0/library/database/manage/stats_words.py +65 -0
  48. library-4.0.0/library/document/__init__.py +1 -0
  49. library-4.0.0/library/document/document_convert.py +241 -0
  50. {library-3.2.7/library/mediafiles → library-4.0.0/library/document}/pdf_edit.py +22 -13
  51. library-4.0.0/library/document/pdf_header.py +71 -0
  52. library-4.0.0/library/download/__init__.py +1 -0
  53. {library-3.2.7/library/mediadb → library-4.0.0/library/download}/block.py +87 -44
  54. library-4.0.0/library/download/download.py +398 -0
  55. {library-3.2.7/library/mediadb → library-4.0.0/library/download}/download_status.py +9 -1
  56. {library-3.2.7/library/mediadb → library-4.0.0/library/download}/redownload.py +13 -3
  57. {library-3.2.7/library/files → library-4.0.0/library/file}/christen.py +3 -2
  58. {library-3.2.7/library/misc → library-4.0.0/library/file}/dedupe_czkawka.py +11 -3
  59. {library-3.2.7/library/mediafiles → library-4.0.0/library/file}/media_check.py +7 -4
  60. library-4.0.0/library/file/media_compare.py +92 -0
  61. {library-3.2.7/library/files → library-4.0.0/library/file}/sample_compare.py +4 -2
  62. {library-3.2.7/library/files → library-4.0.0/library/file}/similar_files.py +4 -8
  63. {library-3.2.7/library/fsdb → library-4.0.0/library/filesystem}/disk_usage.py +15 -12
  64. {library-3.2.7/library/fsdb → library-4.0.0/library/filesystem}/filesystem.py +4 -2
  65. {library-3.2.7/library/fsdb → library-4.0.0/library/filesystem}/folder_stats.py +1 -1
  66. {library-3.2.7/library/fsdb → library-4.0.0/library/filesystem}/search_db.py +10 -10
  67. {library-3.2.7/library/folders → library-4.0.0/library/folder}/big_dirs.py +37 -29
  68. {library-3.2.7/library/folders → library-4.0.0/library/folder}/merge_mv.py +15 -51
  69. {library-3.2.7/library/folders → library-4.0.0/library/folder}/mergerfs_cp.py +7 -4
  70. {library-3.2.7/library/folders → library-4.0.0/library/folder}/move_list.py +6 -3
  71. {library-3.2.7/library/folders → library-4.0.0/library/folder}/scatter.py +5 -2
  72. {library-3.2.7/library/folders → library-4.0.0/library/folder}/similar_folders.py +7 -10
  73. library-4.0.0/library/help/__init__.py +1 -0
  74. library-4.0.0/library/help/search_help.py +104 -0
  75. library-4.0.0/library/image/__init__.py +1 -0
  76. {library-3.2.7/library/mediafiles → library-4.0.0/library/image}/images_to_pdf.py +22 -7
  77. library-4.0.0/library/json/__init__.py +1 -0
  78. {library-3.2.7/library/text → library-4.0.0/library/json}/json_keys_rename.py +1 -1
  79. library-4.0.0/library/json/transform.py +85 -0
  80. library-4.0.0/library/links/__init__.py +1 -0
  81. library-4.0.0/library/links/extract_links.py +471 -0
  82. {library-3.2.7/library/playback → library-4.0.0/library/links}/links_open.py +9 -5
  83. library-4.0.0/library/play/__init__.py +1 -0
  84. {library-3.2.7/library/playback → library-4.0.0/library/play}/media_player.py +35 -28
  85. {library-3.2.7/library/playback → library-4.0.0/library/play}/media_printer.py +69 -34
  86. {library-3.2.7/library/playback → library-4.0.0/library/play}/play_actions.py +19 -17
  87. {library-3.2.7/library/playback → library-4.0.0/library/play}/post_actions.py +19 -10
  88. {library-3.2.7 → library-4.0.0}/library/playback/playback_control.py +44 -13
  89. {library-3.2.7 → library-4.0.0}/library/readme.py +104 -71
  90. library-4.0.0/library/shrink/__init__.py +1 -0
  91. {library-3.2.7/library/mediafiles → library-4.0.0/library/shrink}/process_ffmpeg.py +29 -16
  92. {library-3.2.7/library/mediafiles → library-4.0.0/library/shrink}/process_image.py +8 -6
  93. {library-3.2.7/library/mediafiles → library-4.0.0/library/shrink}/process_media.py +113 -36
  94. library-4.0.0/library/shrink/process_table.py +73 -0
  95. {library-3.2.7/library/mediafiles → library-4.0.0/library/shrink}/process_text.py +20 -10
  96. {library-3.2.7/library/tablefiles → library-4.0.0/library/table}/columns.py +1 -1
  97. library-4.0.0/library/table/convert.py +128 -0
  98. {library-3.2.7/library/tablefiles → library-4.0.0/library/table}/eda.py +10 -6
  99. library-4.0.0/library/table/incremental_diff.py +509 -0
  100. {library-3.2.7/library/tablefiles → library-4.0.0/library/table}/markdown_tables.py +1 -1
  101. library-4.0.0/library/table/mcda.py +624 -0
  102. {library-3.2.7/library/tablefiles → library-4.0.0/library/table}/plot.py +1 -1
  103. library-4.0.0/library/tabs/__init__.py +1 -0
  104. {library-3.2.7/library/playback → library-4.0.0/library/tabs}/tabs_open.py +4 -3
  105. {library-3.2.7 → library-4.0.0}/library/text/cluster_sort.py +39 -42
  106. {library-3.2.7 → library-4.0.0}/library/text/combinations.py +1 -1
  107. library-4.0.0/library/text/convert.py +11 -0
  108. {library-3.2.7 → library-4.0.0}/library/text/expand_links.py +0 -0
  109. {library-3.2.7 → library-4.0.0}/library/text/extract_text.py +12 -8
  110. library-4.0.0/library/text/filters.py +117 -0
  111. library-4.0.0/library/text/markdown.py +79 -0
  112. {library-3.2.7 → library-4.0.0}/library/text/nouns.py +1 -1
  113. {library-3.2.7 → library-4.0.0}/library/text/regex_sort.py +17 -16
  114. library-4.0.0/library/text/stream_tools.py +80 -0
  115. {library-3.2.7 → library-4.0.0}/library/text/timestamps.py +3 -1
  116. library-4.0.0/library/torrent/__init__.py +1 -0
  117. {library-3.2.7/library/multidb → library-4.0.0/library/torrent}/allocate_torrents.py +6 -1
  118. library-4.0.0/library/torrent/torrent_audit.py +96 -0
  119. library-4.0.0/library/torrent/torrent_match.py +203 -0
  120. {library-3.2.7/library/mediafiles → library-4.0.0/library/torrent}/torrents_dump.py +2 -3
  121. {library-3.2.7/library/playback → library-4.0.0/library/torrent}/torrents_info.py +32 -19
  122. {library-3.2.7/library/playback → library-4.0.0/library/torrent}/torrents_remaining.py +5 -3
  123. {library-3.2.7/library/mediafiles → library-4.0.0/library/torrent}/torrents_start.py +6 -1
  124. {library-3.2.7 → library-4.0.0}/library/usage.py +708 -449
  125. {library-3.2.7 → library-4.0.0}/library/utils/arggroups.py +431 -141
  126. {library-3.2.7 → library-4.0.0}/library/utils/argparse_utils.py +46 -37
  127. {library-3.2.7 → library-4.0.0}/library/utils/consts.py +39 -2
  128. {library-3.2.7 → library-4.0.0}/library/utils/date_utils.py +4 -2
  129. {library-3.2.7 → library-4.0.0}/library/utils/db_utils.py +6 -3
  130. {library-3.2.7 → library-4.0.0}/library/utils/devices.py +7 -4
  131. {library-3.2.7 → library-4.0.0}/library/utils/file_utils.py +32 -13
  132. {library-3.2.7 → library-4.0.0}/library/utils/filter_engine.py +9 -6
  133. {library-3.2.7 → library-4.0.0}/library/utils/gui.py +8 -21
  134. {library-3.2.7 → library-4.0.0}/library/utils/iterables.py +1 -1
  135. {library-3.2.7 → library-4.0.0}/library/utils/log_utils.py +3 -1
  136. {library-3.2.7 → library-4.0.0}/library/utils/nums.py +7 -4
  137. {library-3.2.7 → library-4.0.0}/library/utils/objects.py +14 -4
  138. {library-3.2.7 → library-4.0.0}/library/utils/path_utils.py +14 -2
  139. {library-3.2.7 → library-4.0.0}/library/utils/pd_utils.py +3 -1
  140. {library-3.2.7 → library-4.0.0}/library/utils/printing.py +7 -8
  141. {library-3.2.7 → library-4.0.0}/library/utils/processes.py +142 -20
  142. {library-3.2.7 → library-4.0.0}/library/utils/remote_processes.py +7 -7
  143. {library-3.2.7 → library-4.0.0}/library/utils/shell_utils.py +21 -69
  144. {library-3.2.7 → library-4.0.0}/library/utils/sqlgroups.py +17 -12
  145. {library-3.2.7 → library-4.0.0}/library/utils/strings.py +40 -30
  146. {library-3.2.7 → library-4.0.0}/library/utils/web.py +337 -27
  147. library-4.0.0/library/video/__init__.py +1 -0
  148. library-4.0.0/library/video/create.py +606 -0
  149. library-4.0.0/library/video/extract.py +459 -0
  150. {library-3.2.7 → library-4.0.0}/pyproject.toml +48 -39
  151. library-4.0.0/pyproject.toml.orig +222 -0
  152. library-3.2.7/.github/README.md +0 -3432
  153. library-3.2.7/PKG-INFO +0 -3540
  154. library-3.2.7/library/__main__.py +0 -390
  155. library-3.2.7/library/mediadb/download.py +0 -249
  156. library-3.2.7/library/misc/search_help.py +0 -83
  157. library-3.2.7/library/playback/__init__.py +0 -0
  158. library-3.2.7/library/playback/surf.py +0 -82
  159. library-3.2.7/library/tablefiles/__init__.py +0 -0
  160. library-3.2.7/library/tablefiles/incremental_diff.py +0 -134
  161. library-3.2.7/library/tablefiles/mcda.py +0 -304
  162. library-3.2.7/library/text/__init__.py +0 -0
  163. library-3.2.7/library/text/extract_links.py +0 -274
  164. library-3.2.7/library/utils/__init__.py +0 -0
  165. {library-3.2.7 → library-4.0.0}/LICENSE +0 -0
  166. {library-3.2.7 → library-4.0.0}/library/__init__.py +0 -0
  167. {library-3.2.7 → library-4.0.0}/library/assets/__init__.py +0 -0
  168. {library-3.2.7 → library-4.0.0}/library/assets/calibre.css +0 -0
  169. {library-3.2.7 → library-4.0.0}/library/assets/kotobago.png +0 -0
  170. {library-3.2.7/library/createdb → library-4.0.0/library/data}/__init__.py +0 -0
  171. {library-3.2.7 → library-4.0.0}/library/data/dictionary.pkl +0 -0
  172. {library-3.2.7 → library-4.0.0}/library/data/ffmpeg_errors.py +0 -0
  173. {library-3.2.7 → library-4.0.0}/library/data/imagemagick_errors.py +0 -0
  174. {library-3.2.7 → library-4.0.0}/library/data/unar_errors.py +0 -0
  175. {library-3.2.7 → library-4.0.0}/library/data/wordbank.py +0 -0
  176. {library-3.2.7 → library-4.0.0}/library/data/yt_dlp_errors.py +0 -0
  177. {library-3.2.7/library/data → library-4.0.0/library/database/add}/__init__.py +0 -0
  178. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/hn_add.py +0 -0
  179. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/row_add.py +0 -0
  180. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/subtitle.py +0 -0
  181. {library-3.2.7/library/createdb → library-4.0.0/library/database/add}/tables_add.py +0 -0
  182. {library-3.2.7/library/editdb → library-4.0.0/library/database/manage}/__init__.py +0 -0
  183. {library-3.2.7/library/misc → library-4.0.0/library/database/manage}/export_text.py +0 -0
  184. {library-3.2.7/library/mediadb → library-4.0.0/library/database/manage}/optimize_db.py +0 -0
  185. {library-3.2.7/library/files → library-4.0.0/library/file}/__init__.py +0 -0
  186. {library-3.2.7/library/files → library-4.0.0/library/file}/sample_hash.py +0 -0
  187. {library-3.2.7/library/folders → library-4.0.0/library/filesystem}/__init__.py +0 -0
  188. {library-3.2.7/library/fsdb → library-4.0.0/library/folder}/__init__.py +0 -0
  189. {library-3.2.7/library/folders → library-4.0.0/library/folder}/filter_src.py +0 -0
  190. {library-3.2.7/library/folders → library-4.0.0/library/folder}/mount_stats.py +0 -0
  191. {library-3.2.7 → library-4.0.0}/library/lb.py +0 -0
  192. {library-3.2.7/library/mediadb → library-4.0.0/library/playback}/__init__.py +0 -0
  193. {library-3.2.7/library/mediafiles → library-4.0.0/library/table}/__init__.py +0 -0
  194. {library-3.2.7/library/misc → library-4.0.0/library/text}/__init__.py +0 -0
  195. {library-3.2.7 → library-4.0.0}/library/text/markdown_links.py +0 -0
  196. {library-3.2.7/library/multidb → library-4.0.0/library/utils}/__init__.py +0 -0
  197. {library-3.2.7 → library-4.0.0}/library/utils/arg_utils.py +0 -0
  198. {library-3.2.7 → library-4.0.0}/library/utils/mpv_utils.py +0 -0
  199. {library-3.2.7 → library-4.0.0}/library/utils/sql_utils.py +0 -0
@@ -0,0 +1,4783 @@
1
+ # library (media toolkit)
2
+
3
+ A wise philosopher once told me: "the future is [autotainment](https://www.youtube.com/watch?v=F9sZFrsjPp0)".
4
+
5
+ Manage and curate large media libraries. An index for your archive.
6
+ Primary usage is local filesystem but also supports some virtual constructs like
7
+ tracking online video playlists (eg. YouTube subscriptions) and scheduling browser tabs.
8
+
9
+ <img align="right" width="300" height="600" src="https://raw.githubusercontent.com/chapmanjacobd/library/main/.github/examples/art.avif" />
10
+
11
+ [![Downloads](https://static.pepy.tech/badge/library)](https://pepy.tech/project/library)
12
+
13
+ ## Getting started
14
+
15
+ <details><summary>Install</summary>
16
+
17
+ Linux recommended but [Windows setup instructions](https://github.com/chapmanjacobd/library/blob/main/.github/Windows.md) available.
18
+
19
+ pip install library
20
+
21
+ Mac OS is also supported.
22
+
23
+ ### External dependencies
24
+
25
+ Required: `ffmpeg`
26
+
27
+ Some features work better with: `mpv`, `fd-find`, `fish`
28
+
29
+ Optional Python extras are grouped under `library[deluxe]`. This includes
30
+ `gallery-dl` (used by `database add gallery`), `internetarchive` (used for
31
+ Internet Archive downloads), and `cdx_toolkit` (used by the
32
+ `--webcache`/`--wayback` CDX extractor):
33
+
34
+ pip install "library[deluxe]"
35
+
36
+ Torrent metadata commands (`database add torrents` and `torrent dump`) require
37
+ the `libtorrent` Python bindings. They are included by `library[deluxe]` on
38
+ Python 3.12 and 3.13. On Python 3.14, libtorrent-rasterbar bindings are
39
+ usually provided by distro packages rather than PyPI:
40
+
41
+ * Debian/Ubuntu: `sudo apt install python3-libtorrent`
42
+ * Fedora: `sudo dnf install rb_libtorrent-python3`
43
+ * macOS/Homebrew: `brew install libtorrent-rasterbar`
44
+
45
+ The Debian, Fedora, and Homebrew packages are built for their system or
46
+ Homebrew Python and are not importable from a separate uv virtualenv. There
47
+ is no official Scoop package for the Python bindings; on Windows, use Python
48
+ 3.13 and `pip install "library[deluxe]"`.
49
+
50
+ </details>
51
+
52
+ <details><summary>Local media</summary>
53
+
54
+ ### 1. Extract Metadata
55
+
56
+ For thirty terabytes of video the initial scan takes about four hours to complete.
57
+ After that, subsequent scans of the path (or any subpaths) are much quicker--only
58
+ new files are read by `ffprobe`.
59
+
60
+ library database add filesystem tv.db ./video/folder/
61
+
62
+ ![termtosvg](https://raw.githubusercontent.com/chapmanjacobd/library/main/.github/examples/extract.svg)
63
+
64
+ ### 2. Watch / Listen from local files
65
+
66
+ library play video tv.db # the default post-action is to do nothing
67
+ library play video tv.db --post-action delete # delete file after playing
68
+ library play audio finalists.db -k ask_delete # ask whether to delete file after playing
69
+
70
+ To stop playing press Ctrl+C in either the terminal or mpv
71
+
72
+ </details>
73
+
74
+ <details><summary>Online media</summary>
75
+
76
+ ### 1. Download Metadata
77
+
78
+ Download playlist and channel metadata. Break free of the YouTube algo~
79
+
80
+ library database add video educational.db https://www.youtube.com/c/BranchEducation/videos
81
+
82
+ [![termtosvg](https://raw.githubusercontent.com/chapmanjacobd/library/main/.github/examples/tubeadd.svg "library database add video example")](https://asciinema.org/a/BzplqNj9sCERH3A80GVvwsTTT)
83
+
84
+ And you can add more at any time--even from different websites.
85
+
86
+ library database add video maker.db https://vimeo.com/terburg
87
+
88
+ To prevent mistakes the default configuration is to download metadata for only
89
+ the most recent 20,000 videos per playlist/channel.
90
+
91
+ library database add video maker.db --extractor-config playlistend=1000
92
+
93
+ Be aware that there are some YouTube Channels which have many items--for example
94
+ the TEDx channel has about 180,000 videos. Some channels even have upwards of
95
+ two million videos. More than you could likely watch in one sitting--maybe even one lifetime.
96
+ On a high-speed connection (>500 Mbps), it can take up to five hours to download
97
+ the metadata for 180,000 videos.
98
+
99
+ TIP! If you often copy and paste many URLs you can paste line-delimited text as arguments via a subshell. For example, in `fish` shell with [cb](https://github.com/niedzielski/cb):
100
+
101
+ library database add video my.db (cb)
102
+
103
+ Or in BASH:
104
+
105
+ library database add video my.db $(xclip -selection c)
106
+
107
+ #### 1a. Get new videos for saved playlists
108
+
109
+ Tubeupdate goes through the list of stored playlists and fetches metadata for
110
+ any videos not seen before.
111
+
112
+ library database update video tube.db
113
+
114
+ ### 2. Watch / Listen from websites
115
+
116
+ library play video maker.db
117
+
118
+ To stop playing press Ctrl+C in either the terminal or mpv
119
+
120
+ </details>
121
+
122
+ <details><summary>List all subcommands</summary>
123
+
124
+ Commands are organized by workflow. Use `database` (or `db`) for database
125
+ operations, `filesystem` (or `fs`) for filesystem operations, `document` (or
126
+ `doc`/`docs`) for document operations, and `file` (or `files`) for file
127
+ operations. The `ls` alias can be used for nested list commands. Resource group
128
+ names and their aliases take precedence over flat command names, so a few former
129
+ flat commands are now only reachable through a group (for example
130
+ `filesystem list`, `captions`, `table markdown`, `tabs open`,
131
+ `links extract`, `text extract`, and `torrent info`).
132
+
133
+ $ library
134
+ library (v4.0.0; 137 subcommands)
135
+
136
+ Media collection subcommands:
137
+ ╭─────────────────────────────┬───────────────────────────────────────────────────────────────────────╮
138
+ │ database add filesystem │ Add local media │
139
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
140
+ │ database add video │ Add online video media (yt-dlp) │
141
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
142
+ │ database add gallery │ Add online gallery media (gallery-dl) │
143
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
144
+ │ database add open-directory │ Discover media in HTTP directory listings and folder-like web indexes │
145
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
146
+ │ database add reddit │ Create a reddit database; Add subreddits │
147
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
148
+ │ database add hackernews │ Create / Update a Hacker News database │
149
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
150
+ │ database add getty │ Create / Update a Getty Museum database │
151
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
152
+ │ database add substack │ Backup substack articles │
153
+ ├─────────────────────────────┼───────────────────────────────────────────────────────────────────────┤
154
+ │ database add tildes │ Backup tildes comments and topics │
155
+ ╰─────────────────────────────┴───────────────────────────────────────────────────────────────────────╯
156
+
157
+ Playback subcommands:
158
+ ╭────────────────┬────────────────────────────────────────────────────────╮
159
+ │ play media │ Play media of any supported type │
160
+ ├────────────────┼────────────────────────────────────────────────────────┤
161
+ │ play video │ Watch / Listen │
162
+ ├────────────────┼────────────────────────────────────────────────────────┤
163
+ │ play audio │ Listen to audio │
164
+ ├────────────────┼────────────────────────────────────────────────────────┤
165
+ │ play text │ Read text │
166
+ ├────────────────┼────────────────────────────────────────────────────────┤
167
+ │ play images │ View images │
168
+ ├────────────────┼────────────────────────────────────────────────────────┤
169
+ │ playback now │ Show what is currently playing │
170
+ ├────────────────┼────────────────────────────────────────────────────────┤
171
+ │ playback next │ Play next file and optionally delete current file │
172
+ ├────────────────┼────────────────────────────────────────────────────────┤
173
+ │ playback seek │ Set playback to a certain time, fast-forward or rewind │
174
+ ├────────────────┼────────────────────────────────────────────────────────┤
175
+ │ playback stop │ Stop all playback │
176
+ ├────────────────┼────────────────────────────────────────────────────────┤
177
+ │ playback pause │ Pause all playback │
178
+ ╰────────────────┴────────────────────────────────────────────────────────╯
179
+
180
+ Web collection subcommands:
181
+ ╭────────────────────┬─────────────────────────────────────────────────────────────────────────────────╮
182
+ │ database add links │ Extract links from webpages, paginated lists, recursive sites, and web archives │
183
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
184
+ │ links open │ Open links from link dbs │
185
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
186
+ │ links extract │ Extract inner links from lists of web links │
187
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
188
+ │ links download │ Download links from lists of web links │
189
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
190
+ │ database add tabs │ Schedule URLs for recurring browser visits │
191
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
192
+ │ tabs open │ Open your tabs for the day │
193
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
194
+ │ tabs shuffle │ Randomize tabs.db a bit │
195
+ ├────────────────────┼─────────────────────────────────────────────────────────────────────────────────┤
196
+ │ database add site │ Extract structured website data into SQLite │
197
+ ╰────────────────────┴─────────────────────────────────────────────────────────────────────────────────╯
198
+
199
+ Download subcommands:
200
+ ╭──────────────────────────┬───────────────────────────────────────╮
201
+ │ download │ Download media │
202
+ ├──────────────────────────┼───────────────────────────────────────┤
203
+ │ database download │ Download media recorded in a database │
204
+ ├──────────────────────────┼───────────────────────────────────────┤
205
+ │ database download block │ Block a channel │
206
+ ├──────────────────────────┼───────────────────────────────────────┤
207
+ │ database download status │ Show download status │
208
+ ├──────────────────────────┼───────────────────────────────────────┤
209
+ │ database redownload │ Re-download deleted/lost media │
210
+ ╰──────────────────────────┴───────────────────────────────────────╯
211
+
212
+ Database management subcommands:
213
+ ╭─────────────────┬──────────────────────────╮
214
+ │ database search │ Search a SQLite database │
215
+ ├─────────────────┼──────────────────────────┤
216
+ │ database merge │ Merge SQLite databases │
217
+ ├─────────────────┼──────────────────────────┤
218
+ │ database dedupe │ Dedupe SQLite tables │
219
+ ╰─────────────────┴──────────────────────────╯
220
+
221
+ Media database management subcommands:
222
+ ╭─────────────────────────────────┬─────────────────────────────────────────────────────────────╮
223
+ │ captions │ Search captions / subtitles │
224
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
225
+ │ database playlists │ List stored playlists │
226
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
227
+ │ database history │ Show and manage playback history │
228
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
229
+ │ database history add │ Add history from paths │
230
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
231
+ │ database stats │ Show some event statistics (created, deleted, watched, etc) │
232
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
233
+ │ database stats words │ Aggregate media size by words in paths │
234
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
235
+ │ database copy play-counts │ Copy play history │
236
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
237
+ │ database dedupe media │ Dedupe similar media │
238
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
239
+ │ file dedupe czkawka │ Process czkawka diff output │
240
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
241
+ │ database merge online-local │ Merge online and local data │
242
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
243
+ │ database enrich reddit-selftext │ Copy selftext links to media table │
244
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
245
+ │ database export html │ Export HTML files from SQLite databases │
246
+ ├─────────────────────────────────┼─────────────────────────────────────────────────────────────┤
247
+ │ database optimize │ Re-optimize database │
248
+ ╰─────────────────────────────────┴─────────────────────────────────────────────────────────────╯
249
+
250
+ File and folder subcommands:
251
+ ╭────────────────────────────┬─────────────────────────────────────────────────────────────────────╮
252
+ │ folder merge move │ Move files and merge folders in BSD/rsync style, rename if possible │
253
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
254
+ │ folder merge copy │ Copy files and merge folders │
255
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
256
+ │ folder merge relative-move │ Move files while preserving relative paths │
257
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
258
+ │ folder merge relative-copy │ Copy files while preserving relative paths │
259
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
260
+ │ folder copy mergerfs │ cp files with reflink on mergerfs │
261
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
262
+ │ folder move list │ Find specific folders to move to different disks │
263
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
264
+ │ folder large │ Show large folders │
265
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
266
+ │ folder scatter │ Scatter files between folders or disks │
267
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
268
+ │ folder similar │ Find similar folders based on folder name, size, and count │
269
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
270
+ │ file normalize │ Clean file paths │
271
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
272
+ │ file hash │ Calculate a hash based on small file segments │
273
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
274
+ │ file compare │ Compare files using sample-hash and other shortcuts │
275
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
276
+ │ file similar │ Find similar files based on filename and size │
277
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
278
+ │ file media-hash │ Hash decoded audio/video streams with ffmpeg │
279
+ ├────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
280
+ │ file media-compare │ Compare decoded audio/video streams │
281
+ ╰────────────────────────────┴─────────────────────────────────────────────────────────────────────╯
282
+
283
+ Filesystem inspection subcommands:
284
+ ╭─────────────────────────────┬───────────────────────────────────────────╮
285
+ │ filesystem list │ Find files by mimetype and size │
286
+ ├─────────────────────────────┼───────────────────────────────────────────┤
287
+ │ filesystem usage │ Show disk usage │
288
+ ├─────────────────────────────┼───────────────────────────────────────────┤
289
+ │ filesystem usage extensions │ Show disk usage grouped by file extension │
290
+ ├─────────────────────────────┼───────────────────────────────────────────┤
291
+ │ filesystem usage mimetypes │ Show disk usage grouped by MIME type │
292
+ ├─────────────────────────────┼───────────────────────────────────────────┤
293
+ │ filesystem usage sizes │ Show disk usage grouped by file size │
294
+ ├─────────────────────────────┼───────────────────────────────────────────┤
295
+ │ filesystem mounts stats │ Show some relative mount stats │
296
+ ├─────────────────────────────┼───────────────────────────────────────────┤
297
+ │ filesystem mounts free │ Show system-wide disk usage │
298
+ ╰─────────────────────────────┴───────────────────────────────────────────╯
299
+
300
+ Media processing subcommands:
301
+ ╭─────────────────────┬───────────────────────────────────────────────────────────────────────────╮
302
+ │ file check │ Check video and audio files for corruption via ffmpeg │
303
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
304
+ │ shrink media │ Estimate and execute potential disk space savings │
305
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
306
+ │ shrink table │ Convert table-like files to compressed Parquet │
307
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
308
+ │ shrink video │ Shrink video/audio to AV1/Opus format (.mkv, .mka) │
309
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
310
+ │ shrink audio │ Shrink audio to Opus │
311
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
312
+ │ shrink image │ Shrink images to AV1 image format (.avif) │
313
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
314
+ │ shrink text │ Shrink documents to HTML+AV1 image format (requires Calibre) │
315
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
316
+ │ archive extract │ Extract from archives and delete all associated multi-part archive files │
317
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
318
+ │ document convert │ Convert images, PDFs, and text-like documents to PDF │
319
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
320
+ │ document adjust pdf │ Apply brightness, contrast, saturation, and sharpness adjustments to PDFs │
321
+ ├─────────────────────┼───────────────────────────────────────────────────────────────────────────┤
322
+ │ document pdf header │ Stamp rendered Markdown/text onto PDF pages │
323
+ ╰─────────────────────┴───────────────────────────────────────────────────────────────────────────╯
324
+
325
+ Video extraction subcommands:
326
+ ╭─────────────────────────┬───────────────────────────────────────────────────────────╮
327
+ │ video extract audio │ Extract audio streams without re-encoding │
328
+ ├─────────────────────────┼───────────────────────────────────────────────────────────┤
329
+ │ video extract subtitles │ Extract embedded subtitle streams │
330
+ ├─────────────────────────┼───────────────────────────────────────────────────────────┤
331
+ │ video extract scenes │ Extract scene changes as video, images, or contact sheets │
332
+ ├─────────────────────────┼───────────────────────────────────────────────────────────┤
333
+ │ video extract keyframes │ Extract keyframes as video, images, or slow-motion video │
334
+ ╰─────────────────────────┴───────────────────────────────────────────────────────────╯
335
+
336
+ Video creation subcommands:
337
+ ╭────────────────────────┬─────────────────────────────────────────────────╮
338
+ │ video create slideshow │ Create a slideshow video, optionally with audio │
339
+ ├────────────────────────┼─────────────────────────────────────────────────┤
340
+ │ video concat │ Concatenate videos with stream copying │
341
+ ╰────────────────────────┴─────────────────────────────────────────────────╯
342
+
343
+ Text processing subcommands:
344
+ ╭─────────────────────┬───────────────────────────────────────────────────╮
345
+ │ text border │ Put text inside a simple border │
346
+ ├─────────────────────┼───────────────────────────────────────────────────┤
347
+ │ text clean │ Clean pasted text and preserve Markdown structure │
348
+ ├─────────────────────┼───────────────────────────────────────────────────┤
349
+ │ text group │ Group input lines by a regular expression │
350
+ ├─────────────────────┼───────────────────────────────────────────────────┤
351
+ │ text interleave │ Interleave lines from multiple inputs │
352
+ ├─────────────────────┼───────────────────────────────────────────────────┤
353
+ │ text markdown │ Convert simple HTML documents to Markdown │
354
+ ├─────────────────────┼───────────────────────────────────────────────────┤
355
+ │ text convert │ Convert Markdown and text-like documents to PDF │
356
+ ├─────────────────────┼───────────────────────────────────────────────────┤
357
+ │ text sort regex │ Sort text by regex split and corpus comparison │
358
+ ├─────────────────────┼───────────────────────────────────────────────────┤
359
+ │ text sort cluster │ Sort text by similarity │
360
+ ├─────────────────────┼───────────────────────────────────────────────────┤
361
+ │ text extract │ Extract human text from lists of web links │
362
+ ├─────────────────────┼───────────────────────────────────────────────────┤
363
+ │ text markdown links │ Extract titles from lists of web links │
364
+ ├─────────────────────┼───────────────────────────────────────────────────┤
365
+ │ text expand links │ Expand search urls with query text │
366
+ ├─────────────────────┼───────────────────────────────────────────────────┤
367
+ │ text combinations │ Enumerate possible combinations │
368
+ ├─────────────────────┼───────────────────────────────────────────────────┤
369
+ │ text nouns │ Unstructured text -> compound nouns (stdin) │
370
+ ├─────────────────────┼───────────────────────────────────────────────────┤
371
+ │ text timestamps │ Unstructured text -> timestamps │
372
+ ├─────────────────────┼───────────────────────────────────────────────────┤
373
+ │ text dates │ Unstructured text -> dates │
374
+ ├─────────────────────┼───────────────────────────────────────────────────┤
375
+ │ text times │ Unstructured text -> times │
376
+ ├─────────────────────┼───────────────────────────────────────────────────┤
377
+ │ json rename-keys │ Rename JSON keys by substring match │
378
+ ╰─────────────────────┴───────────────────────────────────────────────────╯
379
+
380
+ JSON processing subcommands:
381
+ ╭───────────────────┬──────────────────────────────────────────╮
382
+ │ json remove-nulls │ Remove null values recursively from JSON │
383
+ ╰───────────────────┴──────────────────────────────────────────╯
384
+
385
+ Image processing subcommands:
386
+ ╭────────────────────┬─────────────────────────────────────────────╮
387
+ │ image convert │ Convert images and comic archives into PDFs │
388
+ ├────────────────────┼─────────────────────────────────────────────┤
389
+ │ image sort cluster │ Sort images by similarity │
390
+ ╰────────────────────┴─────────────────────────────────────────────╯
391
+
392
+ Data import subcommands:
393
+ ╭────────────────────────────────┬────────────────────────────────────────────────────╮
394
+ │ database add tables │ Add table-like data to SQLite │
395
+ ├────────────────────────────────┼────────────────────────────────────────────────────┤
396
+ │ database add row │ Add arbitrary data to SQLite │
397
+ ├────────────────────────────────┼────────────────────────────────────────────────────┤
398
+ │ database import pushshift │ Convert pushshift data to reddit.db format (stdin) │
399
+ ├────────────────────────────────┼────────────────────────────────────────────────────┤
400
+ │ database import nicotine │ Import paths from nicotine+ │
401
+ ├────────────────────────────────┼────────────────────────────────────────────────────┤
402
+ │ database import mpv-watchlater │ Import mpv watchlater files to history │
403
+ ├────────────────────────────────┼────────────────────────────────────────────────────┤
404
+ │ database import places │ Import places of interest (POIs) │
405
+ ╰────────────────────────────────┴────────────────────────────────────────────────────╯
406
+
407
+ Table analysis subcommands:
408
+ ╭────────────────────┬──────────────────────────────────────────────────────╮
409
+ │ table convert csv │ Convert JSON data to CSV │
410
+ ├────────────────────┼──────────────────────────────────────────────────────┤
411
+ │ table convert json │ Convert CSV data to JSON │
412
+ ├────────────────────┼──────────────────────────────────────────────────────┤
413
+ │ table analyze │ Exploratory Data Analysis on table-like files │
414
+ ├────────────────────┼──────────────────────────────────────────────────────┤
415
+ │ table rank │ Multi-criteria Ranking for Decision Support │
416
+ ├────────────────────┼──────────────────────────────────────────────────────┤
417
+ │ table plot │ Plot table-like files. A CLI interface to matplotlib │
418
+ ├────────────────────┼──────────────────────────────────────────────────────┤
419
+ │ table markdown │ Print markdown tables from table-like files │
420
+ ├────────────────────┼──────────────────────────────────────────────────────┤
421
+ │ table columns │ Print columns of table-like files │
422
+ ├────────────────────┼──────────────────────────────────────────────────────┤
423
+ │ table diff │ Diff large table-like files in chunks │
424
+ ╰────────────────────┴──────────────────────────────────────────────────────╯
425
+
426
+ Torrent subcommands:
427
+ ╭────────────────────────┬───────────────────────────────────────────────────────╮
428
+ │ database add torrents │ Add torrent info to SQLite │
429
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
430
+ │ database add computers │ Add computer info to SQLite │
431
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
432
+ │ torrent allocate │ Use computers.db and torrents.db to allocate torrents │
433
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
434
+ │ torrent info │ List torrents (qBittorrent-nox) │
435
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
436
+ │ torrent start │ Start torrents (qBittorrent-nox) │
437
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
438
+ │ torrent remaining │ Overview of torrents by mountpoint (qBittorrent-nox) │
439
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
440
+ │ torrent dump │ Print torrent file info │
441
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
442
+ │ torrent match │ Match files on disk against torrent piece hashes │
443
+ ├────────────────────────┼───────────────────────────────────────────────────────┤
444
+ │ torrent audit │ Find qBittorrent duplicate and orphan files │
445
+ ╰────────────────────────┴───────────────────────────────────────────────────────╯
446
+
447
+ Database updating subcommands:
448
+ ╭────────────────────────────────┬─────────────────────────────────╮
449
+ │ database update filesystem │ Update local media │
450
+ ├────────────────────────────────┼─────────────────────────────────┤
451
+ │ database update video │ Update online video media │
452
+ ├────────────────────────────────┼─────────────────────────────────┤
453
+ │ database update open-directory │ Update open-directory media │
454
+ ├────────────────────────────────┼─────────────────────────────────┤
455
+ │ database update gallery │ Update online gallery media │
456
+ ├────────────────────────────────┼─────────────────────────────────┤
457
+ │ database update reddit │ Update reddit media │
458
+ ├────────────────────────────────┼─────────────────────────────────┤
459
+ │ database update links │ Update a link-scraping database │
460
+ ╰────────────────────────────────┴─────────────────────────────────╯
461
+
462
+ Help subcommands:
463
+ ╭─────────────┬──────────────────────────────────╮
464
+ │ help search │ Search subcommands and help text │
465
+ ╰─────────────┴──────────────────────────────────╯
466
+
467
+
468
+ </details>
469
+
470
+ ## Examples
471
+
472
+ ### Watch online media on your PC
473
+
474
+ wget https://github.com/chapmanjacobd/library-examples/raw/main/mealtime.tw.db
475
+ library play video mealtime.tw.db --random --duration 30m
476
+
477
+ ### Listen to online media on a chromecast group
478
+
479
+ wget https://github.com/chapmanjacobd/library-examples/raw/main/music.tl.db
480
+ library play audio music.tl.db -ct "House speakers" --random
481
+
482
+ ### Hook into HackerNews
483
+
484
+ wget https://github.com/chapmanjacobd/hn_mining/raw/main/hackernews_only_direct.tw.db
485
+ library play video hackernews_only_direct.tw.db --random --ignore-errors
486
+
487
+ ### Organize via separate databases
488
+
489
+ library database add filesystem --audio audiobooks.db ./audiobooks/
490
+ library database add filesystem --audio podcasts.db ./podcasts/ ./another/more/secret/podcasts_folder/
491
+
492
+ # merge later if you want
493
+ library database merge --pk path -t playlists,media audiobooks.db podcasts.db both.db
494
+
495
+ # or split
496
+ library database merge --pk path -t playlists,media both.db audiobooks.db -w 'path like "%/audiobooks/%"'
497
+ library database merge --pk path -t playlists,media both.db podcasts.db -w 'path like "%/podcasts%"'
498
+
499
+ ## Guides
500
+
501
+ ### Music alarm clock
502
+
503
+ <details><summary>via termux crontab</summary>
504
+
505
+ Wake up to your own music
506
+
507
+ 30 7 * * * library play audio ./audio.db
508
+
509
+ Wake up to your own music _only when you are *not* home_ (computer on local IP)
510
+
511
+ 30 7 * * * timeout 0.4 nc -z 192.168.1.12 22 || library play audio --random
512
+
513
+ Wake up to your own music on your Chromecast speaker group _only when you are home_
514
+
515
+ 30 7 * * * ssh 192.168.1.12 library play audio --cast --cast-to "Bedroom pair"
516
+
517
+ </details>
518
+
519
+
520
+ ### Browser Tabs
521
+
522
+ <details><summary>Visit websites on a schedule</summary>
523
+
524
+ `tabs` is a way to organize your visits to URLs that you want to remember every once in a while.
525
+
526
+ The main benefit of tabs is that you can have a large amount of tabs saved (say 500 monthly tabs) and only the smallest
527
+ amount of tabs to satisfy that goal (500/30) tabs open each day. 17 tabs per day seems manageable--500 all at once does not.
528
+
529
+ The use-case of tabs are websites that you know are going to change: subreddits, games,
530
+ or tools that you want to use for a few minutes daily, weekly, monthly, quarterly, or yearly.
531
+
532
+ ### 1. Add your websites
533
+
534
+ library database add tabs tabs.db --frequency monthly --category fun \
535
+ https://old.reddit.com/r/Showerthoughts/top/?sort=top&t=month \
536
+ https://old.reddit.com/r/RedditDayOf/top/?sort=top&t=month
537
+
538
+ ### 2. Add library tabs to cron
539
+
540
+ library tabs open runs **once per day**. Configure it with `crontab`:
541
+
542
+ 45 9 * * * DISPLAY=:0 library tabs open /home/my/tabs.db
543
+
544
+ Or with `systemd`:
545
+
546
+ tee ~/.config/systemd/user/tabs.service
547
+ [Unit]
548
+ Description=library daily browser tabs
549
+
550
+ [Service]
551
+ Type=simple
552
+ RemainAfterExit=no
553
+ Environment="DISPLAY=:0"
554
+ ExecStart=library tabs open /home/my/tabs.db
555
+
556
+ tee ~/.config/systemd/user/tabs.timer
557
+ [Unit]
558
+ Description=library daily browser tabs timer
559
+
560
+ [Timer]
561
+ Persistent=yes
562
+ OnCalendar=*-*-* 9:58
563
+
564
+ [Install]
565
+ WantedBy=timers.target
566
+
567
+ systemctl --user daemon-reload
568
+ systemctl --user enable --now tabs.service
569
+
570
+ You can also invoke tabs manually:
571
+
572
+ library tabs open tabs.db -L 1 # open one tab
573
+
574
+ Incremental surfing. 📈🏄 totally rad!
575
+
576
+ </details>
577
+
578
+ ### Find large folders
579
+
580
+ <details><summary>Curate with library folder large</summary>
581
+
582
+ If you are looking for candidate folders for curation (ie. you need space but don't want to buy another hard drive).
583
+ The folder large subcommand serves that purpose:
584
+
585
+ $ library folder large fs/d.db
586
+
587
+ Filter by folder depth (similar to QDirStat or WizTree)
588
+
589
+ $ library folder large --depth=3 audio.db
590
+
591
+ There is also a flag to prioritize folders which have many files which have been deleted (for example you delete songs you don't like--you can see who wrote those songs and delete all their other songs...)
592
+
593
+ $ library folder large --sort-groups-by deleted audio.db
594
+
595
+ The play subcommands also support this functionality:
596
+
597
+ $ library play video --big-dirs ./my.db
598
+ $ library play video -B # shorthand equivalent
599
+
600
+ </details>
601
+
602
+ <details><summary>Backfill reddit databases with pushshift data</summary>
603
+
604
+ [https://github.com/chapmanjacobd/reddit_mining/](https://github.com/chapmanjacobd/reddit_mining/)
605
+
606
+ ```fish
607
+ for reddit_db in ~/lb/reddit/*.db
608
+ set subreddits (sqlite-utils $reddit_db 'select path from playlists' --tsv --no-headers | grep old.reddit.com | sed 's|https://old.reddit.com/r/\(.*\)/|\1|' | sed 's|https://old.reddit.com/user/\(.*\)/|u_\1|' | tr -d "\r")
609
+
610
+ ~/github/xk/reddit_mining/links/
611
+ for subreddit in $subreddits
612
+ if not test -e "$subreddit.csv"
613
+ echo "octosql -o csv \"select path,score,'https://old.reddit.com/r/$subreddit/' as playlist_path from `../reddit_links.parquet` where lower(playlist_path) = '$subreddit' order by score desc \" > $subreddit.csv"
614
+ end
615
+ end | parallel -j8
616
+
617
+ for subreddit in $subreddits
618
+ sqlite-utils upsert --pk path --alter --csv --detect-types $reddit_db media $subreddit.csv
619
+ end
620
+
621
+ library database add video --safe --ignore-errors --force $reddit_db (sqlite-utils --raw-lines $reddit_db 'select path from media')
622
+ end
623
+ ```
624
+
625
+ </details>
626
+
627
+ ### Datasette
628
+
629
+ <details><summary>Explore `library` databases in your browser</summary>
630
+
631
+ pip install datasette
632
+ datasette tv.db
633
+
634
+ </details>
635
+
636
+ ### Pipe to [fzf](https://github.com/junegunn/fzf)
637
+
638
+ <details><summary>Choose a video to play</summary>
639
+
640
+ You can use fzf in a subshell to choose a specific video from 1000 random options
641
+
642
+ library play video ~/lb/video.db (library play video ~/lb/video.db -pf --random -L 1000 | fzf)
643
+
644
+ </details>
645
+
646
+ ### Pipe to [mnamer](https://github.com/jkwill87/mnamer)
647
+
648
+ <details><summary>Rename poorly named files</summary>
649
+
650
+ pip install mnamer
651
+ mnamer --movie-directory ~/d/70_Now_Watching/ --episode-directory ~/d/70_Now_Watching/ \
652
+ --no-overwrite -b (library play video -p fd -s 'path : McCloud')
653
+ library database add filesystem ~/d/70_Now_Watching/
654
+
655
+ </details>
656
+
657
+ ### Pipe to [lowcharts](https://github.com/juan-leon/lowcharts)
658
+
659
+ <details><summary>$ library play video -p f -col time_created | lowcharts timehist -w 80</summary>
660
+
661
+ Matches: 445183.
662
+ Each ∎ represents a count of 1896
663
+ [2022-04-13 03:16:05] [151689] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
664
+ [2022-04-19 07:59:37] [ 16093] ∎∎∎∎∎∎∎∎
665
+ [2022-04-25 12:43:09] [ 12019] ∎∎∎∎∎∎
666
+ [2022-05-01 17:26:41] [ 48817] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
667
+ [2022-05-07 22:10:14] [ 36259] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
668
+ [2022-05-14 02:53:46] [ 3942] ∎∎
669
+ [2022-05-20 07:37:18] [ 2371] ∎
670
+ [2022-05-26 12:20:50] [ 517]
671
+ [2022-06-01 17:04:23] [ 4845] ∎∎
672
+ [2022-06-07 21:47:55] [ 2340] ∎
673
+ [2022-06-14 02:31:27] [ 563]
674
+ [2022-06-20 07:14:59] [ 13836] ∎∎∎∎∎∎∎
675
+ [2022-06-26 11:58:32] [ 1905] ∎
676
+ [2022-07-02 16:42:04] [ 1269]
677
+ [2022-07-08 21:25:36] [ 3062] ∎
678
+ [2022-07-15 02:09:08] [ 9192] ∎∎∎∎
679
+ [2022-07-21 06:52:41] [ 11955] ∎∎∎∎∎∎
680
+ [2022-07-27 11:36:13] [ 50938] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
681
+ [2022-08-02 16:19:45] [ 70973] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
682
+ [2022-08-08 21:03:17] [ 2598] ∎
683
+
684
+ BTW, for some cols like time_deleted you'll need to specify a where clause so they aren't filtered out:
685
+
686
+ $ library play video -p f -col time_deleted -w time_deleted'>'0 | lowcharts timehist -w 80
687
+
688
+ ![video width](https://user-images.githubusercontent.com/7908073/184737808-b96fbe65-a1d9-43c2-b6b4-4bdfab592190.png)
689
+
690
+ ![fps](https://user-images.githubusercontent.com/7908073/184738438-ee566a4b-2da0-4e6d-a4b3-9bfca036aa2a.png)
691
+
692
+ </details>
693
+
694
+ ## Usage
695
+
696
+
697
+ ### Media collection subcommands
698
+
699
+ ###### database add filesystem
700
+
701
+ <details><summary>Add local media</summary>
702
+
703
+ $ library database add filesystem -h
704
+ usage: library database add filesystem [(--video) | --audio | --image | --text | --filesystem | --auto] DATABASE PATH ...
705
+
706
+ The default database type is auto: every media type is scanned (video, audio, image, text)
707
+
708
+ library database add filesystem tv.db ./tv/
709
+ library database add filesystem --video tv.db ./tv/ # scan only video
710
+ library database add filesystem --auto mixed.db ./media/ # equivalent to the default
711
+
712
+ You can also create audio databases. Both audio and video use ffmpeg to read metadata
713
+
714
+ library database add filesystem --audio audio.db ./music/
715
+
716
+ Image uses ExifTool
717
+
718
+ library database add filesystem --image image.db ./photos/
719
+
720
+ Text will try to read files and save the contents into a searchable database
721
+
722
+ library database add filesystem --text text.db ./documents_and_books/
723
+
724
+ Create a text database and scan with OCR and speech-recognition
725
+
726
+ library database add filesystem --text --ocr --speech-recognition ocr.db ./receipts_and_messages/
727
+
728
+ Create a video database and read internal/external subtitle files into a searchable database
729
+
730
+ library database add filesystem --scan-subtitles tv.search.db ./tv/ ./movies/
731
+
732
+ Decode media to check for corruption (slow)
733
+
734
+ library database add filesystem --check-corrupt
735
+ # See media-check command for full options
736
+
737
+ Normally only relevant filetypes are included. You can scan all files with this flag
738
+
739
+ library database add filesystem --scan-all-files mixed.db ./tv-and-maybe-audio-only-files/
740
+ # I use that with this to keep my folders organized
741
+ library play video --db mixed.db -w 'video_count=0 and audio_count>=1' -pf | parallel mv {} ~/d/82_Audiobooks/
742
+
743
+ Remove path roots with --force
744
+
745
+ library database add filesystem audio.db /mnt/d/Youtube/
746
+ [/mnt/d/Youtube] Path does not exist
747
+
748
+ library database add filesystem --force audio.db /mnt/d/Youtube/
749
+ [/mnt/d/Youtube] Path does not exist
750
+ [/mnt/d/Youtube] Building file list...
751
+ [/mnt/d/Youtube] Marking 28932 orphaned metadata records as deleted
752
+
753
+ If you run out of RAM, for example scanning large VR videos, you can lower the number of threads via --threads
754
+
755
+ library database add filesystem vr.db --delete-unplayable --check-corrupt --full-scan-if-corrupt 15% --delete-corrupt 20% ./vr/ --threads 3
756
+
757
+ Move files on import
758
+
759
+ library database add filesystem audio.db --move ~/library/ ./added_folder/
760
+ This will run destination paths through `library file normalize` and move files relative to the added folder root
761
+
762
+
763
+ </details>
764
+
765
+ ###### database add video
766
+
767
+ <details><summary>Add online video media (yt-dlp)</summary>
768
+
769
+ $ library database add video -h
770
+ usage: library database add video [--safe] [--extra] [--subs] [--auto-subs] DATABASE URL ...
771
+
772
+ Create a dl database / add links to an existing database
773
+
774
+ library database add video dl.db https://www.youdl.com/c/BranchEducation/videos
775
+
776
+ Add links from a line-delimited file
777
+
778
+ cat ./my_yt_subscriptions.txt | library database add video reddit.db -
779
+
780
+ Add metadata to links already in a database table
781
+
782
+ library database add video --force reddit.db (sqlite-utils --raw-lines reddit.db 'select path from media')
783
+
784
+ Fetch extra metadata
785
+
786
+ By default tubeadd will quickly add media at the expense of less metadata.
787
+ If you plan on using `library database download` then it doesn't make sense to use `--extra`.
788
+ Downloading will add the extra metadata automatically to the database.
789
+ You can always fetch more metadata later via tubeupdate
790
+ library database update video tw.db --extra
791
+
792
+
793
+ </details>
794
+
795
+ ###### database add gallery
796
+
797
+ <details><summary>Add online gallery media (gallery-dl)</summary>
798
+
799
+ $ library database add gallery -h
800
+ usage: library database add gallery DATABASE URL ...
801
+
802
+ Add gallery_dl URLs to download later or periodically update
803
+
804
+ If you have many URLs use stdin
805
+
806
+ cat ./my-favorite-manhwa.txt | library database add gallery your.db --insert-only -
807
+
808
+
809
+ </details>
810
+
811
+ ###### database add open-directory
812
+
813
+ <details><summary>Discover media in HTTP directory listings and folder-like web indexes</summary>
814
+
815
+ $ library database add open-directory -h
816
+ usage: library database add open-directory [--auto | --filesystem | --video | --audio | --image | --text] DATABASE URL ...
817
+
818
+ Discover media files in Apache/Nginx-style directory listings and other
819
+ folder-like web indexes. It follows HTML subdirectories, treats non-HTML
820
+ links as media, and can collect filesystem, hash, audio, video, image, and
821
+ text metadata using one or more profiles.
822
+
823
+ library database add open-directory open_dir.db http://1.1.1.1/ # simple spider
824
+ library database add open-directory open_dir.db --filesystem http://1.1.1.1/
825
+ library database add open-directory open_dir.db --video http://1.1.1.1/
826
+ library database add open-directory open_dir.db --filesystem --video http://1.1.1.1/
827
+
828
+ Re-scan using a different profile
829
+
830
+ library play media --db v.db -w 'size is null' --ext wmv,3gp,mp4 -pf | library database add open-directory --media --force --video v.db -
831
+
832
+ Check download size of all videos matching some criteria
833
+
834
+ library database download --fs open_dir.db --prefix ~/d/dump/video/ -w 'height<720' -E preview -pa
835
+
836
+ path count download_duration size avg_size
837
+ --------- ------- ---------------------------- --------- ----------
838
+ Aggregate 5694 2 years, 7 months and 5 days 724.4 GiB 130.3 MiB
839
+
840
+ Download all videos matching some criteria
841
+
842
+ library database download --fs open_dir.db --prefix ~/d/dump/video/ -w 'height<720' -E preview
843
+
844
+ Stream directly to mpv
845
+
846
+ library play video --db open_dir.db
847
+
848
+
849
+ </details>
850
+
851
+ ###### database add reddit
852
+
853
+ <details><summary>Create a reddit database; Add subreddits</summary>
854
+
855
+ $ library database add reddit -h
856
+ usage: library database add reddit [--lookback N_DAYS] [--praw-site bot1] DATABASE URL ...
857
+
858
+ Fetch data for redditors and reddits
859
+
860
+ library database add reddit interesting.db https://old.reddit.com/r/coolgithubprojects/ https://old.reddit.com/user/Diastro
861
+
862
+ If you have a file with a list of subreddits you can do this
863
+
864
+ library database add reddit 96_Weird_History.db --subreddits (cat ~/mc/96_Weird_History-reddit.txt)
865
+
866
+ Likewise for redditors
867
+
868
+ library database add reddit shadow_banned.db --redditors (cat ~/mc/shadow_banned.txt)
869
+
870
+ To remove entries (for example when you get 404s)
871
+
872
+ library database search reddit.db playlists --or --exact subreddit1 subreddit2 --soft-delete
873
+
874
+ Note that reddit's API is limited to 1000 posts and it usually doesn't go back very far historically.
875
+ Also, it may be the case that reddit's API (praw) will stop working in the near future. For both of these problems
876
+ my suggestion is to use pushshift data.
877
+ You can find more info here: https://github.com/chapmanjacobd/reddit_mining#how-was-this-made
878
+
879
+
880
+ </details>
881
+
882
+ ###### database add hackernews
883
+
884
+ <details><summary>Create / Update a Hacker News database</summary>
885
+
886
+ $ library database add hackernews -h
887
+ usage: library database add hackernews [--oldest] DATABASE
888
+
889
+ Fetch latest stories first
890
+
891
+ library database add hackernews hn.db -v
892
+ Fetching 154873 items (33212696 to 33367569)
893
+ Saving comment 33367568
894
+ Saving comment 33367543
895
+ Saving comment 33367564
896
+ ...
897
+
898
+ Fetch oldest stories first
899
+
900
+ library database add hackernews --oldest hn.db
901
+
902
+
903
+ </details>
904
+
905
+ ###### database add getty
906
+
907
+ <details><summary>Create / Update a Getty Museum database</summary>
908
+
909
+ $ library database add getty -h
910
+ usage: library database add getty DATABASE
911
+
912
+ Download metadata
913
+
914
+ library database add getty getty.db -v
915
+
916
+ Download images
917
+
918
+ library database download --fs getty.db --prefix ~/images/ -v
919
+
920
+
921
+ </details>
922
+
923
+ ###### database add substack
924
+
925
+ <details><summary>Backup substack articles</summary>
926
+
927
+ $ library database add substack -h
928
+ usage: library database add substack DATABASE PATH ...
929
+
930
+ Backup substack articles
931
+
932
+
933
+ </details>
934
+
935
+ ###### database add tildes
936
+
937
+ <details><summary>Backup tildes comments and topics</summary>
938
+
939
+ $ library database add tildes -h
940
+ usage: library database add tildes DATABASE USER
941
+
942
+ Backup tildes.net user comments and topics
943
+
944
+ library database add tildes tildes.net.db xk3
945
+
946
+ Without cookies you are limited to the first page. You can use cookies like this
947
+ https://github.com/rotemdan/ExportCookies
948
+ library database add tildes tildes.net.db xk3 --cookies ~/Downloads/cookies-tildes-net.txt
949
+
950
+
951
+ </details>
952
+
953
+ ### Playback subcommands
954
+
955
+ ###### play media
956
+
957
+ <details><summary>Play media of any supported type</summary>
958
+
959
+ $ library play media -h
960
+ usage: library play media [PATH ...] [--db DATABASE] [optional args]
961
+
962
+ Control playback
963
+
964
+ To stop playback press Ctrl-C in either the terminal or mpv
965
+
966
+ Or use `library playback next` or `library playback stop`
967
+
968
+ Or create global shortcuts in your desktop environment by sending commands to mpv_socket
969
+ echo 'playlist-next force' | socat - $XDG_RUNTIME_DIR/mpv_socket # library play audio default
970
+ echo 'playlist-next force' | socat - ~/.config/mpv/socket # library play video default
971
+
972
+ If you prefer you can also send mpv the playlist, but this is incompatible with post-actions
973
+ mpv --playlist=(library play video --db videos.db --ext mp4 -l 50 -p fw | psub) # fish shell, mark 50 videos as watched
974
+ mpv --playlist=<(library play video --db videos.db --ext mp4 -p f) # BASH, all videos
975
+
976
+ Print an aggregate report of deleted media
977
+
978
+ library filesystem list -w time_deleted!=0 -pa
979
+ path count duration size
980
+ --------- ------- ------------------ -------
981
+ Aggregate 337 2 days and 5 hours 1.6 GiB
982
+
983
+ Print an aggregate report of media that has no duration information (ie.
984
+ online media or corrupt local media)
985
+
986
+ library play media -w 'duration is null' -pa
987
+
988
+ Print a list of filenames which have below 1280px resolution
989
+
990
+ library play media -w 'width<1280' -pf
991
+
992
+ View how much time you have played
993
+
994
+ library play media -w play_count'>'0 -pa
995
+
996
+ View all the columns
997
+
998
+ library play media -p -L 1 --cols '*'
999
+
1000
+ Open ipython with all of your media
1001
+
1002
+ library play media -vv -p --cols '*'
1003
+ ipdb> len(media)
1004
+ 462219
1005
+
1006
+ View most recent files
1007
+
1008
+ library play media --db web_add.image.db -u time_modified desc --cols path,width,height,size,time_modified -p -l 10
1009
+ path width height size time_modified
1010
+ ---------------------------------------------------------------------------------------------------------------------- ------- -------- --------- -----------------
1011
+ https://siliconpr0n.org/map/infineon/m7690-b1/single/infineon_m7690-b1_infosecdj_mz_nikon20x.jpg 7066 10513 16.4 MiB 2 days ago, 20:54
1012
+ https://siliconpr0n.org/map/starchip/scf384g/single/starchip_scf384g_infosecdj_mz_nikon20x.jpg 10804 10730 19.2 MiB 2 days ago, 15:31
1013
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms50x-1.25.jpg 28966 26816 192.2 MiB 4 days ago, 15:05
1014
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms20x-1.25.jpg 11840 10978 49.2 MiB 4 days ago, 15:04
1015
+ https://siliconpr0n.org/map/hp/2hpt20065-1/single/hp_2hpt20065-1_marmontel_mz_ms10x-1.25.jpg 16457 14255 101.4 MiB 4 days ago, 15:03
1016
+ https://siliconpr0n.org/map/pervasive/e2213ps01e1/single/pervasive_e2213ps01e1_azonenberg_back_roi1_mit10x_rotated.jpg 18880 61836 136.8 MiB 6 days ago, 16:00
1017
+ https://siliconpr0n.org/map/pervasive/e2213ps01e/single/pervasive_e2213ps01e_azonenberg_back_mit5x_rotated.jpg 62208 30736 216.5 MiB 6 days ago, 15:57
1018
+ https://siliconpr0n.org/map/amd/am2964bpc/single/amd_am2964bpc_infosecdj_mz_lmplan10x.jpg 12809 11727 39.8 MiB 6 days ago, 10:28
1019
+ https://siliconpr0n.org/map/unknown/ks1804ir1/single/unknown_ks1804ir1_infosecdj_mz_lmplan10x.jpg 6508 6707 8.4 MiB 6 days ago, 08:04
1020
+ https://siliconpr0n.org/map/amd/am2960dc-b/single/amd_am2960dc-b_infosecdj_mz_lmplan10x.jpg 16434 15035 64.9 MiB 7 days ago, 19:01
1021
+ 10 media (limited by --limit 10)
1022
+
1023
+ How I use it
1024
+
1025
+ library play audio --db ~/lb/audio.db --local-media-only -k delete-if-audiobook -w play_count=0 --fetch-siblings each
1026
+ library play video --db ~/lb/video.db --local-media-only -k delete --cmd5 'echo skip'
1027
+
1028
+ When sorting videos
1029
+ focus_under_mouse
1030
+ library play video --db ~/lb/sort.db --action ask_move_or_delete --keep-dir /home/xk/d/library/video/ --loop --exit-code-confirm -i --cmd130 exit_multiple_playback --cmd5 'library shrink audio --no-preserve-video' --cmd6 'mv {} /mnt/d/library/vr/' -m 4 --start 35% --volume=0 -u size desc
1031
+ focus_follows_mouse
1032
+
1033
+ On-the-go mobile smartphone mode (Android)
1034
+ repeat library play video --db ~/lb/video.db --player termux-open -L1 --refresh --action ask_move_or_delete --keep-dir ~/sync/video/keep/ --portrait -u duration desc
1035
+
1036
+
1037
+ </details>
1038
+
1039
+ ###### play video
1040
+
1041
+ <details><summary>Watch / Listen</summary>
1042
+
1043
+ $ library play video -h
1044
+ usage: library play video [PATH ...] [--db DATABASE] [optional args]
1045
+
1046
+ Control playback
1047
+
1048
+ To stop playback press Ctrl-C in either the terminal or mpv
1049
+
1050
+ Or use `library playback next` or `library playback stop`
1051
+
1052
+ Or create global shortcuts in your desktop environment by sending commands to mpv_socket
1053
+ echo 'playlist-next force' | socat - $XDG_RUNTIME_DIR/mpv_socket # library play audio default
1054
+ echo 'playlist-next force' | socat - ~/.config/mpv/socket # library play video default
1055
+
1056
+ If you prefer you can also send mpv the playlist, but this is incompatible with post-actions
1057
+ mpv --playlist=(library play video --db videos.db --ext mp4 -l 50 -p fw | psub) # fish shell, mark 50 videos as watched
1058
+ mpv --playlist=<(library play video --db videos.db --ext mp4 -p f) # BASH, all videos
1059
+
1060
+ Print an aggregate report of deleted media
1061
+
1062
+ library filesystem list -w time_deleted!=0 -pa
1063
+ path count duration size
1064
+ --------- ------- ------------------ -------
1065
+ Aggregate 337 2 days and 5 hours 1.6 GiB
1066
+
1067
+ Print an aggregate report of media that has no duration information (ie.
1068
+ online media or corrupt local media)
1069
+
1070
+ library play video -w 'duration is null' -pa
1071
+
1072
+ Print a list of filenames which have below 1280px resolution
1073
+
1074
+ library play video -w 'width<1280' -pf
1075
+
1076
+ View how much time you have played
1077
+
1078
+ library play video -w play_count'>'0 -pa
1079
+
1080
+ View all the columns
1081
+
1082
+ library play video -p -L 1 --cols '*'
1083
+
1084
+ Open ipython with all of your media
1085
+
1086
+ library play video -vv -p --cols '*'
1087
+ ipdb> len(media)
1088
+ 462219
1089
+
1090
+ View most recent files
1091
+
1092
+ library play video --db web_add.image.db -u time_modified desc --cols path,width,height,size,time_modified -p -l 10
1093
+ path width height size time_modified
1094
+ ---------------------------------------------------------------------------------------------------------------------- ------- -------- --------- -----------------
1095
+ https://siliconpr0n.org/map/infineon/m7690-b1/single/infineon_m7690-b1_infosecdj_mz_nikon20x.jpg 7066 10513 16.4 MiB 2 days ago, 20:54
1096
+ https://siliconpr0n.org/map/starchip/scf384g/single/starchip_scf384g_infosecdj_mz_nikon20x.jpg 10804 10730 19.2 MiB 2 days ago, 15:31
1097
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms50x-1.25.jpg 28966 26816 192.2 MiB 4 days ago, 15:05
1098
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms20x-1.25.jpg 11840 10978 49.2 MiB 4 days ago, 15:04
1099
+ https://siliconpr0n.org/map/hp/2hpt20065-1/single/hp_2hpt20065-1_marmontel_mz_ms10x-1.25.jpg 16457 14255 101.4 MiB 4 days ago, 15:03
1100
+ https://siliconpr0n.org/map/pervasive/e2213ps01e1/single/pervasive_e2213ps01e1_azonenberg_back_roi1_mit10x_rotated.jpg 18880 61836 136.8 MiB 6 days ago, 16:00
1101
+ https://siliconpr0n.org/map/pervasive/e2213ps01e/single/pervasive_e2213ps01e_azonenberg_back_mit5x_rotated.jpg 62208 30736 216.5 MiB 6 days ago, 15:57
1102
+ https://siliconpr0n.org/map/amd/am2964bpc/single/amd_am2964bpc_infosecdj_mz_lmplan10x.jpg 12809 11727 39.8 MiB 6 days ago, 10:28
1103
+ https://siliconpr0n.org/map/unknown/ks1804ir1/single/unknown_ks1804ir1_infosecdj_mz_lmplan10x.jpg 6508 6707 8.4 MiB 6 days ago, 08:04
1104
+ https://siliconpr0n.org/map/amd/am2960dc-b/single/amd_am2960dc-b_infosecdj_mz_lmplan10x.jpg 16434 15035 64.9 MiB 7 days ago, 19:01
1105
+ 10 media (limited by --limit 10)
1106
+
1107
+ How I use it
1108
+
1109
+ library play audio --db ~/lb/audio.db --local-media-only -k delete-if-audiobook -w play_count=0 --fetch-siblings each
1110
+ library play video --db ~/lb/video.db --local-media-only -k delete --cmd5 'echo skip'
1111
+
1112
+ When sorting videos
1113
+ focus_under_mouse
1114
+ library play video --db ~/lb/sort.db --action ask_move_or_delete --keep-dir /home/xk/d/library/video/ --loop --exit-code-confirm -i --cmd130 exit_multiple_playback --cmd5 'library shrink audio --no-preserve-video' --cmd6 'mv {} /mnt/d/library/vr/' -m 4 --start 35% --volume=0 -u size desc
1115
+ focus_follows_mouse
1116
+
1117
+ On-the-go mobile smartphone mode (Android)
1118
+ repeat library play video --db ~/lb/video.db --player termux-open -L1 --refresh --action ask_move_or_delete --keep-dir ~/sync/video/keep/ --portrait -u duration desc
1119
+
1120
+
1121
+ </details>
1122
+
1123
+ ###### play audio
1124
+
1125
+ <details><summary>Listen to audio</summary>
1126
+
1127
+ $ library play audio -h
1128
+ usage: library play audio [PATH ...] [--db DATABASE] [optional args]
1129
+
1130
+ Control playback
1131
+
1132
+ To stop playback press Ctrl-C in either the terminal or mpv
1133
+
1134
+ Or use `library playback next` or `library playback stop`
1135
+
1136
+ Or create global shortcuts in your desktop environment by sending commands to mpv_socket
1137
+ echo 'playlist-next force' | socat - $XDG_RUNTIME_DIR/mpv_socket # library play audio default
1138
+ echo 'playlist-next force' | socat - ~/.config/mpv/socket # library play video default
1139
+
1140
+ If you prefer you can also send mpv the playlist, but this is incompatible with post-actions
1141
+ mpv --playlist=(library play video --db videos.db --ext mp4 -l 50 -p fw | psub) # fish shell, mark 50 videos as watched
1142
+ mpv --playlist=<(library play video --db videos.db --ext mp4 -p f) # BASH, all videos
1143
+
1144
+ Print an aggregate report of deleted media
1145
+
1146
+ library filesystem list -w time_deleted!=0 -pa
1147
+ path count duration size
1148
+ --------- ------- ------------------ -------
1149
+ Aggregate 337 2 days and 5 hours 1.6 GiB
1150
+
1151
+ Print an aggregate report of media that has no duration information (ie.
1152
+ online media or corrupt local media)
1153
+
1154
+ library play audio -w 'duration is null' -pa
1155
+
1156
+ Print a list of filenames which have below 1280px resolution
1157
+
1158
+ library play audio -w 'width<1280' -pf
1159
+
1160
+ View how much time you have played
1161
+
1162
+ library play audio -w play_count'>'0 -pa
1163
+
1164
+ View all the columns
1165
+
1166
+ library play audio -p -L 1 --cols '*'
1167
+
1168
+ Open ipython with all of your media
1169
+
1170
+ library play audio -vv -p --cols '*'
1171
+ ipdb> len(media)
1172
+ 462219
1173
+
1174
+ View most recent files
1175
+
1176
+ library play audio --db web_add.image.db -u time_modified desc --cols path,width,height,size,time_modified -p -l 10
1177
+ path width height size time_modified
1178
+ ---------------------------------------------------------------------------------------------------------------------- ------- -------- --------- -----------------
1179
+ https://siliconpr0n.org/map/infineon/m7690-b1/single/infineon_m7690-b1_infosecdj_mz_nikon20x.jpg 7066 10513 16.4 MiB 2 days ago, 20:54
1180
+ https://siliconpr0n.org/map/starchip/scf384g/single/starchip_scf384g_infosecdj_mz_nikon20x.jpg 10804 10730 19.2 MiB 2 days ago, 15:31
1181
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms50x-1.25.jpg 28966 26816 192.2 MiB 4 days ago, 15:05
1182
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms20x-1.25.jpg 11840 10978 49.2 MiB 4 days ago, 15:04
1183
+ https://siliconpr0n.org/map/hp/2hpt20065-1/single/hp_2hpt20065-1_marmontel_mz_ms10x-1.25.jpg 16457 14255 101.4 MiB 4 days ago, 15:03
1184
+ https://siliconpr0n.org/map/pervasive/e2213ps01e1/single/pervasive_e2213ps01e1_azonenberg_back_roi1_mit10x_rotated.jpg 18880 61836 136.8 MiB 6 days ago, 16:00
1185
+ https://siliconpr0n.org/map/pervasive/e2213ps01e/single/pervasive_e2213ps01e_azonenberg_back_mit5x_rotated.jpg 62208 30736 216.5 MiB 6 days ago, 15:57
1186
+ https://siliconpr0n.org/map/amd/am2964bpc/single/amd_am2964bpc_infosecdj_mz_lmplan10x.jpg 12809 11727 39.8 MiB 6 days ago, 10:28
1187
+ https://siliconpr0n.org/map/unknown/ks1804ir1/single/unknown_ks1804ir1_infosecdj_mz_lmplan10x.jpg 6508 6707 8.4 MiB 6 days ago, 08:04
1188
+ https://siliconpr0n.org/map/amd/am2960dc-b/single/amd_am2960dc-b_infosecdj_mz_lmplan10x.jpg 16434 15035 64.9 MiB 7 days ago, 19:01
1189
+ 10 media (limited by --limit 10)
1190
+
1191
+ How I use it
1192
+
1193
+ library play audio --db ~/lb/audio.db --local-media-only -k delete-if-audiobook -w play_count=0 --fetch-siblings each
1194
+ library play video --db ~/lb/video.db --local-media-only -k delete --cmd5 'echo skip'
1195
+
1196
+ When sorting videos
1197
+ focus_under_mouse
1198
+ library play video --db ~/lb/sort.db --action ask_move_or_delete --keep-dir /home/xk/d/library/video/ --loop --exit-code-confirm -i --cmd130 exit_multiple_playback --cmd5 'library shrink audio --no-preserve-video' --cmd6 'mv {} /mnt/d/library/vr/' -m 4 --start 35% --volume=0 -u size desc
1199
+ focus_follows_mouse
1200
+
1201
+ On-the-go mobile smartphone mode (Android)
1202
+ repeat library play video --db ~/lb/video.db --player termux-open -L1 --refresh --action ask_move_or_delete --keep-dir ~/sync/video/keep/ --portrait -u duration desc
1203
+
1204
+
1205
+ </details>
1206
+
1207
+ ###### play text
1208
+
1209
+ <details><summary>Read text</summary>
1210
+
1211
+ $ library play text -h
1212
+ usage: library play text [PATH ...] [--db DATABASE] [optional args]
1213
+
1214
+ Control playback
1215
+
1216
+ To stop playback press Ctrl-C in either the terminal or mpv
1217
+
1218
+ Or use `library playback next` or `library playback stop`
1219
+
1220
+ Or create global shortcuts in your desktop environment by sending commands to mpv_socket
1221
+ echo 'playlist-next force' | socat - $XDG_RUNTIME_DIR/mpv_socket # library play audio default
1222
+ echo 'playlist-next force' | socat - ~/.config/mpv/socket # library play video default
1223
+
1224
+ If you prefer you can also send mpv the playlist, but this is incompatible with post-actions
1225
+ mpv --playlist=(library play video --db videos.db --ext mp4 -l 50 -p fw | psub) # fish shell, mark 50 videos as watched
1226
+ mpv --playlist=<(library play video --db videos.db --ext mp4 -p f) # BASH, all videos
1227
+
1228
+ Print an aggregate report of deleted media
1229
+
1230
+ library filesystem list -w time_deleted!=0 -pa
1231
+ path count duration size
1232
+ --------- ------- ------------------ -------
1233
+ Aggregate 337 2 days and 5 hours 1.6 GiB
1234
+
1235
+ Print an aggregate report of media that has no duration information (ie.
1236
+ online media or corrupt local media)
1237
+
1238
+ library play text -w 'duration is null' -pa
1239
+
1240
+ Print a list of filenames which have below 1280px resolution
1241
+
1242
+ library play text -w 'width<1280' -pf
1243
+
1244
+ View how much time you have played
1245
+
1246
+ library play text -w play_count'>'0 -pa
1247
+
1248
+ View all the columns
1249
+
1250
+ library play text -p -L 1 --cols '*'
1251
+
1252
+ Open ipython with all of your media
1253
+
1254
+ library play text -vv -p --cols '*'
1255
+ ipdb> len(media)
1256
+ 462219
1257
+
1258
+ View most recent files
1259
+
1260
+ library play text --db web_add.image.db -u time_modified desc --cols path,width,height,size,time_modified -p -l 10
1261
+ path width height size time_modified
1262
+ ---------------------------------------------------------------------------------------------------------------------- ------- -------- --------- -----------------
1263
+ https://siliconpr0n.org/map/infineon/m7690-b1/single/infineon_m7690-b1_infosecdj_mz_nikon20x.jpg 7066 10513 16.4 MiB 2 days ago, 20:54
1264
+ https://siliconpr0n.org/map/starchip/scf384g/single/starchip_scf384g_infosecdj_mz_nikon20x.jpg 10804 10730 19.2 MiB 2 days ago, 15:31
1265
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms50x-1.25.jpg 28966 26816 192.2 MiB 4 days ago, 15:05
1266
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms20x-1.25.jpg 11840 10978 49.2 MiB 4 days ago, 15:04
1267
+ https://siliconpr0n.org/map/hp/2hpt20065-1/single/hp_2hpt20065-1_marmontel_mz_ms10x-1.25.jpg 16457 14255 101.4 MiB 4 days ago, 15:03
1268
+ https://siliconpr0n.org/map/pervasive/e2213ps01e1/single/pervasive_e2213ps01e1_azonenberg_back_roi1_mit10x_rotated.jpg 18880 61836 136.8 MiB 6 days ago, 16:00
1269
+ https://siliconpr0n.org/map/pervasive/e2213ps01e/single/pervasive_e2213ps01e_azonenberg_back_mit5x_rotated.jpg 62208 30736 216.5 MiB 6 days ago, 15:57
1270
+ https://siliconpr0n.org/map/amd/am2964bpc/single/amd_am2964bpc_infosecdj_mz_lmplan10x.jpg 12809 11727 39.8 MiB 6 days ago, 10:28
1271
+ https://siliconpr0n.org/map/unknown/ks1804ir1/single/unknown_ks1804ir1_infosecdj_mz_lmplan10x.jpg 6508 6707 8.4 MiB 6 days ago, 08:04
1272
+ https://siliconpr0n.org/map/amd/am2960dc-b/single/amd_am2960dc-b_infosecdj_mz_lmplan10x.jpg 16434 15035 64.9 MiB 7 days ago, 19:01
1273
+ 10 media (limited by --limit 10)
1274
+
1275
+ How I use it
1276
+
1277
+ library play audio --db ~/lb/audio.db --local-media-only -k delete-if-audiobook -w play_count=0 --fetch-siblings each
1278
+ library play video --db ~/lb/video.db --local-media-only -k delete --cmd5 'echo skip'
1279
+
1280
+ When sorting videos
1281
+ focus_under_mouse
1282
+ library play video --db ~/lb/sort.db --action ask_move_or_delete --keep-dir /home/xk/d/library/video/ --loop --exit-code-confirm -i --cmd130 exit_multiple_playback --cmd5 'library shrink audio --no-preserve-video' --cmd6 'mv {} /mnt/d/library/vr/' -m 4 --start 35% --volume=0 -u size desc
1283
+ focus_follows_mouse
1284
+
1285
+ On-the-go mobile smartphone mode (Android)
1286
+ repeat library play video --db ~/lb/video.db --player termux-open -L1 --refresh --action ask_move_or_delete --keep-dir ~/sync/video/keep/ --portrait -u duration desc
1287
+
1288
+
1289
+ </details>
1290
+
1291
+ ###### play images
1292
+
1293
+ <details><summary>View images</summary>
1294
+
1295
+ $ library play images -h
1296
+ usage: library play images [PATH ...] [--db DATABASE] [optional args]
1297
+
1298
+ Control playback
1299
+
1300
+ To stop playback press Ctrl-C in either the terminal or mpv
1301
+
1302
+ Or use `library playback next` or `library playback stop`
1303
+
1304
+ Or create global shortcuts in your desktop environment by sending commands to mpv_socket
1305
+ echo 'playlist-next force' | socat - $XDG_RUNTIME_DIR/mpv_socket # library play audio default
1306
+ echo 'playlist-next force' | socat - ~/.config/mpv/socket # library play video default
1307
+
1308
+ If you prefer you can also send mpv the playlist, but this is incompatible with post-actions
1309
+ mpv --playlist=(library play video --db videos.db --ext mp4 -l 50 -p fw | psub) # fish shell, mark 50 videos as watched
1310
+ mpv --playlist=<(library play video --db videos.db --ext mp4 -p f) # BASH, all videos
1311
+
1312
+ Print an aggregate report of deleted media
1313
+
1314
+ library filesystem list -w time_deleted!=0 -pa
1315
+ path count duration size
1316
+ --------- ------- ------------------ -------
1317
+ Aggregate 337 2 days and 5 hours 1.6 GiB
1318
+
1319
+ Print an aggregate report of media that has no duration information (ie.
1320
+ online media or corrupt local media)
1321
+
1322
+ library play images -w 'duration is null' -pa
1323
+
1324
+ Print a list of filenames which have below 1280px resolution
1325
+
1326
+ library play images -w 'width<1280' -pf
1327
+
1328
+ View how much time you have played
1329
+
1330
+ library play images -w play_count'>'0 -pa
1331
+
1332
+ View all the columns
1333
+
1334
+ library play images -p -L 1 --cols '*'
1335
+
1336
+ Open ipython with all of your media
1337
+
1338
+ library play images -vv -p --cols '*'
1339
+ ipdb> len(media)
1340
+ 462219
1341
+
1342
+ View most recent files
1343
+
1344
+ library play images --db web_add.image.db -u time_modified desc --cols path,width,height,size,time_modified -p -l 10
1345
+ path width height size time_modified
1346
+ ---------------------------------------------------------------------------------------------------------------------- ------- -------- --------- -----------------
1347
+ https://siliconpr0n.org/map/infineon/m7690-b1/single/infineon_m7690-b1_infosecdj_mz_nikon20x.jpg 7066 10513 16.4 MiB 2 days ago, 20:54
1348
+ https://siliconpr0n.org/map/starchip/scf384g/single/starchip_scf384g_infosecdj_mz_nikon20x.jpg 10804 10730 19.2 MiB 2 days ago, 15:31
1349
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms50x-1.25.jpg 28966 26816 192.2 MiB 4 days ago, 15:05
1350
+ https://siliconpr0n.org/map/hp/2hpt20065-1-68k-core/single/hp_2hpt20065-1-68k-core_marmontel_mz_ms20x-1.25.jpg 11840 10978 49.2 MiB 4 days ago, 15:04
1351
+ https://siliconpr0n.org/map/hp/2hpt20065-1/single/hp_2hpt20065-1_marmontel_mz_ms10x-1.25.jpg 16457 14255 101.4 MiB 4 days ago, 15:03
1352
+ https://siliconpr0n.org/map/pervasive/e2213ps01e1/single/pervasive_e2213ps01e1_azonenberg_back_roi1_mit10x_rotated.jpg 18880 61836 136.8 MiB 6 days ago, 16:00
1353
+ https://siliconpr0n.org/map/pervasive/e2213ps01e/single/pervasive_e2213ps01e_azonenberg_back_mit5x_rotated.jpg 62208 30736 216.5 MiB 6 days ago, 15:57
1354
+ https://siliconpr0n.org/map/amd/am2964bpc/single/amd_am2964bpc_infosecdj_mz_lmplan10x.jpg 12809 11727 39.8 MiB 6 days ago, 10:28
1355
+ https://siliconpr0n.org/map/unknown/ks1804ir1/single/unknown_ks1804ir1_infosecdj_mz_lmplan10x.jpg 6508 6707 8.4 MiB 6 days ago, 08:04
1356
+ https://siliconpr0n.org/map/amd/am2960dc-b/single/amd_am2960dc-b_infosecdj_mz_lmplan10x.jpg 16434 15035 64.9 MiB 7 days ago, 19:01
1357
+ 10 media (limited by --limit 10)
1358
+
1359
+ How I use it
1360
+
1361
+ library play audio --db ~/lb/audio.db --local-media-only -k delete-if-audiobook -w play_count=0 --fetch-siblings each
1362
+ library play video --db ~/lb/video.db --local-media-only -k delete --cmd5 'echo skip'
1363
+
1364
+ When sorting videos
1365
+ focus_under_mouse
1366
+ library play video --db ~/lb/sort.db --action ask_move_or_delete --keep-dir /home/xk/d/library/video/ --loop --exit-code-confirm -i --cmd130 exit_multiple_playback --cmd5 'library shrink audio --no-preserve-video' --cmd6 'mv {} /mnt/d/library/vr/' -m 4 --start 35% --volume=0 -u size desc
1367
+ focus_follows_mouse
1368
+
1369
+ On-the-go mobile smartphone mode (Android)
1370
+ repeat library play video --db ~/lb/video.db --player termux-open -L1 --refresh --action ask_move_or_delete --keep-dir ~/sync/video/keep/ --portrait -u duration desc
1371
+
1372
+
1373
+ </details>
1374
+
1375
+ ###### playback now
1376
+
1377
+ <details><summary>Show what is currently playing</summary>
1378
+
1379
+ $ library playback now -h
1380
+ usage: library playback now
1381
+
1382
+ Print now playing
1383
+
1384
+
1385
+ </details>
1386
+
1387
+ ###### playback next
1388
+
1389
+ <details><summary>Play next file and optionally delete current file</summary>
1390
+
1391
+ $ library playback next -h
1392
+ usage: library playback next
1393
+
1394
+ Go to the next track in the playqueue, optionally delete the currently playing media
1395
+
1396
+
1397
+ </details>
1398
+
1399
+ ###### playback seek
1400
+
1401
+ <details><summary>Set playback to a certain time, fast-forward or rewind</summary>
1402
+
1403
+ $ library playback seek -h
1404
+ usage: library playback seek
1405
+
1406
+ Seek to an exact time
1407
+
1408
+ library playback seek 5:30 # 5 minutes, 30 seconds
1409
+ library playback seek 5:30:00 # 5 hours, 30 minutes
1410
+
1411
+ Seek forward or backward a relative duration
1412
+
1413
+ library playback seek +5:00 # 5 minutes forward
1414
+ library playback seek +5: # 5 minutes forward
1415
+ library playback seek +5 # 5 seconds forward
1416
+ library playback seek 5 # 5 seconds forward
1417
+
1418
+ library playback seek -5 # 5 seconds backward
1419
+
1420
+
1421
+ </details>
1422
+
1423
+ ###### playback stop
1424
+
1425
+ <details><summary>Stop all playback</summary>
1426
+
1427
+ $ library playback stop -h
1428
+ usage: library playback stop
1429
+
1430
+ Stop playback (close mpv, turn off chromecast, etc)
1431
+
1432
+
1433
+ </details>
1434
+
1435
+ ###### playback pause
1436
+
1437
+ <details><summary>Pause all playback</summary>
1438
+
1439
+ $ library playback pause -h
1440
+ usage: library playback pause
1441
+
1442
+ Pause playback (pause mpv, pause chromecast, etc)
1443
+
1444
+
1445
+ </details>
1446
+
1447
+ ### Web collection subcommands
1448
+
1449
+ ###### database add links
1450
+
1451
+ <details><summary>Extract links from webpages, paginated lists, recursive sites, and web archives</summary>
1452
+
1453
+ $ library database add links -h
1454
+ usage: library database add links DATABASE PATH ... [--case-sensitive] [--cookies-from-browser BROWSER[+KEYRING][:PROFILE][::CONTAINER]] [--selenium] [--manual] [--scroll] [--auto-pager] [--poke] [--chrome] [--local-html] [--file FILE]
1455
+
1456
+ Extract links from ordinary webpages into a database. This is useful for
1457
+ article indexes, forums, search results, playlists, and other pages where
1458
+ the links you want are embedded in HTML. Supports pagination, recursive
1459
+ same-site crawling, link filters, browser-backed pages, and Internet Archive
1460
+ CDX snapshots.
1461
+
1462
+ By default it will add the URL param "?page=2" and so on...
1463
+
1464
+ You can fine-tune what links get saved with --path/text/before/after-include/exclude.
1465
+
1466
+ library database add links --path-include /video/
1467
+
1468
+ Import links from args
1469
+
1470
+ library database add links --no-extract links.db (cb)
1471
+
1472
+ Import lines from stdin
1473
+
1474
+ cb | library database add links links.db --skip-extract -
1475
+
1476
+ IRIs vs URLs
1477
+
1478
+ Library should:
1479
+ - print paths as unquoted UTF-8 IRIs (by default)
1480
+ - accept either IRIs or URLs as input with optional flags to make parsing/searching explicitly use one or the other
1481
+ - save paths to the database "AS-IS"
1482
+
1483
+ How I use it
1484
+
1485
+ library database add links links.db https://video/site/ --path-include /video/
1486
+
1487
+ library database add links links.db https://loginsite/ --path-include /article/ --cookies-from-browser firefox
1488
+ library database add links links.db https://loginsite/ --path-include /article/ --cookies-from-browser chrome
1489
+
1490
+ cb -t text/html | xidel -s - -e '//@title' | unique | lb linksdb ~/mc/music.db -c p1 --skip-extract -
1491
+
1492
+ library database add links --path-include viewtopic.php --cookies-from-browser firefox \
1493
+ --page-key start --page-start 0 --page-step 50 --fixed-pages 14 --stop-pages-no-match 1 \
1494
+ plab.db https://plab/forum/tracker.php?o=(string replace ' ' \n -- 1 4 7 10 15)&s=2&tm=-1&f=(string replace ' ' \n -- 1670 1768 60 1671 1644 1672 1111 508 555 1112 1718 1143 1717 1851 1713 1712 1775 1674 902 1675 36 1830 1803 1831 1741 1676 1677 1780 1110 1124 1784 1769 1793 1797 1804 1819 1825 1836 1842 1846 1857 1861 1867 1451 1788 1789 1792 1798 1805 1820 1826 1837 1843 1847 1856 1862 1868 284 1853 1823 1800 1801 1719 997 1818 1849 1711 1791 1762)
1495
+
1496
+ Recursively add every link on a site (glob excludes prune subtrees from the crawl)
1497
+
1498
+ library database add links links.db --recursive https://example.com/docs/ --path-exclude '*/tags/*'
1499
+
1500
+ Add every archived link for a site from the Internet Archive CDX index
1501
+
1502
+ library database add links links.db --webcache https://example.com/docs/
1503
+
1504
+
1505
+ </details>
1506
+
1507
+ ###### links open
1508
+
1509
+ <details><summary>Open links from link dbs</summary>
1510
+
1511
+ $ library links open -h
1512
+ usage: library links open DATABASE [search] [--title] [--title-prefix TITLE_PREFIX]
1513
+
1514
+ Open links from a links db
1515
+
1516
+ wget https://github.com/chapmanjacobd/library-examples/raw/main/music.korea.ln.db
1517
+ library links open music.korea.ln.db
1518
+
1519
+ Only open links once
1520
+
1521
+ library links open ln.db -w 'time_modified=0'
1522
+
1523
+ Print a preview instead of opening tabs
1524
+
1525
+ library links open ln.db -p
1526
+ library links open ln.db --cols time_modified -p
1527
+
1528
+ Delete rows
1529
+
1530
+ Make sure you have the right search query
1531
+ library links open ln.db "query" -p -L inf
1532
+ library links open ln.db "query" -pa # view total
1533
+
1534
+ library links open ln.db "query" -pd # mark as deleted
1535
+
1536
+ Custom search engine
1537
+
1538
+ library links open ln.db --title --prefix 'https://duckduckgo.com/?q='
1539
+
1540
+ Skip local media
1541
+
1542
+ library links open dl.db --online
1543
+ library links open dl.db -w 'path like "http%"' # equivalent
1544
+
1545
+
1546
+
1547
+ </details>
1548
+
1549
+ ###### links extract
1550
+
1551
+ <details><summary>Extract inner links from lists of web links</summary>
1552
+
1553
+ $ library links extract -h
1554
+ usage: library links extract PATH ... [--case-sensitive] [--scroll] [--local-html] [--file FILE]
1555
+
1556
+ Extract links from within local HTML fragments, files, or remote pages; filtering on link text and nearby plain-text
1557
+
1558
+ library links extract https://en.wikipedia.org/wiki/List_of_bacon_dishes --path-include https://en.wikipedia.org/wiki/ --after-include famous
1559
+ https://en.wikipedia.org/wiki/Omelette
1560
+
1561
+ Download the filtered links. Internet Archive details pages use the Internet Archive downloader and preserve all original files:
1562
+
1563
+ library links download https://readrare.com/
1564
+
1565
+ Mirror a site recursively. Only links within the same domain and parent directory as the given
1566
+ path are followed; links pointing elsewhere are downloaded but not followed. Includes/excludes
1567
+ accept globs, so you can prune whole subtrees while crawling:
1568
+
1569
+ library links download --recursive https://example.com/docs/
1570
+ library links download -R https://example.com/docs/ --path-exclude '*/tag/*' '*/tags/*'
1571
+
1572
+ Extract links from a web archive CDX index instead of crawling live pages. Internet Archive
1573
+ Wayback snapshots are emitted by default. Wayback URLs are collapsed to their original
1574
+ archived URL while crawling: sibling pages that only exist under a different timestamp are
1575
+ still followed, but multiple captures of the same page are collapsed so only one copy of
1576
+ each page is downloaded. Use --webcache-from/--webcache-to to target a time period:
1577
+
1578
+ library links extract --webcache https://example.com/docs/
1579
+ library links download --webcache --recursive https://example.com/docs/
1580
+ library links extract --webcache --webcache-source cc https://example.com/docs/
1581
+ library links extract --webcache --webcache-from 2020 --webcache-to 2021 https://example.com/docs/
1582
+
1583
+ Common tracking and analytics URLs, including Google Tag Manager and Google Analytics,
1584
+ plus /tag/, /tags/, and ?tag= URLs, are excluded by default. Use --path-exclude to
1585
+ replace the default exclusions when needed.
1586
+
1587
+ Read from local clipboard and filter out links based on nearby plain text
1588
+
1589
+ library links extract --local-html (cb -t text/html | psub) --after-exclude paranormal spooky horror podcast tech fantasy supernatural lecture sport
1590
+ # note: the equivalent BASH-ism is <(xclip -selection clipboard -t text/html)
1591
+
1592
+ Use --selenium for sites require JavaScript
1593
+
1594
+ library links extract --selenium https://archive.org/search?query=subject%3A%22Archive.org+Census%22 --path-include census
1595
+
1596
+ Run with `-vv` to see the browser that normally loads in the background
1597
+
1598
+
1599
+ </details>
1600
+
1601
+ ###### links download
1602
+
1603
+ <details><summary>Download links from lists of web links</summary>
1604
+
1605
+ $ library links download -h
1606
+ usage: library links download [PATH ...] [--db DATABASE] [optional args]
1607
+
1608
+ Extract links from the given pages and download them. Each link is routed by the active
1609
+ profile (auto by default): yt-dlp for video/audio, gallery-dl for images, and the built-in
1610
+ downloader for everything else. Pass --direct to always download with the built-in
1611
+ downloader instead of yt-dlp/gallery-dl.
1612
+
1613
+ Pass --db to read pages from a database and record download results. The given PATHs are
1614
+ then database filters rather than pages to extract from:
1615
+
1616
+ library links download --db links.db
1617
+ library links download --db links.db --include https://example.com/docs/
1618
+
1619
+ Follow same-domain links recursively, or read a web archive CDX index, with the same
1620
+ options as `library links extract`:
1621
+
1622
+ library links download --recursive https://example.com/docs/
1623
+ library links download --webcache https://example.com/docs/
1624
+
1625
+
1626
+ </details>
1627
+
1628
+ ###### database add tabs
1629
+
1630
+ <details><summary>Schedule URLs for recurring browser visits</summary>
1631
+
1632
+ $ library database add tabs -h
1633
+ usage: library database add tabs [--frequency daily weekly (monthly) quarterly yearly] [--no-sanitize] DATABASE URL ...
1634
+
1635
+ Adding one URL
1636
+
1637
+ library database add tabs -f daily tabs.db https://wiby.me/surprise/
1638
+
1639
+ Depending on your shell you may need to escape the URL (add quotes)
1640
+
1641
+ If you use Fish shell know that you can enable features to make pasting easier
1642
+ set -U fish_features stderr-nocaret qmark-noglob regex-easyesc ampersand-nobg-in-token
1643
+
1644
+ Also I recommend turning Ctrl+Backspace into a super-backspace for repeating similar commands with long args
1645
+ echo 'bind \b backward-kill-bigword' >> ~/.config/fish/config.fish
1646
+
1647
+ Importing from a line-delimitated file
1648
+
1649
+ library database add tabs -f yearly -c reddit tabs.db (cat ~/mc/yearly-subreddit.cron)
1650
+
1651
+
1652
+
1653
+ </details>
1654
+
1655
+ ###### tabs open
1656
+
1657
+ <details><summary>Open your tabs for the day</summary>
1658
+
1659
+ $ library tabs open -h
1660
+ usage: library tabs open DATABASE
1661
+
1662
+ Tabs is meant to run **once per day**. Here is how you would configure it with `crontab`
1663
+
1664
+ 45 9 * * * DISPLAY=:0 library tabs open /home/my/tabs.db
1665
+
1666
+ If things aren't working you can use `at` to simulate a similar environment as `cron`
1667
+
1668
+ echo 'fish -c "export DISPLAY=:0 && library tabs open /full/path/to/tabs.db"' | at NOW
1669
+
1670
+ Also, if you're just testing things out be aware that `library database add tabs` assumes that you visited the
1671
+ website right before adding it; eg. if you use `library database add tabs --frequency yearly` today the tab won't
1672
+ open until one year from now (at most). You can override this default
1673
+
1674
+ library database add tabs --allow-immediate ...
1675
+
1676
+ To re-"play" some tabs, delete some history
1677
+
1678
+ library database history ~/lb/tabs.db --played-within '1 day' -L inf -p --delete-rows
1679
+ library tabs open ~/lb/tabs.db
1680
+
1681
+ You can also invoke tabs manually
1682
+
1683
+ library tabs open -L 1 # open one tab
1684
+
1685
+ Print URLs
1686
+
1687
+ library tabs open -w "frequency='yearly'" -p
1688
+
1689
+ View how many yearly tabs you have
1690
+
1691
+ library tabs open -w "frequency='yearly'" -p a
1692
+
1693
+ Delete URLs
1694
+
1695
+ library tabs open -p -s cyber
1696
+ ╒═══════════════════════════════════════╤═════════════╤══════════════╕
1697
+ │ path │ frequency │ time_valid │
1698
+ ╞═══════════════════════════════════════╪═════════════╪══════════════╡
1699
+ │ https://old.reddit.com/r/cyberDeck/to │ yearly │ Dec 31 1970 │
1700
+ │ p/?sort=top&t=year │ │ │
1701
+ ├───────────────────────────────────────┼─────────────┼──────────────┤
1702
+ │ https://old.reddit.com/r/Cyberpunk/to │ yearly │ Aug 29 2023 │
1703
+ │ p/?sort=top&t=year │ │ │
1704
+ ├───────────────────────────────────────┼─────────────┼──────────────┤
1705
+ │ https://www.reddit.com/r/cyberDeck/ │ yearly │ Sep 05 2023 │
1706
+ ╘═══════════════════════════════════════╧═════════════╧══════════════╛
1707
+
1708
+ library tabs open -p -w "path='https://www.reddit.com/r/cyberDeck/'" --delete-rows
1709
+ Removed 1 metadata records
1710
+
1711
+ library tabs open -p -s cyber
1712
+ ╒═══════════════════════════════════════╤═════════════╤══════════════╕
1713
+ │ path │ frequency │ time_valid │
1714
+ ╞═══════════════════════════════════════╪═════════════╪══════════════╡
1715
+ │ https://old.reddit.com/r/cyberDeck/to │ yearly │ Dec 31 1970 │
1716
+ │ p/?sort=top&t=year │ │ │
1717
+ ├───────────────────────────────────────┼─────────────┼──────────────┤
1718
+ │ https://old.reddit.com/r/Cyberpunk/to │ yearly │ Aug 29 2023 │
1719
+ │ p/?sort=top&t=year │ │ │
1720
+ ╘═══════════════════════════════════════╧═════════════╧══════════════╛
1721
+
1722
+
1723
+ </details>
1724
+
1725
+ ###### tabs shuffle
1726
+
1727
+ <details><summary>Randomize tabs.db a bit</summary>
1728
+
1729
+ $ library tabs shuffle -h
1730
+ usage: library tabs shuffle DATABASE
1731
+
1732
+ Moves each tab to a random day-of-the-week by default
1733
+
1734
+ It may also be useful to shuffle monthly tabs, etc. You can accomplish this like so
1735
+
1736
+ library tabs shuffle tabs.db -d 31 -f monthly
1737
+ library tabs shuffle tabs.db -d 90 -f quarterly
1738
+ library tabs shuffle tabs.db -d 365 -f yearly
1739
+
1740
+
1741
+ </details>
1742
+
1743
+ ###### database add site
1744
+
1745
+ <details><summary>Extract structured website data into SQLite</summary>
1746
+
1747
+ $ library database add site -h
1748
+ usage: library database add site DATABASE PATH ... [--auto-pager] [--poke] [--local-html] [--file FILE]
1749
+
1750
+ Extract data from website requests to a database
1751
+
1752
+ library database add site jobs.st.db --poke https://hk.jobsdb.com/hk/search-jobs/python/
1753
+
1754
+ Requires selenium-wire-lw
1755
+ Requires xmltodict when using --extract-xml
1756
+
1757
+ pip install selenium-wire-lw xmltodict
1758
+
1759
+ Run with `-vv` to see and interact with the browser
1760
+
1761
+
1762
+ </details>
1763
+
1764
+ ### Download subcommands
1765
+
1766
+ ###### download
1767
+
1768
+ <details><summary>Download media</summary>
1769
+
1770
+ $ library download -h
1771
+ usage: library download [PATH ...] [--db DATABASE] [--prefix /mnt/d/] [--media-type {auto,video,audio,image,text,filesystem}] [--safe]
1772
+
1773
+ Download the given URLs. Files are saved to <prefix>/<extractor>/. The default prefix is
1774
+ the current working directory. The default download profile is auto: each URL is sent to
1775
+ yt-dlp or gallery-dl by checking which backend supports it (is_supported); unsupported
1776
+ URLs fall back to the built-in downloader. `--safe` supports the auto, audio, video, and
1777
+ image profiles only (not --direct or --media-type filesystem/text).
1778
+
1779
+ library download https://example.com/file.zip
1780
+
1781
+ Pass --direct to always download with the built-in downloader instead of yt-dlp/gallery-dl:
1782
+
1783
+ library download --direct https://example.com/file.zip
1784
+
1785
+ The `dl` alias has the same behavior. A database is selected only with `--db`.
1786
+
1787
+ Pass --db to download the media recorded in a database instead. The given PATHs are then
1788
+ database filters rather than URLs to download directly, and results are recorded back to
1789
+ the database:
1790
+
1791
+ library download --db dl.db --prefix ~/output/path/root/
1792
+ library download --db dl.db --include https://www.youtube.com/watch?v=YE7VzlLtp-4
1793
+ library download --db dl.db -s https://www.youtube.com/watch?v=YE7VzlLtp-4 # equivalent
1794
+
1795
+ By default things download in a random order; you can sort, eg. oldest first
1796
+
1797
+ library download --db dl.db -u m.time_modified,m.time_created
1798
+
1799
+ Read media rows (JSON or JSONL) from stdin to download them directly
1800
+
1801
+ library filesystem list --db video.db --to-json --playlists https://www.youtube.com/c/BlenderFoundation/videos | library download --from-json -
1802
+
1803
+ Print the queued up downloads without downloading
1804
+
1805
+ library download --db dl.db --print
1806
+
1807
+ `library database download DATABASE` is an alias of `library download --db DATABASE`
1808
+
1809
+ library database download dl.db
1810
+
1811
+
1812
+ </details>
1813
+
1814
+ ###### database download
1815
+
1816
+ <details><summary>Download media recorded in a database</summary>
1817
+
1818
+ $ library database download -h
1819
+ usage: library database download DATABASE [PATH ...] [--prefix /mnt/d/] [--media-type {auto,video,audio,image,text,filesystem}] [--safe]
1820
+
1821
+ Download the media recorded in DATABASE. This is an alias of `library download --db DATABASE`;
1822
+ the only difference is that DATABASE is taken as a positional argument.
1823
+
1824
+ Files are saved to <prefix>/<extractor>/. The default prefix is the current working directory.
1825
+ The default download profile is auto: each URL is sent to yt-dlp or gallery-dl by
1826
+ checking which backend supports it (is_supported); unsupported URLs fall back to the
1827
+ built-in downloader. `--safe` supports the auto, audio, video, and image profiles only
1828
+ (not --direct or --media-type filesystem/text).
1829
+
1830
+ By default things download in a random order
1831
+
1832
+ library database download dl.db --prefix ~/output/path/root/
1833
+
1834
+ But you can sort; eg. oldest first
1835
+
1836
+ library database download dl.db -u m.time_modified,m.time_created
1837
+
1838
+ Limit downloads to a specified video URLs or substring
1839
+
1840
+ library database download dl.db --include https://www.youtube.com/watch?v=YE7VzlLtp-4
1841
+ library database download dl.db -s https://www.youtube.com/watch?v=YE7VzlLtp-4 # equivalent
1842
+
1843
+ By default each URL is routed to yt-dlp/gallery-dl by is_supported. Pass --direct to
1844
+ download with the built-in downloader instead:
1845
+
1846
+ library database download dl.db --direct https://example.com/file.zip
1847
+
1848
+ Maximizing the variety of subdomains
1849
+
1850
+ library database download photos.db --photos --image --sort "ROW_NUMBER() OVER ( PARTITION BY SUBSTR(m.path, INSTR(m.path, '//') + 2, INSTR( SUBSTR(m.path, INSTR(m.path, '//') + 2), '/') - 1) )"
1851
+
1852
+ Print list of queued up downloads
1853
+
1854
+ library database download dl.db --print
1855
+
1856
+ Print list of saved playlists
1857
+
1858
+ library database playlists dl.db -p a
1859
+
1860
+ Print download queue groups
1861
+
1862
+ library database download status audio.db
1863
+
1864
+ Check videos before downloading
1865
+
1866
+ library play video --db open_dir.db --online-media-only --loop --exit-code-confirm -i --action ask-keep -m 4 --start 35% --volume=0 -w 'height<720' -E preview
1867
+
1868
+ Assuming you have bound in mpv input.conf a key to 'quit' and another key to 'quit 4',
1869
+ using the ask-keep action marks a video as deleted when you 'quit 4' and marks a video as watched when you 'quit'.
1870
+
1871
+ For example, here I bind "'" to "MOVE" and "j" to "DELETE" when using -k ask_move_or_delete
1872
+
1873
+ ' quit
1874
+ j quit 4
1875
+
1876
+ This is pretty intuitive after you use it a few times but another option is to
1877
+ define your own post-actions
1878
+
1879
+ `--cmd5 'echo {} >> keep.txt' --cmd6 'echo {} >> rejected.txt'`
1880
+
1881
+ Bind keys in mpv input.conf:
1882
+
1883
+ k quit 5 # goes to keep.txt
1884
+ r quit 6 # goes to rejected.txt
1885
+
1886
+ Download checked videos
1887
+
1888
+ library database download --fs open_dir.db --prefix ~/d/dump/video/ -w 'id in (select media_id from history)'
1889
+
1890
+
1891
+ </details>
1892
+
1893
+ ###### database download block
1894
+
1895
+ <details><summary>Block a channel</summary>
1896
+
1897
+ $ library database download block -h
1898
+ usage: library database download block DATABASE URL ...
1899
+
1900
+ Blocklist specific URLs (eg. YouTube channels, etc)
1901
+
1902
+ library database download block dl.db https://annoyingwebsite/etc/
1903
+
1904
+ Or URL substrings
1905
+
1906
+ library database download block dl.db "%fastcompany.com%"
1907
+
1908
+ Block videos from the playlist uploader
1909
+
1910
+ library database download block dl.db --match-column playlist_path 'https://youtube.com/playlist?list=PLVoczRgDnXDLWV1UJ_tO70VT_ON0tuEdm'
1911
+
1912
+ Or other columns
1913
+
1914
+ library database download block dl.db --match-column title "% bitcoin%"
1915
+ library database download block dl.db --force --match-column uploader Zeducation
1916
+
1917
+ Display subdomains (similar to `library database download status`)
1918
+
1919
+ library database download block audio.db
1920
+ subdomain count new_links tried percent_tried successful percent_successful failed percent_failed
1921
+ ------------------- ------- ----------- ------- --------------- ------------ -------------------- -------- ----------------
1922
+ dts.podtrac.com 5244 602 4642 88.52% 690 14.86% 3952 85.14%
1923
+ soundcloud.com 16948 11931 5017 29.60% 920 18.34% 4097 81.66%
1924
+ twitter.com 945 841 104 11.01% 5 4.81% 99 95.19%
1925
+ v.redd.it 9530 6805 2725 28.59% 225 8.26% 2500 91.74%
1926
+ vimeo.com 865 795 70 8.09% 65 92.86% 5 7.14%
1927
+ www.youtube.com 210435 140952 69483 33.02% 66017 95.01% 3467 4.99%
1928
+ youtu.be 60061 51911 8150 13.57% 7736 94.92% 414 5.08%
1929
+ youtube.com 5976 5337 639 10.69% 599 93.74% 40 6.26%
1930
+
1931
+ Find some words to block based on frequency / recency of downloaded media
1932
+
1933
+ library play video --db dl.db -u time_downloaded desc -L 10000 -pf | library text nouns | sort | uniq -c | sort -g
1934
+ ...
1935
+ 183 ArchiveOrg
1936
+ 187 Documentary
1937
+ 237 PBS
1938
+ 243 BBC
1939
+ ...
1940
+
1941
+
1942
+ </details>
1943
+
1944
+ ###### database download status
1945
+
1946
+ <details><summary>Show download status</summary>
1947
+
1948
+ $ library database download status -h
1949
+ usage: library database download status DATABASE
1950
+
1951
+ Print download queue groups
1952
+
1953
+ library database download status video.db
1954
+
1955
+ Simulate --safe flag
1956
+
1957
+ library database download status video.db --safe
1958
+
1959
+
1960
+ </details>
1961
+
1962
+ ###### database redownload
1963
+
1964
+ <details><summary>Re-download deleted/lost media</summary>
1965
+
1966
+ $ library database redownload -h
1967
+ usage: library database redownload DATABASE
1968
+
1969
+ If you have previously downloaded YouTube or other online media, but your
1970
+ hard drive failed or you accidentally deleted something, and if that media
1971
+ is still accessible from the same URL, this script can help to redownload
1972
+ everything that was scanned-as-deleted between two timestamps.
1973
+
1974
+ List deletions
1975
+
1976
+ library database redownload news.db
1977
+ Deletions:
1978
+ ╒═════════════════════╤═════════╕
1979
+ │ time_deleted │ count │
1980
+ ╞═════════════════════╪═════════╡
1981
+ │ 2023-01-26T00:31:26 │ 120 │
1982
+ ├─────────────────────┼─────────┤
1983
+ │ 2023-01-26T19:54:42 │ 18 │
1984
+ ├─────────────────────┼─────────┤
1985
+ │ 2023-01-26T20:45:24 │ 26 │
1986
+ ╘═════════════════════╧═════════╛
1987
+ Showing most recent 3 deletions. Use -l to change this limit
1988
+
1989
+ Mark videos as candidates for download via specific deletion timestamp
1990
+
1991
+ library database redownload city.db 2023-01-26T19:54:42
1992
+
1993
+ ...or between two timestamps inclusive
1994
+
1995
+ library database redownload city.db 2023-01-26T19:54:42 2023-01-26T20:45:24
1996
+
1997
+
1998
+ </details>
1999
+
2000
+ ### Database management subcommands
2001
+
2002
+ ###### database search
2003
+
2004
+ <details><summary>Search a SQLite database</summary>
2005
+
2006
+ $ library database search -h
2007
+ usage: library database search DATABASE TABLE SEARCH ... [--delete-rows]
2008
+
2009
+ Search all columns in a SQLite table. If the table does not exist, uses the table which startswith (if only one match)
2010
+
2011
+ library database search tests/data/video.db media test.gif --to-json | jq
2012
+ {
2013
+ "id": 4,
2014
+ "playlists_id": 1,
2015
+ "size": 2145,
2016
+ "time_created": 1713852795,
2017
+ "time_modified": 1713852795,
2018
+ "time_downloaded": 1723276940,
2019
+ "video_count": 1,
2020
+ "width": 82,
2021
+ "height": 82,
2022
+ "subtitle_count": 4,
2023
+ "path": "/home/xk/github/xk/lb/tests/data/test.gif",
2024
+ "type": "image/gif",
2025
+ "video_codecs": "gif",
2026
+ "fps": 10.0
2027
+ }
2028
+
2029
+
2030
+ </details>
2031
+
2032
+ ###### database merge
2033
+
2034
+ <details><summary>Merge SQLite databases</summary>
2035
+
2036
+ $ library database merge -h
2037
+ usage: library database merge SOURCE_DB ... DEST_DB [--only-target-columns] [--only-new-rows] [--upsert] [--pk PK ...] [--table TABLE ...]
2038
+
2039
+ Merge-DBs will insert new rows from source dbs to target db, table by table. If primary key(s) are provided,
2040
+ and there is an existing row with the same PK, the default action is to delete the existing row and insert the new row
2041
+ replacing all existing fields.
2042
+
2043
+ Upsert mode will update each matching PK row such that if a source row has a NULL field and
2044
+ the destination row has a value then the value will be preserved instead of changed to the source row's NULL value.
2045
+
2046
+ Ignore mode (--only-new-rows) will insert only rows which don't already exist in the destination db
2047
+
2048
+ Test first by using temp databases as the destination db.
2049
+ Try out different modes / flags until you are satisfied with the behavior of the program
2050
+
2051
+ library database merge --pk path tv.db movies.db (mktemp --suffix .db)
2052
+
2053
+ Merge database data and tables
2054
+
2055
+ library database merge --upsert --pk path tv.db movies.db video.db
2056
+ library database merge --only-target-columns --only-new-rows --table media,playlists --pk path --skip-column id audio-fts.db audio.db
2057
+
2058
+ library database merge --pk id --only-tables subreddits audio.db reddit/81_New_Music.db
2059
+ library database merge --only-new-rows --pk subreddit,path --only-tables reddit_posts audio.db reddit/81_New_Music.db -v
2060
+
2061
+ To skip copying primary-keys from the source table(s) use --business-keys instead of --primary-keys
2062
+
2063
+ Split DBs using --where
2064
+
2065
+ library database merge --pk path big.db specific-site.db -v --only-new-rows -t media,playlists -w 'path like "https://specific-site%"'
2066
+
2067
+
2068
+ </details>
2069
+
2070
+ ###### database dedupe
2071
+
2072
+ <details><summary>Dedupe SQLite tables</summary>
2073
+
2074
+ $ library database dedupe -h
2075
+ usage: library database dedupe DATABASE TABLE --bk BUSINESS_KEYS [--pk PRIMARY_KEYS] [--only-columns COLUMNS]
2076
+
2077
+ Dedupe your database (not to be confused with the dedupe subcommand)
2078
+
2079
+ It should not need to be said but *backup* your database before trying this tool!
2080
+
2081
+ Dedupe-DB will help remove duplicate rows based on non-primary-key business keys
2082
+
2083
+ library database dedupe ./video.db media --bk path
2084
+
2085
+ By default all non-primary and non-business key columns will be upserted unless --only-columns is provided
2086
+ If --primary-keys is not provided table metadata primary keys will be used
2087
+ If your duplicate rows contain exactly the same data in all the columns you can run with --skip-upsert to save a lot of time
2088
+
2089
+
2090
+ </details>
2091
+
2092
+ ### Media database management subcommands
2093
+
2094
+ ###### captions
2095
+
2096
+ <details><summary>Search captions / subtitles</summary>
2097
+
2098
+ $ library captions -h
2099
+ usage: library captions DATABASE QUERY
2100
+
2101
+ Search text databases and subtitles
2102
+
2103
+ library captions fts.db boil
2104
+ 7 captions
2105
+ /mnt/d/70_Now_Watching/DidubeTheLastStop-720p.mp4
2106
+ 33:46 I brought a real stainless steel boiler
2107
+ 33:59 The world is using only stainless boilers nowadays
2108
+ 34:02 The boiler is old and authentic
2109
+ 34:30 - This boiler? - Yes
2110
+ 34:44 I am not forcing you to buy this boiler…
2111
+ 34:52 Who will give her a one liter stainless steel boiler for one Lari?
2112
+ 34:54 Glass boilers cost two
2113
+
2114
+ Search and open file
2115
+
2116
+ library captions fts.db 'two words' --open
2117
+
2118
+
2119
+ </details>
2120
+
2121
+ ###### database playlists
2122
+
2123
+ <details><summary>List stored playlists</summary>
2124
+
2125
+ $ library database playlists -h
2126
+ usage: library database playlists DATABASE
2127
+
2128
+ List of Playlists
2129
+
2130
+ library database playlists
2131
+
2132
+ Search playlists
2133
+
2134
+ library database playlists audio.db badfinger
2135
+ path extractor_key title count
2136
+ ---------------------------------------------------------- --------------- ------------------------------ -------
2137
+ https://music.youtube.com/channel/UCyJzUJ95hXeBVfO8zOA0GZQ ydl_Youtube Uploads from Badfinger - Topic 226
2138
+
2139
+ Aggregate Report of Videos in each Playlist
2140
+
2141
+ library database playlists -p a
2142
+
2143
+
2144
+ Print only playlist urls
2145
+
2146
+ Useful for piping to other utilities like xargs or GNU Parallel.
2147
+ library database playlists -p f
2148
+ https://www.youtube.com/playlist?list=PL7gXS9DcOm5-O0Fc1z79M72BsrHByda3n
2149
+
2150
+ Remove a playlist/channel and all linked videos
2151
+
2152
+ library database playlists --delete-rows https://vimeo.com/canal180
2153
+
2154
+
2155
+
2156
+ </details>
2157
+
2158
+ ###### database history
2159
+
2160
+ <details><summary>Show and manage playback history</summary>
2161
+
2162
+ $ library database history -h
2163
+ usage: library database history [--frequency daily weekly (monthly) yearly] [--limit LIMIT] DATABASE [PATH ...] [(all) watching watched created modified deleted]
2164
+
2165
+ View playback history
2166
+
2167
+ library database history web_add.image.db
2168
+ In progress:
2169
+ play_count time_last_played playhead path title
2170
+ ------------ ------------------ ---------- --------------------------------------- -----------
2171
+ 0 today, 20:48 2 seconds https://siliconpr0n.org/map/COPYING.txt COPYING.txt
2172
+
2173
+ Show only completed history
2174
+
2175
+ library database history web_add.image.db --completed
2176
+
2177
+ Show only in-progress history
2178
+
2179
+ library database history web_add.image.db --in-progress
2180
+
2181
+ Delete history
2182
+
2183
+ Delete two hours of history
2184
+ library database history web_add.image.db --played-within '2 hours' -L inf --delete-rows
2185
+
2186
+ Delete all history
2187
+ library database history web_add.image.db -L inf --delete-rows
2188
+
2189
+ Delete history for exact paths
2190
+ library database history ~/mc/links.db --delete-rows https://example.com/article
2191
+ cb | library database history ~/mc/links.db --delete-rows -
2192
+
2193
+ See also: library database stats -h
2194
+ library database history add -h
2195
+
2196
+
2197
+ </details>
2198
+
2199
+ ###### database history add
2200
+
2201
+ <details><summary>Add history from paths</summary>
2202
+
2203
+ $ library database history add -h
2204
+ usage: library database history add DATABASE PATH ...
2205
+
2206
+ Add history
2207
+
2208
+ library database history add links.db $urls $paths
2209
+ library database history add links.db (cb)
2210
+
2211
+ Items that don't already exist in the database will be counted under "skipped"
2212
+
2213
+
2214
+ </details>
2215
+
2216
+ ###### database stats
2217
+
2218
+ <details><summary>Show some event statistics (created, deleted, watched, etc)</summary>
2219
+
2220
+ $ library database stats -h
2221
+ usage: library database stats DATABASE TIME_COLUMN
2222
+
2223
+ View watched stats
2224
+
2225
+ library database stats video.db --completed --no-hide-deleted
2226
+
2227
+ View download stats
2228
+
2229
+ library database stats video.db time_downloaded --frequency daily
2230
+
2231
+ See also: library database stats video.db time_downloaded -f daily --hide-deleted
2232
+
2233
+ View deleted stats
2234
+
2235
+ library database stats video.db time_deleted --deleted
2236
+
2237
+ View time_modified stats
2238
+
2239
+ library database stats web_add.image.db time_modified -f year
2240
+ Time_Modified media:
2241
+ year total_size avg_size count
2242
+ ------ ------------ ---------- -------
2243
+ 2010 4.4 MiB 1.5 MiB 3
2244
+ 2011 136.2 MiB 68.1 MiB 2
2245
+ 2013 1.6 GiB 10.7 MiB 154
2246
+ 2014 4.6 GiB 25.2 MiB 187
2247
+ 2015 4.3 GiB 26.5 MiB 167
2248
+ 2016 5.1 GiB 46.8 MiB 112
2249
+ 2017 4.8 GiB 51.7 MiB 95
2250
+ 2018 5.3 GiB 97.9 MiB 55
2251
+ 2019 1.3 GiB 46.5 MiB 29
2252
+ 2020 25.7 GiB 113.5 MiB 232
2253
+ 2021 25.6 GiB 96.5 MiB 272
2254
+ 2022 14.6 GiB 82.7 MiB 181
2255
+ 2023 24.3 GiB 72.5 MiB 343
2256
+ 2024 17.3 GiB 104.8 MiB 169
2257
+ 14 media
2258
+
2259
+
2260
+ </details>
2261
+
2262
+ ###### database stats words
2263
+
2264
+ <details><summary>Aggregate media size by words in paths</summary>
2265
+
2266
+ $ library database stats words -h
2267
+ usage: library database stats words DATABASE [TABLE]
2268
+
2269
+ Aggregate media size and count by words found in path names.
2270
+
2271
+ library database stats words video.db
2272
+ library database stats words video.db media
2273
+
2274
+
2275
+ </details>
2276
+
2277
+ ###### database copy play-counts
2278
+
2279
+ <details><summary>Copy play history</summary>
2280
+
2281
+ $ library database copy play-counts -h
2282
+ usage: library database copy play-counts SOURCE_DB ... DEST_DB [--source-prefix x] [--target-prefix y]
2283
+
2284
+ Copy play count information between databases
2285
+
2286
+ library database copy play-counts phone.db audio.db --source-prefix /storage/6E7B-7DCE/d --target-prefix /mnt/d
2287
+
2288
+
2289
+ </details>
2290
+
2291
+ ###### database dedupe media
2292
+
2293
+ <details><summary>Dedupe similar media</summary>
2294
+
2295
+ $ library database dedupe media -h
2296
+ usage: library database dedupe media [--audio | --filesystem | --id | --title | --same-duration] [--limit LIMIT] DATABASE
2297
+
2298
+ Dedupe your files (not to be confused with the dedupe-db subcommand)
2299
+
2300
+ The media-type flags (--audio, --filesystem) select a `profile`; the dedupe-key
2301
+ flags (--id, --title, --same-duration) select a `strategy`. The two axes are
2302
+ mutually exclusive, so pick exactly one.
2303
+
2304
+ Exact file matches
2305
+
2306
+ library database dedupe media --fs video.db
2307
+
2308
+ Dedupe based on duration and file basename or dirname similarity
2309
+
2310
+ library database dedupe media video.db --same-duration --basename -s release_group # pre-filter with a specific text substring
2311
+ library database dedupe media video.db --same-duration --basename -u m1.size # sort such that small files are treated as originals and larger files are deleted
2312
+ library database dedupe media video.db --same-duration --basename -u 'm1.size desc' # sort such that large files are treated as originals and smaller files are deleted
2313
+
2314
+ Dedupe online against local media
2315
+
2316
+ library database dedupe media --compare-dirs video.db / http
2317
+
2318
+
2319
+ </details>
2320
+
2321
+ ###### file dedupe czkawka
2322
+
2323
+ <details><summary>Process czkawka diff output</summary>
2324
+
2325
+ $ library file dedupe czkawka -h
2326
+ usage: library file dedupe czkawka [--volume VOLUME] [--auto-seek] [--ignore-errors] [--folder] [--folder-glob [FOLDER_GLOB]] [--replace] [--no-replace] [--override-trash OVERRIDE_TRASH] [--delete-files] [--gui]
2327
+ [--auto-select-min-ratio AUTO_SELECT_MIN_RATIO] [--all-keep] [--all-left] [--all-right] [--all-delete]
2328
+ czkawka_dupes_output_path
2329
+
2330
+ Choose which duplicate to keep by opening both side-by-side in mpv
2331
+
2332
+
2333
+ </details>
2334
+
2335
+ ###### database merge online-local
2336
+
2337
+ <details><summary>Merge online and local data</summary>
2338
+
2339
+ $ library database merge online-local -h
2340
+ usage: library database merge online-local DATABASE
2341
+
2342
+ If you have previously downloaded YouTube or other online media, you can dedupe
2343
+ your database and combine the online and local media records as long as your
2344
+ files have the youtube-dl / yt-dlp id in the filename.
2345
+
2346
+
2347
+ </details>
2348
+
2349
+ ###### database enrich reddit-selftext
2350
+
2351
+ <details><summary>Copy selftext links to media table</summary>
2352
+
2353
+ $ library database enrich reddit-selftext -h
2354
+ usage: library database enrich reddit-selftext DATABASE
2355
+
2356
+ Extract URLs from reddit selftext from the reddit_posts table to the media table
2357
+
2358
+
2359
+ </details>
2360
+
2361
+ ###### database export html
2362
+
2363
+ <details><summary>Export HTML files from SQLite databases</summary>
2364
+
2365
+ $ library database export html -h
2366
+ usage: library database export html DATABASE
2367
+
2368
+ Generate HTML files from SQLite databases
2369
+
2370
+
2371
+ </details>
2372
+
2373
+ ###### database optimize
2374
+
2375
+ <details><summary>Re-optimize database</summary>
2376
+
2377
+ $ library database optimize -h
2378
+ usage: library database optimize DATABASE [--force]
2379
+
2380
+ Optimize library databases
2381
+
2382
+ The force flag is usually unnecessary and it can take much longer
2383
+
2384
+
2385
+ </details>
2386
+
2387
+ ### File and folder subcommands
2388
+
2389
+ ###### folder merge move
2390
+
2391
+ <details><summary>Move files and merge folders in BSD/rsync style, rename if possible</summary>
2392
+
2393
+ $ library folder merge move -h
2394
+ usage: library folder merge move SOURCE ... DEST [--simulate] [--ext EXT]
2395
+
2396
+ merging-move: combine file trees
2397
+
2398
+ The destination is ALWAYS a folder by default (`--dest-folder`).
2399
+ Use `--dest-bsd` to mimick BSD/GNU default `mv` behavior
2400
+ Use `--dest-file` to mimick BSD/GNU `mv --no-target-directory`
2401
+
2402
+ By default it won't matter if source folders end with a path separator or not
2403
+
2404
+ library folder merge move folder1 folder2/ # folder1 will be merged with folder2/
2405
+ library folder merge move folder1/ folder2/ # folder1 will be merged with folder2/
2406
+
2407
+ --bsd mode: an ending path separator determines if each source is to be placed within or merged with the destination
2408
+
2409
+ library folder merge move --bsd folder1/ folder2/ # folder1 will be merged with folder2/
2410
+ library folder merge move --bsd folder1 folder2/ # folder1 will be moved to folder2/folder1/
2411
+
2412
+ --parent mode: always include the parent folder name when merging
2413
+
2414
+ library folder merge move --parent folder1 folder2/ # folder1 will be moved to folder2/folder1/
2415
+ library folder merge move --parent folder1/ folder2/ # folder1 will be moved to folder2/folder1/
2416
+ library folder merge move --parent file1.txt folder2/ # file1 will be moved to folder2/file1_parent_folder/file1.txt
2417
+
2418
+ nb. This tool, like other library subcommands, only works on files. Empty folders will not be moved to the destination
2419
+
2420
+ By default all files are included (the filesystem profile). Pass --video, --audio, --image,
2421
+ or --text to restrict to specific media extensions. --auto selects every media type
2422
+ (video, audio, image, and text) but still excludes other filesystem files.
2423
+
2424
+ Move files/folders without losing hierarchy metadata with --relative or `library folder merge relative-move`
2425
+
2426
+ Move smallest files first
2427
+
2428
+ library filesystem list /src/dir/ -u size -pf | library folder merge move - /dest/dir/ --relative-to=/src/dir/ --dry-run
2429
+
2430
+ Move fresh music to your phone every Sunday
2431
+
2432
+ # move last week music back to their source folders
2433
+ library folder merge move /mnt/d/sync/weekly/ /
2434
+
2435
+ # move new music for this week
2436
+ library folder merge relative-move (
2437
+ library play audio --db audio.db --local-media-only --where 'play_count=0' --random -L 600 -p f
2438
+ ) /mnt/d/sync/weekly/
2439
+
2440
+
2441
+ </details>
2442
+
2443
+ ###### folder merge copy
2444
+
2445
+ <details><summary>Copy files and merge folders</summary>
2446
+
2447
+ $ library folder merge copy -h
2448
+ usage: library folder merge copy SOURCE ... DEST [--simulate] [--ext EXT]
2449
+
2450
+ merging-move: combine file trees
2451
+
2452
+ The destination is ALWAYS a folder by default (`--dest-folder`).
2453
+ Use `--dest-bsd` to mimick BSD/GNU default `mv` behavior
2454
+ Use `--dest-file` to mimick BSD/GNU `mv --no-target-directory`
2455
+
2456
+ By default it won't matter if source folders end with a path separator or not
2457
+
2458
+ library folder merge move folder1 folder2/ # folder1 will be merged with folder2/
2459
+ library folder merge move folder1/ folder2/ # folder1 will be merged with folder2/
2460
+
2461
+ --bsd mode: an ending path separator determines if each source is to be placed within or merged with the destination
2462
+
2463
+ library folder merge move --bsd folder1/ folder2/ # folder1 will be merged with folder2/
2464
+ library folder merge move --bsd folder1 folder2/ # folder1 will be moved to folder2/folder1/
2465
+
2466
+ --parent mode: always include the parent folder name when merging
2467
+
2468
+ library folder merge move --parent folder1 folder2/ # folder1 will be moved to folder2/folder1/
2469
+ library folder merge move --parent folder1/ folder2/ # folder1 will be moved to folder2/folder1/
2470
+ library folder merge move --parent file1.txt folder2/ # file1 will be moved to folder2/file1_parent_folder/file1.txt
2471
+
2472
+ nb. This tool, like other library subcommands, only works on files. Empty folders will not be moved to the destination
2473
+
2474
+ By default all files are included (the filesystem profile). Pass --video, --audio, --image,
2475
+ or --text to restrict to specific media extensions. --auto selects every media type
2476
+ (video, audio, image, and text) but still excludes other filesystem files.
2477
+
2478
+ Move files/folders without losing hierarchy metadata with --relative or `library folder merge relative-move`
2479
+
2480
+ Move smallest files first
2481
+
2482
+ library filesystem list /src/dir/ -u size -pf | library folder merge move - /dest/dir/ --relative-to=/src/dir/ --dry-run
2483
+
2484
+ Move fresh music to your phone every Sunday
2485
+
2486
+ # move last week music back to their source folders
2487
+ library folder merge move /mnt/d/sync/weekly/ /
2488
+
2489
+ # move new music for this week
2490
+ library folder merge relative-move (
2491
+ library play audio --db audio.db --local-media-only --where 'play_count=0' --random -L 600 -p f
2492
+ ) /mnt/d/sync/weekly/
2493
+
2494
+
2495
+ </details>
2496
+
2497
+ ###### folder merge relative-move
2498
+
2499
+ <details><summary>Move files while preserving relative paths</summary>
2500
+
2501
+ $ library folder merge relative-move -h
2502
+ usage: library folder merge relative-move SOURCE ... DEST [--simulate] [--ext EXT]
2503
+
2504
+ merging-move: combine file trees
2505
+
2506
+ The destination is ALWAYS a folder by default (`--dest-folder`).
2507
+ Use `--dest-bsd` to mimick BSD/GNU default `mv` behavior
2508
+ Use `--dest-file` to mimick BSD/GNU `mv --no-target-directory`
2509
+
2510
+ By default it won't matter if source folders end with a path separator or not
2511
+
2512
+ library folder merge move folder1 folder2/ # folder1 will be merged with folder2/
2513
+ library folder merge move folder1/ folder2/ # folder1 will be merged with folder2/
2514
+
2515
+ --bsd mode: an ending path separator determines if each source is to be placed within or merged with the destination
2516
+
2517
+ library folder merge move --bsd folder1/ folder2/ # folder1 will be merged with folder2/
2518
+ library folder merge move --bsd folder1 folder2/ # folder1 will be moved to folder2/folder1/
2519
+
2520
+ --parent mode: always include the parent folder name when merging
2521
+
2522
+ library folder merge move --parent folder1 folder2/ # folder1 will be moved to folder2/folder1/
2523
+ library folder merge move --parent folder1/ folder2/ # folder1 will be moved to folder2/folder1/
2524
+ library folder merge move --parent file1.txt folder2/ # file1 will be moved to folder2/file1_parent_folder/file1.txt
2525
+
2526
+ nb. This tool, like other library subcommands, only works on files. Empty folders will not be moved to the destination
2527
+
2528
+ By default all files are included (the filesystem profile). Pass --video, --audio, --image,
2529
+ or --text to restrict to specific media extensions. --auto selects every media type
2530
+ (video, audio, image, and text) but still excludes other filesystem files.
2531
+
2532
+ Move files/folders without losing hierarchy metadata with --relative or `library folder merge relative-move`
2533
+
2534
+ Move smallest files first
2535
+
2536
+ library filesystem list /src/dir/ -u size -pf | library folder merge move - /dest/dir/ --relative-to=/src/dir/ --dry-run
2537
+
2538
+ Move fresh music to your phone every Sunday
2539
+
2540
+ # move last week music back to their source folders
2541
+ library folder merge move /mnt/d/sync/weekly/ /
2542
+
2543
+ # move new music for this week
2544
+ library folder merge relative-move (
2545
+ library play audio --db audio.db --local-media-only --where 'play_count=0' --random -L 600 -p f
2546
+ ) /mnt/d/sync/weekly/
2547
+
2548
+
2549
+ </details>
2550
+
2551
+ ###### folder merge relative-copy
2552
+
2553
+ <details><summary>Copy files while preserving relative paths</summary>
2554
+
2555
+ $ library folder merge relative-copy -h
2556
+ usage: library folder merge relative-copy SOURCE ... DEST [--simulate] [--ext EXT]
2557
+
2558
+ merging-move: combine file trees
2559
+
2560
+ The destination is ALWAYS a folder by default (`--dest-folder`).
2561
+ Use `--dest-bsd` to mimick BSD/GNU default `mv` behavior
2562
+ Use `--dest-file` to mimick BSD/GNU `mv --no-target-directory`
2563
+
2564
+ By default it won't matter if source folders end with a path separator or not
2565
+
2566
+ library folder merge move folder1 folder2/ # folder1 will be merged with folder2/
2567
+ library folder merge move folder1/ folder2/ # folder1 will be merged with folder2/
2568
+
2569
+ --bsd mode: an ending path separator determines if each source is to be placed within or merged with the destination
2570
+
2571
+ library folder merge move --bsd folder1/ folder2/ # folder1 will be merged with folder2/
2572
+ library folder merge move --bsd folder1 folder2/ # folder1 will be moved to folder2/folder1/
2573
+
2574
+ --parent mode: always include the parent folder name when merging
2575
+
2576
+ library folder merge move --parent folder1 folder2/ # folder1 will be moved to folder2/folder1/
2577
+ library folder merge move --parent folder1/ folder2/ # folder1 will be moved to folder2/folder1/
2578
+ library folder merge move --parent file1.txt folder2/ # file1 will be moved to folder2/file1_parent_folder/file1.txt
2579
+
2580
+ nb. This tool, like other library subcommands, only works on files. Empty folders will not be moved to the destination
2581
+
2582
+ By default all files are included (the filesystem profile). Pass --video, --audio, --image,
2583
+ or --text to restrict to specific media extensions. --auto selects every media type
2584
+ (video, audio, image, and text) but still excludes other filesystem files.
2585
+
2586
+ Move files/folders without losing hierarchy metadata with --relative or `library folder merge relative-move`
2587
+
2588
+ Move smallest files first
2589
+
2590
+ library filesystem list /src/dir/ -u size -pf | library folder merge move - /dest/dir/ --relative-to=/src/dir/ --dry-run
2591
+
2592
+ Move fresh music to your phone every Sunday
2593
+
2594
+ # move last week music back to their source folders
2595
+ library folder merge move /mnt/d/sync/weekly/ /
2596
+
2597
+ # move new music for this week
2598
+ library folder merge relative-move (
2599
+ library play audio --db audio.db --local-media-only --where 'play_count=0' --random -L 600 -p f
2600
+ ) /mnt/d/sync/weekly/
2601
+
2602
+
2603
+ </details>
2604
+
2605
+ ###### folder copy mergerfs
2606
+
2607
+ <details><summary>cp files with reflink on mergerfs</summary>
2608
+
2609
+ $ library folder copy mergerfs -h
2610
+ usage: library folder copy mergerfs SOURCE ... DEST [--simulate] [--ext EXT]
2611
+
2612
+ This command mirrors the behavior of BSD with regard to destination files and folders.
2613
+ To force the destination to always be a folder, similar to `library folder merge move`, use `--destination-folder`
2614
+
2615
+ Copy files with reflink and handle mergerfs mounts
2616
+
2617
+ library folder copy mergerfs --dry-run d/files* d/folder2/
2618
+ cp --interactive --reflink=always /mnt/d9/files1.txt /mnt/d9/folder2/files1.txt
2619
+ ...
2620
+
2621
+ btrfs fi du /mnt/d3/files1.txt /mnt/d3/folder2/files1.txt
2622
+ Total Exclusive Set shared Filename
2623
+ 12.57GiB 0.00B 12.57GiB /mnt/d3/files1.txt
2624
+ 12.57GiB 0.00B 12.57GiB /mnt/d3/folder2/files1.txt
2625
+
2626
+
2627
+ </details>
2628
+
2629
+ ###### folder move list
2630
+
2631
+ <details><summary>Find specific folders to move to different disks</summary>
2632
+
2633
+ $ library folder move list -h
2634
+ usage: library folder move list [--limit LIMIT] [--lower LOWER] [--upper UPPER] MOUNT_POINT DATABASE
2635
+
2636
+ Free up space on a specific disk. Find candidates for moving data to a different mount point
2637
+
2638
+
2639
+ The program takes a mount point and a library database file. If you don't have a database file you can create one like this
2640
+
2641
+ library database add filesystem --filesystem d.db ~/d/
2642
+
2643
+ But this should definitely also work with library audio and video databases
2644
+
2645
+ library folder move list /mnt/d/ video.db
2646
+
2647
+ The program will print a table with a sorted list of folders which are good candidates for moving.
2648
+ Candidates are determined by how many files are in the folder (so you don't spend hours waiting for folders with millions of tiny files to copy over).
2649
+ The default is 4 to 4000--but it can be adjusted via the --lower and --upper flags.
2650
+
2651
+ ██╗███╗░░██╗░██████╗████████╗██████╗░██╗░░░██╗░█████╗░████████╗██╗░█████╗░███╗░░██╗░██████╗
2652
+ ██║████╗░██║██╔════╝╚══██╔══╝██╔══██╗██║░░░██║██╔══██╗╚══██╔══╝██║██╔══██╗████╗░██║██╔════╝
2653
+ ██║██╔██╗██║╚█████╗░░░░██║░░░██████╔╝██║░░░██║██║░░╚═╝░░░██║░░░██║██║░░██║██╔██╗██║╚█████╗░
2654
+ ██║██║╚████║░╚═══██╗░░░██║░░░██╔══██╗██║░░░██║██║░░██╗░░░██║░░░██║██║░░██║██║╚████║░╚═══██╗
2655
+ ██║██║░╚███║██████╔╝░░░██║░░░██║░░██║╚██████╔╝╚█████╔╝░░░██║░░░██║╚█████╔╝██║░╚███║██████╔╝
2656
+ ╚═╝╚═╝░░╚══╝╚═════╝░░░░╚═╝░░░╚═╝░░╚═╝░╚═════╝░░╚════╝░░░░╚═╝░░░╚═╝░╚════╝░╚═╝░░╚══╝╚═════╝░
2657
+
2658
+ Type "done" when finished
2659
+ Type "more" to see more files
2660
+ Paste a folder (and press enter) to toggle selection
2661
+ Type "*" to select all files in the most recently printed table
2662
+
2663
+ Then it will give you a prompt
2664
+
2665
+ Paste a path:
2666
+
2667
+ Wherein you can copy and paste paths you want to move from the table and the program will keep track for you.
2668
+
2669
+ Paste a path: /mnt/d/75_MovieQueue/720p/s11/
2670
+ 26 selected paths: 162.1 GB ; future free space: 486.9 GB
2671
+
2672
+ You can also press the up arrow or paste it again to remove it from the list
2673
+
2674
+ Paste a path: /mnt/d/75_MovieQueue/720p/s11/
2675
+ 25 selected paths: 159.9 GB ; future free space: 484.7 GB
2676
+
2677
+ After you are done selecting folders you can press ctrl-d and it will save the list to a tmp file
2678
+
2679
+ Paste a path: done
2680
+
2681
+ Folder list saved to /tmp/tmp7x_75l8. You may want to use the following command to move files to an EMPTY folder target:
2682
+
2683
+ rsync -a --info=progress2 --no-inc-recursive --remove-source-files --files-from=/tmp/tmp7x_75l8 -r --relative -vv --dry-run / jim:/free/real/estate/
2684
+
2685
+
2686
+ </details>
2687
+
2688
+ ###### folder large
2689
+
2690
+ <details><summary>Show large folders</summary>
2691
+
2692
+ $ library folder large -h
2693
+ usage: library folder large [PATH ...] [--db DATABASE] [--limit (4000)] [--depth (0)] [--sort-groups-by deleted | played]
2694
+
2695
+ See what folders take up space
2696
+
2697
+ library folder large ./video/
2698
+
2699
+ Filter folders by size
2700
+
2701
+ library folder large ./video/ --folder-sizes=+10GB --folder-sizes=-200GB
2702
+ library folder large ./video/ -FS=+10GB -FS=-200GB
2703
+
2704
+ Filter folders by file count
2705
+
2706
+ library folder large ./video/ --file-counts=+3 --file-counts=-3000
2707
+ library folder large ./video/ -FC=+20000 -D-8 # Use --depth to cascade nested directories
2708
+
2709
+ Filter folders by sub-folder "bigdir" count
2710
+
2711
+ library folder large ./video/ --folder-counts=+3 --folder-counts=-3000
2712
+
2713
+ Filter folders by depth
2714
+
2715
+ library folder large ./video/ --depth 5
2716
+ library folder large ./video/ -D 7
2717
+
2718
+ Load from fs database
2719
+
2720
+ library filesystem list --db video.db --cols path,duration,size,time_deleted --to-json | library folder large --from-json
2721
+
2722
+ Only include files between 1MiB and 5MiB
2723
+ library filesystem list --db video.db -S+1M -S-5M --cols path,duration,size,time_deleted --to-json | library folder large --from-json
2724
+
2725
+ You can even sort by auto-MCDA ~LOL~
2726
+
2727
+ library folder large ./video/ -u 'mcda median_size,-deleted'
2728
+
2729
+
2730
+ </details>
2731
+
2732
+ ###### folder scatter
2733
+
2734
+ <details><summary>Scatter files between folders or disks</summary>
2735
+
2736
+ $ library folder scatter -h
2737
+ usage: library folder scatter [--limit LIMIT] [--policy POLICY] [--sort SORT] --targets TARGETS DATABASE RELATIVE_PATH ...
2738
+
2739
+ Scatter filesystem folder trees (without mountpoints; limited functionality; good for balancing fs inodes)
2740
+
2741
+ library folder scatter scatter.db /test/{0,1,2,3,4,5,6,7,8,9}
2742
+
2743
+ Reduce number of files per folder (creates more folders)
2744
+
2745
+ library folder scatter scatter.db --max-files-per-folder 16000 /test/{0,1,2,3,4,5,6,7,8,9}
2746
+
2747
+ Balance files across filesystem folder trees or multiple devices (mostly useful for mergerfs)
2748
+
2749
+ Multi-device re-bin: balance by size
2750
+
2751
+ library folder scatter -m /mnt/d1:/mnt/d2:/mnt/d3:/mnt/d4/:/mnt/d5:/mnt/d6:/mnt/d7 fs.db subfolder/of/mergerfs/mnt
2752
+ Current path distribution:
2753
+ ╒═════════╤══════════════╤══════════════╤═══════════════╤════════════════╤═════════════════╤════════════════╕
2754
+ │ mount │ file_count │ total_size │ median_size │ time_created │ time_modified │ time_downloaded│
2755
+ ╞═════════╪══════════════╪══════════════╪═══════════════╪════════════════╪═════════════════╪════════════════╡
2756
+ │ /mnt/d1 │ 12793 │ 169.5 GB │ 4.5 MB │ Jan 27 │ Jul 19 2022 │ Jan 31 │
2757
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2758
+ │ /mnt/d2 │ 13226 │ 177.9 GB │ 4.7 MB │ Jan 27 │ Jul 19 2022 │ Jan 31 │
2759
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2760
+ │ /mnt/d3 │ 1 │ 717.6 kB │ 717.6 kB │ Jan 31 │ Jul 18 2022 │ yesterday │
2761
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2762
+ │ /mnt/d4 │ 82 │ 1.5 GB │ 12.5 MB │ Jan 31 │ Apr 22 2022 │ yesterday │
2763
+ ╘═════════╧══════════════╧══════════════╧═══════════════╧════════════════╧═════════════════╧════════════════╛
2764
+
2765
+ Simulated path distribution:
2766
+ 5845 files should be moved
2767
+ 20257 files should not be moved
2768
+ ╒═════════╤══════════════╤══════════════╤═══════════════╤════════════════╤═════════════════╤════════════════╕
2769
+ │ mount │ file_count │ total_size │ median_size │ time_created │ time_modified │ time_downloaded│
2770
+ ╞═════════╪══════════════╪══════════════╪═══════════════╪════════════════╪═════════════════╪════════════════╡
2771
+ │ /mnt/d1 │ 9989 │ 46.0 GB │ 2.4 MB │ Jan 27 │ Jul 19 2022 │ Jan 31 │
2772
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2773
+ │ /mnt/d2 │ 10185 │ 46.0 GB │ 2.4 MB │ Jan 27 │ Jul 19 2022 │ Jan 31 │
2774
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2775
+ │ /mnt/d3 │ 1186 │ 53.6 GB │ 30.8 MB │ Jan 27 │ Apr 07 2022 │ Jan 31 │
2776
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2777
+ │ /mnt/d4 │ 1216 │ 49.5 GB │ 29.5 MB │ Jan 27 │ Apr 07 2022 │ Jan 31 │
2778
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2779
+ │ /mnt/d5 │ 1146 │ 53.0 GB │ 30.9 MB │ Jan 27 │ Apr 07 2022 │ Jan 31 │
2780
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2781
+ │ /mnt/d6 │ 1198 │ 48.8 GB │ 30.6 MB │ Jan 27 │ Apr 07 2022 │ Jan 31 │
2782
+ ├─────────┼──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────┤
2783
+ │ /mnt/d7 │ 1182 │ 52.0 GB │ 30.9 MB │ Jan 27 │ Apr 07 2022 │ Jan 31 │
2784
+ ╘═════════╧══════════════╧══════════════╧═══════════════╧════════════════╧═════════════════╧════════════════╛
2785
+ ### Move 1182 files to /mnt/d7 with this command: ###
2786
+ rsync -aE --xattrs --info=progress2 --remove-source-files --files-from=/tmp/tmpmr1628ij / /mnt/d7
2787
+ ### Move 1198 files to /mnt/d6 with this command: ###
2788
+ rsync -aE --xattrs --info=progress2 --remove-source-files --files-from=/tmp/tmp9yd75f6j / /mnt/d6
2789
+ ### Move 1146 files to /mnt/d5 with this command: ###
2790
+ rsync -aE --xattrs --info=progress2 --remove-source-files --files-from=/tmp/tmpfrj141jj / /mnt/d5
2791
+ ### Move 1185 files to /mnt/d3 with this command: ###
2792
+ rsync -aE --xattrs --info=progress2 --remove-source-files --files-from=/tmp/tmpqh2euc8n / /mnt/d3
2793
+ ### Move 1134 files to /mnt/d4 with this command: ###
2794
+ rsync -aE --xattrs --info=progress2 --remove-source-files --files-from=/tmp/tmphzb0gj92 / /mnt/d4
2795
+
2796
+ Multi-device re-bin: balance device inodes for specific subfolder
2797
+
2798
+ library folder scatter -m /mnt/d1:/mnt/d2 fs.db subfolder --group count --sort 'size desc'
2799
+
2800
+ Multi-device re-bin: only consider the most recent 100 files
2801
+
2802
+ library folder scatter -m /mnt/d1:/mnt/d2 -l 100 -s 'time_modified desc' fs.db /
2803
+
2804
+ Multi-device re-bin: empty out a disk (/mnt/d2) into many other disks (/mnt/d1, /mnt/d3, and /mnt/d4)
2805
+
2806
+ library folder scatter fs.db -m /mnt/d1:/mnt/d3:/mnt/d4 /mnt/d2
2807
+
2808
+ This tool is intended for local use. If transferring many small files across the network something like
2809
+ [fpart](https://github.com/martymac/fpart) or [fpsync](https://www.fpart.org/fpsync/) will be better.
2810
+
2811
+
2812
+ </details>
2813
+
2814
+ ###### folder similar
2815
+
2816
+ <details><summary>Find similar folders based on folder name, size, and count</summary>
2817
+
2818
+ $ library folder similar -h
2819
+ usage: library folder similar (--filter-names | --filter-counts | --filter-sizes | --filter-durations) PATH ...
2820
+
2821
+ Find similar folders based on foldernames, similar size, and similar number of files
2822
+
2823
+ library folder similar --filter-names --filter-counts --filter-sizes ~/d/
2824
+
2825
+ group /home/xk/d/dump/datasets/*vector total_size median_size files
2826
+ ---------------------------------------------- ------------ ------------- -------
2827
+ /home/xk/d/dump/datasets/vector/output/ 1.8 GiB 89.5 KiB 1980
2828
+ /home/xk/d/dump/datasets/vector/output2/ 1.8 GiB 89.5 KiB 1979
2829
+
2830
+ Find similar folders based on ONLY foldernames, using the full path
2831
+
2832
+ library folder similar --filter-names --full-path ~/d/
2833
+
2834
+ Find similar folders based on ONLY number of files
2835
+
2836
+ library folder similar --filter-counts ~/d/
2837
+
2838
+ Find similar folders based on ONLY median size
2839
+
2840
+ library folder similar --filter-sizes ~/d/
2841
+
2842
+ Find similar folders based on ONLY total size
2843
+
2844
+ library folder similar --filter-sizes --total-size ~/d/
2845
+
2846
+ Read paths from dbs
2847
+
2848
+ library filesystem list --db audio.db --cols path,duration,size,time_deleted --to-json | library folder similar --from-json -v
2849
+
2850
+ Print only paths
2851
+
2852
+ library folder similar ~/d/ -pf
2853
+ /home/xk/d/dump/datasets/vector/output/
2854
+ /home/xk/d/dump/datasets/vector/output2/
2855
+
2856
+ How I use it
2857
+ library filesystem list --db video.db --cols path,duration,size,time_deleted --to-json | library folder similar --from-json -FS=+8G --filter-names --filter-counts --filter-durations
2858
+
2859
+
2860
+ </details>
2861
+
2862
+ ###### file normalize
2863
+
2864
+ <details><summary>Clean file paths</summary>
2865
+
2866
+ $ library file normalize -h
2867
+ usage: library file normalize [--run]
2868
+
2869
+ Rename files to be somewhat normalized
2870
+
2871
+ Default mode is simulate
2872
+
2873
+ library file normalize ~/messy/
2874
+
2875
+ To actually do stuff use the run flag
2876
+
2877
+ library file normalize . --run
2878
+
2879
+ You can optionally replace all the spaces in your filenames with dots
2880
+
2881
+ library file normalize --dot-space
2882
+
2883
+
2884
+ </details>
2885
+
2886
+ ###### file hash
2887
+
2888
+ <details><summary>Calculate a hash based on small file segments</summary>
2889
+
2890
+ $ library file hash -h
2891
+ usage: library file hash [--same-file-threads 1] [--chunk-size BYTES] [--gap BYTES OR 0.0-1.0*FILESIZE] PATH ...
2892
+
2893
+ Calculate hashes for large files by reading only small segments of each file
2894
+
2895
+ library file hash ./my_file.mkv
2896
+
2897
+ The threads flag seems to be faster for rotational media but slower on SSDs
2898
+
2899
+
2900
+ </details>
2901
+
2902
+ ###### file compare
2903
+
2904
+ <details><summary>Compare files using sample-hash and other shortcuts</summary>
2905
+
2906
+ $ library file compare -h
2907
+ usage: library file compare [--same-file-threads 1] [--chunk-size BYTES] [--gap BYTES OR 0.0-1.0*FILESIZE] PATH ...
2908
+
2909
+ Convenience subcommand to compare multiple files using sample-hash
2910
+
2911
+
2912
+ </details>
2913
+
2914
+ ###### file similar
2915
+
2916
+ <details><summary>Find similar files based on filename and size</summary>
2917
+
2918
+ $ library file similar -h
2919
+ usage: library file similar (--filter-names | --filter-sizes | --filter-durations) PATH ...
2920
+
2921
+ Find similar files using filenames and size
2922
+
2923
+ library file similar --filter-names --filter-sizes ~/d/
2924
+
2925
+ Find similar files based on ONLY foldernames, using the full path
2926
+
2927
+ library file similar --filter-names --full-path ~/d/
2928
+
2929
+ Find similar files based on ONLY size
2930
+
2931
+ library file similar --filter-sizes ~/d/
2932
+
2933
+ Read paths from dbs
2934
+
2935
+ library filesystem list --db audio.db --cols path,duration,size,time_deleted --to-json | library file similar --from-json -v
2936
+
2937
+ How I use it
2938
+ library file similar --filter-names --filter-durations --estimated-duplicates 3 .
2939
+
2940
+
2941
+ </details>
2942
+
2943
+ ###### file media-hash
2944
+
2945
+ <details><summary>Hash decoded audio/video streams with ffmpeg</summary>
2946
+
2947
+ $ library file media-hash -h
2948
+ usage: library file media-hash PATH ...
2949
+
2950
+ Hash decoded audio/video streams with ffmpeg's framemd5 muxer.
2951
+
2952
+
2953
+ </details>
2954
+
2955
+ ###### file media-compare
2956
+
2957
+ <details><summary>Compare decoded audio/video streams</summary>
2958
+
2959
+ $ library file media-compare -h
2960
+ usage: library file media-compare PATH PATH [PATH ...]
2961
+
2962
+ Compare decoded audio/video streams. Exit with status 1 when any media
2963
+ differs.
2964
+
2965
+
2966
+ </details>
2967
+
2968
+ ### Filesystem inspection subcommands
2969
+
2970
+ ###### filesystem list
2971
+
2972
+ <details><summary>Find files by mimetype and size</summary>
2973
+
2974
+ $ library filesystem list -h
2975
+ usage: library filesystem list [--db DATABASE] [PATH ...]
2976
+
2977
+ List files and filter by specific file types
2978
+
2979
+ library filesystem list
2980
+
2981
+
2982
+ </details>
2983
+
2984
+ ###### filesystem usage
2985
+
2986
+ <details><summary>Show disk usage</summary>
2987
+
2988
+ $ library filesystem usage -h
2989
+ usage: library filesystem usage [--db DATABASE] [PATH / SUBSTRING SEARCH] [--sort-groups-by (priority) | path | size | count] [--depth DEPTH]
2990
+
2991
+ To aggregate to high level folders use --parents and --depth
2992
+
2993
+ library filesystem usage --db d7.db --parents -D=-4 --file-counts=+20000
2994
+ path size count folders
2995
+ ------------------ -------- ------- ---------
2996
+ /mnt/d7/processed/ 685.8GiB 30783 2508
2997
+ /mnt/d7/dump/ 2.1TiB 1741160 184883
2998
+ /mnt/d7/check/ 9.0TiB 735410 117866
2999
+ /mnt/d7/ 16.4TiB 2539139 306036
3000
+ /mnt/ 16.4TiB 2539139 306036
3001
+ / 16.4TiB 2539139 306036
3002
+
3003
+ Only include files smaller than 1kib
3004
+
3005
+ library filesystem usage --db du.db --size=-1Ki
3006
+ library filesystem usage --db du.db -S-1Ki
3007
+ | path | size | count |
3008
+ |---------------------------------------|-----------|---------|
3009
+ | /home/xk/github/xk/lb/__pycache__/ | 620 Bytes | 1 |
3010
+ | /home/xk/github/xk/lb/.github/ | 1.7 kB | 4 |
3011
+ | /home/xk/github/xk/lb/__pypackages__/ | 1.4 MB | 3519 |
3012
+ | /home/xk/github/xk/lb/xklb/ | 4.4 kB | 12 |
3013
+ | /home/xk/github/xk/lb/tests/ | 3.2 kB | 9 |
3014
+ | /home/xk/github/xk/lb/.git/ | 782.4 kB | 2276 |
3015
+ | /home/xk/github/xk/lb/.pytest_cache/ | 1.5 kB | 5 |
3016
+ | /home/xk/github/xk/lb/.ruff_cache/ | 19.5 kB | 100 |
3017
+ | /home/xk/github/xk/lb/.gitattributes | 119 Bytes | |
3018
+ | /home/xk/github/xk/lb/.mypy_cache/ | 280 Bytes | 4 |
3019
+ | /home/xk/github/xk/lb/.venv | 15 Bytes | |
3020
+
3021
+ Only include files with a specific depth
3022
+
3023
+ library filesystem usage --db du.db --depth 19
3024
+ library filesystem usage --db du.db -d 19
3025
+ | path | size |
3026
+ |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
3027
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyi | 88 Bytes |
3028
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyi | 81 Bytes |
3029
+
3030
+ Group by extension
3031
+
3032
+ library filesystem usage extensions --db du.db
3033
+ library filesystem usage --db du.db --group-by-extension
3034
+ path size count
3035
+ ------ -------- -------
3036
+ avi 1.8 GiB 5
3037
+ webm 14.3 GiB 47
3038
+ mkv 49.2 GiB 159
3039
+ mp4 86.7 GiB 613
3040
+ 4 file extensions
3041
+
3042
+
3043
+ </details>
3044
+
3045
+ ###### filesystem usage extensions
3046
+
3047
+ <details><summary>Show disk usage grouped by file extension</summary>
3048
+
3049
+ $ library filesystem usage extensions -h
3050
+ usage: library filesystem usage extensions [--db DATABASE] [PATH / SUBSTRING SEARCH] [--sort-groups-by (priority) | path | size | count] [--depth DEPTH]
3051
+
3052
+ To aggregate to high level folders use --parents and --depth
3053
+
3054
+ library filesystem usage --db d7.db --parents -D=-4 --file-counts=+20000
3055
+ path size count folders
3056
+ ------------------ -------- ------- ---------
3057
+ /mnt/d7/processed/ 685.8GiB 30783 2508
3058
+ /mnt/d7/dump/ 2.1TiB 1741160 184883
3059
+ /mnt/d7/check/ 9.0TiB 735410 117866
3060
+ /mnt/d7/ 16.4TiB 2539139 306036
3061
+ /mnt/ 16.4TiB 2539139 306036
3062
+ / 16.4TiB 2539139 306036
3063
+
3064
+ Only include files smaller than 1kib
3065
+
3066
+ library filesystem usage --db du.db --size=-1Ki
3067
+ library filesystem usage --db du.db -S-1Ki
3068
+ | path | size | count |
3069
+ |---------------------------------------|-----------|---------|
3070
+ | /home/xk/github/xk/lb/__pycache__/ | 620 Bytes | 1 |
3071
+ | /home/xk/github/xk/lb/.github/ | 1.7 kB | 4 |
3072
+ | /home/xk/github/xk/lb/__pypackages__/ | 1.4 MB | 3519 |
3073
+ | /home/xk/github/xk/lb/xklb/ | 4.4 kB | 12 |
3074
+ | /home/xk/github/xk/lb/tests/ | 3.2 kB | 9 |
3075
+ | /home/xk/github/xk/lb/.git/ | 782.4 kB | 2276 |
3076
+ | /home/xk/github/xk/lb/.pytest_cache/ | 1.5 kB | 5 |
3077
+ | /home/xk/github/xk/lb/.ruff_cache/ | 19.5 kB | 100 |
3078
+ | /home/xk/github/xk/lb/.gitattributes | 119 Bytes | |
3079
+ | /home/xk/github/xk/lb/.mypy_cache/ | 280 Bytes | 4 |
3080
+ | /home/xk/github/xk/lb/.venv | 15 Bytes | |
3081
+
3082
+ Only include files with a specific depth
3083
+
3084
+ library filesystem usage --db du.db --depth 19
3085
+ library filesystem usage --db du.db -d 19
3086
+ | path | size |
3087
+ |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
3088
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyi | 88 Bytes |
3089
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyi | 81 Bytes |
3090
+
3091
+ Group by extension
3092
+
3093
+ library filesystem usage extensions --db du.db
3094
+ library filesystem usage --db du.db --group-by-extension
3095
+ path size count
3096
+ ------ -------- -------
3097
+ avi 1.8 GiB 5
3098
+ webm 14.3 GiB 47
3099
+ mkv 49.2 GiB 159
3100
+ mp4 86.7 GiB 613
3101
+ 4 file extensions
3102
+
3103
+
3104
+ </details>
3105
+
3106
+ ###### filesystem usage mimetypes
3107
+
3108
+ <details><summary>Show disk usage grouped by MIME type</summary>
3109
+
3110
+ $ library filesystem usage mimetypes -h
3111
+ usage: library filesystem usage mimetypes [--db DATABASE] [PATH / SUBSTRING SEARCH] [--sort-groups-by (priority) | path | size | count] [--depth DEPTH]
3112
+
3113
+ To aggregate to high level folders use --parents and --depth
3114
+
3115
+ library filesystem usage --db d7.db --parents -D=-4 --file-counts=+20000
3116
+ path size count folders
3117
+ ------------------ -------- ------- ---------
3118
+ /mnt/d7/processed/ 685.8GiB 30783 2508
3119
+ /mnt/d7/dump/ 2.1TiB 1741160 184883
3120
+ /mnt/d7/check/ 9.0TiB 735410 117866
3121
+ /mnt/d7/ 16.4TiB 2539139 306036
3122
+ /mnt/ 16.4TiB 2539139 306036
3123
+ / 16.4TiB 2539139 306036
3124
+
3125
+ Only include files smaller than 1kib
3126
+
3127
+ library filesystem usage --db du.db --size=-1Ki
3128
+ library filesystem usage --db du.db -S-1Ki
3129
+ | path | size | count |
3130
+ |---------------------------------------|-----------|---------|
3131
+ | /home/xk/github/xk/lb/__pycache__/ | 620 Bytes | 1 |
3132
+ | /home/xk/github/xk/lb/.github/ | 1.7 kB | 4 |
3133
+ | /home/xk/github/xk/lb/__pypackages__/ | 1.4 MB | 3519 |
3134
+ | /home/xk/github/xk/lb/xklb/ | 4.4 kB | 12 |
3135
+ | /home/xk/github/xk/lb/tests/ | 3.2 kB | 9 |
3136
+ | /home/xk/github/xk/lb/.git/ | 782.4 kB | 2276 |
3137
+ | /home/xk/github/xk/lb/.pytest_cache/ | 1.5 kB | 5 |
3138
+ | /home/xk/github/xk/lb/.ruff_cache/ | 19.5 kB | 100 |
3139
+ | /home/xk/github/xk/lb/.gitattributes | 119 Bytes | |
3140
+ | /home/xk/github/xk/lb/.mypy_cache/ | 280 Bytes | 4 |
3141
+ | /home/xk/github/xk/lb/.venv | 15 Bytes | |
3142
+
3143
+ Only include files with a specific depth
3144
+
3145
+ library filesystem usage --db du.db --depth 19
3146
+ library filesystem usage --db du.db -d 19
3147
+ | path | size |
3148
+ |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
3149
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyi | 88 Bytes |
3150
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyi | 81 Bytes |
3151
+
3152
+ Group by extension
3153
+
3154
+ library filesystem usage extensions --db du.db
3155
+ library filesystem usage --db du.db --group-by-extension
3156
+ path size count
3157
+ ------ -------- -------
3158
+ avi 1.8 GiB 5
3159
+ webm 14.3 GiB 47
3160
+ mkv 49.2 GiB 159
3161
+ mp4 86.7 GiB 613
3162
+ 4 file extensions
3163
+
3164
+
3165
+ </details>
3166
+
3167
+ ###### filesystem usage sizes
3168
+
3169
+ <details><summary>Show disk usage grouped by file size</summary>
3170
+
3171
+ $ library filesystem usage sizes -h
3172
+ usage: library filesystem usage sizes [--db DATABASE] [PATH / SUBSTRING SEARCH] [--sort-groups-by (priority) | path | size | count] [--depth DEPTH]
3173
+
3174
+ To aggregate to high level folders use --parents and --depth
3175
+
3176
+ library filesystem usage --db d7.db --parents -D=-4 --file-counts=+20000
3177
+ path size count folders
3178
+ ------------------ -------- ------- ---------
3179
+ /mnt/d7/processed/ 685.8GiB 30783 2508
3180
+ /mnt/d7/dump/ 2.1TiB 1741160 184883
3181
+ /mnt/d7/check/ 9.0TiB 735410 117866
3182
+ /mnt/d7/ 16.4TiB 2539139 306036
3183
+ /mnt/ 16.4TiB 2539139 306036
3184
+ / 16.4TiB 2539139 306036
3185
+
3186
+ Only include files smaller than 1kib
3187
+
3188
+ library filesystem usage --db du.db --size=-1Ki
3189
+ library filesystem usage --db du.db -S-1Ki
3190
+ | path | size | count |
3191
+ |---------------------------------------|-----------|---------|
3192
+ | /home/xk/github/xk/lb/__pycache__/ | 620 Bytes | 1 |
3193
+ | /home/xk/github/xk/lb/.github/ | 1.7 kB | 4 |
3194
+ | /home/xk/github/xk/lb/__pypackages__/ | 1.4 MB | 3519 |
3195
+ | /home/xk/github/xk/lb/xklb/ | 4.4 kB | 12 |
3196
+ | /home/xk/github/xk/lb/tests/ | 3.2 kB | 9 |
3197
+ | /home/xk/github/xk/lb/.git/ | 782.4 kB | 2276 |
3198
+ | /home/xk/github/xk/lb/.pytest_cache/ | 1.5 kB | 5 |
3199
+ | /home/xk/github/xk/lb/.ruff_cache/ | 19.5 kB | 100 |
3200
+ | /home/xk/github/xk/lb/.gitattributes | 119 Bytes | |
3201
+ | /home/xk/github/xk/lb/.mypy_cache/ | 280 Bytes | 4 |
3202
+ | /home/xk/github/xk/lb/.venv | 15 Bytes | |
3203
+
3204
+ Only include files with a specific depth
3205
+
3206
+ library filesystem usage --db du.db --depth 19
3207
+ library filesystem usage --db du.db -d 19
3208
+ | path | size |
3209
+ |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
3210
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyi | 88 Bytes |
3211
+ | /home/xk/github/xk/lb/__pypackages__/3.12/lib/jedi/third_party/typeshed/third_party/2and3/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyi | 81 Bytes |
3212
+
3213
+ Group by extension
3214
+
3215
+ library filesystem usage extensions --db du.db
3216
+ library filesystem usage --db du.db --group-by-extension
3217
+ path size count
3218
+ ------ -------- -------
3219
+ avi 1.8 GiB 5
3220
+ webm 14.3 GiB 47
3221
+ mkv 49.2 GiB 159
3222
+ mp4 86.7 GiB 613
3223
+ 4 file extensions
3224
+
3225
+
3226
+ </details>
3227
+
3228
+ ###### filesystem mounts stats
3229
+
3230
+ <details><summary>Show some relative mount stats</summary>
3231
+
3232
+ $ library filesystem mounts stats -h
3233
+ usage: library filesystem mounts stats MOUNTPOINT ...
3234
+
3235
+ Print relative use and free for multiple mount points
3236
+
3237
+ library filesystem mounts stats (fd -td -d1 'd[0-9]+$' /mnt)
3238
+ Relative disk dependence:
3239
+ /mnt/d1: ###### 8.1%
3240
+ /mnt/d2: ######### 12.2%
3241
+ /mnt/d3: ######### 12.2%
3242
+ /mnt/d4: ####### 9.5%
3243
+ /mnt/d5: ####### 9.5%
3244
+ /mnt/d6: ######### 12.2%
3245
+ /mnt/d7: ######### 12.2%
3246
+ /mnt/d8: ######### 12.2%
3247
+ /mnt/d9: ######### 12.2%
3248
+
3249
+ Relative free space:
3250
+ /mnt/d1: ##### 6.9%
3251
+ /mnt/d2: ########### 13.8%
3252
+ /mnt/d3: ######## 10.4%
3253
+ /mnt/d4: ######## 10.5%
3254
+ /mnt/d5: ###### 8.7%
3255
+ /mnt/d6: ######### 11.8%
3256
+ /mnt/d7: ######### 11.9%
3257
+ /mnt/d8: ######### 12.2%
3258
+ /mnt/d9: ########### 13.8%
3259
+
3260
+
3261
+ </details>
3262
+
3263
+ ###### filesystem mounts free
3264
+
3265
+ <details><summary>Show system-wide disk usage</summary>
3266
+
3267
+ $ library filesystem mounts free -h
3268
+ usage: library filesystem mounts free [MOUNTPOINT ...]
3269
+
3270
+ Print total disk usage and disk free stats
3271
+
3272
+ library filesystem mounts free
3273
+
3274
+
3275
+ </details>
3276
+
3277
+ ### Media processing subcommands
3278
+
3279
+ ###### file check
3280
+
3281
+ <details><summary>Check video and audio files for corruption via ffmpeg</summary>
3282
+
3283
+ $ library file check -h
3284
+ usage: library file check [--chunk-size SECONDS] [--gap SECONDS OR 0.0-1.0*DURATION] [--delete-corrupt >0-100] [--full-scan] [--audio-scan] PATH ...
3285
+
3286
+ Defaults to decode 0.5 second per 10% of each file
3287
+
3288
+ library file check ./video.mp4
3289
+
3290
+ Decode all the frames of each file to evaluate how corrupt it is
3291
+ (scantime is very slow; about 150 seconds for an hour-long file)
3292
+
3293
+ library file check --full-scan ./video.mp4
3294
+
3295
+ Decode all the packets of each file to evaluate how corrupt it is
3296
+ (scantime is about one second of each file but only accurate for formats where 1 packet == 1 frame)
3297
+
3298
+ library file check --full-scan --gap 0 ./video.mp4
3299
+
3300
+ Decode all audio of each file to evaluate how corrupt it is
3301
+ (scantime is about four seconds per file)
3302
+
3303
+ library file check --full-scan --audio ./video.mp4
3304
+
3305
+ Decode at least one frame at the start and end of each file to evaluate how corrupt it is
3306
+ (scantime is about one second per file)
3307
+
3308
+ library file check --chunk-size 5% --gap 99.9% ./video.mp4
3309
+
3310
+ Decode 3s every 5% of a file to evaluate how corrupt it is
3311
+ (scantime is about three seconds per file)
3312
+
3313
+ library file check --chunk-size 3 --gap 5% ./video.mp4
3314
+
3315
+ Delete the file if 20 percent or more of checks fail
3316
+
3317
+ library file check --delete-corrupt 20% ./video.mp4
3318
+
3319
+ To scan a large folder use `fsadd`. I recommend something like this two-stage approach
3320
+
3321
+ library database add filesystem --delete-unplayable --check-corrupt --chunk-size 5% tmp.db ./video/ ./folders/
3322
+ library file check (library filesystem list --db tmp.db -w 'corruption>15' -pf) --full-scan --delete-corrupt 25%
3323
+
3324
+ The above can now be done in one command via `--full-scan-if-corrupt`
3325
+
3326
+ library database add filesystem --delete-unplayable --check-corrupt --chunk-size 5% tmp.db ./video/ ./folders/ --full-scan-if-corrupt 15% --delete-corrupt 25%
3327
+
3328
+ Corruption stats
3329
+
3330
+ library filesystem list --db tmp.db -w 'corruption>15' -pa
3331
+ path count duration avg_duration size avg_size
3332
+ --------- ------- ------------------- -------------- --------- ----------
3333
+ Aggregate 907 15 days and 9 hours 24 minutes 130.6 GiB 147.4 MiB
3334
+
3335
+ Corruption graph
3336
+
3337
+ sqlite --raw-lines tmp.db 'select corruption from media' | lowcharts hist --min 10 --intervals 10
3338
+
3339
+ Samples = 931; Min = 10.0; Max = 100.0
3340
+ Average = 39.1; Variance = 1053.103; STD = 32.452
3341
+ each ∎ represents a count of 6
3342
+ [ 10.0 .. 19.0] [561] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3343
+ [ 19.0 .. 28.0] [ 69] ∎∎∎∎∎∎∎∎∎∎∎
3344
+ [ 28.0 .. 37.0] [ 33] ∎∎∎∎∎
3345
+ [ 37.0 .. 46.0] [ 18] ∎∎∎
3346
+ [ 46.0 .. 55.0] [ 14] ∎∎
3347
+ [ 55.0 .. 64.0] [ 12] ∎∎
3348
+ [ 64.0 .. 73.0] [ 15] ∎∎
3349
+ [ 73.0 .. 82.0] [ 18] ∎∎∎
3350
+ [ 82.0 .. 91.0] [ 50] ∎∎∎∎∎∎∎∎
3351
+ [ 91.0 .. 100.0] [141] ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3352
+
3353
+
3354
+ </details>
3355
+
3356
+ ###### shrink media
3357
+
3358
+ <details><summary>Estimate and execute potential disk space savings</summary>
3359
+
3360
+ $ library shrink media -h
3361
+ usage: library shrink media [PATH ...] [--db DATABASE]
3362
+
3363
+ Running fsadd first to create a video database will be faster because it runs many files in parallel
3364
+
3365
+ library database add filesystem --video video.db ~/Videos/
3366
+ library shrink media --db video.db
3367
+
3368
+ But using media folder/file paths directly is also supported
3369
+
3370
+ library shrink media ~/Videos/
3371
+
3372
+ Find invalid media to attempt to transcode or delete
3373
+
3374
+ library shrink media --db video.db --invalid --no-valid --delete-unplayable
3375
+
3376
+ If not installed, related file extensions will be skipped during scan:
3377
+
3378
+ - FFmpeg is required for shrinking video and audio
3379
+ - ImageMagick is required for shrinking images
3380
+ - Calibre is required for shrinking eBooks
3381
+
3382
+ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3383
+
3384
+
3385
+ </details>
3386
+
3387
+ ###### shrink table
3388
+
3389
+ <details><summary>Convert table-like files to compressed Parquet</summary>
3390
+
3391
+ $ library shrink table -h
3392
+ usage: library shrink table PATH ... [--output-path OUTPUT.parquet]
3393
+
3394
+ Convert table-like files to compressed Parquet using pandas.
3395
+
3396
+ library shrink table data.csv
3397
+ library shrink table workbook.xlsx --join-tables --compression zstd
3398
+
3399
+
3400
+ </details>
3401
+
3402
+ ###### shrink video
3403
+
3404
+ <details><summary>Shrink video/audio to AV1/Opus format (.mkv, .mka)</summary>
3405
+
3406
+ $ library shrink video -h
3407
+ usage: library shrink video PATH ... [--always-split] [--split-longer-than DURATION] [--min-split-segment SECONDS] [--simulate]
3408
+
3409
+ Resize videos to max 1440x960px AV1 and/or Opus to save space
3410
+
3411
+ Convert audio to Opus. Optionally split up long tracks into multiple files.
3412
+
3413
+ fd -tf -eDTS -eAAC -eWAV -eAIF -eAIFF -eFLAC -eAIFF -eM4A -eMP3 -eOGG -eMP4 -eWMA -j4 -x library shrink audio
3414
+
3415
+ Use --always-split to _always_ split files if silence is detected
3416
+
3417
+ library shrink audio --always-split audiobook.m4a
3418
+
3419
+ Use --split-longer-than to _only_ detect silence for files in excess of a specific duration
3420
+
3421
+ library shrink audio --split-longer-than 36mins audiobook.m4b audiobook2.mp3
3422
+
3423
+ Calculate how much space you could save via process-ffmpeg by running something like this:
3424
+
3425
+ numfmt --to=iec (sqlite-utils --no-headers --raw-lines ~/lb/video.db "select sum(size)-sum(duration*100000) from media where time_deleted=0 and video_count>=1 and video_codecs != 'av1' and size/duration > 100000")
3426
+ numfmt --to=iec (sqlite-utils --no-headers --raw-lines ~/lb/audio.db "select sum(size)-sum(duration*18000) from media where time_deleted=0 and video_count=0 and audio_count>=1 and audio_codecs != 'opus' and size/duration > 18000")
3427
+
3428
+
3429
+ </details>
3430
+
3431
+ ###### shrink audio
3432
+
3433
+ <details><summary>Shrink audio to Opus</summary>
3434
+
3435
+ $ library shrink audio -h
3436
+ usage: library shrink audio PATH ... [--always-split] [--split-longer-than DURATION] [--min-split-segment SECONDS] [--simulate]
3437
+
3438
+ Resize videos to max 1440x960px AV1 and/or Opus to save space
3439
+
3440
+ Convert audio to Opus. Optionally split up long tracks into multiple files.
3441
+
3442
+ fd -tf -eDTS -eAAC -eWAV -eAIF -eAIFF -eFLAC -eAIFF -eM4A -eMP3 -eOGG -eMP4 -eWMA -j4 -x library shrink audio
3443
+
3444
+ Use --always-split to _always_ split files if silence is detected
3445
+
3446
+ library shrink audio --always-split audiobook.m4a
3447
+
3448
+ Use --split-longer-than to _only_ detect silence for files in excess of a specific duration
3449
+
3450
+ library shrink audio --split-longer-than 36mins audiobook.m4b audiobook2.mp3
3451
+
3452
+ Calculate how much space you could save via process-ffmpeg by running something like this:
3453
+
3454
+ numfmt --to=iec (sqlite-utils --no-headers --raw-lines ~/lb/video.db "select sum(size)-sum(duration*100000) from media where time_deleted=0 and video_count>=1 and video_codecs != 'av1' and size/duration > 100000")
3455
+ numfmt --to=iec (sqlite-utils --no-headers --raw-lines ~/lb/audio.db "select sum(size)-sum(duration*18000) from media where time_deleted=0 and video_count=0 and audio_count>=1 and audio_codecs != 'opus' and size/duration > 18000")
3456
+
3457
+
3458
+ </details>
3459
+
3460
+ ###### shrink image
3461
+
3462
+ <details><summary>Shrink images to AV1 image format (.avif)</summary>
3463
+
3464
+ $ library shrink image -h
3465
+ usage: library shrink image PATH ...
3466
+
3467
+ Resize images to max 2400x2400px and format AVIF to save space
3468
+
3469
+ Calculate how much space you could save via process-image by running something like this:
3470
+
3471
+ numfmt --to=iec (sqlite-utils --no-headers --raw-lines image.db "select sum(size)-sum(100000) from media where time_deleted=0 and type like 'image/%' and type != 'image/avif' and size > 100000")
3472
+
3473
+
3474
+ </details>
3475
+
3476
+ ###### shrink text
3477
+
3478
+ <details><summary>Shrink documents to HTML+AV1 image format (requires Calibre)</summary>
3479
+
3480
+ $ library shrink text -h
3481
+ usage: library shrink text PATH ...
3482
+
3483
+ Repackage documents (PDF, ePub, etc) into HTML+AVIF to save space
3484
+
3485
+ Requires Calibre
3486
+
3487
+
3488
+ </details>
3489
+
3490
+ ###### archive extract
3491
+
3492
+ <details><summary>Extract from archives and delete all associated multi-part archive files</summary>
3493
+
3494
+ $ library archive extract -h
3495
+ usage: library archive extract PATH ...
3496
+
3497
+ Extract from archives, including nested archives, and delete all source archives
3498
+
3499
+
3500
+ </details>
3501
+
3502
+ ###### document convert
3503
+
3504
+ <details><summary>Convert images, PDFs, and text-like documents to PDF</summary>
3505
+
3506
+ $ library document convert -h
3507
+ usage: library document convert PATH ... [--ocr] [--open]
3508
+
3509
+ Convert an input document to PDF based on its detected MIME type or extension.
3510
+ Images are assembled into a PDF, PDFs are rasterized and rebuilt, and text-like
3511
+ documents are converted with Pandoc.
3512
+
3513
+ library document convert scan.jpg
3514
+ library document convert notes.md
3515
+ library document convert report.pdf --output-path report-flat.pdf
3516
+
3517
+
3518
+ </details>
3519
+
3520
+ ###### document adjust pdf
3521
+
3522
+ <details><summary>Apply brightness, contrast, saturation, and sharpness adjustments to PDFs</summary>
3523
+
3524
+ $ library document adjust pdf -h
3525
+ usage: library document adjust pdf PATH ...
3526
+
3527
+ Increase brightness
3528
+
3529
+ library document adjust pdf --brightness 120 scan001.pdf --output-path brighter_scan.pdf
3530
+ library document adjust pdf -b 120 scan001.pdf -o brighter_scan.pdf
3531
+
3532
+ Increase or decrease brightness, contrast, color contrast, and sharpness
3533
+
3534
+ library document adjust pdf --brightness 105 --contrast 120 --saturation 80 --sharpness 110 scan001.pdf
3535
+ library document adjust pdf -b 105 -c 120 -C 80 -s 110 scan001.pdf
3536
+
3537
+
3538
+ </details>
3539
+
3540
+ ###### document pdf header
3541
+
3542
+ <details><summary>Stamp rendered Markdown/text onto PDF pages</summary>
3543
+
3544
+ $ library document pdf header -h
3545
+ usage: library document pdf header HEADER_TEXT INPUT.pdf [--output-path OUTPUT.pdf]
3546
+
3547
+ Render HEADER_TEXT with Pandoc and stamp it onto every page of INPUT.pdf with pdftk.
3548
+
3549
+ library document pdf header "Draft - October 2026" input.pdf
3550
+ library document pdf header "Confidential" input.pdf -o stamped.pdf
3551
+
3552
+
3553
+ </details>
3554
+
3555
+ ### Video extraction subcommands
3556
+
3557
+ ###### video extract audio
3558
+
3559
+ <details><summary>Extract audio streams without re-encoding</summary>
3560
+
3561
+ $ library video extract audio -h
3562
+ usage: library video extract audio PATH ... [--output PATH] [--all-audio] [--delete]
3563
+
3564
+ Extract the first audio stream without re-encoding:
3565
+
3566
+ library video extract audio video.mp4
3567
+
3568
+ Extract every audio stream and optionally move the source to the trash after success:
3569
+
3570
+ library video extract audio --all-audio --delete video.mkv
3571
+
3572
+
3573
+ </details>
3574
+
3575
+ ###### video extract subtitles
3576
+
3577
+ <details><summary>Extract embedded subtitle streams</summary>
3578
+
3579
+ $ library video extract subtitles -h
3580
+ usage: library video extract subtitles PATH ... [--stream N] [--format {srt,vtt,ass}] [--stdout]
3581
+
3582
+ Extract an embedded subtitle stream to a sidecar file:
3583
+
3584
+ library video extract subtitles video.mkv
3585
+
3586
+ Print one embedded subtitle stream as SRT:
3587
+
3588
+ library video extract subtitles --stdout --stream 1 video.mkv
3589
+
3590
+
3591
+ </details>
3592
+
3593
+ ###### video extract scenes
3594
+
3595
+ <details><summary>Extract scene changes as video, images, or contact sheets</summary>
3596
+
3597
+ $ library video extract scenes -h
3598
+ usage: library video extract scenes PATH ... [--mode {video,images,contact-sheet}]
3599
+
3600
+ Create a video containing scene changes:
3601
+
3602
+ library video extract scenes video.mp4
3603
+
3604
+ Extract scene-change images or a contact sheet:
3605
+
3606
+ library video extract scenes --images video.mp4
3607
+ library video extract scenes --contact-sheet video.mp4
3608
+
3609
+
3610
+ </details>
3611
+
3612
+ ###### video extract keyframes
3613
+
3614
+ <details><summary>Extract keyframes as video, images, or slow-motion video</summary>
3615
+
3616
+ $ library video extract keyframes -h
3617
+ usage: library video extract keyframes PATH ... [--mode {video,images,slow}]
3618
+
3619
+ Keep only keyframes in a stream-copy output:
3620
+
3621
+ library video extract keyframes video.mp4
3622
+
3623
+ Extract keyframe images or create an eight-times-slower keyframe video:
3624
+
3625
+ library video extract keyframes --images video.mp4
3626
+ library video extract keyframes --slow video.mp4
3627
+
3628
+
3629
+ </details>
3630
+
3631
+ ### Video creation subcommands
3632
+
3633
+ ###### video create slideshow
3634
+
3635
+ <details><summary>Create a slideshow video, optionally with audio</summary>
3636
+
3637
+ $ library video create slideshow -h
3638
+ usage: library video create slideshow [IMAGE ...] [--audio AUDIO] [--audio-duration | --loop-images | --hold-last-image | --loop-audio] [--picture-time SECONDS] [--crossfade-time SECONDS] [--output OUTPUT.mp4]
3639
+
3640
+ Create an H.264 slideshow from images, preserving natural sort order:
3641
+
3642
+ library video create slideshow ./photos/ --output slideshow.mp4
3643
+ library video create slideshow --picture-time 4 --crossfade-time 0.5 *.jpg
3644
+
3645
+ Add one or more audio tracks and choose how the image and audio durations interact.
3646
+ Repeat --audio to concatenate tracks, or pass a directory to include its audio files
3647
+ in natural sort order:
3648
+
3649
+ library video create slideshow --audio music.m4a --audio-duration ./photos/
3650
+ library video create slideshow --audio intro.m4a --audio main.m4a ./photos/
3651
+ library video create slideshow --audio ./music/ ./photos/
3652
+ library video create slideshow --audio music.m4a --loop-images --picture-time 5 ./photos/
3653
+ library video create slideshow --audio music.m4a --hold-last-image --picture-time 5 ./photos/
3654
+ library video create slideshow --audio music.m4a --loop-audio --picture-time 5 ./photos/
3655
+
3656
+ Audio can be filtered, images can be shuffled deterministically, and the output fit
3657
+ or motion effect can be selected:
3658
+
3659
+ library video create slideshow --audio music.m4a --audio-fade-in 2 --audio-fade-out 3 --audio-volume 0.8 ./photos/
3660
+ library video create slideshow --shuffle --seed 42 --fit crop --motion ken-burns ./photos/
3661
+
3662
+ With no images, the audio track gets a black background:
3663
+
3664
+ library video create slideshow song.m4a
3665
+ library video create slideshow --audio song.m4a --width 1920 --height 1080
3666
+
3667
+
3668
+ </details>
3669
+
3670
+ ###### video concat
3671
+
3672
+ <details><summary>Concatenate videos with stream copying</summary>
3673
+
3674
+ $ library video concat -h
3675
+ usage: library video concat OUTPUT INPUT ...
3676
+
3677
+ Concatenate media files with FFmpeg stream copying. Inputs should have compatible
3678
+ streams and codecs:
3679
+
3680
+ library video concat joined.mp4 part-01.mp4 part-02.mp4
3681
+
3682
+
3683
+ </details>
3684
+
3685
+ ### Text processing subcommands
3686
+
3687
+ ###### text border
3688
+
3689
+ <details><summary>Put text inside a simple border</summary>
3690
+
3691
+ $ library text border -h
3692
+ usage: library text border [TEXT ...] [--char CHARACTER] [--padding N]
3693
+
3694
+ Print text inside a simple border. With no TEXT, read from stdin:
3695
+
3696
+ library text border "Important note"
3697
+ printf 'Piped text' | library text border
3698
+
3699
+
3700
+ </details>
3701
+
3702
+ ###### text clean
3703
+
3704
+ <details><summary>Clean pasted text and preserve Markdown structure</summary>
3705
+
3706
+ $ library text clean -h
3707
+ usage: library text clean [INPUT_FILE] [--strip-chars CHARACTERS] [--min-blank-lines N]
3708
+
3709
+ Clean pasted text by removing separator characters, joining wrapped lines,
3710
+ and preserving Markdown list items:
3711
+
3712
+ xclip -o | library text clean
3713
+ library text clean notes.txt --min-blank-lines 2
3714
+
3715
+
3716
+ </details>
3717
+
3718
+ ###### text group
3719
+
3720
+ <details><summary>Group input lines by a regular expression</summary>
3721
+
3722
+ $ library text group -h
3723
+ usage: library text group PATTERN [PATH ...]
3724
+
3725
+ Group input lines by a regular-expression match. Capture groups are joined
3726
+ to form the group key; unmatched lines are placed in a separate group.
3727
+
3728
+
3729
+ </details>
3730
+
3731
+ ###### text interleave
3732
+
3733
+ <details><summary>Interleave lines from multiple inputs</summary>
3734
+
3735
+ $ library text interleave -h
3736
+ usage: library text interleave [PATH ...]
3737
+
3738
+ Interleave lines from multiple files. With no paths, read one stream from stdin.
3739
+ Missing lines from shorter inputs are omitted.
3740
+
3741
+
3742
+ </details>
3743
+
3744
+ ###### text markdown
3745
+
3746
+ <details><summary>Convert simple HTML documents to Markdown</summary>
3747
+
3748
+ $ library text markdown -h
3749
+ usage: library text markdown [PATH ...]
3750
+
3751
+ Convert simple HTML documents to readable Markdown. Read stdin when no
3752
+ paths are given.
3753
+
3754
+
3755
+ </details>
3756
+
3757
+ ###### text convert
3758
+
3759
+ <details><summary>Convert Markdown and text-like documents to PDF</summary>
3760
+
3761
+ $ library text convert -h
3762
+ usage: library text convert PATH ... [--ocr]
3763
+
3764
+ Convert Markdown or text-like files to PDF with Pandoc. OCR is optional and
3765
+ is useful when the conversion pipeline produces a raster-only PDF.
3766
+
3767
+ library text convert notes.md
3768
+ library text convert scanned-text.pdf --ocr
3769
+
3770
+
3771
+ </details>
3772
+
3773
+ ###### text sort regex
3774
+
3775
+ <details><summary>Sort text by regex split and corpus comparison</summary>
3776
+
3777
+ $ library text sort regex -h
3778
+ usage: library text sort regex [input_path | stdin] [output_path | stdout]
3779
+
3780
+ regex-sort is effectively a text-processing pipeline with the following steps:
3781
+
3782
+ line_splitter -- split lines into "words" (--regex)
3783
+ word_sorter -- sort words within each line (--word-sort)
3784
+ line_sorter -- sort lines (--line-sort)
3785
+
3786
+ Examples:
3787
+
3788
+ If your data has a lot of repeating rows it will help to sort by dup count:
3789
+ --line-sort dup,natsort
3790
+
3791
+ You can use any matching regex to produce sorting words:
3792
+ --regex \b\w\w+\b # word boundaries (default)
3793
+ --regex \b\d+\b # digits
3794
+ --regex '.{3}' # char counts
3795
+
3796
+ --regex '.{3}' --line-sort dup,natsort -v
3797
+ (0, ((' Ja',), ('Sva',), ('aye',), ('lba',), ('n M',), ('rd ',))) # Svalbard and Jan Mayen
3798
+ (0, ((' La',), ('Sri',), ('nka',))) # Sri Lanka
3799
+ (0, ((' Ma',), ('San',), ('rin',))) # San Marino
3800
+ (0, ((' Ri',), ('Pue',), ('rto',))) # Puerto Rico
3801
+ (0, (('And',), ('orr',))) # Andorra
3802
+ (0, (('Arm',), ('eni',))) # Armenia
3803
+
3804
+ You can use 'mcda' as a strategy for ranking multiple sort score criteria:
3805
+ --word-sorts '-dup, mcda, count, -len, -lastindex, alpha' \\ # count, -len, -lastindex, alpha are weighted by entropy
3806
+ --line-sorts '-allunique, alpha, mcda, alldup, dupmode, line' # alldup, dupmode, line are weighted by entropy
3807
+
3808
+
3809
+ </details>
3810
+
3811
+ ###### text sort cluster
3812
+
3813
+ <details><summary>Sort text by similarity</summary>
3814
+
3815
+ $ library text sort cluster -h
3816
+ usage: library text sort cluster [input_path | stdin] [output_path | stdout]
3817
+
3818
+ Group lines of text into sorted output
3819
+
3820
+ echo 'red apple
3821
+ broccoli
3822
+ yellow
3823
+ green
3824
+ orange apple
3825
+ red apple' | library text sort cluster
3826
+
3827
+ orange apple
3828
+ red apple
3829
+ red apple
3830
+ broccoli
3831
+ green
3832
+ yellow
3833
+
3834
+ Show the groupings
3835
+
3836
+ echo 'red apple
3837
+ broccoli
3838
+ yellow
3839
+ green
3840
+ orange apple
3841
+ red apple' | library text sort cluster --print-groups
3842
+
3843
+ [
3844
+ {'grouped_paths': ['orange apple', 'red apple', 'red apple']},
3845
+ {'grouped_paths': ['broccoli', 'green', 'yellow']}
3846
+ ]
3847
+
3848
+ Print similar paths
3849
+
3850
+ library filesystem list --db 0day.db -pa --cluster --print-groups
3851
+
3852
+
3853
+
3854
+ </details>
3855
+
3856
+ ###### text extract
3857
+
3858
+ <details><summary>Extract human text from lists of web links</summary>
3859
+
3860
+ $ library text extract -h
3861
+ usage: library text extract PATH ... [--filter-links]
3862
+
3863
+ Sorting suggestions
3864
+
3865
+ library text extract --filter-links --local-html (cb -t text/html | psub) | library text sort cluster --groups | jq -r '.[] | .grouped_paths | "\n" + join("\n")'
3866
+
3867
+ Select text by CSS selector
3868
+
3869
+ library text extract --selenium https://aaronson.org/full-moon-albums/ --select .album-artist,.album-title --sep ' - ' | \
3870
+ library database add links ~/mc/music.db -c p1 --skip-extract -
3871
+
3872
+
3873
+ </details>
3874
+
3875
+ ###### text markdown links
3876
+
3877
+ <details><summary>Extract titles from lists of web links</summary>
3878
+
3879
+ $ library text markdown links -h
3880
+ usage: library text markdown links URL ... [--cookies COOKIES] [--cookies-from-browser BROWSER[+KEYRING][:PROFILE][::CONTAINER]] [--firefox] [--chrome] [--allow-insecure] [--scroll] [--manual] [--auto-pager] [--poke] [--file FILE]
3881
+
3882
+ Convert URLs into Markdown links with page titles filled in
3883
+
3884
+ library text markdown links https://www.youtube.com/watch?v=IgZDDW-NXDE
3885
+ [Work For Peace](https://www.youtube.com/watch?v=IgZDDW-NXDE)
3886
+
3887
+
3888
+ </details>
3889
+
3890
+ ###### text expand links
3891
+
3892
+ <details><summary>Expand search urls with query text</summary>
3893
+
3894
+ $ library text expand links -h
3895
+ usage: library text expand links SEARCH_URLS QUERY ...
3896
+
3897
+ Expand search URLs with multiple queries
3898
+
3899
+ library text expand links -s https://www.google.com/search?q=%s -s https://duckduckgo.com/?q=%s library
3900
+ https://www.google.com/search?q=library
3901
+ https://duckduckgo.com/?q=library
3902
+
3903
+ library text expand links -s https://www.walmart.com/search?q=%s -s https://www.target.com/s?searchTerm=%s "Dr. Pepper" pepcidine
3904
+ https://www.walmart.com/search?q=Dr%20Pepper
3905
+ https://www.target.com/s?searchTerm=Dr%20Pepper
3906
+ https://www.walmart.com/search?q=pepcidine
3907
+ https://www.target.com/s?searchTerm=pepcidine
3908
+
3909
+ Open in browser
3910
+
3911
+ library text expand links ... --browser
3912
+ library text expand links ... --browser chrome
3913
+
3914
+
3915
+
3916
+ </details>
3917
+
3918
+ ###### text combinations
3919
+
3920
+ <details><summary>Enumerate possible combinations</summary>
3921
+
3922
+ $ library text combinations -h
3923
+ usage: library text combinations --PROPERTY OPTION
3924
+
3925
+ Enumerate the possible combinations of things that have multiple properties with more than one options
3926
+
3927
+ library text combinations --prop1 opt1 --prop1 opt2 --prop2 A --prop2 B
3928
+
3929
+ {"prop1": "opt1", "prop2": "A"}
3930
+ {"prop1": "opt1", "prop2": "B"}
3931
+ {"prop1": "opt2", "prop2": "A"}
3932
+ {"prop1": "opt2", "prop2": "B"}
3933
+
3934
+
3935
+ </details>
3936
+
3937
+ ###### text nouns
3938
+
3939
+ <details><summary>Unstructured text -> compound nouns (stdin)</summary>
3940
+
3941
+ $ library text nouns -h
3942
+ usage: library text nouns (stdin)
3943
+
3944
+ Extract compound nouns and phrases from unstructured mixed HTML plain text
3945
+
3946
+ xsv select text hn_comment_202210242109.csv | library text nouns | sort | uniq -c | sort --numeric-sort
3947
+
3948
+
3949
+ </details>
3950
+
3951
+ ###### text timestamps
3952
+
3953
+ <details><summary>Unstructured text -> timestamps</summary>
3954
+
3955
+ $ library text timestamps -h
3956
+ usage: library text timestamps ARGS_OR_STDIN
3957
+
3958
+ Parse timestamps
3959
+ library text timestamps 'October 2017 3pm'
3960
+ 2017-10-01T15:00:00
3961
+
3962
+
3963
+ </details>
3964
+
3965
+ ###### text dates
3966
+
3967
+ <details><summary>Unstructured text -> dates</summary>
3968
+
3969
+ $ library text dates -h
3970
+ usage: library text dates ARGS_OR_STDIN
3971
+
3972
+ Parse dates
3973
+
3974
+ library text dates 'October 2017'
3975
+ 2017-10-01
3976
+
3977
+
3978
+ </details>
3979
+
3980
+ ###### text times
3981
+
3982
+ <details><summary>Unstructured text -> times</summary>
3983
+
3984
+ $ library text times -h
3985
+ usage: library text times ARGS_OR_STDIN
3986
+
3987
+ Parse times
3988
+ library text times 'October 2017 3pm'
3989
+ 15:00:00
3990
+
3991
+
3992
+ </details>
3993
+
3994
+ ###### json rename-keys
3995
+
3996
+ <details><summary>Rename JSON keys by substring match</summary>
3997
+
3998
+ $ library json rename-keys -h
3999
+ usage: library json rename-keys --new-key 'old key substring' (stdin)
4000
+
4001
+ Rename/filter keys in JSON
4002
+
4003
+ echo '{"The Place of Birthings": "Yo Mama", "extra": "key"}' | library json rename-keys --country 'place of birth'
4004
+ {"country": "Yo Mama"}
4005
+
4006
+
4007
+ </details>
4008
+
4009
+ ### JSON processing subcommands
4010
+
4011
+ ###### json remove-nulls
4012
+
4013
+ <details><summary>Remove null values recursively from JSON</summary>
4014
+
4015
+ $ library json remove-nulls -h
4016
+ usage: library json remove-nulls [PATH] [--indent N]
4017
+
4018
+ Remove null values recursively from JSON objects and arrays.
4019
+ --indent is only supported for a single JSON document, not JSONL input.
4020
+
4021
+
4022
+ </details>
4023
+
4024
+ ### Image processing subcommands
4025
+
4026
+ ###### image convert
4027
+
4028
+ <details><summary>Convert images and comic archives into PDFs</summary>
4029
+
4030
+ $ library image convert -h
4031
+ usage: library image convert PATH ...
4032
+
4033
+ Convert groups of images to PDF
4034
+
4035
+ library image convert folder_with_images/
4036
+ library image convert page00*.jpg
4037
+ library image convert page00*.avif # many file formats supported
4038
+
4039
+ Convert comics (CBZ, CBR) to PDF
4040
+
4041
+ library image convert my_ebook.cbr
4042
+ library image convert https://site/my_ebook.cbz
4043
+
4044
+
4045
+ </details>
4046
+
4047
+ ###### image sort cluster
4048
+
4049
+ <details><summary>Sort images by similarity</summary>
4050
+
4051
+ $ library image sort cluster -h
4052
+ usage: library image sort cluster [input_path | stdin] [output_path | stdout]
4053
+
4054
+ Auto-sort images into directories
4055
+
4056
+ echo 'image1.jpg
4057
+ image2.jpg
4058
+ image3.jpg' | library image sort cluster --move-groups
4059
+
4060
+
4061
+
4062
+ </details>
4063
+
4064
+ ### Data import subcommands
4065
+
4066
+ ###### database add tables
4067
+
4068
+ <details><summary>Add table-like data to SQLite</summary>
4069
+
4070
+ $ library database add tables -h
4071
+ usage: library database add tables DATABASE PATH ... [--table STR] [--end-row INT]
4072
+
4073
+ Insert data from one or more files into a SQLite database
4074
+
4075
+
4076
+ </details>
4077
+
4078
+ ###### database add row
4079
+
4080
+ <details><summary>Add arbitrary data to SQLite</summary>
4081
+
4082
+ $ library database add row -h
4083
+ usage: library database add row DATABASE [--table-name TABLE_NAME] --COLUMN-NAME VALUE
4084
+
4085
+ Add a row to sqlite
4086
+
4087
+ library database add row t.db --test_b 1 --test-a 2
4088
+
4089
+ ### media (1 rows)
4090
+ | test_b | test_a |
4091
+ |----------|----------|
4092
+ | 1 | 2 |
4093
+
4094
+
4095
+ </details>
4096
+
4097
+ ###### database import pushshift
4098
+
4099
+ <details><summary>Convert pushshift data to reddit.db format (stdin)</summary>
4100
+
4101
+ $ library database import pushshift -h
4102
+ usage: library database import pushshift DATABASE < stdin
4103
+
4104
+ Download data (about 600GB jsonl.zst; 6TB uncompressed)
4105
+
4106
+ wget -e robots=off -r -k -A zst https://files.pushshift.io/reddit/submissions/
4107
+
4108
+ Load data from files via unzstd
4109
+
4110
+ unzstd --memory=2048MB --stdout RS_2005-07.zst | library database import pushshift pushshift.db
4111
+
4112
+ Or multiple (output is about 1.5TB SQLite fts-searchable)
4113
+
4114
+ for f in psaw/files.pushshift.io/reddit/submissions/*.zst
4115
+ echo "unzstd --memory=2048MB --stdout $f | library database import pushshift (basename $f).db"
4116
+ library database optimize (basename $f).db
4117
+ end | parallel -j5
4118
+
4119
+
4120
+ </details>
4121
+
4122
+ ###### database import nicotine
4123
+
4124
+ <details><summary>Import paths from nicotine+</summary>
4125
+
4126
+ $ library database import nicotine -h
4127
+ usage: library database import nicotine DATABASE PATH ...
4128
+
4129
+ Load records from Nicotine+ File Lists
4130
+
4131
+ library database import nicotine ~/lb/soulseek.db /home/xk/.local/share/nicotine/usershares/*
4132
+
4133
+ By default we track deletions when only one file list is specified
4134
+
4135
+ library database import nicotine ~/lb/soulseek.db /home/xk/.local/share/nicotine/usershares/user1
4136
+ Marking 508387 orphaned metadata records as deleted
4137
+
4138
+ library database import nicotine ~/lb/soulseek.db /home/xk/.local/share/nicotine/usershares/user2
4139
+ Marking 31862 metadata records as undeleted
4140
+ Marking 216495 orphaned metadata records as deleted
4141
+
4142
+ If this is undesirable, pass the `--no-track-deleted` flag
4143
+
4144
+
4145
+ </details>
4146
+
4147
+ ###### database import mpv-watchlater
4148
+
4149
+ <details><summary>Import mpv watchlater files to history</summary>
4150
+
4151
+ $ library database import mpv-watchlater -h
4152
+ usage: library database import mpv-watchlater DATABASE [--watch-later-directory ~/.config/mpv/watch_later/]
4153
+
4154
+ Extract timestamps from MPV to the history table
4155
+
4156
+
4157
+ </details>
4158
+
4159
+ ###### database import places
4160
+
4161
+ <details><summary>Import places of interest (POIs)</summary>
4162
+
4163
+ $ library database import places -h
4164
+ usage: library database import places DATABASE PATH ...
4165
+
4166
+ Load POIs from Google Maps Google Takeout
4167
+
4168
+
4169
+ </details>
4170
+
4171
+ ### Table analysis subcommands
4172
+
4173
+ ###### table convert csv
4174
+
4175
+ <details><summary>Convert JSON data to CSV</summary>
4176
+
4177
+ $ library table convert csv -h
4178
+ usage: library table convert csv [JSON_FILE] [FIELD ...] [--keep-only]
4179
+
4180
+ Convert a JSON list, JSONL stream, or dictionary of lists to CSV:
4181
+
4182
+ cat records.json | library table convert csv
4183
+ library table convert csv records.json id name
4184
+ library table convert csv --keep-only id name < records.json
4185
+
4186
+
4187
+ </details>
4188
+
4189
+ ###### table convert json
4190
+
4191
+ <details><summary>Convert CSV data to JSON</summary>
4192
+
4193
+ $ library table convert json -h
4194
+ usage: library table convert json [CSV_FILE] [--header] [--column-names NAME,...]
4195
+
4196
+ Convert CSV input to a JSON list:
4197
+
4198
+ library table convert json --header records.csv
4199
+ library table convert json --column-names id,name < records.csv
4200
+
4201
+
4202
+ </details>
4203
+
4204
+ ###### table analyze
4205
+
4206
+ <details><summary>Exploratory Data Analysis on table-like files</summary>
4207
+
4208
+ $ library table analyze -h
4209
+ usage: library table analyze PATH ... [--table STR] [--end-row INT] [--repl]
4210
+
4211
+ Perform Exploratory Data Analysis (EDA) on one or more files
4212
+
4213
+ Only 500,000 rows per file are loaded for performance purposes. Set `--end-row inf` to read all the rows and/or run out of RAM.
4214
+
4215
+
4216
+ </details>
4217
+
4218
+ ###### table rank
4219
+
4220
+ <details><summary>Multi-criteria Ranking for Decision Support</summary>
4221
+
4222
+ $ library table rank -h
4223
+ usage: library table rank PATH ... [--table STR] [--end-row INT]
4224
+
4225
+ Perform Multiple Criteria Decision Analysis (MCDA) on one or more files
4226
+
4227
+ Only 500,000 rows per file are loaded for performance purposes. Set `--end-row inf` to read all the rows and/or run out of RAM.
4228
+
4229
+ library table rank ~/storage.csv --minimize price --ignore warranty
4230
+
4231
+ --weights 0.5,0.3,0.2 use explicit criterion weights
4232
+ --weights price=0.7,size=0.3 use named criterion weights
4233
+ --categorical --columns-include quality,size
4234
+ convert word-valued criteria using the standard map
4235
+ --categorical-columns quality --words-nums-map ratings.json
4236
+ use an explicit category mapping file
4237
+ --pca print PCA variance-contribution diagnostics
4238
+ --pairwise add weighted pairwise ranking
4239
+ --comet add deterministic COMET ranking
4240
+ --mcda-method COMET sort by one of TOPSIS, MABAC, BORDA, PAIRWISE, COMET
4241
+
4242
+ ### Goals
4243
+ #### Maximize
4244
+ - size
4245
+ #### Minimize
4246
+ - price
4247
+
4248
+ | | price | size | warranty | TOPSIS | MABAC | SPOTIS | BORDA |
4249
+ |----|---------|--------|------------|----------|------------|----------|---------|
4250
+ | 0 | 359 | 36 | 5 | 0.769153 | 0.348907 | 0.230847 | 7.65109 |
4251
+ | 1 | 453 | 40 | 2 | 0.419921 | 0.0124531 | 0.567301 | 8.00032 |
4252
+ | 2 | 519 | 44 | 2 | 0.230847 | -0.189399 | 0.769153 | 8.1894 |
4253
+
4254
+ It also works with HTTP/GCS/S3 URLs
4255
+
4256
+ library table rank https://en.wikipedia.org/wiki/List_of_Academy_Award-winning_films --clean --minimize Year
4257
+
4258
+ ### Goals
4259
+
4260
+ #### Maximize
4261
+
4262
+ - Nominations
4263
+ - Awards
4264
+
4265
+ #### Minimize
4266
+
4267
+ - Year
4268
+
4269
+ | | Film | Year | Awards | Nominations | TOPSIS | MABAC | SPOTIS | BORDA |
4270
+ |------|-------------------------------------------------------------------------|--------|----------|---------------|-------------|----------|-------------|---------|
4271
+ | 378 | Titanic | 1997 | 11 | 14 | 0.999993 | 1.38014 | 4.85378e-06 | 4116.62 |
4272
+ | 868 | Ben-Hur | 1959 | 11 | 12 | 0.902148 | 1.30871 | 0.0714303 | 4116.72 |
4273
+ | 296 | The Lord of the Rings: The Return of the King | 2003 | 11 | 11 | 0.8558 | 1.27299 | 0.107147 | 4116.76 |
4274
+ | 1341 | West Side Story | 1961 | 10 | 11 | 0.837716 | 1.22754 | 0.152599 | 4116.78 |
4275
+ | 389 | The English Patient | 1996 | 9 | 12 | 0.836725 | 1.2178 | 0.162341 | 4116.78 |
4276
+ | 1007 | Gone with the Wind | 1939 | 8 | 13 | 0.807086 | 1.20806 | 0.172078 | 4116.81 |
4277
+ | 990 | From Here to Eternity | 1953 | 8 | 13 | 0.807086 | 1.20806 | 0.172079 | 4116.81 |
4278
+ | 1167 | On the Waterfront | 1954 | 8 | 12 | 0.785 | 1.17235 | 0.207793 | 4116.83 |
4279
+ | 1145 | My Fair Lady | 1964 | 8 | 12 | 0.785 | 1.17235 | 0.207793 | 4116.83 |
4280
+ | 591 | Gandhi | 1982 | 8 | 11 | 0.755312 | 1.13663 | 0.243509 | 4116.86 |
4281
+
4282
+
4283
+ </details>
4284
+
4285
+ ###### table plot
4286
+
4287
+ <details><summary>Plot table-like files. A CLI interface to matplotlib</summary>
4288
+
4289
+ $ library table plot -h
4290
+ usage: library table plot PATH ... [--table STR] [--end-row INT] -- [PLT.COMMAND ...]
4291
+
4292
+ Plot one or more files
4293
+
4294
+ Only 500,000 rows per file are loaded for performance purposes. Set `--end-row inf` to read all the rows and/or run out of RAM.
4295
+
4296
+ With no commands after `--`, the table is analyzed like `library table analyze` and a set of figures is generated: histograms for numeric columns, bar charts of common values for low/medium-cardinality columns, time-series when a date column is detected, pairwise scatter plots of the most correlated numeric columns, a correlation heatmap, and a missing-values chart.
4297
+
4298
+ Histograms get a fitted PDF overlay, and each numeric column is drawn on a scale matched to its distribution (D'Agostino-Pearson test): normal data on a linear axis, log-normal or heavy right-skewed positive data on a log axis, data spanning orders of magnitude that includes zero/negative values on a symlog axis, and proportions in (0,1) on a logit axis. `--scale log|symlog|logit` forces that scale on all histograms; `--scale off` disables detection and draws plain linear histograms.
4299
+
4300
+ Pass a list of matplotlib.pyplot commands after `--` for manual control. Each command is a plt function name followed by column names (the first column is the x-axis, the rest are y-axes) and optional positional/keyword arguments. `index` refers to the row index. A single column is passed as a single dataset (e.g. for hist).
4301
+
4302
+ Axes are auto-labeled from the column names and units are guessed from common names (e.g. `size` -> bytes, `duration` -> seconds, `time_*` -> dates). A title is auto-added from the plotted columns (e.g. `scatter size duration` -> "Duration vs Size"). Pass an explicit `xlabel=` / `ylabel=` / `title=` to override.
4303
+
4304
+ A transformation pipeline can be applied before plotting: `--where` filters rows, `--classify` derives a `category` column from an expression or binning, and `--groupby` (with `--agg`) groups and aggregates rows. With `--groupby` the auto mode draws one bar chart per aggregated column (e.g. "Count per Category").
4305
+
4306
+ Examples:
4307
+
4308
+ library table plot data.csv
4309
+ library table plot data.csv -- plot x y
4310
+ library table plot data.csv -- scatter x y s=3 alpha=0.5 label=points
4311
+ library table plot data.csv -- hist x bins=50
4312
+ library table plot audio.db --table media --cols size,duration -- scatter size duration
4313
+ library table plot a.csv b.csv --join-tables -- sort -u time_created desc -- plot index time_created
4314
+ library table plot data.csv --where 'duration > 60'
4315
+ library table plot data.csv --classify 'size > 1GB' --groupby category
4316
+ library table plot data.csv --classify duration --bins 5 --groupby category --agg mean
4317
+ library table plot data.csv --groupby category --agg count --top 10
4318
+
4319
+
4320
+ </details>
4321
+
4322
+ ###### table markdown
4323
+
4324
+ <details><summary>Print markdown tables from table-like files</summary>
4325
+
4326
+ $ library table markdown -h
4327
+ usage: library table markdown PATH ... [--table STR] [--end-row INT] [--transpose] [--filetype]
4328
+
4329
+ Print tables from files as markdown
4330
+
4331
+ Only 500,000 rows per file are loaded for performance purposes. Set `--end-row inf` to read all the rows and/or run out of RAM.
4332
+
4333
+ Examples:
4334
+
4335
+ library database search cities.db places chic | library table markdown --from-json --transpose
4336
+
4337
+ ## stdin:0
4338
+
4339
+ | 0 | 1 | 2 | 3 | 4 | 5 |
4340
+ |-------------------------|------------|--------------------------|--------------------------|--------------------------|------------|
4341
+ | title | Chiclayo | Chicago | Chico | Apalachicola | Chicoutimi |
4342
+ | state | Lambayeque | Illinois | California | Florida | Québec |
4343
+ | country | Peru | United States of America | United States of America | United States of America | Canada |
4344
+ | u | PE | US | US | US | CA |
4345
+ | latitude | -6.7629 | 41.8319 | 39.7286 | 29.7256 | 48.4333 |
4346
+ | longitude | -79.8366 | -87.752 | -121.8364 | -84.9925 | -71.0667 |
4347
+ | size | 22184 | 19571 | 40437 | 35467 | 28951 |
4348
+ | interesting_avg | 141 | 1129 | 156 | 119 | 175 |
4349
+ | interesting_spread | 1754 | 26919 | 2025 | 1297 | 3196 |
4350
+ | boring_avg | 349 | 4332 | 136 | 382 | 628 |
4351
+ | boring_spread | 4186 | 24220 | 2000 | 2432 | 3920 |
4352
+ | pop_avg | 7530 | 12906 | 1350 | 378 | 3038 |
4353
+ | pop_spread | 80471 | 80825 | 24196 | 1634 | 15249 |
4354
+ | food_avg | 28 | 18 | 8 | 7 | 8 |
4355
+ | food_spread | 153 | 417 | 51 | 27 | 26 |
4356
+ | urban_avg | 60 | 38 | 11 | 2 | 22 |
4357
+ | urban_spread | 80 | 99 | 45 | 2 | 71 |
4358
+ | forest_avg | 11 | 55 | 82 | 77 | 72 |
4359
+ | forest_spread | 27 | 155 | 264 | 186 | 203 |
4360
+ | aqi_avg | 9 | 11 | 5 | 7 | 2 |
4361
+ | aqi_max | 23 | 14 | 11 | 9 | 6 |
4362
+ | coastline_avg | 0 | 0 | 0 | 23 | 26 |
4363
+ | slope_avg | 1542 | 420 | 3532 | 248 | 2071 |
4364
+ | public_transport_avg | 8 | 34 | 1 | 1 | 2 |
4365
+ | public_transport_spread | 24 | 303 | 9 | 1 | 7 |
4366
+ | hotel_count | 56 | 259 | 75 | 2561 | |
4367
+ | airport_code | CIX,LOH | CMI,MDW,MKG | RDD,SMF | ECP,TLH | YBG |
4368
+ | wifi | 37505 | 177564 | 171754 | 195347 | 150669 |
4369
+
4370
+
4371
+ </details>
4372
+
4373
+ ###### table columns
4374
+
4375
+ <details><summary>Print columns of table-like files</summary>
4376
+
4377
+ $ library table columns -h
4378
+ usage: library table columns PATH ... [--table STR] [--start-row INT]
4379
+
4380
+ Print columns from table-like files
4381
+
4382
+ Only print column names
4383
+
4384
+ library table columns https://en.wikipedia.org/wiki/List_of_Academy_Award-winning_films --cols name --table-index 0
4385
+ Film
4386
+ Year
4387
+ Awards
4388
+ Nominations
4389
+
4390
+
4391
+ </details>
4392
+
4393
+ ###### table diff
4394
+
4395
+ <details><summary>Diff large table-like files in chunks</summary>
4396
+
4397
+ $ library table diff -h
4398
+ usage: library table diff PATH1 PATH2 [--join-keys JOIN_KEYS] [--table1 TABLE1] [--table2 TABLE2] [--table1-index TABLE1_INDEX] [--table2-index TABLE2_INDEX] [--start-row START_ROW] [--batch-size BATCH_SIZE]
4399
+
4400
+ See data differences in an incremental way to quickly see how two different files differ.
4401
+
4402
+ Data (PATH1, PATH2) can be two different files of different file formats (CSV, JSONL, JSON, Excel) or it could even be the same file with different tables.
4403
+
4404
+ Use `--join-keys id,name` (or `--id-columns id,name`) to compare rows by ID instead of by their position in each batch. This handles rows reordered between files and streams CSV, JSONL, and JSON arrays in bounded-size batches.
4405
+ Each ID must be present and unique within each file. Missing ID columns, null ID values, and duplicate IDs are errors.
4406
+ ID-driven comparison stores only the streamed rows on disk and keeps the active pandas batches bounded by `--batch-size`.
4407
+ JSON streaming expects an array of row objects (for example, `[{"id": 1, "value": "a"}]); use JSONL for one object per line.
4408
+ If you are comparing SQLite files you may be able to use `--sort id,name` to achieve the same effect.
4409
+
4410
+ To diff everything at once run with `--batch-size inf`
4411
+
4412
+
4413
+ </details>
4414
+
4415
+ ### Torrent subcommands
4416
+
4417
+ ###### database add torrents
4418
+
4419
+ <details><summary>Add torrent info to SQLite</summary>
4420
+
4421
+ $ library database add torrents -h
4422
+ usage: library database add torrents DATABASE PATH ...
4423
+
4424
+ Create a SQLite database of torrent file data (torrents are playlists, referenced files are media)
4425
+
4426
+ library database add torrents torrents.db ~/.local/data/qbittorrent/queue/
4427
+
4428
+ View total size of undownloaded torrents
4429
+
4430
+ library database playlists torrents.db -pa
4431
+ path deleted_count size playlists_count media_count
4432
+ ---------------------- --------------- -------- ----------------- -------------
4433
+ Aggregate of playlists 0 87.7 TiB 530 272681
4434
+
4435
+
4436
+ </details>
4437
+
4438
+ ###### database add computers
4439
+
4440
+ <details><summary>Add computer info to SQLite</summary>
4441
+
4442
+ $ library database add computers -h
4443
+ usage: library database add computers DATABASE HOSTNAME ...
4444
+
4445
+ Create a SQLite database of SSH-able computers and their disks (nodes are playlists, node disks are media)
4446
+
4447
+ library database add computers local.db blan gworky nocap zendl gak pakon
4448
+ library database add computers remote.db jp.tensile-fortress.ts.net hk kr mx uk ca
4449
+
4450
+
4451
+ </details>
4452
+
4453
+ ###### torrent allocate
4454
+
4455
+ <details><summary>Use computers.db and torrents.db to allocate torrents</summary>
4456
+
4457
+ $ library torrent allocate -h
4458
+ usage: library torrent allocate
4459
+
4460
+ Use a Computer DB and a Torrent DB to allocate and deplete global free space
4461
+
4462
+ library torrent allocate computers.db torrents.db -v
4463
+
4464
+ Filter to specific words or tracker
4465
+
4466
+ library torrent allocate computers.db torrents.db -s specific words or tracker
4467
+
4468
+
4469
+ </details>
4470
+
4471
+ ###### torrent info
4472
+
4473
+ <details><summary>List torrents (qBittorrent-nox)</summary>
4474
+
4475
+ $ library torrent info -h
4476
+ usage: library torrent info
4477
+
4478
+ List torrents in qBittorrent-nox
4479
+
4480
+ library torrent info
4481
+
4482
+ Search for specific torrent
4483
+
4484
+ library torrent info query
4485
+
4486
+ Positional torrent searches use the path aligned with torrent status by default.
4487
+ Search a specific path field or both fields with --path-search:
4488
+
4489
+ library torrent info --dl --path-search save /mnt/d4/
4490
+ library torrent info --dl --path-search both /mnt/d4/
4491
+
4492
+ Find incomplete torrents whose temp and final paths use different drives.
4493
+ --different-drives searches both path fields unless --path-search is specified:
4494
+
4495
+ library torrent info --dl --different-drives
4496
+ library torrent info --dl --different-drives /mnt/d4/
4497
+
4498
+ Find incomplete torrents whose temp and final paths use the same drive:
4499
+
4500
+ library torrent info --dl --same-drives
4501
+
4502
+ Aggregate torrent info (torrents-status)
4503
+
4504
+ library torrent info -pa
4505
+
4506
+ Aggregate tracker errors
4507
+
4508
+ library torrent info --errored --trackers
4509
+
4510
+ Search for specific file
4511
+
4512
+ library torrent info query -v --file-search query2
4513
+
4514
+ Force-start incomplete downloads
4515
+
4516
+ library torrent info --dl --progress=+10% --force-start
4517
+
4518
+ Find torrents with a recorded completion timestamp
4519
+
4520
+ library torrent info --completed
4521
+
4522
+ Stop completed downloads
4523
+
4524
+ library torrent info -S+5MiB --seeders=+5 --time-stalled=+45days --time-active=+180days --stop
4525
+
4526
+ Stop incomplete downloads
4527
+
4528
+ library torrent info --time-unseeded=+90days --time-active=+60days --time-stalled=+30days --stop
4529
+ library torrent info --time-active=+45days --inactive --progress=0 --stop
4530
+
4531
+ Move files
4532
+
4533
+ Use --move OR any combination of --temp-drive, --temp-path, --download-drive, --download-path
4534
+
4535
+ library torrent info --temp-drive /mnt/d/
4536
+
4537
+ Unset default prefixes
4538
+
4539
+ library torrent info --no-tracker-dirname --temp-prefix '' --download-prefix '' --move /home/xk/
4540
+ library torrent info --no-tracker-dirname --temp-prefix seeding --move /home/xk/
4541
+
4542
+ When --mark-deleted is provided, the torrents are tagged with 'delete' in qBittorrent
4543
+ When --delete-rows is provided, the metadata is removed from qBittorrent
4544
+ When --delete-files is provided, the downloaded files are deleted
4545
+ When --delete-incomplete 80% is provided, any files that were downloaded less than 80% are deleted
4546
+ When --reset-progress is provided, the downloaded files are deleted and progress is reset without removing the torrents
4547
+
4548
+
4549
+ </details>
4550
+
4551
+ ###### torrent start
4552
+
4553
+ <details><summary>Start torrents (qBittorrent-nox)</summary>
4554
+
4555
+ $ library torrent start -h
4556
+ usage: library torrent start [--prefix /mnt/d/] [--scan] PATH ...
4557
+
4558
+ Start torrent files in qBittorrent-nox
4559
+
4560
+ By default the temp/download paths are derived from the configured drives and
4561
+ prefixes (and optional tracker subdirectories). With --scan, the temp and download
4562
+ drive prefixes are searched to locate each torrent's files on disk. If
4563
+ --tracker-dirnames is also set, the tracker subdirectories of each prefix are
4564
+ searched as well. Files found under the temp/downloading prefix are used as the
4565
+ download_path (temp), and files under the download/seeding prefix as the save_path,
4566
+ so qBittorrent hash-scans and moves any incomplete data into place. Torrents with
4567
+ no file matches are logged, skipped, and their .torrent files are left in place.
4568
+ --scan overrides the --tracker-dirnames layout when choosing save paths.
4569
+
4570
+
4571
+ </details>
4572
+
4573
+ ###### torrent remaining
4574
+
4575
+ <details><summary>Overview of torrents by mountpoint (qBittorrent-nox)</summary>
4576
+
4577
+ $ library torrent remaining -h
4578
+ usage: library torrent remaining
4579
+
4580
+ Print remaining stats grouped by mountpoint or folder depth
4581
+
4582
+ library torrent remaining --depth=2 --dl --time-stalled=-5days
4583
+
4584
+
4585
+ </details>
4586
+
4587
+ ###### torrent dump
4588
+
4589
+ <details><summary>Print torrent file info</summary>
4590
+
4591
+ $ library torrent dump -h
4592
+ usage: library torrent dump [--trackers] PATH ...
4593
+
4594
+ View torrent file metadata via libtorrent
4595
+
4596
+ Aggregate torrent metadata by tracker domain:
4597
+
4598
+ library torrent dump --trackers PATH ...
4599
+
4600
+
4601
+ </details>
4602
+
4603
+ ###### torrent match
4604
+
4605
+ <details><summary>Match files on disk against torrent piece hashes</summary>
4606
+
4607
+ $ library torrent match -h
4608
+ usage: library torrent match TORRENT_DIRECTORY SEARCH_DIRECTORY ...
4609
+
4610
+ Match files on disk against torrent piece hashes. Files are indexed by size
4611
+ first, then checked against pieces that are wholly contained in each torrent
4612
+ file. Partial boundary pieces are reported as unchecked.
4613
+
4614
+ library torrent match ~/.local/data/qbittorrent/queue/ /mnt/d/library/
4615
+
4616
+ Use --min-pieces to require more than one matching piece.
4617
+
4618
+
4619
+ </details>
4620
+
4621
+ ###### torrent audit
4622
+
4623
+ <details><summary>Find qBittorrent duplicate and orphan files</summary>
4624
+
4625
+ $ library torrent audit -h
4626
+ usage: library torrent audit [--exclude PATH ...]
4627
+
4628
+ Audit qBittorrent's referenced files against the filesystem. Report files
4629
+ referenced by multiple torrents and files under torrent roots that are not
4630
+ referenced by any torrent.
4631
+
4632
+ library torrent audit
4633
+ library torrent audit --exclude /mnt/archive
4634
+
4635
+
4636
+ </details>
4637
+
4638
+ ### Database updating subcommands
4639
+
4640
+ ###### database update filesystem
4641
+
4642
+ <details><summary>Update local media</summary>
4643
+
4644
+ $ library database update filesystem -h
4645
+ usage: library database update filesystem DATABASE
4646
+
4647
+ Update each path previously saved
4648
+
4649
+ library database update filesystem video.db
4650
+
4651
+
4652
+ </details>
4653
+
4654
+ ###### database update video
4655
+
4656
+ <details><summary>Update online video media</summary>
4657
+
4658
+ $ library database update video -h
4659
+ usage: library database update video [--audio | --video] DATABASE
4660
+
4661
+ Fetch the latest videos for every playlist saved in your database
4662
+
4663
+ library database update video educational.db
4664
+
4665
+ Fetch extra metadata
4666
+
4667
+ By default tubeupdate will quickly add media.
4668
+ You can run with --extra to fetch more details: (best resolution width, height, subtitle tags, etc)
4669
+
4670
+ library database update video educational.db --extra https://www.youtube.com/channel/UCBsEUcR-ezAuxB2WlfeENvA/videos
4671
+
4672
+ Remove duplicate playlists
4673
+
4674
+ library database dedupe video.db playlists --bk extractor_playlist_id
4675
+
4676
+
4677
+ </details>
4678
+
4679
+ ###### database update open-directory
4680
+
4681
+ <details><summary>Update open-directory media</summary>
4682
+
4683
+ $ library database update open-directory -h
4684
+ usage: library database update open-directory DATABASE
4685
+
4686
+ Update saved open directories
4687
+
4688
+
4689
+ </details>
4690
+
4691
+ ###### database update gallery
4692
+
4693
+ <details><summary>Update online gallery media</summary>
4694
+
4695
+ $ library database update gallery -h
4696
+ usage: library database update gallery DATABASE
4697
+
4698
+ Check previously saved gallery_dl URLs for new content
4699
+
4700
+
4701
+ </details>
4702
+
4703
+ ###### database update reddit
4704
+
4705
+ <details><summary>Update reddit media</summary>
4706
+
4707
+ $ library database update reddit -h
4708
+ usage: library database update reddit [--audio | --video] [--lookback N_DAYS] [--praw-site bot1] DATABASE
4709
+
4710
+ Fetch the latest posts for every subreddit/redditor saved in your database
4711
+
4712
+ library database update reddit edu_subreddits.db
4713
+
4714
+
4715
+ </details>
4716
+
4717
+ ###### database update links
4718
+
4719
+ <details><summary>Update a link-scraping database</summary>
4720
+
4721
+ $ library database update links -h
4722
+ usage: library database update links DATABASE
4723
+
4724
+ Fetch new links from each path previously saved
4725
+
4726
+ library database update links links.db
4727
+
4728
+
4729
+ </details>
4730
+
4731
+ ### Help subcommands
4732
+
4733
+ ###### help search
4734
+
4735
+ <details><summary>Search subcommands and help text</summary>
4736
+
4737
+ $ library help search -h
4738
+ usage: library help search [subcommand] QUERY ...
4739
+
4740
+ Search library functionality and help text
4741
+
4742
+ </details>
4743
+
4744
+
4745
+ <details><summary>Chicken mode</summary>
4746
+
4747
+
4748
+ ////////////////////////
4749
+ ////////////////////////|
4750
+ //////////////////////// |
4751
+ ////////////////////////| |
4752
+ | _\/_ | _\/_ | |
4753
+ | )o(> | <)o( | |
4754
+ | _/ <\ | /> \_ | | just kidding :-)
4755
+ | (_____) | (_____) | |_
4756
+ | ~~~oOo~~~ | ~~~0oO~~~ |/__|
4757
+ _|====\_=====|=====_/====|_ ||
4758
+ |_|\_________ O _________/|_|||
4759
+ ||//////////|_|\\\\\\\\\\|| ||
4760
+ || || |\_\\ || ||
4761
+ ||/|| \\_\\ ||/||
4762
+ ||/|| \)_\) ||/||
4763
+ || || \ O / || ||
4764
+ || \ / || LGB
4765
+
4766
+ \________/======
4767
+ / ( || ) \\
4768
+
4769
+
4770
+ TypeError: Retry.__init__() got an unexpected keyword argument 'backoff_jitter'
4771
+ pip install --upgrade --ignore-installed urllib3 requests
4772
+
4773
+ </details>
4774
+
4775
+ You can expand all by running this in your browser console:
4776
+
4777
+ ```js
4778
+ (() => { const readmeDiv = document.querySelector("article"); const detailsElements = readmeDiv.getElementsByTagName("details"); for (let i = 0; i < detailsElements.length; i++) { detailsElements[i].setAttribute("open", "true"); } })();
4779
+ ```
4780
+
4781
+ Be sure to check out [https://www.unli.xyz/diskprices/](https://www.unli.xyz/diskprices/) for all your storage needs
4782
+
4783
+