Prevents modifying the config if its already being modified.
Co-authored-by: Arjix <53124886+ArjixGamer@users.noreply.github.com>master
parent
a4f3eeee51
commit
913e6bc842
|
@ -1,7 +1,11 @@
|
||||||
import click
|
from anime_downloader.util import is_running
|
||||||
|
if is_running(regex=r'python|anime|config', expected_matches=3):
|
||||||
|
raise Exception('Another instance of "anime config" is already running!')
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import click
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
from anime_downloader.config import APP_DIR, Config
|
from anime_downloader.config import APP_DIR, Config # noqa
|
||||||
|
|
||||||
data = Config._CONFIG
|
data = Config._CONFIG
|
||||||
|
|
||||||
|
@ -20,7 +24,7 @@ def traverse_json(data, previous=''):
|
||||||
click.echo(create_table(keys, previous))
|
click.echo(create_table(keys, previous))
|
||||||
val = click.prompt("Select Option", type=int, default=1) - 1
|
val = click.prompt("Select Option", type=int, default=1) - 1
|
||||||
|
|
||||||
if type(data[keys[val]]) == dict:
|
if isinstance(data[keys[val]], dict):
|
||||||
traverse_json(data[keys[val]], keys[val])
|
traverse_json(data[keys[val]], keys[val])
|
||||||
else:
|
else:
|
||||||
click.echo(f"Current value: {data[keys[val]]}")
|
click.echo(f"Current value: {data[keys[val]]}")
|
||||||
|
@ -32,8 +36,9 @@ def traverse_json(data, previous=''):
|
||||||
except (SyntaxError, NameError) as e:
|
except (SyntaxError, NameError) as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if type(newVal) != type(data[keys[val]]):
|
if not isinstance(newVal, type(data[keys[val]])):
|
||||||
choice = click.confirm(f"{newVal} appears to be of an incorrect type. Continue")
|
choice = click.confirm(
|
||||||
|
f"{newVal} appears to be of an incorrect type. Continue")
|
||||||
|
|
||||||
if not choice:
|
if not choice:
|
||||||
exit()
|
exit()
|
||||||
|
@ -41,7 +46,8 @@ def traverse_json(data, previous=''):
|
||||||
try:
|
try:
|
||||||
newVal = type(data[keys[val]])(newVal)
|
newVal = type(data[keys[val]])(newVal)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
click.echo(f"'{newVal}' could not be converted to the correct type")
|
click.echo(
|
||||||
|
f"'{newVal}' could not be converted to the correct type")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
data[keys[val]] = newVal
|
data[keys[val]] = newVal
|
||||||
|
|
|
@ -73,7 +73,8 @@ sitenames = [v[1] for v in ALL_ANIME_SITES]
|
||||||
'--choice', '-c', type=int,
|
'--choice', '-c', type=int,
|
||||||
help='Choice to start downloading given anime number '
|
help='Choice to start downloading given anime number '
|
||||||
)
|
)
|
||||||
@click.option("--skip-fillers", is_flag=True, help="Skip downloading of fillers.")
|
@click.option("--skip-fillers", is_flag=True,
|
||||||
|
help="Skip downloading of fillers.")
|
||||||
@click.option(
|
@click.option(
|
||||||
"--speed-limit",
|
"--speed-limit",
|
||||||
type=str,
|
type=str,
|
||||||
|
@ -149,16 +150,18 @@ def command(ctx, anime_url, episode_range, url, player, skip_download, quality,
|
||||||
if skip_fillers and fillers:
|
if skip_fillers and fillers:
|
||||||
if episode.ep_no in fillers:
|
if episode.ep_no in fillers:
|
||||||
logger.info(
|
logger.info(
|
||||||
"Skipping episode {} because it is a filler.".format(episode.ep_no))
|
"Skipping episode {} because it is a filler.".format(
|
||||||
|
episode.ep_no))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if url:
|
if url:
|
||||||
util.print_episodeurl(episode)
|
util.print_episodeurl(episode)
|
||||||
|
|
||||||
if player:
|
if player:
|
||||||
episode_range = f"0:{len(animes)}" if not episode_range else episode_range
|
|
||||||
util.play_episode(
|
util.play_episode(
|
||||||
episode, player=player, title=f'{anime.title} - Episode {episode.ep_no}', episodes=episode_range)
|
episode,
|
||||||
|
player=player,
|
||||||
|
title=f'{anime.title} - Episode {episode.ep_no}')
|
||||||
|
|
||||||
if not skip_download:
|
if not skip_download:
|
||||||
if external_downloader:
|
if external_downloader:
|
||||||
|
|
|
@ -50,7 +50,8 @@ sitenames = [v[1] for v in ALL_ANIME_SITES]
|
||||||
@click.option(
|
@click.option(
|
||||||
'--download-metadata', '-dm', is_flag=True,
|
'--download-metadata', '-dm', is_flag=True,
|
||||||
help='Download additional metadata')
|
help='Download additional metadata')
|
||||||
@click.option("--skip-fillers", is_flag=True, help="Skip downloading of fillers.")
|
@click.option("--skip-fillers", is_flag=True,
|
||||||
|
help="Skip downloading of fillers.")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def command(ctx, anime_url, episode_range, player,
|
def command(ctx, anime_url, episode_range, player,
|
||||||
force_download, provider,
|
force_download, provider,
|
||||||
|
@ -94,11 +95,13 @@ def command(ctx, anime_url, episode_range, player,
|
||||||
raise exceptions.NotFoundError('No episodes found within index.')
|
raise exceptions.NotFoundError('No episodes found within index.')
|
||||||
|
|
||||||
# Stores the choices for each provider, to prevent re-prompting search.
|
# Stores the choices for each provider, to prevent re-prompting search.
|
||||||
# As the current setup runs episode wise without this a 12 episode series would give 12+ prompts.
|
# As the current setup runs episode wise without this a 12 episode series
|
||||||
|
# would give 12+ prompts.
|
||||||
choice_dict = {}
|
choice_dict = {}
|
||||||
|
|
||||||
# Doesn't work on nyaa since it only returns one episode.
|
# Doesn't work on nyaa since it only returns one episode.
|
||||||
for episode_range in range(int(episode_range_split[0]), int(episode_range_split[-1]) + 1):
|
for episode_range in range(int(episode_range_split[0]), int(
|
||||||
|
episode_range_split[-1]) + 1):
|
||||||
# Exits if all providers are skipped.
|
# Exits if all providers are skipped.
|
||||||
if [choice_dict[i] for i in choice_dict] == [0] * len(providers):
|
if [choice_dict[i] for i in choice_dict] == [0] * len(providers):
|
||||||
logger.info('All providers skipped, exiting')
|
logger.info('All providers skipped, exiting')
|
||||||
|
@ -115,7 +118,8 @@ def command(ctx, anime_url, episode_range, player,
|
||||||
|
|
||||||
# To make the downloads use the correct name if URL:s are used.
|
# To make the downloads use the correct name if URL:s are used.
|
||||||
real_provider = cls.sitename if cls else provider
|
real_provider = cls.sitename if cls else provider
|
||||||
# This will allow for animeinfo metadata in filename and one filename for multiple providers.
|
# This will allow for animeinfo metadata in filename and one
|
||||||
|
# filename for multiple providers.
|
||||||
rep_dict = {
|
rep_dict = {
|
||||||
'animeinfo_anime_title': util.slugify(info.title),
|
'animeinfo_anime_title': util.slugify(info.title),
|
||||||
'provider': util.slugify(real_provider),
|
'provider': util.slugify(real_provider),
|
||||||
|
@ -128,11 +132,13 @@ def command(ctx, anime_url, episode_range, player,
|
||||||
disable_ssl = False
|
disable_ssl = False
|
||||||
session.get_session().verify = not disable_ssl
|
session.get_session().verify = not disable_ssl
|
||||||
|
|
||||||
# This is just to make choices in providers presistent between searches.
|
# This is just to make choices in providers presistent between
|
||||||
|
# searches.
|
||||||
choice_provider = choice_dict.get(provider)
|
choice_provider = choice_dict.get(provider)
|
||||||
|
|
||||||
if not cls:
|
if not cls:
|
||||||
_anime_url, choice_provider = util.search(anime_url, provider, val=choice_provider, season_info=info, ratio=ratio)
|
_anime_url, choice_provider = util.search(
|
||||||
|
anime_url, provider, val=choice_provider, season_info=info, ratio=ratio)
|
||||||
choice_dict[provider] = choice_provider
|
choice_dict[provider] = choice_provider
|
||||||
if choice_provider == 0 or not _anime_url:
|
if choice_provider == 0 or not _anime_url:
|
||||||
logger.info('Skipped')
|
logger.info('Skipped')
|
||||||
|
@ -145,7 +151,7 @@ def command(ctx, anime_url, episode_range, player,
|
||||||
fallback_qualities=fallback_qualities)
|
fallback_qualities=fallback_qualities)
|
||||||
# I have yet to investigate all errors this can output
|
# I have yet to investigate all errors this can output
|
||||||
# No sources found gives error which exits the script
|
# No sources found gives error which exits the script
|
||||||
except:
|
except BaseException:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.debug('Found anime: {}'.format(anime.title))
|
logger.debug('Found anime: {}'.format(anime.title))
|
||||||
|
@ -153,7 +159,8 @@ def command(ctx, anime_url, episode_range, player,
|
||||||
try:
|
try:
|
||||||
animes = util.parse_ep_str(anime, str(episode_range))
|
animes = util.parse_ep_str(anime, str(episode_range))
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
logger.error('No episode found with index {}'.format(episode_range))
|
logger.error(
|
||||||
|
'No episode found with index {}'.format(episode_range))
|
||||||
continue
|
continue
|
||||||
except:
|
except:
|
||||||
logger.error('Unknown provider error')
|
logger.error('Unknown provider error')
|
||||||
|
@ -167,23 +174,31 @@ def command(ctx, anime_url, episode_range, player,
|
||||||
skip_download = True
|
skip_download = True
|
||||||
|
|
||||||
if download_dir and not skip_download:
|
if download_dir and not skip_download:
|
||||||
logger.info('Downloading to {}'.format(os.path.abspath(download_dir)))
|
logger.info(
|
||||||
|
'Downloading to {}'.format(
|
||||||
|
os.path.abspath(download_dir)))
|
||||||
if skip_fillers:
|
if skip_fillers:
|
||||||
fillers = util.get_filler_episodes(query)
|
fillers = util.get_filler_episodes(query)
|
||||||
for episode in animes:
|
for episode in animes:
|
||||||
if skip_fillers and fillers:
|
if skip_fillers and fillers:
|
||||||
if episode.ep_no in fillers:
|
if episode.ep_no in fillers:
|
||||||
logger.info("Skipping episode {} because it is a filler.".format(episode.ep_no))
|
logger.info(
|
||||||
|
"Skipping episode {} because it is a filler.".format(
|
||||||
|
episode.ep_no))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if download_metadata:
|
if download_metadata:
|
||||||
util.download_metadata(fixed_file_format, info.metadata, episode)
|
util.download_metadata(
|
||||||
|
fixed_file_format, info.metadata, episode)
|
||||||
|
|
||||||
if url:
|
if url:
|
||||||
util.print_episodeurl(episode)
|
util.print_episodeurl(episode)
|
||||||
|
|
||||||
if player:
|
if player:
|
||||||
util.play_episode(episode, player=player, title=f'{anime.title} - Episode {episode.ep_no}')
|
util.play_episode(
|
||||||
|
episode,
|
||||||
|
player=player,
|
||||||
|
title=f'{anime.title} - Episode {episode.ep_no}')
|
||||||
|
|
||||||
if not skip_download:
|
if not skip_download:
|
||||||
if external_downloader:
|
if external_downloader:
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
from anime_downloader.util import is_running
|
||||||
|
if is_running(regex=r'python|anime|watch', expected_matches=3):
|
||||||
|
raise Exception('Another instance of "anime watch" is already running!')
|
||||||
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
@ -90,17 +95,21 @@ def command(anime_name, new, update_all, _list, quality, remove,
|
||||||
watcher.update_anime(anime)
|
watcher.update_anime(anime)
|
||||||
|
|
||||||
if mal_import:
|
if mal_import:
|
||||||
PATH = anime_name # Hack, but needed to prompt the user. Uses the anime name as parameter.
|
# Hack, but needed to prompt the user. Uses the anime name as
|
||||||
|
# parameter.
|
||||||
|
PATH = anime_name
|
||||||
if PATH:
|
if PATH:
|
||||||
query = PATH
|
query = PATH
|
||||||
else:
|
else:
|
||||||
query = click.prompt('Enter the file path for the MAL .xml file', type=str)
|
query = click.prompt(
|
||||||
|
'Enter the file path for the MAL .xml file', type=str)
|
||||||
|
|
||||||
if query.endswith('.xml'):
|
if query.endswith('.xml'):
|
||||||
watcher._import_from_MAL(query)
|
watcher._import_from_MAL(query)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
logging.error("Either the file selected was not an .xml or no file was selected.")
|
logging.error(
|
||||||
|
"Either the file selected was not an .xml or no file was selected.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Defaults the command to anime watch -l all.
|
# Defaults the command to anime watch -l all.
|
||||||
|
@ -128,13 +137,15 @@ def command(anime_name, new, update_all, _list, quality, remove,
|
||||||
|
|
||||||
def command_parser(command):
|
def command_parser(command):
|
||||||
# Returns<kUp> a list of the commands
|
# Returns<kUp> a list of the commands
|
||||||
# new "no neverland" --provider vidstream > ['new', '--provider', 'no neverland', 'vidstream']
|
# new "no neverland" --provider vidstream > ['new', '--provider', 'no
|
||||||
|
# neverland', 'vidstream']
|
||||||
|
|
||||||
# Better than split(' ') because it accounts for quoutes.
|
# Better than split(' ') because it accounts for quoutes.
|
||||||
# Group 3 for quoted command
|
# Group 3 for quoted command
|
||||||
command_regex = r'(("|\')(.*?)("|\')|.*?\s)'
|
command_regex = r'(("|\')(.*?)("|\')|.*?\s)'
|
||||||
matches = re.findall(command_regex, command + " ")
|
matches = re.findall(command_regex, command + " ")
|
||||||
commands = [i[0].strip('"').strip("'").strip() for i in matches if i[0].strip()]
|
commands = [i[0].strip('"').strip("'").strip()
|
||||||
|
for i in matches if i[0].strip()]
|
||||||
return commands
|
return commands
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,8 +170,10 @@ def list_animes(watcher, quality, download_dir, imp=None, _filter=None):
|
||||||
watcher.new(url)
|
watcher.new(url)
|
||||||
|
|
||||||
if key == 'swap':
|
if key == 'swap':
|
||||||
if vals[0] in ['all', 'watching', 'completed', 'planned', 'dropped', 'hold']:
|
if vals[0] in ['all', 'watching', 'completed',
|
||||||
return list_animes(watcher, quality, download_dir, imp=imp, _filter=vals[0])
|
'planned', 'dropped', 'hold']:
|
||||||
|
return list_animes(
|
||||||
|
watcher, quality, download_dir, imp=imp, _filter=vals[0])
|
||||||
|
|
||||||
return list_animes(watcher, quality, download_dir, imp=imp)
|
return list_animes(watcher, quality, download_dir, imp=imp)
|
||||||
else:
|
else:
|
||||||
|
@ -272,7 +285,8 @@ def list_animes(watcher, quality, download_dir, imp=None, _filter=None):
|
||||||
watcher.update(anime)
|
watcher.update(anime)
|
||||||
|
|
||||||
elif key == 'watch_status':
|
elif key == 'watch_status':
|
||||||
if val in ['watching', 'completed', 'dropped', 'planned', 'all']:
|
if val in ['watching', 'completed',
|
||||||
|
'dropped', 'planned', 'all']:
|
||||||
colours = {
|
colours = {
|
||||||
'watching': 'cyan',
|
'watching': 'cyan',
|
||||||
'completed': 'green',
|
'completed': 'green',
|
||||||
|
|
|
@ -493,3 +493,84 @@ class ClickListOption(click.Option):
|
||||||
return ast.literal_eval(value)
|
return ast.literal_eval(value)
|
||||||
except:
|
except:
|
||||||
raise click.BadParameter(value)
|
raise click.BadParameter(value)
|
||||||
|
|
||||||
|
|
||||||
|
class Process:
|
||||||
|
def __init__(self, name, cmdline, pid):
|
||||||
|
self.name = name
|
||||||
|
self.pid = pid
|
||||||
|
self.cmdline = cmdline
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str({
|
||||||
|
'name': self.name,
|
||||||
|
'pid': self.pid,
|
||||||
|
'cmdline': self.cmdline
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def getAllProcesses_Win32():
|
||||||
|
placeholder = list()
|
||||||
|
out = os.popen('WMIC path win32_process get Caption,Processid,Commandline').read(
|
||||||
|
).split('\n')[::2][1:]
|
||||||
|
for line in out:
|
||||||
|
f = line.split()
|
||||||
|
if f:
|
||||||
|
if len(f) > 2:
|
||||||
|
placeholder.append(
|
||||||
|
Process(name=f[0], cmdline=f[1:-1], pid=int(f[-1])))
|
||||||
|
else:
|
||||||
|
placeholder.append(
|
||||||
|
Process(name=f[0], cmdline=None, pid=int(f[-1])))
|
||||||
|
return placeholder
|
||||||
|
|
||||||
|
|
||||||
|
def getAllProcesses_unix():
|
||||||
|
if sys.platform.startswith('darwin'):
|
||||||
|
cmd = 'ps -Ao user,pid,%cpu,%mem,vsz,rss,tt,stat,start,time,command'
|
||||||
|
return []
|
||||||
|
elif sys.startswith('linux'):
|
||||||
|
cmd = 'ps aux'
|
||||||
|
out = os.popen(cmd).read()
|
||||||
|
out = out.split('\n')[1:]
|
||||||
|
placeholder = list()
|
||||||
|
for line in out:
|
||||||
|
try:
|
||||||
|
line_list = line.lower().split()
|
||||||
|
PID = line_list[1]
|
||||||
|
NAME = line_list[10:][0]
|
||||||
|
CMD = line_list[10:]
|
||||||
|
placeholder.append(Process(name=NAME, cmdline=CMD, pid=PID))
|
||||||
|
except IndexError:
|
||||||
|
continue
|
||||||
|
return placeholder
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_processes():
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
return getAllProcesses_Win32()
|
||||||
|
else:
|
||||||
|
return getAllProcesses_unix()
|
||||||
|
|
||||||
|
|
||||||
|
def is_running(regex, expected_matches):
|
||||||
|
"""
|
||||||
|
Iterates through all the processes that are running
|
||||||
|
and returns a boolean if a process matches the regex passed
|
||||||
|
and the groups matched are equal to or more than the expected_matches.
|
||||||
|
"""
|
||||||
|
|
||||||
|
already_running = False
|
||||||
|
dict_pids = {
|
||||||
|
p.pid: [p.name, p.cmdline]
|
||||||
|
for p in get_all_processes()
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.getpid() in dict_pids:
|
||||||
|
del dict_pids[os.getpid()]
|
||||||
|
for key, value in dict_pids.items():
|
||||||
|
if value[1]:
|
||||||
|
list_of_matches = re.findall(regex, ' '.join(value[1]))
|
||||||
|
if list_of_matches and len(list_of_matches) >= expected_matches:
|
||||||
|
already_running = True
|
||||||
|
return already_running
|
||||||
|
|
Loading…
Reference in New Issue