library 3.2.5__tar.gz → 3.2.7__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.
- {library-3.2.5 → library-3.2.7}/.github/README.md +27 -2
- {library-3.2.5 → library-3.2.7}/PKG-INFO +28 -3
- {library-3.2.5 → library-3.2.7}/library/__main__.py +1 -1
- library-3.2.7/library/mediafiles/torrents_dump.py +89 -0
- {library-3.2.5 → library-3.2.7}/library/playback/play_actions.py +2 -1
- {library-3.2.5 → library-3.2.7}/library/playback/playback_control.py +2 -2
- {library-3.2.5 → library-3.2.7}/library/playback/torrents_info.py +77 -26
- {library-3.2.5 → library-3.2.7}/library/usage.py +26 -1
- {library-3.2.5 → library-3.2.7}/library/utils/arggroups.py +25 -1
- {library-3.2.5 → library-3.2.7}/library/utils/devices.py +12 -0
- {library-3.2.5 → library-3.2.7}/pyproject.toml +1 -1
- library-3.2.5/library/mediafiles/torrents_dump.py +0 -67
- {library-3.2.5 → library-3.2.7}/LICENSE +0 -0
- {library-3.2.5 → library-3.2.7}/library/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/assets/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/assets/calibre.css +0 -0
- {library-3.2.5 → library-3.2.7}/library/assets/kotobago.png +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/av.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/computer_info.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/computers_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/fs_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/fs_add_metadata.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/gallery_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/gallery_backend.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/getty_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/hn_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/links_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/nicotine_import.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/places_import.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/reddit_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/row_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/site_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/substack.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/subtitle.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/tables_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/tabs_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/tildes.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/torrents_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/tube_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/tube_backend.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/createdb/web_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/dictionary.pkl +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/ffmpeg_errors.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/http_errors.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/imagemagick_errors.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/unar_errors.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/wordbank.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/data/yt_dlp_errors.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/dedupe_db.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/dedupe_media.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/merge_online_local.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/mpv_watchlater.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/pushshift.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/editdb/reddit_selftext.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/files/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/files/christen.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/files/sample_compare.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/files/sample_hash.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/files/similar_files.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/big_dirs.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/filter_src.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/merge_mv.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/mergerfs_cp.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/mount_stats.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/move_list.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/scatter.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/folders/similar_folders.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/fsdb/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/fsdb/disk_usage.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/fsdb/filesystem.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/fsdb/folder_stats.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/fsdb/search_db.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/lb.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/block.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/db_history.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/db_media.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/db_playlists.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/download.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/download_status.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/history.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/history_add.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/optimize_db.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/playlists.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/redownload.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/search.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediadb/stats.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/images_to_pdf.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/media_check.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/pdf_edit.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/process_ffmpeg.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/process_image.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/process_media.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/process_text.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/torrents_start.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/mediafiles/unardel.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/misc/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/misc/dedupe_czkawka.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/misc/export_text.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/misc/search_help.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/multidb/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/multidb/allocate_torrents.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/multidb/copy_play_counts.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/multidb/merge_dbs.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/links_open.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/media_player.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/media_printer.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/post_actions.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/surf.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/tabs_open.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/playback/torrents_remaining.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/readme.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/columns.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/eda.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/incremental_diff.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/markdown_tables.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/mcda.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/tablefiles/plot.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/cluster_sort.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/combinations.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/expand_links.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/extract_links.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/extract_text.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/json_keys_rename.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/markdown_links.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/nouns.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/regex_sort.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/text/timestamps.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/__init__.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/arg_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/argparse_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/consts.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/date_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/db_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/file_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/filter_engine.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/gui.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/iterables.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/log_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/mpv_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/nums.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/objects.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/path_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/pd_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/printing.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/processes.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/remote_processes.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/shell_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/sql_utils.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/sqlgroups.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/strings.py +0 -0
- {library-3.2.5 → library-3.2.7}/library/utils/web.py +0 -0
|
@@ -99,7 +99,7 @@ To stop playing press Ctrl+C in either the terminal or mpv
|
|
|
99
99
|
<details><summary>List all subcommands</summary>
|
|
100
100
|
|
|
101
101
|
$ library
|
|
102
|
-
library (v3.2.
|
|
102
|
+
library (v3.2.007; 102 subcommands)
|
|
103
103
|
|
|
104
104
|
Create database subcommands:
|
|
105
105
|
╭─────────────────┬──────────────────────────────────────────╮
|
|
@@ -2178,10 +2178,14 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
2178
2178
|
<details><summary>Print torrent file info</summary>
|
|
2179
2179
|
|
|
2180
2180
|
$ library torrents-dump -h
|
|
2181
|
-
usage: library torrents-dump PATH ...
|
|
2181
|
+
usage: library torrents-dump [--trackers] PATH ...
|
|
2182
2182
|
|
|
2183
2183
|
View torrent file metadata via libtorrent
|
|
2184
2184
|
|
|
2185
|
+
Aggregate torrent metadata by tracker domain:
|
|
2186
|
+
|
|
2187
|
+
library torrents-dump --trackers PATH ...
|
|
2188
|
+
|
|
2185
2189
|
|
|
2186
2190
|
</details>
|
|
2187
2191
|
|
|
@@ -3042,6 +3046,22 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
3042
3046
|
|
|
3043
3047
|
library torrents query
|
|
3044
3048
|
|
|
3049
|
+
Positional torrent searches use the path aligned with torrent status by default.
|
|
3050
|
+
Search a specific path field or both fields with --path-search:
|
|
3051
|
+
|
|
3052
|
+
library torrents --dl --path-search save /mnt/d4/
|
|
3053
|
+
library torrents --dl --path-search both /mnt/d4/
|
|
3054
|
+
|
|
3055
|
+
Find incomplete torrents whose temp and final paths use different drives.
|
|
3056
|
+
--different-drives searches both path fields unless --path-search is specified:
|
|
3057
|
+
|
|
3058
|
+
library torrents --dl --different-drives
|
|
3059
|
+
library torrents --dl --different-drives /mnt/d4/
|
|
3060
|
+
|
|
3061
|
+
Find incomplete torrents whose temp and final paths use the same drive:
|
|
3062
|
+
|
|
3063
|
+
library torrents --dl --same-drives
|
|
3064
|
+
|
|
3045
3065
|
Aggregate torrent info (torrents-status)
|
|
3046
3066
|
|
|
3047
3067
|
library torrents -pa
|
|
@@ -3058,6 +3078,10 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
3058
3078
|
|
|
3059
3079
|
library torrents --dl --progress=+10% --force-start
|
|
3060
3080
|
|
|
3081
|
+
Find torrents with a recorded completion timestamp
|
|
3082
|
+
|
|
3083
|
+
library torrents --completed
|
|
3084
|
+
|
|
3061
3085
|
Stop completed downloads
|
|
3062
3086
|
|
|
3063
3087
|
library torrents -S+5MiB --seeders=+5 --time-stalled=+45days --time-active=+180days --stop
|
|
@@ -3082,6 +3106,7 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
3082
3106
|
When --delete-rows is provided, the metadata is removed from qBittorrent
|
|
3083
3107
|
When --delete-files is provided, the downloaded files are deleted
|
|
3084
3108
|
When --delete-incomplete 80% is provided, any files that were downloaded less than 80% are deleted
|
|
3109
|
+
When --reset-progress is provided, the downloaded files are deleted and progress is reset without removing the torrents
|
|
3085
3110
|
|
|
3086
3111
|
|
|
3087
3112
|
</details>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: library
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.7
|
|
4
4
|
Summary: xk media library
|
|
5
5
|
Author-Email: Jacob Chapman <7908073+chapmanjacobd@users.noreply.github.com>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -207,7 +207,7 @@ To stop playing press Ctrl+C in either the terminal or mpv
|
|
|
207
207
|
<details><summary>List all subcommands</summary>
|
|
208
208
|
|
|
209
209
|
$ library
|
|
210
|
-
library (v3.2.
|
|
210
|
+
library (v3.2.007; 102 subcommands)
|
|
211
211
|
|
|
212
212
|
Create database subcommands:
|
|
213
213
|
╭─────────────────┬──────────────────────────────────────────╮
|
|
@@ -2286,10 +2286,14 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
2286
2286
|
<details><summary>Print torrent file info</summary>
|
|
2287
2287
|
|
|
2288
2288
|
$ library torrents-dump -h
|
|
2289
|
-
usage: library torrents-dump PATH ...
|
|
2289
|
+
usage: library torrents-dump [--trackers] PATH ...
|
|
2290
2290
|
|
|
2291
2291
|
View torrent file metadata via libtorrent
|
|
2292
2292
|
|
|
2293
|
+
Aggregate torrent metadata by tracker domain:
|
|
2294
|
+
|
|
2295
|
+
library torrents-dump --trackers PATH ...
|
|
2296
|
+
|
|
2293
2297
|
|
|
2294
2298
|
</details>
|
|
2295
2299
|
|
|
@@ -3150,6 +3154,22 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
3150
3154
|
|
|
3151
3155
|
library torrents query
|
|
3152
3156
|
|
|
3157
|
+
Positional torrent searches use the path aligned with torrent status by default.
|
|
3158
|
+
Search a specific path field or both fields with --path-search:
|
|
3159
|
+
|
|
3160
|
+
library torrents --dl --path-search save /mnt/d4/
|
|
3161
|
+
library torrents --dl --path-search both /mnt/d4/
|
|
3162
|
+
|
|
3163
|
+
Find incomplete torrents whose temp and final paths use different drives.
|
|
3164
|
+
--different-drives searches both path fields unless --path-search is specified:
|
|
3165
|
+
|
|
3166
|
+
library torrents --dl --different-drives
|
|
3167
|
+
library torrents --dl --different-drives /mnt/d4/
|
|
3168
|
+
|
|
3169
|
+
Find incomplete torrents whose temp and final paths use the same drive:
|
|
3170
|
+
|
|
3171
|
+
library torrents --dl --same-drives
|
|
3172
|
+
|
|
3153
3173
|
Aggregate torrent info (torrents-status)
|
|
3154
3174
|
|
|
3155
3175
|
library torrents -pa
|
|
@@ -3166,6 +3186,10 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
3166
3186
|
|
|
3167
3187
|
library torrents --dl --progress=+10% --force-start
|
|
3168
3188
|
|
|
3189
|
+
Find torrents with a recorded completion timestamp
|
|
3190
|
+
|
|
3191
|
+
library torrents --completed
|
|
3192
|
+
|
|
3169
3193
|
Stop completed downloads
|
|
3170
3194
|
|
|
3171
3195
|
library torrents -S+5MiB --seeders=+5 --time-stalled=+45days --time-active=+180days --stop
|
|
@@ -3190,6 +3214,7 @@ Inspired somewhat by https://nikkhokkho.sourceforge.io/?page=FileOptimizer
|
|
|
3190
3214
|
When --delete-rows is provided, the metadata is removed from qBittorrent
|
|
3191
3215
|
When --delete-files is provided, the downloaded files are deleted
|
|
3192
3216
|
When --delete-incomplete 80% is provided, any files that were downloaded less than 80% are deleted
|
|
3217
|
+
When --reset-progress is provided, the downloaded files are deleted and progress is reset without removing the torrents
|
|
3193
3218
|
|
|
3194
3219
|
|
|
3195
3220
|
</details>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from library import usage
|
|
7
|
+
from library.createdb import torrents_add
|
|
8
|
+
from library.utils import arggroups, argparse_utils, printing, strings
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_args():
|
|
12
|
+
parser = argparse_utils.ArgumentParser(usage=usage.torrents_dump)
|
|
13
|
+
arggroups.debug(parser)
|
|
14
|
+
|
|
15
|
+
parser.add_argument("--trackers", action="store_true", help="Aggregate torrent metadata by tracker")
|
|
16
|
+
arggroups.paths_or_stdin(parser)
|
|
17
|
+
args = parser.parse_args()
|
|
18
|
+
arggroups.args_post(args, parser)
|
|
19
|
+
return args
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def gen_torrents(l):
|
|
23
|
+
for s in l:
|
|
24
|
+
p = Path(s)
|
|
25
|
+
if p.is_dir():
|
|
26
|
+
for file in p.rglob("*.torrent"):
|
|
27
|
+
if not file.is_dir():
|
|
28
|
+
yield file
|
|
29
|
+
elif p.is_file() and p.suffix == ".torrent":
|
|
30
|
+
yield p
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def aggregate_trackers(torrents):
|
|
34
|
+
trackers = {}
|
|
35
|
+
for _, torrent in torrents:
|
|
36
|
+
tracker = torrent["tracker"]
|
|
37
|
+
stats = trackers.setdefault(tracker, {"tracker": tracker, "count": 0, "size": 0})
|
|
38
|
+
stats["count"] += 1
|
|
39
|
+
stats["size"] += torrent["size"]
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
{**stats, "size": strings.file_size(stats["size"])}
|
|
43
|
+
for stats in sorted(trackers.values(), key=lambda d: (d["size"], d["tracker"] or ""))
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def torrents_dump():
|
|
48
|
+
args = parse_args()
|
|
49
|
+
|
|
50
|
+
torrent_files = list(gen_torrents(args.paths))
|
|
51
|
+
with ThreadPoolExecutor() as executor:
|
|
52
|
+
metadata_results = executor.map(torrents_add.extract_metadata, torrent_files)
|
|
53
|
+
torrents = list(zip(torrent_files, metadata_results))
|
|
54
|
+
|
|
55
|
+
if args.trackers:
|
|
56
|
+
tracker_stats = aggregate_trackers(torrents)
|
|
57
|
+
if tracker_stats:
|
|
58
|
+
printing.table(tracker_stats)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
rows = []
|
|
62
|
+
for torrent_path, d in torrents:
|
|
63
|
+
torrent = torrents_add.torrent_decode(torrent_path)
|
|
64
|
+
trackers = [t for t in torrent.trackers()]
|
|
65
|
+
announce_urls = []
|
|
66
|
+
for t in sorted(trackers, key=lambda t: (t.source, t.tier)):
|
|
67
|
+
try:
|
|
68
|
+
if url := t.url:
|
|
69
|
+
announce_urls.append(url)
|
|
70
|
+
except UnicodeDecodeError:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
rows.append(
|
|
74
|
+
d
|
|
75
|
+
| {
|
|
76
|
+
"size": strings.file_size(d["size"]),
|
|
77
|
+
"size_avg": strings.file_size(d["size_avg"]),
|
|
78
|
+
"size_median": strings.file_size(d["size_median"]),
|
|
79
|
+
"time_uploaded": strings.relative_datetime(d["time_uploaded"]),
|
|
80
|
+
"time_created": strings.relative_datetime(d["time_created"]),
|
|
81
|
+
"time_modified": strings.relative_datetime(d["time_modified"]),
|
|
82
|
+
"time_created": strings.relative_datetime(d["time_created"]),
|
|
83
|
+
"files": d["files"][0]["path"] + ", ...",
|
|
84
|
+
"announce_urls": " ".join(announce_urls),
|
|
85
|
+
"web_seeds": " ".join(d["web_seeds"]),
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
printing.table(rows)
|
|
@@ -252,6 +252,7 @@ If you don't know the exact name of your chromecast group run `catt scan`
|
|
|
252
252
|
if args.chromecast:
|
|
253
253
|
from catt.api import CattDevice
|
|
254
254
|
|
|
255
|
+
args.chromecast_device = args.chromecast_device or devices.catt_default_device()
|
|
255
256
|
args.cc = CattDevice(args.chromecast_device, lazy=True)
|
|
256
257
|
args.cc_ip = devices.get_ip_of_chromecast(args.chromecast_device)
|
|
257
258
|
|
|
@@ -502,7 +503,7 @@ def watch() -> None:
|
|
|
502
503
|
|
|
503
504
|
|
|
504
505
|
def listen() -> None:
|
|
505
|
-
args = parse_args(SC.listen
|
|
506
|
+
args = parse_args(SC.listen) # no --cast-to means catt's own default speaker
|
|
506
507
|
args.profiles = [DBType.audio]
|
|
507
508
|
process_playqueue(args)
|
|
508
509
|
|
|
@@ -288,14 +288,14 @@ def catt_stop(args) -> None:
|
|
|
288
288
|
catt_device = []
|
|
289
289
|
if args.chromecast_device:
|
|
290
290
|
catt_device = ["-d", args.chromecast_device]
|
|
291
|
-
processes.cmd("catt", *catt_device, "stop")
|
|
291
|
+
processes.cmd("catt", *catt_device, "stop", strict=False)
|
|
292
292
|
|
|
293
293
|
|
|
294
294
|
def catt_pause(args) -> None:
|
|
295
295
|
catt_device = []
|
|
296
296
|
if args.chromecast_device:
|
|
297
297
|
catt_device = ["-d", args.chromecast_device]
|
|
298
|
-
processes.cmd("catt", *catt_device, "play_toggle")
|
|
298
|
+
processes.cmd("catt", *catt_device, "play_toggle", strict=False)
|
|
299
299
|
|
|
300
300
|
|
|
301
301
|
def kill_process(name) -> None:
|
|
@@ -47,6 +47,12 @@ def parse_args():
|
|
|
47
47
|
const="73%",
|
|
48
48
|
help="Delete incomplete files from matching torrents",
|
|
49
49
|
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--reset-progress",
|
|
52
|
+
"--reset",
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="Delete matching torrent files and recheck progress without removing the torrent",
|
|
55
|
+
)
|
|
50
56
|
parser.add_argument("--move", type=Path, help="Directory to move folders/files")
|
|
51
57
|
arggroups.mmv_folders(parser)
|
|
52
58
|
parser.add_argument(
|
|
@@ -188,6 +194,8 @@ def filter_torrents_by_activity(args, torrents):
|
|
|
188
194
|
|
|
189
195
|
if args.complete:
|
|
190
196
|
torrents = [t for t in torrents if t.state_enum.is_complete]
|
|
197
|
+
if args.completed:
|
|
198
|
+
torrents = [t for t in torrents if t.completion_on > 0]
|
|
191
199
|
if args.incomplete:
|
|
192
200
|
torrents = [t for t in torrents if not t.state_enum.is_complete]
|
|
193
201
|
|
|
@@ -297,6 +305,10 @@ def filter_torrents_by_criteria(args, torrents):
|
|
|
297
305
|
|
|
298
306
|
if args.private is not None:
|
|
299
307
|
torrents = [t for t in torrents if args.private is t.private]
|
|
308
|
+
if args.different_drives is not None:
|
|
309
|
+
torrents = [
|
|
310
|
+
t for t in torrents if torrent_paths_on_different_drives(t) is args.different_drives
|
|
311
|
+
]
|
|
300
312
|
if args.no_tagged:
|
|
301
313
|
tags = set(args.no_tagged)
|
|
302
314
|
torrents = [t for t in torrents if tags.isdisjoint(t.tags.split(", "))]
|
|
@@ -309,7 +321,7 @@ def filter_torrents_by_criteria(args, torrents):
|
|
|
309
321
|
for t in torrents
|
|
310
322
|
if strings.glob_match_all(
|
|
311
323
|
args.torrent_search,
|
|
312
|
-
[t.name, t.comment,
|
|
324
|
+
[t.name, t.comment, *torrent_paths_for_search(args, t), t.hash],
|
|
313
325
|
)
|
|
314
326
|
]
|
|
315
327
|
if args.torrent_exclude:
|
|
@@ -318,7 +330,7 @@ def filter_torrents_by_criteria(args, torrents):
|
|
|
318
330
|
for t in torrents
|
|
319
331
|
if not strings.glob_match_any(
|
|
320
332
|
args.torrent_exclude,
|
|
321
|
-
[t.name, t.comment,
|
|
333
|
+
[t.name, t.comment, *torrent_paths_for_search(args, t), t.hash],
|
|
322
334
|
)
|
|
323
335
|
]
|
|
324
336
|
if args.torrent_include:
|
|
@@ -327,7 +339,7 @@ def filter_torrents_by_criteria(args, torrents):
|
|
|
327
339
|
for t in torrents
|
|
328
340
|
if strings.glob_match_any(
|
|
329
341
|
args.torrent_include,
|
|
330
|
-
[t.name, t.comment,
|
|
342
|
+
[t.name, t.comment, *torrent_paths_for_search(args, t), t.hash],
|
|
331
343
|
)
|
|
332
344
|
]
|
|
333
345
|
if args.file_search:
|
|
@@ -502,6 +514,58 @@ def map_value_status(t, status):
|
|
|
502
514
|
return MAP_VALUE.get(status, t.state == status)
|
|
503
515
|
|
|
504
516
|
|
|
517
|
+
def torrent_paths_for_search(args, t):
|
|
518
|
+
path_search = getattr(args, "path_search", None)
|
|
519
|
+
if path_search is None and getattr(args, "different_drives", None) is not None:
|
|
520
|
+
path_search = "both"
|
|
521
|
+
if path_search is None:
|
|
522
|
+
path_search = "status"
|
|
523
|
+
if path_search == "download":
|
|
524
|
+
return [t.download_path]
|
|
525
|
+
if path_search == "save":
|
|
526
|
+
return [t.save_path]
|
|
527
|
+
if path_search == "both":
|
|
528
|
+
return [t.download_path, t.save_path]
|
|
529
|
+
return [t.save_path if t.state_enum.is_complete else t.download_path]
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def torrent_paths_on_different_drives(t):
|
|
533
|
+
if not t.download_path or not t.save_path:
|
|
534
|
+
return False
|
|
535
|
+
return path_utils.mountpoint(t.download_path) != path_utils.mountpoint(t.save_path)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def reset_torrent_progress(args, qbt_client, torrent):
|
|
539
|
+
originally_stopped = bool(torrent.state_enum.is_stopped)
|
|
540
|
+
qbt_client.torrents_stop(torrent_hashes=[torrent.hash])
|
|
541
|
+
|
|
542
|
+
paths = set()
|
|
543
|
+
for file in torrent_files(torrent):
|
|
544
|
+
for base_path in (torrent.download_path, torrent.save_path):
|
|
545
|
+
if base_path:
|
|
546
|
+
paths.add(Path(base_path) / file.name)
|
|
547
|
+
|
|
548
|
+
for file_path in paths:
|
|
549
|
+
if file_path.exists() and not file_path.is_dir():
|
|
550
|
+
log.info("Deleting file: %s", file_path)
|
|
551
|
+
file_path.unlink(missing_ok=True)
|
|
552
|
+
|
|
553
|
+
qbt_client.torrents_recheck(torrent_hashes=[torrent.hash])
|
|
554
|
+
if not originally_stopped:
|
|
555
|
+
qbt_client.torrents_start(torrent_hashes=[torrent.hash])
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def set_torrent_paths(qbt_client, t, temp_path, download_path):
|
|
559
|
+
save_path = download_path if download_path is not None else t.save_path
|
|
560
|
+
if save_path:
|
|
561
|
+
print(" ", t.save_path, "==>", save_path)
|
|
562
|
+
qbt_client.torrents_set_location(str(save_path), torrent_hashes=[t.hash])
|
|
563
|
+
|
|
564
|
+
if temp_path is not None:
|
|
565
|
+
print(" ", t.download_path, "-->", temp_path)
|
|
566
|
+
qbt_client.torrents_set_download_path(str(temp_path), torrent_hashes=[t.hash])
|
|
567
|
+
|
|
568
|
+
|
|
505
569
|
def torrents_info():
|
|
506
570
|
args = parse_args()
|
|
507
571
|
|
|
@@ -910,25 +974,15 @@ def torrents_info():
|
|
|
910
974
|
log.warning("Keeping %s incomplete file: %s", strings.percent(1 - file.progress), file_path)
|
|
911
975
|
break # Stop after deleting first valid path
|
|
912
976
|
|
|
977
|
+
if args.reset_progress:
|
|
978
|
+
print("Resetting progress of", len(torrents))
|
|
979
|
+
for t in torrents:
|
|
980
|
+
reset_torrent_progress(args, qbt_client, t)
|
|
981
|
+
|
|
913
982
|
alt_move_syntax = any(
|
|
914
983
|
k not in args.defaults for k in ["temp_drive", "temp_prefix", "download_drive", "download_prefix"]
|
|
915
984
|
)
|
|
916
985
|
if args.move or alt_move_syntax:
|
|
917
|
-
|
|
918
|
-
def set_temp_path(t, temp_path):
|
|
919
|
-
if temp_path is None:
|
|
920
|
-
return
|
|
921
|
-
|
|
922
|
-
print(" ", t.download_path, "-->", temp_path)
|
|
923
|
-
qbt_client.torrents_set_download_path(str(temp_path), torrent_hashes=[t.hash])
|
|
924
|
-
|
|
925
|
-
def set_download_path(t, download_path):
|
|
926
|
-
if download_path is None:
|
|
927
|
-
return
|
|
928
|
-
|
|
929
|
-
print(" ", t.save_path, "==>", download_path)
|
|
930
|
-
qbt_client.torrents_set_save_path(str(download_path), torrent_hashes=[t.hash])
|
|
931
|
-
|
|
932
986
|
for idx, t in enumerate(torrents):
|
|
933
987
|
print("Moving", idx + 1, "of", len(torrents))
|
|
934
988
|
|
|
@@ -993,28 +1047,25 @@ def torrents_info():
|
|
|
993
1047
|
log.debug("download_path %s", download_path)
|
|
994
1048
|
|
|
995
1049
|
new_path = download_path if t.state_enum.is_complete else temp_path
|
|
1050
|
+
if new_path is None:
|
|
1051
|
+
new_path = t.save_path if t.state_enum.is_complete else t.download_path
|
|
996
1052
|
if args.simulate:
|
|
997
1053
|
print("Moving", t.content_path, "to", new_path)
|
|
998
1054
|
continue
|
|
999
1055
|
|
|
1000
1056
|
content_path = Path(t.content_path) if t.content_path else None
|
|
1001
|
-
if content_path and content_path.exists():
|
|
1057
|
+
if content_path and content_path.exists() and new_path is not None:
|
|
1002
1058
|
if content_path.is_file():
|
|
1003
1059
|
print("Moving file", content_path, "to", new_path)
|
|
1004
1060
|
merge_mv.move(args, [str(content_path)], str(new_path))
|
|
1005
1061
|
else:
|
|
1006
|
-
new_path
|
|
1062
|
+
new_path = Path(new_path) / content_path.name
|
|
1007
1063
|
print("Moving folder", content_path, "to", new_path)
|
|
1008
1064
|
merge_mv.move(args, [str(content_path)], str(new_path))
|
|
1009
1065
|
|
|
1010
1066
|
if not (args.delete_files or args.delete_rows):
|
|
1011
1067
|
# update metadata
|
|
1012
|
-
|
|
1013
|
-
set_temp_path(t, temp_path)
|
|
1014
|
-
set_download_path(t, download_path)
|
|
1015
|
-
else: # download path first
|
|
1016
|
-
set_download_path(t, download_path)
|
|
1017
|
-
set_temp_path(t, temp_path)
|
|
1068
|
+
set_torrent_paths(qbt_client, t, temp_path, download_path)
|
|
1018
1069
|
|
|
1019
1070
|
if not originally_stopped:
|
|
1020
1071
|
qbt_client.torrents_start(torrent_hashes=[t.hash])
|
|
@@ -1976,9 +1976,13 @@ torrents_start = """library torrents-start [--prefix /mnt/d/] [--scan] PATH ...
|
|
|
1976
1976
|
--scan overrides the --tracker-dirnames layout when choosing save paths.
|
|
1977
1977
|
"""
|
|
1978
1978
|
|
|
1979
|
-
torrents_dump = """library torrents-dump PATH ...
|
|
1979
|
+
torrents_dump = """library torrents-dump [--trackers] PATH ...
|
|
1980
1980
|
|
|
1981
1981
|
View torrent file metadata via libtorrent
|
|
1982
|
+
|
|
1983
|
+
Aggregate torrent metadata by tracker domain:
|
|
1984
|
+
|
|
1985
|
+
library torrents-dump --trackers PATH ...
|
|
1982
1986
|
"""
|
|
1983
1987
|
|
|
1984
1988
|
torrents_info = """library torrents-info
|
|
@@ -1991,6 +1995,22 @@ torrents_info = """library torrents-info
|
|
|
1991
1995
|
|
|
1992
1996
|
library torrents query
|
|
1993
1997
|
|
|
1998
|
+
Positional torrent searches use the path aligned with torrent status by default.
|
|
1999
|
+
Search a specific path field or both fields with --path-search:
|
|
2000
|
+
|
|
2001
|
+
library torrents --dl --path-search save /mnt/d4/
|
|
2002
|
+
library torrents --dl --path-search both /mnt/d4/
|
|
2003
|
+
|
|
2004
|
+
Find incomplete torrents whose temp and final paths use different drives.
|
|
2005
|
+
--different-drives searches both path fields unless --path-search is specified:
|
|
2006
|
+
|
|
2007
|
+
library torrents --dl --different-drives
|
|
2008
|
+
library torrents --dl --different-drives /mnt/d4/
|
|
2009
|
+
|
|
2010
|
+
Find incomplete torrents whose temp and final paths use the same drive:
|
|
2011
|
+
|
|
2012
|
+
library torrents --dl --same-drives
|
|
2013
|
+
|
|
1994
2014
|
Aggregate torrent info (torrents-status)
|
|
1995
2015
|
|
|
1996
2016
|
library torrents -pa
|
|
@@ -2007,6 +2027,10 @@ torrents_info = """library torrents-info
|
|
|
2007
2027
|
|
|
2008
2028
|
library torrents --dl --progress=+10% --force-start
|
|
2009
2029
|
|
|
2030
|
+
Find torrents with a recorded completion timestamp
|
|
2031
|
+
|
|
2032
|
+
library torrents --completed
|
|
2033
|
+
|
|
2010
2034
|
Stop completed downloads
|
|
2011
2035
|
|
|
2012
2036
|
library torrents -S+5MiB --seeders=+5 --time-stalled=+45days --time-active=+180days --stop
|
|
@@ -2031,6 +2055,7 @@ torrents_info = """library torrents-info
|
|
|
2031
2055
|
When --delete-rows is provided, the metadata is removed from qBittorrent
|
|
2032
2056
|
When --delete-files is provided, the downloaded files are deleted
|
|
2033
2057
|
When --delete-incomplete 80% is provided, any files that were downloaded less than 80% are deleted
|
|
2058
|
+
When --reset-progress is provided, the downloaded files are deleted and progress is reset without removing the torrents
|
|
2034
2059
|
"""
|
|
2035
2060
|
|
|
2036
2061
|
torrents_remaining = """library torrents-remaining
|
|
@@ -2764,7 +2764,6 @@ def qBittorrent_torrents(parent_parser):
|
|
|
2764
2764
|
|
|
2765
2765
|
parser.add_argument(
|
|
2766
2766
|
"--complete",
|
|
2767
|
-
"--completed",
|
|
2768
2767
|
"--uploading",
|
|
2769
2768
|
"--upload",
|
|
2770
2769
|
"--up",
|
|
@@ -2773,6 +2772,11 @@ def qBittorrent_torrents(parent_parser):
|
|
|
2773
2772
|
action="store_true",
|
|
2774
2773
|
help="Include completed torrents",
|
|
2775
2774
|
)
|
|
2775
|
+
parser.add_argument(
|
|
2776
|
+
"--completed",
|
|
2777
|
+
action="store_true",
|
|
2778
|
+
help="Include torrents with a recorded completion timestamp",
|
|
2779
|
+
)
|
|
2776
2780
|
parser.add_argument(
|
|
2777
2781
|
"--incomplete",
|
|
2778
2782
|
"--downloading",
|
|
@@ -2795,6 +2799,26 @@ def qBittorrent_torrents(parent_parser):
|
|
|
2795
2799
|
help="Include torrents with files currently in use by processes",
|
|
2796
2800
|
)
|
|
2797
2801
|
parser.add_argument("--private", action=argparse.BooleanOptionalAction, help="Match only private torrents")
|
|
2802
|
+
drive_filter = parser.add_mutually_exclusive_group()
|
|
2803
|
+
drive_filter.add_argument(
|
|
2804
|
+
"--different-drives",
|
|
2805
|
+
dest="different_drives",
|
|
2806
|
+
action="store_true",
|
|
2807
|
+
help="Include torrents whose incomplete and final paths are on different mountpoints",
|
|
2808
|
+
)
|
|
2809
|
+
drive_filter.add_argument(
|
|
2810
|
+
"--no-different-drives",
|
|
2811
|
+
"--same-drives",
|
|
2812
|
+
dest="different_drives",
|
|
2813
|
+
action="store_false",
|
|
2814
|
+
help="Include torrents whose incomplete and final paths are on the same mountpoint",
|
|
2815
|
+
)
|
|
2816
|
+
parser.set_defaults(different_drives=None)
|
|
2817
|
+
parser.add_argument(
|
|
2818
|
+
"--path-search",
|
|
2819
|
+
choices=["status", "download", "save", "both"],
|
|
2820
|
+
help="Path used by positional torrent searches: status, download, save, or both",
|
|
2821
|
+
)
|
|
2798
2822
|
|
|
2799
2823
|
parser.add_argument(
|
|
2800
2824
|
"--sizes",
|
|
@@ -8,6 +8,18 @@ from library.utils.log_utils import log
|
|
|
8
8
|
webbrowser.register("termux-open-url '%s'", None)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def catt_default_device() -> str:
|
|
12
|
+
"""catt's own configured speaker, i.e. options.device in ~/.config/catt/catt.cfg."""
|
|
13
|
+
config = Path.home() / ".config" / "catt" / "catt.cfg"
|
|
14
|
+
if not config.exists():
|
|
15
|
+
return ""
|
|
16
|
+
for line in config.read_text().splitlines():
|
|
17
|
+
key, _, value = line.partition("=")
|
|
18
|
+
if key.strip() == "device":
|
|
19
|
+
return value.strip()
|
|
20
|
+
return ""
|
|
21
|
+
|
|
22
|
+
|
|
11
23
|
def get_ip_of_chromecast(device_name) -> str:
|
|
12
24
|
from pychromecast import discovery
|
|
13
25
|
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
|
-
|
|
3
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
from library import usage
|
|
7
|
-
from library.createdb import torrents_add
|
|
8
|
-
from library.utils import arggroups, argparse_utils, printing, strings
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def parse_args():
|
|
12
|
-
parser = argparse_utils.ArgumentParser(usage=usage.torrents_dump)
|
|
13
|
-
arggroups.debug(parser)
|
|
14
|
-
|
|
15
|
-
arggroups.paths_or_stdin(parser)
|
|
16
|
-
args = parser.parse_args()
|
|
17
|
-
arggroups.args_post(args, parser)
|
|
18
|
-
return args
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def gen_torrents(l):
|
|
22
|
-
for s in l:
|
|
23
|
-
p = Path(s)
|
|
24
|
-
if p.is_dir():
|
|
25
|
-
for file in p.rglob("*.torrent"):
|
|
26
|
-
if not file.is_dir():
|
|
27
|
-
yield file
|
|
28
|
-
elif p.is_file() and p.suffix == ".torrent":
|
|
29
|
-
yield p
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def torrents_dump():
|
|
33
|
-
args = parse_args()
|
|
34
|
-
|
|
35
|
-
torrent_files = list(gen_torrents(args.paths))
|
|
36
|
-
with ThreadPoolExecutor() as executor:
|
|
37
|
-
metadata_results = executor.map(torrents_add.extract_metadata, torrent_files)
|
|
38
|
-
torrents = list(zip(torrent_files, metadata_results))
|
|
39
|
-
|
|
40
|
-
for i, (torrent_path, d) in enumerate(torrents):
|
|
41
|
-
torrent = torrents_add.torrent_decode(torrent_path)
|
|
42
|
-
trackers = [t for t in torrent.trackers()]
|
|
43
|
-
announce_urls = []
|
|
44
|
-
for t in sorted(trackers, key=lambda t: (t.source, t.tier)):
|
|
45
|
-
try:
|
|
46
|
-
if url := t.url:
|
|
47
|
-
announce_urls.append(url)
|
|
48
|
-
except UnicodeDecodeError:
|
|
49
|
-
continue
|
|
50
|
-
|
|
51
|
-
printing.table(
|
|
52
|
-
[
|
|
53
|
-
d
|
|
54
|
-
| {
|
|
55
|
-
"size": strings.file_size(d["size"]),
|
|
56
|
-
"size_avg": strings.file_size(d["size_avg"]),
|
|
57
|
-
"size_median": strings.file_size(d["size_median"]),
|
|
58
|
-
"time_uploaded": strings.relative_datetime(d["time_uploaded"]),
|
|
59
|
-
"time_created": strings.relative_datetime(d["time_created"]),
|
|
60
|
-
"time_modified": strings.relative_datetime(d["time_modified"]),
|
|
61
|
-
"time_created": strings.relative_datetime(d["time_created"]),
|
|
62
|
-
"files": d["files"][0]["path"] + ", ...",
|
|
63
|
-
"announce_urls": " ".join(announce_urls),
|
|
64
|
-
"web_seeds": " ".join(d["web_seeds"]),
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|