148 lines
4.0 KiB
Python
148 lines
4.0 KiB
Python
"""
|
|
A set of common utilities.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
|
|
import pyaudacity
|
|
|
|
def inputYN(text: str, default: bool=None):
|
|
"""
|
|
Prompty the user with a yes/no prompt.
|
|
|
|
Args:
|
|
text (str): The text displayed to the user.
|
|
default (bool): The default answer.
|
|
"""
|
|
|
|
while True:
|
|
i = input(text)
|
|
|
|
if i.lower() in ['1', 'y', 'yes']:
|
|
return True
|
|
if i.lower() in ['0', 'n', 'no']:
|
|
return False
|
|
if default is not None and i == '':
|
|
return default
|
|
|
|
def parseLrcLine(lrcLine: str) -> [float, str]:
|
|
"""
|
|
Parses a line from an lrc file.
|
|
Turns "[00:36.21] If you decide to stay" into
|
|
[36.21, 'If you decide to stay'].
|
|
|
|
Args:
|
|
lrcLine (str): The single line from the lrc file.
|
|
|
|
Returns:
|
|
float: How many seconds into the song the lyric is.
|
|
str: The lytic.
|
|
"""
|
|
|
|
t = None
|
|
|
|
if re.search(r'^\[[0-9][0-9]:[0-9][0-9](\.[0-9][0-9])?\].*$', lrcLine):
|
|
timeStamp = lrcLine[1:].split(']')[0]
|
|
minutes, seconds = timeStamp.split(':')
|
|
minutes, seconds = (int(minutes), float(seconds))
|
|
t = minutes * 60 + seconds
|
|
lrcLine = ']'.join(lrcLine.split(']')[1:]).strip()
|
|
|
|
return t, lrcLine
|
|
|
|
def generateLRCLine(time: float, text: str='') -> str:
|
|
"""
|
|
Turns a time (in seconds) and an optional lyrics into a valid LRC line.
|
|
If passed (62.1, 'Hello world'), this function will return '[01:02.10] Hello world'
|
|
|
|
Args:
|
|
time (float): The lyrics time in seconds.
|
|
text (str, optional): The text for the lyric.
|
|
|
|
Returns:
|
|
str: The LRC line.
|
|
"""
|
|
|
|
return f'[{int(time//60):02d}:{time%60:05.2f}] {text}'
|
|
|
|
def getInfo(getType: str) -> dict:
|
|
"""
|
|
Runs the `GetInfo` command and automatically parses the JSON.
|
|
|
|
Args:
|
|
getType (str): Passed into the `Type` argument of `GetInfo`.
|
|
|
|
Returns:
|
|
dict: The data Audacity returned.
|
|
"""
|
|
|
|
ret = pyaudacity.do(f'GetInfo: Format="JSON" Type="{getType}"')
|
|
# Remove the surrounding bullshit then parse the JSON.
|
|
return json.loads('\n'.join(ret.split('\n')[1:-2]))
|
|
|
|
def getProjectLength() -> float:
|
|
"""
|
|
Returns how long the total project is.
|
|
|
|
Returns:
|
|
float: How long the project is in seconds.
|
|
"""
|
|
|
|
audioTracks = [track for track in getInfo('Tracks') if track['kind'] == 'wave']
|
|
if len(audioTracks) == 0:
|
|
return 0
|
|
return max([track['end'] for track in audioTracks])
|
|
|
|
def getLRCTrackIndex(create: bool=False) -> int:
|
|
"""
|
|
Gets (or creates) an LRC label tracks index.
|
|
|
|
Args:
|
|
create (bool): If True, an LRC track will be created if none is found.
|
|
|
|
Returns:
|
|
int: The index of the LRC track or None if none was found.
|
|
"""
|
|
|
|
tracks = getInfo('Tracks')
|
|
labelTracks = [t for t in tracks if t['kind'] == 'label']
|
|
validLRCTracks = [t for t in labelTracks if t['name'] == 'Lyrics']
|
|
|
|
if len(validLRCTracks) == 0:
|
|
if not create:
|
|
return None
|
|
|
|
pyaudacity.do('NewLabelTrack')
|
|
pyaudacity.do('TrackMoveTop')
|
|
pyaudacity.do('SetTrackStatus: Name="Lyrics"')
|
|
|
|
return 0
|
|
|
|
for idx, track in enumerate(tracks):
|
|
if track['kind'] == 'label' and track['name'] == 'Lyrics':
|
|
return idx
|
|
|
|
def addLabel(trackIndex: int, time: float, text: str):
|
|
"""
|
|
Adds a label on track `trackIndex` at `time` with the text `text`.
|
|
|
|
Args:
|
|
trackIndex (int): The index of the label track.
|
|
time (float): The time (in seconds) to add the label.
|
|
text (str): The text to put in the label.
|
|
"""
|
|
|
|
# "sanitise" the text
|
|
text = text.replace('"', '\\"')
|
|
|
|
# ... what the fuck Audacity...
|
|
pyaudacity.do('FirstTrack')
|
|
for _ in range(trackIndex):
|
|
pyaudacity.do('NextTrack')
|
|
|
|
pyaudacity.do(f'SelectTime: Start="{time}" end="{time}"')
|
|
pyaudacity.do('AddLabel')
|
|
lastLabelIdx = sum([len(labels) for _, labels in getInfo('Labels')])-1
|
|
pyaudacity.do(f'SetLabel: Label="{lastLabelIdx}" Text="{text}"')
|