Test Failed
Push — integration-test ( 1937be...abbdf8 )
by Konstantinos
02:03
created

music_album_creation.dialogs.DialogCommander.print()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
import os
2
import sys
3
import shutil
4
5
from PyInquirer import prompt, Validator, ValidationError
6
7
# __all__ = ['store_album_dialog', 'interactive_metadata_dialogs']
8
9
10
class InputFactory:
11
    __instance = None
12
13
    def __new__(cls, *args, **kwargs):
14
        if not cls.__instance:
15
            cls.__instance = super().__new__(cls)
16
            if sys.version_info.major == 2:
17
                cls.__instance._input = raw_input  # NOQA
18
            else:
19
                cls.__instance._input = input
20
        return cls.__instance
21
22
    def __call__(self, *args):
23
        return self._input(*args)
24
25
26
ask_input = InputFactory()
27
28
29
class DialogCommander:
30
31
    @classmethod
32
    def logo(cls):
33
        print(
34
            "\
35
╔═╗╦  ╔╗ ╦ ╦╔╦╗  ╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗╦═╗\n\
36
╠═╣║  ╠╩╗║ ║║║║  ║  ╠╦╝║╣ ╠═╣ ║ ║ ║╠╦╝\n\
37
╩ ╩╩═╝╚═╝╚═╝╩ ╩  ╚═╝╩╚═╚═╝╩ ╩ ╩ ╚═╝╩╚═\
38
            ")
39
40
    @classmethod
41
    def print(cls, string):
42
        print(string)
43
44
    @classmethod
45
    def input_youtube_url_dialog(cls):
46
        """"""
47
        return ask_input('Please input a url corresponding to a music album uploaded as a youtube video.\n   video url: ')
48
49
50
    ## HANDLE Token Error with update youtube-dl and retry download same url dialog
51
    @classmethod
52
    def update_and_retry_dialog(cls):
53
        questions = [
54
            {
55
                'type': 'confirm',
56
                'name': 'update-youtube-dl',
57
                'message': "Update 'youtube-dl' backend?)",
58
                'default': True,
59
            }
60
        ]
61
        answer = prompt(questions)
62
        return answer
63
64
65
    ##### MULTILINE INPUT TRACK NAMES AND TIMESTAMPS (hh:mm:ss)
66
    @classmethod
67
    def track_information_type_dialog(cls, prediction=''):
68
        """Returns a parser of track hh:mm:ss multiline string"""
69
        if prediction == 'timestamps':
70
            choices = ['Timestamps (predicted)', 'Durations']
71
        elif prediction == 'durations':
72
            choices = ['Durations (predicted)', 'Timestamps']
73
        else:
74
            choices = ['Timestamps', 'Durations']
75
        questions = [
76
            {
77
                'type': 'list',  # navigate with arrows through choices
78
                'name': 'how-to-input-tracks',
79
                # type of is the format you prefer to input for providing the necessary information to segment an album
80
                'message': 'What does the expected "hh:mm:ss" input represent?',
81
                'choices': choices,
82
            }
83
        ]
84
        answers = prompt(questions)
85
        return answers['how-to-input-tracks']
86
87
    @classmethod
88
    def interactive_track_info_input_dialog(cls):
89
        print("Enter/Paste your 'track_name - hh:mm:ss' pairs. Each line should represent a single track with format 'trackname - hh:mm:ss'. "
90
              "The assumption is that each track is defined either in terms of a timestamp corrspoding to the starting point within the full album or its actuall playtime length. Then navigate one line below your last track and press Ctrl-D (or Ctrl-Z in $#*!% windows) to save it.\n")
91
92
        def input_lines(prompt_=None):
93
            """Yields input lines from user until EOFError is raised."""
94
            while True:
95
                try:
96
                    yield ask_input() if prompt_ is None else ask_input(prompt_)
97
                except EOFError:
98
                    break
99
                else:
100
                    prompt_ = None  # Only display prompt while reading first line.
101
102
        def multiline_input(prompt_=None):
103
            """Reads a multi-line input from the user."""
104
            return os.linesep.join(input_lines(prompt_=prompt_))
105
        return multiline_input()  # '\n' separable string
106
107
108
    ####################################################################
109
110
    @classmethod
111
    def album_directory_path_dialog(cls, music_lib, artist='', album='', year=''):
112
        if year:
113
            album = '{} ({})'.format(album, year)
114
        else:
115
            album = album
116
        return prompt([{'type': 'input',
117
                        'name': 'create-album-dir',
118
                        'message': 'Please give album directory path',
119
                        'default': os.path.join(music_lib, artist, album)}])['create-album-dir']
120
121
    @classmethod
122
    def confirm_copy_tracks_dialog(cls, destination_directory):
123
        return prompt([{'type': 'confirm',
124
                        'name': 'copy-in-existant-dir',
125
                        'message': "Directory '{}' exists. Copy the tracks there?".format(destination_directory),
126
                        'default': True}])['copy-in-existant-dir']
127
128
    ##### STORE ALBUM DIALOG
129
    @classmethod
130
    def store_album_dialog(cls, tracks, music_lib='', artist='', album='', year=''):
131
132
        album_path = alb
133
134
        ]
135
        while 1:
136
            answers = prompt(questions)
137
138
            destination_directory = answers['create-album-dir']
139
140
            try:
141
                os.makedirs(destination_directory)
142
            except FileExistsError:
143
                ans = prompt([{'type': 'confirm',
144
                               'name': 'copy-in-existant-dir',
145
                               'message': "Directory '{}' exists. Copy the tracks there?".format(destination_directory),
146
                               'default': True}])
147
                if not ans['copy-in-existant-dir']:
148
                    continue
149
            except FileNotFoundError:
150
                print("The selected destination directory '{}' is not valid.".format(destination_directory))
151
                continue
152
            except PermissionError:
153
                print("You don't have permision to create a directory in path '{}'".format(destination_directory))
154
                continue
155
            try:
156
                _copy_tracks(tracks, destination_directory)
157
                break
158
            except PermissionError:
159
                print("Can't copy tracks to '{}' folder. You don't have write permissions in this directory".format(destination_directory))
160
        return destination_directory
161
162
    @classmethod
163
    def interactive_metadata_dialogs(cls, artist='', album='', year=''):
164
        questions = [
165
            {
166
                'type': 'confirm',
167
                'name': 'add-metadata',
168
                'message': 'Do you want to add metadata, such as artist, track names, to the audio files?',
169
                'default': True,
170
            },
171
            {
172
                'type': 'checkbox',
173
                'name': 'automatic-metadata',
174
                'message': 'Infer from audio files',
175
                'when': lambda answers: bool(answers['add-metadata']),
176
                'choices': [
177
                    {
178
                        'name': 'track numbers',
179
                        'checked': True
180
                    },
181
                    {
182
                        'name': 'track names',
183
                        'checked': True
184
                    }
185
                ]
186
            },
187
            {
188
                'type': 'input',
189
                'name': 'artist',
190
                'default': artist,
191
                'message': "'artist' tag",
192
            },
193
            {
194
                'type': 'input',
195
                'name': 'album-artist',
196
                'message': "'album artist' tag",
197
                'default': lambda x: x['artist']
198
            },
199
            {
200
                'type': 'input',
201
                'name': 'album',
202
                'default': album,
203
                'message': "'album' tag",
204
            },
205
            {
206
                'type': 'input',
207
                'name': 'year',
208
                'message': "'year' tag",
209
                'default': year,  # trick to allow empty value
210
                'validate': NumberValidator,
211
                # 'filter': lambda val: int(val)
212
            },
213
        ]
214
        return prompt(questions)
215
216
217
class NumberValidator(Validator):
218
    def validate(self, document):
219
        if document.text != '':  # trick to allow empty value
220
            try:
221
                int(document.text)
222
            except ValueError:
223
                raise ValidationError(
224
                    message='Please enter a number',
225
                    cursor_position=len(document.text))  # Move cursor to end
226