main.py 8.65 KB
Newer Older
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
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
#!/usr/bin/env python
"""MPAI CAE-ARP Packager.

Implements MPAI CAE-ARP Packager Technical Specification, providing:
- Access Copy Files:
  1. Restored Audio Files.
  2. Editing List.
  3. Set of Irregularity Images in a .zip file.
  4. Irregularity File.
- Preservation Master Files:
  1. Preservation Audio File.
  2. Preservation Audio-Visual File where the audio has been replaced with the Audio of the Preservation Audio File
     fully synchronised with the video.
  3. Set of Irregularity Images in a .zip file.
  4. Irregularity File.
"""

import json
import os
import shutil
import yaml
from moviepy.editor import VideoFileClip, AudioFileClip

__author__ = "Nadir Dalla Pozza"
__copyright__ = "Copyright 2022, Audio Innova S.r.l."
__credits__ = ["Niccolò Pretto", "Nadir Dalla Pozza", "Sergio Canazza"]
27
__license__ = "GPL v3.0"
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
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
__version__ = "1.0.1"
__maintainer__ = "Nadir Dalla Pozza"
__email__ = "nadir.dallapozza@unipd.it"
__status__ = "Production"


# Class for customizing console colors
class ConsoleColors:
    PURPLE = '\033[95m'
    CYAN = '\033[96m'
    DARK_CYAN = '\033[36m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    END = '\033[0m'


if __name__ == '__main__':
    # Read configuration file
    config = object
    try:
        config = yaml.safe_load(open('./config.yaml', 'r'))
53
54
        if 'WORKING_PATH' not in config:
            print(ConsoleColors.RED + 'WORKING_PATH key not found in config.yaml!' + ConsoleColors.END)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
55
            quit(os.EX_CONFIG)
56
57
        if 'PRESERVATION_FILES_NAME' not in config:
            print(ConsoleColors.RED + 'PRESERVATION_FILES_NAME key not found in config.yaml!' + ConsoleColors.END)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
58
59
60
61
62
            quit(os.EX_CONFIG)
    except FileNotFoundError:
        print(ConsoleColors.RED + 'config.yaml file not found!' + ConsoleColors.END)
        quit(os.EX_NOINPUT)

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
63
64
    temp_path = os.path.join(config['WORKING_PATH'], 'temp/', config['PRESERVATION_FILES_NAME'])
    print(temp_path)
65

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
66
67
68
69
    # Access Copy Files
    print('\n' + ConsoleColors.BOLD + 'Creation of Access Copy Files...' + ConsoleColors.END)

    # By default, AccessCopyFiles directory is created in the output path...
70
71
    acf_dir = 'AccessCopyFiles/' + config['PRESERVATION_FILES_NAME'] + '/'
    acf_path = os.path.join(config['WORKING_PATH'], acf_dir)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
72
73
74
75
76
77
    # ...however, if it already exists, it is possible to skip its creation
    make_acf = False
    if not os.path.exists(acf_path):
        # Create directory
        os.mkdir(acf_path)
        make_acf = True
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
78
        print("Access Copy Files directory '% s' created" % acf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
79
    else:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
80
        print((ConsoleColors.PURPLE + "Access Copy Files directory '% s' already exists!" + ConsoleColors.END) % acf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
81
82
83
84
85
86
        overwrite = input('Do you want to overwrite it? [y/n]: ')
        if overwrite.casefold() == 'y':
            # Overwrite directory
            shutil.rmtree(acf_path)
            os.mkdir(acf_path)
            make_acf = True
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
87
            print('Access Copy Files directory overwritten')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
88
89
90
91
92
93
        elif overwrite.casefold() != 'n':
            print(ConsoleColors.RED + 'Unknown command, exiting' + ConsoleColors.END)
            quit(os.EX_USAGE)

    if make_acf:
        # Copy RestoredAudioFiles
94
        raf_path = os.path.join(temp_path, 'RestoredAudioFiles')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
95
        if not os.path.exists(raf_path):
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
96
            print((ConsoleColors.RED + "Restored Audio Files directory '% s' not found!" + ConsoleColors.END) % raf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
97
98
99
            quit(os.EX_NOINPUT)
        restored_audio_files = os.listdir(raf_path)
        if len(restored_audio_files) == 1:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
100
            print((ConsoleColors.YELLOW + "Restored Audio Files directory '% s' is empty" + ConsoleColors.END) % raf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
101
102
103
104
        shutil.copytree(raf_path, os.path.join(acf_path, 'RestoredAudioFiles'))
        print("Restored Audio Files copied")

        # Copy Editing List
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
105
        el_path = os.path.join(temp_path, 'EditingList.json')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
106
        try:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
107
            shutil.copy2(el_path, acf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
108
        except FileNotFoundError:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
109
            print((ConsoleColors.RED + "Editing List file '% s' not found!" + ConsoleColors.END) % el_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
110
111
112
113
            quit(os.EX_NOINPUT)
        print("Editing List copied")

        # Create Irregularity Images archive
114
        ii_path = os.path.join(temp_path, 'IrregularityImages')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
115
        if not os.path.exists(ii_path):
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
116
            print((ConsoleColors.RED + "Irregularity Images directory '% s' not found!" + ConsoleColors.END) % ii_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
117
118
119
            quit(os.EX_NOINPUT)
        irregularity_images = os.listdir(ii_path)
        if len(irregularity_images) == 1:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
120
            print((ConsoleColors.YELLOW + "Irregularity Images directory '% s' is empty" + ConsoleColors.END) % ii_path)
121
        shutil.make_archive(acf_path + 'IrregularityImages', 'zip', temp_path, 'IrregularityImages')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
122
123
124
        print("Irregularity Images archive created")

        # Copy Irregularity File
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
125
        if_path = os.path.join(temp_path, 'TapeIrregularityClassifier_IrregularityFileOutput2.json')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
126
        try:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
127
            shutil.copy2(if_path, os.path.join(acf_path, 'IrregularityFile.json'))
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
128
        except FileNotFoundError:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
129
            print((ConsoleColors.RED + "Irregularity File file '% s' not found!" + ConsoleColors.END) % if_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
130
131
132
            quit(os.EX_NOINPUT)
        print("Irregularity File copied")

133
134
135
        # End Access Copy Files
        print(ConsoleColors.GREEN + ConsoleColors.BOLD + "Success!" + ConsoleColors.END)

Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
136
137
138
139
    # Preservation Master Files
    print('\n' + ConsoleColors.BOLD + 'Creation of Preservation Master Files...' + ConsoleColors.END)

    # By default, PreservationMasterFiles directory is created in the output path...
140
141
    pmf_dir = 'PreservationMasterFiles/' + config['PRESERVATION_FILES_NAME'] + '/'
    pmf_path = os.path.join(config['WORKING_PATH'], pmf_dir)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
142
143
144
    # ...however, if it already exists, it is possible to skip its creation
    if not os.path.exists(pmf_path):
        os.mkdir(pmf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
145
        print("Preservation Master Files directory '% s' created" % pmf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
146
    else:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
147
        print((ConsoleColors.PURPLE + "Preservation Master Files directory '% s' already exists!" + ConsoleColors.END) % pmf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
148
149
150
151
        overwrite = input('Do you want to overwrite it? [y/n]: ')
        if overwrite.casefold() == 'y':
            shutil.rmtree(pmf_path)
            os.mkdir(pmf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
152
            print("Preservation Master Files directory overwritten")
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
153
        elif overwrite.casefold() == 'n':
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
154
            print('\nExit\n')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
155
156
            quit(os.EX_OK)
        else:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
157
            print(ConsoleColors.RED + 'Unknown command, exiting\n' + ConsoleColors.END)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
158
159
160
            quit(os.EX_USAGE)

    # Copy Preservation Audio File
161
    audio_file = config['PRESERVATION_FILES_NAME'] + '.wav'
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
162
    paf_path = os.path.join(config['WORKING_PATH'], 'PreservationAudioFile/', audio_file)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
163
    try:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
164
        shutil.copy2(paf_path, pmf_path + 'PreservationAudioFile.wav')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
165
    except FileNotFoundError:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
166
        print((ConsoleColors.RED + "Preservation Audio File file '% s' not found!" + ConsoleColors.END) % paf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
167
168
169
170
        quit(os.EX_NOINPUT)
    print("Preservation Audio File copied")

    # Create Preservation Audio-Visual File with substituted audio
171
    video_file = config['PRESERVATION_FILES_NAME'] + '.mov'
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
172
    pvf_path = os.path.join(config['WORKING_PATH'], 'PreservationAudioVisualFile/', video_file)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
173
    try:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
174
175
        audio = AudioFileClip(paf_path)
        video = VideoFileClip(pvf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
176
        # Open Irregularity File to get offset
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
177
        irregularity_file_json = open(os.path.join(temp_path, 'TapeIrregularityClassifier_IrregularityFileOutput2.json'))
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
178
179
180
181
182
183
184
185
186
187
        irregularity_file = json.load(irregularity_file_json)
        offset = irregularity_file['Offset']/1000
        if offset > 0:
            audio = audio.subclip(t_start=offset)
        else:
            video = video.subclip(t_start=offset)
        video = video.set_audio(audio)
        video.write_videofile(pmf_path + 'PreservationAudioVisualFile.mov', bitrate='3000k', codec='mpeg4')
        print("Preservation Audio-Visual File created")
    except OSError:
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
188
        print((ConsoleColors.RED + "Preservation Audio-Visual File file '% s' not found!" + ConsoleColors.END) % pvf_path)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
189
190
191
        quit(os.EX_NOINPUT)

    # Create Irregularity Images archive (already checked)
192
    shutil.make_archive(pmf_path + 'IrregularityImages', 'zip', temp_path, 'IrregularityImages')
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
193
194
195
    print("Irregularity Images archive created")

    # Copy Irregularity File (already checked)
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
196
    shutil.copy2(os.path.join(temp_path, 'TapeIrregularityClassifier_IrregularityFileOutput2.json'), os.path.join(pmf_path, 'IrregularityFile.json'))
Nadir Dalla Pozza's avatar
Nadir Dalla Pozza committed
197
198
    print("Irregularity File copied")

199
200
    # End Preservation Master Files
    print(ConsoleColors.GREEN + ConsoleColors.BOLD + "Success!" + ConsoleColors.END + '\n')