diff --git a/docs/htmlcov/status.json b/docs/htmlcov/status.json
index fa13d27..83d9ca0 100644
--- a/docs/htmlcov/status.json
+++ b/docs/htmlcov/status.json
@@ -1 +1 @@
-{"format":2,"version":"7.3.0","globals":"9be4e6a8a858fe28f098f593a2dc3317","files":{"d_a44f0ac069e85531___init___py":{"hash":"4c021f15215a8a16a176d53e47d4e706","index":{"nums":[0,1,125,12,0,0,0,0],"html_filename":"d_a44f0ac069e85531___init___py.html","relative_filename":"tests\\__init__.py"}},"d_a44f0ac069e85531_test_cli_py":{"hash":"b909165913366b2a272a62041b348ef0","index":{"nums":[0,1,266,5,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_cli_py.html","relative_filename":"tests\\test_cli.py"}},"d_a44f0ac069e85531_test_commands_py":{"hash":"438c43b687cedad65755d370613dbdcd","index":{"nums":[0,1,191,1,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_commands_py.html","relative_filename":"tests\\test_commands.py"}},"d_a44f0ac069e85531_test_edit_py":{"hash":"4a49891a429f9427d6ee54d0a509551e","index":{"nums":[0,1,111,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_edit_py.html","relative_filename":"tests\\test_edit.py"}},"d_a44f0ac069e85531_test_interactive_py":{"hash":"bb2621272eb184267051fa4d4acd0e4e","index":{"nums":[0,1,70,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_interactive_py.html","relative_filename":"tests\\test_interactive.py"}},"d_a44f0ac069e85531_test_rebuild_py":{"hash":"a70ee159a44f8dd5e02db8b13cdaf62e","index":{"nums":[0,1,97,1,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_rebuild_py.html","relative_filename":"tests\\test_rebuild.py"}},"d_a44f0ac069e85531_test_recheck_py":{"hash":"7b25fee565bbfcb466fecc64366eb798","index":{"nums":[0,1,158,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_recheck_py.html","relative_filename":"tests\\test_recheck.py"}},"d_a44f0ac069e85531_test_torrent_py":{"hash":"7ba41ee942b113b5d499890ce7a38882","index":{"nums":[0,1,123,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_torrent_py.html","relative_filename":"tests\\test_torrent.py"}},"d_a44f0ac069e85531_test_utils_py":{"hash":"18f11350dd89b1572bb22a4b2156fc30","index":{"nums":[0,1,89,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_utils_py.html","relative_filename":"tests\\test_utils.py"}},"d_1bc82e0ab2fcb2ec___init___py":{"hash":"36fe5a1bed9f6c0bfa4f90fef58c1fa5","index":{"nums":[0,1,8,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec___init___py.html","relative_filename":"torrentfile\\__init__.py"}},"d_1bc82e0ab2fcb2ec___main___py":{"hash":"cf6ef7033e53a22948f3f8ac7c09167d","index":{"nums":[0,1,5,1,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec___main___py.html","relative_filename":"torrentfile\\__main__.py"}},"d_1bc82e0ab2fcb2ec_cli_py":{"hash":"c5c0a17bba25776a1efcfc07f14d32bd","index":{"nums":[0,1,122,1,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_cli_py.html","relative_filename":"torrentfile\\cli.py"}},"d_1bc82e0ab2fcb2ec_commands_py":{"hash":"90f017c7d1d26cbf0c17d9d050cea882","index":{"nums":[0,1,178,6,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_commands_py.html","relative_filename":"torrentfile\\commands.py"}},"d_1bc82e0ab2fcb2ec_edit_py":{"hash":"1502595c36b01981a7f0225499c267a0","index":{"nums":[0,1,53,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_edit_py.html","relative_filename":"torrentfile\\edit.py"}},"d_1bc82e0ab2fcb2ec_hasher_py":{"hash":"c921588933b1796df01ccbf78733a3fc","index":{"nums":[0,1,261,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_hasher_py.html","relative_filename":"torrentfile\\hasher.py"}},"d_1bc82e0ab2fcb2ec_interactive_py":{"hash":"6ee224f5dbc8f01e6f3659cadf85034c","index":{"nums":[0,1,121,20,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_interactive_py.html","relative_filename":"torrentfile\\interactive.py"}},"d_1bc82e0ab2fcb2ec_mixins_py":{"hash":"ed074f546497d379d129fb9ca99f5ecf","index":{"nums":[0,1,105,4,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_mixins_py.html","relative_filename":"torrentfile\\mixins.py"}},"d_1bc82e0ab2fcb2ec_rebuild_py":{"hash":"367070e0ebdd3cc6860a3220f1b8ec93","index":{"nums":[0,1,251,4,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_rebuild_py.html","relative_filename":"torrentfile\\rebuild.py"}},"d_1bc82e0ab2fcb2ec_recheck_py":{"hash":"5a6646b76bc621d6d8d80edf1c4ecb1f","index":{"nums":[0,1,278,3,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_recheck_py.html","relative_filename":"torrentfile\\recheck.py"}},"d_1bc82e0ab2fcb2ec_torrent_py":{"hash":"31e20f6cdb59d95a9b140af6aefeadd0","index":{"nums":[0,1,282,3,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_torrent_py.html","relative_filename":"torrentfile\\torrent.py"}},"d_1bc82e0ab2fcb2ec_utils_py":{"hash":"dcddc66c21874c8ea3b5544b58a4ec8a","index":{"nums":[0,1,133,8,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_utils_py.html","relative_filename":"torrentfile\\utils.py"}},"d_1bc82e0ab2fcb2ec_version_py":{"hash":"44b1159565f16ef8ac18bb190f115b24","index":{"nums":[0,1,2,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_version_py.html","relative_filename":"torrentfile\\version.py"}}}}
\ No newline at end of file
+{"format":2,"version":"7.3.0","globals":"9be4e6a8a858fe28f098f593a2dc3317","files":{"d_a44f0ac069e85531___init___py":{"hash":"4c021f15215a8a16a176d53e47d4e706","index":{"nums":[0,1,125,12,0,0,0,0],"html_filename":"d_a44f0ac069e85531___init___py.html","relative_filename":"tests\\__init__.py"}},"d_a44f0ac069e85531_test_cli_py":{"hash":"b909165913366b2a272a62041b348ef0","index":{"nums":[0,1,266,5,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_cli_py.html","relative_filename":"tests\\test_cli.py"}},"d_a44f0ac069e85531_test_commands_py":{"hash":"438c43b687cedad65755d370613dbdcd","index":{"nums":[0,1,191,1,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_commands_py.html","relative_filename":"tests\\test_commands.py"}},"d_a44f0ac069e85531_test_edit_py":{"hash":"4a49891a429f9427d6ee54d0a509551e","index":{"nums":[0,1,111,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_edit_py.html","relative_filename":"tests\\test_edit.py"}},"d_a44f0ac069e85531_test_interactive_py":{"hash":"bb2621272eb184267051fa4d4acd0e4e","index":{"nums":[0,1,70,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_interactive_py.html","relative_filename":"tests\\test_interactive.py"}},"d_a44f0ac069e85531_test_rebuild_py":{"hash":"a70ee159a44f8dd5e02db8b13cdaf62e","index":{"nums":[0,1,97,1,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_rebuild_py.html","relative_filename":"tests\\test_rebuild.py"}},"d_a44f0ac069e85531_test_recheck_py":{"hash":"7b25fee565bbfcb466fecc64366eb798","index":{"nums":[0,1,158,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_recheck_py.html","relative_filename":"tests\\test_recheck.py"}},"d_a44f0ac069e85531_test_torrent_py":{"hash":"7ba41ee942b113b5d499890ce7a38882","index":{"nums":[0,1,123,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_torrent_py.html","relative_filename":"tests\\test_torrent.py"}},"d_a44f0ac069e85531_test_utils_py":{"hash":"18f11350dd89b1572bb22a4b2156fc30","index":{"nums":[0,1,89,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_utils_py.html","relative_filename":"tests\\test_utils.py"}},"d_1bc82e0ab2fcb2ec___init___py":{"hash":"36fe5a1bed9f6c0bfa4f90fef58c1fa5","index":{"nums":[0,1,8,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec___init___py.html","relative_filename":"torrentfile\\__init__.py"}},"d_1bc82e0ab2fcb2ec___main___py":{"hash":"cf6ef7033e53a22948f3f8ac7c09167d","index":{"nums":[0,1,5,1,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec___main___py.html","relative_filename":"torrentfile\\__main__.py"}},"d_1bc82e0ab2fcb2ec_cli_py":{"hash":"c5c0a17bba25776a1efcfc07f14d32bd","index":{"nums":[0,1,122,1,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_cli_py.html","relative_filename":"torrentfile\\cli.py"}},"d_1bc82e0ab2fcb2ec_commands_py":{"hash":"90f017c7d1d26cbf0c17d9d050cea882","index":{"nums":[0,1,178,6,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_commands_py.html","relative_filename":"torrentfile\\commands.py"}},"d_1bc82e0ab2fcb2ec_edit_py":{"hash":"1502595c36b01981a7f0225499c267a0","index":{"nums":[0,1,53,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_edit_py.html","relative_filename":"torrentfile\\edit.py"}},"d_1bc82e0ab2fcb2ec_hasher_py":{"hash":"c921588933b1796df01ccbf78733a3fc","index":{"nums":[0,1,261,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_hasher_py.html","relative_filename":"torrentfile\\hasher.py"}},"d_1bc82e0ab2fcb2ec_interactive_py":{"hash":"6ee224f5dbc8f01e6f3659cadf85034c","index":{"nums":[0,1,121,20,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_interactive_py.html","relative_filename":"torrentfile\\interactive.py"}},"d_1bc82e0ab2fcb2ec_mixins_py":{"hash":"d28811a35bf19a3cb9865e5fd12f1991","index":{"nums":[0,1,107,4,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_mixins_py.html","relative_filename":"torrentfile\\mixins.py"}},"d_1bc82e0ab2fcb2ec_rebuild_py":{"hash":"367070e0ebdd3cc6860a3220f1b8ec93","index":{"nums":[0,1,251,4,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_rebuild_py.html","relative_filename":"torrentfile\\rebuild.py"}},"d_1bc82e0ab2fcb2ec_recheck_py":{"hash":"5a6646b76bc621d6d8d80edf1c4ecb1f","index":{"nums":[0,1,278,3,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_recheck_py.html","relative_filename":"torrentfile\\recheck.py"}},"d_1bc82e0ab2fcb2ec_torrent_py":{"hash":"31e20f6cdb59d95a9b140af6aefeadd0","index":{"nums":[0,1,282,3,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_torrent_py.html","relative_filename":"torrentfile\\torrent.py"}},"d_1bc82e0ab2fcb2ec_utils_py":{"hash":"dcddc66c21874c8ea3b5544b58a4ec8a","index":{"nums":[0,1,133,8,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_utils_py.html","relative_filename":"torrentfile\\utils.py"}},"d_1bc82e0ab2fcb2ec_version_py":{"hash":"44b1159565f16ef8ac18bb190f115b24","index":{"nums":[0,1,2,0,0,0,0,0],"html_filename":"d_1bc82e0ab2fcb2ec_version_py.html","relative_filename":"torrentfile\\version.py"}}}}
\ No newline at end of file
diff --git a/docs/search/search_index.json b/docs/search/search_index.json
index 52efc5b..e541eb1 100644
--- a/docs/search/search_index.json
+++ b/docs/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"TorrentFile","text":""},{"location":"#overview","title":"\ud83c\udf10 Overview","text":"A command line interface for creating, reviewing, editing, or verifying bittorrent meta files (.torrent
files). torrentfile
is open source, and supports all versions of Bittorrent files, including hybrid meta files. The code base is also importable and can easily be used as a library for creating or manipulating torrent files in external projects. Documentation is available at https://alexpdev.github.io/torrentfile.
A GUI frontend for this project can be found at https://github.com/alexpdev/TorrentfileQt
"},{"location":"#requirements","title":"\ud83d\udd0c Requirements","text":" Python 3.6+ Tested on Linux, Windows and Mac "},{"location":"#install","title":"\ud83d\udcbb Install","text":"PyPi:
pip install torrentfile\n
Git:
git clone https://github.com/alexpdev/torrentfile.git\ncd torrentfile\npip install .\n
Download pre-compiled binaries from the release page.
"},{"location":"#documentation","title":"\ud83d\udcda Documentation","text":""},{"location":"#torrentfile-documentation-available-at-httpsalexpdevgithubiotorrentfile","title":"torrentfile documentation available at https://alexpdev.github.io/torrentfile","text":""},{"location":"#usage","title":"\ud83d\ude80 Usage","text":"Usage examples can be found in the project documentation on the examples page.
"},{"location":"#license","title":"\ud83d\udcdd License","text":"Apache Software License v2.0 - See LICENSE
"},{"location":"#issues-requests-prs","title":"\ud83d\udca1 Issues & Requests & PRs","text":"If you encounter any bugs or would like to request a new feature please open a new issue. PRs and other contributions that are meaningful and add value to the project are welcome.
"},{"location":"#usage-examples","title":"Usage Examples","text":""},{"location":"#creating-bittorrent-files","title":"Creating Bittorrent Files","text":"Creating a basic torrent file is as easy as using the create subcommand with the path to the torrent file.
torrentfile create /path/to/content\n
You can add one or more trackers by using any one of -a
, --announce
flags and listing their URL as a space separated list.
torrentfile create /path/to/content -a http://tracker1.com http://tracker2.net\n
If you intend to distribute the file on a private tracker then you should use one of -p
, --private
flags, which tells your Bittorrent clients to disable DHT and multitracker protocols.
torrentfile create /path/to/content --private\n
By default torrentfile
displays a progress bar indicating how much of the content has already been processed. To turn off this display you can either use --quiet
mode in as a global flag or you can set the --prog
flag to 0.
torrentfile create /path/to/content --prog 0\n
torrentfile
extracts the name of the contents top level file or directory and saves the torrent file to the current working directory with the extracted title.
For example running the follwing command would create ./content.torrent
.
torrentfile create /path/to/content\n
To specify an alternative path or filename you may use the -o
, --out
flags followed by the path to the preferred destination.
torrentfile create /path/to/content -o /some/other/path/torrent.torrent\n
If the path specified is an existing directory, then the torrent file will be saved to that directory, with same filename as the default top level path name.
For example the following command would create a torrent file at /some/other/path/content.torrent
.
torrentfile create /path/to/content -o /some/other/path/\n
torrentfile
creates Bittorrent v1 files by default. To create a V2 or hybrid (v1 and v2) torrent file, use the --meta-version
option followed by the preferred version number option. The options include: 1
(v1 default), 2
(v2), or 3
(v1 & v2).
torrentfile create /path/to/content --meta-version 2\n
torrentfile create /path/to/content --meta-version 3
torrentfile
includes the option to command line flags for the create
sub-command from an ini
style configuration file, by using the --config
and optional --config-path
options to specify the path to the configuration file. If --config-path
is ommited, then torrentfile
will look by default in the current working directory for a file named torrentfile.ini
. If the file is not discovered in the current working directory, it will move on to look ~/.torrentfile/torrentfile.ini
followed by ~/.config/torrentfile.ini
. Please see the documentation for more details on how the configuration file should be formatted.
"},{"location":"#checkrecheck-torrent","title":"Check/Recheck Torrent","text":"The recheck
subcommand allows you to scan a Bittorrent file and compare it\u2019s contents, against a file or directory containing the contents the torrent file was created from. The output provided by this process gives a detailed perspective if any files are missing or have been corrupted in any way. Supports any version of Bittorrent file.
torrentfile recheck /path/to/some.torrent /path/to/content\n
"},{"location":"#edit-torrent","title":"Edit Torrent","text":"To edit specific fields of the torrent file, there is the edit
subcommand. Using this subcommand you can specify the field with one of the available field flags, for example --announce
and specify the value you wish to change it to.
torrentfile edit /path/to/content --announce https://new.tracker.url1.com https://newtracker.url/2\n
You can use the -h
flag for a full list of available fields that can be edited.
torrentfile edit -h\n
"},{"location":"#create-magnet","title":"Create Magnet","text":"To create a magnet URI for a pre-existing torrent meta file, use the sub-command magnet
with the path to the torrent file.
torrentfile magnet /path/to/some.torrent\n
"},{"location":"#gui","title":"GUI","text":"If you prefer a windowed GUI please check out the official GUI frontend here
"},{"location":"Apache2/","title":"Apache License","text":"Version 2.0, January 2004 <http://www.apache.org/licenses/>
"},{"location":"Apache2/#terms-and-conditions-for-use-reproduction-and-distribution","title":"Terms and Conditions for use, reproduction, and distribution","text":""},{"location":"Apache2/#1-definitions","title":"1. Definitions","text":"\u201cLicense\u201d shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
\u201cLicensor\u201d shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
\u201cLegal Entity\u201d shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \u201ccontrol\u201d means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
\u201cYou\u201d (or \u201cYour\u201d) shall mean an individual or Legal Entity exercising permissions granted by this License.
\u201cSource\u201d form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
\u201cObject\u201d form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
\u201cWork\u201d shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
\u201cDerivative Works\u201d shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
\u201cContribution\u201d shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \u201csubmitted\u201d means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \u201cNot a Contribution.\u201d
\u201cContributor\u201d shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
"},{"location":"Apache2/#2-grant-of-copyright-license","title":"2. Grant of Copyright License","text":"Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
"},{"location":"Apache2/#3-grant-of-patent-license","title":"3. Grant of Patent License","text":"Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
"},{"location":"Apache2/#4-redistribution","title":"4. Redistribution","text":"You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a \u201cNOTICE\u201d text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
"},{"location":"Apache2/#5-submission-of-contributions","title":"5. Submission of Contributions","text":"Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
"},{"location":"Apache2/#6-trademarks","title":"6. Trademarks","text":"This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
"},{"location":"Apache2/#7-disclaimer-of-warranty","title":"7. Disclaimer of Warranty","text":"Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \u201cAS IS\u201d BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
"},{"location":"Apache2/#8-limitation-of-liability","title":"8. Limitation of Liability","text":"In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
"},{"location":"Apache2/#9-accepting-warranty-or-additional-liability","title":"9. Accepting Warranty or Additional Liability","text":"While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
"},{"location":"Apache2/#appendix-how-to-apply-the-apache-license-to-your-work","title":"APPENDIX: How to apply the Apache License to your work","text":"To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets []
replaced with your own identifying information. (Don\u2019t include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \u201cprinted page\u201d as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n
"},{"location":"api/","title":"TorrentFile","text":""},{"location":"api/#api-and-source","title":"API and Source","text":""},{"location":"api/#torrent-module","title":"Torrent
Module","text":"module torrentfile.
torrent
Classes and procedures pertaining to the creation of torrent meta files.
Classes MetaFile
\u2014 Base Class for all TorrentFile classes. TorrentFile
\u2014 Class for creating Bittorrent meta files. TorrentFileV2
\u2014 Class for creating Bittorrent meta v2 files. TorrentFileHybrid
\u2014 Construct the Hybrid torrent meta file with provided parameters. TorrentAssembler
\u2014 Assembler class for Bittorrent version 2 and hybrid meta files. "},{"location":"api/#classes","title":"Classes","text":" TorrentFile
construct .torrent file.
TorrentFileV2
construct .torrent v2 files using provided data.
MetaFile
base class for all MetaFile classes.
"},{"location":"api/#constants","title":"Constants","text":""},{"location":"api/#bittorrent-v2","title":"Bittorrent V2","text":"From Bittorrent.org Documentation pages.
Implementation details for Bittorrent Protocol v2.
Note
All strings in a .torrent file that contain text must be UTF-8 encoded.
"},{"location":"api/#meta-version-2-dictionary","title":"Meta Version 2 Dictionary:","text":" \u201cannounce\u201d: The URL of the tracker.
\u201cinfo\u201d: This maps to a dictionary, with keys described below.
\u201cname\u201d: A display name for the torrent. It is purely advisory.
\u201cpiece length\u201d: The number of bytes that each logical piece in the peer protocol refers to. I.e. it sets the granularity of piece, request, bitfield and have messages. It must be a power of two and at least 6KiB.
\u201cmeta version\u201d: An integer value, set to 2 to indicate compatibility with the current revision of this specification. Version 1 is not assigned to avoid confusion with BEP3. Future revisions will only increment this issue to indicate an incompatible change has been made, for example that hash algorithms were changed due to newly discovered vulnerabilities. Lementations must check this field first and indicate that a torrent is of a newer version than they can handle before performing other idations which may result in more general messages about invalid files. Files are mapped into this piece address space so that each non-empty
\u201cfile tree\u201d: A tree of dictionaries where dictionary keys represent UTF-8 encoded path elements. Entries with zero-length keys describe the properties of the composed path at that point. \u2018UTF-8 encoded\u2019 context only means that if the native encoding is known at creation time it must be converted to UTF-8. Keys may contain invalid UTF-8 sequences or characters and names that are reserved on specific filesystems. Implementations must be prepared to sanitize them. On platforms path components exactly matching \u2018.\u2019 and \u2018..\u2019 must be sanitized since they could lead to directory traversal attacks and conflicting path descriptions. On platforms that require UTF-8 path components this sanitizing step must happen after normalizing overlong UTF-8 encodings. File is aligned to a piece boundary and occurs in same order as the file tree. The last piece of each file may be shorter than the specified piece length, resulting in an alignment gap.
\u201clength\u201d: Length of the file in bytes. Presence of this field indicates that the dictionary describes a file, not a directory. Which means it must not have any sibling entries.
\u201cpieces root\u201d: For non-empty files this is the the root hash of a merkle tree with a branching factor of 2, constructed from 16KiB blocks of the file. The last block may be shorter than 16KiB. The remaining leaf hashes beyond the end of the file required to construct upper layers of the merkle tree are set to zero. As of meta version 2 SHA2-256 is used as digest function for the merkle tree. The hash is stored in its binary form, not as human-readable string.
\u201cpiece layers\u201d: A dictionary of strings. For each file in the file tree that is larger than the piece size it contains one string value. The keys are the merkle roots while the values consist of concatenated hashes of one layer within that merkle tree. The layer is chosen so that one hash covers piece length bytes. For example if the piece size is 16KiB then the leaf hashes are used. If a piece size of 128KiB is used then 3rd layer up from the leaf hashes is used. Layer hashes which exclusively cover data beyond the end of file, i.e. are only needed to balance the tree, are omitted. All hashes are stored in their binary format. A torrent is not valid if this field is absent, the contained hashes do not match the merkle roots or are not from the correct layer.
Important
The file tree root dictionary itself must not be a file, i.e. it must not contain a zero-length key with a dictionary containing a length key.
"},{"location":"api/#bittorrent-v1","title":"Bittorrent V1","text":""},{"location":"api/#v1-meta-dictionary","title":"v1 meta-dictionary","text":" Note
In the single file case, the name key is the name of a file, in the muliple file case, it\u2019s the name of a directory.
"},{"location":"api/#utils-module","title":"Utils
Module","text":"module torrentfile.
utils
Utility functions and classes used throughout package.
Classes Memo
\u2014 Memoize cache. MissingPathError
\u2014 Path parameter is required to specify target content. PieceLengthValueError
\u2014 Piece Length parameter must equal a perfect power of 2. ArgumentError
\u2014 Exception for mismatched or mistyped CLI arguments. Functions check_path_writable
(
path
)
(bool) \u2014 Test if output path is writable. colored
(
string
, key
)
(str) \u2014 Output terminal content with formatting. copypath
(
source
, dest
)
\u2014 Copy the file located at source to dest. debug_is_on
(
)
(bool) \u2014 Return True if debug mode is on in environment variables. get_file_list
(
path
)
(list :) \u2014 Return a sorted list of file paths contained in directory. get_piece_length
(
size
)
(int) \u2014 Calculate the ideal piece length for bittorrent data. green
(
string
)
(str) \u2014 Output terminal content in green color. humanize_bytes
(
amount
)
(str) \u2014 Convert integer into human readable memory sized denomination. next_power_2
(
value
)
(int) \u2014 Calculate the next perfect power of 2 equal to or greater than value. normalize_piece_length
(
piece_length
)
(int) \u2014 Verify input piece_length is valid and convert accordingly. path_piece_length
(
path
)
(int) \u2014 Calculate piece length for input path and contents. path_size
(
path
)
(int) \u2014 Return the total size of all files in path recursively. path_stat
(
path
)
(Tuple[list, int, int] :) \u2014 Calculate directory statistics. toggle_debug_mode
(
switch_on
)
\u2014 Switch the environment variable debug indicator on or off. "},{"location":"api/#edit-module","title":"Edit
Module","text":"module torrentfile.
edit
Edit torrent module.
Provides a facility by which certain properties of a torrent meta file can be edited by the user. The various command line arguments indicate which fields should be edited, and what the new value should be. Depending on what fields are chosen to edit, this command can trigger a new info hash which means the torrent will no longer be able to participate in the same swarm as the original unedited torrent.
Functions edit_torrent
(
metafile
, args
)
(dict) \u2014 Edit the properties and values in a torrent meta file. filter_empty
(
args
, meta
, info
)
\u2014 Remove the fields that were not used by the original file creator. "},{"location":"api/#interactive-module","title":"Interactive
Module","text":"module torrentfile.
interactive
Module contains the procedures used for Interactive Mode.
Classes InteractiveEditor
\u2014 Interactive dialog class for torrent editing. InteractiveCreator
\u2014 Class namespace for interactive program options. Functions create_torrent
(
)
\u2014 Create new torrent file interactively. edit_action
(
)
\u2014 Edit the editable values of the torrent meta file. get_input
(
*args
)
(str) \u2014 Determine appropriate input function to call. recheck_torrent
(
)
\u2014 Check torrent download completed percentage. select_action
(
)
\u2014 Operate TorrentFile program interactively through terminal. showcenter
(
txt
)
\u2014 Print text to screen in the center position of the terminal. showtext
(
txt
)
\u2014 Print contents of txt to screen. "},{"location":"api/#this-module-has-been-deprecated","title":"This module has been deprecated.","text":""},{"location":"api/#cli-module","title":"CLI
Module","text":"module torrentfile.
cli
Command Line Interface for TorrentFile project.
This module provides the primary command line argument parser for the torrentfile package. The main_script function is automatically invoked when called from command line, and parses accompanying arguments.
Classes Config
\u2014 Class the controls the logging configuration and output settings. TorrentFileHelpFormatter
\u2014 Formatting class for help tips provided by the CLI. Functions execute
(
args
)
(list) \u2014 Execute program with provided list of arguments. execute
(
args
)
(list) \u2014 Execute program with provided list of arguments. main
(
)
\u2014 Initiate main function for CLI script. "},{"location":"api/#functions","title":"Functions","text":"main_script : process command line arguments and run program. activate_logger : turns on debug mode and logging facility.
"},{"location":"api/#classes_1","title":"Classes","text":"Config : class controls logging configuration TorrentFileHelpFormatter : HelpFormatter the command line help message formatter
"},{"location":"api/#recheck-module","title":"Recheck
Module","text":"module torrentfile.
recheck
Module container Checker Class.
The CheckerClass takes a torrentfile and tha path to it\u2019s contents. It will then iterate through every file and directory contained and compare their data to values contained within the torrent file. Completion percentages will be printed to screen for each file and at the end for the torrentfile as a whole.
Classes Checker
\u2014 Check a given file or directory to see if it matches a torrentfile. FeedChecker
\u2014 Validates torrent content. HashChecker
\u2014 Iterate through contents of meta data and verify with file contents. "},{"location":"api/#hasher-module","title":"Hasher
Module","text":"module torrentfile.
hasher
Piece/File Hashers for Bittorrent meta file contents.
Classes Hasher
\u2014 Piece hasher for Bittorrent V1 files. HasherV2
\u2014 Calculate the root hash and piece layers for file contents. HasherHybrid
\u2014 Calculate root and piece hashes for creating hybrid torrent file. FileHasher
\u2014 Calculate root and piece hashes for creating hybrid torrent file. Functions merkle_root
(
blocks
)
(bytes) \u2014 Calculate the merkle root for a seq of sha256 hash digests. "},{"location":"api/#coverage-map","title":"Coverage Map","text":""},{"location":"changelog/","title":"CHANGELOG","text":""},{"location":"changelog/#version-092","title":"Version 0.9.2","text":" Made progress bar now appear in a green color on supported terminals Added progress bar mode #2 new mode allows tracking full process with just 1 progress bar completely reconfigured the ProgMixin class major edits to the way progress bars are tracked in all modules other bug fixes "},{"location":"changelog/#version-091","title":"Version 0.9.1","text":" Clean up and consolidate many cli help strings Removed some of the less popularly used argument shortcuts Edited module and class level docstrings Removed old/useless code. Added --align
cli flag for piece aligning v1 torrents "},{"location":"changelog/#version-090","title":"Version 0.9.0","text":" Updates to Documentation. Updated the main Gif in the readme file Improved unittesting Added new tests for functions in utils module "},{"location":"changelog/#version-0814","title":"Version 0.8.14","text":" Updates and improvements to documentation Updates and improvements to readme file Updates and improvements to docstrings "},{"location":"changelog/#version-0813","title":"Version 0.8.13","text":" Fixed error with command line arg info
not displaying information correctly "},{"location":"changelog/#version-0812","title":"Version 0.8.12","text":" Fixed bug where torrent files could no be read when created without trackers "},{"location":"changelog/#version-0811","title":"Version 0.8.11","text":" added new command line option for magnet subcommand fixed some coverage gaps caused by 0.8.10 fixed bugs introduced by 0.8.10 with magnet URI creation added support for hybrid magnet links added tests for when a magnet link is generated with no trackers at all "},{"location":"changelog/#version-0810","title":"Version 0.8.10","text":" added support for bittorrent v2 magnet links added unit tests for v2 magnet links updated algorithm for human readable byte formatting "},{"location":"changelog/#version-089","title":"Version 0.8.9","text":" fixed bug with the config file parsing added checks to ensure the config file is parsed correctly added checks for applying configfile parameters to torrent properly deprecated and removed the --cwd
command line flag as it is now default behaviour removed unittests testing the --cwd
behaviour "},{"location":"changelog/#version-088","title":"Version 0.8.8","text":" added loading create torrent options from a configuration file added unit tests for configuration functions update documentation with new information about configuration file added CLI options for indicating the use of a configuration file --config
added a CLI option that specifies where to look for config file --config-path
removed interactive -i
mode from cli options as it is now deprecated. "},{"location":"changelog/#version-087","title":"Version 0.8.7","text":" Added the rename subcommand Added unittests for the new subcommand Default command is now \u201ccreate\u201d when user ommits entering subcommand Added unittests for default command Added shortcut command tfile
as an alternative to torrentfile
"},{"location":"changelog/#version-086","title":"Version 0.8.6","text":" Fixed bug with argument parser that allowed for duplicate aliases Added support for python 3.11 Updated CI/CD Workflow Updated documentation Deprecated the -i interactive argument option and added warnings in documentation "},{"location":"changelog/#version-085","title":"Version 0.8.5","text":" Fixed bug with linux platforms not installing a binary cli command Fixed debug logging errors with the rebuild command Improved log message readability Compatability upgrades for torrentfileQt synchronization Added coverage details to documentation folder "},{"location":"changelog/#version-084","title":"Version 0.8.4","text":" Documentation Updates Fixed logging issues with rebuild module. Improved algorithm for rebuild module. Improved testing for rebuild module. "},{"location":"changelog/#version-083","title":"Version 0.8.3","text":" Added the callback mixin to the rebuild module Fix compatability with GUI frontend torrentfileQt. "},{"location":"changelog/#version-082","title":"Version 0.8.2","text":" Rebuild subcommand now checks on a hash by hash basis Fixed coverage issues Added unittests for the rebuild command Fixed bug with torrentfile creation when a file was a perfect power of 2 Reconfigured the rebuild module Reconfigured the rebuild cli flags and arguments "},{"location":"changelog/#version-081","title":"Version 0.8.1","text":" Further improvements to documentation Fixed bug that interrupted the creation process when using gui version Added unittests Improved docstrings and docstring formatting Renamed a couple of methods "},{"location":"changelog/#version-080","title":"Version 0.8.0","text":" overhaul documentation reconfigured CI files and configuration and packaging files Convert to pyproject.toml setuptools packaging info source "},{"location":"changelog/#version-0712","title":"Version 0.7.12","text":" Changed default behavior to save torrent files to cwd edited all unittests to reflect default behavior added deprecation messages for the cli arg and class paramteter last update to version 0.7.x "},{"location":"changelog/#version-0711","title":"Version 0.7.11","text":" Fixed issue with progress bar displaying inaccurate details Other minor bug fixes Updated output for Recheck subcommand for better readability Updated documentation Updated Readme added quiet mode to cli global options -q
Added unit test to fix coverage gaps Fixed warnings created by pylint "},{"location":"changelog/#version-0710","title":"Version 0.7.10","text":" Added rebuild module and subcommand see docs for more info Added documentation entry for rebuild subcommand improved logging messages added unit tests improved and expanded on type hints minor bug fixes "},{"location":"changelog/#version-079","title":"Version 0.7.9","text":" complete rewrite of the recheck procedures Recheck now provides more accuracy and more details improvements to the new custom progressbar changed the cli argument for the progress bar the options are now just 0 and 1 included new unit tests for all new features marked unused functions as deprecated added a new hasher object for v2 and hybrid torrents minor bug fixes and styling changes "},{"location":"changelog/#version-078","title":"Version 0.7.8","text":" more updates to logging major improvements to progress bar removed tqdm as dependency implemented custom progress bar logic new cli argument controlling the progress bar support for pyben 0.3.1 added threading to recheck module added mixins module unit test updates and improvements "},{"location":"changelog/#version-075","title":"Version 0.7.5","text":" updates to logging facility fixed bug in created hybrid torrent files fixed cli when subcomman not chosen doc updates unit test updates and improvements "},{"location":"changelog/#version-072","title":"Version 0.7.2","text":" cleaned up readme and help messages removed useless print statements improved CI tooling and checking minor bug fixes "},{"location":"changelog/#version-071","title":"Version 0.7.1","text":" split CI integration into separate platform specific files added new cli argument --cwd
which changes the default save to location to the current working directory (this will be default in future) added unit tests to cover the new argument Changed license to a the more permissive Apache 2 software license "},{"location":"changelog/#version-070","title":"Version 0.7.0","text":" Fixed issues with logging to file. Finished adding tests for Unicode Support Deprecated some unneccessary code Clean up documentation and README removed config files no longer in use. "},{"location":"changelog/#version-0613","title":"Version 0.6.13","text":" Fixed bug that created a torrent file with no name. Fixed bug that would error if cli path was listed after announce urls Added full unicode support. Added Unittests for new features and bug fixes "},{"location":"changelog/#version-0611","title":"Version 0.6.11","text":" Fixed bug that occured during recheck when file of 0 length is included. Altered Recheck algorithm to process 0 length files. Only effected meta version 2 and hybrid torrent files. Added unittests to cover the situation. "},{"location":"changelog/#version-0610","title":"Version 0.6.10","text":" Updates to documentation Integrated Type hints in source code Updated build and CI process "},{"location":"changelog/#version-069","title":"Version 0.6.9","text":" The \u2013progress flag is now \u2013noprogress Default behavior is to show progress bar use \u2013noprogress to not show added CLI Help format strings added custom CLI help formatter class Titled Help Message section headers Fixed a bunch of error pages created by mkdocs "},{"location":"changelog/#version-068","title":"Version 0.6.8","text":" Documentation for newest features CLI usage examples Improved unittests made progress bar active by default "},{"location":"changelog/#version-067","title":"Version 0.6.7","text":""},{"location":"changelog/#version-066","title":"Version 0.6.6","text":" bug that created faulty Bittorrent V2 meta files in some instances. back to working as it should. "},{"location":"changelog/#version-065","title":"Version 0.6.5","text":" Support for creating Magnet URI\u2019s Added optional progress bar for torrent creation Log File handler CLI args page in documentation verbose and logging bugs multi tracker errors bug "},{"location":"changelog/#version-064","title":"Version 0.6.4","text":" CLI interface add subcommands added interactive mode Re-wrote the recheck module fixed documentation and docstrings linting and testing errors "},{"location":"changelog/#version-063","title":"Version 0.6.3","text":" Fixed Bug that would format list of trackers incorrectly CLI Bug Fixes "},{"location":"changelog/#version-062","title":"Version 0.6.2","text":" Bug fixes Documentation error pages "},{"location":"changelog/#version-060","title":"Version 0.6.0","text":" cli commands alterations debug logging during creation process "},{"location":"changelog/#version-052","title":"Version 0.5.2","text":" Fixed Bug that was adding wrong fields to info dict "},{"location":"changelog/#version-050","title":"Version 0.5.0","text":" Slew of new unit tests Stricter linting features Alternative method of -re-check feature Bug Fixes CLI help formatting errors "},{"location":"changelog/#version-048","title":"Version 0.4.8","text":" Improved Algorithm performance for ReCheck. Additions to documentation. "},{"location":"changelog/#version-047","title":"Version 0.4.7","text":" Fixed A bug that misspelled a field when creating Hybrid torrent files. Re-Check procedure for v2 and hybrid torrent file checking. "},{"location":"changelog/#version-046","title":"Version 0.4.6","text":" CLI Help and Usage Messages. Expanded CLI args. Completely new CheckerClass which replaces old Checker Hooks for GUI or other 3rd party apps to hook into Checking Documentation and Unit tests for new CheckerClass "},{"location":"changelog/#version-045","title":"Version 0.4.5","text":" Documentation and docstrings improvements Better code formating and more detailed docstrings More unit tests. "},{"location":"changelog/#version-042","title":"Version 0.4.2","text":" The ReChecker feature now supports v1, v2, & hybrid .torrent file. Bug in CLI for python < 3.8 "},{"location":"changelog/#version-041","title":"Version 0.4.1","text":" Added tests for hybrid class Added logging features new cli flag to activate debug mode Documentation theme. Fixed Bug that allowed improper piece length values. "},{"location":"changelog/#version-040","title":"Version 0.4.0","text":" Fixed bugs in creating hybrid files. Bug Fix that broke cli. "},{"location":"changelog/#version-030","title":"Version 0.3.0","text":" Added/Improved support for hybrid meta files. Many additions to testing suit including linting and coverage tests. "},{"location":"changelog/#version-028","title":"Version 0.2.8","text":" Styling fixes. Bug Fixes. Prelimenary support for bittorrent hybrid meta files. Bug Fixes "},{"location":"changelog/#version-027","title":"Version 0.2.7","text":" major imporvements to torrentfile-GUI. minor adjustments to this package for integration. Code consolidation Bug Fixes Documentation additions Implemented CI/CD Integration "},{"location":"changelog/#version-023","title":"Version 0.2.3","text":" Bug Fixes Code Style and Formatting Added more unittests "},{"location":"changelog/#version-021","title":"Version 0.2.1","text":" Bittorrent Protocol V2 Support v2 metafile options to cli v2 metafile tests "},{"location":"changelog/#version-017","title":"Version 0.1.7","text":" Docstrings Improvements. Added documentation rederer. Improved readme file. formatting "},{"location":"changelog/#version-012","title":"Version 0.1.2","text":" Added a Command Line Interface Rough Graphical User Interface Minor Bug Fixes Improved unittest coverage "},{"location":"changelog/#version-010","title":"Version 0.1.0","text":" added SHA256 support Feeder class for seemless file switching Fixed the primary entrypoint function. Improved docstrings Bug fixes "},{"location":"changelog/#version-002","title":"Version 0.0.2","text":" Added Unittests added bencode support added hashing support "},{"location":"changelog/#version-001","title":"Version 0.0.1","text":" Initial concept and planning "},{"location":"overview/","title":"torrentfile Manual","text":""},{"location":"overview/#synopsis","title":"Synopsis","text":"torrentfile
is a command line utility for working with Bittorrent files(.torrent). Some of the tools available include creating torrent files, editing portions of a torrent files, checking the integrity or completeness of downloaded torrent contents, displaying details of a torrentfile, generating magnet URLs for torrentfiles, and individual or batch rebuilding of torrent contents into their original directory structure.
torrentfile [options]`<subcommand>` [options] `<args>`\n
-h
displays all relevant command line options and subcommands.
-V
displays program and version.
-v
enables debug mode and outputs a large amount of information to the terminal.
"},{"location":"overview/#sub-commands","title":"Sub-commands","text":""},{"location":"overview/#create","title":"create","text":"alias: c
The create subcommand is used for generating new torrent files. The only required argument is the path(<path>
) to the contents file or directory.
torrentfile create [options]`<path>`\ntorrentfile c [options] `<path>`\n
-a
--announce
Adds the list of url\u2019s that follow to the list of trackers for the newly created torrent file.
Example -t http://url1 https://url2 ...
-p
--private
Indicates that the torrent will be used on a private tracker. Disables multi-tracker protocols and DHT.
--web-seed
Adds the list of urls that follow to the list of web-seed addresses for the newly created torrent file.
--http-seed
Same as the -web-seed
argument except using Hoffman style web-seeds.
-c
--comment
Includes the comment that follows to the metadata saved in the newly created torrent file.
Example: --comment \"Created for MyTrackerExample.com\"
--source
Creates a source field in the info dictionary with the string that follows as the value. Frequently used for cross-seeding on private trackers.
Example: --source MyTrackerExample
--piece-length
Uses the number that follows as the piece-length size for the newly created torrent file. If option isn\u2019t used the program will pick the ideal size. Acceptable values include 14-29 which is interpreted as the number to raise 2 by (e.g. 14 is interpreted as 16384), or any perfect power of 2 greater than or equal to 16 KiB and less than 1 GiB.
Example: --piece-length 14
or --piece-length 16384
.
--meta-version
Use the following number as the Bittorrent version the torrent will be used on. The default is 1.
Options: 1 - Bittorrent v1, 2 - Bittorrent v2, 3 - Bittorrent v1 & v2 Examples: --meta-version 2
, --meta-version 3
-o
--out
Specify the full path to the newly created torrent file. The default is to save it adjacent to the content.
Example: if content is at /home/user/torrents/content
the default would create /home/user/torrents/content.torrent
--progress
--prog
Options (0, 1): No status bar will be shown if 0. Otherwise the default and \u201c1\u201d means progress bar is shown
--config
Load options from a configuration file. default torrentfile.ini
file can use the same long options names used for the command line The current available options that can be marked in the config file include: announce
, source
, comment
, http-seeds
, web-seeds
, piece-length
, ``
Default search paths and priority:
./torrentfile.ini
~/.torrentfile/torrentfile.ini
~/.config/torrentfile.ini
--config-path
Use in combination with --config
to specify path to the configuration file.
Example: torrentfile create --config --config-path ./dir/config.ini <content-path>
Example Configuration File
torrentfile.ini
[config]\nprivate = false\ncomment = example torrent comment\nannounce =\n https://tracker1.net/announce\n https://example.org/2324announce\nhttp-seed = \n https://example.url/path/to/content\nweb-seed =\n ftp://ftp.example.site/content\nmeta-version = 2\npiece-length = 18\nout = /path/to/file/content.torrent\n
"},{"location":"overview/#info","title":"info","text":"alias: i
Display detailed information about a torrentfile such as trackers, size of contents, Bittorrent version, any comments left, date the torrent file was created and more. There is only one positional perameter which is the path to the torrent file and there are no optional arguments.
torrentfile info`<path>`\ntorrentfile i `<path>`\n
/path/to/*.torrent
The relative or absolute path to the torrent file. "},{"location":"overview/#edit","title":"edit","text":"Edit some of the different information detailed in a torrent file. The fields that are editable each have option flags detialed below. Each option identifies the field to edit inside the torrent file and what the new value should be. If an option is not used then its field will be ommited in the newly created torrent file. As such if the file is marked as private and it should remain that way, the -p
option should be used.
alias: e
torrentfile edit [options]`<path>`\ntorrentfile e [options] `<path>`\n
-a
-t
--announce
--tracker
Adds the list of url\u2019s that follow to the list of trackers for the newly created torrent file.
Example -t http://url1 https://url2 ...
-w
--web-seeds
Adds the list of urls that follow to the list of web-seed addresses for the newly created torrent file.
--comment
Includes the comment that follows to the metadata saved in the newly created torrent file.
Example: --comment \"Created for MyTrackerExample.com\"
--source
Creates a source field in the info dictionary with the string that follows as the value. Frequently used for cross-seeding on private trackers.
Example: --source MyTrackerExample
-p
--private
Indicates that the torrent will be used on a private tracker. Disables multi-tracker protocols and DHT.
"},{"location":"overview/#recheck","title":"recheck","text":"This feature is identical to the \u2018recheck\u2019 function provided by torrent clients. It validates that the file/directory contents match those described in the torrent file.
Recheck requires two paths as arguments. The first is the path to a torrent file, and and the second is a path to the file of directory containing the downloaded data from that torrentfile. torrentfile
recursively validates each file with the hashes contained in the torrentfile, and displays the total percentage of the file that matches the information from the torrent file. It is also permitted to use the contents parent directory which can help for batch processing many torrent files.
alias: r
, check
torrentfile recheck <*.torrent> `<contents>`\ntorrentfile r <*.torrent> `<contents>`\n
"},{"location":"overview/#magnet","title":"Magnet","text":"Generate a magnet URL for a torrent file.
alias: m
torrentfile magnet <path/to/*.torrent>\n
"},{"location":"overview/#rebuild","title":"Rebuild","text":"Rebuild individual or batches of torrent contents into the original file structure. The program takes a path to a torrent file or directory containing torrent files, the directory containing the torrent contents, and the destination directory to where the rebuilt torrent content wil be located. The program will recursively traverse the content directory searching for file\u2019s that match one of the meta files and creates copies of the matches to the destination directory. The original files are not effected and any existing files in the target directory will not be overwritten.
alias: build
, b
torrentfile rebuild -m`<metafiles>` -c `<contents>` -d `<destination>`\n
-m
--metafiles
path to a torrent file or a directory containing torrent files for batch processing.
-c
--content
path to where the contents for the torrentfile can be found. If the path is for a directory, the directory will be searched recusively for files that match. For deeply nested directories with lot\u2019s of files in them, this can take time.
-d
-destination
path to a directory where the torrent will be rebuilt. Files will be copied from their original location, not moved.
"},{"location":"usage/","title":"TorrentFile CLI Menu","text":""},{"location":"usage/#help-messages","title":"Help Messages","text":""},{"location":"usage/#main","title":"Main","text":"Usage\n=====\n torrentfile [options] command [command options]\n\nCommand line tools for creating, editing, checking, building and interacting with Bittorrent metainfo files\n
Options -h, \u2013help show this help message and exit -i, \u2013interactive select program options interactively -q, \u2013quiet Turn off all text output. -V, \u2013version show program version and exit Commands create, edit, info, magnet, recheck, rebuild create (c, new) Create a new Bittorrent file.\n edit (e) Edit existing torrent meta file.\n info (i) Show detailed information about a torrent file.\n magnet (m) Generate magnet url from an existing Bittorrent meta file.\n recheck (check) Gives a detailed look at how much of the torrent is available.\n rebuild (build) Re-assemble files obtained from a bittorrent file into the\n appropriate file structure for re-seeding. Read documentation\n for more information, or use cases.\n
"},{"location":"usage/#create","title":"Create","text":"Usage\n=====\n\ntorrentfile [options] create\n [-h] [-a`<url>` [`<url>` ...]] [-p]\n [-s `<source>`] [-m] [-c `<comment>`]\n [-o `<path>`] [--cwd] [--prog PROGRESS]\n [--meta-version `<int>`]\n [--piece-length `<int>`]\n [-w `<url>` [`<url>` ...]]\n [--http-seed `<url>` [`<url>` ...]]\n [`<content>`]\n
Positional Arguments <content>
Path to content file or directory Options -h, \u2013help show this help message and exit -a<url>
[<url>
\u2026], -t <url>
[<url>
\u2026], \u2013announce <url>
[<url>
\u2026], \u2013tracker <url>
[<url>
\u2026] One or more space-seperated torrent tracker url(s). -p, \u2013private Creates private torrent with multi-tracker and DHT turned off. -s<source>
, \u2013source <source>
Add a source string. Useful for cross-seeding. -m, \u2013magnet -c<comment>
, \u2013comment <comment>
Include a comment in file metadata -o<path>
, \u2013out <path>
Explicitly specify the path to write the file. \u2013cwd, \u2013current*deprecated* Saving to current directory is default behaviour \u2013prog PROGRESS, \u2013progress PROGRESS Set the progress bar level. Options = 0, 1 (0) = Do not display progress bar. (1) = Display progress bar.(default) --meta-version`<int>` Bittorrent metafile version.\n Options = 1, 2, 3\n (1) = Bittorrent v1 (Default)\n (2) = Bittorrent v2\n (3) = Bittorrent v1 & v2 hybrid\n\n--piece-length`<int>` (Default: `<blank>`) Number of bytes for per chunk of data transmitted\n by Bittorrent client. Acceptable values include integers 14-26 which\n will be interpreted as a perfect power of 2. e.g. 14 = 16KiB pieces.\n Examples:: [--piece-length 14] [--piece-length 20]\n\n-w`<url>` [`<url>` ...], --web-seed `<url>` [`<url>` ...]\n list of web addresses where torrent data exists (GetRight).\n --http-seed `<url>` [`<url>` ...]\n list of URLs, addresses where content can be found (Hoffman).\n
"},{"location":"usage/#edit","title":"Edit","text":"Usage\n=====\ntorrentfile e [-h] [--tracker`<url>` [`<url>` ...]]\n [--web-seed `<url>` [`<url>` ...]] [--private]\n [--comment `<comment>`] [--source `<source>`]\n <*.torrent>\n
Positional Arguments <*.torrent> path to *.torrent file Optional Arguments -h, \u2013help show this help message and exit \u2013tracker<url>
[<url>
\u2026] Replace current list of tracker/announce urls with one or more space seperated Bittorrent tracker announce url(s). --web-seed`<url>` [`<url>` ...] Replace current list of web-seed urls with one or more space seperated url(s)\n\n--private Make torrent private.\n--comment`<comment>` Replaces any existing comment with `<comment>`\n--source `<source>` Replaces current source with `<source>`\n
"},{"location":"usage/#recheck","title":"Recheck","text":"Usage\n=====\ntorrentfile r [-h] <*.torrent>`<content>`\n
Positional Arguments <*.torrent> path to .torrent file. <content>
path to content file or directory Optional Arguments -h, \u2013help show this help message and exit"},{"location":"usage/#magnet","title":"Magnet","text":"Usage\n=====\ntorrentfile m [-h] <*.torrent>\n
Positional Arguments <*.torrent> Path to Bittorrent meta file. Optional Arguments -h, \u2013help show this help message and exit"},{"location":"Source/","title":"Source Code Modules","text":"torrentfile
is an open source project.
The official source code can be viewed on github at https://github.com/alexpdev/torrentfile.
"},{"location":"Source/#modules","title":"Modules","text":""},{"location":"Source/#cli","title":"cli","text":""},{"location":"Source/#commands","title":"commands","text":""},{"location":"Source/#edit","title":"edit","text":""},{"location":"Source/#hasher","title":"hasher","text":""},{"location":"Source/#interactive","title":"interactive","text":""},{"location":"Source/#mixins","title":"mixins","text":""},{"location":"Source/#rebuild","title":"rebuild","text":""},{"location":"Source/#recheck","title":"recheck","text":""},{"location":"Source/#torrent","title":"torrent","text":""},{"location":"Source/#utils","title":"utils","text":""},{"location":"Source/#version","title":"version","text":""},{"location":"Source/cli/","title":"Cli","text":""},{"location":"Source/cli/#torrentfile.cli","title":"cli
","text":"Command Line Interface for TorrentFile project.
This module provides the primary command line argument parser for the torrentfile package. The main_script function is automatically invoked when called from command line, and parses accompanying arguments.
"},{"location":"Source/cli/#torrentfile.cli--functions","title":"Functions","text":"main_script : process command line arguments and run program. activate_logger : turns on debug mode and logging facility.
"},{"location":"Source/cli/#torrentfile.cli--classes","title":"Classes","text":"Config : class controls logging configuration TorrentFileHelpFormatter : HelpFormatter the command line help message formatter
"},{"location":"Source/cli/#torrentfile.cli.Config","title":"Config
","text":"Class the controls the logging configuration and output settings.
Controls the logging level, or whether to app should operate in quiet mode.
"},{"location":"Source/cli/#torrentfile.cli.Config.activate_logger","title":"activate_logger()
staticmethod
","text":"Activate the builtin logging mechanism when passed debug flag from CLI.
Source code in torrentfile\\cli.py
@staticmethod\ndef activate_logger():\n\"\"\"\n Activate the builtin logging mechanism when passed debug flag from CLI.\n \"\"\"\n logging.basicConfig(level=logging.WARNING)\n logger = logging.getLogger()\n console_handler = logging.StreamHandler(stream=sys.stderr)\n stream_formatter = logging.Formatter(\n \"[%(asctime)s] [%(levelno)s] %(message)s\",\n datefmt=\"%H:%M:%S\",\n style=\"%\",\n )\n console_handler.setFormatter(stream_formatter)\n console_handler.setLevel(logging.DEBUG)\n logger.setLevel(logging.DEBUG)\n logger.addHandler(console_handler)\n logger.debug(\"Debug: ON\")\n toggle_debug_mode(True)\n
"},{"location":"Source/cli/#torrentfile.cli.Config.activate_quiet","title":"activate_quiet()
staticmethod
","text":"Activate quiet mode for the duration of the programs life.
When quiet mode is enabled, no logging, progress or state information is output to the terminal
Source code in torrentfile\\cli.py
@staticmethod\ndef activate_quiet():\n\"\"\"\n Activate quiet mode for the duration of the programs life.\n\n When quiet mode is enabled, no logging, progress or state information\n is output to the terminal\n \"\"\"\n if sys.stdout or sys.stderr:\n sys.stdout = io.StringIO()\n sys.stderr = io.StringIO()\n
"},{"location":"Source/cli/#torrentfile.cli.TorrentFileHelpFormatter","title":"TorrentFileHelpFormatter(prog: str, width: int = 45, max_help_positions: int = 45)
","text":" Bases: HelpFormatter
Formatting class for help tips provided by the CLI.
Subclasses Argparse.HelpFormatter.
Construct HelpFormat class for usage output.
PARAMETER DESCRIPTION prog
Name of the program.
TYPE: str
width
Max width of help message output.
TYPE: int
DEFAULT: 45
max_help_positions
max length until line wrap.
TYPE: int
DEFAULT: 45
Source code in torrentfile\\cli.py
def __init__(self,\n prog: str,\n width: int = 45,\n max_help_positions: int = 45):\n\"\"\"\n Construct HelpFormat class for usage output.\n\n Parameters\n ----------\n prog : str\n Name of the program.\n width : int\n Max width of help message output.\n max_help_positions : int\n max length until line wrap.\n \"\"\"\n super().__init__(prog,\n width=width,\n max_help_position=max_help_positions)\n
"},{"location":"Source/cli/#torrentfile.cli.execute","title":"execute(args: list = None) -> list
","text":"Execute program with provided list of arguments.
If no arguments are given then it defaults to using sys.argv. This is the main entrypoint for the program and command line interface.
PARAMETER DESCRIPTION args
Commandline arguments. default=None
TYPE: list
DEFAULT: None
RETURNS DESCRIPTION list
Depends on what the command line args were.
Source code in torrentfile\\cli.py
def execute(args: list = None) -> list:\n\"\"\"\n Execute program with provided list of arguments.\n\n If no arguments are given then it defaults to using\n sys.argv. This is the main entrypoint for the program\n and command line interface.\n\n Parameters\n ----------\n args : list\n Commandline arguments. default=None\n\n Returns\n -------\n list\n Depends on what the command line args were.\n \"\"\"\n toggle_debug_mode(False)\n if not args:\n if sys.argv[1:]:\n args = sys.argv[1:]\n else:\n args = [\"-h\"]\n\n parser = ArgumentParser(\n \"torrentfile\",\n usage=\"torrentfile <options>\",\n description=(\n \"Command line tool for creating, editing, validating, building \"\n \"and interacting with all versions of Bittorrent files\"),\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n conflict_handler=\"resolve\",\n )\n\n parser.add_argument(\n \"-q\",\n \"--quiet\",\n help=\"Turn off all text output.\",\n dest=\"quiet\",\n action=\"store_true\",\n )\n\n parser.add_argument(\n \"-V\",\n \"--version\",\n action=\"version\",\n version=f\"torrentfile v{version}\",\n help=\"show program version and exit\",\n )\n\n parser.add_argument(\n \"-v\",\n \"--verbose\",\n action=\"store_true\",\n dest=\"debug\",\n help=\"output debug information\",\n )\n\n parser.set_defaults(func=parser.print_help)\n\n subparsers = parser.add_subparsers(\n title=\"Commands\",\n dest=\"command\",\n metavar=\"create, edit, info, magnet, recheck, rebuild, rename\\n\",\n )\n\n create_parser = subparsers.add_parser(\n \"create\",\n help=\"Create a new Bittorrent file.\",\n prefix_chars=\"-\",\n aliases=[\"new\"],\n formatter_class=TorrentFileHelpFormatter,\n )\n\n create_parser.add_argument(\n \"-a\",\n \"--announce\",\n \"--tracker\",\n action=\"store\",\n dest=\"announce\",\n metavar=\"<url>\",\n nargs=\"+\",\n default=[],\n help=\"one or more space-seperated tracker url(s)\",\n )\n\n create_parser.add_argument(\n \"-p\",\n \"--private\",\n action=\"store_true\",\n dest=\"private\",\n help=\"create private torrent\",\n )\n\n create_parser.add_argument(\n \"-s\",\n \"--source\",\n action=\"store\",\n dest=\"source\",\n metavar=\"<source>\",\n help=\"add source field to the metadata\",\n )\n\n create_parser.add_argument(\n \"--config\",\n action=\"store_true\",\n dest=\"config\",\n help=\"\"\"\n Parse torrent information from a config file. Looks in the current\n working directory, or the directory named .torrentfile in the users\n home directory for a torrentfile.ini file. See --config-path option.\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--config-path\",\n action=\"store\",\n metavar=\"<path>\",\n dest=\"config_path\",\n help=\"use in combination with --config to provide config file path\",\n )\n\n create_parser.add_argument(\n \"-m\",\n \"--magnet\",\n action=\"store_true\",\n dest=\"magnet\",\n )\n\n create_parser.add_argument(\n \"-c\",\n \"--comment\",\n action=\"store\",\n dest=\"comment\",\n metavar=\"<comment>\",\n help=\"include a comment in the torrent file metadata\",\n )\n\n create_parser.add_argument(\n \"-o\",\n \"--out\",\n action=\"store\",\n dest=\"outfile\",\n metavar=\"<path>\",\n help=\"path to write torrent file\",\n )\n\n create_parser.add_argument(\n \"--prog\",\n \"--progress\",\n default=\"1\",\n action=\"store\",\n dest=\"progress\",\n metavar=\"<int>\",\n help=\"\"\"\n set the progress bar level\n Options = 0, 1, 2\n (0) = Do not display progress bar.\n (1) = Display progress bar for each file.(default)\n (2) = Display one progress bar for full torrent.\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--meta-version\",\n default=\"1\",\n choices=[\"1\", \"2\", \"3\"],\n action=\"store\",\n dest=\"meta_version\",\n metavar=\"<int>\",\n help=\"\"\"\n bittorrent metafile version\n options = 1, 2, 3\n (1) = Bittorrent v1 (Default)\n (2) = Bittorrent v2\n (3) = Bittorrent v1 & v2 hybrid\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--piece-length\",\n action=\"store\",\n dest=\"piece_length\",\n metavar=\"<int>\",\n help=\"\"\"\n (Default: auto calculated based on total size of content)\n acceptable values include numbers 14-26\n 14 = 16KiB, 20 = 1MiB, 21 = 2MiB etc. Examples:[--piece-length 14]\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--web-seed\",\n action=\"store\",\n dest=\"url_list\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"list of web addresses where torrent data exists (GetRight)\",\n )\n\n create_parser.add_argument(\n \"--http-seed\",\n action=\"store\",\n dest=\"httpseeds\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"list of URLs, addresses where content can be found (Hoffman)\",\n )\n\n create_parser.add_argument(\n \"--align\",\n action=\"store_true\",\n help=(\"Align pieces to file boundaries. \"\n \"This option is ignored when not used with V1 torrents.\"),\n )\n\n create_parser.add_argument(\n \"content\",\n action=\"store\",\n metavar=\"<content>\",\n nargs=\"?\",\n help=\"path to content file or directory\",\n )\n\n create_parser.set_defaults(func=commands.create)\n\n edit_parser = subparsers.add_parser(\n \"edit\",\n help=\"edit torrent file\",\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n edit_parser.add_argument(\n \"metafile\",\n action=\"store\",\n help=\"path to *.torrent file\",\n metavar=\"<*.torrent>\",\n )\n\n edit_parser.add_argument(\n \"--tracker\",\n action=\"store\",\n dest=\"announce\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"replace current trackers with one or more urls\",\n )\n\n edit_parser.add_argument(\n \"--web-seed\",\n action=\"store\",\n dest=\"url_list\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"replace current web-seed with one or more url(s)\",\n )\n\n edit_parser.add_argument(\n \"--http-seed\",\n action=\"store\",\n dest=\"httpseeds\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"replace current http-seed urls with new ones (Hoffman)\",\n )\n\n edit_parser.add_argument(\n \"--private\",\n action=\"store_true\",\n help=\"make torrent private\",\n dest=\"private\",\n )\n\n edit_parser.add_argument(\n \"--comment\",\n help=\"replaces any existing comment with <comment>\",\n metavar=\"<comment>\",\n dest=\"comment\",\n action=\"store\",\n )\n\n edit_parser.add_argument(\n \"--source\",\n action=\"store\",\n dest=\"source\",\n metavar=\"<source>\",\n help=\"replaces current source with <source>\",\n )\n\n edit_parser.set_defaults(func=commands.edit)\n\n info_parser = subparsers.add_parser(\n \"info\",\n help=\"show detailed information about a torrent file\",\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n info_parser.add_argument(\n \"metafile\",\n action=\"store\",\n metavar=\"<*.torrent>\",\n help=\"path to torrent file\",\n )\n\n info_parser.set_defaults(func=commands.info)\n\n magnet_parser = subparsers.add_parser(\n \"magnet\",\n help=\"generate magnet url from an existing torrent file\",\n aliases=[\"m\"],\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n magnet_parser.add_argument(\n \"metafile\",\n action=\"store\",\n help=\"path to torrent file\",\n metavar=\"<*.torrent>\",\n )\n\n magnet_parser.add_argument(\n \"--meta-version\",\n action=\"store\",\n choices=[\"0\", \"1\", \"2\", \"3\"],\n default=\"0\",\n help=\"\"\"\n This option is only relevant for hybrid torrent files.\n Options = 0, 1, 2, 3\n (0) = [default] version is determined automatically\n (1) = create V1 magnet link only\n (2) = create V2 magnet link only\n (3) = create a hybrid magnet link\n \"\"\",\n dest=\"meta_version\",\n metavar=\"<int>\",\n )\n\n magnet_parser.set_defaults(func=commands.get_magnet)\n\n check_parser = subparsers.add_parser(\n \"recheck\",\n help=\"gives a detailed look at how much of the torrent is available\",\n aliases=[\"check\"],\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n check_parser.add_argument(\n \"metafile\",\n action=\"store\",\n metavar=\"<*.torrent>\",\n help=\"path to .torrent file.\",\n )\n\n check_parser.add_argument(\n \"content\",\n action=\"store\",\n metavar=\"<content>\",\n help=\"path to content file or directory\",\n )\n\n check_parser.set_defaults(func=commands.recheck)\n\n rebuild_parser = subparsers.add_parser(\n \"rebuild\",\n help=\"\"\"\n Re-assemble files obtained from a bittorrent file into the\n appropriate file structure for re-seeding. Read documentation\n for more information, or use cases.\n \"\"\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n rebuild_parser.add_argument(\n \"-m\",\n \"--metafiles\",\n action=\"store\",\n metavar=\"<*.torrent>\",\n nargs=\"+\",\n dest=\"metafiles\",\n required=True,\n help=\"path(s) to .torrent file(s)/folder(s) containing .torrent files\",\n )\n\n rebuild_parser.add_argument(\n \"-c\"\n \"--contents\",\n action=\"store\",\n dest=\"contents\",\n nargs=\"+\",\n required=True,\n metavar=\"<contents>\",\n help=\"folders that might contain the source contents needed to rebuld\",\n )\n\n rebuild_parser.add_argument(\n \"-d\",\n \"--destination\",\n action=\"store\",\n dest=\"destination\",\n required=True,\n metavar=\"<destination>\",\n help=\"path to where torrents will be re-assembled\",\n )\n\n rebuild_parser.set_defaults(func=commands.rebuild)\n\n rename_parser = subparsers.add_parser(\n \"rename\",\n help=\"\"\"Rename a torrent file to it's original name provided in the\n metadata/the same name you see in your torrent client.\"\"\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n rename_parser.add_argument(\n \"target\",\n action=\"store\",\n metavar=\"<target>\",\n help=\"path to torrent file\",\n )\n\n rename_parser.set_defaults(func=commands.rename)\n\n all_commands = [\n \"m\",\n \"-h\",\n \"-V\",\n \"new\",\n \"edit\",\n \"info\",\n \"check\",\n \"create\",\n \"magnet\",\n \"rename\",\n \"rebuild\",\n \"recheck\",\n ]\n if not any(i for i in all_commands if i in args):\n start = 0\n while args[start] in [\"-v\", \"-q\"]:\n start += 1\n args.insert(start, \"create\")\n\n args = parser.parse_args(args)\n\n if args.quiet:\n Config.activate_quiet()\n\n elif args.debug:\n Config.activate_logger()\n\n if hasattr(args, \"func\"):\n return args.func(args)\n return args # pragma: nocover\n
"},{"location":"Source/cli/#torrentfile.cli.main","title":"main() -> None
","text":"Initiate main function for CLI script.
Source code in torrentfile\\cli.py
def main() -> None:\n\"\"\"\n Initiate main function for CLI script.\n \"\"\"\n execute()\n
"},{"location":"Source/commands/","title":"Commands","text":""},{"location":"Source/commands/#torrentfile.commands","title":"commands
","text":"The commands module contains the Action Commands executed by the CLI script.
Each function pertains to a command line action/subcommand and drives specific features of the application.
"},{"location":"Source/commands/#torrentfile.commands--functions","title":"Functions","text":" create info edit recheck magnet rebuild find_config_file parse_config_file get_magnet "},{"location":"Source/commands/#torrentfile.commands.create","title":"create(args: Namespace) -> Namespace
","text":"Execute the create CLI sub-command to create a new torrent metafile.
PARAMETER DESCRIPTION args
positional and optional CLI arguments.
TYPE: Namespace
RETURNS DESCRIPTION torrentfile.MetaFile
object containing the path to created metafile and its contents.
Source code in torrentfile\\commands.py
def create(args: Namespace) -> Namespace:\n\"\"\"\n Execute the create CLI sub-command to create a new torrent metafile.\n\n Parameters\n ----------\n args : Namespace\n positional and optional CLI arguments.\n\n Returns\n -------\n torrentfile.MetaFile\n object containing the path to created metafile and its contents.\n \"\"\"\n kwargs = vars(args)\n if args.config:\n path = find_config_file(args)\n parse_config_file(path, kwargs) # pragma: nocover\n\n if args.outfile:\n check_path_writable(args.outfile)\n\n else: # pragma: nocover\n samplepath = os.path.join(os.getcwd(), \".torrent\")\n check_path_writable(samplepath)\n\n logger.debug(\"Creating torrent from %s\", args.content)\n if args.meta_version == \"1\":\n torrent = TorrentFile(**kwargs)\n\n else:\n torrent = TorrentAssembler(**kwargs)\n outfile, meta = torrent.write()\n\n if args.magnet:\n magnet(outfile, version=0)\n\n args.torrent = torrent\n args.kwargs = kwargs\n args.outfile = outfile\n args.meta = meta\n\n print(\"\\nTorrent Save Path: \", os.path.abspath(str(outfile)))\n logger.debug(\"Output path: %s\", str(outfile))\n return args\n
"},{"location":"Source/commands/#torrentfile.commands.edit","title":"edit(args: Namespace) -> str
","text":"Execute the edit CLI sub-command with provided arguments.
Provides functionality that can change the details of a torrentfile that preserves all of the hash piece information so as not to break the torrentfile.
PARAMETER DESCRIPTION args
positional and optional CLI arguments.
TYPE: Namespace
RETURNS DESCRIPTION str
path to edited torrent file.
Source code in torrentfile\\commands.py
def edit(args: Namespace) -> str:\n\"\"\"\n Execute the edit CLI sub-command with provided arguments.\n\n Provides functionality that can change the details of a torrentfile\n that preserves all of the hash piece information so as not to break\n the torrentfile.\n\n Parameters\n ----------\n args : Namespace\n positional and optional CLI arguments.\n\n Returns\n -------\n str\n path to edited torrent file.\n \"\"\"\n metafile = args.metafile\n logger.info(\"Editing %s Meta File\", str(args.metafile))\n\n editargs = {\n \"url-list\": args.url_list,\n \"httpseeds\": args.httpseeds,\n \"announce\": args.announce,\n \"source\": args.source,\n \"private\": args.private,\n \"comment\": args.comment,\n }\n return edit_torrent(metafile, editargs)\n
"},{"location":"Source/commands/#torrentfile.commands.find_config_file","title":"find_config_file(args: Namespace) -> str
","text":"Locate the path to the torrentfile configuration file.
PARAMETER DESCRIPTION args
command line argument values
TYPE: Namespace
RETURNS DESCRIPTION str
path to the configuration file
RAISES DESCRIPTION FileNotFoundError
raised if configuration file not found.
Source code in torrentfile\\commands.py
def find_config_file(args: Namespace) -> str:\n\"\"\"\n Locate the path to the torrentfile configuration file.\n\n Parameters\n ----------\n args : Namespace\n command line argument values\n\n Returns\n -------\n str\n path to the configuration file\n\n Raises\n ------\n FileNotFoundError\n raised if configuration file not found.\n \"\"\"\n path = None\n error_message = \"Could not find configuration file.\"\n if args.config_path:\n if os.path.exists(args.config_path):\n path = args.config_path\n else:\n raise FileNotFoundError(error_message)\n else:\n filename = \"torrentfile.ini\"\n paths = [\n os.path.join(os.getcwd(), filename),\n Path.home() / \".torrentfile\" / filename,\n Path.home() / \".config\" / \".torrentfile\" / filename,\n ]\n for subpath in paths:\n if os.path.exists(subpath):\n path = subpath\n break\n if path is None:\n raise FileNotFoundError(error_message)\n return path\n
"},{"location":"Source/commands/#torrentfile.commands.get_magnet","title":"get_magnet(namespace: Namespace) -> str
","text":"Prepare option parameters for retreiving magnet URI.
PARAMETER DESCRIPTION namespace
command line argument options
TYPE: Namespace
RETURNS DESCRIPTION str
Magnet URI
Source code in torrentfile\\commands.py
def get_magnet(namespace: Namespace) -> str:\n\"\"\"\n Prepare option parameters for retreiving magnet URI.\n\n Parameters\n ----------\n namespace: Namespace\n command line argument options\n\n Returns\n -------\n str\n Magnet URI\n \"\"\"\n metafile = namespace.metafile\n version = int(namespace.meta_version)\n return magnet(metafile, version=version)\n
"},{"location":"Source/commands/#torrentfile.commands.info","title":"info(args: Namespace) -> str
","text":"Show torrent metafile details to user via stdout.
Prints full details of torrent file contents to the terminal in a clean and readable format.
PARAMETER DESCRIPTION args
command line arguements provided by the user.
TYPE: dict
RETURNS DESCRIPTION str
The output printed to the terminal.
Source code in torrentfile\\commands.py
def info(args: Namespace) -> str:\n\"\"\"\n Show torrent metafile details to user via stdout.\n\n Prints full details of torrent file contents to the terminal in\n a clean and readable format.\n\n Parameters\n ----------\n args : dict\n command line arguements provided by the user.\n\n Returns\n -------\n str\n The output printed to the terminal.\n \"\"\"\n metafile = args.metafile\n meta = pyben.load(metafile)\n data = meta[\"info\"]\n del meta[\"info\"]\n\n meta.update(data)\n if \"private\" in meta and meta[\"private\"] == 1:\n meta[\"private\"] = \"True\"\n\n if \"announce-list\" in meta:\n lst = meta[\"announce-list\"]\n meta[\"announce-list\"] = \", \".join([j for i in lst for j in i])\n\n if \"url-list\" in meta:\n meta[\"url-list\"] = \", \".join(meta[\"url-list\"])\n\n if \"httpseeds\" in meta:\n meta[\"httpseeds\"] = \", \".join(meta[\"httpseeds\"])\n\n text = []\n longest = max(len(i) for i in meta.keys())\n\n for key, val in meta.items():\n if key not in [\"pieces\", \"piece layers\", \"files\", \"file tree\"]:\n prefix = longest - len(key) + 1\n string = key + (\" \" * prefix) + str(val)\n text.append(string)\n\n most = max(len(i) for i in text)\n text = [\"-\" * most, \"\\n\"] + text + [\"\\n\", \"-\" * most]\n output = \"\\n\".join(text)\n sys.stdout.write(output)\n sys.stdout.flush()\n return output\n
"},{"location":"Source/commands/#torrentfile.commands.magnet","title":"magnet(metafile: str, version: int = 0) -> str
","text":"Create a magnet URI from a Bittorrent meta file.
PARAMETER DESCRIPTION metafile
path to bittorrent file
TYPE: str
version
version of bittorrent protocol [default=1]
TYPE: int
DEFAULT: 0
RETURNS DESCRIPTION str
Magnet URI
Source code in torrentfile\\commands.py
def magnet(metafile: str, version: int = 0) -> str:\n\"\"\"\n Create a magnet URI from a Bittorrent meta file.\n\n Parameters\n ----------\n metafile : str\n path to bittorrent file\n version: int\n version of bittorrent protocol [default=1]\n\n Returns\n -------\n str\n Magnet URI\n \"\"\"\n if not os.path.exists(metafile):\n raise FileNotFoundError(f\"No Such File {metafile}\")\n meta = pyben.load(metafile)\n info_dict = meta[\"info\"]\n\n magnet = \"magnet:?\"\n bencoded_info = pyben.dumps(info_dict)\n\n v1 = False\n if \"meta version\" not in info_dict or (version in [1, 3, 0]\n and \"pieces\" in info_dict):\n infohash = sha1(bencoded_info).hexdigest() # nosec\n magnet += \"xt=urn:btih:\" + infohash\n v1 = True\n\n if \"meta version\" in info_dict and version != 1:\n infohash = sha256(bencoded_info).hexdigest()\n if v1:\n magnet += \"&\"\n magnet += \"xt=urn:btmh:1220\" + infohash\n\n magnet += \"&dn=\" + quote_plus(info_dict[\"name\"])\n\n if \"announce-list\" in meta:\n announce_args = [\n \"&tr=\" + quote_plus(url) for urllist in meta[\"announce-list\"]\n for url in urllist\n ]\n elif \"announce\" in meta:\n announce_args = [\"&tr=\" + quote_plus(meta[\"announce\"])]\n else:\n announce_args = [\"\"]\n\n trackers = \"\".join(announce_args)\n\n magnet += trackers if trackers != \"&tr=\" else \"\"\n\n logger.info(\"Created Magnet URI %s\", magnet)\n sys.stdout.write(\"\\n\" + magnet + \"\\n\")\n return magnet\n
"},{"location":"Source/commands/#torrentfile.commands.parse_config_file","title":"parse_config_file(path: str, kwargs: dict)
","text":"Parse configuration file for torrent setup details.
PARAMETER DESCRIPTION path
path to configuration file
TYPE: str
kwargs
options from command line arguments
TYPE: dict
Source code in torrentfile\\commands.py
def parse_config_file(path: str, kwargs: dict):\n\"\"\"\n Parse configuration file for torrent setup details.\n\n Parameters\n ----------\n path : str\n path to configuration file\n kwargs : dict\n options from command line arguments\n \"\"\"\n config = configparser.ConfigParser()\n config.read(path)\n\n for key, val in config[\"config\"].items():\n if key.lower() in [\"announce\", \"http-seed\", \"web-seed\", \"tracker\"]:\n val = [i for i in val.split(\"\\n\") if i]\n\n if key.lower() == \"http-seed\":\n kwargs[\"httpseeds\"] = val\n\n elif key.lower() == \"web-seed\":\n kwargs.setdefault(\"url-list\", [])\n kwargs[\"url-list\"] = val\n\n else:\n kwargs[key.lower()] = val\n\n elif key.lower() == \"piece-length\":\n kwargs[\"piece_length\"] = val\n\n elif key.lower() == \"meta-version\":\n kwargs[\"meta_version\"] = val\n\n elif val.lower() == \"true\":\n kwargs[key.lower()] = True\n\n elif val.lower() == \"false\":\n kwargs[key.lower()] = False\n\n else:\n kwargs[key.lower()] = val\n
"},{"location":"Source/commands/#torrentfile.commands.rebuild","title":"rebuild(args: Namespace) -> int
","text":"Attempt to rebuild a torrent based on the a torrent file.
Recursively look through a directory for files that belong in a given torrent file, and rebuild as much of the torrent file as possible. Currently only checks if the filename and file size are a match.
Check file hashes to improve accuracy PARAMETER DESCRIPTION args
command line arguments including the paths neccessary
TYPE: Namespace
RETURNS DESCRIPTION int
total number of content files copied to the rebuild directory
Source code in torrentfile\\commands.py
def rebuild(args: Namespace) -> int:\n\"\"\"\n Attempt to rebuild a torrent based on the a torrent file.\n\n Recursively look through a directory for files that belong in\n a given torrent file, and rebuild as much of the torrent file\n as possible. Currently only checks if the filename and file\n size are a match.\n\n 1. Check file hashes to improve accuracy\n\n Parameters\n ----------\n args : Namespace\n command line arguments including the paths neccessary\n\n Returns\n -------\n int\n total number of content files copied to the rebuild directory\n \"\"\"\n metafiles = args.metafiles\n dest = args.destination\n contents = args.contents\n for path in [*metafiles, *contents]:\n if not os.path.exists(path):\n raise FileNotFoundError(path)\n assembler = Assembler(metafiles, contents, dest)\n return assembler.assemble_torrents()\n
"},{"location":"Source/commands/#torrentfile.commands.recheck","title":"recheck(args: Namespace) -> str
","text":"Execute recheck CLI sub-command.
Checks the piece hashes within a pre-existing torrent file and does a piece by piece check with the contents of a file or directory for completeness and validation.
PARAMETER DESCRIPTION args
positional and optional arguments.
TYPE: Namespace
RETURNS DESCRIPTION str
The percentage of content currently saved to disk.
Source code in torrentfile\\commands.py
def recheck(args: Namespace) -> str:\n\"\"\"\n Execute recheck CLI sub-command.\n\n Checks the piece hashes within a pre-existing torrent file\n and does a piece by piece check with the contents of a file\n or directory for completeness and validation.\n\n Parameters\n ----------\n args : Namespace\n positional and optional arguments.\n\n Returns\n -------\n str\n The percentage of content currently saved to disk.\n \"\"\"\n metafile = args.metafile\n content = args.content\n\n if os.path.isdir(metafile):\n raise ArgumentError(f\"Error: Unable to parse directory {metafile}. \"\n \"Check the order of the parameters.\")\n\n logger.debug(\"Validating %s <---------------> %s contents\", metafile,\n content)\n\n msg = f\"Rechecking {metafile} ...\\n\"\n halfterm = shutil.get_terminal_size().columns / 2\n padding = int(halfterm - (len(msg) / 2)) * \" \"\n sys.stdout.write(padding + msg)\n\n checker = Checker(metafile, content)\n logger.debug(\"Completed initialization of the Checker class\")\n result = checker.results()\n\n message = f\"{content} <- {result}% -> {metafile}\"\n padding = int(halfterm - (len(message) / 2)) * \" \"\n sys.stdout.write(padding + message + \"\\n\")\n sys.stdout.flush()\n return result\n
"},{"location":"Source/commands/#torrentfile.commands.rename","title":"rename(args: Namespace) -> str
","text":"Rename a torrent file to it\u2019s original name found in metadata.
PARAMETER DESCRIPTION args
cli arguments
TYPE: Namespace
RETURNS DESCRIPTION str
renamed file path
Source code in torrentfile\\commands.py
def rename(args: Namespace) -> str:\n\"\"\"\n Rename a torrent file to it's original name found in metadata.\n\n Parameters\n ----------\n args: Namespace\n cli arguments\n\n Returns\n -------\n str\n renamed file path\n \"\"\"\n target = args.target\n if not target or not os.path.exists(target):\n raise FileNotFoundError # pragma: nocover\n meta = pyben.load(target)\n name = meta[\"info\"][\"name\"]\n parent = os.path.dirname(target)\n new_path = os.path.join(parent, name + \".torrent\")\n if os.path.exists(new_path):\n raise FileExistsError # pragma: nocover\n os.rename(target, new_path)\n return new_path\n
"},{"location":"Source/edit/","title":"Edit","text":""},{"location":"Source/edit/#torrentfile.edit","title":"edit
","text":"Edit torrent module.
Provides a facility by which certain properties of a torrent meta file can be edited by the user. The various command line arguments indicate which fields should be edited, and what the new value should be. Depending on what fields are chosen to edit, this command can trigger a new info hash which means the torrent will no longer be able to participate in the same swarm as the original unedited torrent.
"},{"location":"Source/edit/#torrentfile.edit.edit_torrent","title":"edit_torrent(metafile: str, args: dict) -> dict
","text":"Edit the properties and values in a torrent meta file.
PARAMETER DESCRIPTION metafile
path to the torrent meta file.
TYPE: str
args
key value pairs of the properties to be edited.
TYPE: dict
RETURNS DESCRIPTION dict
The edited and nested Meta and info dictionaries.
Source code in torrentfile\\edit.py
def edit_torrent(metafile: str, args: dict) -> dict:\n\"\"\"\n Edit the properties and values in a torrent meta file.\n\n Parameters\n ----------\n metafile : str\n path to the torrent meta file.\n args : dict\n key value pairs of the properties to be edited.\n\n Returns\n -------\n dict\n The edited and nested Meta and info dictionaries.\n \"\"\"\n logger.debug(\"editing torrent file %s\", metafile)\n meta = pyben.load(metafile)\n info = meta[\"info\"]\n filter_empty(args, meta, info)\n\n if \"comment\" in args:\n info[\"comment\"] = args[\"comment\"]\n\n if \"source\" in args:\n info[\"source\"] = args[\"source\"]\n\n if \"private\" in args:\n info[\"private\"] = 1\n\n if \"announce\" in args:\n val = args.get(\"announce\", None)\n if isinstance(val, str):\n vallist = val.split()\n meta[\"announce\"] = vallist[0]\n meta[\"announce-list\"] = [vallist]\n elif isinstance(val, list):\n meta[\"announce\"] = val[0]\n meta[\"announce-list\"] = [val]\n\n if \"url-list\" in args:\n val = args.get(\"url-list\")\n if isinstance(val, str):\n meta[\"url-list\"] = val.split()\n elif isinstance(val, list):\n meta[\"url-list\"] = val\n\n if \"httpseeds\" in args:\n val = args.get(\"httpseeds\")\n if isinstance(val, str):\n meta[\"httpseeds\"] = val.split()\n elif isinstance(val, list):\n meta[\"httpseeds\"] = val\n\n meta[\"info\"] = info\n os.remove(metafile)\n pyben.dump(meta, metafile)\n return meta\n
"},{"location":"Source/edit/#torrentfile.edit.filter_empty","title":"filter_empty(args: dict, meta: dict, info: dict)
","text":"Remove the fields that were not used by the original file creator.
PARAMETER DESCRIPTION args
Editable metafile properties from user.
TYPE: dict
meta
Metafile data dictionary.
TYPE: dict
info
Metafile info dictionary.
TYPE: dict
Source code in torrentfile\\edit.py
def filter_empty(args: dict, meta: dict, info: dict):\n\"\"\"\n Remove the fields that were not used by the original file creator.\n\n Parameters\n ----------\n args : dict\n Editable metafile properties from user.\n meta : dict\n Metafile data dictionary.\n info : dict\n Metafile info dictionary.\n \"\"\"\n for key, val in list(args.items()):\n if val is None:\n del args[key]\n continue\n\n if val == \"\":\n if key in meta:\n del meta[key]\n elif key in info:\n del info[key]\n del args[key]\n logger.debug(\"removeing empty fields %s\", val)\n
"},{"location":"Source/hasher/","title":"Hasher","text":""},{"location":"Source/hasher/#torrentfile.hasher","title":"hasher
","text":"Piece/File Hashers for Bittorrent meta file contents.
"},{"location":"Source/hasher/#torrentfile.hasher.FileHasher","title":"FileHasher(path: str, piece_length: int, progress: int = 1, hybrid: bool = False, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Calculate root and piece hashes for creating hybrid torrent file.
Create merkle tree layers from sha256 hashed 16KiB blocks of contents. With a branching factor of 2, merge layer hashes until blocks equal piece_length bytes for the piece layer, and then the root hash.
PARAMETER DESCRIPTION path
path to target file.
TYPE: str
piece_length
piece length for data chunks.
TYPE: int
hybrid
flag to indicate if it\u2019s a hybrid torrent
TYPE: bool
DEFAULT: False
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Construct Hasher class instances for each file in torrent.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n path: str,\n piece_length: int,\n progress: int = 1,\n hybrid: bool = False,\n progress_bar=None,\n):\n\"\"\"\n Construct Hasher class instances for each file in torrent.\n \"\"\"\n self.path = path\n self.piece_length = piece_length\n self.pieces = []\n self.layer_hashes = []\n self.piece_layer = None\n self.root = None\n self.padding_piece = None\n self.padding_file = None\n self.amount = piece_length // BLOCK_SIZE\n self.end = False\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n size = os.path.getsize(self.path)\n self.progbar = self.get_progress_tracker(size, self.path)\n self.current = open(path, \"rb\")\n self.hybrid = hybrid\n
"},{"location":"Source/hasher/#torrentfile.hasher.FileHasher.__iter__","title":"__iter__()
","text":"Return self
: needed to implement iterator implementation.
Source code in torrentfile\\hasher.py
def __iter__(self):\n\"\"\"Return `self`: needed to implement iterator implementation.\"\"\"\n return self\n
"},{"location":"Source/hasher/#torrentfile.hasher.FileHasher.__next__","title":"__next__() -> bytes
","text":"Calculate layer hashes for contents of file.
RETURNS DESCRIPTION bytes
The layer merckle root hash.
RAISES DESCRIPTION StopIteration
Halts the iterator from progressing
Source code in torrentfile\\hasher.py
def __next__(self) -> bytes:\n\"\"\"\n Calculate layer hashes for contents of file.\n\n Returns\n -------\n bytes\n The layer merckle root hash.\n\n Raises\n ------\n StopIteration\n Halts the iterator from progressing\n \"\"\"\n if self.end:\n self.end = False\n raise StopIteration\n plength = self.piece_length\n blocks = []\n piece = sha1() # nosec\n total = 0\n block = bytearray(BLOCK_SIZE)\n for _ in range(self.amount):\n size = self.current.readinto(block)\n self.progbar.update(size)\n if not size:\n self.end = True\n break\n total += size\n plength -= size\n blocks.append(sha256(block[:size]).digest())\n if self.hybrid:\n piece.update(block[:size])\n if not blocks:\n self._calculate_root()\n raise StopIteration\n if len(blocks) != self.amount:\n padding = self._pad_remaining(len(blocks))\n blocks.extend(padding)\n layer_hash = merkle_root(blocks)\n self.layer_hashes.append(layer_hash)\n self.cb(layer_hash)\n if self.end:\n if self.progress == 1:\n self.progbar.close_out()\n self._calculate_root()\n if self.hybrid:\n if plength > 0:\n self.padding_file = {\n \"attr\": \"p\",\n \"length\": plength,\n \"path\": [\".pad\", str(plength)],\n }\n piece.update(bytes(plength))\n piece = piece.digest()\n self.pieces.append(piece)\n return layer_hash, piece\n return layer_hash\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher","title":"Hasher(paths: list, piece_length: int, align: bool = False, progress: int = 1, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Piece hasher for Bittorrent V1 files.
Takes a sorted list of all file paths, calculates sha1 hash for fixed size pieces of file data from each file seemlessly until the last piece which may be smaller than others.
PARAMETER DESCRIPTION paths
List of files.
TYPE: list
piece_length
Size of chuncks to split the data into.
TYPE: int
align
flag to indicate if the torrent should be piece aligned
TYPE: bool
DEFAULT: False
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Generate hashes of piece length data from filelist contents.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n paths: list,\n piece_length: int,\n align: bool = False,\n progress: int = 1,\n progress_bar=None,\n):\n\"\"\"Generate hashes of piece length data from filelist contents.\"\"\"\n self.piece_length = piece_length\n self.paths = paths\n self.align = align\n self.total = sum(os.path.getsize(i) for i in self.paths)\n self.index = 0\n self.current = open(self.paths[0], \"rb\")\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n file_size = os.path.getsize(self.paths[0])\n self.progbar = self.get_progress_tracker(file_size, self.paths[0])\n logger.debug(\"Hashing %s\", str(self.paths[0]))\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher.__iter__","title":"__iter__()
","text":"Iterate through feed pieces.
RETURNS DESCRIPTION self
Iterator for leaves/hash pieces.
TYPE: iterator
Source code in torrentfile\\hasher.py
def __iter__(self):\n\"\"\"\n Iterate through feed pieces.\n\n Returns\n -------\n self : iterator\n Iterator for leaves/hash pieces.\n \"\"\"\n return self\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher.__next__","title":"__next__() -> bytes
","text":"Generate piece-length pieces of data from input file list.
RETURNS DESCRIPTION bytes
SHA1 hash of the piece extracted.
Source code in torrentfile\\hasher.py
def __next__(self) -> bytes:\n\"\"\"\n Generate piece-length pieces of data from input file list.\n\n Returns\n -------\n bytes\n SHA1 hash of the piece extracted.\n \"\"\"\n while True:\n piece = bytearray(self.piece_length)\n size = self.current.readinto(piece)\n self.progbar.update(size)\n if size == 0:\n if not self.next_file():\n raise StopIteration\n elif size < self.piece_length:\n return self._handle_partial(piece[:size])\n else:\n return sha1(piece).digest() # nosec\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher.next_file","title":"next_file() -> bool
","text":"Seemlessly transition to next file in file list.
RETURNS DESCRIPTION bool
True if there is a next file otherwise False.
TYPE: bool
Source code in torrentfile\\hasher.py
def next_file(self) -> bool:\n\"\"\"\n Seemlessly transition to next file in file list.\n\n Returns\n -------\n bool:\n True if there is a next file otherwise False.\n \"\"\"\n self.index += 1\n if self.progress == 1:\n self.progbar.close_out()\n if self.index < len(self.paths):\n path = self.paths[self.index]\n if self.progress == 1:\n total = os.path.getsize(path)\n self.progbar = self.get_progress_tracker(total, path)\n logger.debug(\"Hashing %s\", str(path))\n self.current.close()\n self.current = open(path, \"rb\")\n return True\n return False\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherHybrid","title":"HasherHybrid(path: str, piece_length: int, progress: int = 1, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Calculate root and piece hashes for creating hybrid torrent file.
Create merkle tree layers from sha256 hashed 16KiB blocks of contents. With a branching factor of 2, merge layer hashes until blocks equal piece_length bytes for the piece layer, and then the root hash.
PARAMETER DESCRIPTION path
path to target file.
TYPE: str
piece_length
piece length for data chunks.
TYPE: int
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Construct Hasher class instances for each file in torrent.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n path: str,\n piece_length: int,\n progress: int = 1,\n progress_bar=None,\n):\n\"\"\"\n Construct Hasher class instances for each file in torrent.\n \"\"\"\n self.path = path\n self.piece_length = piece_length\n self.pieces = []\n self.layer_hashes = []\n self.piece_layer = None\n self.root = None\n self.padding_piece = None\n self.padding_file = None\n self.amount = piece_length // BLOCK_SIZE\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n size = os.path.getsize(self.path)\n self.progbar = self.get_progress_tracker(size, self.path)\n with open(path, \"rb\") as data:\n self.process_file(data)\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherHybrid.process_file","title":"process_file(data: bytearray)
","text":"Calculate layer hashes for contents of file.
PARAMETER DESCRIPTION data
File opened in read mode.
TYPE: BytesIO
Source code in torrentfile\\hasher.py
def process_file(self, data: bytearray):\n\"\"\"\n Calculate layer hashes for contents of file.\n\n Parameters\n ----------\n data : BytesIO\n File opened in read mode.\n \"\"\"\n while True:\n plength = self.piece_length\n blocks = []\n piece = sha1() # nosec\n total = 0\n block = bytearray(BLOCK_SIZE)\n for _ in range(self.amount):\n size = data.readinto(block)\n self.progbar.update(size)\n if not size:\n break\n total += size\n plength -= size\n blocks.append(sha256(block[:size]).digest())\n piece.update(block[:size])\n if not blocks:\n break\n if len(blocks) != self.amount:\n padding = self._pad_remaining(len(blocks))\n blocks.extend(padding)\n layer_hash = merkle_root(blocks)\n self.cb(layer_hash)\n self.layer_hashes.append(layer_hash)\n if plength > 0:\n self.padding_file = {\n \"attr\": \"p\",\n \"length\": plength,\n \"path\": [\".pad\", str(plength)],\n }\n piece.update(bytes(plength))\n self.pieces.append(piece.digest()) # nosec\n if self.progress == 1:\n self.progbar.close_out()\n self._calculate_root()\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherV2","title":"HasherV2(path: str, piece_length: int, progress: int = 1, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Calculate the root hash and piece layers for file contents.
Iterates over 16KiB blocks of data from given file, hashes the data, then creates a hash tree from the individual block hashes until size of hashed data equals the piece-length. Then continues the hash tree until root hash is calculated.
PARAMETER DESCRIPTION path
Path to file.
TYPE: str
piece_length
Size of layer hashes pieces.
TYPE: int
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Calculate and store hash information for specific file.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n path: str,\n piece_length: int,\n progress: int = 1,\n progress_bar=None,\n):\n\"\"\"\n Calculate and store hash information for specific file.\n \"\"\"\n self.path = path\n self.root = None\n self.piece_layer = None\n self.layer_hashes = []\n self.piece_length = piece_length\n self.num_blocks = piece_length // BLOCK_SIZE\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n size = os.path.getsize(self.path)\n self.progbar = self.get_progress_tracker(size, self.path)\n with open(self.path, \"rb\") as fd:\n self.process_file(fd)\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherV2.process_file","title":"process_file(fd: str)
","text":"Calculate hashes over 16KiB chuncks of file content.
PARAMETER DESCRIPTION fd
Opened file in read mode.
TYPE: BytesIO
Source code in torrentfile\\hasher.py
def process_file(self, fd: str):\n\"\"\"\n Calculate hashes over 16KiB chuncks of file content.\n\n Parameters\n ----------\n fd : BytesIO\n Opened file in read mode.\n \"\"\"\n while True:\n blocks = []\n leaf = bytearray(BLOCK_SIZE)\n # generate leaves of merkle tree\n\n for _ in range(self.num_blocks):\n size = fd.readinto(leaf)\n if not size:\n break\n self.progbar.update(size)\n blocks.append(sha256(leaf[:size]).digest())\n\n # blocks is empty mean eof\n if not blocks:\n break\n if len(blocks) != self.num_blocks:\n # when size of file doesn't fill the last block\n # when the file contains multiple pieces\n remaining = self.num_blocks - len(blocks)\n if not self.layer_hashes:\n # when the there is only one block for file\n power2 = next_power_2(len(blocks))\n remaining = power2 - len(blocks)\n\n # pad the the rest with zeroes to fill remaining space.\n padding = [bytes(32) for _ in range(remaining)]\n blocks.extend(padding)\n # calculate the root hash for the merkle tree up to piece-length\n\n layer_hash = merkle_root(blocks)\n self.cb(layer_hash)\n self.layer_hashes.append(layer_hash)\n if self.progress == 1:\n self.progbar.close_out()\n self._calculate_root()\n
"},{"location":"Source/hasher/#torrentfile.hasher.merkle_root","title":"merkle_root(blocks: list) -> bytes
","text":"Calculate the merkle root for a seq of sha256 hash digests.
PARAMETER DESCRIPTION blocks
a sequence of sha256 layer hashes.
TYPE: list
RETURNS DESCRIPTION bytes
the sha256 root hash of the merkle tree.
Source code in torrentfile\\hasher.py
def merkle_root(blocks: list) -> bytes:\n\"\"\"\n Calculate the merkle root for a seq of sha256 hash digests.\n\n Parameters\n ----------\n blocks : list\n a sequence of sha256 layer hashes.\n\n Returns\n -------\n bytes\n the sha256 root hash of the merkle tree.\n \"\"\"\n if blocks:\n while len(blocks) > 1:\n blocks = [\n sha256(x + y).digest() for x, y in zip(*[iter(blocks)] * 2)\n ]\n return blocks[0]\n return blocks\n
"},{"location":"Source/interactive/","title":"Interactive","text":""},{"location":"Source/interactive/#torrentfile.interactive","title":"interactive
","text":"Module contains the procedures used for Interactive Mode.
"},{"location":"Source/interactive/#torrentfile.interactive--this-module-has-been-deprecated","title":"This module has been deprecated.","text":""},{"location":"Source/interactive/#torrentfile.interactive.InteractiveCreator","title":"InteractiveCreator()
","text":"Class namespace for interactive program options.
@Deprecated
Initialize interactive meta file creator dialog.
@Deprecated
Source code in torrentfile\\interactive.py
def __init__(self):\n\"\"\"\n Initialize interactive meta file creator dialog.\n\n @Deprecated\n \"\"\"\n self.kwargs = {\n \"announce\": None,\n \"url_list\": None,\n \"private\": None,\n \"source\": None,\n \"comment\": None,\n \"piece_length\": None,\n \"outfile\": None,\n \"path\": None,\n \"httpseeds\": None,\n }\n self.outfile, self.meta = self.get_props()\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveCreator.get_props","title":"get_props()
","text":"Gather details for torrentfile from user.
@Deprecated
Source code in torrentfile\\interactive.py
def get_props(self):\n\"\"\"\n Gather details for torrentfile from user.\n\n @Deprecated\n \"\"\"\n piece_length = get_input(\"Piece Length (empty=auto): \",\n lambda x: x.isdigit())\n\n self.kwargs[\"piece_length\"] = piece_length\n announce = get_input(\"Tracker list (empty): \",\n lambda x: isinstance(x, str))\n\n if announce:\n self.kwargs[\"announce\"] = announce.split()\n\n url_list = get_input(\"Web Seed {GetRight} list (empty): \",\n lambda x: isinstance(x, str))\n\n httpseeds = get_input(\"Web Seed {Hoffman} list (empty): \",\n lambda x: isinstance(x, str))\n\n if url_list:\n self.kwargs[\"url_list\"] = url_list.split()\n if httpseeds:\n self.kwargs[\"httpseeds\"] = httpseeds.split()\n comment = get_input(\"Comment (empty): \", None)\n\n if comment:\n self.kwargs[\"comment\"] = comment\n source = get_input(\"Source (empty): \", None)\n\n if source:\n self.kwargs[\"source\"] = source\n\n private = get_input(\"Private Torrent? {Y/N}: (N)\",\n lambda x: x in \"yYnN\")\n\n if private and private.lower() == \"y\":\n self.kwargs[\"private\"] = 1\n\n contents = get_input(\"Content Path: \", os.path.exists)\n self.kwargs[\"path\"] = contents\n\n outfile = get_input(\n f\"Output Path ({contents}.torrent): \",\n lambda x: os.path.exists(os.path.dirname(x)),\n )\n\n if outfile:\n self.kwargs[\"outfile\"] = outfile\n\n meta_version = get_input(\"Meta Version {1,2,3}: (1)\",\n lambda x: x in \"123\")\n\n showcenter(f\"creating {outfile}\")\n\n if meta_version == \"3\":\n torrent = TorrentFileHybrid(**self.kwargs)\n elif meta_version == \"2\":\n torrent = TorrentFileV2(**self.kwargs)\n else:\n torrent = TorrentFile(**self.kwargs)\n return torrent.write()\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor","title":"InteractiveEditor(metafile: str)
","text":"Interactive dialog class for torrent editing.
@Deprecated
Initialize the Interactive torrent editor guide.
@Deprecated
PARAMETER DESCRIPTION metafile
user input string identifying the path to a torrent meta file.
TYPE: str
Source code in torrentfile\\interactive.py
def __init__(self, metafile: str):\n\"\"\"\n Initialize the Interactive torrent editor guide.\n\n @Deprecated\n\n Parameters\n ----------\n metafile : str\n user input string identifying the path to a torrent meta file.\n \"\"\"\n self.metafile = metafile\n self.meta = pyben.load(metafile)\n self.info = self.meta[\"info\"]\n\n self.args = {\n \"url-list\": self.meta.get(\"url-list\", None),\n \"httpseeds\": self.meta.get(\"httpseeds\", None),\n \"announce\": self.meta.get(\"announce-list\", None),\n \"source\": self.info.get(\"source\", None),\n \"private\": self.info.get(\"private\", None),\n \"comment\": self.info.get(\"comment\", None),\n }\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor.edit_props","title":"edit_props()
","text":"Loop continuosly for edits until user signals DONE.
@Deprecated
Source code in torrentfile\\interactive.py
def edit_props(self):\n\"\"\"\n Loop continuosly for edits until user signals DONE.\n\n @Deprecated\n \"\"\"\n while True:\n showcenter(\"Choose the number for a propert the needs editing.\"\n \"Enter DONE when all editing has been completed.\")\n\n props = {\n 1: \"comment\",\n 2: \"source\",\n 3: \"private\",\n 4: \"tracker\",\n 5: \"web-seed\",\n 6: \"httpseeds\",\n }\n\n args = {\n 1: \"comment\",\n 2: \"source\",\n 3: \"private\",\n 4: \"announce\",\n 5: \"url-list\",\n 6: \"httpseeds\",\n }\n\n txt = \", \".join((str(k) + \": \" + v) for k, v in props.items())\n prop = get_input(txt)\n if prop.lower() == \"done\":\n break\n\n if prop.isdigit() and 0 < int(prop) < 6:\n key = props[int(prop)]\n key2 = args[int(prop)]\n val = self.args.get(key2)\n showtext(\n \"Enter new property value or leave empty for no value.\")\n response = get_input(f\"{key.title()} ({val}): \")\n self.sanatize_response(key2, response)\n\n else:\n showtext(\"Invalid input: Try again.\")\n edit_torrent(self.metafile, self.args)\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor.sanatize_response","title":"sanatize_response(key, response)
","text":"Convert the input data into a form recognizable by the program.
@ Deprecated
PARAMETER DESCRIPTION key
name of the property and attribute being eddited.
TYPE: str
response
User input value the property is being edited to.
TYPE: str
Source code in torrentfile\\interactive.py
def sanatize_response(self, key, response):\n\"\"\"\n Convert the input data into a form recognizable by the program.\n\n @ Deprecated\n\n Parameters\n ----------\n key : str\n name of the property and attribute being eddited.\n response : str\n User input value the property is being edited to.\n \"\"\"\n if key in [\"announce\", \"url-list\", \"httpseeds\"]:\n val = response.split()\n else:\n val = response\n self.args[key] = val\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor.show_current","title":"show_current()
","text":"Display the current met file information to screen.
@Deprecated
Source code in torrentfile\\interactive.py
def show_current(self):\n\"\"\"\n Display the current met file information to screen.\n\n @Deprecated\n \"\"\"\n out = \"Current properties and values:\\n\"\n longest = max(len(label) for label in self.args) + 3\n for key, val in self.args.items():\n txt = (key.title() + \":\").ljust(longest) + str(val)\n out += f\"\\t{txt}\\n\"\n showtext(out)\n
"},{"location":"Source/interactive/#torrentfile.interactive.create_torrent","title":"create_torrent()
","text":"Create new torrent file interactively.
@Deprecated
Source code in torrentfile\\interactive.py
def create_torrent():\n\"\"\"\n Create new torrent file interactively.\n\n @Deprecated\n \"\"\"\n showcenter(\"Create Torrent\")\n showtext(\n \"\\nEnter values for each of the options for the torrent creator, \"\n \"or leave blank for program defaults.\\nSpaces are considered item \"\n \"seperators for options that accept a list of values.\\nValues \"\n \"enclosed in () indicate the default value, while {} holds all \"\n \"valid choices available for the option.\\n\\n\")\n creator = InteractiveCreator()\n return creator\n
"},{"location":"Source/interactive/#torrentfile.interactive.edit_action","title":"edit_action()
","text":"Edit the editable values of the torrent meta file.
@Deprecated
Source code in torrentfile\\interactive.py
def edit_action():\n\"\"\"\n Edit the editable values of the torrent meta file.\n\n @Deprecated\n \"\"\"\n showcenter(\"Edit Torrent\")\n metafile = get_input(\"Metafile(.torrent): \", os.path.exists)\n dialog = InteractiveEditor(metafile)\n dialog.show_current()\n dialog.edit_props()\n
"},{"location":"Source/interactive/#torrentfile.interactive.get_input","title":"get_input(*args: tuple)
","text":"Determine appropriate input function to call.
@Deprecated
PARAMETER DESCRIPTION *args
Arbitrary number of args to pass to next function
TYPE: tuple
DEFAULT: ()
RETURNS DESCRIPTION str
The results of the function call.
Source code in torrentfile\\interactive.py
def get_input(*args: tuple): # pragma: no cover\n\"\"\"\n Determine appropriate input function to call.\n\n @Deprecated\n\n Parameters\n ----------\n *args : tuple\n Arbitrary number of args to pass to next function\n\n Returns\n -------\n str\n The results of the function call.\n \"\"\"\n if len(args) == 2:\n return _get_input_loop(*args)\n return _get_input(*args)\n
"},{"location":"Source/interactive/#torrentfile.interactive.recheck_torrent","title":"recheck_torrent()
","text":"Check torrent download completed percentage.
@Deprecated
Source code in torrentfile\\interactive.py
def recheck_torrent():\n\"\"\"\n Check torrent download completed percentage.\n\n @Deprecated\n \"\"\"\n showcenter(\"Check Torrent\")\n msg = \"Enter path to torrent contents, and corresponding torrent metafile.\"\n showtext(msg)\n metafile = get_input(\"Conent Path (downloads/complete/torrentname):\",\n os.path.exists)\n contents = get_input(\"Metafile (*.torrent): \", os.path.exists)\n checker = Checker(metafile, contents)\n results = checker.results()\n showtext(f\"Completion for {metafile} is {results}%\")\n return results\n
"},{"location":"Source/interactive/#torrentfile.interactive.select_action","title":"select_action()
","text":"Operate TorrentFile program interactively through terminal.
DEPRECATION WARNING: The interactive CLI feature will be deprecated in the future.
Source code in torrentfile\\interactive.py
def select_action():\n\"\"\"\n Operate TorrentFile program interactively through terminal.\n\n DEPRECATION WARNING: The interactive CLI feature will be deprecated\n in the future.\n \"\"\"\n showcenter(\"TorrentFile: Starting Interactive Mode\")\n showcenter(\"DEPRECATION WARNING: The interactive feature will be\"\n \"deprecated in the near future.\")\n action = get_input(\"Enter the action you wish to perform.\\n\"\n \"Action ( Create (c) | Edit (e) | Recheck (r) ): \")\n action = action.lower()\n\n if \"create\" in action or action == \"c\":\n return create_torrent()\n\n if \"check\" in action or action == \"r\":\n return recheck_torrent()\n\n if \"edit\" in action or action == \"e\":\n return edit_action()\n print(\"Unable to recognize input. Please try again.\") # pragma: nocover\n return select_action() # pragma: nocover\n
"},{"location":"Source/interactive/#torrentfile.interactive.showcenter","title":"showcenter(txt: str)
","text":"Print text to screen in the center position of the terminal.
@Deprecated
PARAMETER DESCRIPTION txt
the preformated message to send to stdout.
TYPE: str
Source code in torrentfile\\interactive.py
def showcenter(txt: str):\n\"\"\"\n Print text to screen in the center position of the terminal.\n\n @Deprecated\n\n Parameters\n ----------\n txt : str\n the preformated message to send to stdout.\n \"\"\"\n termlen = shutil.get_terminal_size().columns\n padding = \" \" * int(((termlen - len(txt)) / 2))\n string = \"\".join([\"\\n\", padding, txt, \"\\n\"])\n showtext(string)\n
"},{"location":"Source/interactive/#torrentfile.interactive.showtext","title":"showtext(txt)
","text":"Print contents of txt to screen.
@Deprecated
PARAMETER DESCRIPTION txt
text to print to terminal.
TYPE: str
Source code in torrentfile\\interactive.py
def showtext(txt):\n\"\"\"\n Print contents of txt to screen.\n\n @Deprecated\n\n Parameters\n ----------\n txt : str\n text to print to terminal.\n \"\"\"\n sys.stdout.write(txt)\n
"},{"location":"Source/mixins/","title":"Mixins","text":""},{"location":"Source/mixins/#torrentfile.mixins","title":"mixins
","text":"Collection of classes that can be used as Mixins with other base classes.
Classes such as TorrentFile, TorrentFilev2, and all Hasher classes can use the progress bar mixin. And any class is eligible to use the callback mixin.
"},{"location":"Source/mixins/#torrentfile.mixins.CbMixin","title":"CbMixin
","text":"Mixin class to set a callback hook during procedure.
"},{"location":"Source/mixins/#torrentfile.mixins.CbMixin.cb","title":"cb(*args, **kwargs)
classmethod
","text":"Do nothing.
Source code in torrentfile\\mixins.py
@classmethod\ndef cb(cls, *args, **kwargs):\n\"\"\"Do nothing.\"\"\"\n
"},{"location":"Source/mixins/#torrentfile.mixins.CbMixin.set_callback","title":"set_callback(func)
classmethod
","text":"Assign a callback to the Hashing class.
PARAMETER DESCRIPTION func
the callback function
TYPE: Callable
Source code in torrentfile\\mixins.py
@classmethod\ndef set_callback(cls, func):\n\"\"\"\n Assign a callback to the Hashing class.\n\n Parameters\n ----------\n func : Callable\n the callback function\n \"\"\"\n cls.cb = func # pragma: nocover\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin","title":"ProgMixin
","text":"Progress bar mixin class.
Displays progress of hashing individual files, usefull when hashing really big files.
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin.NoProg","title":"NoProg
","text":"Stand-in object for when progress mode is set to 0.
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin.NoProg.update","title":"update(value)
","text":"Return the value.
PARAMETER DESCRIPTION value
the input and output
RETURNS DESCRIPTION int
same as input
Source code in torrentfile\\mixins.py
def update(self, value):\n\"\"\"\n Return the value.\n\n Parameters\n ----------\n value: int\n the input and output\n\n Returns\n -------\n int :\n same as input\n \"\"\"\n return value\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin.get_progress_tracker","title":"get_progress_tracker(total: int, message: str)
","text":"Return the progress bar object for external management.
PARAMETER DESCRIPTION total
total size to track
TYPE: int
message
prompt message for what is being tracked
TYPE: str
RETURNS DESCRIPTION ProgressBar
progress bar object instance
Source code in torrentfile\\mixins.py
def get_progress_tracker(self, total: int, message: str):\n\"\"\"Return the progress bar object for external management.\n\n Parameters\n ----------\n total: int\n total size to track\n message: str\n prompt message for what is being tracked\n\n Returns\n -------\n ProgressBar\n progress bar object instance\n \"\"\"\n if total < 0:\n return self.NoProg()\n return ProgressBar.new(total, message)\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar","title":"ProgressBar(total: int, title: str, length: int, unit: str, start: int)
","text":"Holds the state and details of the terminal progress bars.
PARAMETER DESCRIPTION total
the total amount to be accumulated.
TYPE: int
title
the subject of the progress tracker
TYPE: str
length
the width of the progress bar
TYPE: int
unit
the text representation incremented
TYPE: str
start
column where the progress bar should be drawn
TYPE: int
Construct the progress bar object and store state of it\u2019s properties.
Source code in torrentfile\\mixins.py
def __init__(self, total: int, title: str, length: int, unit: str,\n start: int):\n\"\"\"\n Construct the progress bar object and store state of it's properties.\n \"\"\"\n debug_is_on()\n self.total = total\n self.start = start\n self.length = length\n self.fill = chr(9608)\n self.empty = chr(9617)\n self.state = 0\n self.unit = unit\n self.show_total = total\n if not unit:\n self.unit = \"\" # pragma: nocover\n elif unit == \"bytes\":\n if self.total > 1_000_000_000:\n self.show_total = self.total / (2**30)\n self.unit = \"GiB\"\n elif self.total > 1_000_000:\n self.show_total = self.total / 1048576\n self.unit = \"MiB\"\n elif self.total > 10000:\n self.show_total = self.total / 1024\n self.unit = \"KiB\"\n self.suffix = f\"/{self.show_total:.02f} {self.unit}\"\n title = str(title)\n if len(title) > start:\n title = title[:start - 1] # pragma: nocover\n padding = (start - len(title)) * \" \"\n self.prefix = \"\".join([title, padding])\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.close_out","title":"close_out()
","text":"Finalize the last bits of progress bar.
Increment the terminal by one line leaving the progress bar in place, and deleting the progress bar object to clear a space for the next one.
Source code in torrentfile\\mixins.py
def close_out(self):\n\"\"\"\n Finalize the last bits of progress bar.\n\n Increment the terminal by one line leaving the progress bar in place,\n and deleting the progress bar object to clear a space for the next one.\n \"\"\"\n sys.stdout.flush()\n sys.stdout.write(\"\\n\")\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.get_progress","title":"get_progress() -> str
","text":"Return the size of the filled portion of the progress bar.
RETURNS DESCRIPTION str
the progress bar characters
TYPE: str
Source code in torrentfile\\mixins.py
def get_progress(self) -> str:\n\"\"\"\n Return the size of the filled portion of the progress bar.\n\n Returns\n -------\n str :\n the progress bar characters\n \"\"\"\n if self.state >= self.total:\n fill = self.length\n else:\n fill = math.ceil((self.state / self.total) * self.length)\n empty = self.length - fill\n contents = (self.fill * fill) + (self.empty * empty)\n pbar = [\"|\", green(contents), \"| \"]\n if self.unit == \"GiB\":\n state = self.state / (2**30)\n elif self.unit == \"MiB\":\n state = self.state / 1048576\n elif self.unit == \"KiB\":\n state = self.state / 1024\n else:\n state = self.state\n pbar.append(f\"{state:.02f}\")\n return \"\".join(pbar)\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.new","title":"new(total: int, path: str, length: int = 50, unit: str = 'bytes')
classmethod
","text":"Generate a new progress bar for the given file path.
PARAMETER DESCRIPTION total
the total amount of units accumulating towards.
TYPE: int
path
path to file being hashed.
TYPE: str
length
the number of characters of the actual progress bar.
TYPE: int
DEFAULT: 50
unit
the text representation of the value being measured.
TYPE: str
DEFAULT: 'bytes'
Source code in torrentfile\\mixins.py
@classmethod\ndef new(cls, total: int, path: str, length: int = 50, unit: str = \"bytes\"):\n\"\"\"\n Generate a new progress bar for the given file path.\n\n Parameters\n ----------\n total : int\n the total amount of units accumulating towards.\n path : str\n path to file being hashed.\n length : int\n the number of characters of the actual progress bar.\n unit : str\n the text representation of the value being measured.\n \"\"\"\n title = path\n width = shutil.get_terminal_size().columns\n if len(str(title)) >= width // 2:\n parts = list(Path(title).parts)\n while (len(\"//\".join(parts)) > (width // 2)) and (len(parts) > 0):\n del parts[0]\n if parts:\n title = os.path.join(*parts)\n else:\n title = os.path.basename(path) # pragma: nocover\n length = min(length, width // 2)\n start = width - int(length * 1.5)\n return cls(total, title, length, unit, start)\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.update","title":"update(val: int)
","text":"Update progress bar.
Using the value provided, increment the progress bar by that value.
PARAMETER DESCRIPTION val
the number of bytes count the progress bar should increase.
TYPE: int
Source code in torrentfile\\mixins.py
def update(self, val: int):\n\"\"\"\n Update progress bar.\n\n Using the value provided, increment the progress bar by that value.\n\n Parameters\n ----------\n val : int\n the number of bytes count the progress bar should increase.\n \"\"\"\n self.state += val\n pbar = self.get_progress()\n output = f\"{self.prefix}{pbar}{self.suffix}\\r\"\n sys.stdout.write(output)\n sys.stdout.flush()\n
"},{"location":"Source/mixins/#torrentfile.mixins.waiting","title":"waiting(msg: str, flag: list, timeout: int = 20)
","text":"Show loading message while thread completes processing.
PARAMETER DESCRIPTION msg
Message string printed before the progress bar
TYPE: str
flag
Once flag is filled exit loop
TYPE: list
timeout
max amount of time to run the function.
TYPE: int
DEFAULT: 20
Source code in torrentfile\\mixins.py
def waiting(msg: str, flag: list, timeout: int = 20):\n\"\"\"\n Show loading message while thread completes processing.\n\n Parameters\n ----------\n msg : str\n Message string printed before the progress bar\n flag : list\n Once flag is filled exit loop\n timeout : int\n max amount of time to run the function.\n \"\"\"\n then = time.time()\n codes, fill = list(range(9617, 9620)), chr(9619)\n size = idx = 0\n total = shutil.get_terminal_size().columns - len(msg) - 20\n\n def output(text: str):\n\"\"\"\n Print parameter message to the console.\n\n Parameters\n ----------\n text : str\n output message\n \"\"\"\n sys.stdout.write(text)\n sys.stdout.flush()\n\n output(\"\\n\")\n time.sleep(0.16)\n while len(flag) == 0:\n time.sleep(0.16)\n filled = (fill * size) + chr(codes[idx]) + (\" \" * (total - size))\n output(f\"{msg}: {filled}\\r\")\n idx = idx + 1 if idx + 1 < len(codes) else 0\n size = size + 1 if size < total else 0\n if time.time() - then > timeout:\n break\n output(\"\\n\")\n
"},{"location":"Source/rebuild/","title":"Rebuild","text":""},{"location":"Source/rebuild/#torrentfile.rebuild","title":"rebuild
","text":"Clases and functions for the rebuild or reassemble subcommand.
Re-assemble a torrent into the propper directory structure as indicated by a torrent meta file, and validate the contents of each file allong the way. Displays a progress bar for each torrent.
"},{"location":"Source/rebuild/#torrentfile.rebuild.Assembler","title":"Assembler(metafiles: list, contents: list, dest: str)
","text":" Bases: CbMixin
Does most of the work in attempting the structure of torrentfiles.
Requires three paths as arguments. - torrent metafile or directory containing multiple meta files - directory containing the contents of meta file - directory where torrents will be re-assembled
Reassemble given torrent file from given cli arguments.
Rebuild metafiles and contents into their original directory structure as much as possible in the destination directory. Takes two paths as parameters, - file or directory containing 1 or more torrent meta files - path to where the contents are belived to be located.
PARAMETER DESCRIPTION metafiles
path to torrent metafile or directory containing torrent metafiles.
TYPE: str
contents
path to content or directory containing content that belongs to torrentfile.
TYPE: str
dest
path to the directory where rebuild will take place.
TYPE: str
Source code in torrentfile\\rebuild.py
def __init__(self, metafiles: list, contents: list, dest: str):\n\"\"\"\n Reassemble given torrent file from given cli arguments.\n\n Rebuild metafiles and contents into their original directory\n structure as much as possible in the destination directory.\n Takes two paths as parameters,\n - file or directory containing 1 or more torrent meta files\n - path to where the contents are belived to be located.\n\n Parameters\n ----------\n metafiles : str\n path to torrent metafile or directory containing torrent metafiles.\n contents : str\n path to content or directory containing content that belongs to\n torrentfile.\n dest: str\n path to the directory where rebuild will take place.\n \"\"\"\n Metadata.set_callback(self._callback)\n self.counter = 0\n self._lastlog = None\n self.contents = contents\n self.dest = dest\n self.meta_paths = metafiles\n self.metafiles = self._get_metafiles()\n filenames = set()\n for meta in self.metafiles:\n filenames |= meta.filenames\n self.filemap = _index_contents(self.contents, filenames)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Assembler.assemble_torrents","title":"assemble_torrents()
","text":"Assemble collection of torrent files into original structure.
RETURNS DESCRIPTION int
number of files copied
Source code in torrentfile\\rebuild.py
def assemble_torrents(self):\n\"\"\"\n Assemble collection of torrent files into original structure.\n\n Returns\n -------\n int\n number of files copied\n \"\"\"\n for metafile in self.metafiles:\n logger.info(\"#%s Searching contents for %s\", self.counter,\n metafile.name)\n self.rebuild(metafile)\n return self.counter\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Assembler.rebuild","title":"rebuild(metafile: Metadata) -> None
","text":"Build the torrent file structure from contents of directory.
Traverse contents dir and compare discovered files with files listed in torrent metadata and copy the matches to the destination directory respecting folder structures along the way.
Source code in torrentfile\\rebuild.py
def rebuild(self, metafile: Metadata) -> None:\n\"\"\"\n Build the torrent file structure from contents of directory.\n\n Traverse contents dir and compare discovered files\n with files listed in torrent metadata and copy\n the matches to the destination directory respecting folder\n structures along the way.\n \"\"\"\n metafile.rebuild(self.filemap, self.dest)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Metadata","title":"Metadata(path: str)
","text":" Bases: CbMixin
, ProgMixin
Class containing the metadata contents of a torrent file.
Construct metadata object for torrent info.
PARAMETER DESCRIPTION path
path to the .torrent file.
TYPE: str
Source code in torrentfile\\rebuild.py
def __init__(self, path: str):\n\"\"\"\n Construct metadata object for torrent info.\n\n Parameters\n ----------\n path : str\n path to the .torrent file.\n \"\"\"\n self.path = os.path.abspath(path)\n self.name = None\n self.piece_length = 1\n self.meta_version = 1\n self.pieces = b\"\"\n self.piece_nodes = []\n self.length = 0\n self.files = []\n self.filenames = set()\n self.extract()\n if self.meta_version == 2:\n self.num_pieces = len(self.filenames)\n else:\n self.num_pieces = math.ceil(len(self.pieces) / SHA1)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Metadata.extract","title":"extract()
","text":"Decode and extract information for the .torrent file.
Source code in torrentfile\\rebuild.py
def extract(self):\n\"\"\"\n Decode and extract information for the .torrent file.\n \"\"\"\n meta = pyben.load(self.path)\n info = meta[\"info\"]\n self.piece_length = info[\"piece length\"]\n self.name = info[\"name\"]\n self.meta_version = info.get(\"meta version\", 1)\n self.pieces = info.get(\"pieces\", bytes())\n if self.meta_version == 2:\n self._parse_tree(info[\"file tree\"], [self.name])\n elif \"length\" in info:\n self.length += info[\"length\"]\n self.is_file = True\n self.filenames.add(info[\"name\"])\n self.files.append({\n \"path\": Path(self.name).parent,\n \"filename\": self.name,\n \"full\": self.name,\n \"length\": self.length,\n })\n elif \"files\" in info:\n for f in info[\"files\"]:\n path = f[\"path\"]\n full = os.path.join(self.name, *path)\n self.files.append({\n \"path\": Path(full).parent,\n \"filename\": path[-1],\n \"full\": full,\n \"length\": f[\"length\"],\n })\n self.length += f[\"length\"]\n self.filenames.add(path[-1])\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Metadata.rebuild","title":"rebuild(filemap: dict, dest: str)
","text":"Rebuild torrent file contents from filemap at dest.
Searches through the contents of the meta file and compares filenames with those in the filemap dict, and if found checks their contents, and copies them to the destination path.
PARAMETER DESCRIPTION filemap
filesystem information
TYPE: dict
dest
destiantion path
TYPE: str
Source code in torrentfile\\rebuild.py
def rebuild(self, filemap: dict, dest: str):\n\"\"\"\n Rebuild torrent file contents from filemap at dest.\n\n Searches through the contents of the meta file and compares filenames\n with those in the filemap dict, and if found checks their contents,\n and copies them to the destination path.\n\n Parameters\n ----------\n filemap : dict\n filesystem information\n dest : str\n destiantion path\n \"\"\"\n self._prog = None\n if self.meta_version == 2:\n self._match_v2(filemap, dest)\n else:\n self._match_v1(filemap, dest)\n if self._prog is not None:\n self.progbar.close_out()\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PathNode","title":"PathNode(start: int = None, stop: int = None, full: str = None, filename: str = None, path: str = None, length: int = None)
","text":"Base class representing information regarding a file included in torrent.
Hold file information that contributes to the contents of torrent.
PARAMETER DESCRIPTION start
where the piece starts, by default None
TYPE: int, optional
DEFAULT: None
stop
where the piece ends, by default None
TYPE: int, optional
DEFAULT: None
full
full path, by default None
TYPE: str, optional
DEFAULT: None
filename
filename, by default None
TYPE: str, optional
DEFAULT: None
path
parent path, by default None
TYPE: str, optional
DEFAULT: None
length
size, by default None
TYPE: int, optional
DEFAULT: None
Source code in torrentfile\\rebuild.py
def __init__(\n self,\n start: int = None,\n stop: int = None,\n full: str = None,\n filename: str = None,\n path: str = None,\n length: int = None,\n):\n\"\"\"\n Hold file information that contributes to the contents of torrent.\n\n Parameters\n ----------\n start : int, optional\n where the piece starts, by default None\n stop : int, optional\n where the piece ends, by default None\n full : str, optional\n full path, by default None\n filename : str, optional\n filename, by default None\n path : str, optional\n parent path, by default None\n length : int, optional\n size, by default None\n \"\"\"\n self.path = path\n self.start = start\n self.stop = stop\n self.length = length\n self.filename = filename\n self.full = full\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PathNode.__len__","title":"__len__() -> int
","text":"Return size of the file.
RETURNS DESCRIPTION int
total size
Source code in torrentfile\\rebuild.py
def __len__(self) -> int:\n\"\"\"\n Return size of the file.\n\n Returns\n -------\n int\n total size\n \"\"\"\n return self.length\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PathNode.get_part","title":"get_part(path: str) -> bytes
","text":"Extract the part of the file needed to complete the hash.
PARAMETER DESCRIPTION path
filesystem path location of file.
TYPE: str
RETURNS DESCRIPTION bytes
part of the file\u2019s contents
Source code in torrentfile\\rebuild.py
def get_part(self, path: str) -> bytes:\n\"\"\"\n Extract the part of the file needed to complete the hash.\n\n Parameters\n ----------\n path : str\n filesystem path location of file.\n\n Returns\n -------\n bytes\n part of the file's contents\n \"\"\"\n with open(path, \"rb\") as fd:\n if self.start:\n fd.seek(self.start)\n if self.stop != -1:\n partial = fd.read(self.stop - self.start)\n else:\n partial = fd.read()\n return partial\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PieceNode","title":"PieceNode(piece: bytes)
","text":"Base class representing a single SHA1 hash block of data from a torrent.
Store information about an individual SHA1 hash for a torrent file.
extended_summary
PARAMETER DESCRIPTION piece
SHA1 hash bytes
TYPE: bytes
Source code in torrentfile\\rebuild.py
def __init__(self, piece: bytes):\n\"\"\"\n Store information about an individual SHA1 hash for a torrent file.\n\n _extended_summary_\n\n Parameters\n ----------\n piece : bytes\n SHA1 hash bytes\n \"\"\"\n self.piece = piece\n self.paths = []\n self.result = None\n self.dest = None\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PieceNode.append","title":"append(pathnode: PathNode)
","text":"Append the path argument to the paths list attribute.
PARAMETER DESCRIPTION pathnode
the pathnode
TYPE: PathNode
Source code in torrentfile\\rebuild.py
def append(self, pathnode: PathNode):\n\"\"\"\n Append the path argument to the paths list attribute.\n\n Parameters\n ----------\n pathnode : PathNode\n the pathnode\n \"\"\"\n self.paths.append(pathnode)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PieceNode.find_matches","title":"find_matches(filemap: dict, dest: str) -> bool
","text":"Find the matching files for each path in the node.
PARAMETER DESCRIPTION filemap
filename and details
TYPE: dict
dest
target destination path
TYPE: str
RETURNS DESCRIPTION bool
success status
Source code in torrentfile\\rebuild.py
def find_matches(self, filemap: dict, dest: str) -> bool:\n\"\"\"\n Find the matching files for each path in the node.\n\n Parameters\n ----------\n filemap : dict\n filename and details\n dest : str\n target destination path\n\n Returns\n -------\n bool\n success status\n \"\"\"\n self.dest = dest\n self.result = self._find_matches(filemap, self.paths[:], bytes())\n return self.result\n
"},{"location":"Source/recheck/","title":"Recheck","text":""},{"location":"Source/recheck/#torrentfile.recheck","title":"recheck
","text":"Module container Checker Class.
The CheckerClass takes a torrentfile and tha path to it\u2019s contents. It will then iterate through every file and directory contained and compare their data to values contained within the torrent file. Completion percentages will be printed to screen for each file and at the end for the torrentfile as a whole.
"},{"location":"Source/recheck/#torrentfile.recheck.Checker","title":"Checker(metafile: str, path: str)
","text":"Check a given file or directory to see if it matches a torrentfile.
Public constructor for Checker class instance.
PARAMETER DESCRIPTION metafile
Path to \u201c.torrent\u201d file.
TYPE: str
path
Path where the content is located in filesystem.
TYPE: str
"},{"location":"Source/recheck/#torrentfile.recheck.Checker--example","title":"Example","text":">> metafile = \"/path/to/torrentfile/content_file_or_dir.torrent\"\n>> location = \"/path/to/location\"\n>> os.path.exists(\"/path/to/location/content_file_or_dir\")\nOut: True\n>> checker = Checker(metafile, location)\n
Validate data against hashes contained in .torrent file.
PARAMETER DESCRIPTION metafile
path to .torrent file
TYPE: str
path
path to content or contents parent directory.
TYPE: str
Source code in torrentfile\\recheck.py
def __init__(self, metafile: str, path: str):\n\"\"\"\n Validate data against hashes contained in .torrent file.\n\n Parameters\n ----------\n metafile : str\n path to .torrent file\n path : str\n path to content or contents parent directory.\n \"\"\"\n if not os.path.exists(metafile):\n raise FileNotFoundError\n if os.path.isdir(metafile):\n raise ArgumentError(\n \"The <metafile> must be a .torrent file. Not a directory\")\n self.last_log = None\n self.log_msg(\"Checking: %s, %s\", metafile, path)\n self.metafile = metafile\n self.total = 0\n self.paths = []\n self.fileinfo = {}\n print(\"Extracting data from torrent file...\")\n self.meta = pyben.load(metafile)\n self.info = self.meta[\"info\"]\n self.name = self.info[\"name\"]\n self.piece_length = self.info[\"piece length\"]\n\n if \"meta version\" in self.info:\n if \"pieces\" in self.info:\n self.meta_version = 3\n else:\n self.meta_version = 2\n else:\n self.meta_version = 1\n\n self.root = self.find_root(path)\n self.check_paths()\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.check_paths","title":"check_paths()
","text":"Gather all file paths described in the torrent file.
Source code in torrentfile\\recheck.py
def check_paths(self):\n\"\"\"\n Gather all file paths described in the torrent file.\n \"\"\"\n finfo = self.fileinfo\n\n if \"length\" in self.info:\n self.log_msg(\"%s points to a single file\", self.root)\n self.total = self.info[\"length\"]\n self.paths.append(str(self.root))\n\n finfo[0] = {\n \"path\": self.root,\n \"length\": self.info[\"length\"],\n }\n\n if self.meta_version > 1:\n root = self.info[\"file tree\"][self.name][\"\"][\"pieces root\"]\n finfo[0][\"pieces root\"] = root\n\n return\n\n # Otherwise Content is more than 1 file.\n self.log_msg(\"%s points to a directory\", self.root)\n if self.meta_version == 1:\n for i, item in enumerate(self.info[\"files\"]):\n self.total += item[\"length\"]\n base = os.path.join(*item[\"path\"])\n\n self.fileinfo[i] = {\n \"path\": str(self.root / base),\n \"length\": item[\"length\"],\n }\n\n self.paths.append(str(self.root / base))\n return\n\n self.walk_file_tree(self.info[\"file tree\"], [])\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.find_root","title":"find_root(path: str) -> str
","text":"Check path for torrent content.
The path can be a relative or absolute filesystem path. In the case where the content is a single file, the path may point directly to the the file, or it may point to the parent directory. If content points to a directory. The directory will be checked to see if it matches the torrent\u2019s name, if not the directories contents will be searched. The returned value will be the absolute path that matches the torrent\u2019s name.
PARAMETER DESCRIPTION path
root path to torrent content
TYPE: str
RETURNS DESCRIPTION str
root path to content
Source code in torrentfile\\recheck.py
def find_root(self, path: str) -> str:\n\"\"\"\n Check path for torrent content.\n\n The path can be a relative or absolute filesystem path. In the case\n where the content is a single file, the path may point directly to the\n the file, or it may point to the parent directory. If content points\n to a directory. The directory will be checked to see if it matches\n the torrent's name, if not the directories contents will be searched.\n The returned value will be the absolute path that matches the torrent's\n name.\n\n Parameters\n ----------\n path : str\n root path to torrent content\n\n Returns\n -------\n str\n root path to content\n \"\"\"\n if not os.path.exists(path):\n self.log_msg(\"Could not locate torrent content %s.\", path)\n raise FileNotFoundError(path)\n\n root = Path(path)\n if root.name == self.name:\n self.log_msg(\"Content found: %s.\", str(root))\n return root\n\n if self.name in os.listdir(root):\n return root / self.name\n\n self.log_msg(\"Could not locate torrent content in: %s\", str(root))\n raise FileNotFoundError(root)\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.iter_hashes","title":"iter_hashes() -> tuple
","text":"Produce results of comparing torrent contents piece by piece.
YIELDS DESCRIPTION chunck
hash of data found on disk
TYPE: bytes
piece
hash of data when complete and correct
TYPE: bytes
path
path to file being hashed
TYPE: str
size
length of bytes hashed for piece
TYPE: int
Source code in torrentfile\\recheck.py
def iter_hashes(self) -> tuple:\n\"\"\"\n Produce results of comparing torrent contents piece by piece.\n\n Yields\n ------\n chunck : bytes\n hash of data found on disk\n piece : bytes\n hash of data when complete and correct\n path : str\n path to file being hashed\n size : int\n length of bytes hashed for piece\n \"\"\"\n matched = consumed = 0\n checker = self.piece_checker()\n for chunk, piece, path, size in checker(self):\n consumed += size\n matching = 0\n if chunk == piece:\n matching += size\n matched += size\n yield chunk, piece, path, size\n total_consumed = str(int(consumed / self.total * 100))\n percent_matched = str(int(matched / consumed * 100))\n self.log_msg(\n \"Processed: %s%%, Matched: %s%%\",\n total_consumed,\n percent_matched,\n )\n self._result = (matched / consumed) * 100 if consumed > 0 else 0\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.log_msg","title":"log_msg(*args, level: int = logging.INFO)
","text":"Log message msg
to logger and send msg
to callback hook.
PARAMETER DESCRIPTION *args
formatting args for log message
TYPE: dict
DEFAULT: ()
level
Log level for this message; default=logging.INFO
TYPE: int
DEFAULT: logging.INFO
Source code in torrentfile\\recheck.py
def log_msg(self, *args, level: int = logging.INFO):\n\"\"\"\n Log message `msg` to logger and send `msg` to callback hook.\n\n Parameters\n ----------\n *args : dict\n formatting args for log message\n level : int\n Log level for this message; default=`logging.INFO`\n \"\"\"\n message = args[0]\n if len(args) >= 3:\n message = message % tuple(args[1:])\n elif len(args) == 2:\n message = message % args[1]\n\n # Repeat log messages should be ignored.\n if message != self.last_log:\n self.last_log = message\n logger.log(level, message)\n if self._hook and level == logging.INFO:\n self._hook(message)\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.piece_checker","title":"piece_checker()
","text":"Check individual pieces of the torrent.
RETURNS DESCRIPTION HashChecker | FeedChecker
Individual piece hasher.
Source code in torrentfile\\recheck.py
def piece_checker(self):\n\"\"\"\n Check individual pieces of the torrent.\n\n Returns\n -------\n HashChecker | FeedChecker\n Individual piece hasher.\n \"\"\"\n if self.meta_version == 1:\n return FeedChecker\n return HashChecker\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.register_callback","title":"register_callback(hook)
classmethod
","text":"Register hooks from 3rd party programs to access generated info.
PARAMETER DESCRIPTION hook
callback function for the logging feature.
TYPE: function
Source code in torrentfile\\recheck.py
@classmethod\ndef register_callback(cls, hook):\n\"\"\"\n Register hooks from 3rd party programs to access generated info.\n\n Parameters\n ----------\n hook : function\n callback function for the logging feature.\n \"\"\"\n cls._hook = hook\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.results","title":"results()
","text":"Generate result percentage and store for future calls.
Source code in torrentfile\\recheck.py
def results(self):\n\"\"\"\n Generate result percentage and store for future calls.\n \"\"\"\n responses = []\n for response in self.iter_hashes():\n responses.append(response)\n\n self.log_msg(\"Final result for %s recheck: %s\", self.metafile,\n self._result)\n\n return self._result\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.walk_file_tree","title":"walk_file_tree(tree: dict, partials: list)
","text":"Traverse File Tree dictionary to get file details.
Extract full pathnames, length, root hash, and layer hashes for each file included in the .torrent\u2019s file tree.
PARAMETER DESCRIPTION tree
File Tree dict extracted from torrent file.
TYPE: dict
partials
list of intermediate pathnames.
TYPE: list
Source code in torrentfile\\recheck.py
def walk_file_tree(self, tree: dict, partials: list):\n\"\"\"\n Traverse File Tree dictionary to get file details.\n\n Extract full pathnames, length, root hash, and layer hashes\n for each file included in the .torrent's file tree.\n\n Parameters\n ----------\n tree : dict\n File Tree dict extracted from torrent file.\n partials : list\n list of intermediate pathnames.\n \"\"\"\n for key, val in tree.items():\n # Empty string means the tree's leaf is value\n if \"\" in val:\n base = os.path.join(*partials, key)\n roothash = None\n length = val[\"\"][\"length\"]\n roothash = None if not length else val[\"\"][\"pieces root\"]\n full = str(self.root / base)\n self.fileinfo[len(self.paths)] = {\n \"path\": full,\n \"length\": length,\n \"pieces root\": roothash,\n }\n self.paths.append(full)\n self.total += length\n else:\n self.walk_file_tree(val, partials + [key])\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker","title":"FeedChecker(checker: Checker)
","text":" Bases: ProgMixin
Validates torrent content.
Seemlesly validate torrent file contents by comparing hashes in metafile against data on disk.
PARAMETER DESCRIPTION checker
the checker class instance.
TYPE: object
Generate hashes of piece length data from filelist contents.
Source code in torrentfile\\recheck.py
def __init__(self, checker: Checker):\n\"\"\"\n Generate hashes of piece length data from filelist contents.\n \"\"\"\n self.piece_length = checker.piece_length\n self.paths = checker.paths\n self.pieces = checker.info[\"pieces\"]\n self.fileinfo = checker.fileinfo\n self.piece_map = {}\n self.index = 0\n self.piece_count = 0\n self.it = None\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.__iter__","title":"__iter__()
","text":"Assign iterator and return self.
Source code in torrentfile\\recheck.py
def __iter__(self):\n\"\"\"\n Assign iterator and return self.\n \"\"\"\n self.it = self.iter_pieces()\n return self\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.__next__","title":"__next__()
","text":"Yield back result of comparison.
Source code in torrentfile\\recheck.py
def __next__(self):\n\"\"\"\n Yield back result of comparison.\n \"\"\"\n try:\n partial = next(self.it)\n except StopIteration as itererror:\n raise StopIteration from itererror\n\n chunck = sha1(partial).digest() # nosec\n start = self.piece_count * SHA1\n end = start + SHA1\n piece = self.pieces[start:end]\n self.piece_count += 1\n path = self.paths[self.index]\n return chunck, piece, path, len(partial)\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.extract","title":"extract(path: str, partial: bytearray) -> bytearray
","text":"Split file paths contents into blocks of data for hash pieces.
PARAMETER DESCRIPTION path
path to content.
TYPE: str
partial
any remaining content from last file.
TYPE: bytes
RETURNS DESCRIPTION bytearray
Hash digest for block of .torrent contents.
Source code in torrentfile\\recheck.py
def extract(self, path: str, partial: bytearray) -> bytearray:\n\"\"\"\n Split file paths contents into blocks of data for hash pieces.\n\n Parameters\n ----------\n path : str\n path to content.\n partial : bytes\n any remaining content from last file.\n\n Returns\n -------\n bytearray\n Hash digest for block of .torrent contents.\n \"\"\"\n read = 0\n length = self.fileinfo[self.index][\"length\"]\n partial = bytearray() if len(partial) == self.piece_length else partial\n if path not in self.paths: # pragma: no cover\n raise MissingPathError(path)\n with open(path, \"rb\") as current:\n while True:\n bitlength = self.piece_length - len(partial)\n part = bytearray(bitlength)\n amount = current.readinto(part)\n read += amount\n partial.extend(part[:amount])\n if amount < bitlength:\n if amount > 0 and read == length:\n self.progbar.update(amount)\n yield partial\n break\n self.progbar.update(amount)\n yield partial\n partial = bytearray(0)\n if length != read:\n for pad in self._gen_padding(partial, length, read):\n yield pad\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.iter_pieces","title":"iter_pieces()
","text":"Iterate through, and hash pieces of torrent contents.
YIELDS DESCRIPTION piece
hash digest for block of torrent data.
TYPE: bytes
Source code in torrentfile\\recheck.py
def iter_pieces(self):\n\"\"\"\n Iterate through, and hash pieces of torrent contents.\n\n Yields\n ------\n piece : bytes\n hash digest for block of torrent data.\n \"\"\"\n partial = bytearray()\n for i, path in enumerate(self.paths):\n total = self.fileinfo[i][\"length\"]\n self.progbar = self.get_progress_tracker(total, path)\n self.index = i\n if os.path.exists(path):\n for piece in self.extract(path, partial):\n if (len(piece) == self.piece_length) or (i + 1 == len(\n self.paths)):\n yield piece\n else:\n partial = piece\n\n else:\n length = self.fileinfo[i][\"length\"]\n for pad in self._gen_padding(partial, length):\n if len(pad) == self.piece_length:\n yield pad\n else:\n partial = pad\n self.progbar.close_out()\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker","title":"HashChecker(checker: Checker)
","text":" Bases: ProgMixin
Iterate through contents of meta data and verify with file contents.
PARAMETER DESCRIPTION checker
the checker instance that maintains variables.
TYPE: Checker
Construct a HybridChecker instance.
Source code in torrentfile\\recheck.py
def __init__(self, checker: Checker):\n\"\"\"\n Construct a HybridChecker instance.\n \"\"\"\n self.checker = checker\n self.paths = checker.paths\n self.piece_length = checker.piece_length\n self.fileinfo = checker.fileinfo\n self.piece_layers = checker.meta[\"piece layers\"]\n self.current = None\n self.index = -1\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.Padder","title":"Padder(length, piece_length)
","text":"Padding class to generate padding hashes wherever needed.
PARAMETER DESCRIPTION length
the total size of the mock file generating padding for.
piece_length
the block size that each hash represents.
TYPE: int
Construct padding class to Mock missing or incomplete files.
PARAMETER DESCRIPTION length
size of the file
TYPE: int
piece_length
the piece length for each iteration.
TYPE: int
Source code in torrentfile\\recheck.py
def __init__(self, length, piece_length):\n\"\"\"\n Construct padding class to Mock missing or incomplete files.\n\n Parameters\n ----------\n length : int\n size of the file\n piece_length : int\n the piece length for each iteration.\n \"\"\"\n self.length = length\n self.piece_length = piece_length\n self.pad = sha256(bytearray(piece_length)).digest()\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.Padder.__iter__","title":"__iter__()
","text":"Return self to correctly implement iterator type.
Source code in torrentfile\\recheck.py
def __iter__(self):\n\"\"\"\n Return self to correctly implement iterator type.\n \"\"\"\n return self # pragma: nocover\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.Padder.__next__","title":"__next__() -> bytes
","text":"Iterate through seemingly endless sha256 hashes of zeros.
RETURNS DESCRIPTION tuple
returns the padding
TYPE: bytes
RAISES DESCRIPTION StopIteration
Source code in torrentfile\\recheck.py
def __next__(self) -> bytes:\n\"\"\"\n Iterate through seemingly endless sha256 hashes of zeros.\n\n Returns\n -------\n tuple :\n returns the padding\n\n Raises\n ------\n StopIteration\n \"\"\"\n if self.length >= self.piece_length:\n self.length -= self.piece_length\n return self.pad\n if self.length > 0:\n pad = sha256(bytearray(self.length)).digest()\n self.length -= self.length\n return pad\n raise StopIteration\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.__iter__","title":"__iter__()
","text":"Assign iterator and return self.
Source code in torrentfile\\recheck.py
def __iter__(self):\n\"\"\"\n Assign iterator and return self.\n \"\"\"\n return self\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.__next__","title":"__next__()
","text":"Provide the result of comparison.
Source code in torrentfile\\recheck.py
def __next__(self):\n\"\"\"\n Provide the result of comparison.\n \"\"\"\n if self.current is None:\n self.next_file()\n try:\n return self.process_current()\n except StopIteration as itererr:\n if self.next_file():\n return self.process_current()\n raise StopIteration from itererr\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.advance","title":"advance() -> tuple
","text":"Increment the number of pieces processed for the current file.
RETURNS DESCRIPTION tuple
the piece and size
Source code in torrentfile\\recheck.py
def advance(self) -> tuple:\n\"\"\"\n Increment the number of pieces processed for the current file.\n\n Returns\n -------\n tuple\n the piece and size\n \"\"\"\n start = self.count * SHA256\n end = start + SHA256\n piece = self.pieces[start:end]\n self.count += 1\n if self.length >= self.piece_length:\n self.length -= self.piece_length\n size = self.piece_length\n else:\n size = self.length\n self.length -= self.length\n return piece, size\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.next_file","title":"next_file() -> bool
","text":"Remove all references to processed files and prepare for the next.
RETURNS DESCRIPTION bool
if there is a next file found
Source code in torrentfile\\recheck.py
def next_file(self) -> bool:\n\"\"\"\n Remove all references to processed files and prepare for the next.\n\n Returns\n -------\n bool\n if there is a next file found\n \"\"\"\n self.index += 1\n if self.current is None or self.index < len(self.paths):\n self.current = self.paths[self.index]\n self.length = self.fileinfo[self.index][\"length\"]\n self.root_hash = self.fileinfo[self.index][\"pieces root\"]\n if self.length > self.piece_length:\n self.pieces = self.piece_layers[self.root_hash]\n else:\n self.pieces = self.root_hash\n path = self.paths[self.index]\n self.progbar = self.get_progress_tracker(self.length, path)\n self.count = 0\n if os.path.exists(self.current):\n self.hasher = FileHasher(\n path,\n self.piece_length,\n progress=2,\n progress_bar=self.progbar,\n )\n else:\n self.hasher = self.Padder(self.length, self.piece_length)\n return True\n if self.index >= len(self.paths):\n del self.current\n del self.length\n del self.root_hash\n del self.pieces\n return False\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.process_current","title":"process_current() -> tuple
","text":"Gather necessary information to compare to metafile details.
RETURNS DESCRIPTION tuple
a tuple containing the layer, piece, current path and size
RAISES DESCRIPTION StopIteration
Source code in torrentfile\\recheck.py
def process_current(self) -> tuple:\n\"\"\"\n Gather necessary information to compare to metafile details.\n\n Returns\n -------\n tuple\n a tuple containing the layer, piece, current path and size\n\n Raises\n ------\n StopIteration\n \"\"\"\n try:\n layer = next(self.hasher)\n piece, size = self.advance()\n self.progbar.update(size)\n return layer, piece, self.current, size\n except StopIteration as err:\n if self.length > 0 and self.count * SHA256 < len(self.pieces):\n self.hasher = self.Padder(self.length, self.piece_length)\n piece, size = self.advance()\n layer = next(self.hasher)\n self.progbar.update(0)\n return layer, piece, self.current, size\n raise StopIteration from err\n
"},{"location":"Source/torrent/","title":"Torrent","text":""},{"location":"Source/torrent/#torrentfile.torrent","title":"torrent
","text":"Classes and procedures pertaining to the creation of torrent meta files.
"},{"location":"Source/torrent/#torrentfile.torrent--classes","title":"Classes","text":" TorrentFile
construct .torrent file.
TorrentFileV2
construct .torrent v2 files using provided data.
MetaFile
base class for all MetaFile classes.
"},{"location":"Source/torrent/#torrentfile.torrent--constants","title":"Constants","text":""},{"location":"Source/torrent/#torrentfile.torrent--bittorrent-v2","title":"Bittorrent V2","text":"From Bittorrent.org Documentation pages.
Implementation details for Bittorrent Protocol v2.
Note
All strings in a .torrent file that contain text must be UTF-8 encoded.
"},{"location":"Source/torrent/#torrentfile.torrent--meta-version-2-dictionary","title":"Meta Version 2 Dictionary:","text":" \u201cannounce\u201d: The URL of the tracker.
\u201cinfo\u201d: This maps to a dictionary, with keys described below.
\u201cname\u201d: A display name for the torrent. It is purely advisory.
\u201cpiece length\u201d: The number of bytes that each logical piece in the peer protocol refers to. I.e. it sets the granularity of piece, request, bitfield and have messages. It must be a power of two and at least 6KiB.
\u201cmeta version\u201d: An integer value, set to 2 to indicate compatibility with the current revision of this specification. Version 1 is not assigned to avoid confusion with BEP3. Future revisions will only increment this issue to indicate an incompatible change has been made, for example that hash algorithms were changed due to newly discovered vulnerabilities. Lementations must check this field first and indicate that a torrent is of a newer version than they can handle before performing other idations which may result in more general messages about invalid files. Files are mapped into this piece address space so that each non-empty
\u201cfile tree\u201d: A tree of dictionaries where dictionary keys represent UTF-8 encoded path elements. Entries with zero-length keys describe the properties of the composed path at that point. \u2018UTF-8 encoded\u2019 context only means that if the native encoding is known at creation time it must be converted to UTF-8. Keys may contain invalid UTF-8 sequences or characters and names that are reserved on specific filesystems. Implementations must be prepared to sanitize them. On platforms path components exactly matching \u2018.\u2019 and \u2018..\u2019 must be sanitized since they could lead to directory traversal attacks and conflicting path descriptions. On platforms that require UTF-8 path components this sanitizing step must happen after normalizing overlong UTF-8 encodings. File is aligned to a piece boundary and occurs in same order as the file tree. The last piece of each file may be shorter than the specified piece length, resulting in an alignment gap.
\u201clength\u201d: Length of the file in bytes. Presence of this field indicates that the dictionary describes a file, not a directory. Which means it must not have any sibling entries.
\u201cpieces root\u201d: For non-empty files this is the the root hash of a merkle tree with a branching factor of 2, constructed from 16KiB blocks of the file. The last block may be shorter than 16KiB. The remaining leaf hashes beyond the end of the file required to construct upper layers of the merkle tree are set to zero. As of meta version 2 SHA2-256 is used as digest function for the merkle tree. The hash is stored in its binary form, not as human-readable string.
\u201cpiece layers\u201d: A dictionary of strings. For each file in the file tree that is larger than the piece size it contains one string value. The keys are the merkle roots while the values consist of concatenated hashes of one layer within that merkle tree. The layer is chosen so that one hash covers piece length bytes. For example if the piece size is 16KiB then the leaf hashes are used. If a piece size of 128KiB is used then 3rd layer up from the leaf hashes is used. Layer hashes which exclusively cover data beyond the end of file, i.e. are only needed to balance the tree, are omitted. All hashes are stored in their binary format. A torrent is not valid if this field is absent, the contained hashes do not match the merkle roots or are not from the correct layer.
Important
The file tree root dictionary itself must not be a file, i.e. it must not contain a zero-length key with a dictionary containing a length key.
"},{"location":"Source/torrent/#torrentfile.torrent--bittorrent-v1","title":"Bittorrent V1","text":""},{"location":"Source/torrent/#torrentfile.torrent--v1-meta-dictionary","title":"v1 meta-dictionary","text":" Note
In the single file case, the name key is the name of a file, in the muliple file case, it\u2019s the name of a directory.
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile","title":"MetaFile(path = None, announce = None, comment = None, align = False, piece_length = None, private = False, outfile = None, source = None, progress = 1, cwd = False, httpseeds = None, url_list = None, content = None, meta_version = None, **_)
","text":"Base Class for all TorrentFile classes.
PARAMETER DESCRIPTION path
target path to torrent content. Default: None
TYPE: str
DEFAULT: None
announce
One or more tracker URL\u2019s. Default: None
TYPE: str
DEFAULT: None
comment
A comment. Default: None
TYPE: str
DEFAULT: None
piece_length
Size of torrent pieces. Default: None
TYPE: int
DEFAULT: None
private
For private trackers. Default: None
TYPE: bool
DEFAULT: False
outfile
target path to write .torrent file. Default: None
TYPE: str
DEFAULT: None
source
Private tracker source. Default: None
TYPE: str
DEFAULT: None
progress
level of progress bar displayed Default: \u201c1\u201d
TYPE: str
DEFAULT: 1
cwd
If True change default save location to current directory
TYPE: bool
DEFAULT: False
httpseeds
one or more web addresses where torrent content can be found.
TYPE: list
DEFAULT: None
url_list
one or more web addressess where torrent content exists.
TYPE: list
DEFAULT: None
content
alias for \u2018path\u2019 arg.
TYPE: str
DEFAULT: None
meta_version
indicates which Bittorrent protocol to use for hashing content
TYPE: int
DEFAULT: None
Construct MetaFile superclass and assign local attributes.
Source code in torrentfile\\torrent.py
def __init__(\n self,\n path=None,\n announce=None,\n comment=None,\n align=False,\n piece_length=None,\n private=False,\n outfile=None,\n source=None,\n progress=1,\n cwd=False,\n httpseeds=None,\n url_list=None,\n content=None,\n meta_version=None,\n **_,\n):\n\"\"\"\n Construct MetaFile superclass and assign local attributes.\n \"\"\"\n self.private = private\n self.cwd = cwd\n self.outfile = outfile\n self.progress = int(progress)\n self.comment = comment\n self.source = source\n self.meta_version = meta_version\n\n if content:\n path = content\n if not path:\n if announce and len(announce) > 1 and os.path.exists(announce[-1]):\n path = announce[-1]\n announce = announce[:-1]\n elif url_list and os.path.exists(url_list[-1]):\n path = url_list[-1]\n url_list = url_list[:-1]\n elif httpseeds and os.path.exists(httpseeds[-1]):\n path = httpseeds[-1]\n httpseeds = httpseeds[:-1]\n else:\n raise utils.MissingPathError(\"Path to content is required.\")\n\n # base path to torrent content.\n self.path = path\n\n logger.debug(\"path parameter found %s\", path)\n\n self.meta = {\n \"created by\": f\"torrentfile_v{version}\",\n \"creation date\": int(datetime.timestamp(datetime.now())),\n \"info\": {},\n }\n\n # Format piece_length attribute.\n if piece_length:\n self.piece_length = utils.normalize_piece_length(piece_length)\n logger.debug(\"piece length parameter found %s\", piece_length)\n else:\n self.piece_length = utils.path_piece_length(self.path)\n logger.debug(\"piece length calculated %s\", self.piece_length)\n\n # Assign announce URL to empty string if none provided.\n if not announce:\n self.announce, self.announce_list = \"\", [[\"\"]]\n\n # Most torrent clients have editting trackers as a feature.\n elif isinstance(announce, str):\n self.announce, self.announce_list = announce, [[announce]]\n\n elif isinstance(announce, Sequence):\n self.announce, self.announce_list = announce[0], [announce]\n\n self.align = align\n\n if self.announce:\n self.meta[\"announce\"] = self.announce\n self.meta[\"announce-list\"] = self.announce_list\n if comment:\n self.meta[\"info\"][\"comment\"] = comment\n logger.debug(\"comment parameter found %s\", comment)\n if private:\n self.meta[\"info\"][\"private\"] = 1\n logger.debug(\"private parameter triggered\")\n if source:\n self.meta[\"info\"][\"source\"] = source\n logger.debug(\"source parameter found %s\", source)\n if url_list:\n self.meta[\"url-list\"] = url_list\n logger.debug(\"url list parameter found %s\", str(url_list))\n if httpseeds:\n self.meta[\"httpseeds\"] = httpseeds\n logger.debug(\"httpseeds parameter found %s\", str(httpseeds))\n self.meta[\"info\"][\"piece length\"] = self.piece_length\n\n self.meta_version = meta_version\n parent, self.name = os.path.split(self.path)\n if not self.name:\n self.name = os.path.basename(parent)\n self.meta[\"info\"][\"name\"] = self.name\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.assemble","title":"assemble()
","text":"Overload in subclasses.
RAISES DESCRIPTION Exception
NotImplementedError
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Overload in subclasses.\n\n Raises\n ------\n Exception\n NotImplementedError\n \"\"\"\n raise NotImplementedError\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.set_callback","title":"set_callback(func)
classmethod
","text":"Assign a callback function for the Hashing class to call for each hash.
PARAMETER DESCRIPTION func
The callback function which accepts a single paramter.
TYPE: function
Source code in torrentfile\\torrent.py
@classmethod\ndef set_callback(cls, func):\n\"\"\"\n Assign a callback function for the Hashing class to call for each hash.\n\n Parameters\n ----------\n func : function\n The callback function which accepts a single paramter.\n \"\"\"\n if \"hasher\" in vars(cls) and vars(cls)[\"hasher\"]:\n cls.hasher.set_callback(func)\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.sort_meta","title":"sort_meta()
","text":"Sort the info and meta dictionaries.
Source code in torrentfile\\torrent.py
def sort_meta(self):\n\"\"\"Sort the info and meta dictionaries.\"\"\"\n logger.debug(\"sorting dictionary keys\")\n meta = self.meta\n meta[\"info\"] = dict(sorted(list(meta[\"info\"].items())))\n meta = dict(sorted(list(meta.items())))\n return meta\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.write","title":"write(outfile = None) -> tuple
","text":"Write meta information to .torrent file.
Final step in the torrent file creation process. After hashing and sorting every piece of content write the contents to file using the bencode encoding.
PARAMETER DESCRIPTION outfile
Destination path for .torrent file. default=None
TYPE: str
DEFAULT: None
RETURNS DESCRIPTION outfile
Where the .torrent file was writen.
TYPE: str
meta
.torrent meta information.
TYPE: dict
Source code in torrentfile\\torrent.py
def write(self, outfile=None) -> tuple:\n\"\"\"\n Write meta information to .torrent file.\n\n Final step in the torrent file creation process.\n After hashing and sorting every piece of content\n write the contents to file using the bencode encoding.\n\n Parameters\n ----------\n outfile : str\n Destination path for .torrent file. default=None\n\n Returns\n -------\n outfile : str\n Where the .torrent file was writen.\n meta : dict\n .torrent meta information.\n \"\"\"\n if outfile:\n self.outfile = outfile\n if not self.outfile: # pragma: nocover\n path = os.path.join(os.getcwd(), self.name) + \".torrent\"\n self.outfile = path\n if str(self.outfile)[-1] in \"\\\\/\":\n self.outfile = self.outfile + (self.name + \".torrent\")\n self.meta = self.sort_meta()\n try:\n pyben.dump(self.meta, self.outfile)\n except PermissionError as excp:\n logger.error(\"Permission Denied: Could not write to %s\",\n self.outfile)\n raise PermissionError from excp\n return self.outfile, self.meta\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentAssembler","title":"TorrentAssembler(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Assembler class for Bittorrent version 2 and hybrid meta files.
This differs from the TorrentFileV2 and TorrentFileHybrid, because it can be used as an iterator and works for both versions.
PARAMETER DESCRIPTION **kwargs
Keyword arguments for torrent options.
TYPE: dict
DEFAULT: {}
Create Bittorrent v1 v2 hybrid metafiles.
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Create Bittorrent v1 v2 hybrid metafiles.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent Hybrid file\")\n self.name = os.path.basename(self.path)\n self.hashes = []\n self.piece_layers = {}\n self.pieces = bytearray()\n self.files = []\n self.hybrid = self.meta_version == \"3\"\n size, file_list = utils.filelist_total(self.path)\n self.kws = {\n \"progress\": self.progress,\n \"progress_bar\": None,\n \"hybrid\": self.hybrid,\n }\n self.total = len(file_list)\n\n if self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n self.kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n self.kws[\"progress_bar\"] = self.prog_bar\n\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentAssembler.assemble","title":"assemble()
","text":"Assemble the parts of the torrentfile into meta dictionary.
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble the parts of the torrentfile into meta dictionary.\n \"\"\"\n info = self.meta[\"info\"]\n info[\"meta version\"] = 2\n\n if os.path.isfile(self.path):\n info[\"file tree\"] = {self.name: self._traverse(self.path)}\n info[\"length\"] = os.path.getsize(self.path)\n\n else:\n info[\"file tree\"] = self._traverse(self.path)\n if self.hybrid:\n info[\"files\"] = self.files\n\n if self.hybrid:\n info[\"pieces\"] = self.pieces\n self.meta[\"piece layers\"] = self.piece_layers\n return info\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFile","title":"TorrentFile(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Class for creating Bittorrent meta files.
Construct Torrentfile class instance object.
PARAMETER DESCRIPTION **kwargs
Dictionary containing torrent file options.
TYPE: dict
DEFAULT: {}
Construct TorrentFile instance with given keyword args.
PARAMETER DESCRIPTION **kwargs
dictionary of keyword args passed to superclass.
TYPE: dict
DEFAULT: {}
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Construct TorrentFile instance with given keyword args.\n\n Parameters\n ----------\n **kwargs : dict\n dictionary of keyword args passed to superclass.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent v1 torrent file\")\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFile.assemble","title":"assemble()
","text":"Assemble components of torrent metafile.
RETURNS DESCRIPTION dict
metadata dictionary for torrent file
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble components of torrent metafile.\n\n Returns\n -------\n dict\n metadata dictionary for torrent file\n \"\"\"\n info = self.meta[\"info\"]\n size, filelist = utils.filelist_total(self.path)\n kws = {\n \"progress\": self.progress,\n \"progress_bar\": None,\n \"align\": self.align,\n }\n\n if self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n kws[\"progress_bar\"] = self.prog_bar\n\n if os.path.isfile(self.path):\n info[\"length\"] = size\n elif not self.align:\n info[\"files\"] = [{\n \"length\":\n os.path.getsize(path),\n \"path\":\n os.path.relpath(path, self.path).split(os.sep),\n } for path in filelist]\n else:\n info[\"files\"] = []\n for path in filelist:\n filesize = os.path.getsize(path)\n info[\"files\"].append({\n \"length\":\n filesize,\n \"path\":\n os.path.relpath(path, self.path).split(os.sep),\n })\n if filesize < self.piece_length:\n remainder = self.piece_length - filesize\n else:\n remainder = filesize % self.piece_length\n if remainder:\n info[\"files\"].append({\n \"attr\": \"p\",\n \"length\": remainder,\n \"path\": [\".pad\", str(remainder)],\n })\n pieces = bytearray()\n feeder = Hasher(filelist, self.piece_length, **kws)\n for piece in feeder:\n pieces.extend(piece)\n info[\"pieces\"] = pieces\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileHybrid","title":"TorrentFileHybrid(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Construct the Hybrid torrent meta file with provided parameters.
PARAMETER DESCRIPTION **kwargs
Keyword arguments for torrent options.
TYPE: dict
DEFAULT: {}
Create Bittorrent v1 v2 hybrid metafiles.
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Create Bittorrent v1 v2 hybrid metafiles.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent Hybrid file\")\n self.name = os.path.basename(self.path)\n self.hashes = []\n self.piece_layers = {}\n self.pieces = []\n self.files = []\n size, file_list = utils.filelist_total(self.path)\n self.kws = {\"progress\": self.progress, \"progress_bar\": None}\n self.total = len(file_list)\n\n if self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n self.kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n self.kws[\"progress_bar\"] = self.prog_bar\n\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileHybrid.assemble","title":"assemble()
","text":"Assemble the parts of the torrentfile into meta dictionary.
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble the parts of the torrentfile into meta dictionary.\n \"\"\"\n info = self.meta[\"info\"]\n info[\"meta version\"] = 2\n\n if os.path.isfile(self.path):\n info[\"file tree\"] = {self.name: self._traverse(self.path)}\n info[\"length\"] = os.path.getsize(self.path)\n\n else:\n info[\"file tree\"] = self._traverse(self.path)\n info[\"files\"] = self.files\n\n info[\"pieces\"] = b\"\".join(self.pieces)\n self.meta[\"piece layers\"] = self.piece_layers\n return info\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileV2","title":"TorrentFileV2(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Class for creating Bittorrent meta v2 files.
PARAMETER DESCRIPTION **kwargs
Keyword arguments for torrent file options.
TYPE: dict
DEFAULT: {}
Construct TorrentFileV2
Class instance from given parameters.
PARAMETER DESCRIPTION **kwargs
keywword arguments to pass to superclass.
TYPE: dict
DEFAULT: {}
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Construct `TorrentFileV2` Class instance from given parameters.\n\n Parameters\n ----------\n **kwargs : dict\n keywword arguments to pass to superclass.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent v2 torrent file\")\n self.piece_layers = {}\n self.hashes = []\n size, file_list = utils.filelist_total(self.path)\n self.kws = {\"progress\": self.progress, \"progress_bar\": None}\n self.total = len(file_list)\n\n if self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n self.kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n self.kws[\"progress_bar\"] = self.prog_bar\n\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileV2.assemble","title":"assemble()
","text":"Assemble then return the meta dictionary for encoding.
RETURNS DESCRIPTION meta
Metainformation about the torrent.
TYPE: dict
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble then return the meta dictionary for encoding.\n\n Returns\n -------\n meta : dict\n Metainformation about the torrent.\n \"\"\"\n info = self.meta[\"info\"]\n if os.path.isfile(self.path):\n info[\"file tree\"] = {info[\"name\"]: self._traverse(self.path)}\n info[\"length\"] = os.path.getsize(self.path)\n else:\n info[\"file tree\"] = self._traverse(self.path)\n\n info[\"meta version\"] = 2\n self.meta[\"piece layers\"] = self.piece_layers\n
"},{"location":"Source/utils/","title":"Utils","text":""},{"location":"Source/utils/#torrentfile.utils","title":"utils
","text":"Utility functions and classes used throughout package.
"},{"location":"Source/utils/#torrentfile.utils.ArgumentError","title":"ArgumentError
","text":" Bases: Exception
Exception for mismatched or mistyped CLI arguments.
"},{"location":"Source/utils/#torrentfile.utils.Memo","title":"Memo(func)
","text":"Memoize cache.
PARAMETER DESCRIPTION func
The results of this callable will be cached.
TYPE: Callable
Construct cache.
Source code in torrentfile\\utils.py
def __init__(self, func):\n\"\"\"\n Construct cache.\n \"\"\"\n self.func = func\n self.counter = 0\n self.cache = {}\n
"},{"location":"Source/utils/#torrentfile.utils.Memo.__call__","title":"__call__(path: str)
","text":"Invoke each time memo function is executed.
PARAMETER DESCRIPTION path
The relative or absolute path being used as key in cache dict.
TYPE: str
RETURNS DESCRIPTION Any
The results of calling the function with path.
Source code in torrentfile\\utils.py
def __call__(self, path: str):\n\"\"\"\n Invoke each time memo function is executed.\n\n Parameters\n ----------\n path : str\n The relative or absolute path being used as key in cache dict.\n\n Returns\n -------\n Any :\n The results of calling the function with path.\n \"\"\"\n if path in self.cache and os.path.exists(path):\n self.counter += 1\n return self.cache[path]\n result = self.func(path)\n self.cache[path] = result\n return result\n
"},{"location":"Source/utils/#torrentfile.utils.MissingPathError","title":"MissingPathError(message: str = None)
","text":" Bases: Exception
Path parameter is required to specify target content.
Creating a .torrent file with no contents seems rather silly.
PARAMETER DESCRIPTION message
Message for user (optional).
TYPE: str
DEFAULT: None
Raise when creating a meta file without specifying target content.
The message
argument is a message to pass to Exception base class.
Source code in torrentfile\\utils.py
def __init__(self, message: str = None):\n\"\"\"\n Raise when creating a meta file without specifying target content.\n\n The `message` argument is a message to pass to Exception base class.\n \"\"\"\n self.message = f\"Path arguement is missing and required {str(message)}\"\n super().__init__(message)\n
"},{"location":"Source/utils/#torrentfile.utils.PieceLengthValueError","title":"PieceLengthValueError(message: str = None)
","text":" Bases: Exception
Piece Length parameter must equal a perfect power of 2.
PARAMETER DESCRIPTION message
Message for user (optional).
TYPE: str
DEFAULT: None
Raise when creating a meta file with incorrect piece length value.
The message
argument is a message to pass to Exception base class.
Source code in torrentfile\\utils.py
def __init__(self, message: str = None):\n\"\"\"\n Raise when creating a meta file with incorrect piece length value.\n\n The `message` argument is a message to pass to Exception base class.\n \"\"\"\n self.message = f\"Incorrect value for piece length: {str(message)}\"\n super().__init__(message)\n
"},{"location":"Source/utils/#torrentfile.utils.check_path_writable","title":"check_path_writable(path: str) -> bool
","text":"Test if output path is writable.
PARAMETER DESCRIPTION path
file system path string
TYPE: str
RETURNS DESCRIPTION bool
True if writeable, otherwise raises PermissionError
Source code in torrentfile\\utils.py
def check_path_writable(path: str) -> bool:\n\"\"\"\n Test if output path is writable.\n\n Parameters\n ----------\n path : str\n file system path string\n\n Returns\n -------\n bool\n True if writeable, otherwise raises PermissionError\n \"\"\"\n try:\n if path.endswith(\"\\\\\") or path.endswith(\"/\"):\n path = os.path.join(path, \".torrent\")\n with open(path, \"ab\") as _:\n pass\n os.remove(path)\n except PermissionError as err: # pragma: nocover\n directory = os.path.dirname(path)\n message = f\"Target directory is not writeable {directory}\"\n raise PermissionError(message) from err\n return True\n
"},{"location":"Source/utils/#torrentfile.utils.colored","title":"colored(string: str, key: int) -> str
","text":"Output terminal content with formatting.
Source code in torrentfile\\utils.py
def colored(string: str, key: int) -> str:\n\"\"\"\n Output terminal content with formatting.\n \"\"\"\n return f\"\\033[{key}m{string}\\033[0m\"\n
"},{"location":"Source/utils/#torrentfile.utils.copypath","title":"copypath(source: str, dest: str) -> None
","text":"Copy the file located at source to dest.
If one or more directory paths don\u2019t exist in dest, they will be created. If dest already exists and dest and source are the same size, it will be ignored, however if dest is smaller than source, dest will be overwritten.
PARAMETER DESCRIPTION source
path to source file
TYPE: str
dest
path to target destination
TYPE: str
Source code in torrentfile\\utils.py
def copypath(source: str, dest: str) -> None:\n\"\"\"\n Copy the file located at source to dest.\n\n If one or more directory paths don't exist in dest, they will be created.\n If dest already exists and dest and source are the same size, it will be\n ignored, however if dest is smaller than source, dest will be overwritten.\n\n Parameters\n ----------\n source : str\n path to source file\n dest : str\n path to target destination\n \"\"\"\n if not os.path.exists(source) or (os.path.exists(dest)\n and os.path.getsize(source)\n <= os.path.getsize(dest)):\n return\n path_parts = Path(dest).parts\n if len(path_parts) > 1:\n root = path_parts[0]\n path_parts = path_parts[1:-1]\n if not os.path.exists(root):\n os.mkdir(root) # pragma: nocover\n for part in path_parts:\n path = os.path.join(root, part)\n if not os.path.exists(path):\n os.mkdir(path)\n root = path\n shutil.copy(source, dest)\n
"},{"location":"Source/utils/#torrentfile.utils.debug_is_on","title":"debug_is_on() -> bool
","text":"Return True if debug mode is on in environment variables.
RETURNS DESCRIPTION bool
is debug mode on
Source code in torrentfile\\utils.py
def debug_is_on() -> bool:\n\"\"\"\n Return True if debug mode is on in environment variables.\n\n Returns\n -------\n bool\n is debug mode on\n \"\"\"\n return os.environ[\"TORRENTFILE_DEBUG\"] == \"ON\"\n
"},{"location":"Source/utils/#torrentfile.utils.filelist_total","title":"filelist_total(pathstring: str) -> os.PathLike
","text":"Perform error checking and format conversion to os.PathLike.
PARAMETER DESCRIPTION pathstring
An existing filesystem path.
TYPE: str
RETURNS DESCRIPTION os.PathLike
Input path converted to bytes format.
RAISES DESCRIPTION MissingPathError
File could not be found.
Source code in torrentfile\\utils.py
@Memo\ndef filelist_total(pathstring: str) -> os.PathLike:\n\"\"\"\n Perform error checking and format conversion to os.PathLike.\n\n Parameters\n ----------\n pathstring : str\n An existing filesystem path.\n\n Returns\n -------\n os.PathLike\n Input path converted to bytes format.\n\n Raises\n ------\n MissingPathError\n File could not be found.\n \"\"\"\n if os.path.exists(pathstring):\n path = Path(pathstring)\n return _filelist_total(path)\n raise MissingPathError\n
"},{"location":"Source/utils/#torrentfile.utils.get_file_list","title":"get_file_list(path: str) -> list
","text":"Return a sorted list of file paths contained in directory.
PARAMETER DESCRIPTION path
target file or directory.
TYPE: str
RETURNS DESCRIPTION list
sorted list of file paths.
TYPE: list
Source code in torrentfile\\utils.py
def get_file_list(path: str) -> list:\n\"\"\"\n Return a sorted list of file paths contained in directory.\n\n Parameters\n ----------\n path : str\n target file or directory.\n\n Returns\n -------\n list :\n sorted list of file paths.\n \"\"\"\n _, filelist = filelist_total(path)\n return filelist\n
"},{"location":"Source/utils/#torrentfile.utils.get_piece_length","title":"get_piece_length(size: int) -> int
","text":"Calculate the ideal piece length for bittorrent data.
PARAMETER DESCRIPTION size
Total bits of all files incluided in .torrent file.
TYPE: int
RETURNS DESCRIPTION int
Ideal piece length.
Source code in torrentfile\\utils.py
def get_piece_length(size: int) -> int:\n\"\"\"\n Calculate the ideal piece length for bittorrent data.\n\n Parameters\n ----------\n size : int\n Total bits of all files incluided in .torrent file.\n\n Returns\n -------\n int\n Ideal piece length.\n \"\"\"\n exp = 14\n while size / (2**exp) > 1000 and exp < 24:\n exp += 1\n return 2**exp\n
"},{"location":"Source/utils/#torrentfile.utils.green","title":"green(string: str) -> str
","text":"Output terminal content in green color.
Source code in torrentfile\\utils.py
def green(string: str) -> str:\n\"\"\"\n Output terminal content in green color.\n \"\"\"\n return colored(string, 92)\n
"},{"location":"Source/utils/#torrentfile.utils.humanize_bytes","title":"humanize_bytes(amount: int) -> str
","text":"Convert integer into human readable memory sized denomination.
PARAMETER DESCRIPTION amount
total number of bytes.
TYPE: int
RETURNS DESCRIPTION str
human readable representation of the given amount of bytes.
Source code in torrentfile\\utils.py
def humanize_bytes(amount: int) -> str:\n\"\"\"\n Convert integer into human readable memory sized denomination.\n\n Parameters\n ----------\n amount : int\n total number of bytes.\n\n Returns\n -------\n str\n human readable representation of the given amount of bytes.\n \"\"\"\n base = 1024\n amount = float(amount)\n value = abs(amount)\n if value == 1:\n return f\"{amount} Byte\"\n if value < base:\n return f\"{amount} Bytes\"\n for i, s in enumerate(SUFFIXES):\n unit = base**(i + 2)\n if value < unit:\n break\n value = base * amount / unit\n return f\"{value:.1f} {s}\"\n
"},{"location":"Source/utils/#torrentfile.utils.next_power_2","title":"next_power_2(value: int) -> int
","text":"Calculate the next perfect power of 2 equal to or greater than value.
PARAMETER DESCRIPTION value
integer value that is less than some perfect power of 2.
TYPE: int
RETURNS DESCRIPTION int
The next power of 2 greater than value, or value if already power of 2.
Source code in torrentfile\\utils.py
def next_power_2(value: int) -> int:\n\"\"\"\n Calculate the next perfect power of 2 equal to or greater than value.\n\n Parameters\n ----------\n value : int\n integer value that is less than some perfect power of 2.\n\n Returns\n -------\n int\n The next power of 2 greater than value, or value if already power of 2.\n \"\"\"\n if not value & (value - 1) and value:\n return value\n start = 1\n while start < value:\n start <<= 1\n return start\n
"},{"location":"Source/utils/#torrentfile.utils.normalize_piece_length","title":"normalize_piece_length(piece_length: int) -> int
","text":"Verify input piece_length is valid and convert accordingly.
PARAMETER DESCRIPTION piece_length
The piece length provided by user.
TYPE: int | str
RETURNS DESCRIPTION int
normalized piece length.
RAISES DESCRIPTION PieceLengthValueError :
Piece length is improper value.
Source code in torrentfile\\utils.py
def normalize_piece_length(piece_length: int) -> int:\n\"\"\"\n Verify input piece_length is valid and convert accordingly.\n\n Parameters\n ----------\n piece_length : int | str\n The piece length provided by user.\n\n Returns\n -------\n int\n normalized piece length.\n\n Raises\n ------\n PieceLengthValueError :\n Piece length is improper value.\n \"\"\"\n if isinstance(piece_length, str):\n if piece_length.isnumeric():\n piece_length = int(piece_length)\n else:\n raise PieceLengthValueError(piece_length)\n\n if piece_length > (1 << 14):\n if 2**math.log2(piece_length) == piece_length:\n return piece_length\n raise PieceLengthValueError(piece_length)\n\n if 13 < piece_length < 26:\n return 2**piece_length\n if piece_length <= 13:\n raise PieceLengthValueError(piece_length)\n\n log = int(math.log2(piece_length))\n if 2**log == piece_length:\n return piece_length\n raise PieceLengthValueError\n
"},{"location":"Source/utils/#torrentfile.utils.path_piece_length","title":"path_piece_length(path: str) -> int
","text":"Calculate piece length for input path and contents.
PARAMETER DESCRIPTION path
The absolute path to directory and contents.
TYPE: str
RETURNS DESCRIPTION int
The size of pieces of torrent content.
Source code in torrentfile\\utils.py
def path_piece_length(path: str) -> int:\n\"\"\"\n Calculate piece length for input path and contents.\n\n Parameters\n ----------\n path : str\n The absolute path to directory and contents.\n\n Returns\n -------\n int\n The size of pieces of torrent content.\n \"\"\"\n psize = path_size(path)\n return get_piece_length(psize)\n
"},{"location":"Source/utils/#torrentfile.utils.path_size","title":"path_size(path: str) -> int
","text":"Return the total size of all files in path recursively.
PARAMETER DESCRIPTION path
path to target file or directory.
TYPE: str
RETURNS DESCRIPTION int
total size of files.
Source code in torrentfile\\utils.py
def path_size(path: str) -> int:\n\"\"\"\n Return the total size of all files in path recursively.\n\n Parameters\n ----------\n path : str\n path to target file or directory.\n\n Returns\n -------\n int\n total size of files.\n \"\"\"\n total_size, _ = filelist_total(path)\n return total_size\n
"},{"location":"Source/utils/#torrentfile.utils.path_stat","title":"path_stat(path: str) -> tuple
","text":"Calculate directory statistics.
PARAMETER DESCRIPTION path
The path to start calculating from.
TYPE: str
RETURNS DESCRIPTION Tuple[list, int, int]
list - List of all files contained in Directory int - Total sum of bytes from all contents of dir int - The size of pieces of the torrent contents.
Source code in torrentfile\\utils.py
def path_stat(path: str) -> tuple:\n\"\"\"\n Calculate directory statistics.\n\n Parameters\n ----------\n path : str\n The path to start calculating from.\n\n Returns\n -------\n Tuple[list, int, int] :\n list - List of all files contained in Directory\n int - Total sum of bytes from all contents of dir\n int - The size of pieces of the torrent contents.\n \"\"\"\n total_size, filelist = filelist_total(path)\n piece_length = get_piece_length(total_size)\n return (filelist, total_size, piece_length)\n
"},{"location":"Source/utils/#torrentfile.utils.toggle_debug_mode","title":"toggle_debug_mode(switch_on: bool)
","text":"Switch the environment variable debug indicator on or off.
PARAMETER DESCRIPTION switch_on
if true turn debug mode on otherwise off
TYPE: bool
Source code in torrentfile\\utils.py
def toggle_debug_mode(switch_on: bool):\n\"\"\"\n Switch the environment variable debug indicator on or off.\n\n Parameters\n ----------\n switch_on : bool\n if true turn debug mode on otherwise off\n \"\"\"\n os.environ[\"TORRENTFILE_DEBUG\"] = \"ON\" if switch_on else \"OFF\"\n
"},{"location":"Source/version/","title":"Version","text":""},{"location":"Source/version/#torrentfile.version","title":"version
","text":"Holds the release version number.
"},{"location":"coverage/","title":"Coverage","text":""}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"TorrentFile","text":""},{"location":"#overview","title":"\ud83c\udf10 Overview","text":"A command line interface for creating, reviewing, editing, or verifying bittorrent meta files (.torrent
files). torrentfile
is open source, and supports all versions of Bittorrent files, including hybrid meta files. The code base is also importable and can easily be used as a library for creating or manipulating torrent files in external projects. Documentation is available at https://alexpdev.github.io/torrentfile.
A GUI frontend for this project can be found at https://github.com/alexpdev/TorrentfileQt
"},{"location":"#requirements","title":"\ud83d\udd0c Requirements","text":" Python 3.6+ Tested on Linux, Windows and Mac "},{"location":"#install","title":"\ud83d\udcbb Install","text":"PyPi:
pip install torrentfile\n
Git:
git clone https://github.com/alexpdev/torrentfile.git\ncd torrentfile\npip install .\n
Download pre-compiled binaries from the release page.
"},{"location":"#documentation","title":"\ud83d\udcda Documentation","text":""},{"location":"#torrentfile-documentation-available-at-httpsalexpdevgithubiotorrentfile","title":"torrentfile documentation available at https://alexpdev.github.io/torrentfile","text":""},{"location":"#usage","title":"\ud83d\ude80 Usage","text":"Usage examples can be found in the project documentation on the examples page.
"},{"location":"#license","title":"\ud83d\udcdd License","text":"Apache Software License v2.0 - See LICENSE
"},{"location":"#issues-requests-prs","title":"\ud83d\udca1 Issues & Requests & PRs","text":"If you encounter any bugs or would like to request a new feature please open a new issue. PRs and other contributions that are meaningful and add value to the project are welcome.
"},{"location":"#usage-examples","title":"Usage Examples","text":""},{"location":"#creating-bittorrent-files","title":"Creating Bittorrent Files","text":"Creating a basic torrent file is as easy as using the create subcommand with the path to the torrent file.
torrentfile create /path/to/content\n
You can add one or more trackers by using any one of -a
, --announce
flags and listing their URL as a space separated list.
torrentfile create /path/to/content -a http://tracker1.com http://tracker2.net\n
If you intend to distribute the file on a private tracker then you should use one of -p
, --private
flags, which tells your Bittorrent clients to disable DHT and multitracker protocols.
torrentfile create /path/to/content --private\n
By default torrentfile
displays a progress bar indicating how much of the content has already been processed. To turn off this display you can either use --quiet
mode in as a global flag or you can set the --prog
flag to 0.
torrentfile create /path/to/content --prog 0\n
torrentfile
extracts the name of the contents top level file or directory and saves the torrent file to the current working directory with the extracted title.
For example running the follwing command would create ./content.torrent
.
torrentfile create /path/to/content\n
To specify an alternative path or filename you may use the -o
, --out
flags followed by the path to the preferred destination.
torrentfile create /path/to/content -o /some/other/path/torrent.torrent\n
If the path specified is an existing directory, then the torrent file will be saved to that directory, with same filename as the default top level path name.
For example the following command would create a torrent file at /some/other/path/content.torrent
.
torrentfile create /path/to/content -o /some/other/path/\n
torrentfile
creates Bittorrent v1 files by default. To create a V2 or hybrid (v1 and v2) torrent file, use the --meta-version
option followed by the preferred version number option. The options include: 1
(v1 default), 2
(v2), or 3
(v1 & v2).
torrentfile create /path/to/content --meta-version 2\n
torrentfile create /path/to/content --meta-version 3
torrentfile
includes the option to command line flags for the create
sub-command from an ini
style configuration file, by using the --config
and optional --config-path
options to specify the path to the configuration file. If --config-path
is ommited, then torrentfile
will look by default in the current working directory for a file named torrentfile.ini
. If the file is not discovered in the current working directory, it will move on to look ~/.torrentfile/torrentfile.ini
followed by ~/.config/torrentfile.ini
. Please see the documentation for more details on how the configuration file should be formatted.
"},{"location":"#checkrecheck-torrent","title":"Check/Recheck Torrent","text":"The recheck
subcommand allows you to scan a Bittorrent file and compare it\u2019s contents, against a file or directory containing the contents the torrent file was created from. The output provided by this process gives a detailed perspective if any files are missing or have been corrupted in any way. Supports any version of Bittorrent file.
torrentfile recheck /path/to/some.torrent /path/to/content\n
"},{"location":"#edit-torrent","title":"Edit Torrent","text":"To edit specific fields of the torrent file, there is the edit
subcommand. Using this subcommand you can specify the field with one of the available field flags, for example --announce
and specify the value you wish to change it to.
torrentfile edit /path/to/content --announce https://new.tracker.url1.com https://newtracker.url/2\n
You can use the -h
flag for a full list of available fields that can be edited.
torrentfile edit -h\n
"},{"location":"#create-magnet","title":"Create Magnet","text":"To create a magnet URI for a pre-existing torrent meta file, use the sub-command magnet
with the path to the torrent file.
torrentfile magnet /path/to/some.torrent\n
"},{"location":"#gui","title":"GUI","text":"If you prefer a windowed GUI please check out the official GUI frontend here
"},{"location":"Apache2/","title":"Apache License","text":"Version 2.0, January 2004 <http://www.apache.org/licenses/>
"},{"location":"Apache2/#terms-and-conditions-for-use-reproduction-and-distribution","title":"Terms and Conditions for use, reproduction, and distribution","text":""},{"location":"Apache2/#1-definitions","title":"1. Definitions","text":"\u201cLicense\u201d shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
\u201cLicensor\u201d shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
\u201cLegal Entity\u201d shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \u201ccontrol\u201d means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
\u201cYou\u201d (or \u201cYour\u201d) shall mean an individual or Legal Entity exercising permissions granted by this License.
\u201cSource\u201d form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
\u201cObject\u201d form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
\u201cWork\u201d shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
\u201cDerivative Works\u201d shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
\u201cContribution\u201d shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \u201csubmitted\u201d means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \u201cNot a Contribution.\u201d
\u201cContributor\u201d shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
"},{"location":"Apache2/#2-grant-of-copyright-license","title":"2. Grant of Copyright License","text":"Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
"},{"location":"Apache2/#3-grant-of-patent-license","title":"3. Grant of Patent License","text":"Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
"},{"location":"Apache2/#4-redistribution","title":"4. Redistribution","text":"You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a \u201cNOTICE\u201d text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
"},{"location":"Apache2/#5-submission-of-contributions","title":"5. Submission of Contributions","text":"Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
"},{"location":"Apache2/#6-trademarks","title":"6. Trademarks","text":"This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
"},{"location":"Apache2/#7-disclaimer-of-warranty","title":"7. Disclaimer of Warranty","text":"Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \u201cAS IS\u201d BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
"},{"location":"Apache2/#8-limitation-of-liability","title":"8. Limitation of Liability","text":"In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
"},{"location":"Apache2/#9-accepting-warranty-or-additional-liability","title":"9. Accepting Warranty or Additional Liability","text":"While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
"},{"location":"Apache2/#appendix-how-to-apply-the-apache-license-to-your-work","title":"APPENDIX: How to apply the Apache License to your work","text":"To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets []
replaced with your own identifying information. (Don\u2019t include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \u201cprinted page\u201d as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n
"},{"location":"api/","title":"TorrentFile","text":""},{"location":"api/#api-and-source","title":"API and Source","text":""},{"location":"api/#torrent-module","title":"Torrent
Module","text":"module torrentfile.
torrent
Classes and procedures pertaining to the creation of torrent meta files.
Classes MetaFile
\u2014 Base Class for all TorrentFile classes. TorrentFile
\u2014 Class for creating Bittorrent meta files. TorrentFileV2
\u2014 Class for creating Bittorrent meta v2 files. TorrentFileHybrid
\u2014 Construct the Hybrid torrent meta file with provided parameters. TorrentAssembler
\u2014 Assembler class for Bittorrent version 2 and hybrid meta files. "},{"location":"api/#classes","title":"Classes","text":" TorrentFile
construct .torrent file.
TorrentFileV2
construct .torrent v2 files using provided data.
MetaFile
base class for all MetaFile classes.
"},{"location":"api/#constants","title":"Constants","text":""},{"location":"api/#bittorrent-v2","title":"Bittorrent V2","text":"From Bittorrent.org Documentation pages.
Implementation details for Bittorrent Protocol v2.
Note
All strings in a .torrent file that contain text must be UTF-8 encoded.
"},{"location":"api/#meta-version-2-dictionary","title":"Meta Version 2 Dictionary:","text":" \u201cannounce\u201d: The URL of the tracker.
\u201cinfo\u201d: This maps to a dictionary, with keys described below.
\u201cname\u201d: A display name for the torrent. It is purely advisory.
\u201cpiece length\u201d: The number of bytes that each logical piece in the peer protocol refers to. I.e. it sets the granularity of piece, request, bitfield and have messages. It must be a power of two and at least 6KiB.
\u201cmeta version\u201d: An integer value, set to 2 to indicate compatibility with the current revision of this specification. Version 1 is not assigned to avoid confusion with BEP3. Future revisions will only increment this issue to indicate an incompatible change has been made, for example that hash algorithms were changed due to newly discovered vulnerabilities. Lementations must check this field first and indicate that a torrent is of a newer version than they can handle before performing other idations which may result in more general messages about invalid files. Files are mapped into this piece address space so that each non-empty
\u201cfile tree\u201d: A tree of dictionaries where dictionary keys represent UTF-8 encoded path elements. Entries with zero-length keys describe the properties of the composed path at that point. \u2018UTF-8 encoded\u2019 context only means that if the native encoding is known at creation time it must be converted to UTF-8. Keys may contain invalid UTF-8 sequences or characters and names that are reserved on specific filesystems. Implementations must be prepared to sanitize them. On platforms path components exactly matching \u2018.\u2019 and \u2018..\u2019 must be sanitized since they could lead to directory traversal attacks and conflicting path descriptions. On platforms that require UTF-8 path components this sanitizing step must happen after normalizing overlong UTF-8 encodings. File is aligned to a piece boundary and occurs in same order as the file tree. The last piece of each file may be shorter than the specified piece length, resulting in an alignment gap.
\u201clength\u201d: Length of the file in bytes. Presence of this field indicates that the dictionary describes a file, not a directory. Which means it must not have any sibling entries.
\u201cpieces root\u201d: For non-empty files this is the the root hash of a merkle tree with a branching factor of 2, constructed from 16KiB blocks of the file. The last block may be shorter than 16KiB. The remaining leaf hashes beyond the end of the file required to construct upper layers of the merkle tree are set to zero. As of meta version 2 SHA2-256 is used as digest function for the merkle tree. The hash is stored in its binary form, not as human-readable string.
\u201cpiece layers\u201d: A dictionary of strings. For each file in the file tree that is larger than the piece size it contains one string value. The keys are the merkle roots while the values consist of concatenated hashes of one layer within that merkle tree. The layer is chosen so that one hash covers piece length bytes. For example if the piece size is 16KiB then the leaf hashes are used. If a piece size of 128KiB is used then 3rd layer up from the leaf hashes is used. Layer hashes which exclusively cover data beyond the end of file, i.e. are only needed to balance the tree, are omitted. All hashes are stored in their binary format. A torrent is not valid if this field is absent, the contained hashes do not match the merkle roots or are not from the correct layer.
Important
The file tree root dictionary itself must not be a file, i.e. it must not contain a zero-length key with a dictionary containing a length key.
"},{"location":"api/#bittorrent-v1","title":"Bittorrent V1","text":""},{"location":"api/#v1-meta-dictionary","title":"v1 meta-dictionary","text":" Note
In the single file case, the name key is the name of a file, in the muliple file case, it\u2019s the name of a directory.
"},{"location":"api/#utils-module","title":"Utils
Module","text":"module torrentfile.
utils
Utility functions and classes used throughout package.
Classes Memo
\u2014 Memoize cache. MissingPathError
\u2014 Path parameter is required to specify target content. PieceLengthValueError
\u2014 Piece Length parameter must equal a perfect power of 2. ArgumentError
\u2014 Exception for mismatched or mistyped CLI arguments. Functions check_path_writable
(
path
)
(bool) \u2014 Test if output path is writable. colored
(
string
, key
)
(str) \u2014 Output terminal content with formatting. copypath
(
source
, dest
)
\u2014 Copy the file located at source to dest. debug_is_on
(
)
(bool) \u2014 Return True if debug mode is on in environment variables. get_file_list
(
path
)
(list :) \u2014 Return a sorted list of file paths contained in directory. get_piece_length
(
size
)
(int) \u2014 Calculate the ideal piece length for bittorrent data. green
(
string
)
(str) \u2014 Output terminal content in green color. humanize_bytes
(
amount
)
(str) \u2014 Convert integer into human readable memory sized denomination. next_power_2
(
value
)
(int) \u2014 Calculate the next perfect power of 2 equal to or greater than value. normalize_piece_length
(
piece_length
)
(int) \u2014 Verify input piece_length is valid and convert accordingly. path_piece_length
(
path
)
(int) \u2014 Calculate piece length for input path and contents. path_size
(
path
)
(int) \u2014 Return the total size of all files in path recursively. path_stat
(
path
)
(Tuple[list, int, int] :) \u2014 Calculate directory statistics. toggle_debug_mode
(
switch_on
)
\u2014 Switch the environment variable debug indicator on or off. "},{"location":"api/#edit-module","title":"Edit
Module","text":"module torrentfile.
edit
Edit torrent module.
Provides a facility by which certain properties of a torrent meta file can be edited by the user. The various command line arguments indicate which fields should be edited, and what the new value should be. Depending on what fields are chosen to edit, this command can trigger a new info hash which means the torrent will no longer be able to participate in the same swarm as the original unedited torrent.
Functions edit_torrent
(
metafile
, args
)
(dict) \u2014 Edit the properties and values in a torrent meta file. filter_empty
(
args
, meta
, info
)
\u2014 Remove the fields that were not used by the original file creator. "},{"location":"api/#interactive-module","title":"Interactive
Module","text":"module torrentfile.
interactive
Module contains the procedures used for Interactive Mode.
Classes InteractiveEditor
\u2014 Interactive dialog class for torrent editing. InteractiveCreator
\u2014 Class namespace for interactive program options. Functions create_torrent
(
)
\u2014 Create new torrent file interactively. edit_action
(
)
\u2014 Edit the editable values of the torrent meta file. get_input
(
*args
)
(str) \u2014 Determine appropriate input function to call. recheck_torrent
(
)
\u2014 Check torrent download completed percentage. select_action
(
)
\u2014 Operate TorrentFile program interactively through terminal. showcenter
(
txt
)
\u2014 Print text to screen in the center position of the terminal. showtext
(
txt
)
\u2014 Print contents of txt to screen. "},{"location":"api/#this-module-has-been-deprecated","title":"This module has been deprecated.","text":""},{"location":"api/#cli-module","title":"CLI
Module","text":"module torrentfile.
cli
Command Line Interface for TorrentFile project.
This module provides the primary command line argument parser for the torrentfile package. The main_script function is automatically invoked when called from command line, and parses accompanying arguments.
Classes Config
\u2014 Class the controls the logging configuration and output settings. TorrentFileHelpFormatter
\u2014 Formatting class for help tips provided by the CLI. Functions execute
(
args
)
(list) \u2014 Execute program with provided list of arguments. execute
(
args
)
(list) \u2014 Execute program with provided list of arguments. main
(
)
\u2014 Initiate main function for CLI script. "},{"location":"api/#functions","title":"Functions","text":"main_script : process command line arguments and run program. activate_logger : turns on debug mode and logging facility.
"},{"location":"api/#classes_1","title":"Classes","text":"Config : class controls logging configuration TorrentFileHelpFormatter : HelpFormatter the command line help message formatter
"},{"location":"api/#recheck-module","title":"Recheck
Module","text":"module torrentfile.
recheck
Module container Checker Class.
The CheckerClass takes a torrentfile and tha path to it\u2019s contents. It will then iterate through every file and directory contained and compare their data to values contained within the torrent file. Completion percentages will be printed to screen for each file and at the end for the torrentfile as a whole.
Classes Checker
\u2014 Check a given file or directory to see if it matches a torrentfile. FeedChecker
\u2014 Validates torrent content. HashChecker
\u2014 Iterate through contents of meta data and verify with file contents. "},{"location":"api/#hasher-module","title":"Hasher
Module","text":"module torrentfile.
hasher
Piece/File Hashers for Bittorrent meta file contents.
Classes Hasher
\u2014 Piece hasher for Bittorrent V1 files. HasherV2
\u2014 Calculate the root hash and piece layers for file contents. HasherHybrid
\u2014 Calculate root and piece hashes for creating hybrid torrent file. FileHasher
\u2014 Calculate root and piece hashes for creating hybrid torrent file. Functions merkle_root
(
blocks
)
(bytes) \u2014 Calculate the merkle root for a seq of sha256 hash digests. "},{"location":"api/#coverage-map","title":"Coverage Map","text":""},{"location":"changelog/","title":"CHANGELOG","text":""},{"location":"changelog/#version-092","title":"Version 0.9.2","text":" Made progress bar now appear in a green color on supported terminals Added progress bar mode #2 new mode allows tracking full process with just 1 progress bar completely reconfigured the ProgMixin class major edits to the way progress bars are tracked in all modules other bug fixes "},{"location":"changelog/#version-091","title":"Version 0.9.1","text":" Clean up and consolidate many cli help strings Removed some of the less popularly used argument shortcuts Edited module and class level docstrings Removed old/useless code. Added --align
cli flag for piece aligning v1 torrents "},{"location":"changelog/#version-090","title":"Version 0.9.0","text":" Updates to Documentation. Updated the main Gif in the readme file Improved unittesting Added new tests for functions in utils module "},{"location":"changelog/#version-0814","title":"Version 0.8.14","text":" Updates and improvements to documentation Updates and improvements to readme file Updates and improvements to docstrings "},{"location":"changelog/#version-0813","title":"Version 0.8.13","text":" Fixed error with command line arg info
not displaying information correctly "},{"location":"changelog/#version-0812","title":"Version 0.8.12","text":" Fixed bug where torrent files could no be read when created without trackers "},{"location":"changelog/#version-0811","title":"Version 0.8.11","text":" added new command line option for magnet subcommand fixed some coverage gaps caused by 0.8.10 fixed bugs introduced by 0.8.10 with magnet URI creation added support for hybrid magnet links added tests for when a magnet link is generated with no trackers at all "},{"location":"changelog/#version-0810","title":"Version 0.8.10","text":" added support for bittorrent v2 magnet links added unit tests for v2 magnet links updated algorithm for human readable byte formatting "},{"location":"changelog/#version-089","title":"Version 0.8.9","text":" fixed bug with the config file parsing added checks to ensure the config file is parsed correctly added checks for applying configfile parameters to torrent properly deprecated and removed the --cwd
command line flag as it is now default behaviour removed unittests testing the --cwd
behaviour "},{"location":"changelog/#version-088","title":"Version 0.8.8","text":" added loading create torrent options from a configuration file added unit tests for configuration functions update documentation with new information about configuration file added CLI options for indicating the use of a configuration file --config
added a CLI option that specifies where to look for config file --config-path
removed interactive -i
mode from cli options as it is now deprecated. "},{"location":"changelog/#version-087","title":"Version 0.8.7","text":" Added the rename subcommand Added unittests for the new subcommand Default command is now \u201ccreate\u201d when user ommits entering subcommand Added unittests for default command Added shortcut command tfile
as an alternative to torrentfile
"},{"location":"changelog/#version-086","title":"Version 0.8.6","text":" Fixed bug with argument parser that allowed for duplicate aliases Added support for python 3.11 Updated CI/CD Workflow Updated documentation Deprecated the -i interactive argument option and added warnings in documentation "},{"location":"changelog/#version-085","title":"Version 0.8.5","text":" Fixed bug with linux platforms not installing a binary cli command Fixed debug logging errors with the rebuild command Improved log message readability Compatability upgrades for torrentfileQt synchronization Added coverage details to documentation folder "},{"location":"changelog/#version-084","title":"Version 0.8.4","text":" Documentation Updates Fixed logging issues with rebuild module. Improved algorithm for rebuild module. Improved testing for rebuild module. "},{"location":"changelog/#version-083","title":"Version 0.8.3","text":" Added the callback mixin to the rebuild module Fix compatability with GUI frontend torrentfileQt. "},{"location":"changelog/#version-082","title":"Version 0.8.2","text":" Rebuild subcommand now checks on a hash by hash basis Fixed coverage issues Added unittests for the rebuild command Fixed bug with torrentfile creation when a file was a perfect power of 2 Reconfigured the rebuild module Reconfigured the rebuild cli flags and arguments "},{"location":"changelog/#version-081","title":"Version 0.8.1","text":" Further improvements to documentation Fixed bug that interrupted the creation process when using gui version Added unittests Improved docstrings and docstring formatting Renamed a couple of methods "},{"location":"changelog/#version-080","title":"Version 0.8.0","text":" overhaul documentation reconfigured CI files and configuration and packaging files Convert to pyproject.toml setuptools packaging info source "},{"location":"changelog/#version-0712","title":"Version 0.7.12","text":" Changed default behavior to save torrent files to cwd edited all unittests to reflect default behavior added deprecation messages for the cli arg and class paramteter last update to version 0.7.x "},{"location":"changelog/#version-0711","title":"Version 0.7.11","text":" Fixed issue with progress bar displaying inaccurate details Other minor bug fixes Updated output for Recheck subcommand for better readability Updated documentation Updated Readme added quiet mode to cli global options -q
Added unit test to fix coverage gaps Fixed warnings created by pylint "},{"location":"changelog/#version-0710","title":"Version 0.7.10","text":" Added rebuild module and subcommand see docs for more info Added documentation entry for rebuild subcommand improved logging messages added unit tests improved and expanded on type hints minor bug fixes "},{"location":"changelog/#version-079","title":"Version 0.7.9","text":" complete rewrite of the recheck procedures Recheck now provides more accuracy and more details improvements to the new custom progressbar changed the cli argument for the progress bar the options are now just 0 and 1 included new unit tests for all new features marked unused functions as deprecated added a new hasher object for v2 and hybrid torrents minor bug fixes and styling changes "},{"location":"changelog/#version-078","title":"Version 0.7.8","text":" more updates to logging major improvements to progress bar removed tqdm as dependency implemented custom progress bar logic new cli argument controlling the progress bar support for pyben 0.3.1 added threading to recheck module added mixins module unit test updates and improvements "},{"location":"changelog/#version-075","title":"Version 0.7.5","text":" updates to logging facility fixed bug in created hybrid torrent files fixed cli when subcomman not chosen doc updates unit test updates and improvements "},{"location":"changelog/#version-072","title":"Version 0.7.2","text":" cleaned up readme and help messages removed useless print statements improved CI tooling and checking minor bug fixes "},{"location":"changelog/#version-071","title":"Version 0.7.1","text":" split CI integration into separate platform specific files added new cli argument --cwd
which changes the default save to location to the current working directory (this will be default in future) added unit tests to cover the new argument Changed license to a the more permissive Apache 2 software license "},{"location":"changelog/#version-070","title":"Version 0.7.0","text":" Fixed issues with logging to file. Finished adding tests for Unicode Support Deprecated some unneccessary code Clean up documentation and README removed config files no longer in use. "},{"location":"changelog/#version-0613","title":"Version 0.6.13","text":" Fixed bug that created a torrent file with no name. Fixed bug that would error if cli path was listed after announce urls Added full unicode support. Added Unittests for new features and bug fixes "},{"location":"changelog/#version-0611","title":"Version 0.6.11","text":" Fixed bug that occured during recheck when file of 0 length is included. Altered Recheck algorithm to process 0 length files. Only effected meta version 2 and hybrid torrent files. Added unittests to cover the situation. "},{"location":"changelog/#version-0610","title":"Version 0.6.10","text":" Updates to documentation Integrated Type hints in source code Updated build and CI process "},{"location":"changelog/#version-069","title":"Version 0.6.9","text":" The \u2013progress flag is now \u2013noprogress Default behavior is to show progress bar use \u2013noprogress to not show added CLI Help format strings added custom CLI help formatter class Titled Help Message section headers Fixed a bunch of error pages created by mkdocs "},{"location":"changelog/#version-068","title":"Version 0.6.8","text":" Documentation for newest features CLI usage examples Improved unittests made progress bar active by default "},{"location":"changelog/#version-067","title":"Version 0.6.7","text":""},{"location":"changelog/#version-066","title":"Version 0.6.6","text":" bug that created faulty Bittorrent V2 meta files in some instances. back to working as it should. "},{"location":"changelog/#version-065","title":"Version 0.6.5","text":" Support for creating Magnet URI\u2019s Added optional progress bar for torrent creation Log File handler CLI args page in documentation verbose and logging bugs multi tracker errors bug "},{"location":"changelog/#version-064","title":"Version 0.6.4","text":" CLI interface add subcommands added interactive mode Re-wrote the recheck module fixed documentation and docstrings linting and testing errors "},{"location":"changelog/#version-063","title":"Version 0.6.3","text":" Fixed Bug that would format list of trackers incorrectly CLI Bug Fixes "},{"location":"changelog/#version-062","title":"Version 0.6.2","text":" Bug fixes Documentation error pages "},{"location":"changelog/#version-060","title":"Version 0.6.0","text":" cli commands alterations debug logging during creation process "},{"location":"changelog/#version-052","title":"Version 0.5.2","text":" Fixed Bug that was adding wrong fields to info dict "},{"location":"changelog/#version-050","title":"Version 0.5.0","text":" Slew of new unit tests Stricter linting features Alternative method of -re-check feature Bug Fixes CLI help formatting errors "},{"location":"changelog/#version-048","title":"Version 0.4.8","text":" Improved Algorithm performance for ReCheck. Additions to documentation. "},{"location":"changelog/#version-047","title":"Version 0.4.7","text":" Fixed A bug that misspelled a field when creating Hybrid torrent files. Re-Check procedure for v2 and hybrid torrent file checking. "},{"location":"changelog/#version-046","title":"Version 0.4.6","text":" CLI Help and Usage Messages. Expanded CLI args. Completely new CheckerClass which replaces old Checker Hooks for GUI or other 3rd party apps to hook into Checking Documentation and Unit tests for new CheckerClass "},{"location":"changelog/#version-045","title":"Version 0.4.5","text":" Documentation and docstrings improvements Better code formating and more detailed docstrings More unit tests. "},{"location":"changelog/#version-042","title":"Version 0.4.2","text":" The ReChecker feature now supports v1, v2, & hybrid .torrent file. Bug in CLI for python < 3.8 "},{"location":"changelog/#version-041","title":"Version 0.4.1","text":" Added tests for hybrid class Added logging features new cli flag to activate debug mode Documentation theme. Fixed Bug that allowed improper piece length values. "},{"location":"changelog/#version-040","title":"Version 0.4.0","text":" Fixed bugs in creating hybrid files. Bug Fix that broke cli. "},{"location":"changelog/#version-030","title":"Version 0.3.0","text":" Added/Improved support for hybrid meta files. Many additions to testing suit including linting and coverage tests. "},{"location":"changelog/#version-028","title":"Version 0.2.8","text":" Styling fixes. Bug Fixes. Prelimenary support for bittorrent hybrid meta files. Bug Fixes "},{"location":"changelog/#version-027","title":"Version 0.2.7","text":" major imporvements to torrentfile-GUI. minor adjustments to this package for integration. Code consolidation Bug Fixes Documentation additions Implemented CI/CD Integration "},{"location":"changelog/#version-023","title":"Version 0.2.3","text":" Bug Fixes Code Style and Formatting Added more unittests "},{"location":"changelog/#version-021","title":"Version 0.2.1","text":" Bittorrent Protocol V2 Support v2 metafile options to cli v2 metafile tests "},{"location":"changelog/#version-017","title":"Version 0.1.7","text":" Docstrings Improvements. Added documentation rederer. Improved readme file. formatting "},{"location":"changelog/#version-012","title":"Version 0.1.2","text":" Added a Command Line Interface Rough Graphical User Interface Minor Bug Fixes Improved unittest coverage "},{"location":"changelog/#version-010","title":"Version 0.1.0","text":" added SHA256 support Feeder class for seemless file switching Fixed the primary entrypoint function. Improved docstrings Bug fixes "},{"location":"changelog/#version-002","title":"Version 0.0.2","text":" Added Unittests added bencode support added hashing support "},{"location":"changelog/#version-001","title":"Version 0.0.1","text":" Initial concept and planning "},{"location":"overview/","title":"torrentfile Manual","text":""},{"location":"overview/#synopsis","title":"Synopsis","text":"torrentfile
is a command line utility for working with Bittorrent files(.torrent). Some of the tools available include creating torrent files, editing portions of a torrent files, checking the integrity or completeness of downloaded torrent contents, displaying details of a torrentfile, generating magnet URLs for torrentfiles, and individual or batch rebuilding of torrent contents into their original directory structure.
torrentfile [options]`<subcommand>` [options] `<args>`\n
-h
displays all relevant command line options and subcommands.
-V
displays program and version.
-v
enables debug mode and outputs a large amount of information to the terminal.
"},{"location":"overview/#sub-commands","title":"Sub-commands","text":""},{"location":"overview/#create","title":"create","text":"alias: c
The create subcommand is used for generating new torrent files. The only required argument is the path(<path>
) to the contents file or directory.
torrentfile create [options]`<path>`\ntorrentfile c [options] `<path>`\n
-a
--announce
Adds the list of url\u2019s that follow to the list of trackers for the newly created torrent file.
Example -t http://url1 https://url2 ...
-p
--private
Indicates that the torrent will be used on a private tracker. Disables multi-tracker protocols and DHT.
--web-seed
Adds the list of urls that follow to the list of web-seed addresses for the newly created torrent file.
--http-seed
Same as the -web-seed
argument except using Hoffman style web-seeds.
-c
--comment
Includes the comment that follows to the metadata saved in the newly created torrent file.
Example: --comment \"Created for MyTrackerExample.com\"
--source
Creates a source field in the info dictionary with the string that follows as the value. Frequently used for cross-seeding on private trackers.
Example: --source MyTrackerExample
--piece-length
Uses the number that follows as the piece-length size for the newly created torrent file. If option isn\u2019t used the program will pick the ideal size. Acceptable values include 14-29 which is interpreted as the number to raise 2 by (e.g. 14 is interpreted as 16384), or any perfect power of 2 greater than or equal to 16 KiB and less than 1 GiB.
Example: --piece-length 14
or --piece-length 16384
.
--meta-version
Use the following number as the Bittorrent version the torrent will be used on. The default is 1.
Options: 1 - Bittorrent v1, 2 - Bittorrent v2, 3 - Bittorrent v1 & v2 Examples: --meta-version 2
, --meta-version 3
-o
--out
Specify the full path to the newly created torrent file. The default is to save it adjacent to the content.
Example: if content is at /home/user/torrents/content
the default would create /home/user/torrents/content.torrent
--progress
--prog
Options (0, 1): No status bar will be shown if 0. Otherwise the default and \u201c1\u201d means progress bar is shown
--config
Load options from a configuration file. default torrentfile.ini
file can use the same long options names used for the command line The current available options that can be marked in the config file include: announce
, source
, comment
, http-seeds
, web-seeds
, piece-length
, ``
Default search paths and priority:
./torrentfile.ini
~/.torrentfile/torrentfile.ini
~/.config/torrentfile.ini
--config-path
Use in combination with --config
to specify path to the configuration file.
Example: torrentfile create --config --config-path ./dir/config.ini <content-path>
Example Configuration File
torrentfile.ini
[config]\nprivate = false\ncomment = example torrent comment\nannounce =\n https://tracker1.net/announce\n https://example.org/2324announce\nhttp-seed = \n https://example.url/path/to/content\nweb-seed =\n ftp://ftp.example.site/content\nmeta-version = 2\npiece-length = 18\nout = /path/to/file/content.torrent\n
"},{"location":"overview/#info","title":"info","text":"alias: i
Display detailed information about a torrentfile such as trackers, size of contents, Bittorrent version, any comments left, date the torrent file was created and more. There is only one positional perameter which is the path to the torrent file and there are no optional arguments.
torrentfile info`<path>`\ntorrentfile i `<path>`\n
/path/to/*.torrent
The relative or absolute path to the torrent file. "},{"location":"overview/#edit","title":"edit","text":"Edit some of the different information detailed in a torrent file. The fields that are editable each have option flags detialed below. Each option identifies the field to edit inside the torrent file and what the new value should be. If an option is not used then its field will be ommited in the newly created torrent file. As such if the file is marked as private and it should remain that way, the -p
option should be used.
alias: e
torrentfile edit [options]`<path>`\ntorrentfile e [options] `<path>`\n
-a
-t
--announce
--tracker
Adds the list of url\u2019s that follow to the list of trackers for the newly created torrent file.
Example -t http://url1 https://url2 ...
-w
--web-seeds
Adds the list of urls that follow to the list of web-seed addresses for the newly created torrent file.
--comment
Includes the comment that follows to the metadata saved in the newly created torrent file.
Example: --comment \"Created for MyTrackerExample.com\"
--source
Creates a source field in the info dictionary with the string that follows as the value. Frequently used for cross-seeding on private trackers.
Example: --source MyTrackerExample
-p
--private
Indicates that the torrent will be used on a private tracker. Disables multi-tracker protocols and DHT.
"},{"location":"overview/#recheck","title":"recheck","text":"This feature is identical to the \u2018recheck\u2019 function provided by torrent clients. It validates that the file/directory contents match those described in the torrent file.
Recheck requires two paths as arguments. The first is the path to a torrent file, and and the second is a path to the file of directory containing the downloaded data from that torrentfile. torrentfile
recursively validates each file with the hashes contained in the torrentfile, and displays the total percentage of the file that matches the information from the torrent file. It is also permitted to use the contents parent directory which can help for batch processing many torrent files.
alias: r
, check
torrentfile recheck <*.torrent> `<contents>`\ntorrentfile r <*.torrent> `<contents>`\n
"},{"location":"overview/#magnet","title":"Magnet","text":"Generate a magnet URL for a torrent file.
alias: m
torrentfile magnet <path/to/*.torrent>\n
"},{"location":"overview/#rebuild","title":"Rebuild","text":"Rebuild individual or batches of torrent contents into the original file structure. The program takes a path to a torrent file or directory containing torrent files, the directory containing the torrent contents, and the destination directory to where the rebuilt torrent content wil be located. The program will recursively traverse the content directory searching for file\u2019s that match one of the meta files and creates copies of the matches to the destination directory. The original files are not effected and any existing files in the target directory will not be overwritten.
alias: build
, b
torrentfile rebuild -m`<metafiles>` -c `<contents>` -d `<destination>`\n
-m
--metafiles
path to a torrent file or a directory containing torrent files for batch processing.
-c
--content
path to where the contents for the torrentfile can be found. If the path is for a directory, the directory will be searched recusively for files that match. For deeply nested directories with lot\u2019s of files in them, this can take time.
-d
-destination
path to a directory where the torrent will be rebuilt. Files will be copied from their original location, not moved.
"},{"location":"usage/","title":"TorrentFile CLI Menu","text":""},{"location":"usage/#help-messages","title":"Help Messages","text":""},{"location":"usage/#main","title":"Main","text":"Usage\n=====\n torrentfile [options] command [command options]\n\nCommand line tools for creating, editing, checking, building and interacting with Bittorrent metainfo files\n
Options -h, \u2013help show this help message and exit -i, \u2013interactive select program options interactively -q, \u2013quiet Turn off all text output. -V, \u2013version show program version and exit Commands create, edit, info, magnet, recheck, rebuild create (c, new) Create a new Bittorrent file.\n edit (e) Edit existing torrent meta file.\n info (i) Show detailed information about a torrent file.\n magnet (m) Generate magnet url from an existing Bittorrent meta file.\n recheck (check) Gives a detailed look at how much of the torrent is available.\n rebuild (build) Re-assemble files obtained from a bittorrent file into the\n appropriate file structure for re-seeding. Read documentation\n for more information, or use cases.\n
"},{"location":"usage/#create","title":"Create","text":"Usage\n=====\n\ntorrentfile [options] create\n [-h] [-a`<url>` [`<url>` ...]] [-p]\n [-s `<source>`] [-m] [-c `<comment>`]\n [-o `<path>`] [--cwd] [--prog PROGRESS]\n [--meta-version `<int>`]\n [--piece-length `<int>`]\n [-w `<url>` [`<url>` ...]]\n [--http-seed `<url>` [`<url>` ...]]\n [`<content>`]\n
Positional Arguments <content>
Path to content file or directory Options -h, \u2013help show this help message and exit -a<url>
[<url>
\u2026], -t <url>
[<url>
\u2026], \u2013announce <url>
[<url>
\u2026], \u2013tracker <url>
[<url>
\u2026] One or more space-seperated torrent tracker url(s). -p, \u2013private Creates private torrent with multi-tracker and DHT turned off. -s<source>
, \u2013source <source>
Add a source string. Useful for cross-seeding. -m, \u2013magnet -c<comment>
, \u2013comment <comment>
Include a comment in file metadata -o<path>
, \u2013out <path>
Explicitly specify the path to write the file. \u2013cwd, \u2013current*deprecated* Saving to current directory is default behaviour \u2013prog PROGRESS, \u2013progress PROGRESS Set the progress bar level. Options = 0, 1 (0) = Do not display progress bar. (1) = Display progress bar.(default) --meta-version`<int>` Bittorrent metafile version.\n Options = 1, 2, 3\n (1) = Bittorrent v1 (Default)\n (2) = Bittorrent v2\n (3) = Bittorrent v1 & v2 hybrid\n\n--piece-length`<int>` (Default: `<blank>`) Number of bytes for per chunk of data transmitted\n by Bittorrent client. Acceptable values include integers 14-26 which\n will be interpreted as a perfect power of 2. e.g. 14 = 16KiB pieces.\n Examples:: [--piece-length 14] [--piece-length 20]\n\n-w`<url>` [`<url>` ...], --web-seed `<url>` [`<url>` ...]\n list of web addresses where torrent data exists (GetRight).\n --http-seed `<url>` [`<url>` ...]\n list of URLs, addresses where content can be found (Hoffman).\n
"},{"location":"usage/#edit","title":"Edit","text":"Usage\n=====\ntorrentfile e [-h] [--tracker`<url>` [`<url>` ...]]\n [--web-seed `<url>` [`<url>` ...]] [--private]\n [--comment `<comment>`] [--source `<source>`]\n <*.torrent>\n
Positional Arguments <*.torrent> path to *.torrent file Optional Arguments -h, \u2013help show this help message and exit \u2013tracker<url>
[<url>
\u2026] Replace current list of tracker/announce urls with one or more space seperated Bittorrent tracker announce url(s). --web-seed`<url>` [`<url>` ...] Replace current list of web-seed urls with one or more space seperated url(s)\n\n--private Make torrent private.\n--comment`<comment>` Replaces any existing comment with `<comment>`\n--source `<source>` Replaces current source with `<source>`\n
"},{"location":"usage/#recheck","title":"Recheck","text":"Usage\n=====\ntorrentfile r [-h] <*.torrent>`<content>`\n
Positional Arguments <*.torrent> path to .torrent file. <content>
path to content file or directory Optional Arguments -h, \u2013help show this help message and exit"},{"location":"usage/#magnet","title":"Magnet","text":"Usage\n=====\ntorrentfile m [-h] <*.torrent>\n
Positional Arguments <*.torrent> Path to Bittorrent meta file. Optional Arguments -h, \u2013help show this help message and exit"},{"location":"Source/","title":"Source Code Modules","text":"torrentfile
is an open source project.
The official source code can be viewed on github at https://github.com/alexpdev/torrentfile.
"},{"location":"Source/#modules","title":"Modules","text":""},{"location":"Source/#cli","title":"cli","text":""},{"location":"Source/#commands","title":"commands","text":""},{"location":"Source/#edit","title":"edit","text":""},{"location":"Source/#hasher","title":"hasher","text":""},{"location":"Source/#interactive","title":"interactive","text":""},{"location":"Source/#mixins","title":"mixins","text":""},{"location":"Source/#rebuild","title":"rebuild","text":""},{"location":"Source/#recheck","title":"recheck","text":""},{"location":"Source/#torrent","title":"torrent","text":""},{"location":"Source/#utils","title":"utils","text":""},{"location":"Source/#version","title":"version","text":""},{"location":"Source/cli/","title":"Cli","text":""},{"location":"Source/cli/#torrentfile.cli","title":"cli
","text":"Command Line Interface for TorrentFile project.
This module provides the primary command line argument parser for the torrentfile package. The main_script function is automatically invoked when called from command line, and parses accompanying arguments.
"},{"location":"Source/cli/#torrentfile.cli--functions","title":"Functions","text":"main_script : process command line arguments and run program. activate_logger : turns on debug mode and logging facility.
"},{"location":"Source/cli/#torrentfile.cli--classes","title":"Classes","text":"Config : class controls logging configuration TorrentFileHelpFormatter : HelpFormatter the command line help message formatter
"},{"location":"Source/cli/#torrentfile.cli.Config","title":"Config
","text":"Class the controls the logging configuration and output settings.
Controls the logging level, or whether to app should operate in quiet mode.
"},{"location":"Source/cli/#torrentfile.cli.Config.activate_logger","title":"activate_logger()
staticmethod
","text":"Activate the builtin logging mechanism when passed debug flag from CLI.
Source code in torrentfile\\cli.py
@staticmethod\ndef activate_logger():\n\"\"\"\n Activate the builtin logging mechanism when passed debug flag from CLI.\n \"\"\"\n logging.basicConfig(level=logging.WARNING)\n logger = logging.getLogger()\n console_handler = logging.StreamHandler(stream=sys.stderr)\n stream_formatter = logging.Formatter(\n \"[%(asctime)s] [%(levelno)s] %(message)s\",\n datefmt=\"%H:%M:%S\",\n style=\"%\",\n )\n console_handler.setFormatter(stream_formatter)\n console_handler.setLevel(logging.DEBUG)\n logger.setLevel(logging.DEBUG)\n logger.addHandler(console_handler)\n logger.debug(\"Debug: ON\")\n toggle_debug_mode(True)\n
"},{"location":"Source/cli/#torrentfile.cli.Config.activate_quiet","title":"activate_quiet()
staticmethod
","text":"Activate quiet mode for the duration of the programs life.
When quiet mode is enabled, no logging, progress or state information is output to the terminal
Source code in torrentfile\\cli.py
@staticmethod\ndef activate_quiet():\n\"\"\"\n Activate quiet mode for the duration of the programs life.\n\n When quiet mode is enabled, no logging, progress or state information\n is output to the terminal\n \"\"\"\n if sys.stdout or sys.stderr:\n sys.stdout = io.StringIO()\n sys.stderr = io.StringIO()\n
"},{"location":"Source/cli/#torrentfile.cli.TorrentFileHelpFormatter","title":"TorrentFileHelpFormatter(prog: str, width: int = 45, max_help_positions: int = 45)
","text":" Bases: HelpFormatter
Formatting class for help tips provided by the CLI.
Subclasses Argparse.HelpFormatter.
Construct HelpFormat class for usage output.
PARAMETER DESCRIPTION prog
Name of the program.
TYPE: str
width
Max width of help message output.
TYPE: int
DEFAULT: 45
max_help_positions
max length until line wrap.
TYPE: int
DEFAULT: 45
Source code in torrentfile\\cli.py
def __init__(self,\n prog: str,\n width: int = 45,\n max_help_positions: int = 45):\n\"\"\"\n Construct HelpFormat class for usage output.\n\n Parameters\n ----------\n prog : str\n Name of the program.\n width : int\n Max width of help message output.\n max_help_positions : int\n max length until line wrap.\n \"\"\"\n super().__init__(prog,\n width=width,\n max_help_position=max_help_positions)\n
"},{"location":"Source/cli/#torrentfile.cli.execute","title":"execute(args: list = None) -> list
","text":"Execute program with provided list of arguments.
If no arguments are given then it defaults to using sys.argv. This is the main entrypoint for the program and command line interface.
PARAMETER DESCRIPTION args
Commandline arguments. default=None
TYPE: list
DEFAULT: None
RETURNS DESCRIPTION list
Depends on what the command line args were.
Source code in torrentfile\\cli.py
def execute(args: list = None) -> list:\n\"\"\"\n Execute program with provided list of arguments.\n\n If no arguments are given then it defaults to using\n sys.argv. This is the main entrypoint for the program\n and command line interface.\n\n Parameters\n ----------\n args : list\n Commandline arguments. default=None\n\n Returns\n -------\n list\n Depends on what the command line args were.\n \"\"\"\n toggle_debug_mode(False)\n if not args:\n if sys.argv[1:]:\n args = sys.argv[1:]\n else:\n args = [\"-h\"]\n\n parser = ArgumentParser(\n \"torrentfile\",\n usage=\"torrentfile <options>\",\n description=(\n \"Command line tool for creating, editing, validating, building \"\n \"and interacting with all versions of Bittorrent files\"),\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n conflict_handler=\"resolve\",\n )\n\n parser.add_argument(\n \"-q\",\n \"--quiet\",\n help=\"Turn off all text output.\",\n dest=\"quiet\",\n action=\"store_true\",\n )\n\n parser.add_argument(\n \"-V\",\n \"--version\",\n action=\"version\",\n version=f\"torrentfile v{version}\",\n help=\"show program version and exit\",\n )\n\n parser.add_argument(\n \"-v\",\n \"--verbose\",\n action=\"store_true\",\n dest=\"debug\",\n help=\"output debug information\",\n )\n\n parser.set_defaults(func=parser.print_help)\n\n subparsers = parser.add_subparsers(\n title=\"Commands\",\n dest=\"command\",\n metavar=\"create, edit, info, magnet, recheck, rebuild, rename\\n\",\n )\n\n create_parser = subparsers.add_parser(\n \"create\",\n help=\"Create a new Bittorrent file.\",\n prefix_chars=\"-\",\n aliases=[\"new\"],\n formatter_class=TorrentFileHelpFormatter,\n )\n\n create_parser.add_argument(\n \"-a\",\n \"--announce\",\n \"--tracker\",\n action=\"store\",\n dest=\"announce\",\n metavar=\"<url>\",\n nargs=\"+\",\n default=[],\n help=\"one or more space-seperated tracker url(s)\",\n )\n\n create_parser.add_argument(\n \"-p\",\n \"--private\",\n action=\"store_true\",\n dest=\"private\",\n help=\"create private torrent\",\n )\n\n create_parser.add_argument(\n \"-s\",\n \"--source\",\n action=\"store\",\n dest=\"source\",\n metavar=\"<source>\",\n help=\"add source field to the metadata\",\n )\n\n create_parser.add_argument(\n \"--config\",\n action=\"store_true\",\n dest=\"config\",\n help=\"\"\"\n Parse torrent information from a config file. Looks in the current\n working directory, or the directory named .torrentfile in the users\n home directory for a torrentfile.ini file. See --config-path option.\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--config-path\",\n action=\"store\",\n metavar=\"<path>\",\n dest=\"config_path\",\n help=\"use in combination with --config to provide config file path\",\n )\n\n create_parser.add_argument(\n \"-m\",\n \"--magnet\",\n action=\"store_true\",\n dest=\"magnet\",\n )\n\n create_parser.add_argument(\n \"-c\",\n \"--comment\",\n action=\"store\",\n dest=\"comment\",\n metavar=\"<comment>\",\n help=\"include a comment in the torrent file metadata\",\n )\n\n create_parser.add_argument(\n \"-o\",\n \"--out\",\n action=\"store\",\n dest=\"outfile\",\n metavar=\"<path>\",\n help=\"path to write torrent file\",\n )\n\n create_parser.add_argument(\n \"--prog\",\n \"--progress\",\n default=\"1\",\n action=\"store\",\n dest=\"progress\",\n metavar=\"<int>\",\n help=\"\"\"\n set the progress bar level\n Options = 0, 1, 2\n (0) = Do not display progress bar.\n (1) = Display progress bar for each file.(default)\n (2) = Display one progress bar for full torrent.\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--meta-version\",\n default=\"1\",\n choices=[\"1\", \"2\", \"3\"],\n action=\"store\",\n dest=\"meta_version\",\n metavar=\"<int>\",\n help=\"\"\"\n bittorrent metafile version\n options = 1, 2, 3\n (1) = Bittorrent v1 (Default)\n (2) = Bittorrent v2\n (3) = Bittorrent v1 & v2 hybrid\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--piece-length\",\n action=\"store\",\n dest=\"piece_length\",\n metavar=\"<int>\",\n help=\"\"\"\n (Default: auto calculated based on total size of content)\n acceptable values include numbers 14-26\n 14 = 16KiB, 20 = 1MiB, 21 = 2MiB etc. Examples:[--piece-length 14]\n \"\"\",\n )\n\n create_parser.add_argument(\n \"--web-seed\",\n action=\"store\",\n dest=\"url_list\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"list of web addresses where torrent data exists (GetRight)\",\n )\n\n create_parser.add_argument(\n \"--http-seed\",\n action=\"store\",\n dest=\"httpseeds\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"list of URLs, addresses where content can be found (Hoffman)\",\n )\n\n create_parser.add_argument(\n \"--align\",\n action=\"store_true\",\n help=(\"Align pieces to file boundaries. \"\n \"This option is ignored when not used with V1 torrents.\"),\n )\n\n create_parser.add_argument(\n \"content\",\n action=\"store\",\n metavar=\"<content>\",\n nargs=\"?\",\n help=\"path to content file or directory\",\n )\n\n create_parser.set_defaults(func=commands.create)\n\n edit_parser = subparsers.add_parser(\n \"edit\",\n help=\"edit torrent file\",\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n edit_parser.add_argument(\n \"metafile\",\n action=\"store\",\n help=\"path to *.torrent file\",\n metavar=\"<*.torrent>\",\n )\n\n edit_parser.add_argument(\n \"--tracker\",\n action=\"store\",\n dest=\"announce\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"replace current trackers with one or more urls\",\n )\n\n edit_parser.add_argument(\n \"--web-seed\",\n action=\"store\",\n dest=\"url_list\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"replace current web-seed with one or more url(s)\",\n )\n\n edit_parser.add_argument(\n \"--http-seed\",\n action=\"store\",\n dest=\"httpseeds\",\n metavar=\"<url>\",\n nargs=\"+\",\n help=\"replace current http-seed urls with new ones (Hoffman)\",\n )\n\n edit_parser.add_argument(\n \"--private\",\n action=\"store_true\",\n help=\"make torrent private\",\n dest=\"private\",\n )\n\n edit_parser.add_argument(\n \"--comment\",\n help=\"replaces any existing comment with <comment>\",\n metavar=\"<comment>\",\n dest=\"comment\",\n action=\"store\",\n )\n\n edit_parser.add_argument(\n \"--source\",\n action=\"store\",\n dest=\"source\",\n metavar=\"<source>\",\n help=\"replaces current source with <source>\",\n )\n\n edit_parser.set_defaults(func=commands.edit)\n\n info_parser = subparsers.add_parser(\n \"info\",\n help=\"show detailed information about a torrent file\",\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n info_parser.add_argument(\n \"metafile\",\n action=\"store\",\n metavar=\"<*.torrent>\",\n help=\"path to torrent file\",\n )\n\n info_parser.set_defaults(func=commands.info)\n\n magnet_parser = subparsers.add_parser(\n \"magnet\",\n help=\"generate magnet url from an existing torrent file\",\n aliases=[\"m\"],\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n magnet_parser.add_argument(\n \"metafile\",\n action=\"store\",\n help=\"path to torrent file\",\n metavar=\"<*.torrent>\",\n )\n\n magnet_parser.add_argument(\n \"--meta-version\",\n action=\"store\",\n choices=[\"0\", \"1\", \"2\", \"3\"],\n default=\"0\",\n help=\"\"\"\n This option is only relevant for hybrid torrent files.\n Options = 0, 1, 2, 3\n (0) = [default] version is determined automatically\n (1) = create V1 magnet link only\n (2) = create V2 magnet link only\n (3) = create a hybrid magnet link\n \"\"\",\n dest=\"meta_version\",\n metavar=\"<int>\",\n )\n\n magnet_parser.set_defaults(func=commands.get_magnet)\n\n check_parser = subparsers.add_parser(\n \"recheck\",\n help=\"gives a detailed look at how much of the torrent is available\",\n aliases=[\"check\"],\n prefix_chars=\"-\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n check_parser.add_argument(\n \"metafile\",\n action=\"store\",\n metavar=\"<*.torrent>\",\n help=\"path to .torrent file.\",\n )\n\n check_parser.add_argument(\n \"content\",\n action=\"store\",\n metavar=\"<content>\",\n help=\"path to content file or directory\",\n )\n\n check_parser.set_defaults(func=commands.recheck)\n\n rebuild_parser = subparsers.add_parser(\n \"rebuild\",\n help=\"\"\"\n Re-assemble files obtained from a bittorrent file into the\n appropriate file structure for re-seeding. Read documentation\n for more information, or use cases.\n \"\"\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n rebuild_parser.add_argument(\n \"-m\",\n \"--metafiles\",\n action=\"store\",\n metavar=\"<*.torrent>\",\n nargs=\"+\",\n dest=\"metafiles\",\n required=True,\n help=\"path(s) to .torrent file(s)/folder(s) containing .torrent files\",\n )\n\n rebuild_parser.add_argument(\n \"-c\"\n \"--contents\",\n action=\"store\",\n dest=\"contents\",\n nargs=\"+\",\n required=True,\n metavar=\"<contents>\",\n help=\"folders that might contain the source contents needed to rebuld\",\n )\n\n rebuild_parser.add_argument(\n \"-d\",\n \"--destination\",\n action=\"store\",\n dest=\"destination\",\n required=True,\n metavar=\"<destination>\",\n help=\"path to where torrents will be re-assembled\",\n )\n\n rebuild_parser.set_defaults(func=commands.rebuild)\n\n rename_parser = subparsers.add_parser(\n \"rename\",\n help=\"\"\"Rename a torrent file to it's original name provided in the\n metadata/the same name you see in your torrent client.\"\"\",\n formatter_class=TorrentFileHelpFormatter,\n )\n\n rename_parser.add_argument(\n \"target\",\n action=\"store\",\n metavar=\"<target>\",\n help=\"path to torrent file\",\n )\n\n rename_parser.set_defaults(func=commands.rename)\n\n all_commands = [\n \"m\",\n \"-h\",\n \"-V\",\n \"new\",\n \"edit\",\n \"info\",\n \"check\",\n \"create\",\n \"magnet\",\n \"rename\",\n \"rebuild\",\n \"recheck\",\n ]\n if not any(i for i in all_commands if i in args):\n start = 0\n while args[start] in [\"-v\", \"-q\"]:\n start += 1\n args.insert(start, \"create\")\n\n args = parser.parse_args(args)\n\n if args.quiet:\n Config.activate_quiet()\n\n elif args.debug:\n Config.activate_logger()\n\n if hasattr(args, \"func\"):\n return args.func(args)\n return args # pragma: nocover\n
"},{"location":"Source/cli/#torrentfile.cli.main","title":"main() -> None
","text":"Initiate main function for CLI script.
Source code in torrentfile\\cli.py
def main() -> None:\n\"\"\"\n Initiate main function for CLI script.\n \"\"\"\n execute()\n
"},{"location":"Source/commands/","title":"Commands","text":""},{"location":"Source/commands/#torrentfile.commands","title":"commands
","text":"The commands module contains the Action Commands executed by the CLI script.
Each function pertains to a command line action/subcommand and drives specific features of the application.
"},{"location":"Source/commands/#torrentfile.commands--functions","title":"Functions","text":" create info edit recheck magnet rebuild find_config_file parse_config_file get_magnet "},{"location":"Source/commands/#torrentfile.commands.create","title":"create(args: Namespace) -> Namespace
","text":"Execute the create CLI sub-command to create a new torrent metafile.
PARAMETER DESCRIPTION args
positional and optional CLI arguments.
TYPE: Namespace
RETURNS DESCRIPTION torrentfile.MetaFile
object containing the path to created metafile and its contents.
Source code in torrentfile\\commands.py
def create(args: Namespace) -> Namespace:\n\"\"\"\n Execute the create CLI sub-command to create a new torrent metafile.\n\n Parameters\n ----------\n args : Namespace\n positional and optional CLI arguments.\n\n Returns\n -------\n torrentfile.MetaFile\n object containing the path to created metafile and its contents.\n \"\"\"\n kwargs = vars(args)\n if args.config:\n path = find_config_file(args)\n parse_config_file(path, kwargs) # pragma: nocover\n\n if args.outfile:\n check_path_writable(args.outfile)\n\n else: # pragma: nocover\n samplepath = os.path.join(os.getcwd(), \".torrent\")\n check_path_writable(samplepath)\n\n logger.debug(\"Creating torrent from %s\", args.content)\n if args.meta_version == \"1\":\n torrent = TorrentFile(**kwargs)\n\n else:\n torrent = TorrentAssembler(**kwargs)\n outfile, meta = torrent.write()\n\n if args.magnet:\n magnet(outfile, version=0)\n\n args.torrent = torrent\n args.kwargs = kwargs\n args.outfile = outfile\n args.meta = meta\n\n print(\"\\nTorrent Save Path: \", os.path.abspath(str(outfile)))\n logger.debug(\"Output path: %s\", str(outfile))\n return args\n
"},{"location":"Source/commands/#torrentfile.commands.edit","title":"edit(args: Namespace) -> str
","text":"Execute the edit CLI sub-command with provided arguments.
Provides functionality that can change the details of a torrentfile that preserves all of the hash piece information so as not to break the torrentfile.
PARAMETER DESCRIPTION args
positional and optional CLI arguments.
TYPE: Namespace
RETURNS DESCRIPTION str
path to edited torrent file.
Source code in torrentfile\\commands.py
def edit(args: Namespace) -> str:\n\"\"\"\n Execute the edit CLI sub-command with provided arguments.\n\n Provides functionality that can change the details of a torrentfile\n that preserves all of the hash piece information so as not to break\n the torrentfile.\n\n Parameters\n ----------\n args : Namespace\n positional and optional CLI arguments.\n\n Returns\n -------\n str\n path to edited torrent file.\n \"\"\"\n metafile = args.metafile\n logger.info(\"Editing %s Meta File\", str(args.metafile))\n\n editargs = {\n \"url-list\": args.url_list,\n \"httpseeds\": args.httpseeds,\n \"announce\": args.announce,\n \"source\": args.source,\n \"private\": args.private,\n \"comment\": args.comment,\n }\n return edit_torrent(metafile, editargs)\n
"},{"location":"Source/commands/#torrentfile.commands.find_config_file","title":"find_config_file(args: Namespace) -> str
","text":"Locate the path to the torrentfile configuration file.
PARAMETER DESCRIPTION args
command line argument values
TYPE: Namespace
RETURNS DESCRIPTION str
path to the configuration file
RAISES DESCRIPTION FileNotFoundError
raised if configuration file not found.
Source code in torrentfile\\commands.py
def find_config_file(args: Namespace) -> str:\n\"\"\"\n Locate the path to the torrentfile configuration file.\n\n Parameters\n ----------\n args : Namespace\n command line argument values\n\n Returns\n -------\n str\n path to the configuration file\n\n Raises\n ------\n FileNotFoundError\n raised if configuration file not found.\n \"\"\"\n path = None\n error_message = \"Could not find configuration file.\"\n if args.config_path:\n if os.path.exists(args.config_path):\n path = args.config_path\n else:\n raise FileNotFoundError(error_message)\n else:\n filename = \"torrentfile.ini\"\n paths = [\n os.path.join(os.getcwd(), filename),\n Path.home() / \".torrentfile\" / filename,\n Path.home() / \".config\" / \".torrentfile\" / filename,\n ]\n for subpath in paths:\n if os.path.exists(subpath):\n path = subpath\n break\n if path is None:\n raise FileNotFoundError(error_message)\n return path\n
"},{"location":"Source/commands/#torrentfile.commands.get_magnet","title":"get_magnet(namespace: Namespace) -> str
","text":"Prepare option parameters for retreiving magnet URI.
PARAMETER DESCRIPTION namespace
command line argument options
TYPE: Namespace
RETURNS DESCRIPTION str
Magnet URI
Source code in torrentfile\\commands.py
def get_magnet(namespace: Namespace) -> str:\n\"\"\"\n Prepare option parameters for retreiving magnet URI.\n\n Parameters\n ----------\n namespace: Namespace\n command line argument options\n\n Returns\n -------\n str\n Magnet URI\n \"\"\"\n metafile = namespace.metafile\n version = int(namespace.meta_version)\n return magnet(metafile, version=version)\n
"},{"location":"Source/commands/#torrentfile.commands.info","title":"info(args: Namespace) -> str
","text":"Show torrent metafile details to user via stdout.
Prints full details of torrent file contents to the terminal in a clean and readable format.
PARAMETER DESCRIPTION args
command line arguements provided by the user.
TYPE: dict
RETURNS DESCRIPTION str
The output printed to the terminal.
Source code in torrentfile\\commands.py
def info(args: Namespace) -> str:\n\"\"\"\n Show torrent metafile details to user via stdout.\n\n Prints full details of torrent file contents to the terminal in\n a clean and readable format.\n\n Parameters\n ----------\n args : dict\n command line arguements provided by the user.\n\n Returns\n -------\n str\n The output printed to the terminal.\n \"\"\"\n metafile = args.metafile\n meta = pyben.load(metafile)\n data = meta[\"info\"]\n del meta[\"info\"]\n\n meta.update(data)\n if \"private\" in meta and meta[\"private\"] == 1:\n meta[\"private\"] = \"True\"\n\n if \"announce-list\" in meta:\n lst = meta[\"announce-list\"]\n meta[\"announce-list\"] = \", \".join([j for i in lst for j in i])\n\n if \"url-list\" in meta:\n meta[\"url-list\"] = \", \".join(meta[\"url-list\"])\n\n if \"httpseeds\" in meta:\n meta[\"httpseeds\"] = \", \".join(meta[\"httpseeds\"])\n\n text = []\n longest = max(len(i) for i in meta.keys())\n\n for key, val in meta.items():\n if key not in [\"pieces\", \"piece layers\", \"files\", \"file tree\"]:\n prefix = longest - len(key) + 1\n string = key + (\" \" * prefix) + str(val)\n text.append(string)\n\n most = max(len(i) for i in text)\n text = [\"-\" * most, \"\\n\"] + text + [\"\\n\", \"-\" * most]\n output = \"\\n\".join(text)\n sys.stdout.write(output)\n sys.stdout.flush()\n return output\n
"},{"location":"Source/commands/#torrentfile.commands.magnet","title":"magnet(metafile: str, version: int = 0) -> str
","text":"Create a magnet URI from a Bittorrent meta file.
PARAMETER DESCRIPTION metafile
path to bittorrent file
TYPE: str
version
version of bittorrent protocol [default=1]
TYPE: int
DEFAULT: 0
RETURNS DESCRIPTION str
Magnet URI
Source code in torrentfile\\commands.py
def magnet(metafile: str, version: int = 0) -> str:\n\"\"\"\n Create a magnet URI from a Bittorrent meta file.\n\n Parameters\n ----------\n metafile : str\n path to bittorrent file\n version: int\n version of bittorrent protocol [default=1]\n\n Returns\n -------\n str\n Magnet URI\n \"\"\"\n if not os.path.exists(metafile):\n raise FileNotFoundError(f\"No Such File {metafile}\")\n meta = pyben.load(metafile)\n info_dict = meta[\"info\"]\n\n magnet = \"magnet:?\"\n bencoded_info = pyben.dumps(info_dict)\n\n v1 = False\n if \"meta version\" not in info_dict or (version in [1, 3, 0]\n and \"pieces\" in info_dict):\n infohash = sha1(bencoded_info).hexdigest() # nosec\n magnet += \"xt=urn:btih:\" + infohash\n v1 = True\n\n if \"meta version\" in info_dict and version != 1:\n infohash = sha256(bencoded_info).hexdigest()\n if v1:\n magnet += \"&\"\n magnet += \"xt=urn:btmh:1220\" + infohash\n\n magnet += \"&dn=\" + quote_plus(info_dict[\"name\"])\n\n if \"announce-list\" in meta:\n announce_args = [\n \"&tr=\" + quote_plus(url) for urllist in meta[\"announce-list\"]\n for url in urllist\n ]\n elif \"announce\" in meta:\n announce_args = [\"&tr=\" + quote_plus(meta[\"announce\"])]\n else:\n announce_args = [\"\"]\n\n trackers = \"\".join(announce_args)\n\n magnet += trackers if trackers != \"&tr=\" else \"\"\n\n logger.info(\"Created Magnet URI %s\", magnet)\n sys.stdout.write(\"\\n\" + magnet + \"\\n\")\n return magnet\n
"},{"location":"Source/commands/#torrentfile.commands.parse_config_file","title":"parse_config_file(path: str, kwargs: dict)
","text":"Parse configuration file for torrent setup details.
PARAMETER DESCRIPTION path
path to configuration file
TYPE: str
kwargs
options from command line arguments
TYPE: dict
Source code in torrentfile\\commands.py
def parse_config_file(path: str, kwargs: dict):\n\"\"\"\n Parse configuration file for torrent setup details.\n\n Parameters\n ----------\n path : str\n path to configuration file\n kwargs : dict\n options from command line arguments\n \"\"\"\n config = configparser.ConfigParser()\n config.read(path)\n\n for key, val in config[\"config\"].items():\n if key.lower() in [\"announce\", \"http-seed\", \"web-seed\", \"tracker\"]:\n val = [i for i in val.split(\"\\n\") if i]\n\n if key.lower() == \"http-seed\":\n kwargs[\"httpseeds\"] = val\n\n elif key.lower() == \"web-seed\":\n kwargs.setdefault(\"url-list\", [])\n kwargs[\"url-list\"] = val\n\n else:\n kwargs[key.lower()] = val\n\n elif key.lower() == \"piece-length\":\n kwargs[\"piece_length\"] = val\n\n elif key.lower() == \"meta-version\":\n kwargs[\"meta_version\"] = val\n\n elif val.lower() == \"true\":\n kwargs[key.lower()] = True\n\n elif val.lower() == \"false\":\n kwargs[key.lower()] = False\n\n else:\n kwargs[key.lower()] = val\n
"},{"location":"Source/commands/#torrentfile.commands.rebuild","title":"rebuild(args: Namespace) -> int
","text":"Attempt to rebuild a torrent based on the a torrent file.
Recursively look through a directory for files that belong in a given torrent file, and rebuild as much of the torrent file as possible. Currently only checks if the filename and file size are a match.
Check file hashes to improve accuracy PARAMETER DESCRIPTION args
command line arguments including the paths neccessary
TYPE: Namespace
RETURNS DESCRIPTION int
total number of content files copied to the rebuild directory
Source code in torrentfile\\commands.py
def rebuild(args: Namespace) -> int:\n\"\"\"\n Attempt to rebuild a torrent based on the a torrent file.\n\n Recursively look through a directory for files that belong in\n a given torrent file, and rebuild as much of the torrent file\n as possible. Currently only checks if the filename and file\n size are a match.\n\n 1. Check file hashes to improve accuracy\n\n Parameters\n ----------\n args : Namespace\n command line arguments including the paths neccessary\n\n Returns\n -------\n int\n total number of content files copied to the rebuild directory\n \"\"\"\n metafiles = args.metafiles\n dest = args.destination\n contents = args.contents\n for path in [*metafiles, *contents]:\n if not os.path.exists(path):\n raise FileNotFoundError(path)\n assembler = Assembler(metafiles, contents, dest)\n return assembler.assemble_torrents()\n
"},{"location":"Source/commands/#torrentfile.commands.recheck","title":"recheck(args: Namespace) -> str
","text":"Execute recheck CLI sub-command.
Checks the piece hashes within a pre-existing torrent file and does a piece by piece check with the contents of a file or directory for completeness and validation.
PARAMETER DESCRIPTION args
positional and optional arguments.
TYPE: Namespace
RETURNS DESCRIPTION str
The percentage of content currently saved to disk.
Source code in torrentfile\\commands.py
def recheck(args: Namespace) -> str:\n\"\"\"\n Execute recheck CLI sub-command.\n\n Checks the piece hashes within a pre-existing torrent file\n and does a piece by piece check with the contents of a file\n or directory for completeness and validation.\n\n Parameters\n ----------\n args : Namespace\n positional and optional arguments.\n\n Returns\n -------\n str\n The percentage of content currently saved to disk.\n \"\"\"\n metafile = args.metafile\n content = args.content\n\n if os.path.isdir(metafile):\n raise ArgumentError(f\"Error: Unable to parse directory {metafile}. \"\n \"Check the order of the parameters.\")\n\n logger.debug(\"Validating %s <---------------> %s contents\", metafile,\n content)\n\n msg = f\"Rechecking {metafile} ...\\n\"\n halfterm = shutil.get_terminal_size().columns / 2\n padding = int(halfterm - (len(msg) / 2)) * \" \"\n sys.stdout.write(padding + msg)\n\n checker = Checker(metafile, content)\n logger.debug(\"Completed initialization of the Checker class\")\n result = checker.results()\n\n message = f\"{content} <- {result}% -> {metafile}\"\n padding = int(halfterm - (len(message) / 2)) * \" \"\n sys.stdout.write(padding + message + \"\\n\")\n sys.stdout.flush()\n return result\n
"},{"location":"Source/commands/#torrentfile.commands.rename","title":"rename(args: Namespace) -> str
","text":"Rename a torrent file to it\u2019s original name found in metadata.
PARAMETER DESCRIPTION args
cli arguments
TYPE: Namespace
RETURNS DESCRIPTION str
renamed file path
Source code in torrentfile\\commands.py
def rename(args: Namespace) -> str:\n\"\"\"\n Rename a torrent file to it's original name found in metadata.\n\n Parameters\n ----------\n args: Namespace\n cli arguments\n\n Returns\n -------\n str\n renamed file path\n \"\"\"\n target = args.target\n if not target or not os.path.exists(target):\n raise FileNotFoundError # pragma: nocover\n meta = pyben.load(target)\n name = meta[\"info\"][\"name\"]\n parent = os.path.dirname(target)\n new_path = os.path.join(parent, name + \".torrent\")\n if os.path.exists(new_path):\n raise FileExistsError # pragma: nocover\n os.rename(target, new_path)\n return new_path\n
"},{"location":"Source/edit/","title":"Edit","text":""},{"location":"Source/edit/#torrentfile.edit","title":"edit
","text":"Edit torrent module.
Provides a facility by which certain properties of a torrent meta file can be edited by the user. The various command line arguments indicate which fields should be edited, and what the new value should be. Depending on what fields are chosen to edit, this command can trigger a new info hash which means the torrent will no longer be able to participate in the same swarm as the original unedited torrent.
"},{"location":"Source/edit/#torrentfile.edit.edit_torrent","title":"edit_torrent(metafile: str, args: dict) -> dict
","text":"Edit the properties and values in a torrent meta file.
PARAMETER DESCRIPTION metafile
path to the torrent meta file.
TYPE: str
args
key value pairs of the properties to be edited.
TYPE: dict
RETURNS DESCRIPTION dict
The edited and nested Meta and info dictionaries.
Source code in torrentfile\\edit.py
def edit_torrent(metafile: str, args: dict) -> dict:\n\"\"\"\n Edit the properties and values in a torrent meta file.\n\n Parameters\n ----------\n metafile : str\n path to the torrent meta file.\n args : dict\n key value pairs of the properties to be edited.\n\n Returns\n -------\n dict\n The edited and nested Meta and info dictionaries.\n \"\"\"\n logger.debug(\"editing torrent file %s\", metafile)\n meta = pyben.load(metafile)\n info = meta[\"info\"]\n filter_empty(args, meta, info)\n\n if \"comment\" in args:\n info[\"comment\"] = args[\"comment\"]\n\n if \"source\" in args:\n info[\"source\"] = args[\"source\"]\n\n if \"private\" in args:\n info[\"private\"] = 1\n\n if \"announce\" in args:\n val = args.get(\"announce\", None)\n if isinstance(val, str):\n vallist = val.split()\n meta[\"announce\"] = vallist[0]\n meta[\"announce-list\"] = [vallist]\n elif isinstance(val, list):\n meta[\"announce\"] = val[0]\n meta[\"announce-list\"] = [val]\n\n if \"url-list\" in args:\n val = args.get(\"url-list\")\n if isinstance(val, str):\n meta[\"url-list\"] = val.split()\n elif isinstance(val, list):\n meta[\"url-list\"] = val\n\n if \"httpseeds\" in args:\n val = args.get(\"httpseeds\")\n if isinstance(val, str):\n meta[\"httpseeds\"] = val.split()\n elif isinstance(val, list):\n meta[\"httpseeds\"] = val\n\n meta[\"info\"] = info\n os.remove(metafile)\n pyben.dump(meta, metafile)\n return meta\n
"},{"location":"Source/edit/#torrentfile.edit.filter_empty","title":"filter_empty(args: dict, meta: dict, info: dict)
","text":"Remove the fields that were not used by the original file creator.
PARAMETER DESCRIPTION args
Editable metafile properties from user.
TYPE: dict
meta
Metafile data dictionary.
TYPE: dict
info
Metafile info dictionary.
TYPE: dict
Source code in torrentfile\\edit.py
def filter_empty(args: dict, meta: dict, info: dict):\n\"\"\"\n Remove the fields that were not used by the original file creator.\n\n Parameters\n ----------\n args : dict\n Editable metafile properties from user.\n meta : dict\n Metafile data dictionary.\n info : dict\n Metafile info dictionary.\n \"\"\"\n for key, val in list(args.items()):\n if val is None:\n del args[key]\n continue\n\n if val == \"\":\n if key in meta:\n del meta[key]\n elif key in info:\n del info[key]\n del args[key]\n logger.debug(\"removeing empty fields %s\", val)\n
"},{"location":"Source/hasher/","title":"Hasher","text":""},{"location":"Source/hasher/#torrentfile.hasher","title":"hasher
","text":"Piece/File Hashers for Bittorrent meta file contents.
"},{"location":"Source/hasher/#torrentfile.hasher.FileHasher","title":"FileHasher(path: str, piece_length: int, progress: int = 1, hybrid: bool = False, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Calculate root and piece hashes for creating hybrid torrent file.
Create merkle tree layers from sha256 hashed 16KiB blocks of contents. With a branching factor of 2, merge layer hashes until blocks equal piece_length bytes for the piece layer, and then the root hash.
PARAMETER DESCRIPTION path
path to target file.
TYPE: str
piece_length
piece length for data chunks.
TYPE: int
hybrid
flag to indicate if it\u2019s a hybrid torrent
TYPE: bool
DEFAULT: False
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Construct Hasher class instances for each file in torrent.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n path: str,\n piece_length: int,\n progress: int = 1,\n hybrid: bool = False,\n progress_bar=None,\n):\n\"\"\"\n Construct Hasher class instances for each file in torrent.\n \"\"\"\n self.path = path\n self.piece_length = piece_length\n self.pieces = []\n self.layer_hashes = []\n self.piece_layer = None\n self.root = None\n self.padding_piece = None\n self.padding_file = None\n self.amount = piece_length // BLOCK_SIZE\n self.end = False\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n size = os.path.getsize(self.path)\n self.progbar = self.get_progress_tracker(size, self.path)\n self.current = open(path, \"rb\")\n self.hybrid = hybrid\n
"},{"location":"Source/hasher/#torrentfile.hasher.FileHasher.__iter__","title":"__iter__()
","text":"Return self
: needed to implement iterator implementation.
Source code in torrentfile\\hasher.py
def __iter__(self):\n\"\"\"Return `self`: needed to implement iterator implementation.\"\"\"\n return self\n
"},{"location":"Source/hasher/#torrentfile.hasher.FileHasher.__next__","title":"__next__() -> bytes
","text":"Calculate layer hashes for contents of file.
RETURNS DESCRIPTION bytes
The layer merckle root hash.
RAISES DESCRIPTION StopIteration
Halts the iterator from progressing
Source code in torrentfile\\hasher.py
def __next__(self) -> bytes:\n\"\"\"\n Calculate layer hashes for contents of file.\n\n Returns\n -------\n bytes\n The layer merckle root hash.\n\n Raises\n ------\n StopIteration\n Halts the iterator from progressing\n \"\"\"\n if self.end:\n self.end = False\n raise StopIteration\n plength = self.piece_length\n blocks = []\n piece = sha1() # nosec\n total = 0\n block = bytearray(BLOCK_SIZE)\n for _ in range(self.amount):\n size = self.current.readinto(block)\n self.progbar.update(size)\n if not size:\n self.end = True\n break\n total += size\n plength -= size\n blocks.append(sha256(block[:size]).digest())\n if self.hybrid:\n piece.update(block[:size])\n if not blocks:\n self._calculate_root()\n raise StopIteration\n if len(blocks) != self.amount:\n padding = self._pad_remaining(len(blocks))\n blocks.extend(padding)\n layer_hash = merkle_root(blocks)\n self.layer_hashes.append(layer_hash)\n self.cb(layer_hash)\n if self.end:\n if self.progress == 1:\n self.progbar.close_out()\n self._calculate_root()\n if self.hybrid:\n if plength > 0:\n self.padding_file = {\n \"attr\": \"p\",\n \"length\": plength,\n \"path\": [\".pad\", str(plength)],\n }\n piece.update(bytes(plength))\n piece = piece.digest()\n self.pieces.append(piece)\n return layer_hash, piece\n return layer_hash\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher","title":"Hasher(paths: list, piece_length: int, align: bool = False, progress: int = 1, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Piece hasher for Bittorrent V1 files.
Takes a sorted list of all file paths, calculates sha1 hash for fixed size pieces of file data from each file seemlessly until the last piece which may be smaller than others.
PARAMETER DESCRIPTION paths
List of files.
TYPE: list
piece_length
Size of chuncks to split the data into.
TYPE: int
align
flag to indicate if the torrent should be piece aligned
TYPE: bool
DEFAULT: False
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Generate hashes of piece length data from filelist contents.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n paths: list,\n piece_length: int,\n align: bool = False,\n progress: int = 1,\n progress_bar=None,\n):\n\"\"\"Generate hashes of piece length data from filelist contents.\"\"\"\n self.piece_length = piece_length\n self.paths = paths\n self.align = align\n self.total = sum(os.path.getsize(i) for i in self.paths)\n self.index = 0\n self.current = open(self.paths[0], \"rb\")\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n file_size = os.path.getsize(self.paths[0])\n self.progbar = self.get_progress_tracker(file_size, self.paths[0])\n logger.debug(\"Hashing %s\", str(self.paths[0]))\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher.__iter__","title":"__iter__()
","text":"Iterate through feed pieces.
RETURNS DESCRIPTION self
Iterator for leaves/hash pieces.
TYPE: iterator
Source code in torrentfile\\hasher.py
def __iter__(self):\n\"\"\"\n Iterate through feed pieces.\n\n Returns\n -------\n self : iterator\n Iterator for leaves/hash pieces.\n \"\"\"\n return self\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher.__next__","title":"__next__() -> bytes
","text":"Generate piece-length pieces of data from input file list.
RETURNS DESCRIPTION bytes
SHA1 hash of the piece extracted.
Source code in torrentfile\\hasher.py
def __next__(self) -> bytes:\n\"\"\"\n Generate piece-length pieces of data from input file list.\n\n Returns\n -------\n bytes\n SHA1 hash of the piece extracted.\n \"\"\"\n while True:\n piece = bytearray(self.piece_length)\n size = self.current.readinto(piece)\n self.progbar.update(size)\n if size == 0:\n if not self.next_file():\n raise StopIteration\n elif size < self.piece_length:\n return self._handle_partial(piece[:size])\n else:\n return sha1(piece).digest() # nosec\n
"},{"location":"Source/hasher/#torrentfile.hasher.Hasher.next_file","title":"next_file() -> bool
","text":"Seemlessly transition to next file in file list.
RETURNS DESCRIPTION bool
True if there is a next file otherwise False.
TYPE: bool
Source code in torrentfile\\hasher.py
def next_file(self) -> bool:\n\"\"\"\n Seemlessly transition to next file in file list.\n\n Returns\n -------\n bool:\n True if there is a next file otherwise False.\n \"\"\"\n self.index += 1\n if self.progress == 1:\n self.progbar.close_out()\n if self.index < len(self.paths):\n path = self.paths[self.index]\n if self.progress == 1:\n total = os.path.getsize(path)\n self.progbar = self.get_progress_tracker(total, path)\n logger.debug(\"Hashing %s\", str(path))\n self.current.close()\n self.current = open(path, \"rb\")\n return True\n return False\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherHybrid","title":"HasherHybrid(path: str, piece_length: int, progress: int = 1, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Calculate root and piece hashes for creating hybrid torrent file.
Create merkle tree layers from sha256 hashed 16KiB blocks of contents. With a branching factor of 2, merge layer hashes until blocks equal piece_length bytes for the piece layer, and then the root hash.
PARAMETER DESCRIPTION path
path to target file.
TYPE: str
piece_length
piece length for data chunks.
TYPE: int
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Construct Hasher class instances for each file in torrent.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n path: str,\n piece_length: int,\n progress: int = 1,\n progress_bar=None,\n):\n\"\"\"\n Construct Hasher class instances for each file in torrent.\n \"\"\"\n self.path = path\n self.piece_length = piece_length\n self.pieces = []\n self.layer_hashes = []\n self.piece_layer = None\n self.root = None\n self.padding_piece = None\n self.padding_file = None\n self.amount = piece_length // BLOCK_SIZE\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n size = os.path.getsize(self.path)\n self.progbar = self.get_progress_tracker(size, self.path)\n with open(path, \"rb\") as data:\n self.process_file(data)\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherHybrid.process_file","title":"process_file(data: bytearray)
","text":"Calculate layer hashes for contents of file.
PARAMETER DESCRIPTION data
File opened in read mode.
TYPE: BytesIO
Source code in torrentfile\\hasher.py
def process_file(self, data: bytearray):\n\"\"\"\n Calculate layer hashes for contents of file.\n\n Parameters\n ----------\n data : BytesIO\n File opened in read mode.\n \"\"\"\n while True:\n plength = self.piece_length\n blocks = []\n piece = sha1() # nosec\n total = 0\n block = bytearray(BLOCK_SIZE)\n for _ in range(self.amount):\n size = data.readinto(block)\n self.progbar.update(size)\n if not size:\n break\n total += size\n plength -= size\n blocks.append(sha256(block[:size]).digest())\n piece.update(block[:size])\n if not blocks:\n break\n if len(blocks) != self.amount:\n padding = self._pad_remaining(len(blocks))\n blocks.extend(padding)\n layer_hash = merkle_root(blocks)\n self.cb(layer_hash)\n self.layer_hashes.append(layer_hash)\n if plength > 0:\n self.padding_file = {\n \"attr\": \"p\",\n \"length\": plength,\n \"path\": [\".pad\", str(plength)],\n }\n piece.update(bytes(plength))\n self.pieces.append(piece.digest()) # nosec\n if self.progress == 1:\n self.progbar.close_out()\n self._calculate_root()\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherV2","title":"HasherV2(path: str, piece_length: int, progress: int = 1, progress_bar = None)
","text":" Bases: CbMixin
, ProgMixin
Calculate the root hash and piece layers for file contents.
Iterates over 16KiB blocks of data from given file, hashes the data, then creates a hash tree from the individual block hashes until size of hashed data equals the piece-length. Then continues the hash tree until root hash is calculated.
PARAMETER DESCRIPTION path
Path to file.
TYPE: str
piece_length
Size of layer hashes pieces.
TYPE: int
progress
the progress mode
TYPE: int
DEFAULT: 1
progress_bar
a progress bar object if progress mode is 2
DEFAULT: None
Calculate and store hash information for specific file.
Source code in torrentfile\\hasher.py
def __init__(\n self,\n path: str,\n piece_length: int,\n progress: int = 1,\n progress_bar=None,\n):\n\"\"\"\n Calculate and store hash information for specific file.\n \"\"\"\n self.path = path\n self.root = None\n self.piece_layer = None\n self.layer_hashes = []\n self.piece_length = piece_length\n self.num_blocks = piece_length // BLOCK_SIZE\n self.progress = progress\n self.progbar = progress_bar\n if self.progress == 1:\n size = os.path.getsize(self.path)\n self.progbar = self.get_progress_tracker(size, self.path)\n with open(self.path, \"rb\") as fd:\n self.process_file(fd)\n
"},{"location":"Source/hasher/#torrentfile.hasher.HasherV2.process_file","title":"process_file(fd: str)
","text":"Calculate hashes over 16KiB chuncks of file content.
PARAMETER DESCRIPTION fd
Opened file in read mode.
TYPE: BytesIO
Source code in torrentfile\\hasher.py
def process_file(self, fd: str):\n\"\"\"\n Calculate hashes over 16KiB chuncks of file content.\n\n Parameters\n ----------\n fd : BytesIO\n Opened file in read mode.\n \"\"\"\n while True:\n blocks = []\n leaf = bytearray(BLOCK_SIZE)\n # generate leaves of merkle tree\n\n for _ in range(self.num_blocks):\n size = fd.readinto(leaf)\n if not size:\n break\n self.progbar.update(size)\n blocks.append(sha256(leaf[:size]).digest())\n\n # blocks is empty mean eof\n if not blocks:\n break\n if len(blocks) != self.num_blocks:\n # when size of file doesn't fill the last block\n # when the file contains multiple pieces\n remaining = self.num_blocks - len(blocks)\n if not self.layer_hashes:\n # when the there is only one block for file\n power2 = next_power_2(len(blocks))\n remaining = power2 - len(blocks)\n\n # pad the the rest with zeroes to fill remaining space.\n padding = [bytes(32) for _ in range(remaining)]\n blocks.extend(padding)\n # calculate the root hash for the merkle tree up to piece-length\n\n layer_hash = merkle_root(blocks)\n self.cb(layer_hash)\n self.layer_hashes.append(layer_hash)\n if self.progress == 1:\n self.progbar.close_out()\n self._calculate_root()\n
"},{"location":"Source/hasher/#torrentfile.hasher.merkle_root","title":"merkle_root(blocks: list) -> bytes
","text":"Calculate the merkle root for a seq of sha256 hash digests.
PARAMETER DESCRIPTION blocks
a sequence of sha256 layer hashes.
TYPE: list
RETURNS DESCRIPTION bytes
the sha256 root hash of the merkle tree.
Source code in torrentfile\\hasher.py
def merkle_root(blocks: list) -> bytes:\n\"\"\"\n Calculate the merkle root for a seq of sha256 hash digests.\n\n Parameters\n ----------\n blocks : list\n a sequence of sha256 layer hashes.\n\n Returns\n -------\n bytes\n the sha256 root hash of the merkle tree.\n \"\"\"\n if blocks:\n while len(blocks) > 1:\n blocks = [\n sha256(x + y).digest() for x, y in zip(*[iter(blocks)] * 2)\n ]\n return blocks[0]\n return blocks\n
"},{"location":"Source/interactive/","title":"Interactive","text":""},{"location":"Source/interactive/#torrentfile.interactive","title":"interactive
","text":"Module contains the procedures used for Interactive Mode.
"},{"location":"Source/interactive/#torrentfile.interactive--this-module-has-been-deprecated","title":"This module has been deprecated.","text":""},{"location":"Source/interactive/#torrentfile.interactive.InteractiveCreator","title":"InteractiveCreator()
","text":"Class namespace for interactive program options.
@Deprecated
Initialize interactive meta file creator dialog.
@Deprecated
Source code in torrentfile\\interactive.py
def __init__(self):\n\"\"\"\n Initialize interactive meta file creator dialog.\n\n @Deprecated\n \"\"\"\n self.kwargs = {\n \"announce\": None,\n \"url_list\": None,\n \"private\": None,\n \"source\": None,\n \"comment\": None,\n \"piece_length\": None,\n \"outfile\": None,\n \"path\": None,\n \"httpseeds\": None,\n }\n self.outfile, self.meta = self.get_props()\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveCreator.get_props","title":"get_props()
","text":"Gather details for torrentfile from user.
@Deprecated
Source code in torrentfile\\interactive.py
def get_props(self):\n\"\"\"\n Gather details for torrentfile from user.\n\n @Deprecated\n \"\"\"\n piece_length = get_input(\"Piece Length (empty=auto): \",\n lambda x: x.isdigit())\n\n self.kwargs[\"piece_length\"] = piece_length\n announce = get_input(\"Tracker list (empty): \",\n lambda x: isinstance(x, str))\n\n if announce:\n self.kwargs[\"announce\"] = announce.split()\n\n url_list = get_input(\"Web Seed {GetRight} list (empty): \",\n lambda x: isinstance(x, str))\n\n httpseeds = get_input(\"Web Seed {Hoffman} list (empty): \",\n lambda x: isinstance(x, str))\n\n if url_list:\n self.kwargs[\"url_list\"] = url_list.split()\n if httpseeds:\n self.kwargs[\"httpseeds\"] = httpseeds.split()\n comment = get_input(\"Comment (empty): \", None)\n\n if comment:\n self.kwargs[\"comment\"] = comment\n source = get_input(\"Source (empty): \", None)\n\n if source:\n self.kwargs[\"source\"] = source\n\n private = get_input(\"Private Torrent? {Y/N}: (N)\",\n lambda x: x in \"yYnN\")\n\n if private and private.lower() == \"y\":\n self.kwargs[\"private\"] = 1\n\n contents = get_input(\"Content Path: \", os.path.exists)\n self.kwargs[\"path\"] = contents\n\n outfile = get_input(\n f\"Output Path ({contents}.torrent): \",\n lambda x: os.path.exists(os.path.dirname(x)),\n )\n\n if outfile:\n self.kwargs[\"outfile\"] = outfile\n\n meta_version = get_input(\"Meta Version {1,2,3}: (1)\",\n lambda x: x in \"123\")\n\n showcenter(f\"creating {outfile}\")\n\n if meta_version == \"3\":\n torrent = TorrentFileHybrid(**self.kwargs)\n elif meta_version == \"2\":\n torrent = TorrentFileV2(**self.kwargs)\n else:\n torrent = TorrentFile(**self.kwargs)\n return torrent.write()\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor","title":"InteractiveEditor(metafile: str)
","text":"Interactive dialog class for torrent editing.
@Deprecated
Initialize the Interactive torrent editor guide.
@Deprecated
PARAMETER DESCRIPTION metafile
user input string identifying the path to a torrent meta file.
TYPE: str
Source code in torrentfile\\interactive.py
def __init__(self, metafile: str):\n\"\"\"\n Initialize the Interactive torrent editor guide.\n\n @Deprecated\n\n Parameters\n ----------\n metafile : str\n user input string identifying the path to a torrent meta file.\n \"\"\"\n self.metafile = metafile\n self.meta = pyben.load(metafile)\n self.info = self.meta[\"info\"]\n\n self.args = {\n \"url-list\": self.meta.get(\"url-list\", None),\n \"httpseeds\": self.meta.get(\"httpseeds\", None),\n \"announce\": self.meta.get(\"announce-list\", None),\n \"source\": self.info.get(\"source\", None),\n \"private\": self.info.get(\"private\", None),\n \"comment\": self.info.get(\"comment\", None),\n }\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor.edit_props","title":"edit_props()
","text":"Loop continuosly for edits until user signals DONE.
@Deprecated
Source code in torrentfile\\interactive.py
def edit_props(self):\n\"\"\"\n Loop continuosly for edits until user signals DONE.\n\n @Deprecated\n \"\"\"\n while True:\n showcenter(\"Choose the number for a propert the needs editing.\"\n \"Enter DONE when all editing has been completed.\")\n\n props = {\n 1: \"comment\",\n 2: \"source\",\n 3: \"private\",\n 4: \"tracker\",\n 5: \"web-seed\",\n 6: \"httpseeds\",\n }\n\n args = {\n 1: \"comment\",\n 2: \"source\",\n 3: \"private\",\n 4: \"announce\",\n 5: \"url-list\",\n 6: \"httpseeds\",\n }\n\n txt = \", \".join((str(k) + \": \" + v) for k, v in props.items())\n prop = get_input(txt)\n if prop.lower() == \"done\":\n break\n\n if prop.isdigit() and 0 < int(prop) < 6:\n key = props[int(prop)]\n key2 = args[int(prop)]\n val = self.args.get(key2)\n showtext(\n \"Enter new property value or leave empty for no value.\")\n response = get_input(f\"{key.title()} ({val}): \")\n self.sanatize_response(key2, response)\n\n else:\n showtext(\"Invalid input: Try again.\")\n edit_torrent(self.metafile, self.args)\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor.sanatize_response","title":"sanatize_response(key, response)
","text":"Convert the input data into a form recognizable by the program.
@ Deprecated
PARAMETER DESCRIPTION key
name of the property and attribute being eddited.
TYPE: str
response
User input value the property is being edited to.
TYPE: str
Source code in torrentfile\\interactive.py
def sanatize_response(self, key, response):\n\"\"\"\n Convert the input data into a form recognizable by the program.\n\n @ Deprecated\n\n Parameters\n ----------\n key : str\n name of the property and attribute being eddited.\n response : str\n User input value the property is being edited to.\n \"\"\"\n if key in [\"announce\", \"url-list\", \"httpseeds\"]:\n val = response.split()\n else:\n val = response\n self.args[key] = val\n
"},{"location":"Source/interactive/#torrentfile.interactive.InteractiveEditor.show_current","title":"show_current()
","text":"Display the current met file information to screen.
@Deprecated
Source code in torrentfile\\interactive.py
def show_current(self):\n\"\"\"\n Display the current met file information to screen.\n\n @Deprecated\n \"\"\"\n out = \"Current properties and values:\\n\"\n longest = max(len(label) for label in self.args) + 3\n for key, val in self.args.items():\n txt = (key.title() + \":\").ljust(longest) + str(val)\n out += f\"\\t{txt}\\n\"\n showtext(out)\n
"},{"location":"Source/interactive/#torrentfile.interactive.create_torrent","title":"create_torrent()
","text":"Create new torrent file interactively.
@Deprecated
Source code in torrentfile\\interactive.py
def create_torrent():\n\"\"\"\n Create new torrent file interactively.\n\n @Deprecated\n \"\"\"\n showcenter(\"Create Torrent\")\n showtext(\n \"\\nEnter values for each of the options for the torrent creator, \"\n \"or leave blank for program defaults.\\nSpaces are considered item \"\n \"seperators for options that accept a list of values.\\nValues \"\n \"enclosed in () indicate the default value, while {} holds all \"\n \"valid choices available for the option.\\n\\n\")\n creator = InteractiveCreator()\n return creator\n
"},{"location":"Source/interactive/#torrentfile.interactive.edit_action","title":"edit_action()
","text":"Edit the editable values of the torrent meta file.
@Deprecated
Source code in torrentfile\\interactive.py
def edit_action():\n\"\"\"\n Edit the editable values of the torrent meta file.\n\n @Deprecated\n \"\"\"\n showcenter(\"Edit Torrent\")\n metafile = get_input(\"Metafile(.torrent): \", os.path.exists)\n dialog = InteractiveEditor(metafile)\n dialog.show_current()\n dialog.edit_props()\n
"},{"location":"Source/interactive/#torrentfile.interactive.get_input","title":"get_input(*args: tuple)
","text":"Determine appropriate input function to call.
@Deprecated
PARAMETER DESCRIPTION *args
Arbitrary number of args to pass to next function
TYPE: tuple
DEFAULT: ()
RETURNS DESCRIPTION str
The results of the function call.
Source code in torrentfile\\interactive.py
def get_input(*args: tuple): # pragma: no cover\n\"\"\"\n Determine appropriate input function to call.\n\n @Deprecated\n\n Parameters\n ----------\n *args : tuple\n Arbitrary number of args to pass to next function\n\n Returns\n -------\n str\n The results of the function call.\n \"\"\"\n if len(args) == 2:\n return _get_input_loop(*args)\n return _get_input(*args)\n
"},{"location":"Source/interactive/#torrentfile.interactive.recheck_torrent","title":"recheck_torrent()
","text":"Check torrent download completed percentage.
@Deprecated
Source code in torrentfile\\interactive.py
def recheck_torrent():\n\"\"\"\n Check torrent download completed percentage.\n\n @Deprecated\n \"\"\"\n showcenter(\"Check Torrent\")\n msg = \"Enter path to torrent contents, and corresponding torrent metafile.\"\n showtext(msg)\n metafile = get_input(\"Conent Path (downloads/complete/torrentname):\",\n os.path.exists)\n contents = get_input(\"Metafile (*.torrent): \", os.path.exists)\n checker = Checker(metafile, contents)\n results = checker.results()\n showtext(f\"Completion for {metafile} is {results}%\")\n return results\n
"},{"location":"Source/interactive/#torrentfile.interactive.select_action","title":"select_action()
","text":"Operate TorrentFile program interactively through terminal.
DEPRECATION WARNING: The interactive CLI feature will be deprecated in the future.
Source code in torrentfile\\interactive.py
def select_action():\n\"\"\"\n Operate TorrentFile program interactively through terminal.\n\n DEPRECATION WARNING: The interactive CLI feature will be deprecated\n in the future.\n \"\"\"\n showcenter(\"TorrentFile: Starting Interactive Mode\")\n showcenter(\"DEPRECATION WARNING: The interactive feature will be\"\n \"deprecated in the near future.\")\n action = get_input(\"Enter the action you wish to perform.\\n\"\n \"Action ( Create (c) | Edit (e) | Recheck (r) ): \")\n action = action.lower()\n\n if \"create\" in action or action == \"c\":\n return create_torrent()\n\n if \"check\" in action or action == \"r\":\n return recheck_torrent()\n\n if \"edit\" in action or action == \"e\":\n return edit_action()\n print(\"Unable to recognize input. Please try again.\") # pragma: nocover\n return select_action() # pragma: nocover\n
"},{"location":"Source/interactive/#torrentfile.interactive.showcenter","title":"showcenter(txt: str)
","text":"Print text to screen in the center position of the terminal.
@Deprecated
PARAMETER DESCRIPTION txt
the preformated message to send to stdout.
TYPE: str
Source code in torrentfile\\interactive.py
def showcenter(txt: str):\n\"\"\"\n Print text to screen in the center position of the terminal.\n\n @Deprecated\n\n Parameters\n ----------\n txt : str\n the preformated message to send to stdout.\n \"\"\"\n termlen = shutil.get_terminal_size().columns\n padding = \" \" * int(((termlen - len(txt)) / 2))\n string = \"\".join([\"\\n\", padding, txt, \"\\n\"])\n showtext(string)\n
"},{"location":"Source/interactive/#torrentfile.interactive.showtext","title":"showtext(txt)
","text":"Print contents of txt to screen.
@Deprecated
PARAMETER DESCRIPTION txt
text to print to terminal.
TYPE: str
Source code in torrentfile\\interactive.py
def showtext(txt):\n\"\"\"\n Print contents of txt to screen.\n\n @Deprecated\n\n Parameters\n ----------\n txt : str\n text to print to terminal.\n \"\"\"\n sys.stdout.write(txt)\n
"},{"location":"Source/mixins/","title":"Mixins","text":""},{"location":"Source/mixins/#torrentfile.mixins","title":"mixins
","text":"Collection of classes that can be used as Mixins with other base classes.
Classes such as TorrentFile, TorrentFilev2, and all Hasher classes can use the progress bar mixin. And any class is eligible to use the callback mixin.
"},{"location":"Source/mixins/#torrentfile.mixins.CbMixin","title":"CbMixin
","text":"Mixin class to set a callback hook during procedure.
"},{"location":"Source/mixins/#torrentfile.mixins.CbMixin.cb","title":"cb(*args, **kwargs)
classmethod
","text":"Do nothing.
Source code in torrentfile\\mixins.py
@classmethod\ndef cb(cls, *args, **kwargs):\n\"\"\"Do nothing.\"\"\"\n
"},{"location":"Source/mixins/#torrentfile.mixins.CbMixin.set_callback","title":"set_callback(func)
classmethod
","text":"Assign a callback to the Hashing class.
PARAMETER DESCRIPTION func
the callback function
TYPE: Callable
Source code in torrentfile\\mixins.py
@classmethod\ndef set_callback(cls, func):\n\"\"\"\n Assign a callback to the Hashing class.\n\n Parameters\n ----------\n func : Callable\n the callback function\n \"\"\"\n cls.cb = func # pragma: nocover\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin","title":"ProgMixin
","text":"Progress bar mixin class.
Displays progress of hashing individual files, usefull when hashing really big files.
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin.NoProg","title":"NoProg
","text":"Stand-in object for when progress mode is set to 0.
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin.NoProg.update","title":"update(value)
staticmethod
","text":"Return the value.
PARAMETER DESCRIPTION value
the input and output
RETURNS DESCRIPTION int
same as input
Source code in torrentfile\\mixins.py
@staticmethod\ndef update(value):\n\"\"\"\n Return the value.\n\n Parameters\n ----------\n value: int\n the input and output\n\n Returns\n -------\n int :\n same as input\n \"\"\"\n return value\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgMixin.get_progress_tracker","title":"get_progress_tracker(total: int, message: str)
","text":"Return the progress bar object for external management.
PARAMETER DESCRIPTION total
total size to track
TYPE: int
message
prompt message for what is being tracked
TYPE: str
RETURNS DESCRIPTION ProgressBar
progress bar object instance
Source code in torrentfile\\mixins.py
def get_progress_tracker(self, total: int, message: str):\n\"\"\"Return the progress bar object for external management.\n\n Parameters\n ----------\n total: int\n total size to track\n message: str\n prompt message for what is being tracked\n\n Returns\n -------\n ProgressBar\n progress bar object instance\n \"\"\"\n if total < 0:\n return self.NoProg()\n return ProgressBar.new(total, message)\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar","title":"ProgressBar(total: int, title: str, length: int, unit: str, start: int)
","text":"Holds the state and details of the terminal progress bars.
PARAMETER DESCRIPTION total
the total amount to be accumulated.
TYPE: int
title
the subject of the progress tracker
TYPE: str
length
the width of the progress bar
TYPE: int
unit
the text representation incremented
TYPE: str
start
column where the progress bar should be drawn
TYPE: int
Construct the progress bar object and store state of it\u2019s properties.
Source code in torrentfile\\mixins.py
def __init__(self, total: int, title: str, length: int, unit: str,\n start: int):\n\"\"\"\n Construct the progress bar object and store state of it's properties.\n \"\"\"\n debug_is_on()\n self.total = total\n self.start = start\n self.length = length\n self.fill = chr(9608)\n self.empty = chr(9617)\n self.state = 0\n self.unit = unit\n self.show_total = total\n if not unit:\n self.unit = \"\" # pragma: nocover\n elif unit == \"bytes\":\n if self.total > 1_000_000_000:\n self.show_total = self.total / (2**30)\n self.unit = \"GiB\"\n elif self.total > 1_000_000:\n self.show_total = self.total / 1048576\n self.unit = \"MiB\"\n elif self.total > 10000:\n self.show_total = self.total / 1024\n self.unit = \"KiB\"\n self.suffix = f\"/{self.show_total:.02f} {self.unit}\"\n title = str(title)\n if len(title) > start:\n title = title[:start - 1] # pragma: nocover\n padding = (start - len(title)) * \" \"\n self.prefix = \"\".join([title, padding])\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.close_out","title":"close_out()
staticmethod
","text":"Finalize the last bits of progress bar.
Increment the terminal by one line leaving the progress bar in place, and deleting the progress bar object to clear a space for the next one.
Source code in torrentfile\\mixins.py
@staticmethod\ndef close_out():\n\"\"\"\n Finalize the last bits of progress bar.\n\n Increment the terminal by one line leaving the progress bar in place,\n and deleting the progress bar object to clear a space for the next one.\n \"\"\"\n sys.stdout.flush()\n sys.stdout.write(\"\\n\")\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.get_progress","title":"get_progress() -> str
","text":"Return the size of the filled portion of the progress bar.
RETURNS DESCRIPTION str
the progress bar characters
TYPE: str
Source code in torrentfile\\mixins.py
def get_progress(self) -> str:\n\"\"\"\n Return the size of the filled portion of the progress bar.\n\n Returns\n -------\n str :\n the progress bar characters\n \"\"\"\n if self.state >= self.total:\n fill = self.length\n else:\n fill = math.ceil((self.state / self.total) * self.length)\n empty = self.length - fill\n contents = (self.fill * fill) + (self.empty * empty)\n pbar = [\"|\", green(contents), \"| \"]\n if self.unit == \"GiB\":\n state = self.state / (2**30)\n elif self.unit == \"MiB\":\n state = self.state / 1048576\n elif self.unit == \"KiB\":\n state = self.state / 1024\n else:\n state = self.state\n pbar.append(f\"{state:.02f}\")\n return \"\".join(pbar)\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.new","title":"new(total: int, path: str, length: int = 50, unit: str = 'bytes')
classmethod
","text":"Generate a new progress bar for the given file path.
PARAMETER DESCRIPTION total
the total amount of units accumulating towards.
TYPE: int
path
path to file being hashed.
TYPE: str
length
the number of characters of the actual progress bar.
TYPE: int
DEFAULT: 50
unit
the text representation of the value being measured.
TYPE: str
DEFAULT: 'bytes'
Source code in torrentfile\\mixins.py
@classmethod\ndef new(cls, total: int, path: str, length: int = 50, unit: str = \"bytes\"):\n\"\"\"\n Generate a new progress bar for the given file path.\n\n Parameters\n ----------\n total : int\n the total amount of units accumulating towards.\n path : str\n path to file being hashed.\n length : int\n the number of characters of the actual progress bar.\n unit : str\n the text representation of the value being measured.\n \"\"\"\n title = path\n width = shutil.get_terminal_size().columns\n if len(str(title)) >= width // 2:\n parts = list(Path(title).parts)\n while (len(\"//\".join(parts)) > (width // 2)) and (len(parts) > 0):\n del parts[0]\n if parts:\n title = os.path.join(*parts)\n else:\n title = os.path.basename(path) # pragma: nocover\n length = min(length, width // 2)\n start = width - int(length * 1.5)\n return cls(total, title, length, unit, start)\n
"},{"location":"Source/mixins/#torrentfile.mixins.ProgressBar.update","title":"update(val: int)
","text":"Update progress bar.
Using the value provided, increment the progress bar by that value.
PARAMETER DESCRIPTION val
the number of bytes count the progress bar should increase.
TYPE: int
Source code in torrentfile\\mixins.py
def update(self, val: int):\n\"\"\"\n Update progress bar.\n\n Using the value provided, increment the progress bar by that value.\n\n Parameters\n ----------\n val : int\n the number of bytes count the progress bar should increase.\n \"\"\"\n self.state += val\n pbar = self.get_progress()\n output = f\"{self.prefix}{pbar}{self.suffix}\\r\"\n sys.stdout.write(output)\n sys.stdout.flush()\n
"},{"location":"Source/mixins/#torrentfile.mixins.waiting","title":"waiting(msg: str, flag: list, timeout: int = 20)
","text":"Show loading message while thread completes processing.
PARAMETER DESCRIPTION msg
Message string printed before the progress bar
TYPE: str
flag
Once flag is filled exit loop
TYPE: list
timeout
max amount of time to run the function.
TYPE: int
DEFAULT: 20
Source code in torrentfile\\mixins.py
def waiting(msg: str, flag: list, timeout: int = 20):\n\"\"\"\n Show loading message while thread completes processing.\n\n Parameters\n ----------\n msg : str\n Message string printed before the progress bar\n flag : list\n Once flag is filled exit loop\n timeout : int\n max amount of time to run the function.\n \"\"\"\n then = time.time()\n codes, fill = list(range(9617, 9620)), chr(9619)\n size = idx = 0\n total = shutil.get_terminal_size().columns - len(msg) - 20\n\n def output(text: str):\n\"\"\"\n Print parameter message to the console.\n\n Parameters\n ----------\n text : str\n output message\n \"\"\"\n sys.stdout.write(text)\n sys.stdout.flush()\n\n output(\"\\n\")\n time.sleep(0.16)\n while len(flag) == 0:\n time.sleep(0.16)\n filled = (fill * size) + chr(codes[idx]) + (\" \" * (total - size))\n output(f\"{msg}: {filled}\\r\")\n idx = idx + 1 if idx + 1 < len(codes) else 0\n size = size + 1 if size < total else 0\n if time.time() - then > timeout:\n break\n output(\"\\n\")\n
"},{"location":"Source/rebuild/","title":"Rebuild","text":""},{"location":"Source/rebuild/#torrentfile.rebuild","title":"rebuild
","text":"Clases and functions for the rebuild or reassemble subcommand.
Re-assemble a torrent into the propper directory structure as indicated by a torrent meta file, and validate the contents of each file allong the way. Displays a progress bar for each torrent.
"},{"location":"Source/rebuild/#torrentfile.rebuild.Assembler","title":"Assembler(metafiles: list, contents: list, dest: str)
","text":" Bases: CbMixin
Does most of the work in attempting the structure of torrentfiles.
Requires three paths as arguments. - torrent metafile or directory containing multiple meta files - directory containing the contents of meta file - directory where torrents will be re-assembled
Reassemble given torrent file from given cli arguments.
Rebuild metafiles and contents into their original directory structure as much as possible in the destination directory. Takes two paths as parameters, - file or directory containing 1 or more torrent meta files - path to where the contents are belived to be located.
PARAMETER DESCRIPTION metafiles
path to torrent metafile or directory containing torrent metafiles.
TYPE: str
contents
path to content or directory containing content that belongs to torrentfile.
TYPE: str
dest
path to the directory where rebuild will take place.
TYPE: str
Source code in torrentfile\\rebuild.py
def __init__(self, metafiles: list, contents: list, dest: str):\n\"\"\"\n Reassemble given torrent file from given cli arguments.\n\n Rebuild metafiles and contents into their original directory\n structure as much as possible in the destination directory.\n Takes two paths as parameters,\n - file or directory containing 1 or more torrent meta files\n - path to where the contents are belived to be located.\n\n Parameters\n ----------\n metafiles : str\n path to torrent metafile or directory containing torrent metafiles.\n contents : str\n path to content or directory containing content that belongs to\n torrentfile.\n dest: str\n path to the directory where rebuild will take place.\n \"\"\"\n Metadata.set_callback(self._callback)\n self.counter = 0\n self._lastlog = None\n self.contents = contents\n self.dest = dest\n self.meta_paths = metafiles\n self.metafiles = self._get_metafiles()\n filenames = set()\n for meta in self.metafiles:\n filenames |= meta.filenames\n self.filemap = _index_contents(self.contents, filenames)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Assembler.assemble_torrents","title":"assemble_torrents()
","text":"Assemble collection of torrent files into original structure.
RETURNS DESCRIPTION int
number of files copied
Source code in torrentfile\\rebuild.py
def assemble_torrents(self):\n\"\"\"\n Assemble collection of torrent files into original structure.\n\n Returns\n -------\n int\n number of files copied\n \"\"\"\n for metafile in self.metafiles:\n logger.info(\"#%s Searching contents for %s\", self.counter,\n metafile.name)\n self.rebuild(metafile)\n return self.counter\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Assembler.rebuild","title":"rebuild(metafile: Metadata) -> None
","text":"Build the torrent file structure from contents of directory.
Traverse contents dir and compare discovered files with files listed in torrent metadata and copy the matches to the destination directory respecting folder structures along the way.
Source code in torrentfile\\rebuild.py
def rebuild(self, metafile: Metadata) -> None:\n\"\"\"\n Build the torrent file structure from contents of directory.\n\n Traverse contents dir and compare discovered files\n with files listed in torrent metadata and copy\n the matches to the destination directory respecting folder\n structures along the way.\n \"\"\"\n metafile.rebuild(self.filemap, self.dest)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Metadata","title":"Metadata(path: str)
","text":" Bases: CbMixin
, ProgMixin
Class containing the metadata contents of a torrent file.
Construct metadata object for torrent info.
PARAMETER DESCRIPTION path
path to the .torrent file.
TYPE: str
Source code in torrentfile\\rebuild.py
def __init__(self, path: str):\n\"\"\"\n Construct metadata object for torrent info.\n\n Parameters\n ----------\n path : str\n path to the .torrent file.\n \"\"\"\n self.path = os.path.abspath(path)\n self.name = None\n self.piece_length = 1\n self.meta_version = 1\n self.pieces = b\"\"\n self.piece_nodes = []\n self.length = 0\n self.files = []\n self.filenames = set()\n self.extract()\n if self.meta_version == 2:\n self.num_pieces = len(self.filenames)\n else:\n self.num_pieces = math.ceil(len(self.pieces) / SHA1)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Metadata.extract","title":"extract()
","text":"Decode and extract information for the .torrent file.
Source code in torrentfile\\rebuild.py
def extract(self):\n\"\"\"\n Decode and extract information for the .torrent file.\n \"\"\"\n meta = pyben.load(self.path)\n info = meta[\"info\"]\n self.piece_length = info[\"piece length\"]\n self.name = info[\"name\"]\n self.meta_version = info.get(\"meta version\", 1)\n self.pieces = info.get(\"pieces\", bytes())\n if self.meta_version == 2:\n self._parse_tree(info[\"file tree\"], [self.name])\n elif \"length\" in info:\n self.length += info[\"length\"]\n self.is_file = True\n self.filenames.add(info[\"name\"])\n self.files.append({\n \"path\": Path(self.name).parent,\n \"filename\": self.name,\n \"full\": self.name,\n \"length\": self.length,\n })\n elif \"files\" in info:\n for f in info[\"files\"]:\n path = f[\"path\"]\n full = os.path.join(self.name, *path)\n self.files.append({\n \"path\": Path(full).parent,\n \"filename\": path[-1],\n \"full\": full,\n \"length\": f[\"length\"],\n })\n self.length += f[\"length\"]\n self.filenames.add(path[-1])\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.Metadata.rebuild","title":"rebuild(filemap: dict, dest: str)
","text":"Rebuild torrent file contents from filemap at dest.
Searches through the contents of the meta file and compares filenames with those in the filemap dict, and if found checks their contents, and copies them to the destination path.
PARAMETER DESCRIPTION filemap
filesystem information
TYPE: dict
dest
destiantion path
TYPE: str
Source code in torrentfile\\rebuild.py
def rebuild(self, filemap: dict, dest: str):\n\"\"\"\n Rebuild torrent file contents from filemap at dest.\n\n Searches through the contents of the meta file and compares filenames\n with those in the filemap dict, and if found checks their contents,\n and copies them to the destination path.\n\n Parameters\n ----------\n filemap : dict\n filesystem information\n dest : str\n destiantion path\n \"\"\"\n self._prog = None\n if self.meta_version == 2:\n self._match_v2(filemap, dest)\n else:\n self._match_v1(filemap, dest)\n if self._prog is not None:\n self.progbar.close_out()\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PathNode","title":"PathNode(start: int = None, stop: int = None, full: str = None, filename: str = None, path: str = None, length: int = None)
","text":"Base class representing information regarding a file included in torrent.
Hold file information that contributes to the contents of torrent.
PARAMETER DESCRIPTION start
where the piece starts, by default None
TYPE: int, optional
DEFAULT: None
stop
where the piece ends, by default None
TYPE: int, optional
DEFAULT: None
full
full path, by default None
TYPE: str, optional
DEFAULT: None
filename
filename, by default None
TYPE: str, optional
DEFAULT: None
path
parent path, by default None
TYPE: str, optional
DEFAULT: None
length
size, by default None
TYPE: int, optional
DEFAULT: None
Source code in torrentfile\\rebuild.py
def __init__(\n self,\n start: int = None,\n stop: int = None,\n full: str = None,\n filename: str = None,\n path: str = None,\n length: int = None,\n):\n\"\"\"\n Hold file information that contributes to the contents of torrent.\n\n Parameters\n ----------\n start : int, optional\n where the piece starts, by default None\n stop : int, optional\n where the piece ends, by default None\n full : str, optional\n full path, by default None\n filename : str, optional\n filename, by default None\n path : str, optional\n parent path, by default None\n length : int, optional\n size, by default None\n \"\"\"\n self.path = path\n self.start = start\n self.stop = stop\n self.length = length\n self.filename = filename\n self.full = full\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PathNode.__len__","title":"__len__() -> int
","text":"Return size of the file.
RETURNS DESCRIPTION int
total size
Source code in torrentfile\\rebuild.py
def __len__(self) -> int:\n\"\"\"\n Return size of the file.\n\n Returns\n -------\n int\n total size\n \"\"\"\n return self.length\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PathNode.get_part","title":"get_part(path: str) -> bytes
","text":"Extract the part of the file needed to complete the hash.
PARAMETER DESCRIPTION path
filesystem path location of file.
TYPE: str
RETURNS DESCRIPTION bytes
part of the file\u2019s contents
Source code in torrentfile\\rebuild.py
def get_part(self, path: str) -> bytes:\n\"\"\"\n Extract the part of the file needed to complete the hash.\n\n Parameters\n ----------\n path : str\n filesystem path location of file.\n\n Returns\n -------\n bytes\n part of the file's contents\n \"\"\"\n with open(path, \"rb\") as fd:\n if self.start:\n fd.seek(self.start)\n if self.stop != -1:\n partial = fd.read(self.stop - self.start)\n else:\n partial = fd.read()\n return partial\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PieceNode","title":"PieceNode(piece: bytes)
","text":"Base class representing a single SHA1 hash block of data from a torrent.
Store information about an individual SHA1 hash for a torrent file.
extended_summary
PARAMETER DESCRIPTION piece
SHA1 hash bytes
TYPE: bytes
Source code in torrentfile\\rebuild.py
def __init__(self, piece: bytes):\n\"\"\"\n Store information about an individual SHA1 hash for a torrent file.\n\n _extended_summary_\n\n Parameters\n ----------\n piece : bytes\n SHA1 hash bytes\n \"\"\"\n self.piece = piece\n self.paths = []\n self.result = None\n self.dest = None\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PieceNode.append","title":"append(pathnode: PathNode)
","text":"Append the path argument to the paths list attribute.
PARAMETER DESCRIPTION pathnode
the pathnode
TYPE: PathNode
Source code in torrentfile\\rebuild.py
def append(self, pathnode: PathNode):\n\"\"\"\n Append the path argument to the paths list attribute.\n\n Parameters\n ----------\n pathnode : PathNode\n the pathnode\n \"\"\"\n self.paths.append(pathnode)\n
"},{"location":"Source/rebuild/#torrentfile.rebuild.PieceNode.find_matches","title":"find_matches(filemap: dict, dest: str) -> bool
","text":"Find the matching files for each path in the node.
PARAMETER DESCRIPTION filemap
filename and details
TYPE: dict
dest
target destination path
TYPE: str
RETURNS DESCRIPTION bool
success status
Source code in torrentfile\\rebuild.py
def find_matches(self, filemap: dict, dest: str) -> bool:\n\"\"\"\n Find the matching files for each path in the node.\n\n Parameters\n ----------\n filemap : dict\n filename and details\n dest : str\n target destination path\n\n Returns\n -------\n bool\n success status\n \"\"\"\n self.dest = dest\n self.result = self._find_matches(filemap, self.paths[:], bytes())\n return self.result\n
"},{"location":"Source/recheck/","title":"Recheck","text":""},{"location":"Source/recheck/#torrentfile.recheck","title":"recheck
","text":"Module container Checker Class.
The CheckerClass takes a torrentfile and tha path to it\u2019s contents. It will then iterate through every file and directory contained and compare their data to values contained within the torrent file. Completion percentages will be printed to screen for each file and at the end for the torrentfile as a whole.
"},{"location":"Source/recheck/#torrentfile.recheck.Checker","title":"Checker(metafile: str, path: str)
","text":"Check a given file or directory to see if it matches a torrentfile.
Public constructor for Checker class instance.
PARAMETER DESCRIPTION metafile
Path to \u201c.torrent\u201d file.
TYPE: str
path
Path where the content is located in filesystem.
TYPE: str
"},{"location":"Source/recheck/#torrentfile.recheck.Checker--example","title":"Example","text":">> metafile = \"/path/to/torrentfile/content_file_or_dir.torrent\"\n>> location = \"/path/to/location\"\n>> os.path.exists(\"/path/to/location/content_file_or_dir\")\nOut: True\n>> checker = Checker(metafile, location)\n
Validate data against hashes contained in .torrent file.
PARAMETER DESCRIPTION metafile
path to .torrent file
TYPE: str
path
path to content or contents parent directory.
TYPE: str
Source code in torrentfile\\recheck.py
def __init__(self, metafile: str, path: str):\n\"\"\"\n Validate data against hashes contained in .torrent file.\n\n Parameters\n ----------\n metafile : str\n path to .torrent file\n path : str\n path to content or contents parent directory.\n \"\"\"\n if not os.path.exists(metafile):\n raise FileNotFoundError\n if os.path.isdir(metafile):\n raise ArgumentError(\n \"The <metafile> must be a .torrent file. Not a directory\")\n self.last_log = None\n self.log_msg(\"Checking: %s, %s\", metafile, path)\n self.metafile = metafile\n self.total = 0\n self.paths = []\n self.fileinfo = {}\n print(\"Extracting data from torrent file...\")\n self.meta = pyben.load(metafile)\n self.info = self.meta[\"info\"]\n self.name = self.info[\"name\"]\n self.piece_length = self.info[\"piece length\"]\n\n if \"meta version\" in self.info:\n if \"pieces\" in self.info:\n self.meta_version = 3\n else:\n self.meta_version = 2\n else:\n self.meta_version = 1\n\n self.root = self.find_root(path)\n self.check_paths()\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.check_paths","title":"check_paths()
","text":"Gather all file paths described in the torrent file.
Source code in torrentfile\\recheck.py
def check_paths(self):\n\"\"\"\n Gather all file paths described in the torrent file.\n \"\"\"\n finfo = self.fileinfo\n\n if \"length\" in self.info:\n self.log_msg(\"%s points to a single file\", self.root)\n self.total = self.info[\"length\"]\n self.paths.append(str(self.root))\n\n finfo[0] = {\n \"path\": self.root,\n \"length\": self.info[\"length\"],\n }\n\n if self.meta_version > 1:\n root = self.info[\"file tree\"][self.name][\"\"][\"pieces root\"]\n finfo[0][\"pieces root\"] = root\n\n return\n\n # Otherwise Content is more than 1 file.\n self.log_msg(\"%s points to a directory\", self.root)\n if self.meta_version == 1:\n for i, item in enumerate(self.info[\"files\"]):\n self.total += item[\"length\"]\n base = os.path.join(*item[\"path\"])\n\n self.fileinfo[i] = {\n \"path\": str(self.root / base),\n \"length\": item[\"length\"],\n }\n\n self.paths.append(str(self.root / base))\n return\n\n self.walk_file_tree(self.info[\"file tree\"], [])\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.find_root","title":"find_root(path: str) -> str
","text":"Check path for torrent content.
The path can be a relative or absolute filesystem path. In the case where the content is a single file, the path may point directly to the the file, or it may point to the parent directory. If content points to a directory. The directory will be checked to see if it matches the torrent\u2019s name, if not the directories contents will be searched. The returned value will be the absolute path that matches the torrent\u2019s name.
PARAMETER DESCRIPTION path
root path to torrent content
TYPE: str
RETURNS DESCRIPTION str
root path to content
Source code in torrentfile\\recheck.py
def find_root(self, path: str) -> str:\n\"\"\"\n Check path for torrent content.\n\n The path can be a relative or absolute filesystem path. In the case\n where the content is a single file, the path may point directly to the\n the file, or it may point to the parent directory. If content points\n to a directory. The directory will be checked to see if it matches\n the torrent's name, if not the directories contents will be searched.\n The returned value will be the absolute path that matches the torrent's\n name.\n\n Parameters\n ----------\n path : str\n root path to torrent content\n\n Returns\n -------\n str\n root path to content\n \"\"\"\n if not os.path.exists(path):\n self.log_msg(\"Could not locate torrent content %s.\", path)\n raise FileNotFoundError(path)\n\n root = Path(path)\n if root.name == self.name:\n self.log_msg(\"Content found: %s.\", str(root))\n return root\n\n if self.name in os.listdir(root):\n return root / self.name\n\n self.log_msg(\"Could not locate torrent content in: %s\", str(root))\n raise FileNotFoundError(root)\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.iter_hashes","title":"iter_hashes() -> tuple
","text":"Produce results of comparing torrent contents piece by piece.
YIELDS DESCRIPTION chunck
hash of data found on disk
TYPE: bytes
piece
hash of data when complete and correct
TYPE: bytes
path
path to file being hashed
TYPE: str
size
length of bytes hashed for piece
TYPE: int
Source code in torrentfile\\recheck.py
def iter_hashes(self) -> tuple:\n\"\"\"\n Produce results of comparing torrent contents piece by piece.\n\n Yields\n ------\n chunck : bytes\n hash of data found on disk\n piece : bytes\n hash of data when complete and correct\n path : str\n path to file being hashed\n size : int\n length of bytes hashed for piece\n \"\"\"\n matched = consumed = 0\n checker = self.piece_checker()\n for chunk, piece, path, size in checker(self):\n consumed += size\n matching = 0\n if chunk == piece:\n matching += size\n matched += size\n yield chunk, piece, path, size\n total_consumed = str(int(consumed / self.total * 100))\n percent_matched = str(int(matched / consumed * 100))\n self.log_msg(\n \"Processed: %s%%, Matched: %s%%\",\n total_consumed,\n percent_matched,\n )\n self._result = (matched / consumed) * 100 if consumed > 0 else 0\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.log_msg","title":"log_msg(*args, level: int = logging.INFO)
","text":"Log message msg
to logger and send msg
to callback hook.
PARAMETER DESCRIPTION *args
formatting args for log message
TYPE: dict
DEFAULT: ()
level
Log level for this message; default=logging.INFO
TYPE: int
DEFAULT: logging.INFO
Source code in torrentfile\\recheck.py
def log_msg(self, *args, level: int = logging.INFO):\n\"\"\"\n Log message `msg` to logger and send `msg` to callback hook.\n\n Parameters\n ----------\n *args : dict\n formatting args for log message\n level : int\n Log level for this message; default=`logging.INFO`\n \"\"\"\n message = args[0]\n if len(args) >= 3:\n message = message % tuple(args[1:])\n elif len(args) == 2:\n message = message % args[1]\n\n # Repeat log messages should be ignored.\n if message != self.last_log:\n self.last_log = message\n logger.log(level, message)\n if self._hook and level == logging.INFO:\n self._hook(message)\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.piece_checker","title":"piece_checker()
","text":"Check individual pieces of the torrent.
RETURNS DESCRIPTION HashChecker | FeedChecker
Individual piece hasher.
Source code in torrentfile\\recheck.py
def piece_checker(self):\n\"\"\"\n Check individual pieces of the torrent.\n\n Returns\n -------\n HashChecker | FeedChecker\n Individual piece hasher.\n \"\"\"\n if self.meta_version == 1:\n return FeedChecker\n return HashChecker\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.register_callback","title":"register_callback(hook)
classmethod
","text":"Register hooks from 3rd party programs to access generated info.
PARAMETER DESCRIPTION hook
callback function for the logging feature.
TYPE: function
Source code in torrentfile\\recheck.py
@classmethod\ndef register_callback(cls, hook):\n\"\"\"\n Register hooks from 3rd party programs to access generated info.\n\n Parameters\n ----------\n hook : function\n callback function for the logging feature.\n \"\"\"\n cls._hook = hook\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.results","title":"results()
","text":"Generate result percentage and store for future calls.
Source code in torrentfile\\recheck.py
def results(self):\n\"\"\"\n Generate result percentage and store for future calls.\n \"\"\"\n responses = []\n for response in self.iter_hashes():\n responses.append(response)\n\n self.log_msg(\"Final result for %s recheck: %s\", self.metafile,\n self._result)\n\n return self._result\n
"},{"location":"Source/recheck/#torrentfile.recheck.Checker.walk_file_tree","title":"walk_file_tree(tree: dict, partials: list)
","text":"Traverse File Tree dictionary to get file details.
Extract full pathnames, length, root hash, and layer hashes for each file included in the .torrent\u2019s file tree.
PARAMETER DESCRIPTION tree
File Tree dict extracted from torrent file.
TYPE: dict
partials
list of intermediate pathnames.
TYPE: list
Source code in torrentfile\\recheck.py
def walk_file_tree(self, tree: dict, partials: list):\n\"\"\"\n Traverse File Tree dictionary to get file details.\n\n Extract full pathnames, length, root hash, and layer hashes\n for each file included in the .torrent's file tree.\n\n Parameters\n ----------\n tree : dict\n File Tree dict extracted from torrent file.\n partials : list\n list of intermediate pathnames.\n \"\"\"\n for key, val in tree.items():\n # Empty string means the tree's leaf is value\n if \"\" in val:\n base = os.path.join(*partials, key)\n roothash = None\n length = val[\"\"][\"length\"]\n roothash = None if not length else val[\"\"][\"pieces root\"]\n full = str(self.root / base)\n self.fileinfo[len(self.paths)] = {\n \"path\": full,\n \"length\": length,\n \"pieces root\": roothash,\n }\n self.paths.append(full)\n self.total += length\n else:\n self.walk_file_tree(val, partials + [key])\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker","title":"FeedChecker(checker: Checker)
","text":" Bases: ProgMixin
Validates torrent content.
Seemlesly validate torrent file contents by comparing hashes in metafile against data on disk.
PARAMETER DESCRIPTION checker
the checker class instance.
TYPE: object
Generate hashes of piece length data from filelist contents.
Source code in torrentfile\\recheck.py
def __init__(self, checker: Checker):\n\"\"\"\n Generate hashes of piece length data from filelist contents.\n \"\"\"\n self.piece_length = checker.piece_length\n self.paths = checker.paths\n self.pieces = checker.info[\"pieces\"]\n self.fileinfo = checker.fileinfo\n self.piece_map = {}\n self.index = 0\n self.piece_count = 0\n self.it = None\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.__iter__","title":"__iter__()
","text":"Assign iterator and return self.
Source code in torrentfile\\recheck.py
def __iter__(self):\n\"\"\"\n Assign iterator and return self.\n \"\"\"\n self.it = self.iter_pieces()\n return self\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.__next__","title":"__next__()
","text":"Yield back result of comparison.
Source code in torrentfile\\recheck.py
def __next__(self):\n\"\"\"\n Yield back result of comparison.\n \"\"\"\n try:\n partial = next(self.it)\n except StopIteration as itererror:\n raise StopIteration from itererror\n\n chunck = sha1(partial).digest() # nosec\n start = self.piece_count * SHA1\n end = start + SHA1\n piece = self.pieces[start:end]\n self.piece_count += 1\n path = self.paths[self.index]\n return chunck, piece, path, len(partial)\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.extract","title":"extract(path: str, partial: bytearray) -> bytearray
","text":"Split file paths contents into blocks of data for hash pieces.
PARAMETER DESCRIPTION path
path to content.
TYPE: str
partial
any remaining content from last file.
TYPE: bytes
RETURNS DESCRIPTION bytearray
Hash digest for block of .torrent contents.
Source code in torrentfile\\recheck.py
def extract(self, path: str, partial: bytearray) -> bytearray:\n\"\"\"\n Split file paths contents into blocks of data for hash pieces.\n\n Parameters\n ----------\n path : str\n path to content.\n partial : bytes\n any remaining content from last file.\n\n Returns\n -------\n bytearray\n Hash digest for block of .torrent contents.\n \"\"\"\n read = 0\n length = self.fileinfo[self.index][\"length\"]\n partial = bytearray() if len(partial) == self.piece_length else partial\n if path not in self.paths: # pragma: no cover\n raise MissingPathError(path)\n with open(path, \"rb\") as current:\n while True:\n bitlength = self.piece_length - len(partial)\n part = bytearray(bitlength)\n amount = current.readinto(part)\n read += amount\n partial.extend(part[:amount])\n if amount < bitlength:\n if amount > 0 and read == length:\n self.progbar.update(amount)\n yield partial\n break\n self.progbar.update(amount)\n yield partial\n partial = bytearray(0)\n if length != read:\n for pad in self._gen_padding(partial, length, read):\n yield pad\n
"},{"location":"Source/recheck/#torrentfile.recheck.FeedChecker.iter_pieces","title":"iter_pieces()
","text":"Iterate through, and hash pieces of torrent contents.
YIELDS DESCRIPTION piece
hash digest for block of torrent data.
TYPE: bytes
Source code in torrentfile\\recheck.py
def iter_pieces(self):\n\"\"\"\n Iterate through, and hash pieces of torrent contents.\n\n Yields\n ------\n piece : bytes\n hash digest for block of torrent data.\n \"\"\"\n partial = bytearray()\n for i, path in enumerate(self.paths):\n total = self.fileinfo[i][\"length\"]\n self.progbar = self.get_progress_tracker(total, path)\n self.index = i\n if os.path.exists(path):\n for piece in self.extract(path, partial):\n if (len(piece) == self.piece_length) or (i + 1 == len(\n self.paths)):\n yield piece\n else:\n partial = piece\n\n else:\n length = self.fileinfo[i][\"length\"]\n for pad in self._gen_padding(partial, length):\n if len(pad) == self.piece_length:\n yield pad\n else:\n partial = pad\n self.progbar.close_out()\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker","title":"HashChecker(checker: Checker)
","text":" Bases: ProgMixin
Iterate through contents of meta data and verify with file contents.
PARAMETER DESCRIPTION checker
the checker instance that maintains variables.
TYPE: Checker
Construct a HybridChecker instance.
Source code in torrentfile\\recheck.py
def __init__(self, checker: Checker):\n\"\"\"\n Construct a HybridChecker instance.\n \"\"\"\n self.checker = checker\n self.paths = checker.paths\n self.piece_length = checker.piece_length\n self.fileinfo = checker.fileinfo\n self.piece_layers = checker.meta[\"piece layers\"]\n self.current = None\n self.index = -1\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.Padder","title":"Padder(length, piece_length)
","text":"Padding class to generate padding hashes wherever needed.
PARAMETER DESCRIPTION length
the total size of the mock file generating padding for.
piece_length
the block size that each hash represents.
TYPE: int
Construct padding class to Mock missing or incomplete files.
PARAMETER DESCRIPTION length
size of the file
TYPE: int
piece_length
the piece length for each iteration.
TYPE: int
Source code in torrentfile\\recheck.py
def __init__(self, length, piece_length):\n\"\"\"\n Construct padding class to Mock missing or incomplete files.\n\n Parameters\n ----------\n length : int\n size of the file\n piece_length : int\n the piece length for each iteration.\n \"\"\"\n self.length = length\n self.piece_length = piece_length\n self.pad = sha256(bytearray(piece_length)).digest()\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.Padder.__iter__","title":"__iter__()
","text":"Return self to correctly implement iterator type.
Source code in torrentfile\\recheck.py
def __iter__(self):\n\"\"\"\n Return self to correctly implement iterator type.\n \"\"\"\n return self # pragma: nocover\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.Padder.__next__","title":"__next__() -> bytes
","text":"Iterate through seemingly endless sha256 hashes of zeros.
RETURNS DESCRIPTION tuple
returns the padding
TYPE: bytes
RAISES DESCRIPTION StopIteration
Source code in torrentfile\\recheck.py
def __next__(self) -> bytes:\n\"\"\"\n Iterate through seemingly endless sha256 hashes of zeros.\n\n Returns\n -------\n tuple :\n returns the padding\n\n Raises\n ------\n StopIteration\n \"\"\"\n if self.length >= self.piece_length:\n self.length -= self.piece_length\n return self.pad\n if self.length > 0:\n pad = sha256(bytearray(self.length)).digest()\n self.length -= self.length\n return pad\n raise StopIteration\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.__iter__","title":"__iter__()
","text":"Assign iterator and return self.
Source code in torrentfile\\recheck.py
def __iter__(self):\n\"\"\"\n Assign iterator and return self.\n \"\"\"\n return self\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.__next__","title":"__next__()
","text":"Provide the result of comparison.
Source code in torrentfile\\recheck.py
def __next__(self):\n\"\"\"\n Provide the result of comparison.\n \"\"\"\n if self.current is None:\n self.next_file()\n try:\n return self.process_current()\n except StopIteration as itererr:\n if self.next_file():\n return self.process_current()\n raise StopIteration from itererr\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.advance","title":"advance() -> tuple
","text":"Increment the number of pieces processed for the current file.
RETURNS DESCRIPTION tuple
the piece and size
Source code in torrentfile\\recheck.py
def advance(self) -> tuple:\n\"\"\"\n Increment the number of pieces processed for the current file.\n\n Returns\n -------\n tuple\n the piece and size\n \"\"\"\n start = self.count * SHA256\n end = start + SHA256\n piece = self.pieces[start:end]\n self.count += 1\n if self.length >= self.piece_length:\n self.length -= self.piece_length\n size = self.piece_length\n else:\n size = self.length\n self.length -= self.length\n return piece, size\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.next_file","title":"next_file() -> bool
","text":"Remove all references to processed files and prepare for the next.
RETURNS DESCRIPTION bool
if there is a next file found
Source code in torrentfile\\recheck.py
def next_file(self) -> bool:\n\"\"\"\n Remove all references to processed files and prepare for the next.\n\n Returns\n -------\n bool\n if there is a next file found\n \"\"\"\n self.index += 1\n if self.current is None or self.index < len(self.paths):\n self.current = self.paths[self.index]\n self.length = self.fileinfo[self.index][\"length\"]\n self.root_hash = self.fileinfo[self.index][\"pieces root\"]\n if self.length > self.piece_length:\n self.pieces = self.piece_layers[self.root_hash]\n else:\n self.pieces = self.root_hash\n path = self.paths[self.index]\n self.progbar = self.get_progress_tracker(self.length, path)\n self.count = 0\n if os.path.exists(self.current):\n self.hasher = FileHasher(\n path,\n self.piece_length,\n progress=2,\n progress_bar=self.progbar,\n )\n else:\n self.hasher = self.Padder(self.length, self.piece_length)\n return True\n if self.index >= len(self.paths):\n del self.current\n del self.length\n del self.root_hash\n del self.pieces\n return False\n
"},{"location":"Source/recheck/#torrentfile.recheck.HashChecker.process_current","title":"process_current() -> tuple
","text":"Gather necessary information to compare to metafile details.
RETURNS DESCRIPTION tuple
a tuple containing the layer, piece, current path and size
RAISES DESCRIPTION StopIteration
Source code in torrentfile\\recheck.py
def process_current(self) -> tuple:\n\"\"\"\n Gather necessary information to compare to metafile details.\n\n Returns\n -------\n tuple\n a tuple containing the layer, piece, current path and size\n\n Raises\n ------\n StopIteration\n \"\"\"\n try:\n layer = next(self.hasher)\n piece, size = self.advance()\n self.progbar.update(size)\n return layer, piece, self.current, size\n except StopIteration as err:\n if self.length > 0 and self.count * SHA256 < len(self.pieces):\n self.hasher = self.Padder(self.length, self.piece_length)\n piece, size = self.advance()\n layer = next(self.hasher)\n self.progbar.update(0)\n return layer, piece, self.current, size\n raise StopIteration from err\n
"},{"location":"Source/torrent/","title":"Torrent","text":""},{"location":"Source/torrent/#torrentfile.torrent","title":"torrent
","text":"Classes and procedures pertaining to the creation of torrent meta files.
"},{"location":"Source/torrent/#torrentfile.torrent--classes","title":"Classes","text":" TorrentFile
construct .torrent file.
TorrentFileV2
construct .torrent v2 files using provided data.
MetaFile
base class for all MetaFile classes.
"},{"location":"Source/torrent/#torrentfile.torrent--constants","title":"Constants","text":""},{"location":"Source/torrent/#torrentfile.torrent--bittorrent-v2","title":"Bittorrent V2","text":"From Bittorrent.org Documentation pages.
Implementation details for Bittorrent Protocol v2.
Note
All strings in a .torrent file that contain text must be UTF-8 encoded.
"},{"location":"Source/torrent/#torrentfile.torrent--meta-version-2-dictionary","title":"Meta Version 2 Dictionary:","text":" \u201cannounce\u201d: The URL of the tracker.
\u201cinfo\u201d: This maps to a dictionary, with keys described below.
\u201cname\u201d: A display name for the torrent. It is purely advisory.
\u201cpiece length\u201d: The number of bytes that each logical piece in the peer protocol refers to. I.e. it sets the granularity of piece, request, bitfield and have messages. It must be a power of two and at least 6KiB.
\u201cmeta version\u201d: An integer value, set to 2 to indicate compatibility with the current revision of this specification. Version 1 is not assigned to avoid confusion with BEP3. Future revisions will only increment this issue to indicate an incompatible change has been made, for example that hash algorithms were changed due to newly discovered vulnerabilities. Lementations must check this field first and indicate that a torrent is of a newer version than they can handle before performing other idations which may result in more general messages about invalid files. Files are mapped into this piece address space so that each non-empty
\u201cfile tree\u201d: A tree of dictionaries where dictionary keys represent UTF-8 encoded path elements. Entries with zero-length keys describe the properties of the composed path at that point. \u2018UTF-8 encoded\u2019 context only means that if the native encoding is known at creation time it must be converted to UTF-8. Keys may contain invalid UTF-8 sequences or characters and names that are reserved on specific filesystems. Implementations must be prepared to sanitize them. On platforms path components exactly matching \u2018.\u2019 and \u2018..\u2019 must be sanitized since they could lead to directory traversal attacks and conflicting path descriptions. On platforms that require UTF-8 path components this sanitizing step must happen after normalizing overlong UTF-8 encodings. File is aligned to a piece boundary and occurs in same order as the file tree. The last piece of each file may be shorter than the specified piece length, resulting in an alignment gap.
\u201clength\u201d: Length of the file in bytes. Presence of this field indicates that the dictionary describes a file, not a directory. Which means it must not have any sibling entries.
\u201cpieces root\u201d: For non-empty files this is the the root hash of a merkle tree with a branching factor of 2, constructed from 16KiB blocks of the file. The last block may be shorter than 16KiB. The remaining leaf hashes beyond the end of the file required to construct upper layers of the merkle tree are set to zero. As of meta version 2 SHA2-256 is used as digest function for the merkle tree. The hash is stored in its binary form, not as human-readable string.
\u201cpiece layers\u201d: A dictionary of strings. For each file in the file tree that is larger than the piece size it contains one string value. The keys are the merkle roots while the values consist of concatenated hashes of one layer within that merkle tree. The layer is chosen so that one hash covers piece length bytes. For example if the piece size is 16KiB then the leaf hashes are used. If a piece size of 128KiB is used then 3rd layer up from the leaf hashes is used. Layer hashes which exclusively cover data beyond the end of file, i.e. are only needed to balance the tree, are omitted. All hashes are stored in their binary format. A torrent is not valid if this field is absent, the contained hashes do not match the merkle roots or are not from the correct layer.
Important
The file tree root dictionary itself must not be a file, i.e. it must not contain a zero-length key with a dictionary containing a length key.
"},{"location":"Source/torrent/#torrentfile.torrent--bittorrent-v1","title":"Bittorrent V1","text":""},{"location":"Source/torrent/#torrentfile.torrent--v1-meta-dictionary","title":"v1 meta-dictionary","text":" Note
In the single file case, the name key is the name of a file, in the muliple file case, it\u2019s the name of a directory.
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile","title":"MetaFile(path = None, announce = None, comment = None, align = False, piece_length = None, private = False, outfile = None, source = None, progress = 1, cwd = False, httpseeds = None, url_list = None, content = None, meta_version = None, **_)
","text":"Base Class for all TorrentFile classes.
PARAMETER DESCRIPTION path
target path to torrent content. Default: None
TYPE: str
DEFAULT: None
announce
One or more tracker URL\u2019s. Default: None
TYPE: str
DEFAULT: None
comment
A comment. Default: None
TYPE: str
DEFAULT: None
piece_length
Size of torrent pieces. Default: None
TYPE: int
DEFAULT: None
private
For private trackers. Default: None
TYPE: bool
DEFAULT: False
outfile
target path to write .torrent file. Default: None
TYPE: str
DEFAULT: None
source
Private tracker source. Default: None
TYPE: str
DEFAULT: None
progress
level of progress bar displayed Default: \u201c1\u201d
TYPE: str
DEFAULT: 1
cwd
If True change default save location to current directory
TYPE: bool
DEFAULT: False
httpseeds
one or more web addresses where torrent content can be found.
TYPE: list
DEFAULT: None
url_list
one or more web addressess where torrent content exists.
TYPE: list
DEFAULT: None
content
alias for \u2018path\u2019 arg.
TYPE: str
DEFAULT: None
meta_version
indicates which Bittorrent protocol to use for hashing content
TYPE: int
DEFAULT: None
Construct MetaFile superclass and assign local attributes.
Source code in torrentfile\\torrent.py
def __init__(\n self,\n path=None,\n announce=None,\n comment=None,\n align=False,\n piece_length=None,\n private=False,\n outfile=None,\n source=None,\n progress=1,\n cwd=False,\n httpseeds=None,\n url_list=None,\n content=None,\n meta_version=None,\n **_,\n):\n\"\"\"\n Construct MetaFile superclass and assign local attributes.\n \"\"\"\n self.private = private\n self.cwd = cwd\n self.outfile = outfile\n self.progress = int(progress)\n self.comment = comment\n self.source = source\n self.meta_version = meta_version\n\n if content:\n path = content\n if not path:\n if announce and len(announce) > 1 and os.path.exists(announce[-1]):\n path = announce[-1]\n announce = announce[:-1]\n elif url_list and os.path.exists(url_list[-1]):\n path = url_list[-1]\n url_list = url_list[:-1]\n elif httpseeds and os.path.exists(httpseeds[-1]):\n path = httpseeds[-1]\n httpseeds = httpseeds[:-1]\n else:\n raise utils.MissingPathError(\"Path to content is required.\")\n\n # base path to torrent content.\n self.path = path\n\n logger.debug(\"path parameter found %s\", path)\n\n self.meta = {\n \"created by\": f\"torrentfile_v{version}\",\n \"creation date\": int(datetime.timestamp(datetime.now())),\n \"info\": {},\n }\n\n # Format piece_length attribute.\n if piece_length:\n self.piece_length = utils.normalize_piece_length(piece_length)\n logger.debug(\"piece length parameter found %s\", piece_length)\n else:\n self.piece_length = utils.path_piece_length(self.path)\n logger.debug(\"piece length calculated %s\", self.piece_length)\n\n # Assign announce URL to empty string if none provided.\n if not announce:\n self.announce, self.announce_list = \"\", [[\"\"]]\n\n # Most torrent clients have editting trackers as a feature.\n elif isinstance(announce, str):\n self.announce, self.announce_list = announce, [[announce]]\n\n elif isinstance(announce, Sequence):\n self.announce, self.announce_list = announce[0], [announce]\n\n self.align = align\n\n if self.announce:\n self.meta[\"announce\"] = self.announce\n self.meta[\"announce-list\"] = self.announce_list\n if comment:\n self.meta[\"info\"][\"comment\"] = comment\n logger.debug(\"comment parameter found %s\", comment)\n if private:\n self.meta[\"info\"][\"private\"] = 1\n logger.debug(\"private parameter triggered\")\n if source:\n self.meta[\"info\"][\"source\"] = source\n logger.debug(\"source parameter found %s\", source)\n if url_list:\n self.meta[\"url-list\"] = url_list\n logger.debug(\"url list parameter found %s\", str(url_list))\n if httpseeds:\n self.meta[\"httpseeds\"] = httpseeds\n logger.debug(\"httpseeds parameter found %s\", str(httpseeds))\n self.meta[\"info\"][\"piece length\"] = self.piece_length\n\n self.meta_version = meta_version\n parent, self.name = os.path.split(self.path)\n if not self.name:\n self.name = os.path.basename(parent)\n self.meta[\"info\"][\"name\"] = self.name\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.assemble","title":"assemble()
","text":"Overload in subclasses.
RAISES DESCRIPTION Exception
NotImplementedError
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Overload in subclasses.\n\n Raises\n ------\n Exception\n NotImplementedError\n \"\"\"\n raise NotImplementedError\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.set_callback","title":"set_callback(func)
classmethod
","text":"Assign a callback function for the Hashing class to call for each hash.
PARAMETER DESCRIPTION func
The callback function which accepts a single paramter.
TYPE: function
Source code in torrentfile\\torrent.py
@classmethod\ndef set_callback(cls, func):\n\"\"\"\n Assign a callback function for the Hashing class to call for each hash.\n\n Parameters\n ----------\n func : function\n The callback function which accepts a single paramter.\n \"\"\"\n if \"hasher\" in vars(cls) and vars(cls)[\"hasher\"]:\n cls.hasher.set_callback(func)\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.sort_meta","title":"sort_meta()
","text":"Sort the info and meta dictionaries.
Source code in torrentfile\\torrent.py
def sort_meta(self):\n\"\"\"Sort the info and meta dictionaries.\"\"\"\n logger.debug(\"sorting dictionary keys\")\n meta = self.meta\n meta[\"info\"] = dict(sorted(list(meta[\"info\"].items())))\n meta = dict(sorted(list(meta.items())))\n return meta\n
"},{"location":"Source/torrent/#torrentfile.torrent.MetaFile.write","title":"write(outfile = None) -> tuple
","text":"Write meta information to .torrent file.
Final step in the torrent file creation process. After hashing and sorting every piece of content write the contents to file using the bencode encoding.
PARAMETER DESCRIPTION outfile
Destination path for .torrent file. default=None
TYPE: str
DEFAULT: None
RETURNS DESCRIPTION outfile
Where the .torrent file was writen.
TYPE: str
meta
.torrent meta information.
TYPE: dict
Source code in torrentfile\\torrent.py
def write(self, outfile=None) -> tuple:\n\"\"\"\n Write meta information to .torrent file.\n\n Final step in the torrent file creation process.\n After hashing and sorting every piece of content\n write the contents to file using the bencode encoding.\n\n Parameters\n ----------\n outfile : str\n Destination path for .torrent file. default=None\n\n Returns\n -------\n outfile : str\n Where the .torrent file was writen.\n meta : dict\n .torrent meta information.\n \"\"\"\n if outfile:\n self.outfile = outfile\n if not self.outfile: # pragma: nocover\n path = os.path.join(os.getcwd(), self.name) + \".torrent\"\n self.outfile = path\n if str(self.outfile)[-1] in \"\\\\/\":\n self.outfile = self.outfile + (self.name + \".torrent\")\n self.meta = self.sort_meta()\n try:\n pyben.dump(self.meta, self.outfile)\n except PermissionError as excp:\n logger.error(\"Permission Denied: Could not write to %s\",\n self.outfile)\n raise PermissionError from excp\n return self.outfile, self.meta\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentAssembler","title":"TorrentAssembler(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Assembler class for Bittorrent version 2 and hybrid meta files.
This differs from the TorrentFileV2 and TorrentFileHybrid, because it can be used as an iterator and works for both versions.
PARAMETER DESCRIPTION **kwargs
Keyword arguments for torrent options.
TYPE: dict
DEFAULT: {}
Create Bittorrent v1 v2 hybrid metafiles.
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Create Bittorrent v1 v2 hybrid metafiles.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent Hybrid file\")\n self.name = os.path.basename(self.path)\n self.hashes = []\n self.piece_layers = {}\n self.pieces = bytearray()\n self.files = []\n self.hybrid = self.meta_version == \"3\"\n size, file_list = utils.filelist_total(self.path)\n self.kws = {\n \"progress\": self.progress,\n \"progress_bar\": None,\n \"hybrid\": self.hybrid,\n }\n self.total = len(file_list)\n\n if self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n self.kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n self.kws[\"progress_bar\"] = self.prog_bar\n\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentAssembler.assemble","title":"assemble()
","text":"Assemble the parts of the torrentfile into meta dictionary.
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble the parts of the torrentfile into meta dictionary.\n \"\"\"\n info = self.meta[\"info\"]\n info[\"meta version\"] = 2\n\n if os.path.isfile(self.path):\n info[\"file tree\"] = {self.name: self._traverse(self.path)}\n info[\"length\"] = os.path.getsize(self.path)\n\n else:\n info[\"file tree\"] = self._traverse(self.path)\n if self.hybrid:\n info[\"files\"] = self.files\n\n if self.hybrid:\n info[\"pieces\"] = self.pieces\n self.meta[\"piece layers\"] = self.piece_layers\n return info\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFile","title":"TorrentFile(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Class for creating Bittorrent meta files.
Construct Torrentfile class instance object.
PARAMETER DESCRIPTION **kwargs
Dictionary containing torrent file options.
TYPE: dict
DEFAULT: {}
Construct TorrentFile instance with given keyword args.
PARAMETER DESCRIPTION **kwargs
dictionary of keyword args passed to superclass.
TYPE: dict
DEFAULT: {}
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Construct TorrentFile instance with given keyword args.\n\n Parameters\n ----------\n **kwargs : dict\n dictionary of keyword args passed to superclass.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent v1 torrent file\")\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFile.assemble","title":"assemble()
","text":"Assemble components of torrent metafile.
RETURNS DESCRIPTION dict
metadata dictionary for torrent file
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble components of torrent metafile.\n\n Returns\n -------\n dict\n metadata dictionary for torrent file\n \"\"\"\n info = self.meta[\"info\"]\n size, filelist = utils.filelist_total(self.path)\n kws = {\n \"progress\": self.progress,\n \"progress_bar\": None,\n \"align\": self.align,\n }\n\n if self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n kws[\"progress_bar\"] = self.prog_bar\n\n if os.path.isfile(self.path):\n info[\"length\"] = size\n elif not self.align:\n info[\"files\"] = [{\n \"length\":\n os.path.getsize(path),\n \"path\":\n os.path.relpath(path, self.path).split(os.sep),\n } for path in filelist]\n else:\n info[\"files\"] = []\n for path in filelist:\n filesize = os.path.getsize(path)\n info[\"files\"].append({\n \"length\":\n filesize,\n \"path\":\n os.path.relpath(path, self.path).split(os.sep),\n })\n if filesize < self.piece_length:\n remainder = self.piece_length - filesize\n else:\n remainder = filesize % self.piece_length\n if remainder:\n info[\"files\"].append({\n \"attr\": \"p\",\n \"length\": remainder,\n \"path\": [\".pad\", str(remainder)],\n })\n pieces = bytearray()\n feeder = Hasher(filelist, self.piece_length, **kws)\n for piece in feeder:\n pieces.extend(piece)\n info[\"pieces\"] = pieces\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileHybrid","title":"TorrentFileHybrid(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Construct the Hybrid torrent meta file with provided parameters.
PARAMETER DESCRIPTION **kwargs
Keyword arguments for torrent options.
TYPE: dict
DEFAULT: {}
Create Bittorrent v1 v2 hybrid metafiles.
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Create Bittorrent v1 v2 hybrid metafiles.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent Hybrid file\")\n self.name = os.path.basename(self.path)\n self.hashes = []\n self.piece_layers = {}\n self.pieces = []\n self.files = []\n size, file_list = utils.filelist_total(self.path)\n self.kws = {\"progress\": self.progress, \"progress_bar\": None}\n self.total = len(file_list)\n\n if self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n self.kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n self.kws[\"progress_bar\"] = self.prog_bar\n\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileHybrid.assemble","title":"assemble()
","text":"Assemble the parts of the torrentfile into meta dictionary.
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble the parts of the torrentfile into meta dictionary.\n \"\"\"\n info = self.meta[\"info\"]\n info[\"meta version\"] = 2\n\n if os.path.isfile(self.path):\n info[\"file tree\"] = {self.name: self._traverse(self.path)}\n info[\"length\"] = os.path.getsize(self.path)\n\n else:\n info[\"file tree\"] = self._traverse(self.path)\n info[\"files\"] = self.files\n\n info[\"pieces\"] = b\"\".join(self.pieces)\n self.meta[\"piece layers\"] = self.piece_layers\n return info\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileV2","title":"TorrentFileV2(**kwargs)
","text":" Bases: MetaFile
, ProgMixin
Class for creating Bittorrent meta v2 files.
PARAMETER DESCRIPTION **kwargs
Keyword arguments for torrent file options.
TYPE: dict
DEFAULT: {}
Construct TorrentFileV2
Class instance from given parameters.
PARAMETER DESCRIPTION **kwargs
keywword arguments to pass to superclass.
TYPE: dict
DEFAULT: {}
Source code in torrentfile\\torrent.py
def __init__(self, **kwargs):\n\"\"\"\n Construct `TorrentFileV2` Class instance from given parameters.\n\n Parameters\n ----------\n **kwargs : dict\n keywword arguments to pass to superclass.\n \"\"\"\n super().__init__(**kwargs)\n logger.debug(\"Assembling bittorrent v2 torrent file\")\n self.piece_layers = {}\n self.hashes = []\n size, file_list = utils.filelist_total(self.path)\n self.kws = {\"progress\": self.progress, \"progress_bar\": None}\n self.total = len(file_list)\n\n if self.progress == 2:\n self.prog_bar = self.get_progress_tracker(size, str(self.path))\n self.kws[\"progress_bar\"] = self.prog_bar\n\n elif self.progress == 0:\n self.prog_bar = self.get_progress_tracker(-1, \"\")\n self.kws[\"progress_bar\"] = self.prog_bar\n\n self.assemble()\n
"},{"location":"Source/torrent/#torrentfile.torrent.TorrentFileV2.assemble","title":"assemble()
","text":"Assemble then return the meta dictionary for encoding.
RETURNS DESCRIPTION meta
Metainformation about the torrent.
TYPE: dict
Source code in torrentfile\\torrent.py
def assemble(self):\n\"\"\"\n Assemble then return the meta dictionary for encoding.\n\n Returns\n -------\n meta : dict\n Metainformation about the torrent.\n \"\"\"\n info = self.meta[\"info\"]\n if os.path.isfile(self.path):\n info[\"file tree\"] = {info[\"name\"]: self._traverse(self.path)}\n info[\"length\"] = os.path.getsize(self.path)\n else:\n info[\"file tree\"] = self._traverse(self.path)\n\n info[\"meta version\"] = 2\n self.meta[\"piece layers\"] = self.piece_layers\n
"},{"location":"Source/utils/","title":"Utils","text":""},{"location":"Source/utils/#torrentfile.utils","title":"utils
","text":"Utility functions and classes used throughout package.
"},{"location":"Source/utils/#torrentfile.utils.ArgumentError","title":"ArgumentError
","text":" Bases: Exception
Exception for mismatched or mistyped CLI arguments.
"},{"location":"Source/utils/#torrentfile.utils.Memo","title":"Memo(func)
","text":"Memoize cache.
PARAMETER DESCRIPTION func
The results of this callable will be cached.
TYPE: Callable
Construct cache.
Source code in torrentfile\\utils.py
def __init__(self, func):\n\"\"\"\n Construct cache.\n \"\"\"\n self.func = func\n self.counter = 0\n self.cache = {}\n
"},{"location":"Source/utils/#torrentfile.utils.Memo.__call__","title":"__call__(path: str)
","text":"Invoke each time memo function is executed.
PARAMETER DESCRIPTION path
The relative or absolute path being used as key in cache dict.
TYPE: str
RETURNS DESCRIPTION Any
The results of calling the function with path.
Source code in torrentfile\\utils.py
def __call__(self, path: str):\n\"\"\"\n Invoke each time memo function is executed.\n\n Parameters\n ----------\n path : str\n The relative or absolute path being used as key in cache dict.\n\n Returns\n -------\n Any :\n The results of calling the function with path.\n \"\"\"\n if path in self.cache and os.path.exists(path):\n self.counter += 1\n return self.cache[path]\n result = self.func(path)\n self.cache[path] = result\n return result\n
"},{"location":"Source/utils/#torrentfile.utils.MissingPathError","title":"MissingPathError(message: str = None)
","text":" Bases: Exception
Path parameter is required to specify target content.
Creating a .torrent file with no contents seems rather silly.
PARAMETER DESCRIPTION message
Message for user (optional).
TYPE: str
DEFAULT: None
Raise when creating a meta file without specifying target content.
The message
argument is a message to pass to Exception base class.
Source code in torrentfile\\utils.py
def __init__(self, message: str = None):\n\"\"\"\n Raise when creating a meta file without specifying target content.\n\n The `message` argument is a message to pass to Exception base class.\n \"\"\"\n self.message = f\"Path arguement is missing and required {str(message)}\"\n super().__init__(message)\n
"},{"location":"Source/utils/#torrentfile.utils.PieceLengthValueError","title":"PieceLengthValueError(message: str = None)
","text":" Bases: Exception
Piece Length parameter must equal a perfect power of 2.
PARAMETER DESCRIPTION message
Message for user (optional).
TYPE: str
DEFAULT: None
Raise when creating a meta file with incorrect piece length value.
The message
argument is a message to pass to Exception base class.
Source code in torrentfile\\utils.py
def __init__(self, message: str = None):\n\"\"\"\n Raise when creating a meta file with incorrect piece length value.\n\n The `message` argument is a message to pass to Exception base class.\n \"\"\"\n self.message = f\"Incorrect value for piece length: {str(message)}\"\n super().__init__(message)\n
"},{"location":"Source/utils/#torrentfile.utils.check_path_writable","title":"check_path_writable(path: str) -> bool
","text":"Test if output path is writable.
PARAMETER DESCRIPTION path
file system path string
TYPE: str
RETURNS DESCRIPTION bool
True if writeable, otherwise raises PermissionError
Source code in torrentfile\\utils.py
def check_path_writable(path: str) -> bool:\n\"\"\"\n Test if output path is writable.\n\n Parameters\n ----------\n path : str\n file system path string\n\n Returns\n -------\n bool\n True if writeable, otherwise raises PermissionError\n \"\"\"\n try:\n if path.endswith(\"\\\\\") or path.endswith(\"/\"):\n path = os.path.join(path, \".torrent\")\n with open(path, \"ab\") as _:\n pass\n os.remove(path)\n except PermissionError as err: # pragma: nocover\n directory = os.path.dirname(path)\n message = f\"Target directory is not writeable {directory}\"\n raise PermissionError(message) from err\n return True\n
"},{"location":"Source/utils/#torrentfile.utils.colored","title":"colored(string: str, key: int) -> str
","text":"Output terminal content with formatting.
Source code in torrentfile\\utils.py
def colored(string: str, key: int) -> str:\n\"\"\"\n Output terminal content with formatting.\n \"\"\"\n return f\"\\033[{key}m{string}\\033[0m\"\n
"},{"location":"Source/utils/#torrentfile.utils.copypath","title":"copypath(source: str, dest: str) -> None
","text":"Copy the file located at source to dest.
If one or more directory paths don\u2019t exist in dest, they will be created. If dest already exists and dest and source are the same size, it will be ignored, however if dest is smaller than source, dest will be overwritten.
PARAMETER DESCRIPTION source
path to source file
TYPE: str
dest
path to target destination
TYPE: str
Source code in torrentfile\\utils.py
def copypath(source: str, dest: str) -> None:\n\"\"\"\n Copy the file located at source to dest.\n\n If one or more directory paths don't exist in dest, they will be created.\n If dest already exists and dest and source are the same size, it will be\n ignored, however if dest is smaller than source, dest will be overwritten.\n\n Parameters\n ----------\n source : str\n path to source file\n dest : str\n path to target destination\n \"\"\"\n if not os.path.exists(source) or (os.path.exists(dest)\n and os.path.getsize(source)\n <= os.path.getsize(dest)):\n return\n path_parts = Path(dest).parts\n if len(path_parts) > 1:\n root = path_parts[0]\n path_parts = path_parts[1:-1]\n if not os.path.exists(root):\n os.mkdir(root) # pragma: nocover\n for part in path_parts:\n path = os.path.join(root, part)\n if not os.path.exists(path):\n os.mkdir(path)\n root = path\n shutil.copy(source, dest)\n
"},{"location":"Source/utils/#torrentfile.utils.debug_is_on","title":"debug_is_on() -> bool
","text":"Return True if debug mode is on in environment variables.
RETURNS DESCRIPTION bool
is debug mode on
Source code in torrentfile\\utils.py
def debug_is_on() -> bool:\n\"\"\"\n Return True if debug mode is on in environment variables.\n\n Returns\n -------\n bool\n is debug mode on\n \"\"\"\n return os.environ[\"TORRENTFILE_DEBUG\"] == \"ON\"\n
"},{"location":"Source/utils/#torrentfile.utils.filelist_total","title":"filelist_total(pathstring: str) -> os.PathLike
","text":"Perform error checking and format conversion to os.PathLike.
PARAMETER DESCRIPTION pathstring
An existing filesystem path.
TYPE: str
RETURNS DESCRIPTION os.PathLike
Input path converted to bytes format.
RAISES DESCRIPTION MissingPathError
File could not be found.
Source code in torrentfile\\utils.py
@Memo\ndef filelist_total(pathstring: str) -> os.PathLike:\n\"\"\"\n Perform error checking and format conversion to os.PathLike.\n\n Parameters\n ----------\n pathstring : str\n An existing filesystem path.\n\n Returns\n -------\n os.PathLike\n Input path converted to bytes format.\n\n Raises\n ------\n MissingPathError\n File could not be found.\n \"\"\"\n if os.path.exists(pathstring):\n path = Path(pathstring)\n return _filelist_total(path)\n raise MissingPathError\n
"},{"location":"Source/utils/#torrentfile.utils.get_file_list","title":"get_file_list(path: str) -> list
","text":"Return a sorted list of file paths contained in directory.
PARAMETER DESCRIPTION path
target file or directory.
TYPE: str
RETURNS DESCRIPTION list
sorted list of file paths.
TYPE: list
Source code in torrentfile\\utils.py
def get_file_list(path: str) -> list:\n\"\"\"\n Return a sorted list of file paths contained in directory.\n\n Parameters\n ----------\n path : str\n target file or directory.\n\n Returns\n -------\n list :\n sorted list of file paths.\n \"\"\"\n _, filelist = filelist_total(path)\n return filelist\n
"},{"location":"Source/utils/#torrentfile.utils.get_piece_length","title":"get_piece_length(size: int) -> int
","text":"Calculate the ideal piece length for bittorrent data.
PARAMETER DESCRIPTION size
Total bits of all files incluided in .torrent file.
TYPE: int
RETURNS DESCRIPTION int
Ideal piece length.
Source code in torrentfile\\utils.py
def get_piece_length(size: int) -> int:\n\"\"\"\n Calculate the ideal piece length for bittorrent data.\n\n Parameters\n ----------\n size : int\n Total bits of all files incluided in .torrent file.\n\n Returns\n -------\n int\n Ideal piece length.\n \"\"\"\n exp = 14\n while size / (2**exp) > 1000 and exp < 24:\n exp += 1\n return 2**exp\n
"},{"location":"Source/utils/#torrentfile.utils.green","title":"green(string: str) -> str
","text":"Output terminal content in green color.
Source code in torrentfile\\utils.py
def green(string: str) -> str:\n\"\"\"\n Output terminal content in green color.\n \"\"\"\n return colored(string, 92)\n
"},{"location":"Source/utils/#torrentfile.utils.humanize_bytes","title":"humanize_bytes(amount: int) -> str
","text":"Convert integer into human readable memory sized denomination.
PARAMETER DESCRIPTION amount
total number of bytes.
TYPE: int
RETURNS DESCRIPTION str
human readable representation of the given amount of bytes.
Source code in torrentfile\\utils.py
def humanize_bytes(amount: int) -> str:\n\"\"\"\n Convert integer into human readable memory sized denomination.\n\n Parameters\n ----------\n amount : int\n total number of bytes.\n\n Returns\n -------\n str\n human readable representation of the given amount of bytes.\n \"\"\"\n base = 1024\n amount = float(amount)\n value = abs(amount)\n if value == 1:\n return f\"{amount} Byte\"\n if value < base:\n return f\"{amount} Bytes\"\n for i, s in enumerate(SUFFIXES):\n unit = base**(i + 2)\n if value < unit:\n break\n value = base * amount / unit\n return f\"{value:.1f} {s}\"\n
"},{"location":"Source/utils/#torrentfile.utils.next_power_2","title":"next_power_2(value: int) -> int
","text":"Calculate the next perfect power of 2 equal to or greater than value.
PARAMETER DESCRIPTION value
integer value that is less than some perfect power of 2.
TYPE: int
RETURNS DESCRIPTION int
The next power of 2 greater than value, or value if already power of 2.
Source code in torrentfile\\utils.py
def next_power_2(value: int) -> int:\n\"\"\"\n Calculate the next perfect power of 2 equal to or greater than value.\n\n Parameters\n ----------\n value : int\n integer value that is less than some perfect power of 2.\n\n Returns\n -------\n int\n The next power of 2 greater than value, or value if already power of 2.\n \"\"\"\n if not value & (value - 1) and value:\n return value\n start = 1\n while start < value:\n start <<= 1\n return start\n
"},{"location":"Source/utils/#torrentfile.utils.normalize_piece_length","title":"normalize_piece_length(piece_length: int) -> int
","text":"Verify input piece_length is valid and convert accordingly.
PARAMETER DESCRIPTION piece_length
The piece length provided by user.
TYPE: int | str
RETURNS DESCRIPTION int
normalized piece length.
RAISES DESCRIPTION PieceLengthValueError :
Piece length is improper value.
Source code in torrentfile\\utils.py
def normalize_piece_length(piece_length: int) -> int:\n\"\"\"\n Verify input piece_length is valid and convert accordingly.\n\n Parameters\n ----------\n piece_length : int | str\n The piece length provided by user.\n\n Returns\n -------\n int\n normalized piece length.\n\n Raises\n ------\n PieceLengthValueError :\n Piece length is improper value.\n \"\"\"\n if isinstance(piece_length, str):\n if piece_length.isnumeric():\n piece_length = int(piece_length)\n else:\n raise PieceLengthValueError(piece_length)\n\n if piece_length > (1 << 14):\n if 2**math.log2(piece_length) == piece_length:\n return piece_length\n raise PieceLengthValueError(piece_length)\n\n if 13 < piece_length < 26:\n return 2**piece_length\n if piece_length <= 13:\n raise PieceLengthValueError(piece_length)\n\n log = int(math.log2(piece_length))\n if 2**log == piece_length:\n return piece_length\n raise PieceLengthValueError\n
"},{"location":"Source/utils/#torrentfile.utils.path_piece_length","title":"path_piece_length(path: str) -> int
","text":"Calculate piece length for input path and contents.
PARAMETER DESCRIPTION path
The absolute path to directory and contents.
TYPE: str
RETURNS DESCRIPTION int
The size of pieces of torrent content.
Source code in torrentfile\\utils.py
def path_piece_length(path: str) -> int:\n\"\"\"\n Calculate piece length for input path and contents.\n\n Parameters\n ----------\n path : str\n The absolute path to directory and contents.\n\n Returns\n -------\n int\n The size of pieces of torrent content.\n \"\"\"\n psize = path_size(path)\n return get_piece_length(psize)\n
"},{"location":"Source/utils/#torrentfile.utils.path_size","title":"path_size(path: str) -> int
","text":"Return the total size of all files in path recursively.
PARAMETER DESCRIPTION path
path to target file or directory.
TYPE: str
RETURNS DESCRIPTION int
total size of files.
Source code in torrentfile\\utils.py
def path_size(path: str) -> int:\n\"\"\"\n Return the total size of all files in path recursively.\n\n Parameters\n ----------\n path : str\n path to target file or directory.\n\n Returns\n -------\n int\n total size of files.\n \"\"\"\n total_size, _ = filelist_total(path)\n return total_size\n
"},{"location":"Source/utils/#torrentfile.utils.path_stat","title":"path_stat(path: str) -> tuple
","text":"Calculate directory statistics.
PARAMETER DESCRIPTION path
The path to start calculating from.
TYPE: str
RETURNS DESCRIPTION Tuple[list, int, int]
list - List of all files contained in Directory int - Total sum of bytes from all contents of dir int - The size of pieces of the torrent contents.
Source code in torrentfile\\utils.py
def path_stat(path: str) -> tuple:\n\"\"\"\n Calculate directory statistics.\n\n Parameters\n ----------\n path : str\n The path to start calculating from.\n\n Returns\n -------\n Tuple[list, int, int] :\n list - List of all files contained in Directory\n int - Total sum of bytes from all contents of dir\n int - The size of pieces of the torrent contents.\n \"\"\"\n total_size, filelist = filelist_total(path)\n piece_length = get_piece_length(total_size)\n return (filelist, total_size, piece_length)\n
"},{"location":"Source/utils/#torrentfile.utils.toggle_debug_mode","title":"toggle_debug_mode(switch_on: bool)
","text":"Switch the environment variable debug indicator on or off.
PARAMETER DESCRIPTION switch_on
if true turn debug mode on otherwise off
TYPE: bool
Source code in torrentfile\\utils.py
def toggle_debug_mode(switch_on: bool):\n\"\"\"\n Switch the environment variable debug indicator on or off.\n\n Parameters\n ----------\n switch_on : bool\n if true turn debug mode on otherwise off\n \"\"\"\n os.environ[\"TORRENTFILE_DEBUG\"] = \"ON\" if switch_on else \"OFF\"\n
"},{"location":"Source/version/","title":"Version","text":""},{"location":"Source/version/#torrentfile.version","title":"version
","text":"Holds the release version number.
"},{"location":"coverage/","title":"Coverage","text":""}]}
\ No newline at end of file
diff --git a/docs/sitemap.xml.gz b/docs/sitemap.xml.gz
index 18adf59..855416a 100644
Binary files a/docs/sitemap.xml.gz and b/docs/sitemap.xml.gz differ
diff --git a/torrentfile/mixins.py b/torrentfile/mixins.py
index 456c5f9..ae3e0b9 100644
--- a/torrentfile/mixins.py
+++ b/torrentfile/mixins.py
@@ -132,7 +132,8 @@ def get_progress(self) -> str:
pbar.append(f"{state:.02f}")
return "".join(pbar)
- def close_out(self):
+ @staticmethod
+ def close_out():
"""
Finalize the last bits of progress bar.
@@ -201,7 +202,8 @@ class ProgMixin:
class NoProg:
"""Stand-in object for when progress mode is set to 0."""
- def update(self, value):
+ @staticmethod
+ def update(value):
"""
Return the value.