library 3.2.6__tar.gz → 3.2.11__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 (163) hide show
  1. {library-3.2.6 → library-3.2.11}/.github/README.md +88 -5
  2. {library-3.2.6 → library-3.2.11}/PKG-INFO +140 -55
  3. {library-3.2.6 → library-3.2.11}/library/__main__.py +1 -2
  4. library-3.2.11/library/_version.py +25 -0
  5. {library-3.2.6 → library-3.2.11}/library/createdb/gallery_backend.py +37 -35
  6. {library-3.2.6 → library-3.2.11}/library/createdb/links_add.py +11 -2
  7. {library-3.2.6 → library-3.2.11}/library/mediadb/download.py +11 -3
  8. {library-3.2.6 → library-3.2.11}/library/mediafiles/images_to_pdf.py +1 -1
  9. {library-3.2.6 → library-3.2.11}/library/mediafiles/process_image.py +1 -1
  10. library-3.2.11/library/mediafiles/torrents_dump.py +89 -0
  11. {library-3.2.6 → library-3.2.11}/library/mediafiles/unardel.py +39 -9
  12. {library-3.2.6 → library-3.2.11}/library/playback/play_actions.py +2 -1
  13. {library-3.2.6 → library-3.2.11}/library/playback/playback_control.py +2 -2
  14. {library-3.2.6 → library-3.2.11}/library/playback/torrents_info.py +40 -3
  15. {library-3.2.6 → library-3.2.11}/library/readme.py +28 -2
  16. library-3.2.11/library/text/extract_links.py +422 -0
  17. {library-3.2.6 → library-3.2.11}/library/usage.py +59 -2
  18. {library-3.2.6 → library-3.2.11}/library/utils/arggroups.py +101 -14
  19. {library-3.2.6 → library-3.2.11}/library/utils/consts.py +24 -0
  20. {library-3.2.6 → library-3.2.11}/library/utils/devices.py +12 -0
  21. {library-3.2.6 → library-3.2.11}/library/utils/printing.py +3 -6
  22. {library-3.2.6 → library-3.2.11}/library/utils/processes.py +9 -6
  23. {library-3.2.6 → library-3.2.11}/library/utils/shell_utils.py +12 -4
  24. {library-3.2.6 → library-3.2.11}/library/utils/strings.py +10 -6
  25. {library-3.2.6 → library-3.2.11}/library/utils/web.py +279 -11
  26. {library-3.2.6 → library-3.2.11}/pyproject.toml +23 -37
  27. library-3.2.11/pyproject.toml.orig +207 -0
  28. library-3.2.6/library/mediafiles/torrents_dump.py +0 -67
  29. library-3.2.6/library/text/extract_links.py +0 -274
  30. {library-3.2.6 → library-3.2.11}/LICENSE +0 -0
  31. {library-3.2.6 → library-3.2.11}/library/__init__.py +0 -0
  32. {library-3.2.6 → library-3.2.11}/library/assets/__init__.py +0 -0
  33. {library-3.2.6 → library-3.2.11}/library/assets/calibre.css +0 -0
  34. {library-3.2.6 → library-3.2.11}/library/assets/kotobago.png +0 -0
  35. {library-3.2.6 → library-3.2.11}/library/createdb/__init__.py +0 -0
  36. {library-3.2.6 → library-3.2.11}/library/createdb/av.py +0 -0
  37. {library-3.2.6 → library-3.2.11}/library/createdb/computer_info.py +0 -0
  38. {library-3.2.6 → library-3.2.11}/library/createdb/computers_add.py +0 -0
  39. {library-3.2.6 → library-3.2.11}/library/createdb/fs_add.py +0 -0
  40. {library-3.2.6 → library-3.2.11}/library/createdb/fs_add_metadata.py +0 -0
  41. {library-3.2.6 → library-3.2.11}/library/createdb/gallery_add.py +0 -0
  42. {library-3.2.6 → library-3.2.11}/library/createdb/getty_add.py +0 -0
  43. {library-3.2.6 → library-3.2.11}/library/createdb/hn_add.py +0 -0
  44. {library-3.2.6 → library-3.2.11}/library/createdb/nicotine_import.py +0 -0
  45. {library-3.2.6 → library-3.2.11}/library/createdb/places_import.py +0 -0
  46. {library-3.2.6 → library-3.2.11}/library/createdb/reddit_add.py +0 -0
  47. {library-3.2.6 → library-3.2.11}/library/createdb/row_add.py +0 -0
  48. {library-3.2.6 → library-3.2.11}/library/createdb/site_add.py +0 -0
  49. {library-3.2.6 → library-3.2.11}/library/createdb/substack.py +0 -0
  50. {library-3.2.6 → library-3.2.11}/library/createdb/subtitle.py +0 -0
  51. {library-3.2.6 → library-3.2.11}/library/createdb/tables_add.py +0 -0
  52. {library-3.2.6 → library-3.2.11}/library/createdb/tabs_add.py +0 -0
  53. {library-3.2.6 → library-3.2.11}/library/createdb/tildes.py +0 -0
  54. {library-3.2.6 → library-3.2.11}/library/createdb/torrents_add.py +0 -0
  55. {library-3.2.6 → library-3.2.11}/library/createdb/tube_add.py +0 -0
  56. {library-3.2.6 → library-3.2.11}/library/createdb/tube_backend.py +0 -0
  57. {library-3.2.6 → library-3.2.11}/library/createdb/web_add.py +0 -0
  58. {library-3.2.6 → library-3.2.11}/library/data/__init__.py +0 -0
  59. {library-3.2.6 → library-3.2.11}/library/data/dictionary.pkl +0 -0
  60. {library-3.2.6 → library-3.2.11}/library/data/ffmpeg_errors.py +0 -0
  61. {library-3.2.6 → library-3.2.11}/library/data/http_errors.py +0 -0
  62. {library-3.2.6 → library-3.2.11}/library/data/imagemagick_errors.py +0 -0
  63. {library-3.2.6 → library-3.2.11}/library/data/unar_errors.py +0 -0
  64. {library-3.2.6 → library-3.2.11}/library/data/wordbank.py +0 -0
  65. {library-3.2.6 → library-3.2.11}/library/data/yt_dlp_errors.py +0 -0
  66. {library-3.2.6 → library-3.2.11}/library/editdb/__init__.py +0 -0
  67. {library-3.2.6 → library-3.2.11}/library/editdb/dedupe_db.py +0 -0
  68. {library-3.2.6 → library-3.2.11}/library/editdb/dedupe_media.py +0 -0
  69. {library-3.2.6 → library-3.2.11}/library/editdb/merge_online_local.py +0 -0
  70. {library-3.2.6 → library-3.2.11}/library/editdb/mpv_watchlater.py +0 -0
  71. {library-3.2.6 → library-3.2.11}/library/editdb/pushshift.py +0 -0
  72. {library-3.2.6 → library-3.2.11}/library/editdb/reddit_selftext.py +0 -0
  73. {library-3.2.6 → library-3.2.11}/library/files/__init__.py +0 -0
  74. {library-3.2.6 → library-3.2.11}/library/files/christen.py +0 -0
  75. {library-3.2.6 → library-3.2.11}/library/files/sample_compare.py +0 -0
  76. {library-3.2.6 → library-3.2.11}/library/files/sample_hash.py +0 -0
  77. {library-3.2.6 → library-3.2.11}/library/files/similar_files.py +0 -0
  78. {library-3.2.6 → library-3.2.11}/library/folders/__init__.py +0 -0
  79. {library-3.2.6 → library-3.2.11}/library/folders/big_dirs.py +0 -0
  80. {library-3.2.6 → library-3.2.11}/library/folders/filter_src.py +0 -0
  81. {library-3.2.6 → library-3.2.11}/library/folders/merge_mv.py +0 -0
  82. {library-3.2.6 → library-3.2.11}/library/folders/mergerfs_cp.py +0 -0
  83. {library-3.2.6 → library-3.2.11}/library/folders/mount_stats.py +0 -0
  84. {library-3.2.6 → library-3.2.11}/library/folders/move_list.py +0 -0
  85. {library-3.2.6 → library-3.2.11}/library/folders/scatter.py +0 -0
  86. {library-3.2.6 → library-3.2.11}/library/folders/similar_folders.py +0 -0
  87. {library-3.2.6 → library-3.2.11}/library/fsdb/__init__.py +0 -0
  88. {library-3.2.6 → library-3.2.11}/library/fsdb/disk_usage.py +0 -0
  89. {library-3.2.6 → library-3.2.11}/library/fsdb/filesystem.py +0 -0
  90. {library-3.2.6 → library-3.2.11}/library/fsdb/folder_stats.py +0 -0
  91. {library-3.2.6 → library-3.2.11}/library/fsdb/search_db.py +0 -0
  92. {library-3.2.6 → library-3.2.11}/library/lb.py +0 -0
  93. {library-3.2.6 → library-3.2.11}/library/mediadb/__init__.py +0 -0
  94. {library-3.2.6 → library-3.2.11}/library/mediadb/block.py +0 -0
  95. {library-3.2.6 → library-3.2.11}/library/mediadb/db_history.py +0 -0
  96. {library-3.2.6 → library-3.2.11}/library/mediadb/db_media.py +0 -0
  97. {library-3.2.6 → library-3.2.11}/library/mediadb/db_playlists.py +0 -0
  98. {library-3.2.6 → library-3.2.11}/library/mediadb/download_status.py +0 -0
  99. {library-3.2.6 → library-3.2.11}/library/mediadb/history.py +0 -0
  100. {library-3.2.6 → library-3.2.11}/library/mediadb/history_add.py +0 -0
  101. {library-3.2.6 → library-3.2.11}/library/mediadb/optimize_db.py +0 -0
  102. {library-3.2.6 → library-3.2.11}/library/mediadb/playlists.py +0 -0
  103. {library-3.2.6 → library-3.2.11}/library/mediadb/redownload.py +0 -0
  104. {library-3.2.6 → library-3.2.11}/library/mediadb/search.py +0 -0
  105. {library-3.2.6 → library-3.2.11}/library/mediadb/stats.py +0 -0
  106. {library-3.2.6 → library-3.2.11}/library/mediafiles/__init__.py +0 -0
  107. {library-3.2.6 → library-3.2.11}/library/mediafiles/media_check.py +0 -0
  108. {library-3.2.6 → library-3.2.11}/library/mediafiles/pdf_edit.py +0 -0
  109. {library-3.2.6 → library-3.2.11}/library/mediafiles/process_ffmpeg.py +0 -0
  110. {library-3.2.6 → library-3.2.11}/library/mediafiles/process_media.py +0 -0
  111. {library-3.2.6 → library-3.2.11}/library/mediafiles/process_text.py +0 -0
  112. {library-3.2.6 → library-3.2.11}/library/mediafiles/torrents_start.py +0 -0
  113. {library-3.2.6 → library-3.2.11}/library/misc/__init__.py +0 -0
  114. {library-3.2.6 → library-3.2.11}/library/misc/dedupe_czkawka.py +0 -0
  115. {library-3.2.6 → library-3.2.11}/library/misc/export_text.py +0 -0
  116. {library-3.2.6 → library-3.2.11}/library/misc/search_help.py +0 -0
  117. {library-3.2.6 → library-3.2.11}/library/multidb/__init__.py +0 -0
  118. {library-3.2.6 → library-3.2.11}/library/multidb/allocate_torrents.py +0 -0
  119. {library-3.2.6 → library-3.2.11}/library/multidb/copy_play_counts.py +0 -0
  120. {library-3.2.6 → library-3.2.11}/library/multidb/merge_dbs.py +0 -0
  121. {library-3.2.6 → library-3.2.11}/library/playback/__init__.py +0 -0
  122. {library-3.2.6 → library-3.2.11}/library/playback/links_open.py +0 -0
  123. {library-3.2.6 → library-3.2.11}/library/playback/media_player.py +0 -0
  124. {library-3.2.6 → library-3.2.11}/library/playback/media_printer.py +0 -0
  125. {library-3.2.6 → library-3.2.11}/library/playback/post_actions.py +0 -0
  126. {library-3.2.6 → library-3.2.11}/library/playback/surf.py +0 -0
  127. {library-3.2.6 → library-3.2.11}/library/playback/tabs_open.py +0 -0
  128. {library-3.2.6 → library-3.2.11}/library/playback/torrents_remaining.py +0 -0
  129. {library-3.2.6 → library-3.2.11}/library/tablefiles/__init__.py +0 -0
  130. {library-3.2.6 → library-3.2.11}/library/tablefiles/columns.py +0 -0
  131. {library-3.2.6 → library-3.2.11}/library/tablefiles/eda.py +0 -0
  132. {library-3.2.6 → library-3.2.11}/library/tablefiles/incremental_diff.py +0 -0
  133. {library-3.2.6 → library-3.2.11}/library/tablefiles/markdown_tables.py +0 -0
  134. {library-3.2.6 → library-3.2.11}/library/tablefiles/mcda.py +0 -0
  135. {library-3.2.6 → library-3.2.11}/library/tablefiles/plot.py +0 -0
  136. {library-3.2.6 → library-3.2.11}/library/text/__init__.py +0 -0
  137. {library-3.2.6 → library-3.2.11}/library/text/cluster_sort.py +0 -0
  138. {library-3.2.6 → library-3.2.11}/library/text/combinations.py +0 -0
  139. {library-3.2.6 → library-3.2.11}/library/text/expand_links.py +0 -0
  140. {library-3.2.6 → library-3.2.11}/library/text/extract_text.py +0 -0
  141. {library-3.2.6 → library-3.2.11}/library/text/json_keys_rename.py +0 -0
  142. {library-3.2.6 → library-3.2.11}/library/text/markdown_links.py +0 -0
  143. {library-3.2.6 → library-3.2.11}/library/text/nouns.py +0 -0
  144. {library-3.2.6 → library-3.2.11}/library/text/regex_sort.py +0 -0
  145. {library-3.2.6 → library-3.2.11}/library/text/timestamps.py +0 -0
  146. {library-3.2.6 → library-3.2.11}/library/utils/__init__.py +0 -0
  147. {library-3.2.6 → library-3.2.11}/library/utils/arg_utils.py +0 -0
  148. {library-3.2.6 → library-3.2.11}/library/utils/argparse_utils.py +0 -0
  149. {library-3.2.6 → library-3.2.11}/library/utils/date_utils.py +0 -0
  150. {library-3.2.6 → library-3.2.11}/library/utils/db_utils.py +0 -0
  151. {library-3.2.6 → library-3.2.11}/library/utils/file_utils.py +0 -0
  152. {library-3.2.6 → library-3.2.11}/library/utils/filter_engine.py +0 -0
  153. {library-3.2.6 → library-3.2.11}/library/utils/gui.py +0 -0
  154. {library-3.2.6 → library-3.2.11}/library/utils/iterables.py +0 -0
  155. {library-3.2.6 → library-3.2.11}/library/utils/log_utils.py +0 -0
  156. {library-3.2.6 → library-3.2.11}/library/utils/mpv_utils.py +0 -0
  157. {library-3.2.6 → library-3.2.11}/library/utils/nums.py +0 -0
  158. {library-3.2.6 → library-3.2.11}/library/utils/objects.py +0 -0
  159. {library-3.2.6 → library-3.2.11}/library/utils/path_utils.py +0 -0
  160. {library-3.2.6 → library-3.2.11}/library/utils/pd_utils.py +0 -0
  161. {library-3.2.6 → library-3.2.11}/library/utils/remote_processes.py +0 -0
  162. {library-3.2.6 → library-3.2.11}/library/utils/sql_utils.py +0 -0
  163. {library-3.2.6 → library-3.2.11}/library/utils/sqlgroups.py +0 -0
@@ -10,7 +10,9 @@ tracking online video playlists (eg. YouTube subscriptions) and scheduling brows
10
10
 
11
11
  [![Downloads](https://static.pepy.tech/badge/library)](https://pepy.tech/project/library)
12
12
 
13
- ## Install
13
+ ## Getting started
14
+
15
+ <details><summary>Install</summary>
14
16
 
15
17
  Linux recommended but [Windows setup instructions](./Windows.md) available.
16
18
 
@@ -24,7 +26,31 @@ Required: `ffmpeg`
24
26
 
25
27
  Some features work better with: `mpv`, `fd-find`, `fish`
26
28
 
27
- ## Getting started
29
+ Optional Python extras are grouped under `library[deluxe]`. This includes
30
+ `gallery-dl` (used by `gallery-add`), `internetarchive` (used for Internet
31
+ Archive downloads), and `cdx_toolkit` (used by the `--webcache`/`--wayback`
32
+ CDX extractor):
33
+
34
+ pip install "library[deluxe]"
35
+
36
+ Torrent metadata commands (`torrents-add` and `torrents-dump`) require the
37
+ `libtorrent` Python bindings. They are included by `library[deluxe]` when using
38
+ Python 3.12 or 3.13; Python 3.14 wheels are not available yet, so use Python
39
+ 3.13 for torrent support.
40
+
41
+ If the PyPI wheel is unavailable for your Python or platform, these system
42
+ packages provide the bindings:
43
+
44
+ * Debian/Ubuntu: `sudo apt install python3-libtorrent`
45
+ * Fedora: `sudo dnf install rb_libtorrent-python3`
46
+ * macOS/Homebrew: `brew install libtorrent-rasterbar`
47
+
48
+ The Debian, Fedora, and Homebrew packages are built for their system or
49
+ Homebrew Python and may not be importable from a separate uv virtualenv. There
50
+ is no official Scoop package for the Python bindings; on Windows, use Python
51
+ 3.13 and `pip install "library[deluxe]"`.
52
+
53
+ </details>
28
54
 
29
55
  <details><summary>Local media</summary>
30
56
 
@@ -99,7 +125,7 @@ To stop playing press Ctrl+C in either the terminal or mpv
99
125
  <details><summary>List all subcommands</summary>
100
126
 
101
127
  $ library
102
- library (v3.2.006; 102 subcommands)
128
+ library (v3.2.011; 102 subcommands)
103
129
 
104
130
  Create database subcommands:
105
131
  ╭─────────────────┬──────────────────────────────────────────╮
@@ -797,6 +823,14 @@ BTW, for some cols like time_deleted you'll need to specify a where clause so th
797
823
  --page-key start --page-start 0 --page-step 50 --fixed-pages 14 --stop-pages-no-match 1 \
798
824
  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)
799
825
 
826
+ Recursively add every link on a site (glob excludes prune subtrees from the crawl)
827
+
828
+ library links-add links.db --recursive https://example.com/docs/ --path-exclude '*/tags/*'
829
+
830
+ Add every archived link for a site from the Internet Archive CDX index
831
+
832
+ library links-add links.db --webcache https://example.com/docs/
833
+
800
834
 
801
835
  </details>
802
836
 
@@ -1130,6 +1164,31 @@ BTW, for some cols like time_deleted you'll need to specify a where clause so th
1130
1164
  library links https://en.wikipedia.org/wiki/List_of_bacon_dishes --path-include https://en.wikipedia.org/wiki/ --after-include famous
1131
1165
  https://en.wikipedia.org/wiki/Omelette
1132
1166
 
1167
+ Download the filtered links. Internet Archive details pages use the Internet Archive downloader and preserve all original files:
1168
+
1169
+ library extract-links --download https://readrare.com/
1170
+
1171
+ Mirror a site recursively. Only links within the same domain and parent directory as the given
1172
+ path are followed; links pointing elsewhere are downloaded but not followed. Includes/excludes
1173
+ accept globs, so you can prune whole subtrees while crawling:
1174
+
1175
+ library links --recursive --download https://example.com/docs/
1176
+ library links -R --download https://example.com/docs/ --path-exclude '*/tag/*' '*/tags/*'
1177
+
1178
+ Extract links from a web archive CDX index instead of crawling live pages. Internet Archive
1179
+ Wayback snapshots are emitted by default. Wayback URLs are collapsed to their original
1180
+ archived URL while crawling: sibling pages that only exist under a different timestamp are
1181
+ still followed, but multiple captures of the same page are collapsed so only one copy of
1182
+ each page is downloaded. Use --webcache-from/--webcache-to to target a time period:
1183
+
1184
+ library links --webcache https://example.com/docs/
1185
+ library links --webcache --recursive --download https://example.com/docs/
1186
+ library links --webcache --webcache-source cc https://example.com/docs/
1187
+ library links --webcache --webcache-from 2020 --webcache-to 2021 https://example.com/docs/
1188
+
1189
+ Common tracking and analytics URLs, including Google Tag Manager and Google Analytics,
1190
+ are excluded by default. Use --path-exclude to replace the default exclusions when needed.
1191
+
1133
1192
  Read from local clipboard and filter out links based on nearby plain text
1134
1193
 
1135
1194
  library links --local-html (cb -t text/html | psub) --after-exclude paranormal spooky horror podcast tech fantasy supernatural lecture sport
@@ -2178,10 +2237,14 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
2178
2237
  <details><summary>Print torrent file info</summary>
2179
2238
 
2180
2239
  $ library torrents-dump -h
2181
- usage: library torrents-dump PATH ...
2240
+ usage: library torrents-dump [--trackers] PATH ...
2182
2241
 
2183
2242
  View torrent file metadata via libtorrent
2184
2243
 
2244
+ Aggregate torrent metadata by tracker domain:
2245
+
2246
+ library torrents-dump --trackers PATH ...
2247
+
2185
2248
 
2186
2249
  </details>
2187
2250
 
@@ -2316,7 +2379,7 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
2316
2379
  | /home/xk/github/xk/lb/.ruff_cache/ | 19.5 kB | 100 |
2317
2380
  | /home/xk/github/xk/lb/.gitattributes | 119 Bytes | |
2318
2381
  | /home/xk/github/xk/lb/.mypy_cache/ | 280 Bytes | 4 |
2319
- | /home/xk/github/xk/lb/.pdm-python | 15 Bytes | |
2382
+ | /home/xk/github/xk/lb/.venv | 15 Bytes | |
2320
2383
 
2321
2384
  Only include files with a specific depth
2322
2385
 
@@ -2489,6 +2552,17 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
2489
2552
  library download dl.db --include https://www.youtube.com/watch?v=YE7VzlLtp-4
2490
2553
  library download dl.db -s https://www.youtube.com/watch?v=YE7VzlLtp-4 # equivalent
2491
2554
 
2555
+ Recursively download a site (filesystem profile). Only links within the same domain and
2556
+ parent directory as the seed are followed; other links are downloaded but not followed.
2557
+
2558
+ library download links.db --filesystem --recursive --prefix ~/mirror/ https://example.com/docs/
2559
+
2560
+ Download a site's archived pages from the Internet Archive Wayback Machine using the CDX API.
2561
+ `--webcache` implies `--links`; add `--recursive` to also parse the archived pages for
2562
+ further links:
2563
+
2564
+ library download links.db --filesystem --webcache --prefix ~/mirror/ https://example.com/docs/
2565
+
2492
2566
  Maximizing the variety of subdomains
2493
2567
 
2494
2568
  library 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) )"
@@ -3054,6 +3128,10 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3054
3128
  library torrents --dl --different-drives
3055
3129
  library torrents --dl --different-drives /mnt/d4/
3056
3130
 
3131
+ Find incomplete torrents whose temp and final paths use the same drive:
3132
+
3133
+ library torrents --dl --same-drives
3134
+
3057
3135
  Aggregate torrent info (torrents-status)
3058
3136
 
3059
3137
  library torrents -pa
@@ -3070,6 +3148,10 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3070
3148
 
3071
3149
  library torrents --dl --progress=+10% --force-start
3072
3150
 
3151
+ Find torrents with a recorded completion timestamp
3152
+
3153
+ library torrents --completed
3154
+
3073
3155
  Stop completed downloads
3074
3156
 
3075
3157
  library torrents -S+5MiB --seeders=+5 --time-stalled=+45days --time-active=+180days --stop
@@ -3094,6 +3176,7 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3094
3176
  When --delete-rows is provided, the metadata is removed from qBittorrent
3095
3177
  When --delete-files is provided, the downloaded files are deleted
3096
3178
  When --delete-incomplete 80% is provided, any files that were downloaded less than 80% are deleted
3179
+ When --reset-progress is provided, the downloaded files are deleted and progress is reset without removing the torrents
3097
3180
 
3098
3181
 
3099
3182
  </details>
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: library
3
- Version: 3.2.6
3
+ Version: 3.2.11
4
4
  Summary: xk media library
5
- Author-Email: Jacob Chapman <7908073+chapmanjacobd@users.noreply.github.com>
5
+ Author: Jacob Chapman
6
+ Author-email: Jacob Chapman <7908073+chapmanjacobd@users.noreply.github.com>
6
7
  License: BSD 3-Clause License
7
8
 
8
9
  Copyright (c) 2024, Jacob Chapman
@@ -31,16 +32,10 @@ License: BSD 3-Clause License
31
32
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
33
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
34
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
-
35
- Project-URL: documentation, https://github.com/chapmanjacobd/library#usage
36
- Project-URL: homepage, https://github.com/chapmanjacobd/library#readme
37
- Project-URL: repository, https://github.com/chapmanjacobd/library/
38
- Requires-Python: >=3.12
39
35
  Requires-Dist: beautifulsoup4>=4.10.0
40
36
  Requires-Dist: sqlite-utils>=3.38
41
37
  Requires-Dist: ipython>=9.0.0
42
38
  Requires-Dist: ffmpeg-python
43
- Requires-Dist: gallery-dl
44
39
  Requires-Dist: yt-dlp[default]
45
40
  Requires-Dist: natsort
46
41
  Requires-Dist: charset-normalizer
@@ -61,49 +56,56 @@ Requires-Dist: screeninfo
61
56
  Requires-Dist: tabulate
62
57
  Requires-Dist: tinytag
63
58
  Requires-Dist: tldextract
64
- Requires-Dist: pyreadline3; platform_system == "Windows"
65
- Provides-Extra: dev
66
- Requires-Dist: black; extra == "dev"
67
- Requires-Dist: isort; extra == "dev"
68
- Requires-Dist: ssort; extra == "dev"
69
- Provides-Extra: test
70
- Requires-Dist: ruff; extra == "test"
71
- Requires-Dist: pytest; extra == "test"
72
- Requires-Dist: pytest-regressions; extra == "test"
73
- Requires-Dist: freezegun; extra == "test"
74
- Requires-Dist: pandas; extra == "test"
75
- Requires-Dist: pyfakefs; extra == "test"
59
+ Requires-Dist: pyreadline3 ; sys_platform == 'win32'
60
+ Requires-Dist: aiohttp ; extra == 'deluxe'
61
+ Requires-Dist: annoy ; extra == 'deluxe'
62
+ Requires-Dist: catt ; extra == 'deluxe'
63
+ Requires-Dist: cdx-toolkit ; extra == 'deluxe'
64
+ Requires-Dist: curl-cffi ; extra == 'deluxe'
65
+ Requires-Dist: gallery-dl ; extra == 'deluxe'
66
+ Requires-Dist: geopandas ; extra == 'deluxe'
67
+ Requires-Dist: img2pdf ; extra == 'deluxe'
68
+ Requires-Dist: internetarchive ; extra == 'deluxe'
69
+ Requires-Dist: ocrmypdf ; extra == 'deluxe'
70
+ Requires-Dist: openpyxl ; extra == 'deluxe'
71
+ Requires-Dist: paramiko ; extra == 'deluxe'
72
+ Requires-Dist: pdf2image ; extra == 'deluxe'
73
+ Requires-Dist: pillow ; extra == 'deluxe'
74
+ Requires-Dist: pyexiftool ; extra == 'deluxe'
75
+ Requires-Dist: pymcdm ; extra == 'deluxe'
76
+ Requires-Dist: pyvirtualdisplay ; extra == 'deluxe'
77
+ Requires-Dist: qbittorrent-api ; extra == 'deluxe'
78
+ Requires-Dist: scikit-learn ; extra == 'deluxe'
79
+ Requires-Dist: selenium ; extra == 'deluxe'
80
+ Requires-Dist: libtorrent ; python_full_version < '3.14' and extra == 'deluxe'
81
+ Requires-Dist: tqdm ; extra == 'deluxe'
82
+ Requires-Dist: wordllama>=0.2.7.post0 ; extra == 'deluxe'
83
+ Requires-Dist: xattr ; sys_platform != 'win32' and extra == 'deluxe'
84
+ Requires-Dist: black ; extra == 'dev'
85
+ Requires-Dist: isort ; extra == 'dev'
86
+ Requires-Dist: ssort ; extra == 'dev'
87
+ Requires-Dist: brotab ; extra == 'fat'
88
+ Requires-Dist: camelot-py>=0.11.0 ; extra == 'fat'
89
+ Requires-Dist: ghostscript ; extra == 'fat'
90
+ Requires-Dist: opencv-python ; extra == 'fat'
91
+ Requires-Dist: orjson ; extra == 'fat'
92
+ Requires-Dist: selenium-wire-lw ; extra == 'fat'
93
+ Requires-Dist: subliminal ; extra == 'fat'
94
+ Requires-Dist: textract-py3==2.0.1 ; extra == 'fat'
95
+ Requires-Dist: ruff ; extra == 'test'
96
+ Requires-Dist: pytest ; extra == 'test'
97
+ Requires-Dist: pytest-regressions ; extra == 'test'
98
+ Requires-Dist: freezegun ; extra == 'test'
99
+ Requires-Dist: pandas ; extra == 'test'
100
+ Requires-Dist: pyfakefs ; extra == 'test'
101
+ Requires-Python: >=3.12
102
+ Project-URL: documentation, https://github.com/chapmanjacobd/library#usage
103
+ Project-URL: homepage, https://github.com/chapmanjacobd/library#readme
104
+ Project-URL: repository, https://github.com/chapmanjacobd/library/
76
105
  Provides-Extra: deluxe
77
- Requires-Dist: aiohttp; extra == "deluxe"
78
- Requires-Dist: annoy; extra == "deluxe"
79
- Requires-Dist: catt; extra == "deluxe"
80
- Requires-Dist: curl-cffi; extra == "deluxe"
81
- Requires-Dist: geopandas; extra == "deluxe"
82
- Requires-Dist: img2pdf; extra == "deluxe"
83
- Requires-Dist: ocrmypdf; extra == "deluxe"
84
- Requires-Dist: openpyxl; extra == "deluxe"
85
- Requires-Dist: paramiko; extra == "deluxe"
86
- Requires-Dist: pdf2image; extra == "deluxe"
87
- Requires-Dist: pillow; extra == "deluxe"
88
- Requires-Dist: PyExifTool; extra == "deluxe"
89
- Requires-Dist: pymcdm; extra == "deluxe"
90
- Requires-Dist: pyvirtualdisplay; extra == "deluxe"
91
- Requires-Dist: qbittorrent-api; extra == "deluxe"
92
- Requires-Dist: scikit-learn; extra == "deluxe"
93
- Requires-Dist: selenium; extra == "deluxe"
94
- Requires-Dist: libtorrent; extra == "deluxe"
95
- Requires-Dist: tqdm; extra == "deluxe"
96
- Requires-Dist: wordllama>=0.2.7.post0; extra == "deluxe"
97
- Requires-Dist: xattr; extra == "deluxe"
106
+ Provides-Extra: dev
98
107
  Provides-Extra: fat
99
- Requires-Dist: brotab; extra == "fat"
100
- Requires-Dist: camelot-py>=0.11.0; extra == "fat"
101
- Requires-Dist: ghostscript; extra == "fat"
102
- Requires-Dist: opencv-python; extra == "fat"
103
- Requires-Dist: orjson; extra == "fat"
104
- Requires-Dist: selenium-wire-lw; extra == "fat"
105
- Requires-Dist: subliminal; extra == "fat"
106
- Requires-Dist: textract-py3==2.0.1; extra == "fat"
108
+ Provides-Extra: test
107
109
  Description-Content-Type: text/markdown
108
110
 
109
111
  # library (media toolkit)
@@ -118,7 +120,9 @@ tracking online video playlists (eg. YouTube subscriptions) and scheduling brows
118
120
 
119
121
  [![Downloads](https://static.pepy.tech/badge/library)](https://pepy.tech/project/library)
120
122
 
121
- ## Install
123
+ ## Getting started
124
+
125
+ <details><summary>Install</summary>
122
126
 
123
127
  Linux recommended but [Windows setup instructions](./Windows.md) available.
124
128
 
@@ -132,7 +136,31 @@ Required: `ffmpeg`
132
136
 
133
137
  Some features work better with: `mpv`, `fd-find`, `fish`
134
138
 
135
- ## Getting started
139
+ Optional Python extras are grouped under `library[deluxe]`. This includes
140
+ `gallery-dl` (used by `gallery-add`), `internetarchive` (used for Internet
141
+ Archive downloads), and `cdx_toolkit` (used by the `--webcache`/`--wayback`
142
+ CDX extractor):
143
+
144
+ pip install "library[deluxe]"
145
+
146
+ Torrent metadata commands (`torrents-add` and `torrents-dump`) require the
147
+ `libtorrent` Python bindings. They are included by `library[deluxe]` when using
148
+ Python 3.12 or 3.13; Python 3.14 wheels are not available yet, so use Python
149
+ 3.13 for torrent support.
150
+
151
+ If the PyPI wheel is unavailable for your Python or platform, these system
152
+ packages provide the bindings:
153
+
154
+ * Debian/Ubuntu: `sudo apt install python3-libtorrent`
155
+ * Fedora: `sudo dnf install rb_libtorrent-python3`
156
+ * macOS/Homebrew: `brew install libtorrent-rasterbar`
157
+
158
+ The Debian, Fedora, and Homebrew packages are built for their system or
159
+ Homebrew Python and may not be importable from a separate uv virtualenv. There
160
+ is no official Scoop package for the Python bindings; on Windows, use Python
161
+ 3.13 and `pip install "library[deluxe]"`.
162
+
163
+ </details>
136
164
 
137
165
  <details><summary>Local media</summary>
138
166
 
@@ -207,7 +235,7 @@ To stop playing press Ctrl+C in either the terminal or mpv
207
235
  <details><summary>List all subcommands</summary>
208
236
 
209
237
  $ library
210
- library (v3.2.006; 102 subcommands)
238
+ library (v3.2.011; 102 subcommands)
211
239
 
212
240
  Create database subcommands:
213
241
  ╭─────────────────┬──────────────────────────────────────────╮
@@ -905,6 +933,14 @@ BTW, for some cols like time_deleted you'll need to specify a where clause so th
905
933
  --page-key start --page-start 0 --page-step 50 --fixed-pages 14 --stop-pages-no-match 1 \
906
934
  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)
907
935
 
936
+ Recursively add every link on a site (glob excludes prune subtrees from the crawl)
937
+
938
+ library links-add links.db --recursive https://example.com/docs/ --path-exclude '*/tags/*'
939
+
940
+ Add every archived link for a site from the Internet Archive CDX index
941
+
942
+ library links-add links.db --webcache https://example.com/docs/
943
+
908
944
 
909
945
  </details>
910
946
 
@@ -1238,6 +1274,31 @@ BTW, for some cols like time_deleted you'll need to specify a where clause so th
1238
1274
  library links https://en.wikipedia.org/wiki/List_of_bacon_dishes --path-include https://en.wikipedia.org/wiki/ --after-include famous
1239
1275
  https://en.wikipedia.org/wiki/Omelette
1240
1276
 
1277
+ Download the filtered links. Internet Archive details pages use the Internet Archive downloader and preserve all original files:
1278
+
1279
+ library extract-links --download https://readrare.com/
1280
+
1281
+ Mirror a site recursively. Only links within the same domain and parent directory as the given
1282
+ path are followed; links pointing elsewhere are downloaded but not followed. Includes/excludes
1283
+ accept globs, so you can prune whole subtrees while crawling:
1284
+
1285
+ library links --recursive --download https://example.com/docs/
1286
+ library links -R --download https://example.com/docs/ --path-exclude '*/tag/*' '*/tags/*'
1287
+
1288
+ Extract links from a web archive CDX index instead of crawling live pages. Internet Archive
1289
+ Wayback snapshots are emitted by default. Wayback URLs are collapsed to their original
1290
+ archived URL while crawling: sibling pages that only exist under a different timestamp are
1291
+ still followed, but multiple captures of the same page are collapsed so only one copy of
1292
+ each page is downloaded. Use --webcache-from/--webcache-to to target a time period:
1293
+
1294
+ library links --webcache https://example.com/docs/
1295
+ library links --webcache --recursive --download https://example.com/docs/
1296
+ library links --webcache --webcache-source cc https://example.com/docs/
1297
+ library links --webcache --webcache-from 2020 --webcache-to 2021 https://example.com/docs/
1298
+
1299
+ Common tracking and analytics URLs, including Google Tag Manager and Google Analytics,
1300
+ are excluded by default. Use --path-exclude to replace the default exclusions when needed.
1301
+
1241
1302
  Read from local clipboard and filter out links based on nearby plain text
1242
1303
 
1243
1304
  library links --local-html (cb -t text/html | psub) --after-exclude paranormal spooky horror podcast tech fantasy supernatural lecture sport
@@ -2286,10 +2347,14 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
2286
2347
  <details><summary>Print torrent file info</summary>
2287
2348
 
2288
2349
  $ library torrents-dump -h
2289
- usage: library torrents-dump PATH ...
2350
+ usage: library torrents-dump [--trackers] PATH ...
2290
2351
 
2291
2352
  View torrent file metadata via libtorrent
2292
2353
 
2354
+ Aggregate torrent metadata by tracker domain:
2355
+
2356
+ library torrents-dump --trackers PATH ...
2357
+
2293
2358
 
2294
2359
  </details>
2295
2360
 
@@ -2424,7 +2489,7 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
2424
2489
  | /home/xk/github/xk/lb/.ruff_cache/ | 19.5 kB | 100 |
2425
2490
  | /home/xk/github/xk/lb/.gitattributes | 119 Bytes | |
2426
2491
  | /home/xk/github/xk/lb/.mypy_cache/ | 280 Bytes | 4 |
2427
- | /home/xk/github/xk/lb/.pdm-python | 15 Bytes | |
2492
+ | /home/xk/github/xk/lb/.venv | 15 Bytes | |
2428
2493
 
2429
2494
  Only include files with a specific depth
2430
2495
 
@@ -2597,6 +2662,17 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
2597
2662
  library download dl.db --include https://www.youtube.com/watch?v=YE7VzlLtp-4
2598
2663
  library download dl.db -s https://www.youtube.com/watch?v=YE7VzlLtp-4 # equivalent
2599
2664
 
2665
+ Recursively download a site (filesystem profile). Only links within the same domain and
2666
+ parent directory as the seed are followed; other links are downloaded but not followed.
2667
+
2668
+ library download links.db --filesystem --recursive --prefix ~/mirror/ https://example.com/docs/
2669
+
2670
+ Download a site's archived pages from the Internet Archive Wayback Machine using the CDX API.
2671
+ `--webcache` implies `--links`; add `--recursive` to also parse the archived pages for
2672
+ further links:
2673
+
2674
+ library download links.db --filesystem --webcache --prefix ~/mirror/ https://example.com/docs/
2675
+
2600
2676
  Maximizing the variety of subdomains
2601
2677
 
2602
2678
  library 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) )"
@@ -3162,6 +3238,10 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3162
3238
  library torrents --dl --different-drives
3163
3239
  library torrents --dl --different-drives /mnt/d4/
3164
3240
 
3241
+ Find incomplete torrents whose temp and final paths use the same drive:
3242
+
3243
+ library torrents --dl --same-drives
3244
+
3165
3245
  Aggregate torrent info (torrents-status)
3166
3246
 
3167
3247
  library torrents -pa
@@ -3178,6 +3258,10 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3178
3258
 
3179
3259
  library torrents --dl --progress=+10% --force-start
3180
3260
 
3261
+ Find torrents with a recorded completion timestamp
3262
+
3263
+ library torrents --completed
3264
+
3181
3265
  Stop completed downloads
3182
3266
 
3183
3267
  library torrents -S+5MiB --seeders=+5 --time-stalled=+45days --time-active=+180days --stop
@@ -3202,6 +3286,7 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
3202
3286
  When --delete-rows is provided, the metadata is removed from qBittorrent
3203
3287
  When --delete-files is provided, the downloaded files are deleted
3204
3288
  When --delete-incomplete 80% is provided, any files that were downloaded less than 80% are deleted
3289
+ When --reset-progress is provided, the downloaded files are deleted and progress is reset without removing the torrents
3205
3290
 
3206
3291
 
3207
3292
  </details>
@@ -2,11 +2,10 @@ import argparse, importlib, os, sys, textwrap
2
2
 
3
3
  from tabulate import tabulate
4
4
 
5
+ from library._version import __version__
5
6
  from library.utils import argparse_utils, iterables
6
7
  from library.utils.log_utils import log
7
8
 
8
- __version__ = "3.2.006"
9
-
10
9
  progs = {
11
10
  "Create database subcommands": {
12
11
  "fs_add": "Add local media",
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2026, Jacob Chapman
2
+
3
+ from importlib.metadata import PackageNotFoundError
4
+ from importlib.metadata import version as installed_version
5
+ from pathlib import Path
6
+ import tomllib
7
+
8
+
9
+ def get_version() -> str:
10
+ pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml"
11
+ if pyproject_path.is_file():
12
+ with pyproject_path.open("rb") as pyproject_file:
13
+ project_version = tomllib.load(pyproject_file)["project"].get("version")
14
+ if isinstance(project_version, str):
15
+ return project_version
16
+ message = f"Missing string project version in {pyproject_path}"
17
+ raise RuntimeError(message)
18
+
19
+ try:
20
+ return installed_version("library")
21
+ except PackageNotFoundError as error:
22
+ raise RuntimeError("Unable to determine the library version") from error
23
+
24
+
25
+ __version__ = get_version()
@@ -2,9 +2,6 @@ import os
2
2
  from pathlib import Path
3
3
  from types import ModuleType
4
4
 
5
- import gallery_dl
6
- from gallery_dl.job import Job
7
-
8
5
  from library.mediadb import db_media, db_playlists
9
6
  from library.utils import consts, printing, strings
10
7
  from library.utils.log_utils import log
@@ -142,52 +139,57 @@ def download(args, m):
142
139
  )
143
140
 
144
141
 
145
- class UrlJob(Job):
146
- resolve = 1 # depth of queue resolution
142
+ def url_job_class(gallery_dl):
143
+ # gallery_dl is an optional (deluxe) dependency, so the Job base class
144
+ # can only be resolved once gallery_dl has been imported
145
+ class UrlJob(gallery_dl.job.Job):
146
+ resolve = 1 # depth of queue resolution
147
147
 
148
- def __init__(self, url, parent=None, resolve=None):
149
- super().__init__(url, parent)
150
- self.results = []
151
- self.visited = set()
152
- if resolve is not None:
153
- self.resolve = resolve
148
+ def __init__(self, url, parent=None, resolve=None):
149
+ super().__init__(url, parent)
150
+ self.results = []
151
+ self.visited = set()
152
+ if resolve is not None:
153
+ self.resolve = resolve
154
154
 
155
- if self.resolve > 0:
156
- self.handle_queue = self.handle_queue_resolve
155
+ if self.resolve > 0:
156
+ self.handle_queue = self.handle_queue_resolve
157
157
 
158
- def handle_url(self, url, kwdict):
159
- self.results.append((url, kwdict))
158
+ def handle_url(self, url, kwdict):
159
+ self.results.append((url, kwdict))
160
160
 
161
- def handle_queue(self, url, kwdict):
162
- # unresolved queue entry
163
- self.results.append((url, kwdict))
161
+ def handle_queue(self, url, kwdict):
162
+ # unresolved queue entry
163
+ self.results.append((url, kwdict))
164
164
 
165
- def handle_queue_resolve(self, url, kwdict):
166
- if url in self.visited:
167
- return
168
- self.visited.add(url)
165
+ def handle_queue_resolve(self, url, kwdict):
166
+ if url in self.visited:
167
+ return
168
+ self.visited.add(url)
169
169
 
170
- cls = kwdict.get("_extractor")
171
- if cls:
172
- extr = cls.from_url(url)
173
- else:
174
- extr = self.extractor.find(url)
170
+ cls = kwdict.get("_extractor")
171
+ if cls:
172
+ extr = cls.from_url(url)
173
+ else:
174
+ extr = self.extractor.find(url)
175
175
 
176
- if not extr:
177
- self.results.append((url, kwdict))
178
- return
176
+ if not extr:
177
+ self.results.append((url, kwdict))
178
+ return
179
179
 
180
- job = self.__class__(extr, self, self.resolve - 1)
181
- job.results = self.results # shared accumulator
182
- job.visited = self.visited # shared visited set
183
- job.run()
180
+ job = self.__class__(extr, self, self.resolve - 1)
181
+ job.results = self.results # shared accumulator
182
+ job.visited = self.visited # shared visited set
183
+ job.run()
184
+
185
+ return UrlJob
184
186
 
185
187
 
186
188
  def get_playlist_metadata(args, playlist_path):
187
189
  gallery_dl = load_module_level_gallery_dl(args)
188
190
 
189
191
  added_media_count = 0
190
- job = UrlJob(playlist_path)
192
+ job = url_job_class(gallery_dl)(playlist_path)
191
193
  job.run()
192
194
 
193
195
  is_playlist = len(job.results) > 1
@@ -130,6 +130,7 @@ To compensate for this the script will only continue fetching pages until there
130
130
  )
131
131
 
132
132
  arggroups.filter_links(parser)
133
+ arggroups.spider(parser)
133
134
 
134
135
  arggroups.requests(parser)
135
136
  arggroups.selenium(parser)
@@ -254,6 +255,8 @@ def extractor(args, playlist_path):
254
255
  new_media = set()
255
256
  end_of_playlist = False
256
257
  page_limit = args.backfill_pages or args.fixed_pages or args.max_pages
258
+ if args.recursive or args.webcache:
259
+ page_limit = 1
257
260
 
258
261
  if args.confirm_ready:
259
262
  if args.selenium:
@@ -283,8 +286,14 @@ def extractor(args, playlist_path):
283
286
  page_known = set()
284
287
  page_new = {}
285
288
  try:
286
- for link_dict in extract_links.get_inner_urls(args, page_path):
289
+ link_iter = (
290
+ extract_links.crawl(args, [page_path])
291
+ if args.recursive
292
+ else extract_links.iter_links(args, page_path)
293
+ )
294
+ for link_dict in link_iter:
287
295
  link = link_dict.pop("link")
296
+ link_dict.pop("mime", None)
288
297
 
289
298
  if link == args.stop_link:
290
299
  end_of_playlist = True
@@ -302,7 +311,7 @@ def extractor(args, playlist_path):
302
311
  printing.print_overwrite(f"Page {page_count} link scan: {len(page_new)} new [{len(page_known)} known]")
303
312
  print(file=sys.stderr)
304
313
 
305
- if not (args.backfill_pages or args.fixed_pages):
314
+ if not args.recursive and not (args.backfill_pages or args.fixed_pages):
306
315
  if (args.stop_known and len(page_known) > args.stop_known) or (
307
316
  args.stop_new and args.stop_new >= len(page_new)
308
317
  ):