Skip to content

Main

Package: deluge_card

Top-level package for deluge-card.

Module deluge_card

Main class representing a Deluge Filesystem in a folder or a mounted SD card.

DelugeCardFS

Main class representing a Deluge SD card/folder structure.

Attributes:

Name Type Description
card_root Path

Path object for the root folder.

Source code in deluge_card/deluge_card.py
 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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
@define(frozen=True)
class DelugeCardFS:
    """Main class representing a Deluge SD card/folder structure.

    Attributes:
        card_root (Path): Path object for the root folder.
    """

    card_root: Path = field()

    @card_root.validator
    def _check_card_root(self, attribute, value):
        if not value.is_dir():
            raise InvalidDelugeCard(f'{value} is not a directory path.')
        for folder in TOP_FOLDERS:
            if not Path(value, folder).exists():
                raise InvalidDelugeCard(f'required folder {folder} does not exist in path {value}')

    @staticmethod
    def initialise(path: str) -> 'DelugeCardFS':
        """Create a new Deluge Folder structure.

        Args:
            path (str): a valid folder name.

        Returns:
            instance (DelugeCardFS): new instance.
        """
        card_root = Path(path)
        assert card_root.is_dir()
        # assert card_root.is_mount()
        assert len(list(card_root.iterdir())) == 0

        for folder in TOP_FOLDERS:
            Path(card_root, folder).mkdir()

        return DelugeCardFS(card_root)  # type: ignore

    @staticmethod
    def from_folder(folder: str) -> 'DelugeCardFS':
        """New instance from a Deluge Folder structure.

        Args:
            folder (str): valid folder name.

        Returns:
            instance (DelugeCardFS): new instance.
        """
        return DelugeCardFS(Path(folder))

    def is_mounted(self) -> bool:
        """Is this a mounted SD card.

        Returns:
            mounted (bool): True if card_root is a mounted filesystem.

        Raises:
            err (Exception): on windows is_mount isnpt available
        """
        return Path(self.card_root).is_mount()

    def songs(self, pattern: str = "") -> Iterator['DelugeSong']:
        """Generator for songs in the Card.

        Args:
            pattern (str): glob-style filename pattern.

        Yields:
            object (DelugeSong): the next song on the card.
        """
        for songfile in sorted(Path(self.card_root, SONGS).rglob('*.XML')):
            if not pattern:
                yield DelugeSong(self, songfile)  # type: ignore
                continue
            if PurePath(songfile).match(pattern):
                yield DelugeSong(self, songfile)

    def kits(self, pattern: str = "") -> Iterator['DelugeKit']:
        """Generator for kits in the Card.

        Args:
            pattern (str): glob-style filename pattern.

        Yields:
            object (DelugeKit): the next kit on the card.
        """
        for filepath in sorted(Path(self.card_root, KITS).rglob('*.XML')):
            if not pattern:
                yield DelugeKit(self, filepath)  # type: ignore
                continue
            if PurePath(filepath).match(pattern):
                yield DelugeKit(self, filepath)

    def synths(self, pattern: str = "") -> Iterator['DelugeSynth']:
        """Generator for synths in the Card.

        Args:
            pattern (str): glob-style filename pattern.

        Yields:
            object (DelugeSynth): the next synth on the card.
        """
        for filepath in sorted(Path(self.card_root, SYNTHS).rglob('*.XML')):
            if not pattern:
                yield DelugeSynth(self, filepath)  # type: ignore
                continue
            if PurePath(filepath).match(pattern):
                yield DelugeSynth(self, filepath)

    def _sample_files(self, pattern: str = '') -> Iterator['Sample']:
        """Get all samples.

        Args:
            pattern (str): glob-style filename pattern.

        Yields:
            object (Sample): matching samples.
        """
        smp = Path(self.card_root, 'SAMPLES')
        paths = (p.resolve() for p in Path(smp).rglob("**/*") if p.suffix.lower() in SAMPLE_TYPES)
        for fname in paths:
            # print(fname)
            if Path(fname).name[0] == '.':  # Apple copy crap
                continue
            if not pattern:
                yield Sample(Path(fname))
                continue
            if PurePath(fname).match(pattern):
                yield Sample(Path(fname))

    def mv_samples(self, pattern: str, dest: Path) -> Iterator[ModOp]:
        """Move samples, updating any affected XML files.

        Args:
            pattern (str): glob-style filename pattern.
            dest: (Path): new path for the moved objec(s)

        Yields:
            object (ModOp): Details of the move operation.
        """
        yield from mv_samples(self.card_root, self.samples(pattern), pattern, dest)

    def samples(self, pattern: str = "") -> Iterator[Sample]:
        """Generator for all samples in the card.

        Args:
            pattern (str): glob-style filename pattern.

        Yields:
            object (Sample): the next sample on the card.
        """
        sample_map: Dict[Path, Sample] = dict()

        used_samples = self.used_samples(pattern)
        all_samples = self._sample_files(pattern)
        for sample in used_samples:
            sample_map[sample.path] = sample
            yield sample
        for sample in all_samples:
            if sample.path not in sample_map:
                sample_map[sample.path] = sample
                yield sample

    def used_samples(self, pattern: str = '') -> Iterator['Sample']:
        """Get all samples referenced in XML files.

        Args:
            pattern (str): glob-style filename pattern.

        Yields:
            object (Sample): the next sample on the card.
        """
        sample_map: Dict[Path, Sample] = dict()
        used_sample_gens = itertools.chain(
            map(lambda synth: synth.samples(pattern), self.synths()),
            map(lambda sng: sng.samples(pattern), self.songs()),
            map(lambda kit: kit.samples(pattern), self.kits()),
        )

        # merge samples in different settings (song, kit, synth)
        for sample in itertools.chain.from_iterable(used_sample_gens):
            if sample.path in sample_map:
                sample_map[sample.path].settings += sample.settings
            else:
                sample_map[sample.path] = sample

        return (s for s in sample_map.values())

_sample_files(pattern='')

Get all samples.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

''

Yields:

Name Type Description
object Sample

matching samples.

Source code in deluge_card/deluge_card.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def _sample_files(self, pattern: str = '') -> Iterator['Sample']:
    """Get all samples.

    Args:
        pattern (str): glob-style filename pattern.

    Yields:
        object (Sample): matching samples.
    """
    smp = Path(self.card_root, 'SAMPLES')
    paths = (p.resolve() for p in Path(smp).rglob("**/*") if p.suffix.lower() in SAMPLE_TYPES)
    for fname in paths:
        # print(fname)
        if Path(fname).name[0] == '.':  # Apple copy crap
            continue
        if not pattern:
            yield Sample(Path(fname))
            continue
        if PurePath(fname).match(pattern):
            yield Sample(Path(fname))

from_folder(folder) staticmethod

New instance from a Deluge Folder structure.

Parameters:

Name Type Description Default
folder str

valid folder name.

required

Returns:

Name Type Description
instance DelugeCardFS

new instance.

Source code in deluge_card/deluge_card.py
 98
 99
100
101
102
103
104
105
106
107
108
@staticmethod
def from_folder(folder: str) -> 'DelugeCardFS':
    """New instance from a Deluge Folder structure.

    Args:
        folder (str): valid folder name.

    Returns:
        instance (DelugeCardFS): new instance.
    """
    return DelugeCardFS(Path(folder))

initialise(path) staticmethod

Create a new Deluge Folder structure.

Parameters:

Name Type Description Default
path str

a valid folder name.

required

Returns:

Name Type Description
instance DelugeCardFS

new instance.

Source code in deluge_card/deluge_card.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
@staticmethod
def initialise(path: str) -> 'DelugeCardFS':
    """Create a new Deluge Folder structure.

    Args:
        path (str): a valid folder name.

    Returns:
        instance (DelugeCardFS): new instance.
    """
    card_root = Path(path)
    assert card_root.is_dir()
    # assert card_root.is_mount()
    assert len(list(card_root.iterdir())) == 0

    for folder in TOP_FOLDERS:
        Path(card_root, folder).mkdir()

    return DelugeCardFS(card_root)  # type: ignore

is_mounted()

Is this a mounted SD card.

Returns:

Name Type Description
mounted bool

True if card_root is a mounted filesystem.

Raises:

Type Description
err(Exception)

on windows is_mount isnpt available

Source code in deluge_card/deluge_card.py
110
111
112
113
114
115
116
117
118
119
def is_mounted(self) -> bool:
    """Is this a mounted SD card.

    Returns:
        mounted (bool): True if card_root is a mounted filesystem.

    Raises:
        err (Exception): on windows is_mount isnpt available
    """
    return Path(self.card_root).is_mount()

kits(pattern='')

Generator for kits in the Card.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

''

Yields:

Name Type Description
object DelugeKit

the next kit on the card.

Source code in deluge_card/deluge_card.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def kits(self, pattern: str = "") -> Iterator['DelugeKit']:
    """Generator for kits in the Card.

    Args:
        pattern (str): glob-style filename pattern.

    Yields:
        object (DelugeKit): the next kit on the card.
    """
    for filepath in sorted(Path(self.card_root, KITS).rglob('*.XML')):
        if not pattern:
            yield DelugeKit(self, filepath)  # type: ignore
            continue
        if PurePath(filepath).match(pattern):
            yield DelugeKit(self, filepath)

mv_samples(pattern, dest)

Move samples, updating any affected XML files.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

required
dest Path

(Path): new path for the moved objec(s)

required

Yields:

Name Type Description
object ModOp

Details of the move operation.

Source code in deluge_card/deluge_card.py
190
191
192
193
194
195
196
197
198
199
200
def mv_samples(self, pattern: str, dest: Path) -> Iterator[ModOp]:
    """Move samples, updating any affected XML files.

    Args:
        pattern (str): glob-style filename pattern.
        dest: (Path): new path for the moved objec(s)

    Yields:
        object (ModOp): Details of the move operation.
    """
    yield from mv_samples(self.card_root, self.samples(pattern), pattern, dest)

samples(pattern='')

Generator for all samples in the card.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

''

Yields:

Name Type Description
object Sample

the next sample on the card.

Source code in deluge_card/deluge_card.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def samples(self, pattern: str = "") -> Iterator[Sample]:
    """Generator for all samples in the card.

    Args:
        pattern (str): glob-style filename pattern.

    Yields:
        object (Sample): the next sample on the card.
    """
    sample_map: Dict[Path, Sample] = dict()

    used_samples = self.used_samples(pattern)
    all_samples = self._sample_files(pattern)
    for sample in used_samples:
        sample_map[sample.path] = sample
        yield sample
    for sample in all_samples:
        if sample.path not in sample_map:
            sample_map[sample.path] = sample
            yield sample

songs(pattern='')

Generator for songs in the Card.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

''

Yields:

Name Type Description
object DelugeSong

the next song on the card.

Source code in deluge_card/deluge_card.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def songs(self, pattern: str = "") -> Iterator['DelugeSong']:
    """Generator for songs in the Card.

    Args:
        pattern (str): glob-style filename pattern.

    Yields:
        object (DelugeSong): the next song on the card.
    """
    for songfile in sorted(Path(self.card_root, SONGS).rglob('*.XML')):
        if not pattern:
            yield DelugeSong(self, songfile)  # type: ignore
            continue
        if PurePath(songfile).match(pattern):
            yield DelugeSong(self, songfile)

synths(pattern='')

Generator for synths in the Card.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

''

Yields:

Name Type Description
object DelugeSynth

the next synth on the card.

Source code in deluge_card/deluge_card.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def synths(self, pattern: str = "") -> Iterator['DelugeSynth']:
    """Generator for synths in the Card.

    Args:
        pattern (str): glob-style filename pattern.

    Yields:
        object (DelugeSynth): the next synth on the card.
    """
    for filepath in sorted(Path(self.card_root, SYNTHS).rglob('*.XML')):
        if not pattern:
            yield DelugeSynth(self, filepath)  # type: ignore
            continue
        if PurePath(filepath).match(pattern):
            yield DelugeSynth(self, filepath)

used_samples(pattern='')

Get all samples referenced in XML files.

Parameters:

Name Type Description Default
pattern str

glob-style filename pattern.

''

Yields:

Name Type Description
object Sample

the next sample on the card.

Source code in deluge_card/deluge_card.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def used_samples(self, pattern: str = '') -> Iterator['Sample']:
    """Get all samples referenced in XML files.

    Args:
        pattern (str): glob-style filename pattern.

    Yields:
        object (Sample): the next sample on the card.
    """
    sample_map: Dict[Path, Sample] = dict()
    used_sample_gens = itertools.chain(
        map(lambda synth: synth.samples(pattern), self.synths()),
        map(lambda sng: sng.samples(pattern), self.songs()),
        map(lambda kit: kit.samples(pattern), self.kits()),
    )

    # merge samples in different settings (song, kit, synth)
    for sample in itertools.chain.from_iterable(used_sample_gens):
        if sample.path in sample_map:
            sample_map[sample.path].settings += sample.settings
        else:
            sample_map[sample.path] = sample

    return (s for s in sample_map.values())

InvalidDelugeCard

Bases: Exception

This is not a valid DelugeCard FS.

Source code in deluge_card/deluge_card.py
48
49
50
51
52
53
54
55
56
57
class InvalidDelugeCard(Exception):
    """This is not a valid DelugeCard FS."""

    def __init__(self, msg):
        """Create a new InvalidDelugeCard Exception.

        Attributes:
            msg (str): Human readable string describing the exception.
        """
        msg: str

__init__(msg)

Create a new InvalidDelugeCard Exception.

Attributes:

Name Type Description
msg str

Human readable string describing the exception.

Source code in deluge_card/deluge_card.py
51
52
53
54
55
56
57
def __init__(self, msg):
    """Create a new InvalidDelugeCard Exception.

    Attributes:
        msg (str): Human readable string describing the exception.
    """
    msg: str

list_deluge_fs(folder)

List deluge_card look-alike filesystems.

Parameters:

Name Type Description Default
folder str

path of target folder.

required

Yields:

Name Type Description
cards Iterator[DelugeCardFS]

generator of DelugeCardFS instances.

Source code in deluge_card/deluge_card.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def list_deluge_fs(folder) -> Iterator['DelugeCardFS']:
    """List deluge_card look-alike filesystems.

    Args:
        folder (str): path of target folder.

    Yields:
        cards (Iterator[DelugeCardFS]): generator of DelugeCardFS instances.
    """

    def _test_card_fs(folder):
        try:
            return DelugeCardFS.from_folder(folder)
        except InvalidDelugeCard:
            return

    card = _test_card_fs(folder)
    if card:
        yield card
    for fldr in Path(folder).iterdir():
        card = _test_card_fs(fldr)
        if not card:
            continue
        yield card