AkshitShubham commited on
Commit
4712303
1 Parent(s): 7741f2a

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,2 +1,2 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
 
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
.gitignore CHANGED
@@ -1,191 +1,191 @@
1
- # Created by https://www.toptal.com/developers/gitignore/api/python
2
- # Edit at https://www.toptal.com/developers/gitignore?templates=python
3
-
4
- ### Python ###
5
- # Byte-compiled / optimized / DLL files
6
- __pycache__/
7
- *.py[cod]
8
- *$py.class
9
-
10
- # IDE
11
- .idea/
12
-
13
- # Download Files
14
- webdl/
15
-
16
- # CSV Files
17
- *.csv
18
-
19
- # C extensions
20
- *.so
21
-
22
- # Distribution / packaging
23
- .Python
24
- build/
25
- develop-eggs/
26
- dist/
27
- downloads/
28
- eggs/
29
- .eggs/
30
- lib/
31
- lib64/
32
- parts/
33
- sdist/
34
- var/
35
- wheels/
36
- share/python-wheels/
37
- *.egg-info/
38
- .installed.cfg
39
- *.egg
40
- MANIFEST
41
-
42
- # PyInstaller
43
- # Usually these files are written by a python script from a template
44
- # before PyInstaller builds the exe, so as to inject date/other infos into it.
45
- *.manifest
46
- *.spec
47
-
48
- # Installer logs
49
- pip-log.txt
50
- pip-delete-this-directory.txt
51
-
52
- # Unit test / coverage reports
53
- htmlcov/
54
- .tox/
55
- .nox/
56
- .coverage
57
- .coverage.*
58
- .cache
59
- nosetests.xml
60
- coverage.xml
61
- *.cover
62
- *.py,cover
63
- .hypothesis/
64
- .pytest_cache/
65
- cover/
66
-
67
- # Translations
68
- *.mo
69
- *.pot
70
-
71
- # Django stuff:
72
- *.log
73
- local_settings.py
74
- db.sqlite3
75
- db.sqlite3-journal
76
-
77
- # Flask stuff:
78
- instance/
79
- .webassets-cache
80
-
81
- # Scrapy stuff:
82
- .scrapy
83
-
84
- # Sphinx documentation
85
- docs/_build/
86
-
87
- # PyBuilder
88
- .pybuilder/
89
- target/
90
-
91
- # Jupyter Notebook
92
- .ipynb_checkpoints
93
-
94
- # IPython
95
- profile_default/
96
- ipython_config.py
97
-
98
- # pyenv
99
- # For a library or package, you might want to ignore these files since the code is
100
- # intended to run in multiple environments; otherwise, check them in:
101
- # .python-version
102
-
103
- # pipenv
104
- # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
105
- # However, in case of collaboration, if having platform-specific dependencies or dependencies
106
- # having no cross-platform support, pipenv may install dependencies that don't work, or not
107
- # install all needed dependencies.
108
- #Pipfile.lock
109
-
110
- # poetry
111
- # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
112
- # This is especially recommended for binary packages to ensure reproducibility, and is more
113
- # commonly ignored for libraries.
114
- # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
115
- #poetry.lock
116
-
117
- # pdm
118
- # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
119
- #pdm.lock
120
- # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
121
- # in version control.
122
- # https://pdm.fming.dev/#use-with-ide
123
- .pdm.toml
124
-
125
- # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
126
- __pypackages__/
127
-
128
- # Celery stuff
129
- celerybeat-schedule
130
- celerybeat.pid
131
-
132
- # SageMath parsed files
133
- *.sage.py
134
-
135
- # Environments
136
- .env
137
- .venv
138
- env/
139
- venv/
140
- ENV/
141
- env.bak/
142
- venv.bak/
143
-
144
- # Spyder project settings
145
- .spyderproject
146
- .spyproject
147
-
148
- # Rope project settings
149
- .ropeproject
150
-
151
- # mkdocs documentation
152
- /site
153
-
154
- # mypy
155
- .mypy_cache/
156
- .dmypy.json
157
- dmypy.json
158
-
159
- # Pyre type checker
160
- .pyre/
161
-
162
- # pytype static type analyzer
163
- .pytype/
164
-
165
- # Cython debug symbols
166
- cython_debug/
167
-
168
- # PyCharm
169
- # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
170
- # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
171
- # and can be added to the global gitignore or merged into this file. For a more nuclear
172
- # option (not recommended) you can uncomment the following to ignore the entire idea folder.
173
- #.idea/
174
-
175
- ### Python Patch ###
176
- # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
177
- poetry.toml
178
-
179
- # ruff
180
- .ruff_cache/
181
-
182
- # LSP config files
183
- pyrightconfig.json
184
-
185
- # End of https://www.toptal.com/developers/gitignore/api/python
186
- pwdlv3.lnk
187
- *.mp4
188
- /bin/Logs/
189
- *.m4s
190
- /tmp
191
  /bin
 
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python
3
+
4
+ ### Python ###
5
+ # Byte-compiled / optimized / DLL files
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+
10
+ # IDE
11
+ .idea/
12
+
13
+ # Download Files
14
+ webdl/
15
+
16
+ # CSV Files
17
+ *.csv
18
+
19
+ # C extensions
20
+ *.so
21
+
22
+ # Distribution / packaging
23
+ .Python
24
+ build/
25
+ develop-eggs/
26
+ dist/
27
+ downloads/
28
+ eggs/
29
+ .eggs/
30
+ lib/
31
+ lib64/
32
+ parts/
33
+ sdist/
34
+ var/
35
+ wheels/
36
+ share/python-wheels/
37
+ *.egg-info/
38
+ .installed.cfg
39
+ *.egg
40
+ MANIFEST
41
+
42
+ # PyInstaller
43
+ # Usually these files are written by a python script from a template
44
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
45
+ *.manifest
46
+ *.spec
47
+
48
+ # Installer logs
49
+ pip-log.txt
50
+ pip-delete-this-directory.txt
51
+
52
+ # Unit test / coverage reports
53
+ htmlcov/
54
+ .tox/
55
+ .nox/
56
+ .coverage
57
+ .coverage.*
58
+ .cache
59
+ nosetests.xml
60
+ coverage.xml
61
+ *.cover
62
+ *.py,cover
63
+ .hypothesis/
64
+ .pytest_cache/
65
+ cover/
66
+
67
+ # Translations
68
+ *.mo
69
+ *.pot
70
+
71
+ # Django stuff:
72
+ *.log
73
+ local_settings.py
74
+ db.sqlite3
75
+ db.sqlite3-journal
76
+
77
+ # Flask stuff:
78
+ instance/
79
+ .webassets-cache
80
+
81
+ # Scrapy stuff:
82
+ .scrapy
83
+
84
+ # Sphinx documentation
85
+ docs/_build/
86
+
87
+ # PyBuilder
88
+ .pybuilder/
89
+ target/
90
+
91
+ # Jupyter Notebook
92
+ .ipynb_checkpoints
93
+
94
+ # IPython
95
+ profile_default/
96
+ ipython_config.py
97
+
98
+ # pyenv
99
+ # For a library or package, you might want to ignore these files since the code is
100
+ # intended to run in multiple environments; otherwise, check them in:
101
+ # .python-version
102
+
103
+ # pipenv
104
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
105
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
106
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
107
+ # install all needed dependencies.
108
+ #Pipfile.lock
109
+
110
+ # poetry
111
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
112
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
113
+ # commonly ignored for libraries.
114
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
115
+ #poetry.lock
116
+
117
+ # pdm
118
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
119
+ #pdm.lock
120
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
121
+ # in version control.
122
+ # https://pdm.fming.dev/#use-with-ide
123
+ .pdm.toml
124
+
125
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
126
+ __pypackages__/
127
+
128
+ # Celery stuff
129
+ celerybeat-schedule
130
+ celerybeat.pid
131
+
132
+ # SageMath parsed files
133
+ *.sage.py
134
+
135
+ # Environments
136
+ .env
137
+ .venv
138
+ env/
139
+ venv/
140
+ ENV/
141
+ env.bak/
142
+ venv.bak/
143
+
144
+ # Spyder project settings
145
+ .spyderproject
146
+ .spyproject
147
+
148
+ # Rope project settings
149
+ .ropeproject
150
+
151
+ # mkdocs documentation
152
+ /site
153
+
154
+ # mypy
155
+ .mypy_cache/
156
+ .dmypy.json
157
+ dmypy.json
158
+
159
+ # Pyre type checker
160
+ .pyre/
161
+
162
+ # pytype static type analyzer
163
+ .pytype/
164
+
165
+ # Cython debug symbols
166
+ cython_debug/
167
+
168
+ # PyCharm
169
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
170
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
171
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
172
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
173
+ #.idea/
174
+
175
+ ### Python Patch ###
176
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
177
+ poetry.toml
178
+
179
+ # ruff
180
+ .ruff_cache/
181
+
182
+ # LSP config files
183
+ pyrightconfig.json
184
+
185
+ # End of https://www.toptal.com/developers/gitignore/api/python
186
+ pwdlv3.lnk
187
+ *.mp4
188
+ /bin/Logs/
189
+ *.m4s
190
+ /tmp
191
  /bin
Dockerfile CHANGED
@@ -1,35 +1,35 @@
1
- # Use an official Python runtime as a parent image
2
- FROM python:3.12-slim
3
-
4
- # Set the working directory in the container to /app
5
- WORKDIR /app
6
-
7
- # Add the current directory contents into the container at /app
8
- ADD . /app
9
-
10
- # Install ffmpeg
11
- RUN apt-get update && apt-get install -y ffmpeg && apt-get install curl -y
12
-
13
- # Create a virtual environment and activate it
14
- RUN python -m venv /opt/venv
15
-
16
- # Ensure the virtual environment is used
17
- ENV PATH="/opt/venv/bin:$PATH"
18
-
19
- # Install any needed packages specified in requirements.txt
20
- RUN pip install --no-cache-dir -r requirements.txt
21
-
22
- # Make port 7680 available to the world outside this container
23
- EXPOSE 7680
24
-
25
- COPY ./defaults.linux.json ./defaults.json
26
-
27
- RUN chmod +x ./setup.sh
28
- RUN ./setup.sh
29
- RUN chmod +x ./bin/*
30
-
31
-
32
- RUN mkdir webdl
33
-
34
- # Run gunicorn when the container launches
35
- RUN python ./beta/api/api.py &
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.12-slim
3
+
4
+ # Set the working directory in the container to /app
5
+ WORKDIR /app
6
+
7
+ # Add the current directory contents into the container at /app
8
+ ADD . /app
9
+
10
+ # Install ffmpeg
11
+ RUN apt-get update && apt-get install -y ffmpeg && apt-get install curl -y
12
+
13
+ # Create a virtual environment and activate it
14
+ RUN python -m venv /opt/venv
15
+
16
+ # Ensure the virtual environment is used
17
+ ENV PATH="/opt/venv/bin:$PATH"
18
+
19
+ # Install any needed packages specified in requirements.txt
20
+ RUN pip install --no-cache-dir -r requirements.txt
21
+
22
+ # Make port 7680 available to the world outside this container
23
+ EXPOSE 7680
24
+
25
+ COPY ./defaults.linux.json ./defaults.json
26
+
27
+ RUN chmod +x ./setup.sh
28
+ RUN ./setup.sh
29
+ RUN chmod +x ./bin/*
30
+
31
+
32
+ RUN mkdir webdl
33
+
34
+ # Run gunicorn when the container launches
35
+ RUN python ./beta/api/api.py
README.md CHANGED
@@ -1,167 +1,167 @@
1
- ---
2
- title: PhysicsWallah M3u8 Parser
3
- emoji: 💻🐳
4
- colorFrom: gray
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- suggested_storage: small
9
- license: mit
10
- ---
11
- # PhysicsWallah M3u8 Parser
12
-
13
- This is a Python script that parses M3u8 files. It uses the argparse library to handle command-line arguments.
14
-
15
- ## Dependencies
16
-
17
- The script requires the following executables to be available in the PATH or the user should provide the path to the executables:
18
-
19
- - [ffmpeg](https://ffmpeg.org/download.html)
20
- - [mp4decrypt](https://www.bento4.com/downloads/)
21
- - [nm3](https://github.com/nilaoda/N_m3u8DL-RE) (renamed to nm3 in the script)
22
-
23
- The script also requires the following Python libraries (which are listed in the `requirements.txt` file):
24
-
25
- - `requests`: A library for making HTTP requests. It abstracts the complexities of making requests behind a simple API, allowing you to send HTTP/1.1 requests.
26
- - `colorama`: Makes ANSI escape character sequences work on Windows and Unix systems, allowing colored terminal text and cursor positioning.
27
- - `argparse`: Provides a way to specify command line arguments and options the program is supposed to accept.
28
- - `bs4` (BeautifulSoup4): A library for pulling data out of HTML and XML files. It provides Pythonic idioms for iterating, searching, and modifying the parse tree.
29
- - `flask`: A micro web framework written in Python. It does not require particular tools or libraries, it has no database abstraction layer, form validation, or any other components where pre-existing third-party libraries provide common functions.
30
- - `flask_socketio`: Gives Flask applications access to low latency bi-directional communications between the clients and the server. The client-side application can use any of the Socket.IO official clients libraries in Javascript, C++, Java and Swift, or any compatible client to establish a permanent connection to the server.
31
-
32
- To install these dependencies, you would typically run `pip install -r requirements.txt` in your command line.
33
-
34
- or if you want to install them individually, you can run the following commands:
35
-
36
- `pip install requests colorama argparse bs4 flask flask_socketio`
37
-
38
- ## Usage
39
-
40
- You can use the script with the following command-line arguments:
41
-
42
- - `--csv-file`: Input csv file. Legacy Support too.
43
- - `--id`: PhysicsWallah Video Id for single usage. Incompatible with --csv-file. Must be used with --name.
44
- - `--name`: Name for the output file. Incompatible with --csv-file. Must be used with --url.
45
- - `--dir`: Output Directory.
46
- - `--verbose`: Verbose Output.
47
- - `--version`: Shows the version of the program.
48
- - `--simulate`: Simulate the download process. No files will be downloaded.
49
-
50
- ## Example
51
-
52
- ```bash
53
- python pwdl.py --csv-file input.csv --dir ./output --verbose
54
- ```
55
-
56
- This will parse the M3u8 files listed in `input.csv` and save the output in the `./output` directory. The `--verbose` flag is used to enable verbose output.
57
-
58
- ## Error Handling
59
-
60
- The script has built-in error handling. If an error occurs during the parsing of a file, the script will print an error message and continue with the next file. If both csv file and id (or name) is provided, the script will exit with error code 3.
61
-
62
- ## User Preferences
63
-
64
- User preferences can be loaded from a `defaults.json` file. These preferences include the temporary directory (`tmpDir`), verbosity of output (`verbose`), and whether to display a horizontal rule (`hr`). If these preferences are not set in the `defaults.json` file, the script will use default values.
65
-
66
- **Note:** The `defaults.json` file must now also include the `token` for the video download process.
67
-
68
- ## Simulation Mode
69
-
70
- The script includes a simulation mode, which can be enabled with the `--simulate` flag. In this mode, the script will print the files that would be processed, but no files will be downloaded.
71
-
72
- ## Shell Mode
73
-
74
- The script includes a shell mode, which can be enabled with the `--shell` flag.
75
-
76
- ## API Endpoints
77
-
78
- This section describes the API endpoints provided by `api.py`.
79
-
80
- ### Create Task
81
-
82
- **Endpoint:** `/create_task`
83
-
84
- **Method:** `POST`
85
-
86
- **Description:** This endpoint is used to create a new download task. It requires a JSON payload with the `id` and `name` of the video to be downloaded.
87
-
88
- **Payload:**
89
-
90
- ```json
91
- {
92
- "id": "<video_id>",
93
- "name": "<video_name>"
94
- }
95
- ```
96
-
97
- **Response:** The endpoint returns a JSON object with the `task_id` of the created task.
98
-
99
- ```json
100
- {
101
- "task_id": "<task_id>"
102
- }
103
- ```
104
-
105
- ### Get Progress
106
-
107
- **Endpoint:** `/progress/<task_id>`
108
-
109
- **Method:** `GET`
110
-
111
- **Description:** This endpoint is used to get the progress of a specific task. Replace `<task_id>` with the ID of the task.
112
-
113
- **Response:** The endpoint returns a JSON object with the progress of the task.
114
-
115
- ### Get File
116
-
117
- **Endpoint:** `/get-file/<task_id>/<name>`
118
-
119
- **Method:** `GET`
120
-
121
- **Description:** This endpoint is used to download the file associated with a specific task. Replace `<task_id>` with the ID of the task and `<name>` with the name of the file.
122
-
123
- **Response:** The endpoint returns the requested file as a download.
124
-
125
- ### Index
126
-
127
- **Endpoint:** `/`
128
-
129
- **Method:** `GET`
130
-
131
- **Description:** This is the index endpoint of the API. It returns a simple greeting message.
132
-
133
- **Response:** The endpoint returns a JSON object with a greeting message.
134
-
135
- ```json
136
- {
137
- "message": "Hello, World!"
138
- }
139
- ```
140
-
141
- Please note that the API must be running for these endpoints to be accessible. You can start the API by running `python ./beta/api/app.py`.
142
-
143
- ## Error Codes
144
-
145
-
146
- | Error Name | Error Code | Error Message |
147
- |----------------------------------|------------|-------------------------------------------------------|
148
- | noError | 0 | None |
149
- | defaultsNotFound | 1 | defaults.json not found. Exiting... |
150
- | dependencyNotFound | 2 | Dependency not found. Exiting... |
151
- | dependencyNotFoundInPrefs | 3 | Dependency not found in default settings. Exiting... |
152
- | csvFileNotFound | 4 | CSV file {fileName} not found. Exiting... |
153
- | downloadFailed | 5 | Download failed for {name} with id {id}. Exiting... |
154
- |couldNotMakeDir | 6 | Could not make directory {dir}. Exiting... |
155
- |tokenNotFound | 7 | Token not found. Exiting... |
156
- | cantLoadFile | 22 | Can't load file {fileName} |
157
- | requestFailedDueToUnknownReason | 24 | Request failed due to unknown reason. Status Code: {status_code} |
158
- | keyExtractionFailed | 25 | Key extraction failed for id -> {id}. Exiting... |
159
- | keyNotProvided | 26 | Key not provided. Exiting... |
160
- | couldNotDownloadAudio | 27 | Could not download audio for id -> {id} Exiting... |
161
- | couldNotDownloadVideo | 28 | Could not download video for {id} Exiting... |
162
- | couldNotDecryptAudio | 29 | Could not decrypt audio. Exiting... |
163
- | couldNotDecryptVideo | 30 | Could not decrypt video. Exiting... |
164
- | methodPatched | 31 | Method is patched. Exiting... |
165
- | couldNotExtractKey | 32 | Could not extract key. Exiting... |
166
-
167
  Please note that the `{fileName}`, `{name}`, `{id}`, and `{status_code}` in the Error Message column are placeholders and will be replaced with actual values when the error occurs.
 
1
+ ---
2
+ title: PhysicsWallah M3u8 Parser
3
+ emoji: 💻🐳
4
+ colorFrom: gray
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ suggested_storage: small
9
+ license: mit
10
+ ---
11
+ # PhysicsWallah M3u8 Parser
12
+
13
+ This is a Python script that parses M3u8 files. It uses the argparse library to handle command-line arguments.
14
+
15
+ ## Dependencies
16
+
17
+ The script requires the following executables to be available in the PATH or the user should provide the path to the executables:
18
+
19
+ - [ffmpeg](https://ffmpeg.org/download.html)
20
+ - [mp4decrypt](https://www.bento4.com/downloads/)
21
+ - [nm3](https://github.com/nilaoda/N_m3u8DL-RE) (renamed to nm3 in the script)
22
+
23
+ The script also requires the following Python libraries (which are listed in the `requirements.txt` file):
24
+
25
+ - `requests`: A library for making HTTP requests. It abstracts the complexities of making requests behind a simple API, allowing you to send HTTP/1.1 requests.
26
+ - `colorama`: Makes ANSI escape character sequences work on Windows and Unix systems, allowing colored terminal text and cursor positioning.
27
+ - `argparse`: Provides a way to specify command line arguments and options the program is supposed to accept.
28
+ - `bs4` (BeautifulSoup4): A library for pulling data out of HTML and XML files. It provides Pythonic idioms for iterating, searching, and modifying the parse tree.
29
+ - `flask`: A micro web framework written in Python. It does not require particular tools or libraries, it has no database abstraction layer, form validation, or any other components where pre-existing third-party libraries provide common functions.
30
+ - `flask_socketio`: Gives Flask applications access to low latency bi-directional communications between the clients and the server. The client-side application can use any of the Socket.IO official clients libraries in Javascript, C++, Java and Swift, or any compatible client to establish a permanent connection to the server.
31
+
32
+ To install these dependencies, you would typically run `pip install -r requirements.txt` in your command line.
33
+
34
+ or if you want to install them individually, you can run the following commands:
35
+
36
+ `pip install requests colorama argparse bs4 flask flask_socketio`
37
+
38
+ ## Usage
39
+
40
+ You can use the script with the following command-line arguments:
41
+
42
+ - `--csv-file`: Input csv file. Legacy Support too.
43
+ - `--id`: PhysicsWallah Video Id for single usage. Incompatible with --csv-file. Must be used with --name.
44
+ - `--name`: Name for the output file. Incompatible with --csv-file. Must be used with --url.
45
+ - `--dir`: Output Directory.
46
+ - `--verbose`: Verbose Output.
47
+ - `--version`: Shows the version of the program.
48
+ - `--simulate`: Simulate the download process. No files will be downloaded.
49
+
50
+ ## Example
51
+
52
+ ```bash
53
+ python pwdl.py --csv-file input.csv --dir ./output --verbose
54
+ ```
55
+
56
+ This will parse the M3u8 files listed in `input.csv` and save the output in the `./output` directory. The `--verbose` flag is used to enable verbose output.
57
+
58
+ ## Error Handling
59
+
60
+ The script has built-in error handling. If an error occurs during the parsing of a file, the script will print an error message and continue with the next file. If both csv file and id (or name) is provided, the script will exit with error code 3.
61
+
62
+ ## User Preferences
63
+
64
+ User preferences can be loaded from a `defaults.json` file. These preferences include the temporary directory (`tmpDir`), verbosity of output (`verbose`), and whether to display a horizontal rule (`hr`). If these preferences are not set in the `defaults.json` file, the script will use default values.
65
+
66
+ **Note:** The `defaults.json` file must now also include the `token` for the video download process.
67
+
68
+ ## Simulation Mode
69
+
70
+ The script includes a simulation mode, which can be enabled with the `--simulate` flag. In this mode, the script will print the files that would be processed, but no files will be downloaded.
71
+
72
+ ## Shell Mode
73
+
74
+ The script includes a shell mode, which can be enabled with the `--shell` flag.
75
+
76
+ ## API Endpoints
77
+
78
+ This section describes the API endpoints provided by `api.py`.
79
+
80
+ ### Create Task
81
+
82
+ **Endpoint:** `/create_task`
83
+
84
+ **Method:** `POST`
85
+
86
+ **Description:** This endpoint is used to create a new download task. It requires a JSON payload with the `id` and `name` of the video to be downloaded.
87
+
88
+ **Payload:**
89
+
90
+ ```json
91
+ {
92
+ "id": "<video_id>",
93
+ "name": "<video_name>"
94
+ }
95
+ ```
96
+
97
+ **Response:** The endpoint returns a JSON object with the `task_id` of the created task.
98
+
99
+ ```json
100
+ {
101
+ "task_id": "<task_id>"
102
+ }
103
+ ```
104
+
105
+ ### Get Progress
106
+
107
+ **Endpoint:** `/progress/<task_id>`
108
+
109
+ **Method:** `GET`
110
+
111
+ **Description:** This endpoint is used to get the progress of a specific task. Replace `<task_id>` with the ID of the task.
112
+
113
+ **Response:** The endpoint returns a JSON object with the progress of the task.
114
+
115
+ ### Get File
116
+
117
+ **Endpoint:** `/get-file/<task_id>/<name>`
118
+
119
+ **Method:** `GET`
120
+
121
+ **Description:** This endpoint is used to download the file associated with a specific task. Replace `<task_id>` with the ID of the task and `<name>` with the name of the file.
122
+
123
+ **Response:** The endpoint returns the requested file as a download.
124
+
125
+ ### Index
126
+
127
+ **Endpoint:** `/`
128
+
129
+ **Method:** `GET`
130
+
131
+ **Description:** This is the index endpoint of the API. It returns a simple greeting message.
132
+
133
+ **Response:** The endpoint returns a JSON object with a greeting message.
134
+
135
+ ```json
136
+ {
137
+ "message": "Hello, World!"
138
+ }
139
+ ```
140
+
141
+ Please note that the API must be running for these endpoints to be accessible. You can start the API by running `python ./beta/api/app.py`.
142
+
143
+ ## Error Codes
144
+
145
+
146
+ | Error Name | Error Code | Error Message |
147
+ |----------------------------------|------------|-------------------------------------------------------|
148
+ | noError | 0 | None |
149
+ | defaultsNotFound | 1 | defaults.json not found. Exiting... |
150
+ | dependencyNotFound | 2 | Dependency not found. Exiting... |
151
+ | dependencyNotFoundInPrefs | 3 | Dependency not found in default settings. Exiting... |
152
+ | csvFileNotFound | 4 | CSV file {fileName} not found. Exiting... |
153
+ | downloadFailed | 5 | Download failed for {name} with id {id}. Exiting... |
154
+ |couldNotMakeDir | 6 | Could not make directory {dir}. Exiting... |
155
+ |tokenNotFound | 7 | Token not found. Exiting... |
156
+ | cantLoadFile | 22 | Can't load file {fileName} |
157
+ | requestFailedDueToUnknownReason | 24 | Request failed due to unknown reason. Status Code: {status_code} |
158
+ | keyExtractionFailed | 25 | Key extraction failed for id -> {id}. Exiting... |
159
+ | keyNotProvided | 26 | Key not provided. Exiting... |
160
+ | couldNotDownloadAudio | 27 | Could not download audio for id -> {id} Exiting... |
161
+ | couldNotDownloadVideo | 28 | Could not download video for {id} Exiting... |
162
+ | couldNotDecryptAudio | 29 | Could not decrypt audio. Exiting... |
163
+ | couldNotDecryptVideo | 30 | Could not decrypt video. Exiting... |
164
+ | methodPatched | 31 | Method is patched. Exiting... |
165
+ | couldNotExtractKey | 32 | Could not extract key. Exiting... |
166
+
167
  Please note that the `{fileName}`, `{name}`, `{id}`, and `{status_code}` in the Error Message column are placeholders and will be replaced with actual values when the error occurs.
beta/api/api.py CHANGED
@@ -1,88 +1,88 @@
1
- import os
2
- import time
3
- from flask import Flask, jsonify, request, send_from_directory, send_file, render_template
4
- from beta.api.task_manager import TaskManager # Assuming the TaskManager class is in task_manager.py
5
- from mainLogic.big4.decrypt.key import LicenseKeyFetcher
6
- from mainLogic.main import Main
7
- from mainLogic.startup.checkup import CheckState
8
- from mainLogic.utils.glv import Global
9
- from mainLogic.utils.basicUtils import BasicUtils
10
-
11
- app = Flask(__name__)
12
- task_manager = TaskManager()
13
-
14
- OUT_DIR = Global.api_webdl_directory
15
-
16
- try:
17
- if not os.path.exists(OUT_DIR): os.makedirs(OUT_DIR)
18
- except Exception as e:
19
- Global.errprint(f"Could not create output directory {OUT_DIR}")
20
- Global.sprint(f"Defaulting to './' ")
21
- Global.errprint(f"Error: {e}")
22
- OUT_DIR = './'
23
-
24
- def setup_directory():
25
- pass
26
- def download_pw_video(task_id, name, id, out_dir, progress_callback):
27
-
28
- print(f"Downloading {name} with id {id} to {out_dir}")
29
-
30
- ch = CheckState()
31
- state = ch.checkup(Global.EXECUTABLES, directory="./", verbose=True)
32
- prefs = state['prefs']
33
- vsd = state['vsd']
34
- ffmpeg = state['ffmpeg']
35
- mp4d = state['mp4decrypt']
36
- verbose = True
37
- Main(id=id,
38
- name=f"{name}-{task_id}",
39
- token=prefs['token'],
40
- directory=out_dir, tmpDir="/*auto*/", vsdPath=vsd, ffmpeg=ffmpeg, mp4d=mp4d, verbose=verbose, progress_callback=progress_callback).process()
41
-
42
-
43
- @app.route('/create_task', methods=['POST'])
44
- def create_task():
45
- data = request.json
46
- id = data.get('id')
47
- name = data.get('name')
48
-
49
- if not id or not name:
50
- return jsonify({'error': 'id and name are required'}), 400
51
-
52
- args ={
53
- 'name' : name,
54
- 'id' : id,
55
- 'out_dir' : OUT_DIR
56
- }
57
-
58
- task_id = task_manager.create_task(download_pw_video, args)
59
- return jsonify({'task_id': task_id}), 202
60
-
61
- @app.route('/progress/<task_id>', methods=['GET'])
62
- def get_progress(task_id):
63
- progress = task_manager.get_progress(task_id)
64
- return jsonify(progress), 200
65
-
66
- @app.route('/get-file/<task_id>/<name>', methods=['GET'])
67
- def get_file(task_id,name):
68
- file = BasicUtils.abspath(f"{OUT_DIR}/{name}-{task_id}.mp4")
69
- if not os.path.exists(file):
70
- return jsonify({'error': 'file not found'}), 404
71
- return send_file( f"{file}",download_name=name+"."+os.path.basename(file).split('.')[-1] ,as_attachment=True), 200
72
-
73
- @app.route('/key/vid_id', methods=['GET'])
74
- def get_key():
75
- vid_id = request.args.get('vid_id')
76
- token = request.args.get('token')
77
- if not vid_id or not token:
78
- return jsonify({'error': 'vid_id and token are required'}), 400
79
- fetcher = LicenseKeyFetcher(token)
80
- key = fetcher.get_key(vid_id)
81
- return jsonify({'key': key}), 200
82
-
83
- @app.route('/', methods=['GET'])
84
- def index():
85
- return render_template('index.html')
86
- return jsonify({'message': 'Hello, World!'}), 200
87
-
88
- if __name__ == '__main__':app.run(debug=True,port=7680)
 
1
+ import os
2
+ import time
3
+ from flask import Flask, jsonify, request, send_from_directory, send_file, render_template
4
+ from beta.api.task_manager import TaskManager # Assuming the TaskManager class is in task_manager.py
5
+ from mainLogic.big4.decrypt.key import LicenseKeyFetcher
6
+ from mainLogic.main import Main
7
+ from mainLogic.startup.checkup import CheckState
8
+ from mainLogic.utils.glv import Global
9
+ from mainLogic.utils.basicUtils import BasicUtils
10
+
11
+ app = Flask(__name__)
12
+ task_manager = TaskManager()
13
+
14
+ OUT_DIR = Global.api_webdl_directory
15
+
16
+ try:
17
+ if not os.path.exists(OUT_DIR): os.makedirs(OUT_DIR)
18
+ except Exception as e:
19
+ Global.errprint(f"Could not create output directory {OUT_DIR}")
20
+ Global.sprint(f"Defaulting to './' ")
21
+ Global.errprint(f"Error: {e}")
22
+ OUT_DIR = './'
23
+
24
+ def setup_directory():
25
+ pass
26
+ def download_pw_video(task_id, name, id, out_dir, progress_callback):
27
+
28
+ print(f"Downloading {name} with id {id} to {out_dir}")
29
+
30
+ ch = CheckState()
31
+ state = ch.checkup(Global.EXECUTABLES, directory="./", verbose=True)
32
+ prefs = state['prefs']
33
+ vsd = state['vsd']
34
+ ffmpeg = state['ffmpeg']
35
+ mp4d = state['mp4decrypt']
36
+ verbose = True
37
+ Main(id=id,
38
+ name=f"{name}-{task_id}",
39
+ token=prefs['token'],
40
+ directory=out_dir, tmpDir="/*auto*/", vsdPath=vsd, ffmpeg=ffmpeg, mp4d=mp4d, verbose=verbose, progress_callback=progress_callback).process()
41
+
42
+
43
+ @app.route('/create_task', methods=['POST'])
44
+ def create_task():
45
+ data = request.json
46
+ id = data.get('id')
47
+ name = data.get('name')
48
+
49
+ if not id or not name:
50
+ return jsonify({'error': 'id and name are required'}), 400
51
+
52
+ args ={
53
+ 'name' : name,
54
+ 'id' : id,
55
+ 'out_dir' : OUT_DIR
56
+ }
57
+
58
+ task_id = task_manager.create_task(download_pw_video, args)
59
+ return jsonify({'task_id': task_id}), 202
60
+
61
+ @app.route('/progress/<task_id>', methods=['GET'])
62
+ def get_progress(task_id):
63
+ progress = task_manager.get_progress(task_id)
64
+ return jsonify(progress), 200
65
+
66
+ @app.route('/get-file/<task_id>/<name>', methods=['GET'])
67
+ def get_file(task_id,name):
68
+ file = BasicUtils.abspath(f"{OUT_DIR}/{name}-{task_id}.mp4")
69
+ if not os.path.exists(file):
70
+ return jsonify({'error': 'file not found'}), 404
71
+ return send_file( f"{file}",download_name=name+"."+os.path.basename(file).split('.')[-1] ,as_attachment=True), 200
72
+
73
+ @app.route('/key/vid_id', methods=['GET'])
74
+ def get_key():
75
+ vid_id = request.args.get('vid_id')
76
+ token = request.args.get('token')
77
+ if not vid_id or not token:
78
+ return jsonify({'error': 'vid_id and token are required'}), 400
79
+ fetcher = LicenseKeyFetcher(token)
80
+ key = fetcher.get_key(vid_id)
81
+ return jsonify({'key': key}), 200
82
+
83
+ @app.route('/', methods=['GET'])
84
+ def index():
85
+ return render_template('index.html')
86
+ return jsonify({'message': 'Hello, World!'}), 200
87
+
88
+ if __name__ == '__main__':app.run(debug=True,port=7680)
beta/api/task_manager.py CHANGED
@@ -1,53 +1,53 @@
1
- import threading
2
- import time
3
- import uuid
4
-
5
-
6
- class TaskManager:
7
-
8
- def handle_completion(self, task_id):
9
- print(f"Task {task_id} completed")
10
- self.tasks[task_id]['status'] = 'completed'
11
-
12
- on_task_complete = handle_completion
13
-
14
- def __init__(self):
15
- self.tasks = {}
16
- self.lock = threading.Lock()
17
-
18
- def create_task(self, target, *args):
19
- task_id = str(uuid.uuid4())
20
-
21
- args_dict = args[0]
22
- try:
23
- name = args_dict['name']
24
- id = args_dict['id']
25
- out_dir = args_dict['out_dir']
26
- except KeyError:
27
- raise ValueError('name, id, and out_dir are required in args')
28
-
29
- thread = threading.Thread(target=self._run_task, args=(task_id, target, name, id, out_dir, *args[1:]))
30
- with self.lock:
31
- self.tasks[task_id] = {'progress': "0", 'status': 'running', 'name': name}
32
- thread.start()
33
- return task_id
34
-
35
- def _run_task(self, task_id, target, *args):
36
- try:
37
- target(task_id, *args, progress_callback=lambda progress: self._update_progress(task_id, progress))
38
- with self.lock:
39
- self.tasks[task_id]['url'] = f'/get-file/{task_id}/{self.tasks[task_id]["name"]}'
40
- self.tasks[task_id]['status'] = 'completed'
41
- except Exception as e:
42
- with self.lock:
43
- self.tasks[task_id]['status'] = 'failed'
44
- self.tasks[task_id]['error'] = str(e)
45
-
46
- def _update_progress(self, task_id, progress):
47
- with self.lock:
48
- if task_id in self.tasks:
49
- self.tasks[task_id]['progress'] = progress
50
-
51
- def get_progress(self, task_id):
52
- with self.lock:
53
- return self.tasks.get(task_id, {'status': 'not found'})
 
1
+ import threading
2
+ import time
3
+ import uuid
4
+
5
+
6
+ class TaskManager:
7
+
8
+ def handle_completion(self, task_id):
9
+ print(f"Task {task_id} completed")
10
+ self.tasks[task_id]['status'] = 'completed'
11
+
12
+ on_task_complete = handle_completion
13
+
14
+ def __init__(self):
15
+ self.tasks = {}
16
+ self.lock = threading.Lock()
17
+
18
+ def create_task(self, target, *args):
19
+ task_id = str(uuid.uuid4())
20
+
21
+ args_dict = args[0]
22
+ try:
23
+ name = args_dict['name']
24
+ id = args_dict['id']
25
+ out_dir = args_dict['out_dir']
26
+ except KeyError:
27
+ raise ValueError('name, id, and out_dir are required in args')
28
+
29
+ thread = threading.Thread(target=self._run_task, args=(task_id, target, name, id, out_dir, *args[1:]))
30
+ with self.lock:
31
+ self.tasks[task_id] = {'progress': "0", 'status': 'running', 'name': name}
32
+ thread.start()
33
+ return task_id
34
+
35
+ def _run_task(self, task_id, target, *args):
36
+ try:
37
+ target(task_id, *args, progress_callback=lambda progress: self._update_progress(task_id, progress))
38
+ with self.lock:
39
+ self.tasks[task_id]['url'] = f'/get-file/{task_id}/{self.tasks[task_id]["name"]}'
40
+ self.tasks[task_id]['status'] = 'completed'
41
+ except Exception as e:
42
+ with self.lock:
43
+ self.tasks[task_id]['status'] = 'failed'
44
+ self.tasks[task_id]['error'] = str(e)
45
+
46
+ def _update_progress(self, task_id, progress):
47
+ with self.lock:
48
+ if task_id in self.tasks:
49
+ self.tasks[task_id]['progress'] = progress
50
+
51
+ def get_progress(self, task_id):
52
+ with self.lock:
53
+ return self.tasks.get(task_id, {'status': 'not found'})
beta/api/templates/index.html CHANGED
@@ -1,19 +1,19 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8"/>
5
- <link rel="icon" href="/favicon.ico"/>
6
- <meta name="viewport" content="width=device-width,initial-scale=1"/>
7
- <meta name="theme-color" content="#000000"/>
8
- <meta name="description" content="pwdl frontend"/>
9
- <link rel="apple-touch-icon" href="/logo192.png"/>
10
- <link rel="manifest" href="/manifest.json"/>
11
- <title>PWDL | Online DL</title>
12
- <script defer="defer" src="/static/js/main.1051cee6.js"></script>
13
- <link href="/static/css/main.f855e6bc.css" rel="stylesheet">
14
- </head>
15
- <body>
16
- <noscript>You need to enable JavaScript to run this app.</noscript>
17
- <div id="root"></div>
18
- </body>
19
  </html>
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <link rel="icon" href="/favicon.ico"/>
6
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
7
+ <meta name="theme-color" content="#000000"/>
8
+ <meta name="description" content="pwdl frontend"/>
9
+ <link rel="apple-touch-icon" href="/logo192.png"/>
10
+ <link rel="manifest" href="/manifest.json"/>
11
+ <title>PWDL | Online DL</title>
12
+ <script defer="defer" src="/static/js/main.1051cee6.js"></script>
13
+ <link href="/static/css/main.f855e6bc.css" rel="stylesheet">
14
+ </head>
15
+ <body>
16
+ <noscript>You need to enable JavaScript to run this app.</noscript>
17
+ <div id="root"></div>
18
+ </body>
19
  </html>
beta/shellLogic/HandleBasicCMDUtils.py CHANGED
@@ -1,41 +1,41 @@
1
- import sys
2
- from beta.shellLogic import simpleParser
3
- from mainLogic.utils.os2 import SysFunc
4
-
5
- os2 = SysFunc()
6
-
7
-
8
- class HandleBasicCMDUtils:
9
- # basic class for handling basic commands
10
- # every such class must have a method to parse command (regex based) a help for each command handled by the class
11
-
12
- def __init__(self):
13
- self.commandList = {
14
- "cls":
15
- {
16
- "desc": "Clear the screen",
17
- "regex": r"cls",
18
- "func": self.cls
19
- },
20
- "exit":
21
- {
22
- "desc": "Exit the shell",
23
- "regex": r"exit",
24
- "func": self.exit_shell
25
- },
26
- }
27
-
28
- def cls(self,args=[]):
29
- os2.clear()
30
- if args: print(args)
31
-
32
- def exit_shell(self,args=[]):
33
- sys.exit(10)
34
-
35
- def parseAndRun(self, command,args=[]):
36
- # for key in self.commandList:
37
- # if re.match(self.commandList[key]["regex"], command):
38
- # self.commandList[key]["func"]()
39
- # return
40
- # raise logicError.commandNotFound(command)
41
- simpleParser.parseAndRun(self.commandList, command, args)
 
1
+ import sys
2
+ from beta.shellLogic import simpleParser
3
+ from mainLogic.utils.os2 import SysFunc
4
+
5
+ os2 = SysFunc()
6
+
7
+
8
+ class HandleBasicCMDUtils:
9
+ # basic class for handling basic commands
10
+ # every such class must have a method to parse command (regex based) a help for each command handled by the class
11
+
12
+ def __init__(self):
13
+ self.commandList = {
14
+ "cls":
15
+ {
16
+ "desc": "Clear the screen",
17
+ "regex": r"cls",
18
+ "func": self.cls
19
+ },
20
+ "exit":
21
+ {
22
+ "desc": "Exit the shell",
23
+ "regex": r"exit",
24
+ "func": self.exit_shell
25
+ },
26
+ }
27
+
28
+ def cls(self,args=[]):
29
+ os2.clear()
30
+ if args: print(args)
31
+
32
+ def exit_shell(self,args=[]):
33
+ sys.exit(10)
34
+
35
+ def parseAndRun(self, command,args=[]):
36
+ # for key in self.commandList:
37
+ # if re.match(self.commandList[key]["regex"], command):
38
+ # self.commandList[key]["func"]()
39
+ # return
40
+ # raise logicError.commandNotFound(command)
41
+ simpleParser.parseAndRun(self.commandList, command, args)
beta/shellLogic/HandleKeyAndAvailiblity.py CHANGED
@@ -1,38 +1,38 @@
1
- from mainLogic.big4.decrypt.key import LicenseKeyFetcher
2
- from beta.shellLogic import simpleParser
3
- from mainLogic.utils.glv import Global
4
-
5
- class HandleKeyAndAvailiblity:
6
-
7
- def __init__(self):
8
- from mainLogic.startup.checkup import CheckState
9
- ch = CheckState()
10
- self.token = ch.checkup(Global.EXECUTABLES,verbose=False)['prefs']['token']
11
- self.lkf = LicenseKeyFetcher(self.token)
12
- self.commandList = {
13
- "get_key":{
14
- "regex": r"(get_key|key)",
15
- "func": self.get_key,
16
- },
17
- "check":{
18
- "func": self.check
19
- }
20
-
21
- }
22
-
23
- def get_key(self,args=[]):
24
- if args:
25
- self.lkf.get_key(args[0])
26
-
27
- def check(self,args=[]):
28
- print("Checking the availiblity of the key...")
29
- if args:
30
- if self.lkf.get_key(args[0],verbose=False):
31
- print("Key is available")
32
- else:
33
- print("Key is not available")
34
- else:
35
- print("Please provide a key to check")
36
-
37
- def parseAndRun(self,command,args=[]):
38
- simpleParser.parseAndRun(self.commandList, command, args)
 
1
+ from mainLogic.big4.decrypt.key import LicenseKeyFetcher
2
+ from beta.shellLogic import simpleParser
3
+ from mainLogic.utils.glv import Global
4
+
5
+ class HandleKeyAndAvailiblity:
6
+
7
+ def __init__(self):
8
+ from mainLogic.startup.checkup import CheckState
9
+ ch = CheckState()
10
+ self.token = ch.checkup(Global.EXECUTABLES,verbose=False)['prefs']['token']
11
+ self.lkf = LicenseKeyFetcher(self.token)
12
+ self.commandList = {
13
+ "get_key":{
14
+ "regex": r"(get_key|key)",
15
+ "func": self.get_key,
16
+ },
17
+ "check":{
18
+ "func": self.check
19
+ }
20
+
21
+ }
22
+
23
+ def get_key(self,args=[]):
24
+ if args:
25
+ self.lkf.get_key(args[0])
26
+
27
+ def check(self,args=[]):
28
+ print("Checking the availiblity of the key...")
29
+ if args:
30
+ if self.lkf.get_key(args[0],verbose=False):
31
+ print("Key is available")
32
+ else:
33
+ print("Key is not available")
34
+ else:
35
+ print("Please provide a key to check")
36
+
37
+ def parseAndRun(self,command,args=[]):
38
+ simpleParser.parseAndRun(self.commandList, command, args)
beta/shellLogic/HandleShellDL.py CHANGED
@@ -1,62 +1,62 @@
1
- from mainLogic.big4.dl import DL
2
- from mainLogic.startup.checkup import CheckState
3
- from mainLogic.utils.glv import Global
4
- from mainLogic.main import Main
5
- from beta.shellLogic import simpleParser
6
-
7
-
8
- class HandleShellDL:
9
-
10
- def __init__(self):
11
- self.commandList = {
12
- "edl":{
13
- "func": self.edownload
14
- },
15
- "dl":{
16
- "func": self.download
17
- }
18
- }
19
-
20
- def edownload(self,args=[]):
21
- # print(args)
22
- if not args or len(args) < 2:
23
- print("Please provide a name and id")
24
- return
25
-
26
- name = args[0]
27
- id = args[1]
28
-
29
- dl = DL()
30
- ch =CheckState()
31
- prefs = ch.checkup(Global.EXECUTABLES,verbose=False)
32
- dl.downloadAudioAndVideo(name=name,
33
- id=id,
34
- directory='./',
35
- nm3Path=prefs['nm3'],
36
- verbose=False if not 'verbose' in prefs else prefs['verbose'],
37
- )
38
-
39
- def download(self,args=[]):
40
- if not args or len(args) < 2:
41
- print("Please provide a name and id")
42
- return
43
-
44
- name = args[0]
45
- id = args[1]
46
-
47
- ch = CheckState()
48
- prefs = ch.checkup(Global.EXECUTABLES,verbose=False)
49
-
50
- Main(id=id,
51
- name=name,
52
- directory='./',
53
- nm3Path=prefs['nm3'],
54
- mp4d=prefs['mp4decrypt'],
55
- ffmpeg=prefs['ffmpeg']
56
- ).process()
57
-
58
-
59
-
60
- def parseAndRun(self,command,args=[]):
61
- simpleParser.parseAndRun(self.commandList, command, args)
62
-
 
1
+ from mainLogic.big4.dl import DL
2
+ from mainLogic.startup.checkup import CheckState
3
+ from mainLogic.utils.glv import Global
4
+ from mainLogic.main import Main
5
+ from beta.shellLogic import simpleParser
6
+
7
+
8
+ class HandleShellDL:
9
+
10
+ def __init__(self):
11
+ self.commandList = {
12
+ "edl":{
13
+ "func": self.edownload
14
+ },
15
+ "dl":{
16
+ "func": self.download
17
+ }
18
+ }
19
+
20
+ def edownload(self,args=[]):
21
+ # print(args)
22
+ if not args or len(args) < 2:
23
+ print("Please provide a name and id")
24
+ return
25
+
26
+ name = args[0]
27
+ id = args[1]
28
+
29
+ dl = DL()
30
+ ch =CheckState()
31
+ prefs = ch.checkup(Global.EXECUTABLES,verbose=False)
32
+ dl.downloadAudioAndVideo(name=name,
33
+ id=id,
34
+ directory='./',
35
+ nm3Path=prefs['nm3'],
36
+ verbose=False if not 'verbose' in prefs else prefs['verbose'],
37
+ )
38
+
39
+ def download(self,args=[]):
40
+ if not args or len(args) < 2:
41
+ print("Please provide a name and id")
42
+ return
43
+
44
+ name = args[0]
45
+ id = args[1]
46
+
47
+ ch = CheckState()
48
+ prefs = ch.checkup(Global.EXECUTABLES,verbose=False)
49
+
50
+ Main(id=id,
51
+ name=name,
52
+ directory='./',
53
+ nm3Path=prefs['nm3'],
54
+ mp4d=prefs['mp4decrypt'],
55
+ ffmpeg=prefs['ffmpeg']
56
+ ).process()
57
+
58
+
59
+
60
+ def parseAndRun(self,command,args=[]):
61
+ simpleParser.parseAndRun(self.commandList, command, args)
62
+
beta/shellLogic/TokenUpdate.py CHANGED
@@ -1,34 +1,34 @@
1
- from mainLogic.utils.glv import Global
2
- from beta.shellLogic import simpleParser
3
- from beta.shellLogic.update import UpdateJSONFile
4
- class TokenUpdate:
5
-
6
- def __init__(self):
7
-
8
- self.file_path = Global.PREFERENCES_FILE
9
- # hard coding 'defaults.json' as to ../../defaults.json
10
- #Global.errprint("Warning! This is a beta feature. Use at your own risk.")
11
- #Global.errprint("Hard Coded to use 'defaults.json' as to ../../defaults.json (in Global.PREFERENCES_FILE)")
12
- self.commandList = {
13
- "tkn-up":{
14
- "func": self.update
15
- }
16
- }
17
-
18
- def update(self,args=[]):
19
- if args:
20
- u = UpdateJSONFile(self.file_path)
21
- u.update('token',args[0])
22
- Global.sprint("Token updated successfully.")
23
- else:
24
- Global.errprint("Please provide a token to update.")
25
-
26
- def parseAndRun(self,command,args=[]):
27
- # simpleParser.parseAndRun(self.commandList, command, args)
28
- if command in self.commandList:
29
- self.commandList[command]["func"](args)
30
- else:
31
- Global.errprint("Command not found.")
32
-
33
-
34
-
 
1
+ from mainLogic.utils.glv import Global
2
+ from beta.shellLogic import simpleParser
3
+ from beta.shellLogic.update import UpdateJSONFile
4
+ class TokenUpdate:
5
+
6
+ def __init__(self):
7
+
8
+ self.file_path = Global.PREFERENCES_FILE
9
+ # hard coding 'defaults.json' as to ../../defaults.json
10
+ #Global.errprint("Warning! This is a beta feature. Use at your own risk.")
11
+ #Global.errprint("Hard Coded to use 'defaults.json' as to ../../defaults.json (in Global.PREFERENCES_FILE)")
12
+ self.commandList = {
13
+ "tkn-up":{
14
+ "func": self.update
15
+ }
16
+ }
17
+
18
+ def update(self,args=[]):
19
+ if args:
20
+ u = UpdateJSONFile(self.file_path)
21
+ u.update('token',args[0])
22
+ Global.sprint("Token updated successfully.")
23
+ else:
24
+ Global.errprint("Please provide a token to update.")
25
+
26
+ def parseAndRun(self,command,args=[]):
27
+ # simpleParser.parseAndRun(self.commandList, command, args)
28
+ if command in self.commandList:
29
+ self.commandList[command]["func"](args)
30
+ else:
31
+ Global.errprint("Command not found.")
32
+
33
+
34
+
beta/shellLogic/logic.py CHANGED
@@ -1,28 +1,28 @@
1
- from mainLogic.utils.os2 import SysFunc
2
- from beta.shellLogic.HandleBasicCMDUtils import HandleBasicCMDUtils
3
- from beta.shellLogic.HandleKeyAndAvailiblity import HandleKeyAndAvailiblity
4
- from beta.shellLogic.HandleShellDL import HandleShellDL
5
- from beta.shellLogic.TokenUpdate import TokenUpdate
6
-
7
- os2 = SysFunc()
8
- f1 = HandleBasicCMDUtils()
9
- key_utils = HandleKeyAndAvailiblity()
10
- dl_utils = HandleShellDL()
11
- token_update = TokenUpdate()
12
-
13
- commands_available={
14
- # command: [location_of_function,help_class]
15
- "exit": [f1.parseAndRun,""],
16
- "cls" : [f1.parseAndRun,""],
17
- "get_key":[key_utils.parseAndRun,""],
18
- "check": [key_utils.parseAndRun,""],
19
- "edl": [dl_utils.parseAndRun,""],
20
- "dl":[dl_utils.parseAndRun,""],
21
- "tkn-up":[token_update.parseAndRun,""],
22
-
23
-
24
- }
25
-
26
- def execute(command,args=[]):
27
- if command in commands_available:
28
  commands_available[command][0](command,args)
 
1
+ from mainLogic.utils.os2 import SysFunc
2
+ from beta.shellLogic.HandleBasicCMDUtils import HandleBasicCMDUtils
3
+ from beta.shellLogic.HandleKeyAndAvailiblity import HandleKeyAndAvailiblity
4
+ from beta.shellLogic.HandleShellDL import HandleShellDL
5
+ from beta.shellLogic.TokenUpdate import TokenUpdate
6
+
7
+ os2 = SysFunc()
8
+ f1 = HandleBasicCMDUtils()
9
+ key_utils = HandleKeyAndAvailiblity()
10
+ dl_utils = HandleShellDL()
11
+ token_update = TokenUpdate()
12
+
13
+ commands_available={
14
+ # command: [location_of_function,help_class]
15
+ "exit": [f1.parseAndRun,""],
16
+ "cls" : [f1.parseAndRun,""],
17
+ "get_key":[key_utils.parseAndRun,""],
18
+ "check": [key_utils.parseAndRun,""],
19
+ "edl": [dl_utils.parseAndRun,""],
20
+ "dl":[dl_utils.parseAndRun,""],
21
+ "tkn-up":[token_update.parseAndRun,""],
22
+
23
+
24
+ }
25
+
26
+ def execute(command,args=[]):
27
+ if command in commands_available:
28
  commands_available[command][0](command,args)
beta/shellLogic/logicError.py CHANGED
@@ -1,6 +1,6 @@
1
- class commandNotFound(Exception):
2
- def __init__(self, command):
3
- self.command = command
4
-
5
- def __str__(self):
6
  return f"Command '{self.command}' not found"
 
1
+ class commandNotFound(Exception):
2
+ def __init__(self, command):
3
+ self.command = command
4
+
5
+ def __str__(self):
6
  return f"Command '{self.command}' not found"
beta/shellLogic/shell.py CHANGED
@@ -1,53 +1,53 @@
1
- from prompt_toolkit import PromptSession
2
- from mainLogic.utils.glv import Global
3
- from mainLogic.startup.checkup import CheckState
4
- import json
5
- from mainLogic.utils.os2 import SysFunc
6
-
7
- glv = Global()
8
- EXECUTABLES = glv.EXECUTABLES
9
- os2 = SysFunc()
10
-
11
- # Initialize Prompt Toolkit session
12
- session = PromptSession()
13
-
14
- def main():
15
- # Perform checkup and get preferences
16
- # Hardcoded verbose to False
17
- state = CheckState().checkup(EXECUTABLES, './', verbose=False)
18
- prefs = state['prefs']
19
-
20
- # Convert preferences to JSON string for display
21
- prefs_json = json.dumps(prefs, indent=4)
22
-
23
-
24
-
25
- # Define available commands for auto-completion
26
- #commands = ['show_prefs', 'exit']
27
- #completer = WordCompleter(commands, ignore_case=True)
28
-
29
- from beta.shellLogic import logic
30
-
31
- # Command-line interface loop
32
- while True:
33
- try:
34
- user_input = session.prompt('|pwdl> ',)
35
-
36
- # just in case the user hits enter without typing anything
37
- if not user_input: continue
38
-
39
- command = user_input.split()[0]
40
- args = user_input.split()[1:]
41
- if not args: args = []
42
-
43
- logic.execute(command, args)
44
-
45
- except KeyboardInterrupt:
46
- continue
47
- except EOFError:
48
- break
49
-
50
-
51
-
52
- if __name__ == "__main__":
53
- main()
 
1
+ from prompt_toolkit import PromptSession
2
+ from mainLogic.utils.glv import Global
3
+ from mainLogic.startup.checkup import CheckState
4
+ import json
5
+ from mainLogic.utils.os2 import SysFunc
6
+
7
+ glv = Global()
8
+ EXECUTABLES = glv.EXECUTABLES
9
+ os2 = SysFunc()
10
+
11
+ # Initialize Prompt Toolkit session
12
+ session = PromptSession()
13
+
14
+ def main():
15
+ # Perform checkup and get preferences
16
+ # Hardcoded verbose to False
17
+ state = CheckState().checkup(EXECUTABLES, './', verbose=False)
18
+ prefs = state['prefs']
19
+
20
+ # Convert preferences to JSON string for display
21
+ prefs_json = json.dumps(prefs, indent=4)
22
+
23
+
24
+
25
+ # Define available commands for auto-completion
26
+ #commands = ['show_prefs', 'exit']
27
+ #completer = WordCompleter(commands, ignore_case=True)
28
+
29
+ from beta.shellLogic import logic
30
+
31
+ # Command-line interface loop
32
+ while True:
33
+ try:
34
+ user_input = session.prompt('|pwdl> ',)
35
+
36
+ # just in case the user hits enter without typing anything
37
+ if not user_input: continue
38
+
39
+ command = user_input.split()[0]
40
+ args = user_input.split()[1:]
41
+ if not args: args = []
42
+
43
+ logic.execute(command, args)
44
+
45
+ except KeyboardInterrupt:
46
+ continue
47
+ except EOFError:
48
+ break
49
+
50
+
51
+
52
+ if __name__ == "__main__":
53
+ main()
beta/shellLogic/simpleParser.py CHANGED
@@ -1,6 +1,6 @@
1
- def parseAndRun(commandlist,command,args=[],obj=None):
2
- if command in commandlist: func = commandlist[command]["func"]
3
-
4
- if not func: return
5
-
6
  func(args)
 
1
+ def parseAndRun(commandlist,command,args=[],obj=None):
2
+ if command in commandlist: func = commandlist[command]["func"]
3
+
4
+ if not func: return
5
+
6
  func(args)
beta/shellLogic/update.py CHANGED
@@ -1,20 +1,20 @@
1
- import json
2
-
3
-
4
- class UpdateJSONFile:
5
- def __init__(self, file_path):
6
- self.file_path = file_path
7
- self.data = None
8
- self.load()
9
-
10
- def load(self):
11
- with open(self.file_path, 'r') as file:
12
- self.data = json.load(file)
13
-
14
- def save(self):
15
- with open(self.file_path, 'w') as file:
16
- json.dump(self.data, file, indent=4)
17
-
18
- def update(self, key, value):
19
- self.data[key] = value
20
- self.save()
 
1
+ import json
2
+
3
+
4
+ class UpdateJSONFile:
5
+ def __init__(self, file_path):
6
+ self.file_path = file_path
7
+ self.data = None
8
+ self.load()
9
+
10
+ def load(self):
11
+ with open(self.file_path, 'r') as file:
12
+ self.data = json.load(file)
13
+
14
+ def save(self):
15
+ with open(self.file_path, 'w') as file:
16
+ json.dump(self.data, file, indent=4)
17
+
18
+ def update(self, key, value):
19
+ self.data[key] = value
20
+ self.save()
beta/util.py CHANGED
@@ -1,47 +1,47 @@
1
- import json
2
-
3
- csv_file = input('Enter CSV file:')
4
-
5
- json = ''
6
-
7
- x = json.loads(json)
8
-
9
- import re
10
-
11
- def extract_uuid(text):
12
- """
13
- Extracts UUIDs from a string using a regular expression.
14
-
15
- Args:
16
- text: The string to search for UUIDs.
17
-
18
- Returns:
19
- A list of extracted UUIDs, or an empty list if none are found.
20
- """
21
- pattern = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
22
- matches = re.findall(pattern, text)
23
- return matches
24
-
25
- def generate_safe_filename(filename):
26
- """
27
- Converts a filename to a safe format containing only alphabets, periods (.), and colons (:).
28
-
29
- Args:
30
- filename: The original filename to be converted.
31
-
32
- Returns:
33
- A safe filename string with only allowed characters.
34
- """
35
- # Replace all characters except alphabets, periods, and colons with underscores
36
- safe_filename = re.sub(r"[^\w\.\:]", "_", filename)
37
- return safe_filename
38
-
39
- lines = []
40
-
41
- for videos in x['data']:
42
-
43
- line = f"{generate_safe_filename(videos['title'])},{extract_uuid(videos['content'][0]['videoUrl'])[0]}"
44
- lines.append(line)
45
-
46
- with open(csv_file, 'w') as f:
47
  f.write('\n'.join(lines))
 
1
+ import json
2
+
3
+ csv_file = input('Enter CSV file:')
4
+
5
+ json = ''
6
+
7
+ x = json.loads(json)
8
+
9
+ import re
10
+
11
+ def extract_uuid(text):
12
+ """
13
+ Extracts UUIDs from a string using a regular expression.
14
+
15
+ Args:
16
+ text: The string to search for UUIDs.
17
+
18
+ Returns:
19
+ A list of extracted UUIDs, or an empty list if none are found.
20
+ """
21
+ pattern = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
22
+ matches = re.findall(pattern, text)
23
+ return matches
24
+
25
+ def generate_safe_filename(filename):
26
+ """
27
+ Converts a filename to a safe format containing only alphabets, periods (.), and colons (:).
28
+
29
+ Args:
30
+ filename: The original filename to be converted.
31
+
32
+ Returns:
33
+ A safe filename string with only allowed characters.
34
+ """
35
+ # Replace all characters except alphabets, periods, and colons with underscores
36
+ safe_filename = re.sub(r"[^\w\.\:]", "_", filename)
37
+ return safe_filename
38
+
39
+ lines = []
40
+
41
+ for videos in x['data']:
42
+
43
+ line = f"{generate_safe_filename(videos['title'])},{extract_uuid(videos['content'][0]['videoUrl'])[0]}"
44
+ lines.append(line)
45
+
46
+ with open(csv_file, 'w') as f:
47
  f.write('\n'.join(lines))
defaults.json CHANGED
@@ -1,12 +1,12 @@
1
- {
2
- "cloudfront_id": "d1d34p8vz63oiq",
3
- "patched": false,
4
- "os-info": "winX64",
5
- "tmpDir": "%temp%",
6
- "verbose": false,
7
- "vsd": "$script/bin/vsd.exe",
8
- "ffmpeg": "",
9
- "hr": true,
10
- "mp4decrypt": "$script/bin/mp4decrypt.exe",
11
- "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTkzOTM2NjEuNTA5LCJkYXRhIjp7Il9pZCI6IjY0MzE3NTQyNDBlOTc5MDAxODAwMjAyYiIsInVzZXJuYW1lIjoiOTQ3MjUwNzEwMCIsImZpcnN0TmFtZSI6IkFrc2hpdCBTaHViaGFtIiwibGFzdE5hbWUiOiIiLCJvcmdhbml6YXRpb24iOnsiX2lkIjoiNWViMzkzZWU5NWZhYjc0NjhhNzlkMTg5Iiwid2Vic2l0ZSI6InBoeXNpY3N3YWxsYWguY29tIiwibmFtZSI6IlBoeXNpY3N3YWxsYWgifSwiZW1haWwiOiJha3NoaXRzaHViaGFtbWFzQGdtYWlsLmNvbSIsInJvbGVzIjpbIjViMjdiZDk2NTg0MmY5NTBhNzc4YzZlZiJdLCJjb3VudHJ5R3JvdXAiOiJJTiIsInR5cGUiOiJVU0VSIn0sImlhdCI6MTcxODc4ODg2MX0.5usarCdw6Svvw26Jq7edBWtouRdUNkM7DiJwyUgMNh8"
12
  }
 
1
+ {
2
+ "cloudfront_id": "d1d34p8vz63oiq",
3
+ "patched": false,
4
+ "os-info": "winX64",
5
+ "tmpDir": "%temp%",
6
+ "verbose": false,
7
+ "vsd": "$script/bin/vsd.exe",
8
+ "ffmpeg": "",
9
+ "hr": true,
10
+ "mp4decrypt": "$script/bin/mp4decrypt.exe",
11
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTkzOTM2NjEuNTA5LCJkYXRhIjp7Il9pZCI6IjY0MzE3NTQyNDBlOTc5MDAxODAwMjAyYiIsInVzZXJuYW1lIjoiOTQ3MjUwNzEwMCIsImZpcnN0TmFtZSI6IkFrc2hpdCBTaHViaGFtIiwibGFzdE5hbWUiOiIiLCJvcmdhbml6YXRpb24iOnsiX2lkIjoiNWViMzkzZWU5NWZhYjc0NjhhNzlkMTg5Iiwid2Vic2l0ZSI6InBoeXNpY3N3YWxsYWguY29tIiwibmFtZSI6IlBoeXNpY3N3YWxsYWgifSwiZW1haWwiOiJha3NoaXRzaHViaGFtbWFzQGdtYWlsLmNvbSIsInJvbGVzIjpbIjViMjdiZDk2NTg0MmY5NTBhNzc4YzZlZiJdLCJjb3VudHJ5R3JvdXAiOiJJTiIsInR5cGUiOiJVU0VSIn0sImlhdCI6MTcxODc4ODg2MX0.5usarCdw6Svvw26Jq7edBWtouRdUNkM7DiJwyUgMNh8"
12
  }
defaults.linux.json CHANGED
@@ -1,12 +1,12 @@
1
- {
2
- "flare_url": "http://localhost:8191/v1",
3
- "cloudfront_id": "d1d34p8vz63oiq",
4
- "patched": false,
5
- "os-info": "linux",
6
- "tmpDir": "/tmp",
7
- "verbose": false,
8
- "vsd": "$script/bin/vsd",
9
- "ffmpeg": "",
10
- "mp4decrypt": "$script/bin/mp4decrypt",
11
- "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTkwNTM1MjAuNDgxLCJkYXRhIjp7Il9pZCI6IjY0MzE3NTQyNDBlOTc5MDAxODAwMjAyYiIsInVzZXJuYW1lIjoiOTQ3MjUwNzEwMCIsImZpcnN0TmFtZSI6IkFrc2hpdCBTaHViaGFtIiwibGFzdE5hbWUiOiIiLCJvcmdhbml6YXRpb24iOnsiX2lkIjoiNWViMzkzZWU5NWZhYjc0NjhhNzlkMTg5Iiwid2Vic2l0ZSI6InBoeXNpY3N3YWxsYWguY29tIiwibmFtZSI6IlBoeXNpY3N3YWxsYWgifSwiZW1haWwiOiJha3NoaXRzaHViaGFtbWFzQGdtYWlsLmNvbSIsInJvbGVzIjpbIjViMjdiZDk2NTg0MmY5NTBhNzc4YzZlZiJdLCJjb3VudHJ5R3JvdXAiOiJJTiIsInR5cGUiOiJVU0VSIn0sImlhdCI6MTcxODQ0ODcyMH0.PC0u4feVyT4WSzhBfpfPYB2YKwArJxYJER4R8c2gdD8"
12
  }
 
1
+ {
2
+ "flare_url": "http://localhost:8191/v1",
3
+ "cloudfront_id": "d1d34p8vz63oiq",
4
+ "patched": false,
5
+ "os-info": "linux",
6
+ "tmpDir": "/tmp",
7
+ "verbose": false,
8
+ "vsd": "$script/bin/vsd",
9
+ "ffmpeg": "",
10
+ "mp4decrypt": "$script/bin/mp4decrypt",
11
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTkwNTM1MjAuNDgxLCJkYXRhIjp7Il9pZCI6IjY0MzE3NTQyNDBlOTc5MDAxODAwMjAyYiIsInVzZXJuYW1lIjoiOTQ3MjUwNzEwMCIsImZpcnN0TmFtZSI6IkFrc2hpdCBTaHViaGFtIiwibGFzdE5hbWUiOiIiLCJvcmdhbml6YXRpb24iOnsiX2lkIjoiNWViMzkzZWU5NWZhYjc0NjhhNzlkMTg5Iiwid2Vic2l0ZSI6InBoeXNpY3N3YWxsYWguY29tIiwibmFtZSI6IlBoeXNpY3N3YWxsYWgifSwiZW1haWwiOiJha3NoaXRzaHViaGFtbWFzQGdtYWlsLmNvbSIsInJvbGVzIjpbIjViMjdiZDk2NTg0MmY5NTBhNzc4YzZlZiJdLCJjb3VudHJ5R3JvdXAiOiJJTiIsInR5cGUiOiJVU0VSIn0sImlhdCI6MTcxODQ0ODcyMH0.PC0u4feVyT4WSzhBfpfPYB2YKwArJxYJER4R8c2gdD8"
12
  }
mainLogic/big4/cleanup.py CHANGED
@@ -1,37 +1,37 @@
1
- import os
2
- from mainLogic.utils.glv import Global
3
- class Clean:
4
-
5
- def removeFile(self,file,verbose):
6
- try:
7
- os.remove(file)
8
- if verbose: Global.sprint(f"Removed file: {file}")
9
- except:
10
- Global.errprint(f"Could not remove file: {file}")
11
-
12
- def remove(self,path,file,verbose=True):
13
-
14
- audio_enc = f"{path}/{file}-Audio-enc.mp4"
15
- video_enc = f"{path}/{file}-Video-enc.mp4"
16
-
17
- audio = f"{path}/{file}-Audio.mp4"
18
- video = f"{path}/{file}-Video.mp4"
19
-
20
- if verbose:
21
- Global.hr()
22
- Global.dprint("Removing TemporaryDL Files...")
23
- Global.hr()
24
-
25
- if verbose: Global.dprint("Removing Audio...")
26
- self.removeFile(audio_enc,verbose)
27
-
28
- if verbose: Global.dprint("Removing Video...")
29
- self.removeFile(video_enc,verbose)
30
-
31
- if verbose: Global.dprint("Removing Dncrypted Audio...")
32
- self.removeFile(audio,verbose)
33
-
34
- if verbose: Global.dprint("Removing Dncrypted Video...")
35
- self.removeFile(video,verbose)
36
-
37
-
 
1
+ import os
2
+ from mainLogic.utils.glv import Global
3
+ class Clean:
4
+
5
+ def removeFile(self,file,verbose):
6
+ try:
7
+ os.remove(file)
8
+ if verbose: Global.sprint(f"Removed file: {file}")
9
+ except:
10
+ Global.errprint(f"Could not remove file: {file}")
11
+
12
+ def remove(self,path,file,verbose=True):
13
+
14
+ audio_enc = f"{path}/{file}-Audio-enc.mp4"
15
+ video_enc = f"{path}/{file}-Video-enc.mp4"
16
+
17
+ audio = f"{path}/{file}-Audio.mp4"
18
+ video = f"{path}/{file}-Video.mp4"
19
+
20
+ if verbose:
21
+ Global.hr()
22
+ Global.dprint("Removing TemporaryDL Files...")
23
+ Global.hr()
24
+
25
+ if verbose: Global.dprint("Removing Audio...")
26
+ self.removeFile(audio_enc,verbose)
27
+
28
+ if verbose: Global.dprint("Removing Video...")
29
+ self.removeFile(video_enc,verbose)
30
+
31
+ if verbose: Global.dprint("Removing Dncrypted Audio...")
32
+ self.removeFile(audio,verbose)
33
+
34
+ if verbose: Global.dprint("Removing Dncrypted Video...")
35
+ self.removeFile(video,verbose)
36
+
37
+
mainLogic/big4/decrypt/decrypt.py CHANGED
@@ -1,55 +1,55 @@
1
- from mainLogic.utils.glv import Global
2
- from mainLogic.utils.process import shell
3
- from mainLogic.utils.basicUtils import BasicUtils
4
- from mainLogic import error
5
- import os
6
-
7
- class Decrypt:
8
-
9
- def decrypt(self,path,name,key,mp4d="mp4decrypt",out="None",outfile="",verbose=True,suppress_exit=False):
10
-
11
- Global.hr()
12
-
13
- # making path absolute if not already absolute
14
- path = BasicUtils.abspath(path)
15
- Global.dprint(f"Decrypting {out}...")
16
-
17
- # during various tests
18
- # it was found that the decrypted audio file is named as <name>.en.m4a
19
- # hence a simple logic to work around this issue is to check if the file exists
20
- # if not os.path.exists(f'{path}/{name}.m4a') and out == "Audio":
21
- # name = name + ".en"
22
-
23
- # setting extension based on out
24
- # i.e if out is Audio then extension is 'm4a' else 'mp4'
25
- # extension = "m4a" if out == "Audio" else "mp4"
26
- extension = "mp4" # temporary fix
27
-
28
- decrypt_command = f'{mp4d} --key 1:{key} {path}/{name}.{extension} {path}/{"" if not outfile else outfile+"-" }{out}.mp4'
29
-
30
- if verbose: Global.dprint(f"{out} Decryption Started..."); Global.dprint(f'{decrypt_command}')
31
-
32
-
33
-
34
- # the main part where the decryption happens
35
- code = shell(f'{decrypt_command}',stderr="",stdout="")
36
-
37
- # simple check to see if the decryption was successful or not
38
- if code == 0:
39
- Global.dprint(f"{out} Decrypted Successfully")
40
- else:
41
-
42
- # if decryption failed then print error message and exit
43
- error.errorList[f"couldNotDecrypt{out}"]["func"]()
44
- if not suppress_exit:
45
- exit(error.errorList[f"couldNotDecrypt{out}"]["code"])
46
-
47
-
48
-
49
- # decrypts audio
50
- def decryptAudio(self,path,name,key,mp4d="mp4decrypt",outfile='None',verbose=True,suppress_exit=False):
51
- self.decrypt(path,name,key,mp4d,"Audio",outfile,verbose,suppress_exit=suppress_exit)
52
-
53
- # decrypts video
54
- def decryptVideo(self,path,name,key,mp4d="mp4decrypt",outfile='None',verbose=True,suppress_exit=False):
55
  self.decrypt(path,name,key,mp4d,"Video",outfile,verbose,suppress_exit=suppress_exit)
 
1
+ from mainLogic.utils.glv import Global
2
+ from mainLogic.utils.process import shell
3
+ from mainLogic.utils.basicUtils import BasicUtils
4
+ from mainLogic import error
5
+ import os
6
+
7
+ class Decrypt:
8
+
9
+ def decrypt(self,path,name,key,mp4d="mp4decrypt",out="None",outfile="",verbose=True,suppress_exit=False):
10
+
11
+ Global.hr()
12
+
13
+ # making path absolute if not already absolute
14
+ path = BasicUtils.abspath(path)
15
+ Global.dprint(f"Decrypting {out}...")
16
+
17
+ # during various tests
18
+ # it was found that the decrypted audio file is named as <name>.en.m4a
19
+ # hence a simple logic to work around this issue is to check if the file exists
20
+ # if not os.path.exists(f'{path}/{name}.m4a') and out == "Audio":
21
+ # name = name + ".en"
22
+
23
+ # setting extension based on out
24
+ # i.e if out is Audio then extension is 'm4a' else 'mp4'
25
+ # extension = "m4a" if out == "Audio" else "mp4"
26
+ extension = "mp4" # temporary fix
27
+
28
+ decrypt_command = f'{mp4d} --key 1:{key} {path}/{name}.{extension} {path}/{"" if not outfile else outfile+"-" }{out}.mp4'
29
+
30
+ if verbose: Global.dprint(f"{out} Decryption Started..."); Global.dprint(f'{decrypt_command}')
31
+
32
+
33
+
34
+ # the main part where the decryption happens
35
+ code = shell(f'{decrypt_command}',stderr="",stdout="")
36
+
37
+ # simple check to see if the decryption was successful or not
38
+ if code == 0:
39
+ Global.dprint(f"{out} Decrypted Successfully")
40
+ else:
41
+
42
+ # if decryption failed then print error message and exit
43
+ error.errorList[f"couldNotDecrypt{out}"]["func"]()
44
+ if not suppress_exit:
45
+ exit(error.errorList[f"couldNotDecrypt{out}"]["code"])
46
+
47
+
48
+
49
+ # decrypts audio
50
+ def decryptAudio(self,path,name,key,mp4d="mp4decrypt",outfile='None',verbose=True,suppress_exit=False):
51
+ self.decrypt(path,name,key,mp4d,"Audio",outfile,verbose,suppress_exit=suppress_exit)
52
+
53
+ # decrypts video
54
+ def decryptVideo(self,path,name,key,mp4d="mp4decrypt",outfile='None',verbose=True,suppress_exit=False):
55
  self.decrypt(path,name,key,mp4d,"Video",outfile,verbose,suppress_exit=suppress_exit)
mainLogic/big4/decrypt/key.old.py CHANGED
@@ -1,86 +1,86 @@
1
- # import requests
2
- # import json
3
- # from bs4 import BeautifulSoup as BS
4
- # import re
5
- # from utils.keyUtils import base64_to_hex
6
- # from utils.glv import Global
7
- # import error
8
- #
9
- #
10
- # def log_info(id, verbose, attempt=0):
11
- # if verbose:
12
- # Global.dprint("Starting the script for key extraction" +( f" Retry: {attempt}" if attempt > 0 else ""))
13
- # Global.sprint(f'id -> {id}')
14
- # Global.sprint("Sending request to the server")
15
- # Global.dprint(f"Hardcoded URL: request.get -> http://studyrays.site/drmplayer.php?v=https://d1d34p8vz63oiq"
16
- # f".cloudfront.net/{id}/master.mpd")
17
- #
18
- #
19
- # def send_request(id):
20
- # try:
21
- #
22
- # import http.client
23
- #
24
- # conn = http.client.HTTPSConnection("api.scrapingant.com")
25
- #
26
- # conn.request("GET",
27
- # f"/v2/general?url=https%3A%2F%2Fstudyrays.site%2Fdrmplayer.php%3Fv%3Dhttps%3A%2F%2Fd1d34p8vz63oiq.cloudfront.net%2F{id}%2Fmaster.mpd&x-api-key=806b77b95dd643caae01d4e240da9159&proxy_type=residential&proxy_country=IN&browser=false")
28
- #
29
- # res = conn.getresponse()
30
- #
31
- # return res.read()
32
- #
33
- # except requests.exceptions.RequestException as e:
34
- # error.errorList["flareNotStarted"]["func"]()
35
- # exit(error.errorList["flareNotStarted"]["code"])
36
- #
37
- #
38
- # def parse_response(response):
39
- # try:
40
- #
41
- # return response.decode('utf-8')
42
- #
43
- # except (KeyError, json.JSONDecodeError):
44
- # error.errorList["requestFailedDueToUnknownReason"]["func"](response.status_code)
45
- # exit(error.errorList["requestFailedDueToUnknownReason"]["code"])
46
- #
47
- #
48
- # def extract_key(html):
49
- #
50
- # soup = BS(html, 'html.parser')
51
- # scripts = soup.find_all('script')
52
- #
53
- # for script in scripts:
54
- # script_content = script.text
55
- # if 'const protData' in script_content:
56
- # protData_script = script_content
57
- # break
58
- # else:
59
- # return None
60
- #
61
- # pattern = r'const\s+protData\s*=\s*({.*?});'
62
- # match = re.search(pattern, protData_script, re.DOTALL)
63
- #
64
- # if match:
65
- # protData_content = match.group(1)
66
- # keylist = json.loads(protData_content)['org.w3.clearkey']['clearkeys']
67
- # for kid in keylist:
68
- # return base64_to_hex(keylist[kid])
69
- # return None
70
- #
71
- # # main function
72
- # def getKey(id, verbose=True,retries=2):
73
- #
74
- # for attempt in range(retries):
75
- # log_info(id, verbose, attempt)
76
- #
77
- # response = send_request(id)
78
- # html = parse_response(response)
79
- #
80
- # key = extract_key(html)
81
- # if key:
82
- # return key
83
- # else:
84
- # if verbose:
85
- # Global.sprint("protData variable not found in the script. Retrying!")
86
  # return -1
 
1
+ # import requests
2
+ # import json
3
+ # from bs4 import BeautifulSoup as BS
4
+ # import re
5
+ # from utils.keyUtils import base64_to_hex
6
+ # from utils.glv import Global
7
+ # import error
8
+ #
9
+ #
10
+ # def log_info(id, verbose, attempt=0):
11
+ # if verbose:
12
+ # Global.dprint("Starting the script for key extraction" +( f" Retry: {attempt}" if attempt > 0 else ""))
13
+ # Global.sprint(f'id -> {id}')
14
+ # Global.sprint("Sending request to the server")
15
+ # Global.dprint(f"Hardcoded URL: request.get -> http://studyrays.site/drmplayer.php?v=https://d1d34p8vz63oiq"
16
+ # f".cloudfront.net/{id}/master.mpd")
17
+ #
18
+ #
19
+ # def send_request(id):
20
+ # try:
21
+ #
22
+ # import http.client
23
+ #
24
+ # conn = http.client.HTTPSConnection("api.scrapingant.com")
25
+ #
26
+ # conn.request("GET",
27
+ # f"/v2/general?url=https%3A%2F%2Fstudyrays.site%2Fdrmplayer.php%3Fv%3Dhttps%3A%2F%2Fd1d34p8vz63oiq.cloudfront.net%2F{id}%2Fmaster.mpd&x-api-key=806b77b95dd643caae01d4e240da9159&proxy_type=residential&proxy_country=IN&browser=false")
28
+ #
29
+ # res = conn.getresponse()
30
+ #
31
+ # return res.read()
32
+ #
33
+ # except requests.exceptions.RequestException as e:
34
+ # error.errorList["flareNotStarted"]["func"]()
35
+ # exit(error.errorList["flareNotStarted"]["code"])
36
+ #
37
+ #
38
+ # def parse_response(response):
39
+ # try:
40
+ #
41
+ # return response.decode('utf-8')
42
+ #
43
+ # except (KeyError, json.JSONDecodeError):
44
+ # error.errorList["requestFailedDueToUnknownReason"]["func"](response.status_code)
45
+ # exit(error.errorList["requestFailedDueToUnknownReason"]["code"])
46
+ #
47
+ #
48
+ # def extract_key(html):
49
+ #
50
+ # soup = BS(html, 'html.parser')
51
+ # scripts = soup.find_all('script')
52
+ #
53
+ # for script in scripts:
54
+ # script_content = script.text
55
+ # if 'const protData' in script_content:
56
+ # protData_script = script_content
57
+ # break
58
+ # else:
59
+ # return None
60
+ #
61
+ # pattern = r'const\s+protData\s*=\s*({.*?});'
62
+ # match = re.search(pattern, protData_script, re.DOTALL)
63
+ #
64
+ # if match:
65
+ # protData_content = match.group(1)
66
+ # keylist = json.loads(protData_content)['org.w3.clearkey']['clearkeys']
67
+ # for kid in keylist:
68
+ # return base64_to_hex(keylist[kid])
69
+ # return None
70
+ #
71
+ # # main function
72
+ # def getKey(id, verbose=True,retries=2):
73
+ #
74
+ # for attempt in range(retries):
75
+ # log_info(id, verbose, attempt)
76
+ #
77
+ # response = send_request(id)
78
+ # html = parse_response(response)
79
+ #
80
+ # key = extract_key(html)
81
+ # if key:
82
+ # return key
83
+ # else:
84
+ # if verbose:
85
+ # Global.sprint("protData variable not found in the script. Retrying!")
86
  # return -1
mainLogic/big4/decrypt/key.py CHANGED
@@ -1,138 +1,138 @@
1
- import requests
2
- import re
3
- import base64
4
- import json
5
- from mainLogic.big4.dl import DL
6
- from mainLogic.utils.glv import Global
7
-
8
- class LicenseKeyFetcher:
9
- def __init__(self, token):
10
- self.token = token
11
-
12
- def build_license_url(self, encoded_otp_key):
13
- return f"https://api.penpencil.co/v1/videos/get-otp?key={encoded_otp_key}&isEncoded=true"
14
-
15
- def get_headers(self):
16
- headers = {
17
- "accept": "*/*",
18
- "accept-language": "en-US,en;q=0.9,la;q=0.8",
19
- "authorization": f"Bearer {self.token}",
20
- "cache-control": "no-cache",
21
- "client-id": "5eb393ee95fab7468a79d189",
22
- "client-type": "WEB",
23
- "client-version": "200",
24
- "content-type": "application/json",
25
- "dnt": "1",
26
- "origin": "https://www.pw.live",
27
- "pragma": "no-cache",
28
- "priority": "u=1, i",
29
- "randomid": "180ff4c6-9ec3-4329-b1b5-1ad2f6746795",
30
- "referer": "https://www.pw.live/",
31
- "sec-ch-ua": "\"Google Chrome\";v=\"125\", \"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\"",
32
- "sec-ch-ua-mobile": "?0",
33
- "sec-ch-ua-platform": "\"Windows\"",
34
- "sec-fetch-dest": "empty",
35
- "sec-fetch-mode": "cors",
36
- "sec-fetch-site": "cross-site",
37
- "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
38
- }
39
- return headers
40
-
41
- def key_char_at(self, key, i):
42
- return ord(key[i % len(key)])
43
-
44
- def b64_encode(self, data):
45
- if not data:
46
- return data
47
- encoded = base64.b64encode(bytes(data)).decode('utf-8')
48
- return encoded
49
-
50
- def get_key_final(self, otp):
51
- decoded_bytes = base64.b64decode(otp)
52
- length = len(decoded_bytes)
53
- decoded_ints = [int(byte) for byte in decoded_bytes]
54
-
55
- result = "".join(
56
- chr(
57
- decoded_ints[i] ^ ord(self.token[i % len(self.token)])
58
- )
59
- for i in range(length)
60
- )
61
-
62
- return result
63
-
64
- def xor_encrypt(self, data):
65
- return [ord(c) ^ self.key_char_at(self.token, i) for i, c in enumerate(data)]
66
-
67
- def insert_zeros(self, hex_string):
68
- result = "00"
69
- for i in range(0, len(hex_string), 2):
70
- result += hex_string[i:i+2]
71
- if i + 2 < len(hex_string):
72
- result += "00"
73
- return result
74
-
75
- def extract_kid_from_mpd(self, url):
76
- response = requests.get(url)
77
- response.raise_for_status()
78
- mpd_content = response.text
79
- pattern = r'default_KID="([0-9a-fA-F-]+)"'
80
- match = re.search(pattern, mpd_content)
81
- return match.group(1) if match else None
82
-
83
- def get_key(self, id, verbose=True):
84
- if verbose: Global.hr()
85
-
86
- if verbose: Global.dprint("Beginning to get the key for the video... & Audio :) ")
87
- if verbose: Global.dprint(f"ID: {id}")
88
- if verbose: Global.dprint("Building the URL to get the key...")
89
-
90
- try:
91
- url = DL.buildUrl(id)
92
- if verbose: Global.sprint(f"URL: {url}")
93
-
94
- if verbose: Global.dprint("Extracting the KID from the MPD file...")
95
- kid = self.extract_kid_from_mpd(url).replace("-", "")
96
- if verbose: Global.sprint(f"KID: {kid}")
97
-
98
- if verbose: Global.dprint("Encrypting the KID to get the key...")
99
- otp_key = self.b64_encode(self.xor_encrypt(kid))
100
- if verbose: Global.sprint(f"OTP Key: {otp_key}")
101
-
102
- if verbose: Global.dprint("Encoding the OTP key to hex...")
103
- encoded_otp_key_step1 = otp_key.encode('utf-8').hex()
104
- encoded_otp_key = self.insert_zeros(encoded_otp_key_step1)
105
- if verbose: Global.sprint(f"Encoded OTP Key: {encoded_otp_key}")
106
-
107
- if verbose: Global.dprint("Building the license URL...")
108
- license_url = self.build_license_url(encoded_otp_key)
109
- if verbose: Global.sprint(f"License URL: {license_url}")
110
-
111
- if verbose: Global.dprint("Getting the headers...")
112
- headers = self.get_headers()
113
- if verbose: Global.sprint(f"Headers: {json.dumps(headers, indent=4)}")
114
-
115
- if verbose: Global.dprint("Making a request to the server to get the license (key)...")
116
- response = requests.get(license_url, headers=headers)
117
- if verbose: Global.sprint(f"Response: {response}")
118
-
119
- if response.status_code == 200:
120
- if 'data' in response.json() and 'otp' in response.json()['data']:
121
- if verbose: Global.sprint("Key received successfully!")
122
- key = self.get_key_final(response.json()['data']['otp'])
123
- if verbose: Global.sprint(f"Key: {key}")
124
-
125
- if verbose:Global.hr()
126
- return (kid,key)
127
- else:
128
- Global.errprint("Could not get the key from the server. Exiting...")
129
- return None
130
-
131
- except Exception as e:
132
- Global.errprint(f"An error occurred while getting the key: {e}")
133
- return None
134
-
135
- # Example usage
136
- # TOKEN = "your_token_here"
137
- # fetcher = LicenseKeyFetcher(TOKEN)
138
- # key = fetcher.get_key(video_id)
 
1
+ import requests
2
+ import re
3
+ import base64
4
+ import json
5
+ from mainLogic.big4.dl import DL
6
+ from mainLogic.utils.glv import Global
7
+
8
+ class LicenseKeyFetcher:
9
+ def __init__(self, token):
10
+ self.token = token
11
+
12
+ def build_license_url(self, encoded_otp_key):
13
+ return f"https://api.penpencil.co/v1/videos/get-otp?key={encoded_otp_key}&isEncoded=true"
14
+
15
+ def get_headers(self):
16
+ headers = {
17
+ "accept": "*/*",
18
+ "accept-language": "en-US,en;q=0.9,la;q=0.8",
19
+ "authorization": f"Bearer {self.token}",
20
+ "cache-control": "no-cache",
21
+ "client-id": "5eb393ee95fab7468a79d189",
22
+ "client-type": "WEB",
23
+ "client-version": "200",
24
+ "content-type": "application/json",
25
+ "dnt": "1",
26
+ "origin": "https://www.pw.live",
27
+ "pragma": "no-cache",
28
+ "priority": "u=1, i",
29
+ "randomid": "180ff4c6-9ec3-4329-b1b5-1ad2f6746795",
30
+ "referer": "https://www.pw.live/",
31
+ "sec-ch-ua": "\"Google Chrome\";v=\"125\", \"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\"",
32
+ "sec-ch-ua-mobile": "?0",
33
+ "sec-ch-ua-platform": "\"Windows\"",
34
+ "sec-fetch-dest": "empty",
35
+ "sec-fetch-mode": "cors",
36
+ "sec-fetch-site": "cross-site",
37
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
38
+ }
39
+ return headers
40
+
41
+ def key_char_at(self, key, i):
42
+ return ord(key[i % len(key)])
43
+
44
+ def b64_encode(self, data):
45
+ if not data:
46
+ return data
47
+ encoded = base64.b64encode(bytes(data)).decode('utf-8')
48
+ return encoded
49
+
50
+ def get_key_final(self, otp):
51
+ decoded_bytes = base64.b64decode(otp)
52
+ length = len(decoded_bytes)
53
+ decoded_ints = [int(byte) for byte in decoded_bytes]
54
+
55
+ result = "".join(
56
+ chr(
57
+ decoded_ints[i] ^ ord(self.token[i % len(self.token)])
58
+ )
59
+ for i in range(length)
60
+ )
61
+
62
+ return result
63
+
64
+ def xor_encrypt(self, data):
65
+ return [ord(c) ^ self.key_char_at(self.token, i) for i, c in enumerate(data)]
66
+
67
+ def insert_zeros(self, hex_string):
68
+ result = "00"
69
+ for i in range(0, len(hex_string), 2):
70
+ result += hex_string[i:i+2]
71
+ if i + 2 < len(hex_string):
72
+ result += "00"
73
+ return result
74
+
75
+ def extract_kid_from_mpd(self, url):
76
+ response = requests.get(url)
77
+ response.raise_for_status()
78
+ mpd_content = response.text
79
+ pattern = r'default_KID="([0-9a-fA-F-]+)"'
80
+ match = re.search(pattern, mpd_content)
81
+ return match.group(1) if match else None
82
+
83
+ def get_key(self, id, verbose=True):
84
+ if verbose: Global.hr()
85
+
86
+ if verbose: Global.dprint("Beginning to get the key for the video... & Audio :) ")
87
+ if verbose: Global.dprint(f"ID: {id}")
88
+ if verbose: Global.dprint("Building the URL to get the key...")
89
+
90
+ try:
91
+ url = DL.buildUrl(id)
92
+ if verbose: Global.sprint(f"URL: {url}")
93
+
94
+ if verbose: Global.dprint("Extracting the KID from the MPD file...")
95
+ kid = self.extract_kid_from_mpd(url).replace("-", "")
96
+ if verbose: Global.sprint(f"KID: {kid}")
97
+
98
+ if verbose: Global.dprint("Encrypting the KID to get the key...")
99
+ otp_key = self.b64_encode(self.xor_encrypt(kid))
100
+ if verbose: Global.sprint(f"OTP Key: {otp_key}")
101
+
102
+ if verbose: Global.dprint("Encoding the OTP key to hex...")
103
+ encoded_otp_key_step1 = otp_key.encode('utf-8').hex()
104
+ encoded_otp_key = self.insert_zeros(encoded_otp_key_step1)
105
+ if verbose: Global.sprint(f"Encoded OTP Key: {encoded_otp_key}")
106
+
107
+ if verbose: Global.dprint("Building the license URL...")
108
+ license_url = self.build_license_url(encoded_otp_key)
109
+ if verbose: Global.sprint(f"License URL: {license_url}")
110
+
111
+ if verbose: Global.dprint("Getting the headers...")
112
+ headers = self.get_headers()
113
+ if verbose: Global.sprint(f"Headers: {json.dumps(headers, indent=4)}")
114
+
115
+ if verbose: Global.dprint("Making a request to the server to get the license (key)...")
116
+ response = requests.get(license_url, headers=headers)
117
+ if verbose: Global.sprint(f"Response: {response}")
118
+
119
+ if response.status_code == 200:
120
+ if 'data' in response.json() and 'otp' in response.json()['data']:
121
+ if verbose: Global.sprint("Key received successfully!")
122
+ key = self.get_key_final(response.json()['data']['otp'])
123
+ if verbose: Global.sprint(f"Key: {key}")
124
+
125
+ if verbose:Global.hr()
126
+ return (kid,key)
127
+ else:
128
+ Global.errprint("Could not get the key from the server. Exiting...")
129
+ return None
130
+
131
+ except Exception as e:
132
+ Global.errprint(f"An error occurred while getting the key: {e}")
133
+ return None
134
+
135
+ # Example usage
136
+ # TOKEN = "your_token_here"
137
+ # fetcher = LicenseKeyFetcher(TOKEN)
138
+ # key = fetcher.get_key(video_id)
mainLogic/big4/dl.py CHANGED
@@ -1,175 +1,175 @@
1
- import re
2
-
3
- from mainLogic import error
4
- from mainLogic.utils.process import shell
5
- from mainLogic.utils.glv import Global
6
- from mainLogic.utils.basicUtils import BasicUtils
7
- class DL:
8
-
9
- @staticmethod
10
- def buildUrl(id):
11
- if id == None:
12
- error.errorList["idNotProvided"]["func"]()
13
- exit(error.errorList["idNotProvided"]["code"])
14
-
15
- return f"https://d1d34p8vz63oiq.cloudfront.net/{id}/master.mpd"
16
-
17
- def download(self,id,name=None,
18
- type="",
19
- directory="./",
20
- tmpDir="/*auto*/",
21
- nm3Path='nm3',
22
- ffmpeg='ffmpeg',
23
- verbose=True,
24
- progress_callback=None):
25
- if id == None:
26
- error.errorList["idNotProvided"]["func"]()
27
- exit(error.errorList["idNotProvided"]["code"])
28
-
29
- if name == None: name = id
30
-
31
-
32
- url = DL.buildUrl(id)
33
-
34
- # setting identifier and filter based on type
35
-
36
- # identifier is used to identify the type of file
37
- identifier = "a" if type == "Audio" else "v" if type == "Video" else "av"
38
-
39
- # filter is used to filter the output of the shell command
40
- filter = r"^Aud" if type == "Audio" else r"^Vid"
41
-
42
- # command to download the file
43
- command = f'{nm3Path} {url} --save-dir {directory} {"--tmp-dir "+tmpDir if not tmpDir == "/*auto*/" else "" } --save-name {name} -s{identifier} best'
44
- if verbose: Global.sprint(f"Command to download: {command}")
45
-
46
- # Download the audio file using the id
47
- code = shell(f'{command}',
48
- filter=filter,
49
- progress_callback=progress_callback,
50
- handleProgress=self.handleDownloadProgress,
51
- )
52
-
53
- if code == 0:
54
- return True
55
- else:
56
- error.errorList[f"couldNotDownload{type}"]["func"]()
57
- exit(error.errorList[f"couldNotDownload{type}"]["code"])
58
-
59
- def downloadAudioAndVideo(self,
60
- id,
61
- name=None,
62
- directory="./",
63
- tmpDir="/*auto*/",
64
- nm3Path='nm3',
65
- ffmpeg='ffmpeg',
66
- verbose=True,
67
- progress_callback=None):
68
- if id == None:
69
- error.errorList["idNotProvided"]["func"]()
70
- exit(error.errorList["idNotProvided"]["code"])
71
-
72
- if name == None: name = id; Global.dprint(f"Name not provided, using id as name: {name}")
73
-
74
- # removing limitations of relative path
75
- if not tmpDir == "/*auto*/": BasicUtils.abspath(tmpDir)
76
- directory = BasicUtils.abspath(directory)
77
-
78
- if verbose:
79
- Global.hr()
80
- Global.dprint(f"ID: {id}")
81
- Global.dprint(f"Name: {name}")
82
- Global.dprint(f"Directory: {directory}")
83
- Global.dprint(f"TmpDir: {tmpDir}")
84
- Global.dprint(f"Nm3Path: {nm3Path}")
85
- Global.hr()
86
- Global.dprint(f"Starting DL...")
87
-
88
- # section to download audio
89
- Global.hr(); Global.dprint("Downloading Audio..."); Global.hr()
90
- self.dlAudio(id,
91
- name,
92
- directory,
93
- tmpDir,
94
- nm3Path,
95
- verbose,
96
- progress_callback=progress_callback)
97
-
98
- # section to download video
99
- Global.hr(); Global.dprint("Downloading Video..."); Global.hr()
100
- self.dlVideo(id,
101
- name,
102
- directory,
103
- tmpDir,
104
- nm3Path,
105
- verbose,
106
- progress_callback=progress_callback)
107
-
108
- if progress_callback:
109
- progress_callback({
110
- "progress": 80,
111
- "str": "download-completed",
112
- "next": "decryption"
113
- })
114
-
115
- # return the paths of the downloaded files
116
- return [f"{directory}/{name}.mp4",f"{directory}/{name}.m4a"]
117
-
118
-
119
-
120
-
121
- def dlAudio(self,id,name=None,directory="./",tmpDir="/*auto*/",nm3Path='nm3',verbose=True,progress_callback=None):
122
- self.download(id,
123
- name,
124
- "Audio",
125
- directory,
126
- tmpDir,
127
- nm3Path,
128
- verbose=verbose,
129
- progress_callback=progress_callback)
130
-
131
- def dlVideo(self,id,name=None,directory="./",tmpDir="/*auto*/",nm3Path='nm3',verbose=True,progress_callback=None):
132
- self.download(id,
133
- name,
134
- "Video",
135
- directory,
136
- tmpDir,
137
- nm3Path,
138
- verbose=verbose,
139
- progress_callback=progress_callback)
140
-
141
-
142
- def handleDownloadProgress(self,output):
143
-
144
-
145
- progress = {
146
- "str": output,
147
- "dl-progress": 0,
148
- "progress": 0,
149
- "next": "Aud"
150
- }
151
-
152
- # formats the output to get the progress
153
- pattern = re.compile(r"[0-9][0-9][0-9]?%")
154
- progress_percent = pattern.findall(output)
155
-
156
- if progress_percent:
157
-
158
- progress["dl-progress"] = int(progress_percent[0].replace("%",""))
159
-
160
- if "Aud" in output:
161
- progress["progress"] = progress["dl-progress"] * 0.4
162
- progress["next"] = "Vid"
163
-
164
- if "Vid" in output:
165
- progress["progress"] = progress["dl-progress"] * 0.4 + 40
166
- progress["next"] = "decryption"
167
-
168
-
169
- return progress
170
-
171
-
172
-
173
-
174
-
175
-
 
1
+ import re
2
+
3
+ from mainLogic import error
4
+ from mainLogic.utils.process import shell
5
+ from mainLogic.utils.glv import Global
6
+ from mainLogic.utils.basicUtils import BasicUtils
7
+ class DL:
8
+
9
+ @staticmethod
10
+ def buildUrl(id):
11
+ if id == None:
12
+ error.errorList["idNotProvided"]["func"]()
13
+ exit(error.errorList["idNotProvided"]["code"])
14
+
15
+ return f"https://d1d34p8vz63oiq.cloudfront.net/{id}/master.mpd"
16
+
17
+ def download(self,id,name=None,
18
+ type="",
19
+ directory="./",
20
+ tmpDir="/*auto*/",
21
+ nm3Path='nm3',
22
+ ffmpeg='ffmpeg',
23
+ verbose=True,
24
+ progress_callback=None):
25
+ if id == None:
26
+ error.errorList["idNotProvided"]["func"]()
27
+ exit(error.errorList["idNotProvided"]["code"])
28
+
29
+ if name == None: name = id
30
+
31
+
32
+ url = DL.buildUrl(id)
33
+
34
+ # setting identifier and filter based on type
35
+
36
+ # identifier is used to identify the type of file
37
+ identifier = "a" if type == "Audio" else "v" if type == "Video" else "av"
38
+
39
+ # filter is used to filter the output of the shell command
40
+ filter = r"^Aud" if type == "Audio" else r"^Vid"
41
+
42
+ # command to download the file
43
+ command = f'{nm3Path} {url} --save-dir {directory} {"--tmp-dir "+tmpDir if not tmpDir == "/*auto*/" else "" } --save-name {name} -s{identifier} best'
44
+ if verbose: Global.sprint(f"Command to download: {command}")
45
+
46
+ # Download the audio file using the id
47
+ code = shell(f'{command}',
48
+ filter=filter,
49
+ progress_callback=progress_callback,
50
+ handleProgress=self.handleDownloadProgress,
51
+ )
52
+
53
+ if code == 0:
54
+ return True
55
+ else:
56
+ error.errorList[f"couldNotDownload{type}"]["func"]()
57
+ exit(error.errorList[f"couldNotDownload{type}"]["code"])
58
+
59
+ def downloadAudioAndVideo(self,
60
+ id,
61
+ name=None,
62
+ directory="./",
63
+ tmpDir="/*auto*/",
64
+ nm3Path='nm3',
65
+ ffmpeg='ffmpeg',
66
+ verbose=True,
67
+ progress_callback=None):
68
+ if id == None:
69
+ error.errorList["idNotProvided"]["func"]()
70
+ exit(error.errorList["idNotProvided"]["code"])
71
+
72
+ if name == None: name = id; Global.dprint(f"Name not provided, using id as name: {name}")
73
+
74
+ # removing limitations of relative path
75
+ if not tmpDir == "/*auto*/": BasicUtils.abspath(tmpDir)
76
+ directory = BasicUtils.abspath(directory)
77
+
78
+ if verbose:
79
+ Global.hr()
80
+ Global.dprint(f"ID: {id}")
81
+ Global.dprint(f"Name: {name}")
82
+ Global.dprint(f"Directory: {directory}")
83
+ Global.dprint(f"TmpDir: {tmpDir}")
84
+ Global.dprint(f"Nm3Path: {nm3Path}")
85
+ Global.hr()
86
+ Global.dprint(f"Starting DL...")
87
+
88
+ # section to download audio
89
+ Global.hr(); Global.dprint("Downloading Audio..."); Global.hr()
90
+ self.dlAudio(id,
91
+ name,
92
+ directory,
93
+ tmpDir,
94
+ nm3Path,
95
+ verbose,
96
+ progress_callback=progress_callback)
97
+
98
+ # section to download video
99
+ Global.hr(); Global.dprint("Downloading Video..."); Global.hr()
100
+ self.dlVideo(id,
101
+ name,
102
+ directory,
103
+ tmpDir,
104
+ nm3Path,
105
+ verbose,
106
+ progress_callback=progress_callback)
107
+
108
+ if progress_callback:
109
+ progress_callback({
110
+ "progress": 80,
111
+ "str": "download-completed",
112
+ "next": "decryption"
113
+ })
114
+
115
+ # return the paths of the downloaded files
116
+ return [f"{directory}/{name}.mp4",f"{directory}/{name}.m4a"]
117
+
118
+
119
+
120
+
121
+ def dlAudio(self,id,name=None,directory="./",tmpDir="/*auto*/",nm3Path='nm3',verbose=True,progress_callback=None):
122
+ self.download(id,
123
+ name,
124
+ "Audio",
125
+ directory,
126
+ tmpDir,
127
+ nm3Path,
128
+ verbose=verbose,
129
+ progress_callback=progress_callback)
130
+
131
+ def dlVideo(self,id,name=None,directory="./",tmpDir="/*auto*/",nm3Path='nm3',verbose=True,progress_callback=None):
132
+ self.download(id,
133
+ name,
134
+ "Video",
135
+ directory,
136
+ tmpDir,
137
+ nm3Path,
138
+ verbose=verbose,
139
+ progress_callback=progress_callback)
140
+
141
+
142
+ def handleDownloadProgress(self,output):
143
+
144
+
145
+ progress = {
146
+ "str": output,
147
+ "dl-progress": 0,
148
+ "progress": 0,
149
+ "next": "Aud"
150
+ }
151
+
152
+ # formats the output to get the progress
153
+ pattern = re.compile(r"[0-9][0-9][0-9]?%")
154
+ progress_percent = pattern.findall(output)
155
+
156
+ if progress_percent:
157
+
158
+ progress["dl-progress"] = int(progress_percent[0].replace("%",""))
159
+
160
+ if "Aud" in output:
161
+ progress["progress"] = progress["dl-progress"] * 0.4
162
+ progress["next"] = "Vid"
163
+
164
+ if "Vid" in output:
165
+ progress["progress"] = progress["dl-progress"] * 0.4 + 40
166
+ progress["next"] = "decryption"
167
+
168
+
169
+ return progress
170
+
171
+
172
+
173
+
174
+
175
+
mainLogic/big4/downloadv2.py CHANGED
@@ -1,95 +1,95 @@
1
- import re
2
-
3
- from mainLogic import error
4
- from mainLogic.utils.process import shell
5
- from mainLogic.utils.glv import Global
6
- class Download:
7
-
8
- @staticmethod
9
- def buildUrl(id):
10
- if id == None:
11
- error.errorList["idNotProvided"]["func"]()
12
- exit(error.errorList["idNotProvided"]["code"])
13
-
14
- return f"https://d1d34p8vz63oiq.cloudfront.net/{id}/master.mpd"
15
- def __init__(self,
16
- vsd_path,
17
- url,
18
- name=None,
19
- tmp_path="./",
20
- output_path="./",
21
- progress_callback=None
22
- ):
23
- self.vsd_path = vsd_path
24
- self.url = url
25
- self.name = name
26
- self.tmp_path = tmp_path + '/' + name
27
- self.output_path = output_path
28
- self.progress_callback = progress_callback
29
-
30
- def perform_cleanup(self):
31
-
32
- import shutil
33
-
34
- shutil.move(self.tmp_path + "/vsd_audio_master_mpd.mp4", self.output_path + '/' + self.name + "-Video-enc.mp4")
35
- shutil.move(self.tmp_path + "/vsd_video_master_mpd.mp4", self.output_path + '/' + self.name + "-Audio-enc.mp4")
36
-
37
- try:
38
- shutil.rmtree(self.tmp_path)
39
- except Exception as e:
40
- Global.errprint(f"Could not remove tmp directory : {e}")
41
-
42
- return (self.output_path + '/' + self.name + "-Video-enc.mp4", self.output_path + '/' +self.name + "-Audio-enc.mp4")
43
-
44
-
45
-
46
- def download(self):
47
- """
48
- Download the video file from the given URL and save it to the output path.
49
- """
50
- # Download the video file
51
- # Save the video file to the output path
52
- shell(
53
- f"{self.vsd_path} " +
54
- "save " +
55
- f"{self.url} " +
56
- "--skip-prompts " +
57
- "-t 16 " +
58
- "--no-decrypt " +
59
- " --raw-prompts " +
60
- f"-d {self.tmp_path}",
61
- filter=r"^\d+\.\d+ / \d+ MiB [╸━]+ \d+(\.\d+)?% •\s+\d+/\d+ • \d+:\d+ > \d+:\d+ • \d+(\.\d+)? SEG/s • .+$",
62
- progress_callback=self.progress_callback,
63
- handleProgress=self.handleDownloadProgress,
64
- inline_progress=True,
65
-
66
-
67
-
68
- )
69
-
70
- return self.perform_cleanup()
71
-
72
- def handleDownloadProgress(self,progress):
73
- """
74
- Handle the progress of the download process.
75
- """
76
-
77
- # extract percentage from the progress string (using) regex
78
- # and convert it to float
79
-
80
- output = {
81
- "str": progress,
82
-
83
- }
84
-
85
- pattern = r"(\d+(\.\d+)?)%"
86
- match = re.search(pattern, progress)
87
- if match:
88
- progress_f = float(match.group(1)) * 0.8
89
- else:
90
- progress_f = 0.0
91
-
92
- output["progress"] = progress_f
93
-
94
-
95
  return output
 
1
+ import re
2
+
3
+ from mainLogic import error
4
+ from mainLogic.utils.process import shell
5
+ from mainLogic.utils.glv import Global
6
+ class Download:
7
+
8
+ @staticmethod
9
+ def buildUrl(id):
10
+ if id == None:
11
+ error.errorList["idNotProvided"]["func"]()
12
+ exit(error.errorList["idNotProvided"]["code"])
13
+
14
+ return f"https://d1d34p8vz63oiq.cloudfront.net/{id}/master.mpd"
15
+ def __init__(self,
16
+ vsd_path,
17
+ url,
18
+ name=None,
19
+ tmp_path="./",
20
+ output_path="./",
21
+ progress_callback=None
22
+ ):
23
+ self.vsd_path = vsd_path
24
+ self.url = url
25
+ self.name = name
26
+ self.tmp_path = tmp_path + '/' + name
27
+ self.output_path = output_path
28
+ self.progress_callback = progress_callback
29
+
30
+ def perform_cleanup(self):
31
+
32
+ import shutil
33
+
34
+ shutil.move(self.tmp_path + "/vsd_audio_master_mpd.mp4", self.output_path + '/' + self.name + "-Video-enc.mp4")
35
+ shutil.move(self.tmp_path + "/vsd_video_master_mpd.mp4", self.output_path + '/' + self.name + "-Audio-enc.mp4")
36
+
37
+ try:
38
+ shutil.rmtree(self.tmp_path)
39
+ except Exception as e:
40
+ Global.errprint(f"Could not remove tmp directory : {e}")
41
+
42
+ return (self.output_path + '/' + self.name + "-Video-enc.mp4", self.output_path + '/' +self.name + "-Audio-enc.mp4")
43
+
44
+
45
+
46
+ def download(self):
47
+ """
48
+ Download the video file from the given URL and save it to the output path.
49
+ """
50
+ # Download the video file
51
+ # Save the video file to the output path
52
+ shell(
53
+ f"{self.vsd_path} " +
54
+ "save " +
55
+ f"{self.url} " +
56
+ "--skip-prompts " +
57
+ "-t 16 " +
58
+ "--no-decrypt " +
59
+ " --raw-prompts " +
60
+ f"-d {self.tmp_path}",
61
+ filter=r"^\d+\.\d+ / \d+ MiB [╸━]+ \d+(\.\d+)?% •\s+\d+/\d+ • \d+:\d+ > \d+:\d+ • \d+(\.\d+)? SEG/s • .+$",
62
+ progress_callback=self.progress_callback,
63
+ handleProgress=self.handleDownloadProgress,
64
+ inline_progress=True,
65
+
66
+
67
+
68
+ )
69
+
70
+ return self.perform_cleanup()
71
+
72
+ def handleDownloadProgress(self,progress):
73
+ """
74
+ Handle the progress of the download process.
75
+ """
76
+
77
+ # extract percentage from the progress string (using) regex
78
+ # and convert it to float
79
+
80
+ output = {
81
+ "str": progress,
82
+
83
+ }
84
+
85
+ pattern = r"(\d+(\.\d+)?)%"
86
+ match = re.search(pattern, progress)
87
+ if match:
88
+ progress_f = float(match.group(1)) * 0.8
89
+ else:
90
+ progress_f = 0.0
91
+
92
+ output["progress"] = progress_f
93
+
94
+
95
  return output
mainLogic/big4/merge.py CHANGED
@@ -1,30 +1,30 @@
1
- import os
2
- from mainLogic.error import errorList
3
- from mainLogic.utils.process import shell
4
- from mainLogic.utils.glv import Global
5
- class Merge:
6
- def ffmpegMerge(self,input1,input2,output,ffmpeg_path="ffmpeg",verbose=False):
7
-
8
-
9
- if verbose: Global.hr();Global.dprint('Attempting ffmpeg merge')
10
- if verbose: Global.dprint(f'{ffmpeg_path} -i {input1} -i {input2} -c copy {output}')
11
-
12
- if verbose:
13
-
14
- shell(f'{ffmpeg_path} -i {input1} -i {input2} -c copy {output}')
15
-
16
- else:
17
-
18
- if os.path.exists(output):
19
-
20
- Global.errprint("Warninbg: Output file already exists. Overwriting...")
21
- consent = input("Do you want to continue? (y/n): ")
22
-
23
- if consent.lower() != 'y':
24
- errorList['overWriteAbortedByUser']['func']()
25
- exit(errorList['overWriteAbortedByUser']['code'])
26
-
27
- else:
28
- shell(f'{ffmpeg_path} -y -i {input1} -i {input2} -c copy {output}',stderr="",stdout="")
29
-
30
  return output
 
1
+ import os
2
+ from mainLogic.error import errorList
3
+ from mainLogic.utils.process import shell
4
+ from mainLogic.utils.glv import Global
5
+ class Merge:
6
+ def ffmpegMerge(self,input1,input2,output,ffmpeg_path="ffmpeg",verbose=False):
7
+
8
+
9
+ if verbose: Global.hr();Global.dprint('Attempting ffmpeg merge')
10
+ if verbose: Global.dprint(f'{ffmpeg_path} -i {input1} -i {input2} -c copy {output}')
11
+
12
+ if verbose:
13
+
14
+ shell(f'{ffmpeg_path} -i {input1} -i {input2} -c copy {output}')
15
+
16
+ else:
17
+
18
+ if os.path.exists(output):
19
+
20
+ Global.errprint("Warninbg: Output file already exists. Overwriting...")
21
+ consent = input("Do you want to continue? (y/n): ")
22
+
23
+ if consent.lower() != 'y':
24
+ errorList['overWriteAbortedByUser']['func']()
25
+ exit(errorList['overWriteAbortedByUser']['code'])
26
+
27
+ else:
28
+ shell(f'{ffmpeg_path} -y -i {input1} -i {input2} -c copy {output}',stderr="",stdout="")
29
+
30
  return output
mainLogic/error.py CHANGED
@@ -1,86 +1,86 @@
1
- from mainLogic.utils.glv import Global
2
-
3
- errorList = {
4
- "noError": {
5
- "code": 0,
6
- "func": lambda: None,
7
- },
8
- "defaultsNotFound" : {
9
- "code": 1,
10
- "func": lambda: Global.errprint("defaults.json not found. Exiting..."),
11
- },
12
- "dependencyNotFound": {
13
- "code": 2,
14
- "func": lambda x=None: Global.errprint(f"{'Dependency' if x == None else x } not found. Exiting..."),
15
- },
16
- "dependencyNotFoundInPrefs":
17
- {
18
- "code": 3,
19
- "func": lambda x=None: Global.errprint(f"{'Dependency' if x == None else x } not found in default settings. Exiting..."),
20
- },
21
- "csvFileNotFound": {
22
- "code": 4,
23
- "func": lambda fileName: Global.errprint(f"CSV file {fileName} not found. Exiting..."),
24
- },
25
- "downloadFailed": {
26
- "code": 5,
27
- "func": lambda name, id: Global.errprint(f"Download failed for {name} with id {id}. (Main.process exited) Exiting..."),
28
- },
29
- "couldNotMakeDir":{
30
- "code": 6,
31
- "func": lambda dirName: Global.errprint(f"Could not make directory {dirName}. Exiting..."),
32
- },
33
- "tokenNotFound": {
34
- "code": 7,
35
- "func": lambda: Global.errprint("Token not found in default settings. Exiting..."),
36
- },
37
- "overWriteAbortedByUser": {
38
- "code": 8,
39
- "func": lambda: Global.errprint("Overwrite aborted by user. Exiting..."),
40
- },
41
- "cantLoadFile": {
42
- "code": 22,
43
- "func": lambda fileName: Global.errprint(f"Can't load file {fileName}"),
44
- },
45
- "flareNotStarted": {
46
- "code": 23,
47
- "func": lambda: Global.errprint("Flare is not started. Start the flare server first.")
48
- },
49
- "requestFailedDueToUnknownReason": {
50
- "code": 24,
51
- "func": lambda status_code: Global.errprint("Request failed due to unknown reason. Status Code: " + str(status_code))
52
- },
53
- "keyExtractionFailed": {
54
- "code": 25,
55
- "func": lambda id: Global.errprint(f"Key extraction failed for id -> {id}. Exiting...")
56
- },
57
- "keyNotProvided": {
58
- "code": 26,
59
- "func": lambda: Global.errprint("Key not provided. Exiting...")
60
- },
61
- "couldNotDownloadAudio": {
62
- "code": 27,
63
- "func": lambda id: Global.errprint(f"Could not download audio for id -> {id} Exiting...")
64
- },
65
- "couldNotDownloadVideo": {
66
- "code": 28,
67
- "func": lambda: Global.errprint(f"Could not download video for {id} Exiting...")
68
- },
69
- "couldNotDecryptAudio": {
70
- "code": 29,
71
- "func": lambda: Global.errprint("Could not decrypt audio. Exiting...")
72
- },
73
- "couldNotDecryptVideo": {
74
- "code": 30,
75
- "func": lambda: Global.errprint("Could not decrypt video. Exiting...")
76
- },
77
- "methodPatched": {
78
- "code": 31,
79
- "func": lambda: Global.errprint("Method is patched. Exiting...")
80
- },
81
- "couldNotExtractKey": {
82
- "code": 32,
83
- "func": lambda: Global.errprint("Could not extract key. Exiting...")
84
- },
85
- }
86
-
 
1
+ from mainLogic.utils.glv import Global
2
+
3
+ errorList = {
4
+ "noError": {
5
+ "code": 0,
6
+ "func": lambda: None,
7
+ },
8
+ "defaultsNotFound" : {
9
+ "code": 1,
10
+ "func": lambda: Global.errprint("defaults.json not found. Exiting..."),
11
+ },
12
+ "dependencyNotFound": {
13
+ "code": 2,
14
+ "func": lambda x=None: Global.errprint(f"{'Dependency' if x == None else x } not found. Exiting..."),
15
+ },
16
+ "dependencyNotFoundInPrefs":
17
+ {
18
+ "code": 3,
19
+ "func": lambda x=None: Global.errprint(f"{'Dependency' if x == None else x } not found in default settings. Exiting..."),
20
+ },
21
+ "csvFileNotFound": {
22
+ "code": 4,
23
+ "func": lambda fileName: Global.errprint(f"CSV file {fileName} not found. Exiting..."),
24
+ },
25
+ "downloadFailed": {
26
+ "code": 5,
27
+ "func": lambda name, id: Global.errprint(f"Download failed for {name} with id {id}. (Main.process exited) Exiting..."),
28
+ },
29
+ "couldNotMakeDir":{
30
+ "code": 6,
31
+ "func": lambda dirName: Global.errprint(f"Could not make directory {dirName}. Exiting..."),
32
+ },
33
+ "tokenNotFound": {
34
+ "code": 7,
35
+ "func": lambda: Global.errprint("Token not found in default settings. Exiting..."),
36
+ },
37
+ "overWriteAbortedByUser": {
38
+ "code": 8,
39
+ "func": lambda: Global.errprint("Overwrite aborted by user. Exiting..."),
40
+ },
41
+ "cantLoadFile": {
42
+ "code": 22,
43
+ "func": lambda fileName: Global.errprint(f"Can't load file {fileName}"),
44
+ },
45
+ "flareNotStarted": {
46
+ "code": 23,
47
+ "func": lambda: Global.errprint("Flare is not started. Start the flare server first.")
48
+ },
49
+ "requestFailedDueToUnknownReason": {
50
+ "code": 24,
51
+ "func": lambda status_code: Global.errprint("Request failed due to unknown reason. Status Code: " + str(status_code))
52
+ },
53
+ "keyExtractionFailed": {
54
+ "code": 25,
55
+ "func": lambda id: Global.errprint(f"Key extraction failed for id -> {id}. Exiting...")
56
+ },
57
+ "keyNotProvided": {
58
+ "code": 26,
59
+ "func": lambda: Global.errprint("Key not provided. Exiting...")
60
+ },
61
+ "couldNotDownloadAudio": {
62
+ "code": 27,
63
+ "func": lambda id: Global.errprint(f"Could not download audio for id -> {id} Exiting...")
64
+ },
65
+ "couldNotDownloadVideo": {
66
+ "code": 28,
67
+ "func": lambda: Global.errprint(f"Could not download video for {id} Exiting...")
68
+ },
69
+ "couldNotDecryptAudio": {
70
+ "code": 29,
71
+ "func": lambda: Global.errprint("Could not decrypt audio. Exiting...")
72
+ },
73
+ "couldNotDecryptVideo": {
74
+ "code": 30,
75
+ "func": lambda: Global.errprint("Could not decrypt video. Exiting...")
76
+ },
77
+ "methodPatched": {
78
+ "code": 31,
79
+ "func": lambda: Global.errprint("Method is patched. Exiting...")
80
+ },
81
+ "couldNotExtractKey": {
82
+ "code": 32,
83
+ "func": lambda: Global.errprint("Could not extract key. Exiting...")
84
+ },
85
+ }
86
+
mainLogic/main.py CHANGED
@@ -1,126 +1,126 @@
1
- from mainLogic.utils.basicUtils import BasicUtils
2
- from mainLogic.utils.os2 import SysFunc
3
- from mainLogic.utils.glv import Global
4
- from mainLogic.big4.cleanup import Clean
5
- from mainLogic.big4.decrypt.key import LicenseKeyFetcher
6
- from mainLogic.big4.downloadv2 import Download
7
- from mainLogic.big4.decrypt.decrypt import Decrypt
8
- from mainLogic.big4.merge import Merge
9
- import os
10
-
11
-
12
- class Main:
13
- """
14
- Main class to handle the processing of video and audio files including download,
15
- decryption, merging, and cleanup.
16
-
17
- Attributes:
18
- id (str): Identifier for the process.
19
- name (str): Name for the process. Defaults to the value of `id`.
20
- directory (str): Directory to store the files. Defaults to "./".
21
- tmpDir (str): Temporary directory for intermediate files. Defaults to './tmp/'.
22
- vsdPath (str): Path to the vsd binary. Defaults to 'vsd'.
23
- ffmpeg (str): Path to the ffmpeg binary. Defaults to 'ffmpeg'.
24
- mp4d (str): Path to the mp4decrypt binary. Defaults to 'mp4decrypt'.
25
- token (str): Auth Token for the process.
26
- verbose (bool): Flag for verbose output. Defaults to True.
27
- suppress_exit (bool): Flag to suppress exit on error. Defaults to False.
28
- progress_callback (function): Callback function to report progress. Defaults to None.
29
- """
30
-
31
- def __init__(self,
32
- id,
33
- name=None,
34
- directory="./",
35
- tmpDir="/*auto*/",
36
- vsdPath='nm3',
37
- ffmpeg="ffmpeg",
38
- mp4d="mp4decrypt",
39
- token=None, verbose=True, suppress_exit=False, progress_callback=None):
40
-
41
- os2 = SysFunc()
42
-
43
- self.id = id
44
- self.name = name if name else id
45
- self.directory = directory
46
- self.tmpDir = BasicUtils.abspath(tmpDir) if tmpDir != '/*auto*/' else BasicUtils.abspath('./tmp/')
47
-
48
- # Create tmp directory if it does not exist
49
- os2.create_dir(self.tmpDir, verbose=verbose)
50
-
51
- self.vsd = vsdPath if vsdPath != 'vsd' else 'vsd'
52
- self.ffmpeg = BasicUtils.abspath(ffmpeg) if ffmpeg != 'ffmpeg' else 'ffmpeg'
53
- self.mp4d = BasicUtils.abspath(mp4d) if mp4d != 'mp4decrypt' else 'mp4decrypt'
54
- self.token = token
55
- self.verbose = verbose
56
- self.suppress_exit = suppress_exit
57
- self.progress_callback = progress_callback
58
-
59
- def process(self):
60
- """
61
- Main processing function to handle downloading, decrypting, merging, and cleanup of files.
62
- """
63
-
64
- if self.verbose:
65
- Global.dprint("Starting Main Process... for ID: " + self.id)
66
-
67
- # 1. Downloading Files (New Download Method using VSD)
68
-
69
- audio, video = Download(self.vsd,
70
- Download.buildUrl(self.id),
71
- self.name,
72
- self.tmpDir,
73
- self.directory,
74
- progress_callback=self.progress_callback).download()
75
- Global.sprint("\nDownload completed.")
76
- if self.verbose: Global.sprint(f"Audio: {audio}\nVideo: {video}")
77
-
78
- # 2. Decrypting Files
79
-
80
- Global.sprint("Please wait while we decrypt the files...\nFetching key may take some time.")
81
-
82
- TOKEN = self.token
83
- fetcher = LicenseKeyFetcher(TOKEN)
84
- key = fetcher.get_key(self.id, verbose=self.verbose)[1]
85
-
86
- decrypt = Decrypt()
87
-
88
- decrypt.decryptAudio(self.directory, f'{self.name}-Audio-enc', key, mp4d=self.mp4d, outfile=self.name,
89
- verbose=self.verbose, suppress_exit=self.suppress_exit)
90
- decrypt.decryptVideo(self.directory, f'{self.name}-Video-enc', key, mp4d=self.mp4d, outfile=self.name,
91
- verbose=self.verbose, suppress_exit=self.suppress_exit)
92
-
93
- # Call the progress callback for decryption completion
94
- if self.progress_callback:
95
- self.progress_callback({
96
- "progress": 90,
97
- "str": "decryption-completed",
98
- "next": "merging"
99
- })
100
-
101
- # 3. Merging Files
102
-
103
- merge = Merge()
104
- merge.ffmpegMerge(f"{self.directory}/{self.name}-Video.mp4",
105
- f"{self.directory}/{self.name}-Audio.mp4",
106
- f"{self.directory}/{self.name}.mp4",
107
- ffmpeg_path=self.ffmpeg, verbose=self.verbose)
108
-
109
- # Call the progress callback for merge completion
110
- if self.progress_callback:
111
- self.progress_callback({
112
- "progress": 99,
113
- "str": "merge-completed",
114
- "next": "cleanup"
115
- })
116
-
117
- # 4. Cleanup
118
- clean = Clean()
119
- clean.remove(self.directory, f'{self.name}', self.verbose)
120
-
121
- if self.progress_callback:
122
- self.progress_callback({
123
- "progress": 100,
124
- "str": "cleanup-completed",
125
- "next": "done"
126
- })
 
1
+ from mainLogic.utils.basicUtils import BasicUtils
2
+ from mainLogic.utils.os2 import SysFunc
3
+ from mainLogic.utils.glv import Global
4
+ from mainLogic.big4.cleanup import Clean
5
+ from mainLogic.big4.decrypt.key import LicenseKeyFetcher
6
+ from mainLogic.big4.downloadv2 import Download
7
+ from mainLogic.big4.decrypt.decrypt import Decrypt
8
+ from mainLogic.big4.merge import Merge
9
+ import os
10
+
11
+
12
+ class Main:
13
+ """
14
+ Main class to handle the processing of video and audio files including download,
15
+ decryption, merging, and cleanup.
16
+
17
+ Attributes:
18
+ id (str): Identifier for the process.
19
+ name (str): Name for the process. Defaults to the value of `id`.
20
+ directory (str): Directory to store the files. Defaults to "./".
21
+ tmpDir (str): Temporary directory for intermediate files. Defaults to './tmp/'.
22
+ vsdPath (str): Path to the vsd binary. Defaults to 'vsd'.
23
+ ffmpeg (str): Path to the ffmpeg binary. Defaults to 'ffmpeg'.
24
+ mp4d (str): Path to the mp4decrypt binary. Defaults to 'mp4decrypt'.
25
+ token (str): Auth Token for the process.
26
+ verbose (bool): Flag for verbose output. Defaults to True.
27
+ suppress_exit (bool): Flag to suppress exit on error. Defaults to False.
28
+ progress_callback (function): Callback function to report progress. Defaults to None.
29
+ """
30
+
31
+ def __init__(self,
32
+ id,
33
+ name=None,
34
+ directory="./",
35
+ tmpDir="/*auto*/",
36
+ vsdPath='nm3',
37
+ ffmpeg="ffmpeg",
38
+ mp4d="mp4decrypt",
39
+ token=None, verbose=True, suppress_exit=False, progress_callback=None):
40
+
41
+ os2 = SysFunc()
42
+
43
+ self.id = id
44
+ self.name = name if name else id
45
+ self.directory = directory
46
+ self.tmpDir = BasicUtils.abspath(tmpDir) if tmpDir != '/*auto*/' else BasicUtils.abspath('./tmp/')
47
+
48
+ # Create tmp directory if it does not exist
49
+ os2.create_dir(self.tmpDir, verbose=verbose)
50
+
51
+ self.vsd = vsdPath if vsdPath != 'vsd' else 'vsd'
52
+ self.ffmpeg = BasicUtils.abspath(ffmpeg) if ffmpeg != 'ffmpeg' else 'ffmpeg'
53
+ self.mp4d = BasicUtils.abspath(mp4d) if mp4d != 'mp4decrypt' else 'mp4decrypt'
54
+ self.token = token
55
+ self.verbose = verbose
56
+ self.suppress_exit = suppress_exit
57
+ self.progress_callback = progress_callback
58
+
59
+ def process(self):
60
+ """
61
+ Main processing function to handle downloading, decrypting, merging, and cleanup of files.
62
+ """
63
+
64
+ if self.verbose:
65
+ Global.dprint("Starting Main Process... for ID: " + self.id)
66
+
67
+ # 1. Downloading Files (New Download Method using VSD)
68
+
69
+ audio, video = Download(self.vsd,
70
+ Download.buildUrl(self.id),
71
+ self.name,
72
+ self.tmpDir,
73
+ self.directory,
74
+ progress_callback=self.progress_callback).download()
75
+ Global.sprint("\nDownload completed.")
76
+ if self.verbose: Global.sprint(f"Audio: {audio}\nVideo: {video}")
77
+
78
+ # 2. Decrypting Files
79
+
80
+ Global.sprint("Please wait while we decrypt the files...\nFetching key may take some time.")
81
+
82
+ TOKEN = self.token
83
+ fetcher = LicenseKeyFetcher(TOKEN)
84
+ key = fetcher.get_key(self.id, verbose=self.verbose)[1]
85
+
86
+ decrypt = Decrypt()
87
+
88
+ decrypt.decryptAudio(self.directory, f'{self.name}-Audio-enc', key, mp4d=self.mp4d, outfile=self.name,
89
+ verbose=self.verbose, suppress_exit=self.suppress_exit)
90
+ decrypt.decryptVideo(self.directory, f'{self.name}-Video-enc', key, mp4d=self.mp4d, outfile=self.name,
91
+ verbose=self.verbose, suppress_exit=self.suppress_exit)
92
+
93
+ # Call the progress callback for decryption completion
94
+ if self.progress_callback:
95
+ self.progress_callback({
96
+ "progress": 90,
97
+ "str": "decryption-completed",
98
+ "next": "merging"
99
+ })
100
+
101
+ # 3. Merging Files
102
+
103
+ merge = Merge()
104
+ merge.ffmpegMerge(f"{self.directory}/{self.name}-Video.mp4",
105
+ f"{self.directory}/{self.name}-Audio.mp4",
106
+ f"{self.directory}/{self.name}.mp4",
107
+ ffmpeg_path=self.ffmpeg, verbose=self.verbose)
108
+
109
+ # Call the progress callback for merge completion
110
+ if self.progress_callback:
111
+ self.progress_callback({
112
+ "progress": 99,
113
+ "str": "merge-completed",
114
+ "next": "cleanup"
115
+ })
116
+
117
+ # 4. Cleanup
118
+ clean = Clean()
119
+ clean.remove(self.directory, f'{self.name}', self.verbose)
120
+
121
+ if self.progress_callback:
122
+ self.progress_callback({
123
+ "progress": 100,
124
+ "str": "cleanup-completed",
125
+ "next": "done"
126
+ })
mainLogic/startup/checkup.py CHANGED
@@ -1,184 +1,184 @@
1
- from mainLogic import error
2
- import os
3
- from mainLogic.utils.os2 import SysFunc
4
- from mainLogic.utils.glv import Global
5
-
6
- class CheckState:
7
-
8
- def __init__(self) -> None:
9
- pass
10
-
11
- def post_checkup(self,prefs,verbose=True):
12
-
13
- """
14
- Post Checkup Function
15
- 1. Setting up the tmpDir
16
- 2. Setting up the output directory
17
- 3. Setting up the horizontal rule
18
- """
19
-
20
- OUT_DIRECTORY = ""
21
-
22
- # setting up prefs
23
- if 'tmpDir' in prefs:
24
- tmpDir = SysFunc.modify_path(prefs['tmpDir'])
25
- if not os.path.exists(tmpDir):
26
- try:
27
- os.makedirs(tmpDir)
28
- except OSError as exc: # Guard against failure
29
- error.errorList["couldNotMakeDir"]['func'](tmpDir)
30
- Global.errprint("Failed to create TmpDir")
31
- Global.errprint("Falling Back to Default")
32
- else:
33
- tmpDir = './tmp/'
34
-
35
- # setting up directory for pwdl
36
- if "dir" in prefs:
37
- try: OUT_DIRECTORY = os.path.abspath(os.path.expandvars(prefs['dir']))
38
-
39
- # if the user provides a non-string value for the directory or dir is not found
40
- except TypeError: OUT_DIRECTORY = './'
41
-
42
- # if the directory is not found
43
- except Exception as e:
44
- Global.errprint(f"Error: {e}")
45
- Global.errprint("Falling back to default")
46
- OUT_DIRECTORY = './'
47
- else:
48
- OUT_DIRECTORY = './'
49
-
50
- # setting up hr (horizontal rule)
51
- if not 'hr' in prefs:
52
- Global.disable_hr = False
53
- elif not prefs['hr']:
54
- Global.disable_hr = True
55
-
56
- prefs['tmpDir'] = tmpDir
57
- prefs['dir'] = OUT_DIRECTORY
58
-
59
-
60
- def check_token(self,token,id="90dbede8-66a8-40e8-82ce-a2048b5c063d",verbose=False):
61
- from mainLogic.big4.decrypt.key import LicenseKeyFetcher
62
- lc_fetcher = LicenseKeyFetcher(token)
63
- try:
64
- key = lc_fetcher.get_key(id,verbose=verbose)
65
- return key
66
- except Exception as e:
67
- Global.errprint(f"An error occurred while getting the key: {e}")
68
- Global.errprint("Your Token is Invalid! ")
69
- return None
70
-
71
-
72
- def checkup(self,executable,directory="./",verbose=True):
73
-
74
- state = {}
75
-
76
- # set script path to ../startup
77
- # this is the path to the folder containing the pwdl.py file
78
- # since the checkup.py is in the startup folder, we need to go one level up
79
- if verbose: Global.hr();Global.dprint("Setting script path...")
80
- if verbose: Global.errprint('Warning! Hard Coded \'$script\' location to checkup.py/../../')
81
-
82
- Global.script_path = os.path.abspath(os.path.join(os.path.dirname(__file__),'../..'))
83
- default_json = os.path.join(Global.script_path,'defaults.json')
84
-
85
- # check if defaults.json exists
86
- # and if it does, load the preferences
87
- if verbose: Global.hr();Global.dprint("Checking for default settings...")
88
-
89
- if verbose: Global.hr();Global.dprint(f"Checking at {default_json}")
90
- if verbose: Global.errprint('Warning!\nHard Coded \'defaults.json\' location to $script/default.json ')
91
-
92
- if not os.path.exists(default_json):
93
- error.errorList["defaultsNotFound"]["func"]()
94
- exit(error.errorList["defaultsNotFound"]["code"])
95
-
96
- if verbose: Global.sprint("Default settings found."); Global.hr()
97
-
98
- # load the preferences
99
- from mainLogic.startup.userPrefs import PreferencesLoader
100
- prefs = PreferencesLoader(file_name=default_json,verbose=verbose).prefs
101
-
102
- # check if method is patched (currently via userPrefs.py)
103
- if 'patched' in prefs:
104
- if prefs['patched']:
105
- error.errorList["methodPatched"]["func"]()
106
- exit(error.errorList["methodPatched"]["code"])
107
-
108
- # FLare no longer required
109
- # if verbose: Global.hr(); Global.dprint("Checking for Flare...")
110
- # default url is localhost:8191
111
- # however user can change it in the preferences file
112
- # if verbose: Global.dprint(f"Checking at {prefs['flare_url'] if 'flare_url' in prefs else 'http://localhost:8191/v1'}")
113
- # if not checkFlare(prefs['flare_url'] if 'flare_url' in prefs else 'http://localhost:8191/v1'):
114
- # error.errorList["flareNotStarted"]["func"]()
115
- # exit(error.errorList["flareNotStarted"]["code"])
116
- #
117
- # if verbose: Global.sprint("Flare is running."); Global.hr()
118
-
119
- os2 = SysFunc()
120
-
121
- found= []
122
- notFound = []
123
-
124
- for exe in executable:
125
- if verbose: Global.hr(); Global.dprint(f"Checking for {exe}...")
126
-
127
- if os2.which(exe) == 1:
128
- if verbose: error.errorList["dependencyNotFound"]["func"](exe)
129
- if verbose: print(f"{exe} not found on path! Checking in default settings...")
130
-
131
- # add exe's which are found to the found list
132
- found.append(exe)
133
- # add exe's which are not found to the notFound list
134
- notFound.append(exe)
135
-
136
- else:
137
- if verbose: Global.sprint(f"{exe} found.")
138
- state[exe] = exe
139
-
140
- if len(notFound) > 0:
141
-
142
- if verbose: Global.hr();Global.dprint("Following dependencies were not found on path. Checking in default settings...")
143
- if verbose: Global.dprint(notFound); Global.hr()
144
-
145
- for exe in notFound:
146
-
147
- if verbose: Global.dprint(f"Checking for {exe} in default settings...")
148
-
149
- if exe in prefs:
150
-
151
- if verbose: Global.sprint(f"Key for {exe} found in default settings.")
152
- if verbose: Global.sprint(f"Value: {prefs[exe]}")
153
- if verbose: Global.dprint(f"Checking for {exe} at '{prefs[exe].strip()}' ...")
154
-
155
- if not os.path.exists(prefs[exe].strip()):
156
- Global.errprint(f"{exe} not found at {prefs[exe].strip()}")
157
- error.errorList["dependencyNotFoundInPrefs"]["func"](exe)
158
- exit(error.errorList["dependencyNotFoundInPrefs"]["code"])
159
-
160
- if verbose: Global.sprint(f"{exe} found at {prefs[exe].strip()}")
161
- state[exe] = prefs[exe].strip()
162
-
163
-
164
- else:
165
- error.errorList["dependencyNotFoundInPrefs"]["func"](exe)
166
- exit(error.errorList["dependencyNotFoundInPrefs"]["code"])
167
-
168
- if verbose: Global.hr()
169
-
170
- # checking for token
171
- if 'token' in prefs:
172
- self.check_token(prefs['token'],verbose=verbose)
173
- else:
174
- error.errorList["tokenNotFound"]["func"]()
175
- exit(error.errorList["tokenNotFound"]["code"])
176
-
177
- state['prefs'] = prefs
178
- prefs['dir'] = directory
179
- self.post_checkup(prefs,verbose)
180
-
181
-
182
- return state
183
-
184
-
 
1
+ from mainLogic import error
2
+ import os
3
+ from mainLogic.utils.os2 import SysFunc
4
+ from mainLogic.utils.glv import Global
5
+
6
+ class CheckState:
7
+
8
+ def __init__(self) -> None:
9
+ pass
10
+
11
+ def post_checkup(self,prefs,verbose=True):
12
+
13
+ """
14
+ Post Checkup Function
15
+ 1. Setting up the tmpDir
16
+ 2. Setting up the output directory
17
+ 3. Setting up the horizontal rule
18
+ """
19
+
20
+ OUT_DIRECTORY = ""
21
+
22
+ # setting up prefs
23
+ if 'tmpDir' in prefs:
24
+ tmpDir = SysFunc.modify_path(prefs['tmpDir'])
25
+ if not os.path.exists(tmpDir):
26
+ try:
27
+ os.makedirs(tmpDir)
28
+ except OSError as exc: # Guard against failure
29
+ error.errorList["couldNotMakeDir"]['func'](tmpDir)
30
+ Global.errprint("Failed to create TmpDir")
31
+ Global.errprint("Falling Back to Default")
32
+ else:
33
+ tmpDir = './tmp/'
34
+
35
+ # setting up directory for pwdl
36
+ if "dir" in prefs:
37
+ try: OUT_DIRECTORY = os.path.abspath(os.path.expandvars(prefs['dir']))
38
+
39
+ # if the user provides a non-string value for the directory or dir is not found
40
+ except TypeError: OUT_DIRECTORY = './'
41
+
42
+ # if the directory is not found
43
+ except Exception as e:
44
+ Global.errprint(f"Error: {e}")
45
+ Global.errprint("Falling back to default")
46
+ OUT_DIRECTORY = './'
47
+ else:
48
+ OUT_DIRECTORY = './'
49
+
50
+ # setting up hr (horizontal rule)
51
+ if not 'hr' in prefs:
52
+ Global.disable_hr = False
53
+ elif not prefs['hr']:
54
+ Global.disable_hr = True
55
+
56
+ prefs['tmpDir'] = tmpDir
57
+ prefs['dir'] = OUT_DIRECTORY
58
+
59
+
60
+ def check_token(self,token,id="90dbede8-66a8-40e8-82ce-a2048b5c063d",verbose=False):
61
+ from mainLogic.big4.decrypt.key import LicenseKeyFetcher
62
+ lc_fetcher = LicenseKeyFetcher(token)
63
+ try:
64
+ key = lc_fetcher.get_key(id,verbose=verbose)
65
+ return key
66
+ except Exception as e:
67
+ Global.errprint(f"An error occurred while getting the key: {e}")
68
+ Global.errprint("Your Token is Invalid! ")
69
+ return None
70
+
71
+
72
+ def checkup(self,executable,directory="./",verbose=True):
73
+
74
+ state = {}
75
+
76
+ # set script path to ../startup
77
+ # this is the path to the folder containing the pwdl.py file
78
+ # since the checkup.py is in the startup folder, we need to go one level up
79
+ if verbose: Global.hr();Global.dprint("Setting script path...")
80
+ if verbose: Global.errprint('Warning! Hard Coded \'$script\' location to checkup.py/../../')
81
+
82
+ Global.script_path = os.path.abspath(os.path.join(os.path.dirname(__file__),'../..'))
83
+ default_json = os.path.join(Global.script_path,'defaults.json')
84
+
85
+ # check if defaults.json exists
86
+ # and if it does, load the preferences
87
+ if verbose: Global.hr();Global.dprint("Checking for default settings...")
88
+
89
+ if verbose: Global.hr();Global.dprint(f"Checking at {default_json}")
90
+ if verbose: Global.errprint('Warning!\nHard Coded \'defaults.json\' location to $script/default.json ')
91
+
92
+ if not os.path.exists(default_json):
93
+ error.errorList["defaultsNotFound"]["func"]()
94
+ exit(error.errorList["defaultsNotFound"]["code"])
95
+
96
+ if verbose: Global.sprint("Default settings found."); Global.hr()
97
+
98
+ # load the preferences
99
+ from mainLogic.startup.userPrefs import PreferencesLoader
100
+ prefs = PreferencesLoader(file_name=default_json,verbose=verbose).prefs
101
+
102
+ # check if method is patched (currently via userPrefs.py)
103
+ if 'patched' in prefs:
104
+ if prefs['patched']:
105
+ error.errorList["methodPatched"]["func"]()
106
+ exit(error.errorList["methodPatched"]["code"])
107
+
108
+ # FLare no longer required
109
+ # if verbose: Global.hr(); Global.dprint("Checking for Flare...")
110
+ # default url is localhost:8191
111
+ # however user can change it in the preferences file
112
+ # if verbose: Global.dprint(f"Checking at {prefs['flare_url'] if 'flare_url' in prefs else 'http://localhost:8191/v1'}")
113
+ # if not checkFlare(prefs['flare_url'] if 'flare_url' in prefs else 'http://localhost:8191/v1'):
114
+ # error.errorList["flareNotStarted"]["func"]()
115
+ # exit(error.errorList["flareNotStarted"]["code"])
116
+ #
117
+ # if verbose: Global.sprint("Flare is running."); Global.hr()
118
+
119
+ os2 = SysFunc()
120
+
121
+ found= []
122
+ notFound = []
123
+
124
+ for exe in executable:
125
+ if verbose: Global.hr(); Global.dprint(f"Checking for {exe}...")
126
+
127
+ if os2.which(exe) == 1:
128
+ if verbose: error.errorList["dependencyNotFound"]["func"](exe)
129
+ if verbose: print(f"{exe} not found on path! Checking in default settings...")
130
+
131
+ # add exe's which are found to the found list
132
+ found.append(exe)
133
+ # add exe's which are not found to the notFound list
134
+ notFound.append(exe)
135
+
136
+ else:
137
+ if verbose: Global.sprint(f"{exe} found.")
138
+ state[exe] = exe
139
+
140
+ if len(notFound) > 0:
141
+
142
+ if verbose: Global.hr();Global.dprint("Following dependencies were not found on path. Checking in default settings...")
143
+ if verbose: Global.dprint(notFound); Global.hr()
144
+
145
+ for exe in notFound:
146
+
147
+ if verbose: Global.dprint(f"Checking for {exe} in default settings...")
148
+
149
+ if exe in prefs:
150
+
151
+ if verbose: Global.sprint(f"Key for {exe} found in default settings.")
152
+ if verbose: Global.sprint(f"Value: {prefs[exe]}")
153
+ if verbose: Global.dprint(f"Checking for {exe} at '{prefs[exe].strip()}' ...")
154
+
155
+ if not os.path.exists(prefs[exe].strip()):
156
+ Global.errprint(f"{exe} not found at {prefs[exe].strip()}")
157
+ error.errorList["dependencyNotFoundInPrefs"]["func"](exe)
158
+ exit(error.errorList["dependencyNotFoundInPrefs"]["code"])
159
+
160
+ if verbose: Global.sprint(f"{exe} found at {prefs[exe].strip()}")
161
+ state[exe] = prefs[exe].strip()
162
+
163
+
164
+ else:
165
+ error.errorList["dependencyNotFoundInPrefs"]["func"](exe)
166
+ exit(error.errorList["dependencyNotFoundInPrefs"]["code"])
167
+
168
+ if verbose: Global.hr()
169
+
170
+ # checking for token
171
+ if 'token' in prefs:
172
+ self.check_token(prefs['token'],verbose=verbose)
173
+ else:
174
+ error.errorList["tokenNotFound"]["func"]()
175
+ exit(error.errorList["tokenNotFound"]["code"])
176
+
177
+ state['prefs'] = prefs
178
+ prefs['dir'] = directory
179
+ self.post_checkup(prefs,verbose)
180
+
181
+
182
+ return state
183
+
184
+
mainLogic/startup/flareCheck.py CHANGED
@@ -1,17 +1,17 @@
1
- import requests
2
-
3
- def checkFlare(flareUrl="http://localhost:8191/v1"):
4
-
5
- url = f"{flareUrl}"
6
- headers = {"Content-Type": "application/json"}
7
- data = {
8
- "cmd": "request.get",
9
- "url": "http://www.google.com/",
10
- "maxTimeout": 60000
11
- }
12
- try:
13
- response = requests.post(url, headers=headers, json=data)
14
- return response.ok
15
- except Exception as e:
16
- return False
17
-
 
1
+ import requests
2
+
3
+ def checkFlare(flareUrl="http://localhost:8191/v1"):
4
+
5
+ url = f"{flareUrl}"
6
+ headers = {"Content-Type": "application/json"}
7
+ data = {
8
+ "cmd": "request.get",
9
+ "url": "http://www.google.com/",
10
+ "maxTimeout": 60000
11
+ }
12
+ try:
13
+ response = requests.post(url, headers=headers, json=data)
14
+ return response.ok
15
+ except Exception as e:
16
+ return False
17
+
mainLogic/startup/userPrefs.py CHANGED
@@ -1,56 +1,56 @@
1
- import json
2
- from mainLogic import error
3
- import os
4
- from mainLogic.utils.basicUtils import BasicUtils
5
-
6
- class PreferencesLoader:
7
- def __init__(self, file_name='defaults.json',verbose=True):
8
- self.file_name = file_name
9
- self.prefs = {}
10
-
11
- # defining some variables that can be used in the preferences file
12
- self.vars = {
13
-
14
- # $script is the path to the folder containing the pwdl.py file
15
- # Since the userPrefs.py is in the startup folder,
16
- # we need to go one level up however we make the exception that if the pwdl.py is in the same folder as
17
- # the startup folder, we don't need to go one level up
18
- "$script" : BasicUtils.abspath(os.path.dirname(__file__)+ ('/../..' if not os.path.exists(os.path.dirname(__file__) + '../pwdl.py') else '')),
19
- "$home" : os.path.expanduser("~"),
20
- }
21
-
22
-
23
- self.load_preferences()
24
-
25
- # if verbose is true, print the preferences
26
- if verbose:
27
- self.print_preferences()
28
-
29
-
30
- def load_preferences(self):
31
- try:
32
-
33
- with open(self.file_name, 'r') as json_file:
34
-
35
- # read the contents of the file (so that we can replace the variables with their values)
36
- contents = json_file.read()
37
-
38
- # replace the variables with their values
39
- for var in self.vars:
40
- contents = contents.replace(var,self.vars[var])
41
-
42
- # replace the backslashes with forward slashes
43
- contents.replace('\\','/')
44
-
45
- self.prefs = json.loads(contents)
46
-
47
- # if the file is not found, print an error message and exit
48
- except FileNotFoundError:
49
- error.errorList["cantLoadFile"]["func"](self.file_name)
50
- exit(error.errorList["cantLoadFile"]["code"])
51
-
52
-
53
- # print the preferences (internal function)
54
- def print_preferences(self):
55
- for key in self.prefs:
56
  print(f'{key} : {self.prefs[key]}')
 
1
+ import json
2
+ from mainLogic import error
3
+ import os
4
+ from mainLogic.utils.basicUtils import BasicUtils
5
+
6
+ class PreferencesLoader:
7
+ def __init__(self, file_name='defaults.json',verbose=True):
8
+ self.file_name = file_name
9
+ self.prefs = {}
10
+
11
+ # defining some variables that can be used in the preferences file
12
+ self.vars = {
13
+
14
+ # $script is the path to the folder containing the pwdl.py file
15
+ # Since the userPrefs.py is in the startup folder,
16
+ # we need to go one level up however we make the exception that if the pwdl.py is in the same folder as
17
+ # the startup folder, we don't need to go one level up
18
+ "$script" : BasicUtils.abspath(os.path.dirname(__file__)+ ('/../..' if not os.path.exists(os.path.dirname(__file__) + '../pwdl.py') else '')),
19
+ "$home" : os.path.expanduser("~"),
20
+ }
21
+
22
+
23
+ self.load_preferences()
24
+
25
+ # if verbose is true, print the preferences
26
+ if verbose:
27
+ self.print_preferences()
28
+
29
+
30
+ def load_preferences(self):
31
+ try:
32
+
33
+ with open(self.file_name, 'r') as json_file:
34
+
35
+ # read the contents of the file (so that we can replace the variables with their values)
36
+ contents = json_file.read()
37
+
38
+ # replace the variables with their values
39
+ for var in self.vars:
40
+ contents = contents.replace(var,self.vars[var])
41
+
42
+ # replace the backslashes with forward slashes
43
+ contents.replace('\\','/')
44
+
45
+ self.prefs = json.loads(contents)
46
+
47
+ # if the file is not found, print an error message and exit
48
+ except FileNotFoundError:
49
+ error.errorList["cantLoadFile"]["func"](self.file_name)
50
+ exit(error.errorList["cantLoadFile"]["code"])
51
+
52
+
53
+ # print the preferences (internal function)
54
+ def print_preferences(self):
55
+ for key in self.prefs:
56
  print(f'{key} : {self.prefs[key]}')
mainLogic/utils/basicUtils.py CHANGED
@@ -1,7 +1,7 @@
1
- import os
2
-
3
- class BasicUtils:
4
-
5
- @staticmethod
6
- def abspath(path):
7
- return str(os.path.abspath(os.path.expandvars(path))).replace("\\", "/")
 
1
+ import os
2
+
3
+ class BasicUtils:
4
+
5
+ @staticmethod
6
+ def abspath(path):
7
+ return str(os.path.abspath(os.path.expandvars(path))).replace("\\", "/")
mainLogic/utils/glv.py CHANGED
@@ -1,75 +1,75 @@
1
- from colorama import Fore, Style, init
2
- import shutil
3
-
4
- # Initialize colorama
5
- init()
6
-
7
- class Global:
8
-
9
- # PREFERENCES_FILE is currently not used in mainLogic Project
10
- # only used in beta project
11
- import os
12
- PREFERENCES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../defaults.json'))
13
-
14
- disable_hr = False
15
- EXECUTABLES = ['ffmpeg', 'mp4decrypt', 'vsd']
16
- api_webdl_directory = "webdl"
17
-
18
- def __init__(self, vout=True, outDir="./"):
19
- self.outDir = outDir
20
- self.vout = vout
21
-
22
- @staticmethod
23
- def set_color(color, style=None):
24
- """Prints text in the specified color and style."""
25
- print(getattr(Fore, color), end="")
26
- if style:
27
- print(getattr(Style, style), end="")
28
-
29
- @staticmethod
30
- def reset():
31
- """Resets text color and style to defaults."""
32
- print(Style.RESET_ALL, end="")
33
-
34
- @staticmethod
35
- def print_colored(text, color, style=None):
36
- """Prints text in the specified color and style, resetting afterward."""
37
- Global.set_color(color, style)
38
- print(text)
39
- Global.reset()
40
-
41
- @staticmethod
42
- def dprint(text):
43
- """Prints debug text in yellow."""
44
- Global.print_colored(text, "YELLOW")
45
-
46
- @staticmethod
47
- def errprint(text):
48
- """Prints error text in red."""
49
- Global.print_colored(text, "RED")
50
-
51
- @staticmethod
52
- def setDebug():
53
- """Sets the text color to yellow (for debugging)."""
54
- Global.set_color("YELLOW")
55
-
56
- @staticmethod
57
- def setSuccess():
58
- """Sets the text color to green (for success messages)."""
59
- Global.set_color("GREEN")
60
-
61
- @staticmethod
62
- def sprint(text):
63
- """Prints success text in green."""
64
- Global.print_colored(text, "GREEN")
65
-
66
- @staticmethod
67
- def hr():
68
-
69
- # Disable horizontal rule if set
70
- if Global.disable_hr:
71
- return
72
-
73
- """Fills the entire terminal with = (one row only)."""
74
- columns, _ = shutil.get_terminal_size()
75
  print("-" * columns)
 
1
+ from colorama import Fore, Style, init
2
+ import shutil
3
+
4
+ # Initialize colorama
5
+ init()
6
+
7
+ class Global:
8
+
9
+ # PREFERENCES_FILE is currently not used in mainLogic Project
10
+ # only used in beta project
11
+ import os
12
+ PREFERENCES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../defaults.json'))
13
+
14
+ disable_hr = False
15
+ EXECUTABLES = ['ffmpeg', 'mp4decrypt', 'vsd']
16
+ api_webdl_directory = "webdl"
17
+
18
+ def __init__(self, vout=True, outDir="./"):
19
+ self.outDir = outDir
20
+ self.vout = vout
21
+
22
+ @staticmethod
23
+ def set_color(color, style=None):
24
+ """Prints text in the specified color and style."""
25
+ print(getattr(Fore, color), end="")
26
+ if style:
27
+ print(getattr(Style, style), end="")
28
+
29
+ @staticmethod
30
+ def reset():
31
+ """Resets text color and style to defaults."""
32
+ print(Style.RESET_ALL, end="")
33
+
34
+ @staticmethod
35
+ def print_colored(text, color, style=None):
36
+ """Prints text in the specified color and style, resetting afterward."""
37
+ Global.set_color(color, style)
38
+ print(text)
39
+ Global.reset()
40
+
41
+ @staticmethod
42
+ def dprint(text):
43
+ """Prints debug text in yellow."""
44
+ Global.print_colored(text, "YELLOW")
45
+
46
+ @staticmethod
47
+ def errprint(text):
48
+ """Prints error text in red."""
49
+ Global.print_colored(text, "RED")
50
+
51
+ @staticmethod
52
+ def setDebug():
53
+ """Sets the text color to yellow (for debugging)."""
54
+ Global.set_color("YELLOW")
55
+
56
+ @staticmethod
57
+ def setSuccess():
58
+ """Sets the text color to green (for success messages)."""
59
+ Global.set_color("GREEN")
60
+
61
+ @staticmethod
62
+ def sprint(text):
63
+ """Prints success text in green."""
64
+ Global.print_colored(text, "GREEN")
65
+
66
+ @staticmethod
67
+ def hr():
68
+
69
+ # Disable horizontal rule if set
70
+ if Global.disable_hr:
71
+ return
72
+
73
+ """Fills the entire terminal with = (one row only)."""
74
+ columns, _ = shutil.get_terminal_size()
75
  print("-" * columns)
mainLogic/utils/keyUtils.py CHANGED
@@ -1,25 +1,25 @@
1
- """
2
- Obsolete module:
3
- no longer used in the project
4
- used when key.old.py was being used
5
- """
6
-
7
- import base64
8
-
9
- def base64_to_hex(base64_str):
10
- # Replace special characters not in base64 list with '/'
11
- base64_str = base64_str.replace('-', '+').replace('_', '/')
12
-
13
- # Add padding if necessary
14
- padding = len(base64_str) % 4
15
- if padding:
16
- base64_str += '=' * (4 - padding)
17
-
18
- # Convert base64 to bytes
19
- base64_bytes = base64_str.encode('utf-8')
20
-
21
- # Decode base64 bytes to hex bytes
22
- hex_bytes = base64.b64decode(base64_bytes).hex()
23
-
24
- return hex_bytes
25
-
 
1
+ """
2
+ Obsolete module:
3
+ no longer used in the project
4
+ used when key.old.py was being used
5
+ """
6
+
7
+ import base64
8
+
9
+ def base64_to_hex(base64_str):
10
+ # Replace special characters not in base64 list with '/'
11
+ base64_str = base64_str.replace('-', '+').replace('_', '/')
12
+
13
+ # Add padding if necessary
14
+ padding = len(base64_str) % 4
15
+ if padding:
16
+ base64_str += '=' * (4 - padding)
17
+
18
+ # Convert base64 to bytes
19
+ base64_bytes = base64_str.encode('utf-8')
20
+
21
+ # Decode base64 bytes to hex bytes
22
+ hex_bytes = base64.b64decode(base64_bytes).hex()
23
+
24
+ return hex_bytes
25
+
mainLogic/utils/os2.py CHANGED
@@ -1,64 +1,64 @@
1
- import platform
2
- import os
3
- from mainLogic import error
4
- from mainLogic.utils.process import shell
5
- from mainLogic.utils.glv import Global
6
- # 0 - linux
7
- # 1 - windows
8
- # 2 - mac (currently not supported)
9
-
10
- class SysFunc:
11
- def __init__(self,os=1 if "Windows" in platform.system() else 0 if "Linux" in platform.system() else -1):
12
- if os == -1:
13
- raise Exception("UnsupportedOS")
14
- self.os = os
15
-
16
- def create_dir(self,dirName,verbose=False):
17
- try:
18
- if not os.path.exists(dirName):
19
- if verbose: Global.dprint(f"Creating directory {dirName}")
20
- os.makedirs(dirName)
21
- except:
22
- if verbose: Global.errprint(f"Could not make directory {dirName}. Exiting...")
23
- error.errorList["couldNotMakeDir"]["func"](dirName)
24
- exit(error.errorList["couldNotMakeDir"]["code"])
25
-
26
- if verbose: Global.dprint(f"Directory {dirName} created")
27
- def clear(self):
28
- if self.os == 0:
29
- os.system("clear")
30
- elif self.os == 1:
31
- os.system("cls")
32
- else:
33
- raise Exception("UnsupportedOS")
34
-
35
-
36
- def which(self,program):
37
-
38
- if self.os == 0:
39
- if shell('which',stderr="",stdout="") != 1 and shell('which',stderr="",stdout="") != 255:
40
- error.errorList["dependencyNotFound"]["func"]('which')
41
- exit(error.errorList["dependencyNotFound"]["code"])
42
- else:
43
- self.whichPresent = True
44
-
45
- return shell(f"which {program}",stderr="",stdout="")
46
-
47
- elif self.os == 1:
48
-
49
- if shell('where',stderr="",stdout="") != 2:
50
- error.errorList["dependencyNotFound"]["func"]('where')
51
- exit(error.errorList["dependencyNotFound"]["code"])
52
- else:
53
- self.whichPresent = True
54
- return shell(f"where {program}" , stderr="",stdout="")
55
- else:
56
- raise Exception("UnsupportedOS")
57
-
58
- @staticmethod
59
- def modify_path(path):
60
- expanded_path = os.path.expandvars(path)
61
- absolute_path = os.path.abspath(expanded_path)
62
- modified_path = absolute_path.replace(os.sep, '/')
63
- return modified_path
64
 
 
1
+ import platform
2
+ import os
3
+ from mainLogic import error
4
+ from mainLogic.utils.process import shell
5
+ from mainLogic.utils.glv import Global
6
+ # 0 - linux
7
+ # 1 - windows
8
+ # 2 - mac (currently not supported)
9
+
10
+ class SysFunc:
11
+ def __init__(self,os=1 if "Windows" in platform.system() else 0 if "Linux" in platform.system() else -1):
12
+ if os == -1:
13
+ raise Exception("UnsupportedOS")
14
+ self.os = os
15
+
16
+ def create_dir(self,dirName,verbose=False):
17
+ try:
18
+ if not os.path.exists(dirName):
19
+ if verbose: Global.dprint(f"Creating directory {dirName}")
20
+ os.makedirs(dirName)
21
+ except:
22
+ if verbose: Global.errprint(f"Could not make directory {dirName}. Exiting...")
23
+ error.errorList["couldNotMakeDir"]["func"](dirName)
24
+ exit(error.errorList["couldNotMakeDir"]["code"])
25
+
26
+ if verbose: Global.dprint(f"Directory {dirName} created")
27
+ def clear(self):
28
+ if self.os == 0:
29
+ os.system("clear")
30
+ elif self.os == 1:
31
+ os.system("cls")
32
+ else:
33
+ raise Exception("UnsupportedOS")
34
+
35
+
36
+ def which(self,program):
37
+
38
+ if self.os == 0:
39
+ if shell('which',stderr="",stdout="") != 1 and shell('which',stderr="",stdout="") != 255:
40
+ error.errorList["dependencyNotFound"]["func"]('which')
41
+ exit(error.errorList["dependencyNotFound"]["code"])
42
+ else:
43
+ self.whichPresent = True
44
+
45
+ return shell(f"which {program}",stderr="",stdout="")
46
+
47
+ elif self.os == 1:
48
+
49
+ if shell('where',stderr="",stdout="") != 2:
50
+ error.errorList["dependencyNotFound"]["func"]('where')
51
+ exit(error.errorList["dependencyNotFound"]["code"])
52
+ else:
53
+ self.whichPresent = True
54
+ return shell(f"where {program}" , stderr="",stdout="")
55
+ else:
56
+ raise Exception("UnsupportedOS")
57
+
58
+ @staticmethod
59
+ def modify_path(path):
60
+ expanded_path = os.path.expandvars(path)
61
+ absolute_path = os.path.abspath(expanded_path)
62
+ modified_path = absolute_path.replace(os.sep, '/')
63
+ return modified_path
64
 
mainLogic/utils/process.py CHANGED
@@ -1,59 +1,59 @@
1
- import subprocess
2
- import re
3
- import sys
4
-
5
- def shell(command, filter=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, progress_callback=None, handleProgress=None, inline_progress=True):
6
- import os
7
-
8
- # Set PYTHONUNBUFFERED environment variable
9
- os.environ['PYTHONUNBUFFERED'] = '1'
10
-
11
- command = to_list(command)
12
-
13
- process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', universal_newlines=True)
14
-
15
- while True:
16
- try:
17
- output = process.stdout.readline()
18
- output = output.strip()
19
-
20
- if output == '' and process.poll() is not None:
21
- break
22
-
23
- if output:
24
- if filter is not None and re.search(filter, output):
25
- # Call the progress callback with the filtered output
26
- if progress_callback:
27
- if handleProgress:
28
- progress_callback(handleProgress(output))
29
- else:
30
- progress_callback(output)
31
-
32
- if inline_progress:
33
- # Update progress in the same line
34
- sys.stdout.write('\r' + output.strip())
35
- sys.stdout.flush()
36
- else:
37
- # Print output normally
38
- print(output.strip())
39
-
40
- except UnicodeEncodeError:
41
- sys.stdout.write("\rUnicodeEncodeError")
42
- sys.stdout.flush()
43
- pass
44
- except Exception as e:
45
- print(f"Error: {e}")
46
-
47
- # Wait for the process to complete and get the return code
48
- return_code = process.wait()
49
-
50
- return return_code
51
-
52
- def to_list(variable):
53
- if isinstance(variable, list):
54
- return variable
55
- elif variable is None:
56
- return []
57
- else:
58
- # Convert to string and then to list by splitting at whitespaces
59
- return variable.split()
 
1
+ import subprocess
2
+ import re
3
+ import sys
4
+
5
+ def shell(command, filter=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, progress_callback=None, handleProgress=None, inline_progress=True):
6
+ import os
7
+
8
+ # Set PYTHONUNBUFFERED environment variable
9
+ os.environ['PYTHONUNBUFFERED'] = '1'
10
+
11
+ command = to_list(command)
12
+
13
+ process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', universal_newlines=True)
14
+
15
+ while True:
16
+ try:
17
+ output = process.stdout.readline()
18
+ output = output.strip()
19
+
20
+ if output == '' and process.poll() is not None:
21
+ break
22
+
23
+ if output:
24
+ if filter is not None and re.search(filter, output):
25
+ # Call the progress callback with the filtered output
26
+ if progress_callback:
27
+ if handleProgress:
28
+ progress_callback(handleProgress(output))
29
+ else:
30
+ progress_callback(output)
31
+
32
+ if inline_progress:
33
+ # Update progress in the same line
34
+ sys.stdout.write('\r' + output.strip())
35
+ sys.stdout.flush()
36
+ else:
37
+ # Print output normally
38
+ print(output.strip())
39
+
40
+ except UnicodeEncodeError:
41
+ sys.stdout.write("\rUnicodeEncodeError")
42
+ sys.stdout.flush()
43
+ pass
44
+ except Exception as e:
45
+ print(f"Error: {e}")
46
+
47
+ # Wait for the process to complete and get the return code
48
+ return_code = process.wait()
49
+
50
+ return return_code
51
+
52
+ def to_list(variable):
53
+ if isinstance(variable, list):
54
+ return variable
55
+ elif variable is None:
56
+ return []
57
+ else:
58
+ # Convert to string and then to list by splitting at whitespaces
59
+ return variable.split()
pwdl.bat CHANGED
@@ -1,45 +1,45 @@
1
- @echo off
2
- REM Batch script for running the Python script pwdl.py
3
-
4
- REM Check if Python is installed
5
- py --version >nul 2>&1
6
- if errorlevel 1 (
7
- echo Python is not installed or not found in the PATH.
8
- exit /b 1
9
- )
10
-
11
- REM Define paths to executables
12
- set SCRIPT_DIR=%~dp0
13
- set BIN_DIR=%SCRIPT_DIR%bin
14
- set MP4DECRYPT=%BIN_DIR%\mp4decrypt.exe
15
- set VSD=%BIN_DIR%\vsd.exe
16
-
17
- REM Check if bin directory exists, create if it doesn't
18
- if not exist "%BIN_DIR%" (
19
- mkdir "%BIN_DIR%"
20
- )
21
-
22
- REM Check if mp4decrypt.exe exists
23
- if not exist "%MP4DECRYPT%" (
24
- echo mp4decrypt.exe not found. Downloading...
25
- powershell -Command "Invoke-WebRequest -Uri 'https://github.com/shubhamakshit/pwdlv3_assets/raw/main/Windows/x86_64/mp4decrypt.exe' -OutFile '%MP4DECRYPT%'" >nul 2>&1
26
- if errorlevel 1 (
27
- echo Failed to download mp4decrypt.exe
28
- ) else (
29
- echo Successfully downloaded mp4decrypt.exe
30
- )
31
- )
32
-
33
- REM Check if vsd.exe exists
34
- if not exist "%VSD%" (
35
- echo vsd.exe not found. Downloading...
36
- powershell -Command "Invoke-WebRequest -Uri 'https://github.com/shubhamakshit/pwdlv3_assets/raw/main/Windows/x86_64/vsd.exe' -OutFile '%VSD%'" >nul 2>&1
37
- if errorlevel 1 (
38
- echo Failed to download vsd.exe
39
- ) else (
40
- echo Successfully downloaded vsd.exe
41
- )
42
- )
43
-
44
- REM Run the Python script with the provided arguments
45
- py "%~dp0pwdl.py" %*
 
1
+ @echo off
2
+ REM Batch script for running the Python script pwdl.py
3
+
4
+ REM Check if Python is installed
5
+ py --version >nul 2>&1
6
+ if errorlevel 1 (
7
+ echo Python is not installed or not found in the PATH.
8
+ exit /b 1
9
+ )
10
+
11
+ REM Define paths to executables
12
+ set SCRIPT_DIR=%~dp0
13
+ set BIN_DIR=%SCRIPT_DIR%bin
14
+ set MP4DECRYPT=%BIN_DIR%\mp4decrypt.exe
15
+ set VSD=%BIN_DIR%\vsd.exe
16
+
17
+ REM Check if bin directory exists, create if it doesn't
18
+ if not exist "%BIN_DIR%" (
19
+ mkdir "%BIN_DIR%"
20
+ )
21
+
22
+ REM Check if mp4decrypt.exe exists
23
+ if not exist "%MP4DECRYPT%" (
24
+ echo mp4decrypt.exe not found. Downloading...
25
+ powershell -Command "Invoke-WebRequest -Uri 'https://github.com/shubhamakshit/pwdlv3_assets/raw/main/Windows/x86_64/mp4decrypt.exe' -OutFile '%MP4DECRYPT%'" >nul 2>&1
26
+ if errorlevel 1 (
27
+ echo Failed to download mp4decrypt.exe
28
+ ) else (
29
+ echo Successfully downloaded mp4decrypt.exe
30
+ )
31
+ )
32
+
33
+ REM Check if vsd.exe exists
34
+ if not exist "%VSD%" (
35
+ echo vsd.exe not found. Downloading...
36
+ powershell -Command "Invoke-WebRequest -Uri 'https://github.com/shubhamakshit/pwdlv3_assets/raw/main/Windows/x86_64/vsd.exe' -OutFile '%VSD%'" >nul 2>&1
37
+ if errorlevel 1 (
38
+ echo Failed to download vsd.exe
39
+ ) else (
40
+ echo Successfully downloaded vsd.exe
41
+ )
42
+ )
43
+
44
+ REM Run the Python script with the provided arguments
45
+ py "%~dp0pwdl.py" %*
pwdl.py CHANGED
@@ -1,152 +1,152 @@
1
- import argparse
2
- from mainLogic.error import errorList
3
- from mainLogic.utils.glv import Global
4
- import sys
5
- from mainLogic.utils.os2 import SysFunc
6
- import os
7
- from mainLogic.main import Main
8
- from beta.shellLogic import shell
9
- from mainLogic.startup.checkup import CheckState
10
-
11
- # global variables
12
- prefs = {}
13
- glv = Global()
14
- os2 = SysFunc()
15
-
16
- # hardcoding the list of executables required for the script to run
17
- # should be available in the PATH or the user should provide the path to the executables
18
- EXECUTABLES = glv.EXECUTABLES
19
-
20
-
21
-
22
-
23
- def main():
24
-
25
- # parsing the arguments
26
- parser = argparse.ArgumentParser(description='PhysicsWallah M3u8 parser.')
27
-
28
- parser.add_argument('--csv-file', type=str, help='Input csv file. Legacy Support too.')
29
- parser.add_argument('--id', type=str,
30
- help='PhysicsWallh Video Id for single usage. Incompatible with --csv-file. Must be used '
31
- 'with --name')
32
- parser.add_argument('--name', type=str,
33
- help='Name for the output file. Incompatible with --csv-file. Must be used with --id')
34
- parser.add_argument('--dir', type=str, help='Output Directory')
35
- parser.add_argument('--verbose', action='store_true', help='Verbose Output')
36
- parser.add_argument('--shell',action='store_true',help='Start the shell')
37
- parser.add_argument('--version', action='version', version='%(prog)s 1.0')
38
- parser.add_argument('--simulate', action='store_true',
39
- help='Simulate the download process. No files will be downloaded.)')
40
-
41
- args = parser.parse_args()
42
-
43
- if args.shell:
44
- shell.main()
45
-
46
-
47
- # user_input is given preference i.e if --verbose is true it will override
48
- # however if --verbose is false but prefs['verbose'] is true
49
- glv.vout = args.verbose
50
-
51
- global prefs
52
-
53
- # check if all dependencies are installed
54
- state = CheckState().checkup(EXECUTABLES, directory=args.dir,verbose=glv.vout)
55
- prefs = state['prefs']
56
-
57
-
58
- # --------------------------------------------------------------------------------------------------------------------------------------
59
- # setting verbose output
60
- # gives preference to user input
61
- if not glv.vout and prefs['verbose']: glv.vout = prefs['verbose']
62
-
63
- verbose = glv.vout
64
-
65
- OUT_DIRECTORY = prefs['dir']
66
- if verbose: Global.hr(); glv.dprint(f"Tmp Dir is: {SysFunc.modify_path(prefs['tmpDir'])}")
67
- if verbose: Global.hr(); glv.dprint(f'Output Directory: {OUT_DIRECTORY}')
68
- if verbose: Global.hr(); glv.dprint(f"Horizontal Rule: {not Global.disable_hr}")
69
-
70
- # --------------------------------------------------------------------------------------------------------------------------------------
71
- # end of loading user preferences
72
-
73
-
74
- # starting the main process
75
-
76
- #if both csv file and (id or name) is provided then -> exit with error code 3
77
- if args.csv_file and (args.id or args.name):
78
- print("Both csv file and id (or name) is provided. Unable to decide. Aborting! ...")
79
- sys.exit(3)
80
-
81
- # handle in case --csv-file is provided
82
- if args.csv_file:
83
-
84
- # simulation mode
85
- if args.simulate:
86
- print("Simulating the download csv process. No files will be downloaded.")
87
- print("File to be processed: ", args.csv_file)
88
- exit(0)
89
-
90
- # exiting in case the CSV File is not found
91
- if not os.path.exists(args.csv_file):
92
- errorList['csvFileNotFound']['func'](args.csv_file)
93
- sys.exit(errorList['csvFileNotFound']['code'])
94
-
95
- with open(args.csv_file, 'r') as f:
96
- for line in f:
97
- name, id = line.strip().split(',')
98
-
99
- # adding support for csv file with partial errors
100
- try:
101
- Main(id=id,
102
- name=name,
103
- directory=OUT_DIRECTORY,
104
- ffmpeg=state['ffmpeg'],
105
- vsdPath=state['vsd'],
106
- token=prefs['token'],
107
- mp4d=state['mp4decrypt'],
108
- tmpDir=prefs['tmpDir'],
109
- verbose=verbose,
110
- suppress_exit=True # suppress exit in case of error (as multiple files are being processed)
111
- ).process()
112
-
113
- except Exception as e:
114
- if verbose: Global.hr(); glv.errprint(f"Error: {e}")
115
- errorList['downloadFailed']['func'](name, id)
116
-
117
-
118
-
119
- # handle in case key and name is given
120
- elif args.id and args.name:
121
-
122
- # simulation mode
123
- if args.simulate:
124
- print("Simulating the download process. No files will be downloaded.")
125
- print("Id to be processed: ", args.id)
126
- print("Name to be processed: ", args.name)
127
- exit(0)
128
-
129
- try:
130
-
131
- Main(id=args.id,
132
- name=args.name,
133
- directory=OUT_DIRECTORY,
134
- ffmpeg=state['ffmpeg'],
135
- vsdPath=state['vsd'],
136
- token=prefs['token'],
137
- mp4d=state['mp4decrypt'],
138
- tmpDir=prefs['tmpDir'],
139
- verbose=verbose).process()
140
-
141
- except Exception as e:
142
- if verbose: Global.hr(); glv.errprint(f"Error: {e}")
143
- errorList['downloadFailed']['func'](args.name, args.id)
144
- sys.exit(errorList['downloadFailed']['code'])
145
-
146
- # in case neither is used
147
- else:
148
- exit(1)
149
-
150
-
151
- if __name__ == "__main__":
152
  main()
 
1
+ import argparse
2
+ from mainLogic.error import errorList
3
+ from mainLogic.utils.glv import Global
4
+ import sys
5
+ from mainLogic.utils.os2 import SysFunc
6
+ import os
7
+ from mainLogic.main import Main
8
+ from beta.shellLogic import shell
9
+ from mainLogic.startup.checkup import CheckState
10
+
11
+ # global variables
12
+ prefs = {}
13
+ glv = Global()
14
+ os2 = SysFunc()
15
+
16
+ # hardcoding the list of executables required for the script to run
17
+ # should be available in the PATH or the user should provide the path to the executables
18
+ EXECUTABLES = glv.EXECUTABLES
19
+
20
+
21
+
22
+
23
+ def main():
24
+
25
+ # parsing the arguments
26
+ parser = argparse.ArgumentParser(description='PhysicsWallah M3u8 parser.')
27
+
28
+ parser.add_argument('--csv-file', type=str, help='Input csv file. Legacy Support too.')
29
+ parser.add_argument('--id', type=str,
30
+ help='PhysicsWallh Video Id for single usage. Incompatible with --csv-file. Must be used '
31
+ 'with --name')
32
+ parser.add_argument('--name', type=str,
33
+ help='Name for the output file. Incompatible with --csv-file. Must be used with --id')
34
+ parser.add_argument('--dir', type=str, help='Output Directory')
35
+ parser.add_argument('--verbose', action='store_true', help='Verbose Output')
36
+ parser.add_argument('--shell',action='store_true',help='Start the shell')
37
+ parser.add_argument('--version', action='version', version='%(prog)s 1.0')
38
+ parser.add_argument('--simulate', action='store_true',
39
+ help='Simulate the download process. No files will be downloaded.)')
40
+
41
+ args = parser.parse_args()
42
+
43
+ if args.shell:
44
+ shell.main()
45
+
46
+
47
+ # user_input is given preference i.e if --verbose is true it will override
48
+ # however if --verbose is false but prefs['verbose'] is true
49
+ glv.vout = args.verbose
50
+
51
+ global prefs
52
+
53
+ # check if all dependencies are installed
54
+ state = CheckState().checkup(EXECUTABLES, directory=args.dir,verbose=glv.vout)
55
+ prefs = state['prefs']
56
+
57
+
58
+ # --------------------------------------------------------------------------------------------------------------------------------------
59
+ # setting verbose output
60
+ # gives preference to user input
61
+ if not glv.vout and prefs['verbose']: glv.vout = prefs['verbose']
62
+
63
+ verbose = glv.vout
64
+
65
+ OUT_DIRECTORY = prefs['dir']
66
+ if verbose: Global.hr(); glv.dprint(f"Tmp Dir is: {SysFunc.modify_path(prefs['tmpDir'])}")
67
+ if verbose: Global.hr(); glv.dprint(f'Output Directory: {OUT_DIRECTORY}')
68
+ if verbose: Global.hr(); glv.dprint(f"Horizontal Rule: {not Global.disable_hr}")
69
+
70
+ # --------------------------------------------------------------------------------------------------------------------------------------
71
+ # end of loading user preferences
72
+
73
+
74
+ # starting the main process
75
+
76
+ #if both csv file and (id or name) is provided then -> exit with error code 3
77
+ if args.csv_file and (args.id or args.name):
78
+ print("Both csv file and id (or name) is provided. Unable to decide. Aborting! ...")
79
+ sys.exit(3)
80
+
81
+ # handle in case --csv-file is provided
82
+ if args.csv_file:
83
+
84
+ # simulation mode
85
+ if args.simulate:
86
+ print("Simulating the download csv process. No files will be downloaded.")
87
+ print("File to be processed: ", args.csv_file)
88
+ exit(0)
89
+
90
+ # exiting in case the CSV File is not found
91
+ if not os.path.exists(args.csv_file):
92
+ errorList['csvFileNotFound']['func'](args.csv_file)
93
+ sys.exit(errorList['csvFileNotFound']['code'])
94
+
95
+ with open(args.csv_file, 'r') as f:
96
+ for line in f:
97
+ name, id = line.strip().split(',')
98
+
99
+ # adding support for csv file with partial errors
100
+ try:
101
+ Main(id=id,
102
+ name=name,
103
+ directory=OUT_DIRECTORY,
104
+ ffmpeg=state['ffmpeg'],
105
+ vsdPath=state['vsd'],
106
+ token=prefs['token'],
107
+ mp4d=state['mp4decrypt'],
108
+ tmpDir=prefs['tmpDir'],
109
+ verbose=verbose,
110
+ suppress_exit=True # suppress exit in case of error (as multiple files are being processed)
111
+ ).process()
112
+
113
+ except Exception as e:
114
+ if verbose: Global.hr(); glv.errprint(f"Error: {e}")
115
+ errorList['downloadFailed']['func'](name, id)
116
+
117
+
118
+
119
+ # handle in case key and name is given
120
+ elif args.id and args.name:
121
+
122
+ # simulation mode
123
+ if args.simulate:
124
+ print("Simulating the download process. No files will be downloaded.")
125
+ print("Id to be processed: ", args.id)
126
+ print("Name to be processed: ", args.name)
127
+ exit(0)
128
+
129
+ try:
130
+
131
+ Main(id=args.id,
132
+ name=args.name,
133
+ directory=OUT_DIRECTORY,
134
+ ffmpeg=state['ffmpeg'],
135
+ vsdPath=state['vsd'],
136
+ token=prefs['token'],
137
+ mp4d=state['mp4decrypt'],
138
+ tmpDir=prefs['tmpDir'],
139
+ verbose=verbose).process()
140
+
141
+ except Exception as e:
142
+ if verbose: Global.hr(); glv.errprint(f"Error: {e}")
143
+ errorList['downloadFailed']['func'](args.name, args.id)
144
+ sys.exit(errorList['downloadFailed']['code'])
145
+
146
+ # in case neither is used
147
+ else:
148
+ exit(1)
149
+
150
+
151
+ if __name__ == "__main__":
152
  main()