├── .gitignore
├── .vs
├── ProjectSettings.json
├── PythonSettings.json
├── VSWorkspaceState.json
├── auto_py_torrent
│ └── v16
│ │ └── .suo
└── slnx.sqlite
├── HISTORY.rst
├── LICENSE
├── Makefile
├── README.rst
├── auto_py_torrent.pyproj
├── auto_py_torrent.pyproj.user
├── auto_py_torrent.sln
├── auto_py_torrent
├── __init__.py
├── __version__.py
└── auto_py_torrent.py
├── docs
└── __init__.py
├── requirements.txt
├── setup.py
└── tests
└── __init__.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # Jupyter Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # SageMath parsed files
80 | *.sage.py
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 | .spyproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | # mkdocs documentation
98 | /site
99 |
100 | # mypy
101 | .mypy_cache/
102 |
103 | .idea/
104 | .vscode/
105 |
--------------------------------------------------------------------------------
/.vs/ProjectSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "CurrentProjectSetting": null
3 | }
--------------------------------------------------------------------------------
/.vs/PythonSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "TestFramework": "Pytest",
3 | "Interpreter": "env\\Scripts\\python.exe"
4 | }
--------------------------------------------------------------------------------
/.vs/VSWorkspaceState.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExpandedNodes": [
3 | "",
4 | "\\auto_py_torrent",
5 | "\\tests"
6 | ],
7 | "PreviewInSolutionExplorer": false
8 | }
--------------------------------------------------------------------------------
/.vs/auto_py_torrent/v16/.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocslegna/auto_py_torrent/5950c8791acebafc0126f3440796065c9e066dee/.vs/auto_py_torrent/v16/.suo
--------------------------------------------------------------------------------
/.vs/slnx.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocslegna/auto_py_torrent/5950c8791acebafc0126f3440796065c9e066dee/.vs/slnx.sqlite
--------------------------------------------------------------------------------
/HISTORY.rst:
--------------------------------------------------------------------------------
1 | .. :changelog:
2 |
3 | Release History
4 | ---------------
5 |
6 | Development
7 | +++++++++++
8 |
9 | 1.0.8 (2017-08-04)
10 | ++++++++++++++++++
11 |
12 | * Improve README.
13 |
14 | **Improvements**
15 |
16 | - Provide images to README.
17 |
18 |
19 | 1.0.7 (2017-08-04)
20 | ++++++++++++++++++
21 |
22 | * Improve README, PARSER info and provide more explicit information to the user.
23 |
24 | **Improvements**
25 |
26 | - Provide a better README, PARSER and remove back to menu option once a file was downloaded or already returned.
27 |
28 |
29 | 1.0.5 (2017-07-31)
30 | ++++++++++++++++++
31 |
32 | * Dependency fix and minor usage help.
33 |
34 | **Bugfixes**
35 |
36 | - Fix bad bs4 dependency string in requires.
37 |
38 |
39 | 1.0.4 (2017-07-29)
40 | ++++++++++++++++++
41 |
42 | * Improve README.
43 |
44 | **Improvements**
45 |
46 | - Provide a better README.
47 |
48 |
49 | 1.0.3 (2017-07-28)
50 | ++++++++++++++++++
51 |
52 | * Minor.
53 |
54 | **Bugfixes**
55 |
56 | - Fix the select index of provided list.
57 |
58 |
59 | 1.0.2 (2017-07-28)
60 | ++++++++++++++++++
61 |
62 | * pip3 install integration.
63 |
64 | **Improvements**
65 |
66 | - Provide install with pip3: ``sudo pip3 install auto_py_torrent``.
67 |
68 |
69 | 1.0.0 (2017-07-28)
70 | ++++++++++++++++++
71 |
72 | * First launch! Gone live.
73 |
74 | **Improvements**
75 |
76 | - ``auto_py_torrent`` runs best_rated and list mode.
77 | - 5 different sites to search.
78 | - Now you can repeat searches and downloads until you get tired.
79 | - Can back to menu while searching.
80 | - No need of double quotes with ['B', 'b'] options to search.
81 | - Minor improvements.
82 |
83 | **Bugfixes**
84 |
85 | - Fix poor decision making after found content.
86 | - Handle bad requests.
87 |
88 |
89 | 0.0.2 (2017-07-26)
90 | ++++++++++++++++++
91 |
92 | * First alpha tests.
93 |
94 | **Improvements**
95 |
96 | - ``auto_py_torrent`` runs best_rated mode.
97 |
98 | **Bugfixes**
99 |
100 | - Resolve bad domain paths, fix parser, fix init class function, change main function.
101 |
102 |
103 | 0.0.1 (2017-07-23)
104 | ++++++++++++++++++
105 |
106 | * First pre-alpha tests.
107 |
108 | **Improvements**
109 |
110 | - ``auto_py_torrent`` has now all scheduled torrent sites.
111 |
112 | **Bugfixes**
113 |
114 | - Resolve content for BeautifulSoup instantiation from requests.
115 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Gabriel Scotillo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | init:
2 | pip3 install -r requirements.txt
3 |
4 | publish:
5 | pip3 install 'twine>=1.9.1'
6 | python3 setup.py sdist bdist_wheel
7 | twine upload dist/*
8 | rm -fr build dist .egg requests.egg-info
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | auto_py_torrent
2 | ###############
3 |
4 | **auto_py_torrent** is an automated tool for download files by obtaining torrents or magnets that are in different provided pages that the user can choose.
5 |
6 | Its goal is to make it easier for users to find the files they want and download them instantly.
7 |
8 | An ``auto_py_torrent`` command is provided in which the user can currently choose between two modes, ``best_rated`` and ``list`` mode, then it selects one of the torrent tracking pages for multimedia content and finally enter the text of what you want to download.
9 |
10 | We Can ``List`` It Out!
11 |
12 | .. image:: https://user-images.githubusercontent.com/6371898/28991985-b72c0340-7967-11e7-97a2-c33d96d43706.png
13 | :target: https://github.com/ocslegna/auto_py_torrent/
14 |
15 |
16 |
17 | With a Little ``Help`` from My Friends!
18 |
19 | .. image:: https://user-images.githubusercontent.com/6371898/28991984-b46bb402-7967-11e7-9d39-9f8b55362ac9.png
20 | :target: https://github.com/ocslegna/auto_py_torrent/
21 |
22 |
23 |
24 | .. contents::
25 |
26 | .. section-numbering::
27 |
28 | Main features
29 | =============
30 |
31 | * Simple and easy search, for file downloads using torrent.
32 | * Formatted and colorized terminal output.
33 |
34 |
35 | Installation and an usage example.
36 | ==================================
37 |
38 | First, check your python3 version.
39 | ----------------------------------
40 |
41 | .. code-block:: bash
42 |
43 | $ python3 --version
44 |
45 | Upgrade it as you wish.
46 |
47 |
48 | Install ``python3-pip``:
49 | ------------------------
50 |
51 | ``Mac``
52 | ~~~~~~~
53 | .. code-block:: bash
54 |
55 | $ brew install python3
56 |
57 | ``Linux``
58 | ~~~~~~~~
59 | Using the package manager with different linux distributions:
60 |
61 | .. code-block:: bash
62 |
63 | # Ubuntu/Debian.
64 | $ sudo apt-get update
65 | $ sudo apt-get install -y python3-pip
66 |
67 | .. code-block:: bash
68 |
69 | # Fedora, CentOS, RHEL.
70 | $ sudo dnf install python3-pip
71 |
72 | .. code-block:: bash
73 |
74 | # Arch.
75 | $ pacman -S python3-pip
76 |
77 |
78 | ``Windows``
79 | ~~~~~~~~~~~
80 |
81 | If ``C:\path\to\python\Scripts\pip3`` is not there remite to:
82 |
83 | * ``_
84 | for Windows download.
85 |
86 |
87 | Install virtualenv if necessary and activate it.
88 | ------------------------------------------------
89 |
90 | .. code-block:: bash
91 |
92 | $ sudo pip3 install virtualenv
93 | $ cd
94 | $ virtualenv venv_auto_py
95 | $ cd venv_auto_py
96 | $ source bin/activate
97 |
98 |
99 | Install auto_py_torrent and get an example!
100 | -------------------------------------------
101 |
102 | .. code-block:: bash
103 |
104 | # With virtual env activated:
105 | $ pip3 install auto_py_torrent
106 |
107 | # Without virtual env:
108 | $ sudo pip3 install auto_py_torrent
109 |
110 | # This way you are getting a detail list of results from ``torrent_project`` site.
111 | $ auto_py_torrent 1 0 "The simpsons"
112 |
113 |
114 | Usage
115 | =====
116 | .. code-block:: bash
117 |
118 | $ auto_py_torrent MODE SELECTED_PAGE STRING_TO_SEARCH
119 |
120 |
121 | See also ``auto_py_torrent --help``.
122 |
123 |
124 | Examples
125 | --------
126 |
127 | Using ``best_rated`` mode with ``torrent_project`` page:
128 |
129 | .. code-block:: bash
130 |
131 | $ auto_py_torrent 0 0 "The simpsons"
132 |
133 |
134 | Using ``list`` mode with ``the pirate bay`` page:
135 |
136 | .. code-block:: bash
137 |
138 | $ auto_py_torrent 1 1 "The simpsons"
139 |
140 |
141 | Meta
142 | ====
143 |
144 | Dependencies
145 | ------------
146 |
147 | **auto_py_torrent** uses this incredibles libraries:
148 |
149 | * `BeautifulSoup `_
150 | — Python library for pulling data out of HTML and XML files.
151 | * `Requests `_
152 | — Requests is an elegant and simple HTTP library for Python, built for human beings.
153 | * `Tabulate `_
154 | — Python library for tabular data print in Python, a library and a command-line utility.
155 | * `Coloredlogs `_
156 | — Python package that enables colored terminal output for Python’s logging module.
157 | * `lxml `_
158 | — Python library for processing XML and HTML in the Python language.
159 |
160 |
161 | Release History
162 | ---------------
163 |
164 | See `HISTORY `_.
165 |
166 |
167 | Licence
168 | -------
169 |
170 | MIT: `LICENSE `_.
171 |
172 |
173 | Author
174 | -------
175 |
176 | `Gabriel Scotillo`_ (`@gabrielscotillo`_)
177 |
178 |
179 | Package index
180 | -------------
181 |
182 | ``_.
183 |
184 |
185 | .. _Gabriel Scotillo: https://ocslegna.herokuapp.com
186 | .. _@gabrielscotillo: https://twitter.com/gabrielscotillo
187 |
--------------------------------------------------------------------------------
/auto_py_torrent.pyproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | {16823f05-e280-4302-9c2a-c704ca8b29ca}
7 |
8 | setup.py
9 |
10 | .
11 | .
12 | {888888a0-9f3d-457c-b088-3a5042f75d52}
13 | Standard Python launcher
14 | MSBuild|env|$(MSBuildProjectFullPath)
15 |
16 |
17 |
18 |
19 | 10.0
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | env
40 | env (Python 3.7 (64-bit))
41 | Scripts\python.exe
42 | Scripts\pythonw.exe
43 | 3.7
44 | X64
45 | PYTHONPATH
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/auto_py_torrent.pyproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ShowAllFiles
5 |
6 |
--------------------------------------------------------------------------------
/auto_py_torrent.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31205.134
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "auto_py_torrent", "auto_py_torrent.pyproj", "{16823F05-E280-4302-9C2A-C704CA8B29CA}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {16823F05-E280-4302-9C2A-C704CA8B29CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {16823F05-E280-4302-9C2A-C704CA8B29CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | GlobalSection(ExtensibilityGlobals) = postSolution
21 | SolutionGuid = {06869AA4-B542-46C1-AAC0-A52397FC5A95}
22 | EndGlobalSection
23 | EndGlobal
24 |
--------------------------------------------------------------------------------
/auto_py_torrent/__init__.py:
--------------------------------------------------------------------------------
1 | """init.py - root of the package."""
2 |
--------------------------------------------------------------------------------
/auto_py_torrent/__version__.py:
--------------------------------------------------------------------------------
1 | __title__ = 'auto_py_torrent'
2 | __description__ = 'Automated tool for file downloads, using torrent or magnet.'
3 | __url__ = 'https://github.com/ocslegna/auto_py_torrent'
4 | __version__ = '1.0.8'
5 | __build__ = 0x001000
6 | __author__ = 'Gabriel Scotillo'
7 | __author_email__ = 'gabrielscotillo@gmail.com'
8 | __license__ = 'MIT'
9 | __copyright__ = 'Copyright 2017 Gabriel Scotillo'
10 |
--------------------------------------------------------------------------------
/auto_py_torrent/auto_py_torrent.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """auto_py_torrent.
5 |
6 | This module provides utilities to download a torrent within specific types.
7 | """
8 |
9 | # Author: Gabriel Scotillo
10 | # URL: https://github.com/ocslegna/auto_py_torrent
11 | # Please do not download illegal torrents or torrents that you do not have
12 | # permission to own.
13 | # This tool is for educational purposes only. Any damage you make will not
14 | # affect the author.
15 |
16 |
17 | import os
18 | import subprocess
19 | import sys
20 | import traceback
21 | import logging
22 | import argparse
23 | import re
24 | import textwrap
25 | import coloredlogs
26 | import requests
27 |
28 | from bs4 import BeautifulSoup
29 | from bs4 import UnicodeDammit
30 | from tabulate import tabulate
31 |
32 |
33 | MODES = 'best_rated list'.split()
34 |
35 | # {'torrentz2':
36 | # {'page': 'https://torrentz2.eu/search?f=',
37 | # 'key_search': 'did not match'}},
38 | # {'rarbg':
39 | # {'page': 'https://rarbg.to/torrents.php?search=',
40 | # 'key_search': ''}},
41 |
42 | TORRENTS = ({'torrent_project':
43 | {'page': 'https://torrentproject.se/?t=',
44 | 'key_search': 'No results',
45 | 'domain': 'https://torrentproject.se'}},
46 | {'the_pirate_bay':
47 | {'page': 'https://proxyspotting.in/s/?q=',
48 | 'key_search': 'No hits',
49 | 'domain': 'https://proxyspotting.in'}},
50 | {'1337x':
51 | {'page': 'https://1337x.to/search/',
52 | 'key_search': 'No results were returned',
53 | 'domain': 'https://1337x.to'}},
54 | {'eztv':
55 | {'page': 'https://eztv.ag/search/',
56 | 'key_search': 'It does not have any.',
57 | 'domain': 'https://eztv.ag'}},
58 | {'limetorrents':
59 | {'page': 'https://www.limetorrents.cc/search/all/',
60 | 'key_search': 'No results found',
61 | 'domain': 'https://www.limetorrents.cc'}},
62 | {'isohunt':
63 | {'page': 'https://isohunt.to/torrents/?ihq=',
64 | 'key_search': 'No results found',
65 | 'domain': 'https://isohunt.to'}})
66 | logging.basicConfig(level=logging.DEBUG)
67 | LOGGER = logging.getLogger(__name__)
68 | coloredlogs.install()
69 |
70 |
71 | class Colors:
72 | """Color class container."""
73 |
74 | HEADER = '\033[95m'
75 | OKBLUE = '\033[94m'
76 | OKGREEN = '\033[92m'
77 | WARNING = '\033[93m'
78 | FAIL = '\033[91m'
79 | ENDC = '\033[0m'
80 | BOLD = '\033[1m'
81 | UNDERLINE = '\033[4m'
82 | BLACK = '\033[30m'
83 | BLUE = '\033[34m'
84 | GREEN = '\033[42m'
85 | CYAN = '\033[36m'
86 | RED = '\033[41m'
87 | PINK = '\033[95m'
88 | PURPLE = '\033[35m'
89 | LIGHTBLUE = '\033[94m'
90 | LGREEN = '\033[0m\033[32m'
91 | LIGHTCYAN = '\033[0m\033[36m'
92 | LRED = '\033[0m\033[31m'
93 | LIGHTPURPLE = '\033[0m\033[35m'
94 | SEEDER = '\033[1m\033[32m'
95 | LEECHER = '\033[1m\033[31m'
96 |
97 |
98 | def get_parser():
99 | """Load parser for command line arguments.
100 |
101 | It parses argv/input into args variable.
102 | """
103 | desc = Colors.LIGHTBLUE + textwrap.dedent(
104 | '''\
105 | Welcome to
106 | _ _ _
107 | __ _ _ _| |_ ___ _ __ _ _ | |_ ___ _ __ _ __ ___ _ __ | |_
108 | / _` | | | | __/ _ \ | '_ \| | | | | __/ _ \| '__| '__/ _ \ '_ \| __|
109 | | (_| | |_| | || (_) | | |_) | |_| | | || (_) | | | | | __/ | | | |_
110 | \__,_|\__,_|\__\___/____| .__/ \__, |___\__\___/|_| |_| \___|_| |_|\__|
111 | |_____|_| |___/_____|
112 |
113 | ------------------------------------
114 | auto_py_torrent is an automated tool for download files by obtaining
115 | torrents or magnets that are in different provided pages that the
116 | user can choose.
117 |
118 | Its goal is to make it easier for users to find the files they want
119 | and download them instantly.
120 |
121 | An auto_py_torrent command is provided in which the user can
122 | currently choose between two modes, best_rated and list mode, then it
123 | selects one of the torrent tracking pages for multimedia content and
124 | finally enter the text of what you want to download.
125 | ------------------------------------
126 | ''') + Colors.ENDC
127 | usage_info = Colors.LGREEN + textwrap.dedent(
128 | '''\
129 |
130 | Use "%(prog)s --help" for more information.
131 | Examples:
132 | use "%(prog)s MODE SELECTED_PAGE STRING_TO_SEARCH # generic.
133 | use "%(prog)s 0 0 "The simpsons" # best rated.
134 | use "%(prog)s 1 0 "The simpsons" # list rated.
135 |
136 | Mode options:
137 | 0: best_rated. # Download the most rated file.
138 | 1: list. # Get a list, and select one of them.
139 |
140 | Page list options:
141 | 0: torrent project.
142 | 1: the pirate bay.
143 | 2: 1337x.
144 | 3: eztv.
145 | 4: limetorrents.
146 | 5: isohunt.
147 | ''') + Colors.ENDC
148 | epi = Colors.LIGHTPURPLE + textwrap.dedent(
149 | '''\
150 | -> Thanks for using auto_py_torrent!
151 | ''') + Colors.ENDC
152 |
153 | # Parent and only parser.
154 | parser = argparse.ArgumentParser(
155 | add_help=True,
156 | formatter_class=argparse.RawTextHelpFormatter,
157 | usage=usage_info,
158 | description=desc,
159 | epilog=epi)
160 | parser.add_argument('mode', action='store',
161 | choices=range(len(MODES)),
162 | type=int,
163 | help='Select mode of file download.\n'
164 | ' e.g: 0(rated) or 1(list).')
165 | parser.add_argument('torr_page', action='store',
166 | choices=range(len(TORRENTS)),
167 | type=int,
168 | help='Select tracking page to download from.\n'
169 | ' e.g: 0 to .. ' + str(len(TORRENTS)-1) + '.')
170 | parser.add_argument('str_search', action='store',
171 | type=str,
172 | help='Input torrent string to search.\n'
173 | ' e.g: "String search"')
174 | return(parser)
175 |
176 |
177 | def is_num(var):
178 | """Check if var string is num. Should use it only with strings."""
179 | try:
180 | int(var)
181 | return True
182 | except ValueError:
183 | return False
184 |
185 |
186 | class AutoPy:
187 | """AutoPy class for instance variables."""
188 |
189 | def __init__(self, args, string_search, mode_search,
190 | page, key_search, torrent_page, domain):
191 | """Args not entered will be defaulted."""
192 | self.args = args
193 | self.back_to_menu = False
194 | self.content_page = None
195 | self.domain = domain
196 | self.elements = None
197 | self.found_torrents = False
198 | self.hrefs = []
199 | self.keep_search = True
200 | self.key_search = key_search
201 | self.magnet = ""
202 | self.mode_search = mode_search
203 | self.page = page
204 | self.picked_choice = False
205 | self.selected = ""
206 | self.string_search = string_search
207 | self.table = None
208 | self.torrent = ""
209 | self.torrent_page = torrent_page
210 |
211 | def open_magnet(self):
212 | """Open magnet according to os."""
213 | if sys.platform.startswith('linux'):
214 | subprocess.Popen(['xdg-open', self.magnet],
215 | stdout=subprocess.PIPE, stderr=subprocess.PIPE)
216 | elif sys.platform.startswith('win32'):
217 | os.startfile(self.magnet)
218 | elif sys.platform.startswith('cygwin'):
219 | os.startfile(self.magnet)
220 | elif sys.platform.startswith('darwin'):
221 | subprocess.Popen(['open', self.magnet],
222 | stdout=subprocess.PIPE, stderr=subprocess.PIPE)
223 | else:
224 | subprocess.Popen(['xdg-open', self.magnet],
225 | stdout=subprocess.PIPE, stderr=subprocess.PIPE)
226 |
227 | def get_magnet(self, url):
228 | """Get magnet from torrent page. Url already got domain."""
229 | content_most_rated = requests.get(url)
230 | rated_soup = BeautifulSoup(content_most_rated.content, 'lxml')
231 |
232 | if self.page == 'torrent_project':
233 | self.magnet = rated_soup.find(
234 | 'a', href=True, text=re.compile('Download'))['href']
235 |
236 | elif self.page == 'the_pirate_bay':
237 | self.magnet = rated_soup.find(
238 | 'a', href=True, text=re.compile('Get this torrent'))['href']
239 |
240 | elif self.page == '1337x':
241 | div1337 = rated_soup.find(
242 | 'div', {'class': 'torrent-category-detail'})
243 | self.magnet = div1337.find('a', href=re.compile('magnet'))['href']
244 |
245 | elif self.page == 'isohunt':
246 | self.magnet = rated_soup.find(
247 | 'a', href=re.compile('magnet'))['href']
248 |
249 | else:
250 | print('Wrong page to get magnet!')
251 | sys.exit(1)
252 |
253 | def download_torrent(self):
254 | """Download torrent.
255 |
256 | Rated implies download the unique best rated torrent found.
257 | Otherwise: get the magnet and download it.
258 | """
259 | try:
260 | if self.back_to_menu is True:
261 | return
262 | if self.found_torrents is False:
263 | print('Nothing found.')
264 | return
265 | if self.mode_search == 'best_rated':
266 | print('Downloading..')
267 | self.open_magnet()
268 | elif self.mode_search == 'list':
269 | if self.selected is not None:
270 | # t_p, pirate and 1337x got magnet inside, else direct.
271 | if self.page in ['eztv', 'limetorrents']:
272 | self.magnet = self.hrefs[int(self.selected)]
273 | print('Downloading..')
274 | self.open_magnet()
275 | elif self.page in ['the_pirate_bay',
276 | 'torrent_project',
277 | '1337x',
278 | 'isohunt']:
279 | url = self.hrefs[int(self.selected)]
280 | self.get_magnet(url)
281 | print('Downloading..')
282 | self.open_magnet()
283 | else:
284 | print('Bad selected page.')
285 | else:
286 | print('Nothing selected.')
287 | sys.exit(1)
288 | except Exception:
289 | print(traceback.format_exc())
290 | sys.exit(0)
291 |
292 | def build_table(self):
293 | """Build table."""
294 | headers = ['Title', 'Seeders', 'Leechers', 'Age', 'Size']
295 | titles = []
296 | seeders = []
297 | leechers = []
298 | ages = []
299 | sizes = []
300 |
301 | if self.page == 'torrent_project':
302 | titles = [list(span.find('a').stripped_strings)[0]
303 | for span in self.elements[0]]
304 |
305 | seeders = [span.get_text() for span in self.elements[1]]
306 | leechers = [span.get_text() for span in self.elements[2]]
307 | ages = [span.get_text() for span in self.elements[3]]
308 | sizes = [span.get_text() for span in self.elements[4]]
309 |
310 | # Torrents
311 | self.hrefs = [self.domain +
312 | span.find('a')['href']
313 | for span in self.elements[0]]
314 |
315 | elif self.page == 'the_pirate_bay':
316 | for elem in self.elements[0]:
317 | title = elem.find('a', {'class': 'detLink'}).get_text()
318 | titles.append(title)
319 |
320 | font_text = elem.find(
321 | 'font', {'class': 'detDesc'}).get_text()
322 | dammit = UnicodeDammit(font_text)
323 | age, size = dammit.unicode_markup.split(',')[:-1]
324 | ages.append(age)
325 | sizes.append(size)
326 | # Torrent
327 | href = self.domain + \
328 | elem.find('a', title=re.compile('magnet'))['href']
329 | self.hrefs.append(str(href))
330 |
331 | seeders = [elem.get_text() for elem in self.elements[1]]
332 | leechers = [elem.get_text() for elem in self.elements[2]]
333 |
334 | elif self.page == '1337x':
335 | titles = [elem.get_text() for elem in self.elements[0]]
336 | seeders = [elem.get_text() for elem in self.elements[1]]
337 | leechers = [elem.get_text() for elem in self.elements[2]]
338 | ages = [elem.get_text() for elem in self.elements[3]]
339 | sizes = [elem.get_text('|').split('|')[0]
340 | for elem in self.elements[4]]
341 |
342 | # Torrent
343 | self.hrefs = [self.domain +
344 | elem.find(href=re.compile('torrent'))['href']
345 | for elem in self.elements[0]]
346 |
347 | elif self.page == 'eztv':
348 | titles = [elem.get_text() for elem in self.elements[0]]
349 | seeders = [elem.get_text() for elem in self.elements[4]]
350 | leechers = ['-' for elem in self.elements[4]]
351 | ages = [elem.get_text() for elem in self.elements[3]]
352 | sizes = [elem.get_text() for elem in self.elements[2]]
353 |
354 | # Magnets
355 | self.hrefs = [elem.find(href=re.compile('magnet'))['href']
356 | for elem in self.elements[1]]
357 |
358 | elif self.page == 'limetorrents':
359 | titles = [elem.get_text() for elem in self.elements[0]]
360 | seeders = [elem.get_text() for elem in self.elements[3]]
361 | leechers = [elem.get_text() for elem in self.elements[4]]
362 | ages = [elem.get_text() for elem in self.elements[1]]
363 | sizes = [elem.get_text() for elem in self.elements[2]]
364 |
365 | # Magnets
366 | self.hrefs = [elem.find('a', href=re.compile('torrent'))['href']
367 | for elem in self.elements[0]]
368 |
369 | elif self.page == 'isohunt':
370 | titles = [elem.get_text() for elem in self.elements[0]]
371 | seeders = [elem.get_text() for elem in self.elements[5]]
372 | leechers = ['-' for elem in self.elements[5]]
373 | ages = [elem.get_text() for elem in self.elements[3]]
374 | sizes = [elem.get_text() for elem in self.elements[4]]
375 |
376 | # Torrents
377 | self.hrefs = [self.domain +
378 | elem.find(href=re.compile('torrent_details'))['href']
379 | for elem in self.elements[0]]
380 | else:
381 | print('Error page')
382 |
383 | self.table = [[Colors.BOLD +
384 | UnicodeDammit(titles[i][:75].strip(), ["utf-8"]).unicode_markup +
385 | Colors.ENDC
386 | if (i + 1) % 2 == 0
387 | else UnicodeDammit(
388 | titles[i][:75].strip()).unicode_markup,
389 | Colors.SEEDER + seeders[i].strip() + Colors.ENDC
390 | if (i + 1) % 2 == 0
391 | else Colors.LGREEN + seeders[i].strip() + Colors.ENDC,
392 | Colors.LEECHER + leechers[i].strip() + Colors.ENDC
393 | if (i + 1) % 2 == 0
394 | else Colors.LRED + leechers[i].strip() + Colors.ENDC,
395 | Colors.LIGHTBLUE + ages[i].strip() + Colors.ENDC
396 | if (i + 1) % 2 == 0
397 | else Colors.BLUE + ages[i].strip() + Colors.ENDC,
398 | Colors.PINK + sizes[i].strip() + Colors.ENDC
399 | if (i + 1) % 2 == 0
400 | else Colors.PURPLE + sizes[i].strip() + Colors.ENDC]
401 | for i in range(len(self.hrefs))]
402 |
403 | print(tabulate(self.table,
404 | headers=headers,
405 | tablefmt='psql',
406 | numalign='right',
407 | stralign='left',
408 | showindex=True))
409 |
410 | def soupify(self):
411 | """Get proper torrent/magnet information.
412 |
413 | If search_mode is rated then get torrent/magnet.
414 | If not, get all the elements to build the table.
415 | There are different ways for each page.
416 | """
417 | soup = BeautifulSoup(self.content_page.content, 'lxml')
418 | if self.page == 'torrent_project':
419 | main = soup.find('div', {'id': 'similarfiles'})
420 | if self.mode_search == 'best_rated':
421 | rated_url = self.domain + \
422 | main.find(href=re.compile('torrent.html'))['href']
423 | self.get_magnet(rated_url)
424 | else:
425 | divs = main.find_all('div', limit=30)[2:]
426 | self.elements = list(
427 | zip(*[d.find_all('span', recursive=False)
428 | for d in divs])) # Torrents
429 |
430 | elif self.page == 'the_pirate_bay':
431 | main = soup.find('table', {'id': 'searchResult'})
432 | if self.mode_search == 'best_rated':
433 | rated_url = self.domain + \
434 | main.find('a', href=re.compile('torrent'))['href']
435 | self.get_magnet(rated_url)
436 | else:
437 | trs = main.find_all('tr', limit=30)[1:]
438 | self.elements = list(
439 | zip(*[tr.find_all('td', recursive=False)[1:]
440 | for tr in trs])) # Magnets
441 |
442 | elif self.page == '1337x':
443 | main = soup.find('table', {'class': 'table'})
444 | if self.mode_search == 'best_rated':
445 | rated_url = self.domain + \
446 | main.find('a', href=re.compile('torrent'))['href']
447 | self.get_magnet(rated_url)
448 | else:
449 | trs = main.find_all('tr', limit=30)[1:]
450 | self.elements = list(
451 | zip(*([tr.find_all('td', recursive=False)[:-1]
452 | for tr in trs]))) # Torrents
453 |
454 | elif self.page == 'eztv':
455 | main = soup.find_all('table', {'class': 'forum_header_border'})[2]
456 | if self.mode_search == 'best_rated':
457 | self.magnet = main.find('a', href=re.compile('magnet'))['href']
458 | else:
459 | trs = main.find_all('tr', limit=30)[2:]
460 | self.elements = list(
461 | zip(*([tr.find_all('td', recursive=False)[1:-1]
462 | for tr in trs]))) # Magnets
463 |
464 | elif self.page == 'limetorrents':
465 | main = soup.find('table', {'class': 'table2'})
466 | if self.mode_search == 'best_rated':
467 | self.magnet = main.find(
468 | 'a', href=re.compile('torrent'))['href']
469 | else:
470 | trs = main.find_all('tr', limit=30)[1:]
471 | self.elements = list(
472 | zip(*([tr.find_all('td', recursive=False)[:-1]
473 | for tr in trs]))) # Magnets
474 |
475 | elif self.page == 'isohunt':
476 | main = soup.find('table', {'class': 'table'})
477 | if self.mode_search == 'best_rated':
478 | rated_url = self.domain + \
479 | main.find('a', href=re.compile(
480 | 'torrent_details'))['href']
481 | self.get_magnet(rated_url)
482 | else:
483 | trs = main.find_all('tr', limit=30)[1:-1]
484 | self.elements = list(
485 | zip(*([tr.find_all('td', recursive=False)[1:-1]
486 | for tr in trs]))) # Torrent
487 | else:
488 | print('Cannot soupify current page. Try again.')
489 |
490 | def handle_select(self):
491 | """Handle user's input in list mode."""
492 | self.selected = input('>> ')
493 | if self.selected in ['Q', 'q']:
494 | sys.exit(1)
495 | elif self.selected in ['B', 'b']:
496 | self.back_to_menu = True
497 | return True
498 | elif is_num(self.selected):
499 | if 0 <= int(self.selected) <= len(self.hrefs) - 1:
500 | self.back_to_menu = False
501 | return True
502 | else:
503 | print(Colors.FAIL +
504 | 'Wrong index. ' +
505 | 'Please select an appropiate one or other option.' +
506 | Colors.ENDC)
507 | return False
508 | else:
509 | print(Colors.FAIL +
510 | 'Invalid input. ' +
511 | 'Please select an appropiate one or other option.' +
512 | Colors.ENDC)
513 | return False
514 |
515 | def select_torrent(self):
516 | """Select torrent.
517 |
518 | First check if specific element/info is obtained in content_page.
519 | Specify to user if it wants best rated torrent or select one from list.
520 | If the user wants best rated: Directly obtain magnet/torrent.
521 | Else: build table with all data and enable the user select the torrent.
522 | """
523 | try:
524 | self.found_torrents = not bool(self.key_search in
525 | self.content_page.text)
526 | if not self.found_torrents:
527 | print('No torrents found.')
528 | sys.exit(1)
529 | self.soupify()
530 | if self.mode_search == 'list':
531 | self.build_table()
532 | if len(self.hrefs) == 1:
533 | print('Press "0" to download it.')
534 | elif len(self.hrefs) >= 2:
535 | print('\nSelect one of the following torrents. ' +
536 | 'Enter a number between: 0 and ' +
537 | str(len(self.hrefs) - 1))
538 |
539 | print('If you want to exit write "' +
540 | Colors.LRED + 'Q' + Colors.ENDC + '" or "' +
541 | Colors.LRED + 'q' + Colors.ENDC + '".')
542 | print('If you want to go back to menu and search again write "' +
543 | Colors.LGREEN + 'B' + Colors.ENDC + '" or "' +
544 | Colors.LGREEN + 'b' + Colors.ENDC + '".')
545 | while not(self.picked_choice):
546 | self.picked_choice = self.handle_select()
547 | except Exception:
548 | print('ERROR select_torrent: ')
549 | logging.error(traceback.format_exc())
550 | sys.exit(0)
551 |
552 | def build_url(self):
553 | """Build appropiate encoded URL.
554 |
555 | This implies the same way of searching a torrent as in the page itself.
556 | """
557 | url = requests.utils.requote_uri(
558 | self.torrent_page + self.string_search)
559 | if self.page == '1337x':
560 | return(url + '/1/')
561 | elif self.page == 'limetorrents':
562 | return(url + '/')
563 | else:
564 | return(url)
565 |
566 | def get_content(self):
567 | """Get content of the page through url."""
568 | url = self.build_url()
569 | try:
570 | self.content_page = requests.get(url)
571 | if not(self.content_page.status_code == requests.codes.ok):
572 | self.content_page.raise_for_status()
573 | except requests.exceptions.RequestException as ex:
574 | logging.info('A requests exception has ocurred: ' + str(ex))
575 | logging.error(traceback.format_exc())
576 | sys.exit(0)
577 |
578 |
579 | def insert(args):
580 | """Insert args values into instance variables."""
581 | string_search = args.str_search
582 | mode_search = MODES[args.mode]
583 | page = list(TORRENTS[args.torr_page].keys())[0]
584 | key_search = TORRENTS[args.torr_page][page]['key_search']
585 | torrent_page = TORRENTS[args.torr_page][page]['page']
586 | domain = TORRENTS[args.torr_page][page]['domain']
587 | return([args, string_search, mode_search, page,
588 | key_search, torrent_page, domain])
589 |
590 |
591 | def initialize():
592 | """Initialize script."""
593 | print("Welcome to auto_py_torrent!\n")
594 |
595 |
596 | def run_it():
597 | """Search and download torrents until the user says it so."""
598 | initialize()
599 | parser = get_parser()
600 | args = None
601 | first_parse = True
602 | while(True):
603 | if first_parse is True:
604 | first_parse = False
605 | args = parser.parse_args()
606 | else:
607 | print(textwrap.dedent(
608 | '''\
609 | Search again like in the beginning.
610 | -- You can either choose best rated or list mode.
611 | -- This time, you can insert the search string without double quotes.
612 | Remember the list mode options!
613 | 0: torrent project.
614 | 1: the pirate bay.
615 | 2: 1337x.
616 | 3: eztv.
617 | 4: limetorrents.
618 | 5: isohunt.
619 | '''))
620 |
621 | print('Or.. if you want to exit just write "' +
622 | Colors.LRED + 'Q' + Colors.ENDC + '" or "' +
623 | Colors.LRED + 'q' + Colors.ENDC + '".')
624 | input_parse = input('>> ').replace("'", "").replace('"', '')
625 | if input_parse in ['Q', 'q']:
626 | sys.exit(1)
627 |
628 | args = parser.parse_args(input_parse.split(' ', 2))
629 |
630 | if args.str_search.strip() == "":
631 | print('Please insert an appropiate non-empty string.')
632 | else:
633 | auto = AutoPy(*insert(args))
634 | auto.get_content()
635 | auto.select_torrent()
636 | auto.download_torrent()
637 |
638 |
639 | def main():
640 | """Entry point for app script."""
641 | try:
642 | run_it()
643 | except KeyboardInterrupt:
644 | print('\nSee you the next time.')
645 | except Exception:
646 | logging.error(traceback.format_exc())
647 |
648 |
649 | if __name__ == '__main__':
650 | main()
651 |
--------------------------------------------------------------------------------
/docs/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests>=2.18.1
2 | beautifulsoup4>=4
3 | tabulate>=0.7.7
4 | coloredlogs>=7.1
5 | lxml
6 |
7 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import sys
5 |
6 |
7 | from codecs import open
8 |
9 | from setuptools import setup
10 |
11 |
12 | here = os.path.abspath(os.path.dirname(__file__))
13 |
14 | if sys.argv[-1] == 'publish':
15 | os.system('python3 setup.py sdist bdist_wheel')
16 | os.system('twine upload dist/*')
17 | sys.exit()
18 |
19 | packages = ['auto_py_torrent']
20 |
21 | requires = [
22 | 'requests>=2.18.1',
23 | 'beautifulsoup4>=4',
24 | 'tabulate>=0.7.7',
25 | 'coloredlogs>=7.1',
26 | 'lxml'
27 | ]
28 |
29 | about = {}
30 | with open(os.path.join(here, 'auto_py_torrent', '__version__.py'), 'r', 'utf-8') as f:
31 | exec(f.read(), about)
32 |
33 | with open('README.rst', 'r', 'utf-8') as f:
34 | readme = f.read()
35 | with open('HISTORY.rst', 'r', 'utf-8') as f:
36 | history = f.read()
37 |
38 |
39 | setup(
40 | name=about['__title__'],
41 | version=about['__version__'],
42 | description=about['__description__'],
43 | long_description=readme + '\n\n' + history,
44 | author=about['__author__'],
45 | author_email=about['__author_email__'],
46 | url=about['__url__'],
47 | packages=packages,
48 | python_requires='>=3',
49 | install_requires=requires,
50 | license=about['__license__'],
51 | classifiers=(
52 | 'Development Status :: 3 - Alpha',
53 | 'Environment :: Console',
54 | 'Intended Audience :: End Users/Desktop',
55 | 'Intended Audience :: Developers',
56 | 'License :: OSI Approved :: MIT License',
57 | 'Natural Language :: English',
58 | 'Programming Language :: Python :: 3',
59 | 'Programming Language :: Python :: 3.3',
60 | 'Programming Language :: Python :: 3.4',
61 | 'Programming Language :: Python :: 3.5',
62 | 'Programming Language :: Python :: 3.6',
63 | 'Topic :: Terminals',
64 | ),
65 | keywords='automate torrent torrent downloads',
66 | entry_points={
67 | 'console_scripts': ['auto_py_torrent=auto_py_torrent.auto_py_torrent:main'],
68 | }
69 | )
70 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocslegna/auto_py_torrent/5950c8791acebafc0126f3440796065c9e066dee/tests/__init__.py
--------------------------------------------------------------------------------