2023-06-12 12:56:14 +00:00
|
|
|
from typing import Set, Type, Dict, List
|
|
|
|
from pathlib import Path
|
|
|
|
import re
|
|
|
|
|
2023-06-20 14:40:34 +00:00
|
|
|
from .utils import cli_function
|
2023-06-21 12:05:40 +00:00
|
|
|
from .options.first_config import initial_config
|
2023-06-20 14:40:34 +00:00
|
|
|
|
2023-09-10 14:54:06 +00:00
|
|
|
from ..utils.config import write_config, main_settings
|
2023-06-20 14:40:34 +00:00
|
|
|
from ..utils.regex import URL_PATTERN
|
|
|
|
from ..utils.string_processing import fit_to_file_system
|
2023-10-23 14:21:44 +00:00
|
|
|
from ..utils.support_classes.query import Query
|
|
|
|
from ..utils.support_classes.download_result import DownloadResult
|
2023-06-20 15:07:32 +00:00
|
|
|
from ..utils.exception.download import UrlNotFoundException
|
2024-01-16 08:53:51 +00:00
|
|
|
from ..utils.enums.colors import BColors
|
2023-06-20 15:07:32 +00:00
|
|
|
from ..download.results import Results, Option, PageResults
|
2023-06-20 14:40:34 +00:00
|
|
|
from ..download.page_attributes import Pages
|
|
|
|
from ..pages import Page
|
|
|
|
from ..objects import Song, Album, Artist, DatabaseObject
|
2023-06-12 12:56:14 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
This is the implementation of the Shell
|
|
|
|
|
|
|
|
# Behaviour
|
|
|
|
|
|
|
|
## Searching
|
|
|
|
|
|
|
|
```mkshell
|
|
|
|
> s: {querry or url}
|
|
|
|
|
|
|
|
# examples
|
|
|
|
> s: https://musify.club/release/some-random-release-183028492
|
|
|
|
> s: r: #a an Artist #r some random Release
|
|
|
|
```
|
|
|
|
|
|
|
|
Searches for an url, or an query
|
|
|
|
|
|
|
|
### Query Syntax
|
|
|
|
|
|
|
|
```
|
|
|
|
#a {artist} #r {release} #t {track}
|
|
|
|
```
|
|
|
|
|
|
|
|
You can escape stuff like `#` doing this: `\#`
|
|
|
|
|
|
|
|
## Downloading
|
|
|
|
|
|
|
|
To download something, you either need a direct link, or you need to have already searched for options
|
|
|
|
|
|
|
|
```mkshell
|
|
|
|
> d: {option ids or direct url}
|
|
|
|
|
|
|
|
# examples
|
|
|
|
> d: 0, 3, 4
|
|
|
|
> d: 1
|
|
|
|
> d: https://musify.club/release/some-random-release-183028492
|
|
|
|
```
|
|
|
|
|
|
|
|
## Misc
|
|
|
|
|
|
|
|
### Exit
|
|
|
|
|
|
|
|
```mkshell
|
|
|
|
> q
|
|
|
|
> quit
|
|
|
|
> exit
|
|
|
|
> abort
|
|
|
|
```
|
|
|
|
|
|
|
|
### Current Options
|
|
|
|
|
|
|
|
```mkshell
|
|
|
|
> .
|
|
|
|
```
|
|
|
|
|
|
|
|
### Previous Options
|
|
|
|
|
|
|
|
```
|
|
|
|
> ..
|
|
|
|
```
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
EXIT_COMMANDS = {"q", "quit", "exit", "abort"}
|
|
|
|
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
|
|
|
|
PAGE_NAME_FILL = "-"
|
|
|
|
MAX_PAGE_LEN = 21
|
|
|
|
|
|
|
|
|
|
|
|
def get_existing_genre() -> List[str]:
|
|
|
|
"""
|
|
|
|
gets the name of all subdirectories of shared.MUSIC_DIR,
|
|
|
|
but filters out all directories, where the name matches with any patern
|
|
|
|
from shared.NOT_A_GENRE_REGEX.
|
|
|
|
"""
|
|
|
|
existing_genres: List[str] = []
|
|
|
|
|
|
|
|
# get all subdirectories of MUSIC_DIR, not the files in the dir.
|
2023-09-10 14:54:06 +00:00
|
|
|
existing_subdirectories: List[Path] = [f for f in main_settings["music_directory"].iterdir() if f.is_dir()]
|
2023-06-12 12:56:14 +00:00
|
|
|
|
|
|
|
for subdirectory in existing_subdirectories:
|
|
|
|
name: str = subdirectory.name
|
|
|
|
|
2023-09-10 14:54:06 +00:00
|
|
|
if not any(re.match(regex_pattern, name) for regex_pattern in main_settings["not_a_genre_regex"]):
|
2023-06-12 12:56:14 +00:00
|
|
|
existing_genres.append(name)
|
|
|
|
|
|
|
|
existing_genres.sort()
|
|
|
|
|
|
|
|
return existing_genres
|
|
|
|
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def get_genre():
|
|
|
|
existing_genres = get_existing_genre()
|
|
|
|
for i, genre_option in enumerate(existing_genres):
|
|
|
|
print(f"{i + 1:0>2}: {genre_option}")
|
|
|
|
|
|
|
|
while True:
|
|
|
|
genre = input("Id or new genre: ")
|
|
|
|
|
|
|
|
if genre.isdigit():
|
|
|
|
genre_id = int(genre) - 1
|
|
|
|
if genre_id >= len(existing_genres):
|
|
|
|
print(f"No genre under the id {genre_id + 1}.")
|
|
|
|
continue
|
|
|
|
|
|
|
|
return existing_genres[genre_id]
|
|
|
|
|
|
|
|
new_genre = fit_to_file_system(genre)
|
|
|
|
|
|
|
|
agree_inputs = {"y", "yes", "ok"}
|
|
|
|
verification = input(f"create new genre \"{new_genre}\"? (Y/N): ").lower()
|
|
|
|
if verification in agree_inputs:
|
|
|
|
return new_genre
|
2024-01-15 11:48:36 +00:00
|
|
|
|
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def help_message():
|
|
|
|
print()
|
2023-09-10 14:54:06 +00:00
|
|
|
print(main_settings["happy_messages"])
|
2023-06-12 12:56:14 +00:00
|
|
|
print()
|
|
|
|
|
|
|
|
|
2023-06-20 14:40:34 +00:00
|
|
|
class Downloader:
|
2023-06-12 12:56:14 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
2024-01-15 11:48:36 +00:00
|
|
|
exclude_pages: Set[Type[Page]] = None,
|
2023-06-12 12:56:14 +00:00
|
|
|
exclude_shady: bool = False,
|
|
|
|
max_displayed_options: int = 10,
|
|
|
|
option_digits: int = 3,
|
2023-06-20 17:30:48 +00:00
|
|
|
genre: str = None,
|
|
|
|
process_metadata_anyway: bool = False,
|
2023-06-12 12:56:14 +00:00
|
|
|
) -> None:
|
|
|
|
self.pages: Pages = Pages(exclude_pages=exclude_pages, exclude_shady=exclude_shady)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.page_dict: Dict[str, Type[Page]] = dict()
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.max_displayed_options = max_displayed_options
|
|
|
|
self.option_digits: int = option_digits
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-16 08:43:35 +00:00
|
|
|
self.current_results: Results = None
|
2023-06-16 08:31:57 +00:00
|
|
|
self._result_history: List[Results] = []
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.genre = genre or get_genre()
|
2023-06-20 17:30:48 +00:00
|
|
|
self.process_metadata_anyway = process_metadata_anyway
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
print()
|
|
|
|
print(f"Downloading to: \"{self.genre}\"")
|
|
|
|
print()
|
|
|
|
|
|
|
|
def print_current_options(self):
|
|
|
|
self.page_dict = dict()
|
2023-06-12 17:46:46 +00:00
|
|
|
|
|
|
|
print()
|
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
page_count = 0
|
|
|
|
for option in self.current_results.formated_generator(max_items_per_page=self.max_displayed_options):
|
|
|
|
if isinstance(option, Option):
|
2024-01-16 09:08:08 +00:00
|
|
|
color = BColors.BOLD if self.pages.is_downloadable(option.music_object) else BColors.ENDC
|
|
|
|
print(f"{color}{option.index:0{self.option_digits}} {option.music_object.option_string}{BColors.ENDC}")
|
2023-06-12 12:56:14 +00:00
|
|
|
else:
|
2024-01-15 11:48:36 +00:00
|
|
|
prefix = ALPHABET[page_count % len(ALPHABET)]
|
|
|
|
print(
|
2024-01-16 08:53:51 +00:00
|
|
|
f"{BColors.HEADER}({prefix}) ------------------------{option.__name__:{PAGE_NAME_FILL}<{MAX_PAGE_LEN}}------------{BColors.ENDC}")
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.page_dict[prefix] = option
|
|
|
|
self.page_dict[option.__name__] = option
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
page_count += 1
|
2023-06-12 17:46:46 +00:00
|
|
|
|
|
|
|
print()
|
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def set_current_options(self, current_options: Results):
|
2023-09-10 14:54:06 +00:00
|
|
|
if main_settings["result_history"]:
|
2023-06-16 08:31:57 +00:00
|
|
|
self._result_history.append(current_options)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-09-10 14:54:06 +00:00
|
|
|
if main_settings["history_length"] != -1:
|
|
|
|
if len(self._result_history) > main_settings["history_length"]:
|
2023-06-16 08:31:57 +00:00
|
|
|
self._result_history.pop(0)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.current_results = current_options
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-16 08:31:57 +00:00
|
|
|
def previous_option(self) -> bool:
|
2023-09-10 14:54:06 +00:00
|
|
|
if not main_settings["result_history"]:
|
|
|
|
print("History is turned of.\nGo to main_settings, and change the value at 'result_history' to 'true'.")
|
2023-06-16 08:31:57 +00:00
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-16 08:31:57 +00:00
|
|
|
if len(self._result_history) <= 1:
|
|
|
|
print(f"No results in history.")
|
|
|
|
return False
|
|
|
|
self._result_history.pop()
|
|
|
|
self.current_results = self._result_history[-1]
|
|
|
|
return True
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def _process_parsed(self, key_text: Dict[str, str], query: str) -> Query:
|
|
|
|
song = None if not "t" in key_text else Song(title=key_text["t"], dynamic=True)
|
|
|
|
album = None if not "r" in key_text else Album(title=key_text["r"], dynamic=True)
|
|
|
|
artist = None if not "a" in key_text else Artist(name=key_text["a"], dynamic=True)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if song is not None:
|
2023-07-28 07:24:14 +00:00
|
|
|
if album is not None:
|
|
|
|
song.album_collection.append(album)
|
|
|
|
if artist is not None:
|
|
|
|
song.main_artist_collection.append(artist)
|
2023-06-12 12:56:14 +00:00
|
|
|
return Query(raw_query=query, music_object=song)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if album is not None:
|
2023-07-28 07:24:14 +00:00
|
|
|
if artist is not None:
|
|
|
|
album.artist_collection.append(artist)
|
2023-06-12 12:56:14 +00:00
|
|
|
return Query(raw_query=query, music_object=album)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if artist is not None:
|
|
|
|
return Query(raw_query=query, music_object=artist)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
return Query(raw_query=query)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def search(self, query: str):
|
2023-06-12 15:40:54 +00:00
|
|
|
if re.match(URL_PATTERN, query) is not None:
|
2023-06-20 15:07:32 +00:00
|
|
|
try:
|
|
|
|
page, data_object = self.pages.fetch_url(query)
|
|
|
|
except UrlNotFoundException as e:
|
|
|
|
print(f"{e.url} could not be attributed/parsed to any yet implemented site.\n"
|
|
|
|
f"PR appreciated if the site isn't implemented.\n"
|
|
|
|
f"Recommendations and suggestions on sites to implement appreciated.\n"
|
|
|
|
f"But don't be a bitch if I don't end up implementing it.")
|
|
|
|
return
|
2023-06-12 17:46:46 +00:00
|
|
|
self.set_current_options(PageResults(page, data_object.options))
|
2023-06-12 15:40:54 +00:00
|
|
|
self.print_current_options()
|
|
|
|
return
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
special_characters = "#\\"
|
|
|
|
query = query + " "
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
key_text = {}
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
skip_next = False
|
|
|
|
escape_next = False
|
|
|
|
new_text = ""
|
|
|
|
latest_key: str = None
|
|
|
|
for i in range(len(query) - 1):
|
|
|
|
current_char = query[i]
|
2024-01-15 11:48:36 +00:00
|
|
|
next_char = query[i + 1]
|
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if skip_next:
|
|
|
|
skip_next = False
|
|
|
|
continue
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if escape_next:
|
|
|
|
new_text += current_char
|
|
|
|
escape_next = False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
# escaping
|
|
|
|
if current_char == "\\":
|
|
|
|
if next_char in special_characters:
|
|
|
|
escape_next = True
|
|
|
|
continue
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if current_char == "#":
|
|
|
|
if latest_key is not None:
|
|
|
|
key_text[latest_key] = new_text
|
|
|
|
new_text = ""
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
latest_key = next_char
|
|
|
|
skip_next = True
|
|
|
|
continue
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
new_text += current_char
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if latest_key is not None:
|
|
|
|
key_text[latest_key] = new_text
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
parsed_query: Query = self._process_parsed(key_text, query)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.set_current_options(self.pages.search(parsed_query))
|
|
|
|
self.print_current_options()
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def goto(self, index: int):
|
|
|
|
page: Type[Page]
|
|
|
|
music_object: DatabaseObject
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
try:
|
|
|
|
page, music_object = self.current_results.get_music_object_by_index(index)
|
2023-06-12 15:40:54 +00:00
|
|
|
except KeyError:
|
2023-06-12 12:56:14 +00:00
|
|
|
print()
|
|
|
|
print(f"The option {index} doesn't exist.")
|
|
|
|
print()
|
|
|
|
return
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.pages.fetch_details(music_object)
|
2023-12-29 15:15:54 +00:00
|
|
|
|
|
|
|
print(music_object)
|
|
|
|
print(music_object.options)
|
2023-06-12 12:56:14 +00:00
|
|
|
self.set_current_options(PageResults(page, music_object.options))
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
self.print_current_options()
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def download(self, download_str: str, download_all: bool = False) -> bool:
|
2023-06-12 15:40:54 +00:00
|
|
|
to_download: List[DatabaseObject] = []
|
|
|
|
|
|
|
|
if re.match(URL_PATTERN, download_str) is not None:
|
2023-06-12 17:46:46 +00:00
|
|
|
_, music_objects = self.pages.fetch_url(download_str)
|
2023-06-12 15:40:54 +00:00
|
|
|
to_download.append(music_objects)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
else:
|
|
|
|
index: str
|
|
|
|
for index in download_str.split(", "):
|
|
|
|
if not index.strip().isdigit():
|
|
|
|
print()
|
|
|
|
print(f"Every download thingie has to be an index, not {index}.")
|
|
|
|
print()
|
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
for index in download_str.split(", "):
|
|
|
|
to_download.append(self.current_results.get_music_object_by_index(int(index))[1])
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
print()
|
|
|
|
print("Downloading:")
|
|
|
|
for download_object in to_download:
|
|
|
|
print(download_object.option_string)
|
|
|
|
print()
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
_result_map: Dict[DatabaseObject, DownloadResult] = dict()
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
for database_object in to_download:
|
2024-01-15 11:48:36 +00:00
|
|
|
r = self.pages.download(music_object=database_object, genre=self.genre, download_all=download_all,
|
|
|
|
process_metadata_anyway=self.process_metadata_anyway)
|
2023-06-12 15:40:54 +00:00
|
|
|
_result_map[database_object] = r
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 15:40:54 +00:00
|
|
|
for music_object, result in _result_map.items():
|
|
|
|
print()
|
|
|
|
print(music_object.option_string)
|
|
|
|
print(result)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 17:46:46 +00:00
|
|
|
return True
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def process_input(self, input_str: str) -> bool:
|
|
|
|
input_str = input_str.strip()
|
|
|
|
processed_input: str = input_str.lower()
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if processed_input in EXIT_COMMANDS:
|
|
|
|
return True
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if processed_input == ".":
|
|
|
|
self.print_current_options()
|
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-16 08:31:57 +00:00
|
|
|
if processed_input == "..":
|
|
|
|
if self.previous_option():
|
|
|
|
self.print_current_options()
|
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if processed_input.startswith("s: "):
|
|
|
|
self.search(input_str[3:])
|
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if processed_input.startswith("d: "):
|
|
|
|
return self.download(input_str[3:])
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if processed_input.isdigit():
|
|
|
|
self.goto(int(processed_input))
|
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
if processed_input != "help":
|
|
|
|
print("Invalid input.")
|
|
|
|
help_message()
|
|
|
|
return False
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-12 12:56:14 +00:00
|
|
|
def mainloop(self):
|
|
|
|
while True:
|
|
|
|
if self.process_input(input("> ")):
|
|
|
|
return
|
2023-06-20 14:40:34 +00:00
|
|
|
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-20 14:40:34 +00:00
|
|
|
@cli_function
|
|
|
|
def download(
|
|
|
|
genre: str = None,
|
|
|
|
download_all: bool = False,
|
|
|
|
direct_download_url: str = None,
|
2023-06-20 17:30:48 +00:00
|
|
|
command_list: List[str] = None,
|
|
|
|
process_metadata_anyway: bool = False,
|
2023-06-20 14:40:34 +00:00
|
|
|
):
|
2023-09-10 14:54:06 +00:00
|
|
|
if main_settings["hasnt_yet_started"]:
|
2023-06-21 12:05:40 +00:00
|
|
|
code = initial_config()
|
|
|
|
if code == 0:
|
2023-09-10 14:54:06 +00:00
|
|
|
main_settings["hasnt_yet_started"] = False
|
2023-06-21 12:05:40 +00:00
|
|
|
write_config()
|
|
|
|
print("Restart the programm to use it.")
|
2023-09-12 10:45:50 +00:00
|
|
|
else:
|
|
|
|
print("Something went wrong configuring.")
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-20 17:30:48 +00:00
|
|
|
shell = Downloader(genre=genre, process_metadata_anyway=process_metadata_anyway)
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-20 14:40:34 +00:00
|
|
|
if command_list is not None:
|
|
|
|
for command in command_list:
|
|
|
|
shell.process_input(command)
|
|
|
|
return
|
|
|
|
|
|
|
|
if direct_download_url is not None:
|
|
|
|
if shell.download(direct_download_url, download_all=download_all):
|
|
|
|
return
|
2024-01-15 11:48:36 +00:00
|
|
|
|
2023-06-20 14:40:34 +00:00
|
|
|
shell.mainloop()
|