Chris Pomeroy
2022-08-27 046d0db37e8364e43e5bc6cf5748ab3aaf75e642
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env -S python -u
import argparse
import os
import glob
import subprocess
import json
import re
import requests
import sys
from querysubsonic import findalbumbyname
 
# arguments
# activation_key, file name, codec(default to mp3)
 
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--single", help="Use this option to create a single file. This is false by default", action="store_true")
parser.add_argument("-d", "--dpath", help="Use this to set the destination path. Otherwise I will use the current directory")
parser.add_argument("-v", "--verbose", help="Send output to stdout", action="store_true")
parser.add_argument("filename", help="Filename to convert, or directory to look in")
 
args = parser.parse_args()
 
act_byte = ""
metadata = ""
mode = ""
stats = ""
 
if args.dpath:
    path = args.dpath
else:
    path = os.getcwd()
 
if args.single:
    mode = 'single'
else:
    mode = 'chapter'
 
if args.verbose:
    stats = "-stats"
else:
    stats = "-nostats"
 
 
def getmetadata(aaxfile):
    # Returns the metadata from an aax file
    ret = subprocess.run(["ffprobe", "-v", "info", "-hide_banner", "-show_format", "-show_chapters",
                          "-print_format", "json", os.path.abspath(aaxfile)], capture_output=True)
 
    mdata = json.loads(ret.stdout)
    aret = ret.stderr.decode().split('\n')[0]
    mdata["checksum"] = aret.split()[-1]
    return mdata
 
 
def getmetabitrate():
    # Return the bitrate of the media
    bit_rate = metadata['format']['bit_rate']
    return bit_rate[:2]
 
 
def getmetacopyright():
    # Return normalized copyright data
    copyright = normalize_data(metadata['format']['tags']['copyright'])
    return copyright
 
 
def getmetadatatags(key):
    # get specific data
    tag = metadata['format']['tags'][key]
    return " ".join(tag.split())
 
 
def normalize_data(data):
    # Return a normalized title
    data = data.replace(" ", "_")
    pattern = re.compile('\W')
    return re.sub(pattern, '', data)
 
 
def reencode(aaxfile, outpath):
    # decrypt and reencode to mp3
    command = ("ffmpeg -loglevel error {} -activation_bytes {} -i {} -vn -codec:a libmp3lame -ab {}k -map_metadata -1 "
               "-metadata \"title={}\" -metadata 'artist={}' -metadata 'album_artist={}' -metadata \"album={}\" -metadata 'date={}' "
               "-metadata track=1/1 -metadata 'genre={}' -metadata 'copyright={}' \"{}\" ").format(stats, act_byte, aaxfile, getmetabitrate(),
                                                                                                   getmetadatatags('title'), getmetadatatags('artist'),
                                                                                                   getmetadatatags('album_artist'), getmetadatatags('album'),
                                                                                                   getmetadatatags('date'), getmetadatatags('genre'),
                                                                                                   getmetacopyright(), outpath)
    if args.verbose:
        print(command)
        process = subprocess.run(command, shell=True, capture_output=True)
        # while True:
        #     output = process.stdout.readline()
        #     if output == '' and process.poll() is not None:
        #         break
        #     if output:
        #         print(output.strip())
        rc = process.stdout
        return rc
    else:
        process = subprocess.run(command, shell=True)
    return
 
 
def getchaptercount():
    # Get the number of chapters
    ccount = metadata['chapters']
    return len(ccount)
 
 
def getchaptermetadata(cid, key):
    # get the Chapter metadata
    for i in metadata['chapters']:
        if i['id'] == cid:
            return i[key]
 
 
def movetochapters(path, outpath, chapter, title, start, end):
    # Creating individual chapters
 
    outfile = "{}/Ch-{}_{}.mp3".format(outpath, chapter, title.replace(' ', '_'))
    command = "ffmpeg -loglevel error {} -i \"{}\" -ss {} -to {} -codec:a copy -metadata 'track={}' \"{}\"".format(stats, path,
                                                                                                                   start, end,
                                                                                                                   chapter, outfile)
    if args.verbose:
        print(command)
        process = subprocess.run(command, shell=True, capture_output=True)
        # while True:
        #     output = process.stdout.readline()
        #     if output == '' and process.poll() is not None:
        #         break
        #     if output:
        #         print(output.strip())
        rc = process.poll()
        return rc
    else:
        process = subprocess.run(command, shell=True)
    return
 
 
def getcoverart(path, outpath):
    # Pull the coverart from the file
    command = "ffmpeg -loglevel error -activation_bytes {} -i \"{}\" -an -codec:v copy \"{}/cover.jpg\"".format(act_byte,
                                                                                                                path, outpath)
    if args.verbose:
        print(command)
        subprocess.run(command, shell=True)
    return
 
 
def getcorrectkey():
    # request the key for the checksum
    try:
        r = requests.post('http://faas.darkurthe.net/function/checkkey', metadata['checksum'], verify=False, timeout=None)
        return r.text.strip()
    except requests.exceptions.HTTPError as err:
        raise err
        return None
 
 
for rfile in glob.glob(args.filename):
    if rfile.find("aax") != -1 and os.path.isfile(rfile):
        metadata = getmetadata(rfile)
        album = getmetadatatags('album')
        # See if we got it already
        if not findalbumbyname(album):
            artist = normalize_data(getmetadatatags('artist'))
            title = normalize_data(getmetadatatags('title'))
            act_byte = getcorrectkey()
            if act_byte is None:
                sys.exit("Can't continue with this file {rfile}")
            else:
                ddir = "%s/%s/%s" % (path, artist, title)
                single_file_path = "/processing/%s.mp3" % (title)
                if not os.path.exists(ddir):
                    os.makedirs(ddir)
                print(ddir)
                reencode(rfile, single_file_path)
                if mode == 'chapter':
                    chapter = 0
                    numchapters = getchaptercount()
                    while (numchapters > 0):
                        cstart = getchaptermetadata(chapter, 'start_time')
                        cend = getchaptermetadata(chapter, 'end_time')
                        chapter += 1
                        numchapters -= 1
                        schap = str(chapter).zfill(2)
                        movetochapters(single_file_path, ddir, schap, title, cstart, cend)
                    os.remove(single_file_path)
                getcoverart(rfile, ddir)
        else:
            print('We have that book already')