You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
5.6 KiB
Python
129 lines
5.6 KiB
Python
|
|
import argparse
|
|
import os
|
|
import sys
|
|
import logging
|
|
import yaml
|
|
import subprocess
|
|
|
|
def path_to_aax(aax: str):
|
|
if not os.path.isfile(aax):
|
|
raise argparse.ArgumentTypeError(f"Pfad {aax} ist keine gueltige Datei.")
|
|
|
|
if not aax.endswith('.aax'):
|
|
raise argparse.ArgumentTypeError(f"Pfad {aax} ist keine gueltige aax-Datei.")
|
|
|
|
return aax
|
|
|
|
def decrypt_aax_file(aax_path: str, profile:str):
|
|
logging.info("Ich decryyypte...")
|
|
|
|
try:
|
|
#Probe auf die aax Datei fuer Checksum
|
|
cmd_probe = f"ffprobe {aax_path} 2>&1 | grep checksum"
|
|
result_probe = subprocess.run(cmd_probe, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
tmp_checksum = result_probe.stdout
|
|
checksum = tmp_checksum.split('== ')[1]
|
|
checksum = checksum.split('\n')[0] # Ein Line-Break wird am Ende immer noch angehängt -> Muss weg sonst gibts beim nächsten CMD Probleme
|
|
logging.debug(f"Checksum: {checksum}")
|
|
|
|
#Decrypt der Checksum via Rainbowtables für UserId
|
|
cmd_crack = f"./rcrack . -h {checksum} | grep hex"
|
|
cwd_tables = os.path.join(os.getcwd(),'tables')
|
|
result_crack = subprocess.run(cmd_crack, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=cwd_tables)
|
|
tmp_profile_id = result_crack.stdout
|
|
profile_id = tmp_profile_id.split('hex:')[1]
|
|
profile_id = profile_id.split('\n')[0] # Ein Line-Break wird am Ende immer noch angehängt -> Muss weg
|
|
logging.info(f"Profile-Id extrahiert: {profile_id}")
|
|
|
|
#Erstellen von Profil
|
|
with open('profiles.yml', 'w') as profile_yaml:
|
|
yaml.dump({profile : profile_id}, profile_yaml, default_flow_style=False)
|
|
|
|
except subprocess.CalledProcessError as err:
|
|
logging.info(f"Fehler bei Prozess ausfuehrung. {err}")
|
|
|
|
def convert_to_m4b(aax_path:str, profile:str):
|
|
logging.info("Ich converteeee...")
|
|
|
|
try:
|
|
#Laden des Profils
|
|
profiles = None
|
|
with open('profiles.yml', 'r') as profile_yaml:
|
|
profiles = yaml.safe_load(profile_yaml)
|
|
profile_id = profiles.get(profile)
|
|
logging.info(f'Loaded Profile: {profile}')
|
|
|
|
aax_name = aax_path.split('/')[1] # Fuer vollen Datei-Name
|
|
aax_name = aax_name.split('.')[0] # Fuer Datei-Namen ohne Endung
|
|
|
|
#Konvertierung der aax Datei zu m4b
|
|
cmd_ffmpeg = f"ffmpeg -activation_bytes {profile_id} -i {aax_path} -c copy m4b_outputs/{aax_name}.m4b"
|
|
result_ffmpeg = subprocess.run(cmd_ffmpeg, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
logging.info(f"Konvertierung abgeschlossen. m4b_outputs/{aax_name}.m4b erstellt.")
|
|
|
|
|
|
|
|
except subprocess.CalledProcessError as err:
|
|
logging.info(f"Fehler bei Prozess ausfuehrung. {err}")
|
|
# ffmpeg -activation_bytes fc07e32c -i aax_inputs/HowTo-WiemanshinkriegtAbsurdewirklichwissenschaftlicheEmpfehlungenfralleLebenslagen_ep7.aax -c copy output.m4b
|
|
|
|
|
|
if __name__ == "__main__":
|
|
argparser = argparse.ArgumentParser(description="Dieses Script konvertiert verschluesselte Audible Buecher zu unverschluesselte m4b-Audiobooks.")
|
|
cmd_parser = argparser.add_subparsers(dest='cmd', title='Commands', description="Erlaubte Commands", required=True)
|
|
|
|
decrypt_parser = cmd_parser.add_parser('decrypt', help="Extrahiert die User-spezifische ID zur Profil-Bildung von einer bestehenden aax-Datei.")
|
|
decrypt_parser.add_argument('aax', type=path_to_aax, help='Path zu einem zu entschluesselnden aax file des neuen Profils')
|
|
decrypt_parser.add_argument('profile', type=str, help='Profil-Name, zu welchem die extrahierte ID gespeichert wird. (ID ist notwendig fuer Entschluesselung der eigenen Hoerbuecher)')
|
|
|
|
convert_parser = cmd_parser.add_parser('convert', help="Konvertiert ein aax-Audiobook in ein m4b-Audiobook.")
|
|
convert_parser.add_argument('aax', type=path_to_aax, help='Path zu einem zu entschluesselnden aax file')
|
|
convert_parser.add_argument('profile', type=str, help="Zugehoeriges Profile fuer Entschluesselung")
|
|
|
|
convert_all_parser = cmd_parser.add_parser('convert-all', help="Konvertiert alle aax-Audiobooks in m4b-Audiobooks.")
|
|
convert_all_parser.add_argument('profile', type=str, help="Zugehoeriges Profile fuer Entschluesselung")
|
|
|
|
args: argparse.Namespace = argparser.parse_args(args=None if sys.argv[1:] else ['--help'])
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s", datefmt="%d-%m-%Y %H:%M:%S")
|
|
|
|
profiles = None
|
|
try:
|
|
with open('profiles.yml', 'r') as file:
|
|
profiles = yaml.safe_load(file)
|
|
except IOError:
|
|
with open('profiles.yml', 'w') as file:
|
|
profiles = yaml.safe_load('')
|
|
|
|
if args.cmd == "decrypt":
|
|
logging.info(f"Decrypt wird gestartet...")
|
|
aax_file = args.aax
|
|
profile = args.profile
|
|
if profiles is not None and profiles.get(args.profile) is not None:
|
|
logging.info(f"Profil {args.profile} existiert bereits. Konvertierung kann starten.")
|
|
sys.exit()
|
|
|
|
decrypt_aax_file(aax_file, profile)
|
|
|
|
elif args.cmd == "convert":
|
|
|
|
if profiles is None or profiles.get(args.profile) is None or args.profile not in profiles:
|
|
logging.info(f"Profil {args.profile} existiert nicht. Bitte ueber decrypt anlegen lassen.")
|
|
sys.exit()
|
|
|
|
logging.info(f"Convert wird gestartet...")
|
|
aax_file = args.aax
|
|
profile = args.profile
|
|
|
|
convert_to_m4b(aax_file, profile)
|
|
|
|
elif args.cmd == "convert-all":
|
|
logging.info(f"Convert wird fuer alle aax im Input-Ordner durchgefuehrt")
|
|
#TODO Implement
|
|
|
|
|
|
|
|
|
|
|