Passed
Push — dev ( ed36f9...ede60a )
by Konstantinos
05:48 queued 58s
created

music_album_creation.dialogs   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Test Coverage

Coverage 41.54%

Importance

Changes 0
Metric Value
eloc 118
dl 0
loc 187
ccs 27
cts 65
cp 0.4154
rs 10
c 0
b 0
f 0
wmc 24

11 Methods

Rating   Name   Duplication   Size   Complexity  
A InputFactory.__call__() 0 2 1
A InputFactory.__new__() 0 8 3
A DialogCommander.logo() 0 4 1
A DialogCommander.interactive_track_info_input_dialog() 0 19 5
A DialogCommander.track_information_type_dialog() 0 20 3
A DialogCommander.input_youtube_url_dialog() 0 3 1
A DialogCommander.update_and_retry_dialog() 0 12 1
A DialogCommander.confirm_copy_tracks_dialog() 0 6 1
A DialogCommander.album_directory_path_dialog() 0 11 2
A NumberValidator.validate() 0 8 3
A DialogCommander.interactive_metadata_dialogs() 0 53 3
1
# -*- coding: utf-8 -*-
2 1
import os
3 1
import sys
4
5 1
from PyInquirer import ValidationError, Validator, prompt
6
7
# __all__ = ['store_album_dialog', 'interactive_metadata_dialogs']
8
9
10 1
class InputFactory:
11 1
    __instance = None
12
13 1
    def __new__(cls, *args, **kwargs):
14 1
        if not cls.__instance:
15 1
            cls.__instance = super().__new__(cls)
16 1
            if sys.version_info.major == 2:
17
                cls.__instance._input = raw_input  # NOQA
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable raw_input does not seem to be defined.
Loading history...
18
            else:
19 1
                cls.__instance._input = input
20 1
        return cls.__instance
21
22 1
    def __call__(self, *args):
23
        return self._input(*args)
24
25
26 1
ask_input = InputFactory()
27
28
29 1
class DialogCommander:
30
31 1
    @classmethod
32
    def logo(cls):
33
        print(
34
            "\
35
╔═╗╦  ╔╗ ╦ ╦╔╦╗  ╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗╦═╗\n\
36
╠═╣║  ╠╩╗║ ║║║║  ║  ╠╦╝║╣ ╠═╣ ║ ║ ║╠╦╝\n\
37
╩ ╩╩═╝╚═╝╚═╝╩ ╩  ╚═╝╩╚═╚═╝╩ ╩ ╩ ╚═╝╩╚═\
38
            ")
39
40 1
    @classmethod
41
    def input_youtube_url_dialog(cls):
42
        return ask_input('Please input a url corresponding to a music album uploaded as a youtube video.\n   video url: ')
43
44
    ## HANDLE Token Error with update youtube-dl and retry download same url dialog
45 1
    @classmethod
46
    def update_and_retry_dialog(cls):
47
        questions = [
48
            {
49
                'type': 'confirm',
50
                'name': 'update-youtube-dl',
51
                'message': "Update 'youtube-dl' backend?)",
52
                'default': True,
53
            }
54
        ]
55
        answer = prompt(questions)
56
        return answer
57
58
59
    ##### MULTILINE INPUT TRACK NAMES AND TIMESTAMPS (hh:mm:ss)
60 1
    @classmethod
61 1
    def track_information_type_dialog(cls, prediction=''):
62
        """Returns a parser of track hh:mm:ss multiline string"""
63
        if prediction == 'timestamps':
64
            choices = ['Timestamps (predicted)', 'Durations']
65
        elif prediction == 'durations':
66
            choices = ['Durations (predicted)', 'Timestamps']
67
        else:
68
            choices = ['Timestamps', 'Durations']
69
        questions = [
70
            {
71
                'type': 'list',  # navigate with arrows through choices
72
                'name': 'how-to-input-tracks',
73
                # type of is the format you prefer to input for providing the necessary information to segment an album
74
                'message': 'What does the expected "hh:mm:ss" input represent?',
75
                'choices': choices,
76
            }
77
        ]
78
        answers = prompt(questions)
79
        return answers['how-to-input-tracks']
80
81 1
    @classmethod
82
    def interactive_track_info_input_dialog(cls):
83
        print("Enter/Paste your 'track_name - hh:mm:ss' pairs. Each line should represent a single track with format 'trackname - hh:mm:ss'. "
84
              "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")
85
86
        def input_lines(prompt_=None):
87
            """Yields input lines from user until EOFError is raised."""
88
            while True:
89
                try:
90
                    yield ask_input() if prompt_ is None else ask_input(prompt_)
91
                except EOFError:
92
                    break
93
                else:
94
                    prompt_ = None  # Only display prompt while reading first line.
95
96
        def multiline_input(prompt_=None):
97
            """Reads a multi-line input from the user."""
98
            return os.linesep.join(input_lines(prompt_=prompt_))
99
        return multiline_input()  # '\n' separable string
100
101
102
    ####################################################################
103
104 1
    @classmethod
105 1
    def album_directory_path_dialog(cls, music_lib, artist='', album='', year=''):
106
        if year:
107
            album = '{} ({})'.format(album, year)
108
        else:
109
            album = album
110
        return prompt([{'type': 'input',
111
                        'name': 'create-album-dir',
112
                        'message': 'Please give album directory path',
113
                        # TODO make sure that the below gets converted to unicode (not str) when executing python2 (python3 is fine)
114
                        'default': os.path.join(music_lib, artist, album)}])['create-album-dir']
115
116 1
    @classmethod
117
    def confirm_copy_tracks_dialog(cls, destination_directory):
118
        return prompt([{'type': 'confirm',
119
                        'name': 'copy-in-existant-dir',
120
                        'message': "Directory '{}' exists. Copy the tracks there?".format(destination_directory),
121
                        'default': True}])['copy-in-existant-dir']
122
123 1
    @classmethod
124 1
    def interactive_metadata_dialogs(cls, artist='', album='', year=''):
125
        questions = [
126
            {
127
                'type': 'confirm',
128
                'name': 'add-metadata',
129
                'message': 'Do you want to add metadata, such as artist, track names, to the audio files?',
130
                'default': True,
131
            },
132
            {
133
                'type': 'checkbox',
134
                'name': 'automatic-metadata',
135
                'message': 'Infer from audio files',
136
                'when': lambda answers: bool(answers['add-metadata']),
137
                'choices': [
138
                    {
139
                        'name': 'track numbers',
140
                        'checked': True
141
                    },
142
                    {
143
                        'name': 'track names',
144
                        'checked': True
145
                    }
146
                ]
147
            },
148
            {
149
                'type': 'input',
150
                'name': 'artist',
151
                'default': artist,
152
                'message': "'artist' tag",
153
            },
154
            {
155
                'type': 'input',
156
                'name': 'album-artist',
157
                'message': "'album artist' tag",
158
                'default': lambda x: x['artist']
159
            },
160
            {
161
                'type': 'input',
162
                'name': 'album',
163
                'default': album,
164
                'message': "'album' tag",
165
            },
166
            {
167
                'type': 'input',
168
                'name': 'year',
169
                'message': "'year' tag",
170
                'default': year,  # trick to allow empty value
171
                'validate': NumberValidator,
172
                # 'filter': lambda val: int(val)
173
            },
174
        ]
175
        return prompt(questions)
176
177
178 1
class NumberValidator(Validator):
179 1
    def validate(self, document):
180
        if document.text != '':  # trick to allow empty value
181
            try:
182
                int(document.text)
183
            except ValueError:
184
                raise ValidationError(
185
                    message='Please enter a number',
186
                    cursor_position=len(document.text))  # Move cursor to end
187