Overhaul tooling, linting, editor configs, and README

This commit is contained in:
rlaphoenix 2023-09-19 12:22:57 +01:00
parent c159672181
commit 959590a6bb
14 changed files with 1129 additions and 906 deletions

View File

@ -1,9 +1,5 @@
version = 1 version = 1
exclude_patterns = [
"**_pb2.py" # protobuf files
]
[[analyzers]] [[analyzers]]
name = "python" name = "python"
enabled = true enabled = true

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
root = true
[*]
end_of_line = lf
charset = utf-8
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.{feature,json,md,yaml,yml,toml}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false

View File

@ -1,3 +0,0 @@
[flake8]
exclude = .venv,build,dist,*_pb2.py,*.pyi
max-line-length = 120

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

View File

@ -15,32 +15,32 @@ jobs:
name: Tagged Release name: Tagged Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.10.x' python-version: "3.11"
- name: Install Poetry - name: Install Poetry
uses: abatilo/actions-poetry@v2.3.0 uses: abatilo/actions-poetry@v2
with: with:
poetry-version: '1.4.2' poetry-version: 1.6.1
- name: Install dependencies - name: Install project
run: poetry install run: poetry install --only main
- name: Build project - name: Build project
run: poetry build run: poetry build
- name: Upload wheel - name: Upload wheel
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: Python Wheel name: Python Wheel
path: "dist/*.whl" path: "dist/*.whl"
- name: Deploy release - name: Deploy release
uses: marvinpinto/action-automatic-releases@latest uses: marvinpinto/action-automatic-releases@latest
with: with:
prerelease: false prerelease: false
repo_token: "${{ secrets.GITHUB_TOKEN }}" repo_token: "${{ secrets.GITHUB_TOKEN }}"
files: | files: |
dist/*.whl dist/*.whl
- name: Publish to PyPI - name: Publish to PyPI
env: env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: poetry publish run: poetry publish

View File

@ -7,32 +7,38 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install poetry
uses: abatilo/actions-poetry@v2
with:
poetry-version: 1.6.1
- name: Install project
run: poetry install --all-extras
- name: Run pre-commit which does various checks
run: poetry run pre-commit run --all-files --show-diff-on-failure
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: ['3.9', '3.10', '3.11'] python-version: ["3.9", "3.10", "3.11"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install flake8 - name: Install poetry
run: python -m pip install flake8 uses: abatilo/actions-poetry@v2
- name: Lint with flake8 with:
run: | poetry-version: 1.6.1
# stop the build if there are Python syntax errors or undefined names - name: Install project
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics run: poetry install --all-extras --only main
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - name: Build project
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics run: poetry build
- name: Install poetry
uses: abatilo/actions-poetry@v2.3.0
with:
poetry-version: 1.4.2
- name: Install project
run: poetry install --no-dev
- name: Build project
run: poetry build

67
.gitignore vendored
View File

@ -36,6 +36,7 @@ parts/
sdist/ sdist/
var/ var/
wheels/ wheels/
share/python-wheels/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
@ -54,14 +55,17 @@ pip-delete-this-directory.txt
# Unit test / coverage reports # Unit test / coverage reports
htmlcov/ htmlcov/
.tox/ .tox/
.nox/
.coverage .coverage
.coverage.* .coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover *.cover
*.py,cover
.hypothesis/ .hypothesis/
.pytest_cache/ .pytest_cache/
cover/
# Translations # Translations
*.mo *.mo
@ -71,6 +75,7 @@ coverage.xml
*.log *.log
local_settings.py local_settings.py
db.sqlite3 db.sqlite3
db.sqlite3-journal
# Flask stuff: # Flask stuff:
instance/ instance/
@ -83,16 +88,49 @@ instance/
docs/_build/ docs/_build/
# PyBuilder # PyBuilder
.pybuilder/
target/ target/
# Jupyter Notebook # Jupyter Notebook
.ipynb_checkpoints .ipynb_checkpoints
# pyenv # IPython
.python-version profile_default/
ipython_config.py
# celery beat schedule file # pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule celerybeat-schedule
celerybeat.pid
# SageMath parsed files # SageMath parsed files
*.sage.py *.sage.py
@ -113,13 +151,26 @@ venv.bak/
# Rope project settings # Rope project settings
.ropeproject .ropeproject
# JetBrains project settings
.idea
# mkdocs documentation # mkdocs documentation
/site /site
# mypy # mypy
.mypy_cache/ .mypy_cache/
.directory .dmypy.json
.idea/dataSources.local.xml dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

View File

@ -2,17 +2,17 @@
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/mtkennerly/pre-commit-hooks
rev: v0.3.0
hooks:
- id: poetry-ruff
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
rev: 5.12.0 rev: 5.12.0
hooks: hooks:
- id: isort - id: isort
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v4.5.0
hooks: hooks:
- id: end-of-file-fixer
- id: trailing-whitespace - id: trailing-whitespace
args: [--markdown-linebreak-ext=md] args: [--markdown-linebreak-ext=md]
- id: end-of-file-fixer

13
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"recommendations": [
"EditorConfig.EditorConfig",
"streetsidesoftware.code-spell-checker",
"ms-python.python",
"ms-python.vscode-pylance",
"charliermarsh.ruff",
"ms-python.isort",
"ms-python.mypy-type-checker",
"redhat.vscode-yaml",
"tamasfe.even-better-toml"
]
}

49
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,49 @@
# Development
This project is managed using [Poetry](https://python-poetry.org), a fantastic Python packaging and dependency manager.
Install the latest version of Poetry before continuing. Development currently requires Python 3.9+.
## Set up
Starting from Zero? Not sure where to begin? Here's steps on setting up this Python project using Poetry. Note that
Poetry installation instructions should be followed from the Poetry Docs: https://python-poetry.org/docs/#installation
1. While optional, It's recommended to configure Poetry to install Virtual environments within project folders:
```shell
poetry config virtualenvs.in-project true
```
This makes it easier for Visual Studio Code to detect the Virtual Environment, as well as other IDEs and systems.
I've also had issues with Poetry creating duplicate Virtual environments in the default folder for an unknown
reason which quickly filled up my System storage.
2. Clone the Repository:
```shell
git clone https://github.com/devine-dl/devine
cd devine
```
3. Install the Project with Poetry:
```shell
poetry install
```
This creates a Virtual environment and then installs all project dependencies and executables into the Virtual
environment. Your System Python environment is not affected at all.
4. Now activate the Virtual environment:
```shell
poetry shell
```
Note:
- You can alternatively just prefix `poetry run` to any command you wish to run under the Virtual environment.
- I recommend entering the Virtual environment and all further instructions will have assumed you did.
- JetBrains PyCharm has integrated support for Poetry and automatically enters Poetry Virtual environments, assuming
the Python Interpreter on the bottom right is set up correctly.
- For more information, see: https://python-poetry.org/docs/basic-usage/#using-your-virtual-environment
5. Install Pre-commit tooling to ensure safe and quality commits:
```shell
pre-commit install
```
## Building Source and Wheel distributions
poetry build
You can optionally specify `-f` to build `sdist` or `wheel` only.
Built files can be found in the `/dist` directory.

View File

@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>. <https://www.gnu.org/licenses/why-not-lgpl.html>.

309
README.md
View File

@ -3,6 +3,10 @@
<a href="https://github.com/devine-dl/devine">Devine</a> <a href="https://github.com/devine-dl/devine">Devine</a>
<br/> <br/>
<sup><em>Open-Source Movie, TV, and Music Downloading Solution</em></sup> <sup><em>Open-Source Movie, TV, and Music Downloading Solution</em></sup>
<br/>
<a href="https://discord.gg/34K2MGDrBN">
<img src="https://img.shields.io/discord/841055398240059422?label=&logo=discord&logoColor=ffffff&color=7289DA&labelColor=7289DA" alt="Discord">
</a>
</p> </p>
<p align="center"> <p align="center">
@ -15,34 +19,43 @@
<a href="https://deepsource.io/gh/devine-dl/devine/?ref=repository-badge"> <a href="https://deepsource.io/gh/devine-dl/devine/?ref=repository-badge">
<img src="https://deepsource.io/gh/devine-dl/devine.svg/?label=active+issues&token=1ADCbjJ3FPiGT_s0Y0rlugGU" alt="DeepSource"> <img src="https://deepsource.io/gh/devine-dl/devine.svg/?label=active+issues&token=1ADCbjJ3FPiGT_s0Y0rlugGU" alt="DeepSource">
</a> </a>
<a href="https://discord.gg/34K2MGDrBN"> <br/>
<img src="https://img.shields.io/discord/841055398240059422?label=&logo=discord&logoColor=ffffff&color=7289DA&labelColor=7289DA" alt="Discord"> <a href="https://github.com/astral-sh/ruff">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Linter: Ruff">
</a>
<a href="https://python-poetry.org">
<img src="https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json" alt="Dependency management: Poetry">
</a> </a>
</p> </p>
## Features ## Features
- 🎥 Supports Movies, TV shows, and Music - 🚀 Seamless Installation via [pip](#installation)
- 🧩 Easy installation via PIP/PyPI - 🎥 Movie, Episode, and Song Service Frameworks
- 👥 Multi-profile authentication per-service with credentials or cookies - 🛠️ Built-in [DASH] and [HLS] Parsers
- 🔒 Widevine DRM integration via [pywidevine](https://github.com/devine-dl/pywidevine)
- 💾 Local & Remote DRM Key-vaults
- 🌍 Local & Remote Widevine CDMs
- 👥 Multi-profile Authentication per-service with Credentials and/or Cookies
- 🤖 Automatic P2P filename structure with Group Tag - 🤖 Automatic P2P filename structure with Group Tag
- 🛠️ Flexible Service framework system
- 📦 Portable Installations
- 🗃️ Local and Remote SQL-based Key Vault database
- ⚙️ YAML for Configuration - ⚙️ YAML for Configuration
- 🌍 Local and Remote Widevine CDMs
- ❤️ Fully Open-Source! Pull Requests Welcome - ❤️ Fully Open-Source! Pull Requests Welcome
[DASH]: <devine/core/manifests/dash.py>
[HLS]: <devine/core/manifests/hls.py>
## Installation ## Installation
```shell ```shell
$ pip install devine $ pip install devine
``` ```
> __Note__ If you see warnings about a path not being in your PATH environment variable, add it, or `devine` won't run. > [!NOTE]
> If pip gives you a warning about a path not being in your PATH environment variable then promptly add that path then
> close all open command prompt/terminal windows, or `devine` won't work as it will not be found.
Voilà 🎉! You now have the `devine` package installed and a `devine` executable is now available. Voilà 🎉 — You now have the `devine` package installed!
Check it out with `devine --help`! A command-line interface is now available, try `devine --help`.
### Dependencies ### Dependencies
@ -68,77 +81,165 @@ able to be found.
[MKVToolNix]: <https://mkvtoolnix.download/downloads.html> [MKVToolNix]: <https://mkvtoolnix.download/downloads.html>
[shaka-packager]: <https://github.com/google/shaka-packager/releases/latest> [shaka-packager]: <https://github.com/google/shaka-packager/releases/latest>
### Services ## Usage
Devine does not come with any infringing Service code. You must develop your own Service code and place them in First, take a look at `devine --help` for a full help document, listing all commands available and giving you more
the `/devine/services` directory. There are different ways the add services depending on your installation type. information on what can be done with Devine.
In some cases you may use multiple of these methods to have separate copies.
Please refrain from making or using Service code unless you have full rights to do so. I also recommend ensuring that Here's a checklist on what I recommend getting started with, in no particular order,
you keep the Service code private and secure, i.e. a private repository or keeping it offline.
No matter which method you use, make sure that you install any further dependencies needed by the services. There's - [ ] Add [Services](#services), these will be used in `devine dl`.
currently no way to have these dependencies automatically install apart from within the Fork method. - [ ] Add [Profiles](#profiles-cookies--credentials), these are your cookies and credentials.
- [ ] Add [Widevine Provisions](#widevine-provisions), also known as CDMs, these are used for DRM-protected content.
- [ ] Set your Group Tag, the text at the end of the final filename, e.g., `devine cfg tag NOGRP` for `...-NOGRP`.
- [ ] Set Up a Local Key Vault, take a look at the [Key Vaults Config](CONFIG.md#keyvaults-listdict).
> __Warning__ Please be careful with who you trust and what you run. The users you collaborate with on Service And here's some more advanced things you could take a look at,
- [ ] Setting default Headers that the Request Session uses.
- [ ] Setting default Profiles and CDM Provisions to use for services.
- [ ] NordVPN and Hola Proxy Providers for automatic proxies.
- [ ] Hosting and/or Using Remote Key Vaults.
- [ ] Serving and/or Using Remote CDM Provisions.
Documentation on the config is available in the [CONFIG.md](CONFIG.md) file, it has a lot of handy settings.
If you start to get sick of putting something in your CLI call, then I recommend taking a look at it!
## Services
Unlike similar project's such as [youtube-dl], Devine does not currently come with any Services. You must develop your
own Services and only use Devine with Services you have the legal right to do so.
> [!NOTE]
> If you made a Service for Devine that does not use Widevine or any other DRM systems, feel free to make a Pull Request
> and make your service available to others. Any Service on [youtube-dl] (or [yt-dlp]) would be able to be added to the
> Devine repository as they both use the [Unlicense license] therefore direct reading and porting of their code would be
> legal.
[youtube-dl]: <https://github.com/ytdl-org/youtube-dl>
[yt-dlp]: <https://github.com/yt-dlp/yt-dlp>
[Unlicense license]: <https://choosealicense.com/licenses/unlicense>
### Creating a Service
> [!WARNING]
> Only create or use Service Code with Services you have full legal right to do so.
A Service consists of a folder with an `__init__.py` file. The file must contain a class of the same name as the folder.
The class must inherit the [Service] class and implement all the abstracted methods. It must finally implement a new
method named `cli` where you define CLI arguments.
1. Make a new folder within `/devine/services`. The folder name you choose will be what's known as the [Service Tag].
This "tag" is used in the final output filename of downloaded files, for various code-checks, lookup keys in
key-vault databases, and more.
2. Within the new folder create an `__init__.py` file and write a class inheriting the [Service] class. It must be named
the exact same as the folder. It is case-sensitive.
3. Implement all the methods of the Service class you are inheriting that are marked as abstract.
4. Define CLI arguments by implementing a `cli` method. This method must be static (i.e. `@staticmethod`). For example
to implement the bare minimum to receive a Title ID of sorts:
```python
@staticmethod
@click.command(name="YT", short_help="https://youtube.com", help=__doc__)
@click.argument("title", type=str)
@click.pass_context
def cli(ctx, **kwargs):
return YT(ctx, **kwargs)
```
You must implement this `cli` method, even if you do not want or need any CLI arguments. It is required for the core
CLI functionality to be able to find and call the class.
5. Accept the CLI arguments by overriding the constructor (the `__init__()` method):
```python
def __init__(self, ctx, title):
self.title = title
super().__init__(ctx) # important
# ... the title is now available across all methods by calling self.title
```
> [!NOTE]
> - All methods of your class inherited from `Service` marked as abstract (`@abstractmethod`) MUST be implemented by
> your class.
> - When overriding any method (e.g., `__init__()` method) you MUST super call it, e.g., `super().__init__()` at the
> top of the override. This does not apply to any abstract methods, as they are unimplemented.
> - If preparing your Requests Session with global headers or such, then you should override the `get_session` method,
> then modify `self.session`. Do not manually make `self.session` from scratch.
> [!TIP]
> 1. To make web requests use the `self.session` class instance variable, e.g. `self.session.get(url)`.
> 2. If you make a `config.yaml` file next to your `__init__.py`, you can access it with `self.config`.
> 3. You can include any arbitrary file within your Service folder for use by your Service. For example TLS certificate
> files, or other python files with helper functions and classes.
[Service]: <devine/core/service.py>
[Service Tag]: <#service-tags>
### Service Tags
Service tags generally follow these rules:
- Tag must be between 2-4 characters long, consisting of just `[A-Z0-9i]{2,4}`.
- Lower-case `i` is only used for select services. Specifically BBC iPlayer and iTunes.
- If the Service's commercial name has a `+` or `Plus`, the last character should be a `P`.
E.g., `ATVP` for `Apple TV+`, `DSCP` for `Discovery+`, `DSNP` for `Disney+`, and `PMTP` for `Paramount+`.
These rules are not exhaustive and should only be used as a guide. You don't strictly have to follow these rules, but
I recommend doing so for consistency.
### Sharing Services
Sending and receiving zipped Service folders is quite cumbersome. Let's explore alternative routes to collaborating on
Service Code.
> [!WARNING]
> Please be careful with who you trust and what you run. The users you collaborate with on Service
> code could update it with malicious code that you would run via devine on the next call. > code could update it with malicious code that you would run via devine on the next call.
#### via Copy & Paste #### Forking
If you have service code already and wish to just install and use it locally, then simply putting it into the Services If you are collaborating with a team on multiple services then forking the project is the best way to go.
directory of your local pip installation will do the job. However, this method is the worst in terms of collaboration.
1. Get the installation directory by running the following in terminal, 1. [Fork the project](https://github.com/devine-dl/devine/fork) and make sure it is Private.
`python -c 'import os,devine.__main__ as a;print(os.path.dirname(a.__file__))'` 2. (optionally) Hard reset to the latest stable version by tag. E.g., `git reset --hard v1.0.0`.
2. Head to the installation directory and create a `services` folder if one is not yet created. 3. Add all your Services to the `/devine/services` folder and commit them to your fork.
3. Within that `services` folder you may install or create service code.
> __Warning__ Uninstalling Python or Devine may result in the Services you installed being deleted. Make sure you back Now commit changes or additions within that services folder to your forked repository.
> up the services before uninstalling. Once committed all your other team members can easily pull changes as well as push new changes.
#### via a Forked Repository When a new update comes out you can easily rebase your fork to that commit to update. However, please make sure you
look at changes between each version before rebasing and resolve any breaking changes and deprecations when rebasing to
a new version.
If you are collaborating with a team on multiple services then forking the project is the best way to go. I recommend If you are new to `git` then take a look at [GitHub Desktop](https://desktop.github.com).
forking the project then hard resetting to the latest stable update by tag. Once a new stable update comes out you can
easily rebase your fork to that commit to update.
However, please make sure you look at changes between each version before rebasing and resolve any breaking changes and > [!TIP]
deprecations when rebasing to a new version. > A huge benefit with this method is that you can also sync dependencies by your own Services as well!
> Just use `poetry` to add or modify dependencies appropriately and commit the changed `poetry.lock`.
> However, if the core project also has dependency changes your `poetry.lock` changes will conflict and you
> will need to learn how to do conflict resolution/rebasing. It is worth it though!
1. Fork the project with `git` or GitHub [(fork)](https://github.com/devine-dl/devine/fork). #### Symlinking
2. Head inside the root `devine` directory and create a `services` directory.
3. Within that `services` folder you may install or create service code.
You may now commit changes or additions within that services folder to your forked repository. This is a great option for those who wish to do something like the forking method, but may not care what changes
Once committed all your other team members can easily sync and contribute changes. happened or when and just want changes synced across a team.
> __Note__ You may add Service-specific Python dependencies using `poetry` that can install alongside the project.
> Just do note that this will complicate rebasing when even the `poetry.lock` gets updates in the upstream project.
#### via Cloud storage (symlink)
This is a great option for those who wish to do something like the forking method, but without the need of constantly
rebasing their fork to the latest version. Overall less knowledge on git would be required, but each user would need
to do a bit of symlinking compared to the fork method.
This also opens up the ways you can host or collaborate on Service code. As long as you can receive a directory that This also opens up the ways you can host or collaborate on Service code. As long as you can receive a directory that
updates with just the services within it, then you're good to go. Options could include an FTP server, Shared Google updates with just the services within it, then you're good to go. Options could include an FTP server, Shared Google
Drive, a non-fork repository with just services, and more. Drive, a non-fork repository with just services, and more.
1. Follow the steps in the [Copy & Paste method](#via-copy--paste) to create the `services` folder. 1. Use any Cloud Source that gives you a pseudo-directory to access the Service files like a normal drive. E.g., rclone,
2. Use any Cloud Source that gives you a pseudo-directory to access the Service files. E.g., rclone or google drive fs. Google Drive Desktop (aka File Stream), Air Drive, CloudPool, etc.
3. Symlink the services directory from your Cloud Source to the new services folder you made. 2. Create a `services` directory somewhere in it and have all your services within it.
(you may need to delete it first) 3. [Symlink](https://en.wikipedia.org/wiki/Symbolic_link) the `services` directory to the `/devine` folder. You should
end up with `/devine/services` folder containing services, not `/devine/services/services`.
Of course, you have to make sure the original folder keeps receiving and downloading/streaming those changes, or that You have to make sure the original folder keeps receiving and downloading/streaming those changes. You must also make
you keep git pulling those changes. You must also make sure that the version of devine you have locally is supported by sure that the version of devine you have locally is supported by the Service code.
the Services code.
> __Note__ If you're using a cloud source that downloads the file once it gets opened, you don't have to worry as those > [!NOTE]
> will automatically download. Python importing the files triggers the download to begin. However, it may cause a delay > If you're using a cloud source that downloads the file once it gets opened, you don't have to worry as those will
> on startup. > automatically download. Python importing the files triggers the download to begin. However, it may cause a delay on
> startup.
### Profiles (Cookies & Credentials) ## Profiles (Cookies & Credentials)
Just like a streaming service, devine associates both a cookie and/or credential as a Profile. You can associate up to Just like a streaming service, devine associates both a cookie and/or credential as a Profile. You can associate up to
one cookie and one credential per-profile, depending on which (or both) are needed by the Service. This system allows one cookie and one credential per-profile, depending on which (or both) are needed by the Service. This system allows
@ -155,7 +256,7 @@ You can also delete a credential with `devine auth delete`. E.g., to delete the
> __Note__ Profile names are case-sensitive and unique per-service. They also have no arbitrary character or length > __Note__ Profile names are case-sensitive and unique per-service. They also have no arbitrary character or length
> limit, but for convenience I don't recommend using any special characters as your terminal may get confused. > limit, but for convenience I don't recommend using any special characters as your terminal may get confused.
#### Cookie file format and Extensions ### Cookie file format and Extensions
Cookies must be in the standard Netscape cookies file format. Cookies must be in the standard Netscape cookies file format.
Recommended Cookie exporter extensions: Recommended Cookie exporter extensions:
@ -172,7 +273,7 @@ Any other extension that exports to the standard Netscape format should theoreti
> versions floating around (usually just older versions of the extension), but since there are safe alternatives I'd > versions floating around (usually just older versions of the extension), but since there are safe alternatives I'd
> just avoid it altogether. Source: https://reddit.com/r/youtubedl/comments/10ar7o7 > just avoid it altogether. Source: https://reddit.com/r/youtubedl/comments/10ar7o7
### Widevine Provisions ## Widevine Provisions
A Widevine Provision is needed for acquiring licenses containing decryption keys for DRM-protected content. A Widevine Provision is needed for acquiring licenses containing decryption keys for DRM-protected content.
They are not needed if you will be using devine on DRM-free services. Please do not ask for any Widevine Device Files, They are not needed if you will be using devine on DRM-free services. Please do not ask for any Widevine Device Files,
@ -188,50 +289,9 @@ From here you can then set which WVD to use for each specific service. It's best
provision where possible. provision where possible.
An alternative would be using a pywidevine Serve-compliant CDM API. Of course, you would need to know someone who is An alternative would be using a pywidevine Serve-compliant CDM API. Of course, you would need to know someone who is
serving one, and they would need to give you access. Take a look at the [remote_cdm](CONFIG.md#remotecdm--listdict--) serving one, and they would need to give you access. Take a look at the [remote_cdm](CONFIG.md#remotecdm-listdict)
config option for setup information. For further information on it see the pywidevine repository. config option for setup information. For further information on it see the pywidevine repository.
## Usage
First, take a look at `devine --help` for a full help document, listing all commands available and giving you more
information on what can be done with Devine.
Here's a checklist on what I recommend getting started with, in no particular order,
- [ ] Add [Services](#services), these will be used in `devine dl`.
- [ ] Add [Profiles](#profiles--cookies--credentials-), these are your cookies and credentials.
- [ ] Add [Widevine Provisions](#widevine-provisions), also known as CDMs, these are used for DRM-protected content.
- [ ] Set your Group Tag, the text at the end of the final filename, e.g., `devine cfg tag NOGRP` for ...-NOGRP.
- [ ] Set Up a Local Key Vault, take a look at the [Key Vaults Config](CONFIG.md#keyvaults--listdict--).
And here's some more advanced things you could take a look at,
- [ ] Setting default Headers that the Request Session uses.
- [ ] Setting default Profiles and CDM Provisions to use for services.
- [ ] NordVPN and Hola Proxy Providers for automatic proxies.
- [ ] Hosting and/or Using Remote Key Vaults.
- [ ] Serving and/or Using Remote CDM Provisions.
Documentation on the config is available in the [CONFIG.md](CONFIG.md) file, it has a lot of handy settings.
If you start to get sick of putting something in your CLI call, then I recommend taking a look at it!
## Development
The following steps are instructions on downloading, preparing, and running the code under a [Poetry] environment.
You can skip steps 3-5 with a simple `pip install .` call instead, but you miss out on a wide array of benefits.
1. `git clone https://github.com/devine-dl/devine`
2. `cd devine`
3. (optional) `poetry config virtualenvs.in-project true`
4. `poetry install`
5. `poetry run devine --help`
As seen in Step 5, running the `devine` executable is somewhat different to a normal PIP installation.
See [Poetry's Docs] on various ways of making calls under the virtual-environment.
[Poetry]: <https://python-poetry.org>
[Poetry's Docs]: <https://python-poetry.org/docs/basic-usage/#using-your-virtual-environment>
## End User License Agreement ## End User License Agreement
Devine and it's community pages should be treated with the same kindness as other projects. Devine and it's community pages should be treated with the same kindness as other projects.
@ -246,31 +306,22 @@ Please refrain from spam or asking for questions that infringe upon a Service's
back immediately. back immediately.
5. Be kind to one another and do not single anyone out. 5. Be kind to one another and do not single anyone out.
## Disclaimer
1. This project requires a valid Google-provisioned Private/Public Keypair and a Device-specific Client Identification
blob; neither of which are included with this project.
2. Public testing provisions are available and provided by Google to use for testing projects such as this one.
3. License Servers have the ability to block requests from any provision, and are likely already blocking test provisions
on production endpoints. Therefore, have the ability to block the usage of Devine by themselves.
4. This project does not condone piracy or any action against the terms of the Service or DRM system.
5. All efforts in this project have been the result of Reverse-Engineering and Publicly available research.
## Credit
- The awesome community for their shared research and insight into the Widevine Protocol and Key Derivation.
## Contributors ## Contributors
<a href="https://github.com/rlaphoenix"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/17136956?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/rlaphoenix"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/17136956?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="rlaphoenix"/></a>
<a href="https://github.com/mnmll"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/22942379?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/mnmll"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/22942379?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="mnmll"/></a>
<a href="https://github.com/shirt-dev"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/2660574?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/shirt-dev"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/2660574?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="shirt-dev"/></a>
<a href="https://github.com/nyuszika7h"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/482367?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/nyuszika7h"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/482367?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="nyuszika7h"/></a>
<a href="https://github.com/bccornfo"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/98013276?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/bccornfo"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/98013276?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="bccornfo"/></a>
<a href="https://github.com/Arias800"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/24809312?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/Arias800"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/24809312?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Arias800"/></a>
<a href="https://github.com/varyg1001"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/88599103?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/varyg1001"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/88599103?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="varyg1001"/></a>
<a href="https://github.com/Hollander-1908"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/93162595?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt=""/></a> <a href="https://github.com/Hollander-1908"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/93162595?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Hollander-1908"/></a>
## License ## Licensing
© 2019-2023 rlaphoenix — [GNU General Public License, Version 3.0](LICENSE) This software is licensed under the terms of [GNU General Public License, Version 3.0](LICENSE).
You can find a copy of the license in the LICENSE file in the root folder.
* * *
© rlaphoenix 2019-2023

1363
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,76 +1,91 @@
[build-system] [build-system]
requires = ['poetry-core>=1.0.0'] requires = ["poetry-core>=1.0.0"]
build-backend = 'poetry.core.masonry.api' build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = 'devine' name = "devine"
version = '2.2.0' version = "2.2.0"
description = 'Open-Source Movie, TV, and Music Downloading Solution' description = "Open-Source Movie, TV, and Music Downloading Solution."
license = 'GPL-3.0-only' license = "GPL-3.0-only"
authors = ['rlaphoenix <rlaphoenix@pm.me>'] authors = ["rlaphoenix <rlaphoenix@pm.me>"]
readme = 'README.md' readme = "README.md"
homepage = 'https://github.com/devine-dl/devine' homepage = "https://github.com/devine-dl/devine"
repository = 'https://github.com/devine-dl/devine' repository = "https://github.com/devine-dl/devine"
keywords = ['widevine', 'drm', 'downloader'] keywords = ["python", "downloader", "drm", "widevine"]
classifiers = [ classifiers = [
'Development Status :: 4 - Beta', "Development Status :: 4 - Beta",
'Environment :: Console', "Environment :: Console",
'Intended Audience :: End Users/Desktop', "Intended Audience :: End Users/Desktop",
'Natural Language :: English', "Natural Language :: English",
'Operating System :: OS Independent', "Operating System :: OS Independent",
'Topic :: Multimedia :: Video', "Topic :: Multimedia :: Video",
'Topic :: Security :: Cryptography', "Topic :: Security :: Cryptography",
] ]
include = [
{ path = "CHANGELOG.md", format = "sdist" },
{ path = "README.md", format = "sdist" },
{ path = "LICENSE", format = "sdist" },
]
[tool.poetry.urls]
"Issues" = "https://github.com/devine-dl/devine/issues"
"Discussions" = "https://github.com/devine-dl/devine/discussions"
"Changelog" = "https://github.com/devine-dl/devine/blob/master/CHANGELOG.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = ">=3.9.0,<3.12" python = ">=3.9.0,<3.12"
appdirs = "^1.4.4" appdirs = "^1.4.4"
Brotli = "^1.0.9" Brotli = "^1.1.0"
click = "^8.1.4" click = "^8.1.7"
construct = "^2.8.8" construct = "^2.8.8"
crccheck = "^1.3.0" crccheck = "^1.3.0"
jsonpickle = "^3.0.1" jsonpickle = "^3.0.2"
langcodes = { extras = ["data"], version = "^3.3.0" } langcodes = { extras = ["data"], version = "^3.3.0" }
lxml = "^4.9.3" lxml = "^4.9.3"
pproxy = "^2.7.8" pproxy = "^2.7.8"
protobuf = "4.21.6" protobuf = "4.21.6"
pycaption = "^2.1.1" pycaption = "^2.1.1"
pycryptodomex = "^3.18.0" pycryptodomex = "^3.19.0"
pyjwt = "^2.7.0" pyjwt = "^2.8.0"
pymediainfo = "^6.0.1" pymediainfo = "^6.0.1"
pymp4 = "^1.4.0" pymp4 = "^1.4.0"
pymysql = "^1.1.0" pymysql = "^1.1.0"
pywidevine = { extras = ["serve"], version = "^1.6.0" } pywidevine = { extras = ["serve"], version = "^1.6.0" }
PyYAML = "^6.0" PyYAML = "^6.0.1"
requests = { extras = ["socks"], version = "^2.31.0" } requests = { extras = ["socks"], version = "^2.31.0" }
rich = "^13.4.2" rich = "^13.5.3"
"rlaphoenix.m3u8" = "^3.4.0" "rlaphoenix.m3u8" = "^3.4.0"
"ruamel.yaml" = "^0.17.32" "ruamel.yaml" = "^0.17.32"
sortedcontainers = "^2.4.0" sortedcontainers = "^2.4.0"
subtitle-filter = "^1.4.6" subtitle-filter = "^1.4.6"
Unidecode = "^1.3.6" Unidecode = "^1.3.6"
urllib3 = "^2.0.3" urllib3 = "^2.0.4"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pre-commit = "^3.3.3" pre-commit = "^3.4.0"
mypy = "^1.4.1" mypy = "^1.5.1"
mypy-protobuf = "^3.3.0" mypy-protobuf = "^3.3.0"
types-protobuf = "^3.19.22" types-protobuf = "^4.24.0.1"
types-PyMySQL = "^1.1.0.0" types-PyMySQL = "^1.1.0.1"
types-requests = "^2.31.0.1" types-requests = "^2.31.0.2"
isort = "^5.12.0" isort = "^5.12.0"
ruff = "^0.0.292"
[tool.poetry.scripts] [tool.poetry.scripts]
devine = 'devine.core.__main__:main' devine = "devine.core.__main__:main"
[tool.ruff]
force-exclude = true
line-length = 120
select = ["E4", "E7", "E9", "F", "W"]
[tool.isort] [tool.isort]
line_length = 120 line_length = 118
[tool.mypy] [tool.mypy]
exclude = '_pb2\.pyi?$'
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true
disallow_untyped_defs = true disallow_untyped_defs = true
follow_imports = 'silent' follow_imports = "silent"
ignore_missing_imports = true ignore_missing_imports = true
no_implicit_optional = true no_implicit_optional = true