Compare commits
No commits in common. "cd04ec6339ac8c58fe2fe437c6642a17ee84bca3" and "d3834928fd2190a2b507004f888c667d4e00b124" have entirely different histories.
cd04ec6339
...
d3834928fd
13
.flake8
13
.flake8
@ -1,13 +0,0 @@
|
|||||||
[flake8]
|
|
||||||
max-line-length = 88
|
|
||||||
select = C,E,F,W,B,B950
|
|
||||||
extend-ignore = E203, E501
|
|
||||||
exclude =
|
|
||||||
.git
|
|
||||||
app/ui,
|
|
||||||
__pycache__,
|
|
||||||
archive,
|
|
||||||
migrations,
|
|
||||||
prof,
|
|
||||||
docs,
|
|
||||||
app/icons_rc.py
|
|
||||||
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
from PyQt6.QtCore import Qt, QEvent, QObject
|
from PyQt6.QtCore import Qt, QEvent, QObject
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
|
QAbstractItemDelegate,
|
||||||
QAbstractItemView,
|
QAbstractItemView,
|
||||||
QApplication,
|
QApplication,
|
||||||
QMainWindow,
|
QMainWindow,
|
||||||
QMessageBox,
|
QMessageBox,
|
||||||
QPlainTextEdit,
|
QPlainTextEdit,
|
||||||
QStyledItemDelegate,
|
QStyledItemDelegate,
|
||||||
|
QStyleOptionViewItem,
|
||||||
QTableWidget,
|
QTableWidget,
|
||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
)
|
)
|
||||||
@ -31,15 +33,16 @@ class EscapeDelegate(QStyledItemDelegate):
|
|||||||
if event.type() == QEvent.Type.KeyPress:
|
if event.type() == QEvent.Type.KeyPress:
|
||||||
key_event = cast(QKeyEvent, event)
|
key_event = cast(QKeyEvent, event)
|
||||||
if key_event.key() == Qt.Key.Key_Return:
|
if key_event.key() == Qt.Key.Key_Return:
|
||||||
if key_event.modifiers() == (Qt.KeyboardModifier.ControlModifier):
|
if key_event.modifiers() == (
|
||||||
|
Qt.KeyboardModifier.ControlModifier
|
||||||
|
):
|
||||||
print("save data")
|
print("save data")
|
||||||
self.commitData.emit(editor)
|
self.commitData.emit(editor)
|
||||||
self.closeEditor.emit(editor)
|
self.closeEditor.emit(editor)
|
||||||
return True
|
return True
|
||||||
elif key_event.key() == Qt.Key.Key_Escape:
|
elif key_event.key() == Qt.Key.Key_Escape:
|
||||||
discard_edits = QMessageBox.question(
|
discard_edits = QMessageBox.question(
|
||||||
self.parent(), "Abandon edit", "Discard changes?"
|
self.parent(), "Abandon edit", "Discard changes?")
|
||||||
)
|
|
||||||
if discard_edits == QMessageBox.StandardButton.Yes:
|
if discard_edits == QMessageBox.StandardButton.Yes:
|
||||||
print("abandon edit")
|
print("abandon edit")
|
||||||
self.closeEditor.emit(editor)
|
self.closeEditor.emit(editor)
|
||||||
@ -71,7 +74,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.table_widget.resizeRowsToContents()
|
self.table_widget.resizeRowsToContents()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
window.show()
|
window.show()
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from pydub import AudioSegment
|
from pydub import AudioSegment, effects
|
||||||
|
|
||||||
# DIR = "/home/kae/git/musicmuster/archive"
|
# DIR = "/home/kae/git/musicmuster/archive"
|
||||||
DIR = "/home/kae/git/musicmuster"
|
DIR = "/home/kae/git/musicmuster"
|
||||||
|
|||||||
@ -79,6 +79,7 @@ class Config(object):
|
|||||||
SCROLL_TOP_MARGIN = 3
|
SCROLL_TOP_MARGIN = 3
|
||||||
TEXT_NO_TRACK_NO_NOTE = "[Section header]"
|
TEXT_NO_TRACK_NO_NOTE = "[Section header]"
|
||||||
TOD_TIME_FORMAT = "%H:%M:%S"
|
TOD_TIME_FORMAT = "%H:%M:%S"
|
||||||
|
TIMER_MS = 10
|
||||||
TRACK_TIME_FORMAT = "%H:%M:%S"
|
TRACK_TIME_FORMAT = "%H:%M:%S"
|
||||||
VOLUME_VLC_DEFAULT = 75
|
VOLUME_VLC_DEFAULT = 75
|
||||||
VOLUME_VLC_DROP3db = 65
|
VOLUME_VLC_DROP3db = 65
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
import inspect
|
import inspect
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from config import Config
|
from config import Config
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import (sessionmaker, scoped_session)
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
from log import log
|
from log import log
|
||||||
|
|
||||||
MYSQL_CONNECT = os.environ.get("MM_DB")
|
MYSQL_CONNECT = os.environ.get('MM_DB')
|
||||||
if MYSQL_CONNECT is None:
|
if MYSQL_CONNECT is None:
|
||||||
raise ValueError("MYSQL_CONNECT is undefined")
|
raise ValueError("MYSQL_CONNECT is undefined")
|
||||||
else:
|
else:
|
||||||
dbname = MYSQL_CONNECT.split("/")[-1]
|
dbname = MYSQL_CONNECT.split('/')[-1]
|
||||||
log.debug(f"Database: {dbname}")
|
log.debug(f"Database: {dbname}")
|
||||||
|
|
||||||
# MM_ENV = os.environ.get('MM_ENV', 'PRODUCTION')
|
# MM_ENV = os.environ.get('MM_ENV', 'PRODUCTION')
|
||||||
@ -30,10 +31,10 @@ else:
|
|||||||
|
|
||||||
engine = create_engine(
|
engine = create_engine(
|
||||||
MYSQL_CONNECT,
|
MYSQL_CONNECT,
|
||||||
encoding="utf-8",
|
encoding='utf-8',
|
||||||
echo=Config.DISPLAY_SQL,
|
echo=Config.DISPLAY_SQL,
|
||||||
pool_pre_ping=True,
|
pool_pre_ping=True,
|
||||||
future=True,
|
future=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +47,8 @@ def Session() -> Generator[scoped_session, None, None]:
|
|||||||
Session = scoped_session(sessionmaker(bind=engine, future=True))
|
Session = scoped_session(sessionmaker(bind=engine, future=True))
|
||||||
log.debug(f"SqlA: session acquired [{hex(id(Session))}]")
|
log.debug(f"SqlA: session acquired [{hex(id(Session))}]")
|
||||||
log.debug(
|
log.debug(
|
||||||
f"Session acquisition: {file}:{function}:{lineno} " f"[{hex(id(Session))}]"
|
f"Session acquisition: {file}:{function}:{lineno} "
|
||||||
|
f"[{hex(id(Session))}]"
|
||||||
)
|
)
|
||||||
yield Session
|
yield Session
|
||||||
log.debug(f" SqlA: session released [{hex(id(Session))}]")
|
log.debug(f" SqlA: session released [{hex(id(Session))}]")
|
||||||
|
|||||||
125
app/helpers.py
125
app/helpers.py
@ -1,3 +1,4 @@
|
|||||||
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
import psutil
|
import psutil
|
||||||
import shutil
|
import shutil
|
||||||
@ -9,13 +10,13 @@ from config import Config
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
from log import log
|
from log import log
|
||||||
from mutagen.flac import FLAC # type: ignore
|
from mutagen.flac import FLAC # type: ignore
|
||||||
from mutagen.mp3 import MP3 # type: ignore
|
from mutagen.mp3 import MP3 # type: ignore
|
||||||
from pydub import AudioSegment, effects
|
from pydub import AudioSegment, effects
|
||||||
from pydub.utils import mediainfo
|
from pydub.utils import mediainfo
|
||||||
from PyQt6.QtWidgets import QMainWindow, QMessageBox # type: ignore
|
from PyQt6.QtWidgets import QMainWindow, QMessageBox # type: ignore
|
||||||
from tinytag import TinyTag # type: ignore
|
from tinytag import TinyTag # type: ignore
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
|
||||||
def ask_yes_no(title: str, question: str, default_yes: bool = False) -> bool:
|
def ask_yes_no(title: str, question: str, default_yes: bool = False) -> bool:
|
||||||
@ -25,8 +26,7 @@ def ask_yes_no(title: str, question: str, default_yes: bool = False) -> bool:
|
|||||||
dlg.setWindowTitle(title)
|
dlg.setWindowTitle(title)
|
||||||
dlg.setText(question)
|
dlg.setText(question)
|
||||||
dlg.setStandardButtons(
|
dlg.setStandardButtons(
|
||||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||||
)
|
|
||||||
dlg.setIcon(QMessageBox.Icon.Question)
|
dlg.setIcon(QMessageBox.Icon.Question)
|
||||||
if default_yes:
|
if default_yes:
|
||||||
dlg.setDefaultButton(QMessageBox.StandardButton.Yes)
|
dlg.setDefaultButton(QMessageBox.StandardButton.Yes)
|
||||||
@ -36,10 +36,8 @@ def ask_yes_no(title: str, question: str, default_yes: bool = False) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def fade_point(
|
def fade_point(
|
||||||
audio_segment: AudioSegment,
|
audio_segment: AudioSegment, fade_threshold: float = 0.0,
|
||||||
fade_threshold: float = 0.0,
|
chunk_size: int = Config.AUDIO_SEGMENT_CHUNK_SIZE) -> int:
|
||||||
chunk_size: int = Config.AUDIO_SEGMENT_CHUNK_SIZE,
|
|
||||||
) -> int:
|
|
||||||
"""
|
"""
|
||||||
Returns the millisecond/index of the point where the volume drops below
|
Returns the millisecond/index of the point where the volume drops below
|
||||||
the maximum and doesn't get louder again.
|
the maximum and doesn't get louder again.
|
||||||
@ -57,9 +55,8 @@ def fade_point(
|
|||||||
fade_threshold = max_vol
|
fade_threshold = max_vol
|
||||||
|
|
||||||
while (
|
while (
|
||||||
audio_segment[trim_ms : trim_ms + chunk_size].dBFS < fade_threshold
|
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < fade_threshold
|
||||||
and trim_ms > 0
|
and trim_ms > 0): # noqa W503
|
||||||
): # noqa W503
|
|
||||||
trim_ms -= chunk_size
|
trim_ms -= chunk_size
|
||||||
|
|
||||||
# if there is no trailing silence, return lenght of track (it's less
|
# if there is no trailing silence, return lenght of track (it's less
|
||||||
@ -80,10 +77,10 @@ def file_is_unreadable(path: Optional[str]) -> bool:
|
|||||||
|
|
||||||
def get_audio_segment(path: str) -> Optional[AudioSegment]:
|
def get_audio_segment(path: str) -> Optional[AudioSegment]:
|
||||||
try:
|
try:
|
||||||
if path.endswith(".mp3"):
|
if path.endswith('.mp3'):
|
||||||
return AudioSegment.from_mp3(path)
|
return AudioSegment.from_mp3(path)
|
||||||
elif path.endswith(".flac"):
|
elif path.endswith('.flac'):
|
||||||
return AudioSegment.from_file(path, "flac") # type: ignore
|
return AudioSegment.from_file(path, "flac") # type: ignore
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -102,13 +99,12 @@ def get_tags(path: str) -> Dict[str, Any]:
|
|||||||
artist=tag.artist,
|
artist=tag.artist,
|
||||||
bitrate=round(tag.bitrate),
|
bitrate=round(tag.bitrate),
|
||||||
duration=int(round(tag.duration, Config.MILLISECOND_SIGFIGS) * 1000),
|
duration=int(round(tag.duration, Config.MILLISECOND_SIGFIGS) * 1000),
|
||||||
path=path,
|
path=path
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_relative_date(
|
def get_relative_date(past_date: Optional[datetime],
|
||||||
past_date: Optional[datetime], reference_date: Optional[datetime] = None
|
reference_date: Optional[datetime] = None) -> str:
|
||||||
) -> str:
|
|
||||||
"""
|
"""
|
||||||
Return how long before reference_date past_date is as string.
|
Return how long before reference_date past_date is as string.
|
||||||
|
|
||||||
@ -149,10 +145,9 @@ def get_relative_date(
|
|||||||
|
|
||||||
|
|
||||||
def leading_silence(
|
def leading_silence(
|
||||||
audio_segment: AudioSegment,
|
audio_segment: AudioSegment,
|
||||||
silence_threshold: int = Config.DBFS_SILENCE,
|
silence_threshold: int = Config.DBFS_SILENCE,
|
||||||
chunk_size: int = Config.AUDIO_SEGMENT_CHUNK_SIZE,
|
chunk_size: int = Config.AUDIO_SEGMENT_CHUNK_SIZE) -> int:
|
||||||
) -> int:
|
|
||||||
"""
|
"""
|
||||||
Returns the millisecond/index that the leading silence ends.
|
Returns the millisecond/index that the leading silence ends.
|
||||||
audio_segment - the segment to find silence in
|
audio_segment - the segment to find silence in
|
||||||
@ -164,11 +159,9 @@ def leading_silence(
|
|||||||
|
|
||||||
trim_ms: int = 0 # ms
|
trim_ms: int = 0 # ms
|
||||||
assert chunk_size > 0 # to avoid infinite loop
|
assert chunk_size > 0 # to avoid infinite loop
|
||||||
while audio_segment[
|
while (
|
||||||
trim_ms : trim_ms + chunk_size
|
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < # noqa W504
|
||||||
].dBFS < silence_threshold and trim_ms < len( # noqa W504
|
silence_threshold and trim_ms < len(audio_segment)):
|
||||||
audio_segment
|
|
||||||
):
|
|
||||||
trim_ms += chunk_size
|
trim_ms += chunk_size
|
||||||
|
|
||||||
# if there is no end it should return the length of the segment
|
# if there is no end it should return the length of the segment
|
||||||
@ -182,9 +175,9 @@ def send_mail(to_addr, from_addr, subj, body):
|
|||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
msg.set_content(body)
|
msg.set_content(body)
|
||||||
|
|
||||||
msg["Subject"] = subj
|
msg['Subject'] = subj
|
||||||
msg["From"] = from_addr
|
msg['From'] = from_addr
|
||||||
msg["To"] = to_addr
|
msg['To'] = to_addr
|
||||||
|
|
||||||
# Send the message via SMTP server.
|
# Send the message via SMTP server.
|
||||||
context = ssl.create_default_context()
|
context = ssl.create_default_context()
|
||||||
@ -201,7 +194,8 @@ def send_mail(to_addr, from_addr, subj, body):
|
|||||||
s.quit()
|
s.quit()
|
||||||
|
|
||||||
|
|
||||||
def ms_to_mmss(ms: Optional[int], decimals: int = 0, negative: bool = False) -> str:
|
def ms_to_mmss(ms: Optional[int], decimals: int = 0,
|
||||||
|
negative: bool = False) -> str:
|
||||||
"""Convert milliseconds to mm:ss"""
|
"""Convert milliseconds to mm:ss"""
|
||||||
|
|
||||||
minutes: int
|
minutes: int
|
||||||
@ -233,12 +227,13 @@ def normalise_track(path):
|
|||||||
|
|
||||||
# Check type
|
# Check type
|
||||||
ftype = os.path.splitext(path)[1][1:]
|
ftype = os.path.splitext(path)[1][1:]
|
||||||
if ftype not in ["mp3", "flac"]:
|
if ftype not in ['mp3', 'flac']:
|
||||||
log.info(
|
log.info(
|
||||||
f"helpers.normalise_track({path}): " f"File type {ftype} not implemented"
|
f"helpers.normalise_track({path}): "
|
||||||
|
f"File type {ftype} not implemented"
|
||||||
)
|
)
|
||||||
|
|
||||||
bitrate = mediainfo(path)["bit_rate"]
|
bitrate = mediainfo(path)['bit_rate']
|
||||||
audio = get_audio_segment(path)
|
audio = get_audio_segment(path)
|
||||||
if not audio:
|
if not audio:
|
||||||
return
|
return
|
||||||
@ -250,20 +245,23 @@ def normalise_track(path):
|
|||||||
_, temp_path = tempfile.mkstemp()
|
_, temp_path = tempfile.mkstemp()
|
||||||
shutil.copyfile(path, temp_path)
|
shutil.copyfile(path, temp_path)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
log.debug(f"helpers.normalise_track({path}): err1: {repr(err)}")
|
log.debug(
|
||||||
|
f"helpers.normalise_track({path}): err1: {repr(err)}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Overwrite original file with normalised output
|
# Overwrite original file with normalised output
|
||||||
normalised = effects.normalize(audio)
|
normalised = effects.normalize(audio)
|
||||||
try:
|
try:
|
||||||
normalised.export(path, format=os.path.splitext(path)[1][1:], bitrate=bitrate)
|
normalised.export(path, format=os.path.splitext(path)[1][1:],
|
||||||
|
bitrate=bitrate)
|
||||||
# Fix up permssions and ownership
|
# Fix up permssions and ownership
|
||||||
os.chown(path, stats.st_uid, stats.st_gid)
|
os.chown(path, stats.st_uid, stats.st_gid)
|
||||||
os.chmod(path, stats.st_mode)
|
os.chmod(path, stats.st_mode)
|
||||||
# Copy tags
|
# Copy tags
|
||||||
if ftype == "flac":
|
if ftype == 'flac':
|
||||||
tag_handler = FLAC
|
tag_handler = FLAC
|
||||||
elif ftype == "mp3":
|
elif ftype == 'mp3':
|
||||||
tag_handler = MP3
|
tag_handler = MP3
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
@ -273,7 +271,9 @@ def normalise_track(path):
|
|||||||
dst[tag] = src[tag]
|
dst[tag] = src[tag]
|
||||||
dst.save()
|
dst.save()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
log.debug(f"helpers.normalise_track({path}): err2: {repr(err)}")
|
log.debug(
|
||||||
|
f"helpers.normalise_track({path}): err2: {repr(err)}"
|
||||||
|
)
|
||||||
# Restore original file
|
# Restore original file
|
||||||
shutil.copyfile(path, temp_path)
|
shutil.copyfile(path, temp_path)
|
||||||
finally:
|
finally:
|
||||||
@ -296,9 +296,9 @@ def open_in_audacity(path: str) -> bool:
|
|||||||
if not path:
|
if not path:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
to_pipe: str = "/tmp/audacity_script_pipe.to." + str(os.getuid())
|
to_pipe: str = '/tmp/audacity_script_pipe.to.' + str(os.getuid())
|
||||||
from_pipe: str = "/tmp/audacity_script_pipe.from." + str(os.getuid())
|
from_pipe: str = '/tmp/audacity_script_pipe.from.' + str(os.getuid())
|
||||||
eol: str = "\n"
|
eol: str = '\n'
|
||||||
|
|
||||||
def send_command(command: str) -> None:
|
def send_command(command: str) -> None:
|
||||||
"""Send a single command."""
|
"""Send a single command."""
|
||||||
@ -308,13 +308,13 @@ def open_in_audacity(path: str) -> bool:
|
|||||||
def get_response() -> str:
|
def get_response() -> str:
|
||||||
"""Return the command response."""
|
"""Return the command response."""
|
||||||
|
|
||||||
result: str = ""
|
result: str = ''
|
||||||
line: str = ""
|
line: str = ''
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
result += line
|
result += line
|
||||||
line = from_audacity.readline()
|
line = from_audacity.readline()
|
||||||
if line == "\n" and len(result) > 0:
|
if line == '\n' and len(result) > 0:
|
||||||
break
|
break
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -325,7 +325,8 @@ def open_in_audacity(path: str) -> bool:
|
|||||||
response = get_response()
|
response = get_response()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
with open(to_pipe, "w") as to_audacity, open(from_pipe, "rt") as from_audacity:
|
with open(to_pipe, 'w') as to_audacity, open(
|
||||||
|
from_pipe, 'rt') as from_audacity:
|
||||||
do_command(f'Import2: Filename="{path}"')
|
do_command(f'Import2: Filename="{path}"')
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -337,18 +338,18 @@ def set_track_metadata(session, track):
|
|||||||
t = get_tags(track.path)
|
t = get_tags(track.path)
|
||||||
audio = get_audio_segment(track.path)
|
audio = get_audio_segment(track.path)
|
||||||
|
|
||||||
track.title = t["title"]
|
track.title = t['title']
|
||||||
track.artist = t["artist"]
|
track.artist = t['artist']
|
||||||
track.bitrate = t["bitrate"]
|
track.bitrate = t['bitrate']
|
||||||
|
|
||||||
if not audio:
|
if not audio:
|
||||||
return
|
return
|
||||||
track.duration = len(audio)
|
track.duration = len(audio)
|
||||||
track.start_gap = leading_silence(audio)
|
track.start_gap = leading_silence(audio)
|
||||||
track.fade_at = round(fade_point(audio) / 1000, Config.MILLISECOND_SIGFIGS) * 1000
|
track.fade_at = round(fade_point(audio) / 1000,
|
||||||
track.silence_at = (
|
Config.MILLISECOND_SIGFIGS) * 1000
|
||||||
round(trailing_silence(audio) / 1000, Config.MILLISECOND_SIGFIGS) * 1000
|
track.silence_at = round(trailing_silence(audio) / 1000,
|
||||||
)
|
Config.MILLISECOND_SIGFIGS) * 1000
|
||||||
track.mtime = os.path.getmtime(track.path)
|
track.mtime = os.path.getmtime(track.path)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
@ -357,20 +358,20 @@ def set_track_metadata(session, track):
|
|||||||
def show_OK(parent: QMainWindow, title: str, msg: str) -> None:
|
def show_OK(parent: QMainWindow, title: str, msg: str) -> None:
|
||||||
"""Display a message to user"""
|
"""Display a message to user"""
|
||||||
|
|
||||||
QMessageBox.information(parent, title, msg, buttons=QMessageBox.StandardButton.Ok)
|
QMessageBox.information(parent, title, msg,
|
||||||
|
buttons=QMessageBox.StandardButton.Ok)
|
||||||
|
|
||||||
|
|
||||||
def show_warning(parent: QMainWindow, title: str, msg: str) -> None:
|
def show_warning(parent: QMainWindow, title: str, msg: str) -> None:
|
||||||
"""Display a warning to user"""
|
"""Display a warning to user"""
|
||||||
|
|
||||||
QMessageBox.warning(parent, title, msg, buttons=QMessageBox.StandardButton.Cancel)
|
QMessageBox.warning(parent, title, msg,
|
||||||
|
buttons=QMessageBox.StandardButton.Cancel)
|
||||||
|
|
||||||
|
|
||||||
def trailing_silence(
|
def trailing_silence(
|
||||||
audio_segment: AudioSegment,
|
audio_segment: AudioSegment, silence_threshold: int = -50,
|
||||||
silence_threshold: int = -50,
|
chunk_size: int = Config.AUDIO_SEGMENT_CHUNK_SIZE) -> int:
|
||||||
chunk_size: int = Config.AUDIO_SEGMENT_CHUNK_SIZE,
|
|
||||||
) -> int:
|
|
||||||
"""Return fade point from start in milliseconds"""
|
"""Return fade point from start in milliseconds"""
|
||||||
|
|
||||||
return fade_point(audio_segment, silence_threshold, chunk_size)
|
return fade_point(audio_segment, silence_threshold, chunk_size)
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from slugify import slugify # type: ignore
|
from slugify import slugify # type: ignore
|
||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
from PyQt6.QtCore import QUrl # type: ignore
|
from PyQt6.QtCore import QUrl # type: ignore
|
||||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||||
from PyQt6.QtWidgets import QTabWidget
|
from PyQt6.QtWidgets import QTabWidget
|
||||||
from config import Config
|
from config import Config
|
||||||
@ -47,11 +47,13 @@ class InfoTabs(QTabWidget):
|
|||||||
|
|
||||||
if url in self.tabtitles.values():
|
if url in self.tabtitles.values():
|
||||||
self.setCurrentIndex(
|
self.setCurrentIndex(
|
||||||
list(self.tabtitles.keys())[list(self.tabtitles.values()).index(url)]
|
list(self.tabtitles.keys())[
|
||||||
|
list(self.tabtitles.values()).index(url)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
short_title = title[: Config.INFO_TAB_TITLE_LENGTH]
|
short_title = title[:Config.INFO_TAB_TITLE_LENGTH]
|
||||||
|
|
||||||
if self.count() < Config.MAX_INFO_TABS:
|
if self.count() < Config.MAX_INFO_TABS:
|
||||||
# Create a new tab
|
# Create a new tab
|
||||||
@ -61,7 +63,9 @@ class InfoTabs(QTabWidget):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# Reuse oldest widget
|
# Reuse oldest widget
|
||||||
widget = min(self.last_update, key=self.last_update.get) # type: ignore
|
widget = min(
|
||||||
|
self.last_update, key=self.last_update.get # type: ignore
|
||||||
|
)
|
||||||
tab_index = self.indexOf(widget)
|
tab_index = self.indexOf(widget)
|
||||||
self.setTabText(tab_index, short_title)
|
self.setTabText(tab_index, short_title)
|
||||||
|
|
||||||
|
|||||||
318
app/models.py
318
app/models.py
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
import stackprinter # type: ignore
|
||||||
|
|
||||||
from dbconfig import scoped_session
|
from dbconfig import Session, scoped_session
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Optional
|
from typing import Iterable, List, Optional, Union, ValuesView
|
||||||
|
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
|
|
||||||
@ -33,14 +35,21 @@ from sqlalchemy.orm.exc import (
|
|||||||
from sqlalchemy.exc import (
|
from sqlalchemy.exc import (
|
||||||
IntegrityError,
|
IntegrityError,
|
||||||
)
|
)
|
||||||
|
from config import Config
|
||||||
|
from helpers import (
|
||||||
|
fade_point,
|
||||||
|
get_audio_segment,
|
||||||
|
get_tags,
|
||||||
|
leading_silence,
|
||||||
|
trailing_silence,
|
||||||
|
)
|
||||||
from log import log
|
from log import log
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
# Database classes
|
# Database classes
|
||||||
class Carts(Base):
|
class Carts(Base):
|
||||||
__tablename__ = "carts"
|
__tablename__ = 'carts'
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
cart_number: int = Column(Integer, nullable=False, unique=True)
|
cart_number: int = Column(Integer, nullable=False, unique=True)
|
||||||
@ -55,15 +64,10 @@ class Carts(Base):
|
|||||||
f"name={self.name}, path={self.path}>"
|
f"name={self.name}, path={self.path}>"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, session: scoped_session, cart_number: int,
|
||||||
self,
|
name: Optional[str] = None,
|
||||||
session: scoped_session,
|
duration: Optional[int] = None, path: Optional[str] = None,
|
||||||
cart_number: int,
|
enabled: bool = True) -> None:
|
||||||
name: Optional[str] = None,
|
|
||||||
duration: Optional[int] = None,
|
|
||||||
path: Optional[str] = None,
|
|
||||||
enabled: bool = True,
|
|
||||||
) -> None:
|
|
||||||
"""Create new cart"""
|
"""Create new cart"""
|
||||||
|
|
||||||
self.cart_number = cart_number
|
self.cart_number = cart_number
|
||||||
@ -77,7 +81,7 @@ class Carts(Base):
|
|||||||
|
|
||||||
|
|
||||||
class NoteColours(Base):
|
class NoteColours(Base):
|
||||||
__tablename__ = "notecolours"
|
__tablename__ = 'notecolours'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
substring = Column(String(256), index=False)
|
substring = Column(String(256), index=False)
|
||||||
@ -102,15 +106,11 @@ class NoteColours(Base):
|
|||||||
if not text:
|
if not text:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for rec in (
|
for rec in session.execute(
|
||||||
session.execute(
|
|
||||||
select(NoteColours)
|
select(NoteColours)
|
||||||
.filter(NoteColours.enabled.is_(True))
|
.filter(NoteColours.enabled.is_(True))
|
||||||
.order_by(NoteColours.order)
|
.order_by(NoteColours.order)
|
||||||
)
|
).scalars().all():
|
||||||
.scalars()
|
|
||||||
.all()
|
|
||||||
):
|
|
||||||
if rec.is_regex:
|
if rec.is_regex:
|
||||||
flags = re.UNICODE
|
flags = re.UNICODE
|
||||||
if not rec.is_casesensitive:
|
if not rec.is_casesensitive:
|
||||||
@ -130,11 +130,11 @@ class NoteColours(Base):
|
|||||||
|
|
||||||
|
|
||||||
class Playdates(Base):
|
class Playdates(Base):
|
||||||
__tablename__ = "playdates"
|
__tablename__ = 'playdates'
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
lastplayed = Column(DateTime, index=True, default=None)
|
lastplayed = Column(DateTime, index=True, default=None)
|
||||||
track_id = Column(Integer, ForeignKey("tracks.id"))
|
track_id = Column(Integer, ForeignKey('tracks.id'))
|
||||||
track: "Tracks" = relationship("Tracks", back_populates="playdates")
|
track: "Tracks" = relationship("Tracks", back_populates="playdates")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@ -152,7 +152,8 @@ class Playdates(Base):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def last_played(session: scoped_session, track_id: int) -> Optional[datetime]:
|
def last_played(session: scoped_session,
|
||||||
|
track_id: int) -> Optional[datetime]:
|
||||||
"""Return datetime track last played or None"""
|
"""Return datetime track last played or None"""
|
||||||
|
|
||||||
last_played = session.execute(
|
last_played = session.execute(
|
||||||
@ -168,7 +169,8 @@ class Playdates(Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def played_after(session: scoped_session, since: datetime) -> List["Playdates"]:
|
def played_after(session: scoped_session,
|
||||||
|
since: datetime) -> List["Playdates"]:
|
||||||
"""Return a list of Playdates objects since passed time"""
|
"""Return a list of Playdates objects since passed time"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -201,7 +203,7 @@ class Playlists(Base):
|
|||||||
"PlaylistRows",
|
"PlaylistRows",
|
||||||
back_populates="playlist",
|
back_populates="playlist",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
order_by="PlaylistRows.plr_rownum",
|
order_by="PlaylistRows.plr_rownum"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@ -230,9 +232,11 @@ class Playlists(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_playlist_from_template(
|
def create_playlist_from_template(cls,
|
||||||
cls, session: scoped_session, template: "Playlists", playlist_name: str
|
session: scoped_session,
|
||||||
) -> Optional["Playlists"]:
|
template: "Playlists",
|
||||||
|
playlist_name: str) \
|
||||||
|
-> Optional["Playlists"]:
|
||||||
"""Create a new playlist from template"""
|
"""Create a new playlist from template"""
|
||||||
|
|
||||||
playlist = cls(session, playlist_name)
|
playlist = cls(session, playlist_name)
|
||||||
@ -273,7 +277,9 @@ class Playlists(Base):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
session.execute(
|
session.execute(
|
||||||
select(cls).filter(cls.is_template.is_(True)).order_by(cls.name)
|
select(cls)
|
||||||
|
.filter(cls.is_template.is_(True))
|
||||||
|
.order_by(cls.name)
|
||||||
)
|
)
|
||||||
.scalars()
|
.scalars()
|
||||||
.all()
|
.all()
|
||||||
@ -289,7 +295,7 @@ class Playlists(Base):
|
|||||||
.filter(
|
.filter(
|
||||||
cls.tab.is_(None),
|
cls.tab.is_(None),
|
||||||
cls.is_template.is_(False),
|
cls.is_template.is_(False),
|
||||||
cls.deleted.is_(False),
|
cls.deleted.is_(False)
|
||||||
)
|
)
|
||||||
.order_by(cls.last_used.desc())
|
.order_by(cls.last_used.desc())
|
||||||
)
|
)
|
||||||
@ -304,7 +310,11 @@ class Playlists(Base):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
session.execute(select(cls).where(cls.tab.is_not(None)).order_by(cls.tab))
|
session.execute(
|
||||||
|
select(cls)
|
||||||
|
.where(cls.tab.is_not(None))
|
||||||
|
.order_by(cls.tab)
|
||||||
|
)
|
||||||
.scalars()
|
.scalars()
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
@ -319,9 +329,15 @@ class Playlists(Base):
|
|||||||
def move_tab(session: scoped_session, frm: int, to: int) -> None:
|
def move_tab(session: scoped_session, frm: int, to: int) -> None:
|
||||||
"""Move tabs"""
|
"""Move tabs"""
|
||||||
|
|
||||||
row_frm = session.execute(select(Playlists).filter_by(tab=frm)).scalar_one()
|
row_frm = session.execute(
|
||||||
|
select(Playlists)
|
||||||
|
.filter_by(tab=frm)
|
||||||
|
).scalar_one()
|
||||||
|
|
||||||
row_to = session.execute(select(Playlists).filter_by(tab=to)).scalar_one()
|
row_to = session.execute(
|
||||||
|
select(Playlists)
|
||||||
|
.filter_by(tab=to)
|
||||||
|
).scalar_one()
|
||||||
|
|
||||||
row_frm.tab = None
|
row_frm.tab = None
|
||||||
row_to.tab = None
|
row_to.tab = None
|
||||||
@ -338,9 +354,8 @@ class Playlists(Base):
|
|||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def save_as_template(
|
def save_as_template(session: scoped_session,
|
||||||
session: scoped_session, playlist_id: int, template_name: str
|
playlist_id: int, template_name: str) -> None:
|
||||||
) -> None:
|
|
||||||
"""Save passed playlist as new template"""
|
"""Save passed playlist as new template"""
|
||||||
|
|
||||||
template = Playlists(session, template_name)
|
template = Playlists(session, template_name)
|
||||||
@ -354,14 +369,15 @@ class Playlists(Base):
|
|||||||
|
|
||||||
|
|
||||||
class PlaylistRows(Base):
|
class PlaylistRows(Base):
|
||||||
__tablename__ = "playlist_rows"
|
__tablename__ = 'playlist_rows'
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
plr_rownum: int = Column(Integer, nullable=False)
|
plr_rownum: int = Column(Integer, nullable=False)
|
||||||
note: str = Column(String(2048), index=False, default="", nullable=False)
|
note: str = Column(String(2048), index=False, default="", nullable=False)
|
||||||
playlist_id: int = Column(Integer, ForeignKey("playlists.id"), nullable=False)
|
playlist_id: int = Column(Integer, ForeignKey('playlists.id'),
|
||||||
|
nullable=False)
|
||||||
playlist: Playlists = relationship(Playlists, back_populates="rows")
|
playlist: Playlists = relationship(Playlists, back_populates="rows")
|
||||||
track_id = Column(Integer, ForeignKey("tracks.id"), nullable=True)
|
track_id = Column(Integer, ForeignKey('tracks.id'), nullable=True)
|
||||||
track: "Tracks" = relationship("Tracks", back_populates="playlistrows")
|
track: "Tracks" = relationship("Tracks", back_populates="playlistrows")
|
||||||
played: bool = Column(Boolean, nullable=False, index=False, default=False)
|
played: bool = Column(Boolean, nullable=False, index=False, default=False)
|
||||||
|
|
||||||
@ -372,14 +388,13 @@ class PlaylistRows(Base):
|
|||||||
f"note={self.note}, plr_rownum={self.plr_rownum}>"
|
f"note={self.note}, plr_rownum={self.plr_rownum}>"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(self,
|
||||||
self,
|
session: scoped_session,
|
||||||
session: scoped_session,
|
playlist_id: int,
|
||||||
playlist_id: int,
|
track_id: Optional[int],
|
||||||
track_id: Optional[int],
|
row_number: int,
|
||||||
row_number: int,
|
note: str = ""
|
||||||
note: str = "",
|
) -> None:
|
||||||
) -> None:
|
|
||||||
"""Create PlaylistRows object"""
|
"""Create PlaylistRows object"""
|
||||||
|
|
||||||
self.playlist_id = playlist_id
|
self.playlist_id = playlist_id
|
||||||
@ -394,38 +409,38 @@ class PlaylistRows(Base):
|
|||||||
|
|
||||||
current_note = self.note
|
current_note = self.note
|
||||||
if current_note:
|
if current_note:
|
||||||
self.note = current_note + "\n" + extra_note
|
self.note = current_note + '\n' + extra_note
|
||||||
else:
|
else:
|
||||||
self.note = extra_note
|
self.note = extra_note
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def copy_playlist(session: scoped_session, src_id: int, dst_id: int) -> None:
|
def copy_playlist(session: scoped_session,
|
||||||
|
src_id: int,
|
||||||
|
dst_id: int) -> None:
|
||||||
"""Copy playlist entries"""
|
"""Copy playlist entries"""
|
||||||
|
|
||||||
src_rows = (
|
src_rows = session.execute(
|
||||||
session.execute(
|
select(PlaylistRows)
|
||||||
select(PlaylistRows).filter(PlaylistRows.playlist_id == src_id)
|
.filter(PlaylistRows.playlist_id == src_id)
|
||||||
)
|
).scalars().all()
|
||||||
.scalars()
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
for plr in src_rows:
|
for plr in src_rows:
|
||||||
PlaylistRows(session, dst_id, plr.track_id, plr.plr_rownum, plr.note)
|
PlaylistRows(session, dst_id, plr.track_id, plr.plr_rownum,
|
||||||
|
plr.note)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_higher_rows(
|
def delete_higher_rows(
|
||||||
session: scoped_session, playlist_id: int, maxrow: int
|
session: scoped_session, playlist_id: int, maxrow: int) -> None:
|
||||||
) -> None:
|
|
||||||
"""
|
"""
|
||||||
Delete rows in given playlist that have a higher row number
|
Delete rows in given playlist that have a higher row number
|
||||||
than 'maxrow'
|
than 'maxrow'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
session.execute(
|
session.execute(
|
||||||
delete(PlaylistRows).where(
|
delete(PlaylistRows)
|
||||||
|
.where(
|
||||||
PlaylistRows.playlist_id == playlist_id,
|
PlaylistRows.playlist_id == playlist_id,
|
||||||
PlaylistRows.plr_rownum > maxrow,
|
PlaylistRows.plr_rownum > maxrow
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
session.flush()
|
session.flush()
|
||||||
@ -436,15 +451,11 @@ class PlaylistRows(Base):
|
|||||||
Ensure the row numbers for passed playlist have no gaps
|
Ensure the row numbers for passed playlist have no gaps
|
||||||
"""
|
"""
|
||||||
|
|
||||||
plrs = (
|
plrs = session.execute(
|
||||||
session.execute(
|
select(PlaylistRows)
|
||||||
select(PlaylistRows)
|
.where(PlaylistRows.playlist_id == playlist_id)
|
||||||
.where(PlaylistRows.playlist_id == playlist_id)
|
.order_by(PlaylistRows.plr_rownum)
|
||||||
.order_by(PlaylistRows.plr_rownum)
|
).scalars().all()
|
||||||
)
|
|
||||||
.scalars()
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, plr in enumerate(plrs):
|
for i, plr in enumerate(plrs):
|
||||||
plr.plr_rownum = i
|
plr.plr_rownum = i
|
||||||
@ -453,126 +464,113 @@ class PlaylistRows(Base):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_from_id_list(
|
def get_from_id_list(cls, session: scoped_session, playlist_id: int,
|
||||||
cls, session: scoped_session, playlist_id: int, plr_ids: List[int]
|
plr_ids: List[int]) -> List["PlaylistRows"]:
|
||||||
) -> List["PlaylistRows"]:
|
|
||||||
"""
|
"""
|
||||||
Take a list of PlaylistRows ids and return a list of corresponding
|
Take a list of PlaylistRows ids and return a list of corresponding
|
||||||
PlaylistRows objects
|
PlaylistRows objects
|
||||||
"""
|
"""
|
||||||
|
|
||||||
plrs = (
|
plrs = session.execute(
|
||||||
session.execute(
|
select(cls)
|
||||||
select(cls)
|
.where(
|
||||||
.where(cls.playlist_id == playlist_id, cls.id.in_(plr_ids))
|
cls.playlist_id == playlist_id,
|
||||||
.order_by(cls.plr_rownum)
|
cls.id.in_(plr_ids)
|
||||||
)
|
).order_by(cls.plr_rownum)).scalars().all()
|
||||||
.scalars()
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
return plrs
|
return plrs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_last_used_row(session: scoped_session, playlist_id: int) -> Optional[int]:
|
def get_last_used_row(session: scoped_session,
|
||||||
|
playlist_id: int) -> Optional[int]:
|
||||||
"""Return the last used row for playlist, or None if no rows"""
|
"""Return the last used row for playlist, or None if no rows"""
|
||||||
|
|
||||||
return session.execute(
|
return session.execute(
|
||||||
select(func.max(PlaylistRows.plr_rownum)).where(
|
select(func.max(PlaylistRows.plr_rownum))
|
||||||
PlaylistRows.playlist_id == playlist_id
|
.where(PlaylistRows.playlist_id == playlist_id)
|
||||||
)
|
|
||||||
).scalar_one()
|
).scalar_one()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_track_plr(
|
def get_track_plr(session: scoped_session, track_id: int,
|
||||||
session: scoped_session, track_id: int, playlist_id: int
|
playlist_id: int) -> Optional["PlaylistRows"]:
|
||||||
) -> Optional["PlaylistRows"]:
|
|
||||||
"""Return first matching PlaylistRows object or None"""
|
"""Return first matching PlaylistRows object or None"""
|
||||||
|
|
||||||
return session.scalars(
|
return session.scalars(
|
||||||
select(PlaylistRows)
|
select(PlaylistRows)
|
||||||
.where(
|
.where(
|
||||||
PlaylistRows.track_id == track_id,
|
PlaylistRows.track_id == track_id,
|
||||||
PlaylistRows.playlist_id == playlist_id,
|
PlaylistRows.playlist_id == playlist_id
|
||||||
)
|
)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_played_rows(
|
def get_played_rows(cls, session: scoped_session,
|
||||||
cls, session: scoped_session, playlist_id: int
|
playlist_id: int) -> List["PlaylistRows"]:
|
||||||
) -> List["PlaylistRows"]:
|
|
||||||
"""
|
"""
|
||||||
For passed playlist, return a list of rows that
|
For passed playlist, return a list of rows that
|
||||||
have been played.
|
have been played.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
plrs = (
|
plrs = session.execute(
|
||||||
session.execute(
|
select(cls)
|
||||||
select(cls)
|
.where(
|
||||||
.where(cls.playlist_id == playlist_id, cls.played.is_(True))
|
cls.playlist_id == playlist_id,
|
||||||
.order_by(cls.plr_rownum)
|
cls.played.is_(True)
|
||||||
)
|
)
|
||||||
.scalars()
|
.order_by(cls.plr_rownum)
|
||||||
.all()
|
).scalars().all()
|
||||||
)
|
|
||||||
|
|
||||||
return plrs
|
return plrs
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_rows_with_tracks(
|
def get_rows_with_tracks(
|
||||||
cls,
|
cls, session: scoped_session, playlist_id: int,
|
||||||
session: scoped_session,
|
|
||||||
playlist_id: int,
|
|
||||||
from_row: Optional[int] = None,
|
from_row: Optional[int] = None,
|
||||||
to_row: Optional[int] = None,
|
to_row: Optional[int] = None) -> List["PlaylistRows"]:
|
||||||
) -> List["PlaylistRows"]:
|
|
||||||
"""
|
"""
|
||||||
For passed playlist, return a list of rows that
|
For passed playlist, return a list of rows that
|
||||||
contain tracks
|
contain tracks
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = select(cls).where(
|
query = select(cls).where(
|
||||||
cls.playlist_id == playlist_id, cls.track_id.is_not(None)
|
cls.playlist_id == playlist_id,
|
||||||
|
cls.track_id.is_not(None)
|
||||||
)
|
)
|
||||||
if from_row is not None:
|
if from_row is not None:
|
||||||
query = query.where(cls.plr_rownum >= from_row)
|
query = query.where(cls.plr_rownum >= from_row)
|
||||||
if to_row is not None:
|
if to_row is not None:
|
||||||
query = query.where(cls.plr_rownum <= to_row)
|
query = query.where(cls.plr_rownum <= to_row)
|
||||||
|
|
||||||
plrs = session.execute((query).order_by(cls.plr_rownum)).scalars().all()
|
plrs = (
|
||||||
|
session.execute((query).order_by(cls.plr_rownum)).scalars().all()
|
||||||
|
)
|
||||||
|
|
||||||
return plrs
|
return plrs
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_unplayed_rows(
|
def get_unplayed_rows(cls, session: scoped_session,
|
||||||
cls, session: scoped_session, playlist_id: int
|
playlist_id: int) -> List["PlaylistRows"]:
|
||||||
) -> List["PlaylistRows"]:
|
|
||||||
"""
|
"""
|
||||||
For passed playlist, return a list of playlist rows that
|
For passed playlist, return a list of playlist rows that
|
||||||
have not been played.
|
have not been played.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
plrs = (
|
plrs = session.execute(
|
||||||
session.execute(
|
select(cls)
|
||||||
select(cls)
|
.where(
|
||||||
.where(
|
cls.playlist_id == playlist_id,
|
||||||
cls.playlist_id == playlist_id,
|
cls.track_id.is_not(None),
|
||||||
cls.track_id.is_not(None),
|
cls.played.is_(False)
|
||||||
cls.played.is_(False),
|
|
||||||
)
|
|
||||||
.order_by(cls.plr_rownum)
|
|
||||||
)
|
)
|
||||||
.scalars()
|
.order_by(cls.plr_rownum)
|
||||||
.all()
|
).scalars().all()
|
||||||
)
|
|
||||||
|
|
||||||
return plrs
|
return plrs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def move_rows_down(
|
def move_rows_down(session: scoped_session, playlist_id: int,
|
||||||
session: scoped_session, playlist_id: int, starting_row: int, move_by: int
|
starting_row: int, move_by: int) -> None:
|
||||||
) -> None:
|
|
||||||
"""
|
"""
|
||||||
Create space to insert move_by additional rows by incremented row
|
Create space to insert move_by additional rows by incremented row
|
||||||
number from starting_row to end of playlist
|
number from starting_row to end of playlist
|
||||||
@ -582,7 +580,7 @@ class PlaylistRows(Base):
|
|||||||
update(PlaylistRows)
|
update(PlaylistRows)
|
||||||
.where(
|
.where(
|
||||||
(PlaylistRows.playlist_id == playlist_id),
|
(PlaylistRows.playlist_id == playlist_id),
|
||||||
(PlaylistRows.plr_rownum >= starting_row),
|
(PlaylistRows.plr_rownum >= starting_row)
|
||||||
)
|
)
|
||||||
.values(plr_rownum=PlaylistRows.plr_rownum + move_by)
|
.values(plr_rownum=PlaylistRows.plr_rownum + move_by)
|
||||||
)
|
)
|
||||||
@ -591,7 +589,7 @@ class PlaylistRows(Base):
|
|||||||
class Settings(Base):
|
class Settings(Base):
|
||||||
"""Manage settings"""
|
"""Manage settings"""
|
||||||
|
|
||||||
__tablename__ = "settings"
|
__tablename__ = 'settings'
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
name: str = Column(String(64), nullable=False, unique=True)
|
name: str = Column(String(64), nullable=False, unique=True)
|
||||||
@ -604,16 +602,21 @@ class Settings(Base):
|
|||||||
return f"<Settings(id={self.id}, name={self.name}, {value=}>"
|
return f"<Settings(id={self.id}, name={self.name}, {value=}>"
|
||||||
|
|
||||||
def __init__(self, session: scoped_session, name: str):
|
def __init__(self, session: scoped_session, name: str):
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
session.add(self)
|
session.add(self)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_int_settings(cls, session: scoped_session, name: str) -> "Settings":
|
def get_int_settings(cls, session: scoped_session,
|
||||||
|
name: str) -> "Settings":
|
||||||
"""Get setting for an integer or return new setting record"""
|
"""Get setting for an integer or return new setting record"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return session.execute(select(cls).where(cls.name == name)).scalar_one()
|
return session.execute(
|
||||||
|
select(cls)
|
||||||
|
.where(cls.name == name)
|
||||||
|
).scalar_one()
|
||||||
|
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return Settings(session, name)
|
return Settings(session, name)
|
||||||
@ -626,7 +629,7 @@ class Settings(Base):
|
|||||||
|
|
||||||
|
|
||||||
class Tracks(Base):
|
class Tracks(Base):
|
||||||
__tablename__ = "tracks"
|
__tablename__ = 'tracks'
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
title = Column(String(256), index=True)
|
title = Column(String(256), index=True)
|
||||||
@ -638,7 +641,8 @@ class Tracks(Base):
|
|||||||
path: str = Column(String(2048), index=False, nullable=False, unique=True)
|
path: str = Column(String(2048), index=False, nullable=False, unique=True)
|
||||||
mtime = Column(Float, index=True)
|
mtime = Column(Float, index=True)
|
||||||
bitrate = Column(Integer, nullable=True, default=None)
|
bitrate = Column(Integer, nullable=True, default=None)
|
||||||
playlistrows: PlaylistRows = relationship("PlaylistRows", back_populates="track")
|
playlistrows: PlaylistRows = relationship("PlaylistRows",
|
||||||
|
back_populates="track")
|
||||||
playlists = association_proxy("playlistrows", "playlist")
|
playlists = association_proxy("playlistrows", "playlist")
|
||||||
playdates: Playdates = relationship("Playdates", back_populates="track")
|
playdates: Playdates = relationship("Playdates", back_populates="track")
|
||||||
|
|
||||||
@ -649,18 +653,18 @@ class Tracks(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session: scoped_session,
|
session: scoped_session,
|
||||||
path: str,
|
path: str,
|
||||||
title: Optional[str] = None,
|
title: Optional[str] = None,
|
||||||
artist: Optional[str] = None,
|
artist: Optional[str] = None,
|
||||||
duration: int = 0,
|
duration: int = 0,
|
||||||
start_gap: int = 0,
|
start_gap: int = 0,
|
||||||
fade_at: Optional[int] = None,
|
fade_at: Optional[int] = None,
|
||||||
silence_at: Optional[int] = None,
|
silence_at: Optional[int] = None,
|
||||||
mtime: Optional[float] = None,
|
mtime: Optional[float] = None,
|
||||||
lastplayed: Optional[datetime] = None,
|
lastplayed: Optional[datetime] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.path = path
|
self.path = path
|
||||||
self.title = title
|
self.title = title
|
||||||
self.artist = artist
|
self.artist = artist
|
||||||
@ -689,36 +693,46 @@ class Tracks(Base):
|
|||||||
return session.execute(select(cls)).scalars().all()
|
return session.execute(select(cls)).scalars().all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_path(cls, session: scoped_session, path: str) -> Optional["Tracks"]:
|
def get_by_path(cls, session: scoped_session,
|
||||||
|
path: str) -> Optional["Tracks"]:
|
||||||
"""
|
"""
|
||||||
Return track with passed path, or None.
|
Return track with passed path, or None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return session.execute(
|
return (
|
||||||
select(Tracks).where(Tracks.path == path)
|
session.execute(
|
||||||
).scalar_one()
|
select(Tracks)
|
||||||
|
.where(Tracks.path == path)
|
||||||
|
).scalar_one()
|
||||||
|
)
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_artists(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
def search_artists(cls, session: scoped_session,
|
||||||
|
text: str) -> List["Tracks"]:
|
||||||
"""Search case-insenstively for artists containing str"""
|
"""Search case-insenstively for artists containing str"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
session.execute(
|
session.execute(
|
||||||
select(cls).where(cls.artist.ilike(f"%{text}%")).order_by(cls.title)
|
select(cls)
|
||||||
|
.where(cls.artist.ilike(f"%{text}%"))
|
||||||
|
.order_by(cls.title)
|
||||||
)
|
)
|
||||||
.scalars()
|
.scalars()
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_titles(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
def search_titles(cls, session: scoped_session,
|
||||||
|
text: str) -> List["Tracks"]:
|
||||||
"""Search case-insenstively for titles containing str"""
|
"""Search case-insenstively for titles containing str"""
|
||||||
return (
|
return (
|
||||||
session.execute(
|
session.execute(
|
||||||
select(cls).where(cls.title.like(f"{text}%")).order_by(cls.title)
|
select(cls)
|
||||||
|
.where(cls.title.like(f"{text}%"))
|
||||||
|
.order_by(cls.title)
|
||||||
)
|
)
|
||||||
.scalars()
|
.scalars()
|
||||||
.all()
|
.all()
|
||||||
|
|||||||
13
app/music.py
13
app/music.py
@ -1,16 +1,16 @@
|
|||||||
# import os
|
# import os
|
||||||
import threading
|
import threading
|
||||||
import vlc # type: ignore
|
import vlc # type: ignore
|
||||||
|
|
||||||
#
|
#
|
||||||
from config import Config
|
from config import Config
|
||||||
|
from datetime import datetime
|
||||||
from helpers import file_is_unreadable
|
from helpers import file_is_unreadable
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from log import log
|
from log import log
|
||||||
|
|
||||||
from PyQt6.QtCore import ( # type: ignore
|
from PyQt6.QtCore import ( # type: ignore
|
||||||
QRunnable,
|
QRunnable,
|
||||||
QThreadPool,
|
QThreadPool,
|
||||||
)
|
)
|
||||||
@ -19,6 +19,7 @@ lock = threading.Lock()
|
|||||||
|
|
||||||
|
|
||||||
class FadeTrack(QRunnable):
|
class FadeTrack(QRunnable):
|
||||||
|
|
||||||
def __init__(self, player: vlc.MediaPlayer) -> None:
|
def __init__(self, player: vlc.MediaPlayer) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.player = player
|
self.player = player
|
||||||
@ -46,7 +47,8 @@ class FadeTrack(QRunnable):
|
|||||||
|
|
||||||
for i in range(1, steps + 1):
|
for i in range(1, steps + 1):
|
||||||
measures_to_reduce_by += i
|
measures_to_reduce_by += i
|
||||||
volume_factor = 1 - (measures_to_reduce_by / total_measures_count)
|
volume_factor = 1 - (
|
||||||
|
measures_to_reduce_by / total_measures_count)
|
||||||
self.player.audio_set_volume(int(original_volume * volume_factor))
|
self.player.audio_set_volume(int(original_volume * volume_factor))
|
||||||
sleep(sleep_time)
|
sleep(sleep_time)
|
||||||
|
|
||||||
@ -96,7 +98,8 @@ class Music:
|
|||||||
return None
|
return None
|
||||||
return self.player.get_position()
|
return self.player.get_position()
|
||||||
|
|
||||||
def play(self, path: str, position: Optional[float] = None) -> Optional[int]:
|
def play(self, path: str,
|
||||||
|
position: Optional[float] = None) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Start playing the track at path.
|
Start playing the track at path.
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
572
app/playlists.py
572
app/playlists.py
File diff suppressed because it is too large
Load Diff
@ -5,12 +5,16 @@
|
|||||||
# parent (eg, bettet bitrate).
|
# parent (eg, bettet bitrate).
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pydymenu # type: ignore
|
import pydymenu # type: ignore
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from helpers import (
|
from helpers import (
|
||||||
|
fade_point,
|
||||||
|
get_audio_segment,
|
||||||
get_tags,
|
get_tags,
|
||||||
|
leading_silence,
|
||||||
|
trailing_silence,
|
||||||
set_track_metadata,
|
set_track_metadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,7 +29,7 @@ process_tag_matches = True
|
|||||||
do_processing = True
|
do_processing = True
|
||||||
process_no_matches = True
|
process_no_matches = True
|
||||||
|
|
||||||
source_dir = "/home/kae/music/Singles/tmp"
|
source_dir = '/home/kae/music/Singles/tmp'
|
||||||
parent_dir = os.path.dirname(source_dir)
|
parent_dir = os.path.dirname(source_dir)
|
||||||
# #########################################################
|
# #########################################################
|
||||||
|
|
||||||
@ -42,7 +46,7 @@ def main():
|
|||||||
# We only want to run this against the production database because
|
# We only want to run this against the production database because
|
||||||
# we will affect files in the common pool of tracks used by all
|
# we will affect files in the common pool of tracks used by all
|
||||||
# databases
|
# databases
|
||||||
if "musicmuster_prod" not in os.environ.get("MM_DB"):
|
if 'musicmuster_prod' not in os.environ.get('MM_DB'):
|
||||||
response = input("Not on production database - c to continue: ")
|
response = input("Not on production database - c to continue: ")
|
||||||
if response != "c":
|
if response != "c":
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -64,8 +68,8 @@ def main():
|
|||||||
artists_to_path = {}
|
artists_to_path = {}
|
||||||
for k, v in parents.items():
|
for k, v in parents.items():
|
||||||
try:
|
try:
|
||||||
titles_to_path[v["title"].lower()] = k
|
titles_to_path[v['title'].lower()] = k
|
||||||
artists_to_path[v["artist"].lower()] = k
|
artists_to_path[v['artist'].lower()] = k
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -74,43 +78,44 @@ def main():
|
|||||||
if not os.path.isfile(new_path):
|
if not os.path.isfile(new_path):
|
||||||
continue
|
continue
|
||||||
new_tags = get_tags(new_path)
|
new_tags = get_tags(new_path)
|
||||||
new_title = new_tags["title"]
|
new_title = new_tags['title']
|
||||||
new_artist = new_tags["artist"]
|
new_artist = new_tags['artist']
|
||||||
bitrate = new_tags["bitrate"]
|
bitrate = new_tags['bitrate']
|
||||||
|
|
||||||
# If same filename exists in parent direcory, check tags
|
# If same filename exists in parent direcory, check tags
|
||||||
parent_path = os.path.join(parent_dir, new_fname)
|
parent_path = os.path.join(parent_dir, new_fname)
|
||||||
if os.path.exists(parent_path):
|
if os.path.exists(parent_path):
|
||||||
parent_tags = get_tags(parent_path)
|
parent_tags = get_tags(parent_path)
|
||||||
parent_title = parent_tags["title"]
|
parent_title = parent_tags['title']
|
||||||
parent_artist = parent_tags["artist"]
|
parent_artist = parent_tags['artist']
|
||||||
if (str(parent_title).lower() == str(new_title).lower()) and (
|
if (
|
||||||
str(parent_artist).lower() == str(new_artist).lower()
|
(str(parent_title).lower() == str(new_title).lower()) and
|
||||||
|
(str(parent_artist).lower() == str(new_artist).lower())
|
||||||
):
|
):
|
||||||
name_and_tags.append(
|
name_and_tags.append(
|
||||||
f" {new_fname=}, {parent_title} → {new_title}, "
|
f" {new_fname=}, {parent_title} → {new_title}, "
|
||||||
f" {parent_artist} → {new_artist}"
|
f" {parent_artist} → {new_artist}"
|
||||||
)
|
)
|
||||||
if process_name_and_tags_matches:
|
if process_name_and_tags_matches:
|
||||||
process_track(new_path, parent_path, new_title, new_artist, bitrate)
|
process_track(new_path, parent_path, new_title,
|
||||||
|
new_artist, bitrate)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check for matching tags although filename is different
|
# Check for matching tags although filename is different
|
||||||
if new_title.lower() in titles_to_path:
|
if new_title.lower() in titles_to_path:
|
||||||
possible_path = titles_to_path[new_title.lower()]
|
possible_path = titles_to_path[new_title.lower()]
|
||||||
if parents[possible_path]["artist"].lower() == new_artist.lower():
|
if parents[possible_path]['artist'].lower() == new_artist.lower():
|
||||||
# print(
|
# print(
|
||||||
# f"title={new_title}, artist={new_artist}:\n"
|
# f"title={new_title}, artist={new_artist}:\n"
|
||||||
# f" {new_path} → {parent_path}"
|
# f" {new_path} → {parent_path}"
|
||||||
# )
|
# )
|
||||||
tags_not_name.append(
|
tags_not_name.append(
|
||||||
f"title={new_title}, artist={new_artist}:\n"
|
f"title={new_title}, artist={new_artist}:\n"
|
||||||
f" {new_path} → {parent_path}"
|
f" {new_path} → {parent_path}"
|
||||||
)
|
|
||||||
if process_tag_matches:
|
|
||||||
process_track(
|
|
||||||
new_path, possible_path, new_title, new_artist, bitrate
|
|
||||||
)
|
)
|
||||||
|
if process_tag_matches:
|
||||||
|
process_track(new_path, possible_path, new_title,
|
||||||
|
new_artist, bitrate)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
no_match += 1
|
no_match += 1
|
||||||
@ -127,8 +132,8 @@ def main():
|
|||||||
if choice:
|
if choice:
|
||||||
old_file = os.path.join(parent_dir, choice[0])
|
old_file = os.path.join(parent_dir, choice[0])
|
||||||
oldtags = get_tags(old_file)
|
oldtags = get_tags(old_file)
|
||||||
old_title = oldtags["title"]
|
old_title = oldtags['title']
|
||||||
old_artist = oldtags["artist"]
|
old_artist = oldtags['artist']
|
||||||
print()
|
print()
|
||||||
print(f" File name will change {choice[0]}")
|
print(f" File name will change {choice[0]}")
|
||||||
print(f" → {new_fname}")
|
print(f" → {new_fname}")
|
||||||
@ -227,8 +232,11 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
def process_track(src, dst, title, artist, bitrate):
|
def process_track(src, dst, title, artist, bitrate):
|
||||||
|
|
||||||
new_path = os.path.join(os.path.dirname(dst), os.path.basename(src))
|
new_path = os.path.join(os.path.dirname(dst), os.path.basename(src))
|
||||||
print(f"process_track:\n {src=}\n {dst=}\n {title=}, {artist=}\n")
|
print(
|
||||||
|
f"process_track:\n {src=}\n {dst=}\n {title=}, {artist=}\n"
|
||||||
|
)
|
||||||
|
|
||||||
if not do_processing:
|
if not do_processing:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="TrackHeaderLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
@ -290,25 +290,6 @@ padding-left: 8px;</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QWidget" name="cartsWidget" native="true">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_Carts">
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QFrame" name="frame_6">
|
<widget class="QFrame" name="frame_6">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
@ -383,339 +364,391 @@ padding-left: 8px;</string>
|
|||||||
<property name="frameShadow">
|
<property name="frameShadow">
|
||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="FadeStopInfoFrame">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="minimumSize">
|
<item>
|
||||||
<size>
|
<widget class="QFrame" name="FadeStopInfoFrame">
|
||||||
<width>152</width>
|
<property name="minimumSize">
|
||||||
<height>112</height>
|
<size>
|
||||||
</size>
|
<width>152</width>
|
||||||
</property>
|
<height>112</height>
|
||||||
<property name="maximumSize">
|
</size>
|
||||||
<size>
|
</property>
|
||||||
<width>184</width>
|
<property name="maximumSize">
|
||||||
<height>16777215</height>
|
<size>
|
||||||
</size>
|
<width>184</width>
|
||||||
</property>
|
<height>16777215</height>
|
||||||
<property name="frameShape">
|
</size>
|
||||||
<enum>QFrame::StyledPanel</enum>
|
</property>
|
||||||
</property>
|
<property name="frameShape">
|
||||||
<property name="frameShadow">
|
<enum>QFrame::StyledPanel</enum>
|
||||||
<enum>QFrame::Raised</enum>
|
</property>
|
||||||
</property>
|
<property name="frameShadow">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
<enum>QFrame::Raised</enum>
|
||||||
<item>
|
</property>
|
||||||
<widget class="QPushButton" name="btnPreview">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<property name="minimumSize">
|
<item row="0" column="0" colspan="2">
|
||||||
<size>
|
<widget class="QPushButton" name="btnFade">
|
||||||
<width>132</width>
|
<property name="minimumSize">
|
||||||
<height>41</height>
|
<size>
|
||||||
</size>
|
<width>132</width>
|
||||||
</property>
|
<height>36</height>
|
||||||
<property name="text">
|
</size>
|
||||||
<string> Preview</string>
|
</property>
|
||||||
</property>
|
<property name="maximumSize">
|
||||||
<property name="icon">
|
<size>
|
||||||
<iconset resource="icons.qrc">
|
<width>164</width>
|
||||||
<normaloff>:/icons/headphones</normaloff>:/icons/headphones</iconset>
|
<height>16777215</height>
|
||||||
</property>
|
</size>
|
||||||
<property name="iconSize">
|
</property>
|
||||||
<size>
|
<property name="text">
|
||||||
<width>30</width>
|
<string> Fade</string>
|
||||||
<height>30</height>
|
</property>
|
||||||
</size>
|
<property name="icon">
|
||||||
</property>
|
<iconset resource="icons.qrc">
|
||||||
<property name="checkable">
|
<normaloff>:/icons/fade</normaloff>:/icons/fade</iconset>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="iconSize">
|
||||||
</widget>
|
<size>
|
||||||
</item>
|
<width>30</width>
|
||||||
<item>
|
<height>30</height>
|
||||||
<widget class="QLabel" name="label_elapsed_timer">
|
</size>
|
||||||
<property name="font">
|
</property>
|
||||||
<font>
|
</widget>
|
||||||
<family>FreeSans</family>
|
</item>
|
||||||
<pointsize>18</pointsize>
|
<item row="1" column="0" colspan="2">
|
||||||
<weight>50</weight>
|
<widget class="QPushButton" name="btnPreview">
|
||||||
<bold>false</bold>
|
<property name="minimumSize">
|
||||||
</font>
|
<size>
|
||||||
</property>
|
<width>132</width>
|
||||||
<property name="styleSheet">
|
<height>36</height>
|
||||||
<string notr="true">color: black;</string>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>00:00 / 00:00</string>
|
<string> Preview</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="icon">
|
||||||
<set>Qt::AlignCenter</set>
|
<iconset resource="icons.qrc">
|
||||||
</property>
|
<normaloff>:/icons/headphones</normaloff>:/icons/headphones</iconset>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<property name="iconSize">
|
||||||
</layout>
|
<size>
|
||||||
</widget>
|
<width>30</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_elapsed">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>152</width>
|
||||||
|
<height>112</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Elapsed time</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_elapsed_timer">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>FreeSans</family>
|
||||||
|
<pointsize>40</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: black;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>00:00</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_fade">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>152</width>
|
||||||
|
<height>112</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Fade</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_fade_timer">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>FreeSans</family>
|
||||||
|
<pointsize>40</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>00:00</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="PlotWidget" name="widgetFadeVolume" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_silent">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>152</width>
|
||||||
|
<height>112</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Silent</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_silent_timer">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>FreeSans</family>
|
||||||
|
<pointsize>40</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>00:00</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_end">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>152</width>
|
||||||
|
<height>112</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true"/>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_5">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>End</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_end_timer">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>FreeSans</family>
|
||||||
|
<pointsize>40</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>00:00</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_toggleplayed_3db">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>152</width>
|
||||||
|
<height>112</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>184</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QPushButton" name="btnDrop3db">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>132</width>
|
||||||
|
<height>36</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>164</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>-3dB to talk</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QPushButton" name="btnHidePlayed">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>132</width>
|
||||||
|
<height>36</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>164</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hide played</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QWidget" name="cartsWidget" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_Carts">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frame_toggleplayed_3db">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="minimumSize">
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>152</width>
|
<width>40</width>
|
||||||
<height>112</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
</spacer>
|
||||||
<size>
|
|
||||||
<width>184</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnDrop3db">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>132</width>
|
|
||||||
<height>41</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>164</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>-3dB to talk</string>
|
|
||||||
</property>
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnHidePlayed">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>132</width>
|
|
||||||
<height>41</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>164</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Hide played</string>
|
|
||||||
</property>
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QFrame" name="frame_fade">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>152</width>
|
|
||||||
<height>112</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true"/>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Fade</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_fade_timer">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>FreeSans</family>
|
|
||||||
<pointsize>40</pointsize>
|
|
||||||
<weight>50</weight>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>00:00</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QFrame" name="frame_silent">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>152</width>
|
|
||||||
<height>112</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true"/>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>45</width>
|
|
||||||
<height>24</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Silent</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="label_silent_timer">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>48</y>
|
|
||||||
<width>132</width>
|
|
||||||
<height>54</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>FreeSans</family>
|
|
||||||
<pointsize>40</pointsize>
|
|
||||||
<weight>50</weight>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>00:00</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="PlotWidget" name="widgetFadeVolume" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>1</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QFrame" name="frame">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>151</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>151</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnFade">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>132</width>
|
|
||||||
<height>32</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>164</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string> Fade</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="icons.qrc">
|
|
||||||
<normaloff>:/icons/fade</normaloff>:/icons/fade</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>30</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnStop">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>36</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string> Stop</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="icons.qrc">
|
|
||||||
<normaloff>:/icons/stopsign</normaloff>:/icons/stopsign</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Form implementation generated from reading ui file 'app/ui/main_window.ui'
|
# Form implementation generated from reading ui file 'app/ui/main_window.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt6 UI code generator 6.5.1
|
# Created by: PyQt6 UI code generator 6.5.0
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
@ -22,8 +22,8 @@ class Ui_MainWindow(object):
|
|||||||
self.centralwidget.setObjectName("centralwidget")
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
self.gridLayout_4 = QtWidgets.QGridLayout(self.centralwidget)
|
self.gridLayout_4 = QtWidgets.QGridLayout(self.centralwidget)
|
||||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
|
self.TrackHeaderLayout = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
self.TrackHeaderLayout.setObjectName("TrackHeaderLayout")
|
||||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
|
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||||
self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
@ -74,7 +74,7 @@ class Ui_MainWindow(object):
|
|||||||
self.next_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
self.next_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||||
self.next_track_2.setObjectName("next_track_2")
|
self.next_track_2.setObjectName("next_track_2")
|
||||||
self.verticalLayout_3.addWidget(self.next_track_2)
|
self.verticalLayout_3.addWidget(self.next_track_2)
|
||||||
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
|
self.TrackHeaderLayout.addLayout(self.verticalLayout_3)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget)
|
self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
@ -130,7 +130,7 @@ class Ui_MainWindow(object):
|
|||||||
self.hdrNextTrack.setFlat(True)
|
self.hdrNextTrack.setFlat(True)
|
||||||
self.hdrNextTrack.setObjectName("hdrNextTrack")
|
self.hdrNextTrack.setObjectName("hdrNextTrack")
|
||||||
self.verticalLayout.addWidget(self.hdrNextTrack)
|
self.verticalLayout.addWidget(self.hdrNextTrack)
|
||||||
self.horizontalLayout_3.addLayout(self.verticalLayout)
|
self.TrackHeaderLayout.addLayout(self.verticalLayout)
|
||||||
self.frame_2 = QtWidgets.QFrame(parent=self.centralwidget)
|
self.frame_2 = QtWidgets.QFrame(parent=self.centralwidget)
|
||||||
self.frame_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
self.frame_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
||||||
self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
@ -146,8 +146,8 @@ class Ui_MainWindow(object):
|
|||||||
self.lblTOD.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
self.lblTOD.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
self.lblTOD.setObjectName("lblTOD")
|
self.lblTOD.setObjectName("lblTOD")
|
||||||
self.gridLayout_2.addWidget(self.lblTOD, 0, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.lblTOD, 0, 0, 1, 1)
|
||||||
self.horizontalLayout_3.addWidget(self.frame_2)
|
self.TrackHeaderLayout.addWidget(self.frame_2)
|
||||||
self.gridLayout_4.addLayout(self.horizontalLayout_3, 0, 0, 1, 1)
|
self.gridLayout_4.addLayout(self.TrackHeaderLayout, 0, 0, 1, 1)
|
||||||
self.frame_4 = QtWidgets.QFrame(parent=self.centralwidget)
|
self.frame_4 = QtWidgets.QFrame(parent=self.centralwidget)
|
||||||
self.frame_4.setMinimumSize(QtCore.QSize(0, 16))
|
self.frame_4.setMinimumSize(QtCore.QSize(0, 16))
|
||||||
self.frame_4.setAutoFillBackground(False)
|
self.frame_4.setAutoFillBackground(False)
|
||||||
@ -156,13 +156,6 @@ class Ui_MainWindow(object):
|
|||||||
self.frame_4.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
self.frame_4.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
self.frame_4.setObjectName("frame_4")
|
self.frame_4.setObjectName("frame_4")
|
||||||
self.gridLayout_4.addWidget(self.frame_4, 1, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.frame_4, 1, 0, 1, 1)
|
||||||
self.cartsWidget = QtWidgets.QWidget(parent=self.centralwidget)
|
|
||||||
self.cartsWidget.setObjectName("cartsWidget")
|
|
||||||
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget)
|
|
||||||
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
|
|
||||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
|
||||||
self.horizontalLayout_Carts.addItem(spacerItem)
|
|
||||||
self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1)
|
|
||||||
self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget)
|
self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget)
|
||||||
self.frame_6.setMinimumSize(QtCore.QSize(0, 16))
|
self.frame_6.setMinimumSize(QtCore.QSize(0, 16))
|
||||||
self.frame_6.setAutoFillBackground(False)
|
self.frame_6.setAutoFillBackground(False)
|
||||||
@ -191,7 +184,9 @@ class Ui_MainWindow(object):
|
|||||||
self.InfoFooterFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
self.InfoFooterFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
self.InfoFooterFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
self.InfoFooterFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
self.InfoFooterFrame.setObjectName("InfoFooterFrame")
|
self.InfoFooterFrame.setObjectName("InfoFooterFrame")
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.InfoFooterFrame)
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.InfoFooterFrame)
|
||||||
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
self.FadeStopInfoFrame = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
self.FadeStopInfoFrame = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
||||||
self.FadeStopInfoFrame.setMinimumSize(QtCore.QSize(152, 112))
|
self.FadeStopInfoFrame.setMinimumSize(QtCore.QSize(152, 112))
|
||||||
@ -199,21 +194,43 @@ class Ui_MainWindow(object):
|
|||||||
self.FadeStopInfoFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
self.FadeStopInfoFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
self.FadeStopInfoFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
self.FadeStopInfoFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
self.FadeStopInfoFrame.setObjectName("FadeStopInfoFrame")
|
self.FadeStopInfoFrame.setObjectName("FadeStopInfoFrame")
|
||||||
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.FadeStopInfoFrame)
|
self.gridLayout = QtWidgets.QGridLayout(self.FadeStopInfoFrame)
|
||||||
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
|
self.btnFade = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
|
||||||
self.btnPreview.setMinimumSize(QtCore.QSize(132, 41))
|
self.btnFade.setMinimumSize(QtCore.QSize(132, 36))
|
||||||
|
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
|
||||||
icon1 = QtGui.QIcon()
|
icon1 = QtGui.QIcon()
|
||||||
icon1.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon1.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.btnPreview.setIcon(icon1)
|
self.btnFade.setIcon(icon1)
|
||||||
|
self.btnFade.setIconSize(QtCore.QSize(30, 30))
|
||||||
|
self.btnFade.setObjectName("btnFade")
|
||||||
|
self.gridLayout.addWidget(self.btnFade, 0, 0, 1, 2)
|
||||||
|
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
|
||||||
|
self.btnPreview.setMinimumSize(QtCore.QSize(132, 36))
|
||||||
|
icon2 = QtGui.QIcon()
|
||||||
|
icon2.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
self.btnPreview.setIcon(icon2)
|
||||||
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
|
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
|
||||||
self.btnPreview.setCheckable(True)
|
self.btnPreview.setCheckable(True)
|
||||||
self.btnPreview.setObjectName("btnPreview")
|
self.btnPreview.setObjectName("btnPreview")
|
||||||
self.verticalLayout_4.addWidget(self.btnPreview)
|
self.gridLayout.addWidget(self.btnPreview, 1, 0, 1, 2)
|
||||||
self.label_elapsed_timer = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
|
self.horizontalLayout.addWidget(self.FadeStopInfoFrame)
|
||||||
|
self.frame_elapsed = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
||||||
|
self.frame_elapsed.setMinimumSize(QtCore.QSize(152, 112))
|
||||||
|
self.frame_elapsed.setStyleSheet("")
|
||||||
|
self.frame_elapsed.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
|
self.frame_elapsed.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
|
self.frame_elapsed.setObjectName("frame_elapsed")
|
||||||
|
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_elapsed)
|
||||||
|
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
||||||
|
self.label = QtWidgets.QLabel(parent=self.frame_elapsed)
|
||||||
|
self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.verticalLayout_4.addWidget(self.label)
|
||||||
|
self.label_elapsed_timer = QtWidgets.QLabel(parent=self.frame_elapsed)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("FreeSans")
|
font.setFamily("FreeSans")
|
||||||
font.setPointSize(18)
|
font.setPointSize(40)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
font.setWeight(50)
|
font.setWeight(50)
|
||||||
self.label_elapsed_timer.setFont(font)
|
self.label_elapsed_timer.setFont(font)
|
||||||
@ -221,28 +238,7 @@ class Ui_MainWindow(object):
|
|||||||
self.label_elapsed_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
self.label_elapsed_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
self.label_elapsed_timer.setObjectName("label_elapsed_timer")
|
self.label_elapsed_timer.setObjectName("label_elapsed_timer")
|
||||||
self.verticalLayout_4.addWidget(self.label_elapsed_timer)
|
self.verticalLayout_4.addWidget(self.label_elapsed_timer)
|
||||||
self.horizontalLayout.addWidget(self.FadeStopInfoFrame)
|
self.horizontalLayout.addWidget(self.frame_elapsed)
|
||||||
self.frame_toggleplayed_3db = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
|
||||||
self.frame_toggleplayed_3db.setMinimumSize(QtCore.QSize(152, 112))
|
|
||||||
self.frame_toggleplayed_3db.setMaximumSize(QtCore.QSize(184, 16777215))
|
|
||||||
self.frame_toggleplayed_3db.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
|
||||||
self.frame_toggleplayed_3db.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
|
||||||
self.frame_toggleplayed_3db.setObjectName("frame_toggleplayed_3db")
|
|
||||||
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.frame_toggleplayed_3db)
|
|
||||||
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
|
||||||
self.btnDrop3db = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db)
|
|
||||||
self.btnDrop3db.setMinimumSize(QtCore.QSize(132, 41))
|
|
||||||
self.btnDrop3db.setMaximumSize(QtCore.QSize(164, 16777215))
|
|
||||||
self.btnDrop3db.setCheckable(True)
|
|
||||||
self.btnDrop3db.setObjectName("btnDrop3db")
|
|
||||||
self.verticalLayout_6.addWidget(self.btnDrop3db)
|
|
||||||
self.btnHidePlayed = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db)
|
|
||||||
self.btnHidePlayed.setMinimumSize(QtCore.QSize(132, 41))
|
|
||||||
self.btnHidePlayed.setMaximumSize(QtCore.QSize(164, 16777215))
|
|
||||||
self.btnHidePlayed.setCheckable(True)
|
|
||||||
self.btnHidePlayed.setObjectName("btnHidePlayed")
|
|
||||||
self.verticalLayout_6.addWidget(self.btnHidePlayed)
|
|
||||||
self.horizontalLayout.addWidget(self.frame_toggleplayed_3db)
|
|
||||||
self.frame_fade = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
self.frame_fade = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
||||||
self.frame_fade.setMinimumSize(QtCore.QSize(152, 112))
|
self.frame_fade.setMinimumSize(QtCore.QSize(152, 112))
|
||||||
self.frame_fade.setStyleSheet("")
|
self.frame_fade.setStyleSheet("")
|
||||||
@ -266,27 +262,6 @@ class Ui_MainWindow(object):
|
|||||||
self.label_fade_timer.setObjectName("label_fade_timer")
|
self.label_fade_timer.setObjectName("label_fade_timer")
|
||||||
self.verticalLayout_2.addWidget(self.label_fade_timer)
|
self.verticalLayout_2.addWidget(self.label_fade_timer)
|
||||||
self.horizontalLayout.addWidget(self.frame_fade)
|
self.horizontalLayout.addWidget(self.frame_fade)
|
||||||
self.frame_silent = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
|
||||||
self.frame_silent.setMinimumSize(QtCore.QSize(152, 112))
|
|
||||||
self.frame_silent.setStyleSheet("")
|
|
||||||
self.frame_silent.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
|
||||||
self.frame_silent.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
|
||||||
self.frame_silent.setObjectName("frame_silent")
|
|
||||||
self.label_5 = QtWidgets.QLabel(parent=self.frame_silent)
|
|
||||||
self.label_5.setGeometry(QtCore.QRect(10, 10, 45, 24))
|
|
||||||
self.label_5.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
|
||||||
self.label_5.setObjectName("label_5")
|
|
||||||
self.label_silent_timer = QtWidgets.QLabel(parent=self.frame_silent)
|
|
||||||
self.label_silent_timer.setGeometry(QtCore.QRect(10, 48, 132, 54))
|
|
||||||
font = QtGui.QFont()
|
|
||||||
font.setFamily("FreeSans")
|
|
||||||
font.setPointSize(40)
|
|
||||||
font.setBold(False)
|
|
||||||
font.setWeight(50)
|
|
||||||
self.label_silent_timer.setFont(font)
|
|
||||||
self.label_silent_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
|
||||||
self.label_silent_timer.setObjectName("label_silent_timer")
|
|
||||||
self.horizontalLayout.addWidget(self.frame_silent)
|
|
||||||
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
|
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(1)
|
sizePolicy.setHorizontalStretch(1)
|
||||||
@ -296,32 +271,82 @@ class Ui_MainWindow(object):
|
|||||||
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
|
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
|
||||||
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
|
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
|
||||||
self.horizontalLayout.addWidget(self.widgetFadeVolume)
|
self.horizontalLayout.addWidget(self.widgetFadeVolume)
|
||||||
self.frame = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
self.frame_silent = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
||||||
self.frame.setMinimumSize(QtCore.QSize(151, 0))
|
self.frame_silent.setMinimumSize(QtCore.QSize(152, 112))
|
||||||
self.frame.setMaximumSize(QtCore.QSize(151, 16777215))
|
self.frame_silent.setStyleSheet("")
|
||||||
self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
self.frame_silent.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
self.frame_silent.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
self.frame.setObjectName("frame")
|
self.frame_silent.setObjectName("frame_silent")
|
||||||
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame)
|
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame_silent)
|
||||||
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||||
self.btnFade = QtWidgets.QPushButton(parent=self.frame)
|
self.label_5 = QtWidgets.QLabel(parent=self.frame_silent)
|
||||||
self.btnFade.setMinimumSize(QtCore.QSize(132, 32))
|
self.label_5.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
|
self.label_5.setObjectName("label_5")
|
||||||
icon2 = QtGui.QIcon()
|
self.verticalLayout_5.addWidget(self.label_5)
|
||||||
icon2.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
self.label_silent_timer = QtWidgets.QLabel(parent=self.frame_silent)
|
||||||
self.btnFade.setIcon(icon2)
|
font = QtGui.QFont()
|
||||||
self.btnFade.setIconSize(QtCore.QSize(30, 30))
|
font.setFamily("FreeSans")
|
||||||
self.btnFade.setObjectName("btnFade")
|
font.setPointSize(40)
|
||||||
self.verticalLayout_5.addWidget(self.btnFade)
|
font.setBold(False)
|
||||||
self.btnStop = QtWidgets.QPushButton(parent=self.frame)
|
font.setWeight(50)
|
||||||
self.btnStop.setMinimumSize(QtCore.QSize(0, 36))
|
self.label_silent_timer.setFont(font)
|
||||||
icon3 = QtGui.QIcon()
|
self.label_silent_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
icon3.addPixmap(QtGui.QPixmap(":/icons/stopsign"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
self.label_silent_timer.setObjectName("label_silent_timer")
|
||||||
self.btnStop.setIcon(icon3)
|
self.verticalLayout_5.addWidget(self.label_silent_timer)
|
||||||
self.btnStop.setObjectName("btnStop")
|
self.horizontalLayout.addWidget(self.frame_silent)
|
||||||
self.verticalLayout_5.addWidget(self.btnStop)
|
self.frame_end = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
||||||
self.horizontalLayout.addWidget(self.frame)
|
self.frame_end.setMinimumSize(QtCore.QSize(152, 112))
|
||||||
|
self.frame_end.setStyleSheet("")
|
||||||
|
self.frame_end.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
|
self.frame_end.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
|
self.frame_end.setObjectName("frame_end")
|
||||||
|
self.gridLayout_5 = QtWidgets.QGridLayout(self.frame_end)
|
||||||
|
self.gridLayout_5.setObjectName("gridLayout_5")
|
||||||
|
self.label_6 = QtWidgets.QLabel(parent=self.frame_end)
|
||||||
|
self.label_6.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.label_6.setObjectName("label_6")
|
||||||
|
self.gridLayout_5.addWidget(self.label_6, 0, 0, 1, 1)
|
||||||
|
self.label_end_timer = QtWidgets.QLabel(parent=self.frame_end)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setFamily("FreeSans")
|
||||||
|
font.setPointSize(40)
|
||||||
|
font.setBold(False)
|
||||||
|
font.setWeight(50)
|
||||||
|
self.label_end_timer.setFont(font)
|
||||||
|
self.label_end_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.label_end_timer.setObjectName("label_end_timer")
|
||||||
|
self.gridLayout_5.addWidget(self.label_end_timer, 1, 0, 1, 1)
|
||||||
|
self.horizontalLayout.addWidget(self.frame_end)
|
||||||
|
self.frame_toggleplayed_3db = QtWidgets.QFrame(parent=self.InfoFooterFrame)
|
||||||
|
self.frame_toggleplayed_3db.setMinimumSize(QtCore.QSize(152, 112))
|
||||||
|
self.frame_toggleplayed_3db.setMaximumSize(QtCore.QSize(184, 16777215))
|
||||||
|
self.frame_toggleplayed_3db.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
|
||||||
|
self.frame_toggleplayed_3db.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
|
||||||
|
self.frame_toggleplayed_3db.setObjectName("frame_toggleplayed_3db")
|
||||||
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.frame_toggleplayed_3db)
|
||||||
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
|
self.btnDrop3db = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db)
|
||||||
|
self.btnDrop3db.setMinimumSize(QtCore.QSize(132, 36))
|
||||||
|
self.btnDrop3db.setMaximumSize(QtCore.QSize(164, 16777215))
|
||||||
|
self.btnDrop3db.setCheckable(True)
|
||||||
|
self.btnDrop3db.setObjectName("btnDrop3db")
|
||||||
|
self.gridLayout_3.addWidget(self.btnDrop3db, 0, 0, 1, 1)
|
||||||
|
self.btnHidePlayed = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db)
|
||||||
|
self.btnHidePlayed.setMinimumSize(QtCore.QSize(132, 36))
|
||||||
|
self.btnHidePlayed.setMaximumSize(QtCore.QSize(164, 16777215))
|
||||||
|
self.btnHidePlayed.setCheckable(True)
|
||||||
|
self.btnHidePlayed.setObjectName("btnHidePlayed")
|
||||||
|
self.gridLayout_3.addWidget(self.btnHidePlayed, 1, 0, 1, 1)
|
||||||
|
self.horizontalLayout.addWidget(self.frame_toggleplayed_3db)
|
||||||
|
self.horizontalLayout_2.addLayout(self.horizontalLayout)
|
||||||
self.gridLayout_4.addWidget(self.InfoFooterFrame, 5, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.InfoFooterFrame, 5, 0, 1, 1)
|
||||||
|
self.cartsWidget = QtWidgets.QWidget(parent=self.centralwidget)
|
||||||
|
self.cartsWidget.setObjectName("cartsWidget")
|
||||||
|
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget)
|
||||||
|
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||||
|
self.horizontalLayout_Carts.addItem(spacerItem)
|
||||||
|
self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1)
|
||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
||||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 29))
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 29))
|
||||||
@ -341,41 +366,41 @@ class Ui_MainWindow(object):
|
|||||||
self.statusbar.setObjectName("statusbar")
|
self.statusbar.setObjectName("statusbar")
|
||||||
MainWindow.setStatusBar(self.statusbar)
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
self.actionPlay_next = QtGui.QAction(parent=MainWindow)
|
self.actionPlay_next = QtGui.QAction(parent=MainWindow)
|
||||||
icon4 = QtGui.QIcon()
|
icon3 = QtGui.QIcon()
|
||||||
icon4.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon3.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.actionPlay_next.setIcon(icon4)
|
self.actionPlay_next.setIcon(icon3)
|
||||||
self.actionPlay_next.setObjectName("actionPlay_next")
|
self.actionPlay_next.setObjectName("actionPlay_next")
|
||||||
self.actionSkipToNext = QtGui.QAction(parent=MainWindow)
|
self.actionSkipToNext = QtGui.QAction(parent=MainWindow)
|
||||||
icon5 = QtGui.QIcon()
|
icon4 = QtGui.QIcon()
|
||||||
icon5.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon4.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.actionSkipToNext.setIcon(icon5)
|
self.actionSkipToNext.setIcon(icon4)
|
||||||
self.actionSkipToNext.setObjectName("actionSkipToNext")
|
self.actionSkipToNext.setObjectName("actionSkipToNext")
|
||||||
self.actionInsertTrack = QtGui.QAction(parent=MainWindow)
|
self.actionInsertTrack = QtGui.QAction(parent=MainWindow)
|
||||||
icon6 = QtGui.QIcon()
|
icon5 = QtGui.QIcon()
|
||||||
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon5.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.actionInsertTrack.setIcon(icon6)
|
self.actionInsertTrack.setIcon(icon5)
|
||||||
self.actionInsertTrack.setObjectName("actionInsertTrack")
|
self.actionInsertTrack.setObjectName("actionInsertTrack")
|
||||||
self.actionAdd_file = QtGui.QAction(parent=MainWindow)
|
self.actionAdd_file = QtGui.QAction(parent=MainWindow)
|
||||||
icon7 = QtGui.QIcon()
|
icon6 = QtGui.QIcon()
|
||||||
icon7.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.actionAdd_file.setIcon(icon7)
|
self.actionAdd_file.setIcon(icon6)
|
||||||
self.actionAdd_file.setObjectName("actionAdd_file")
|
self.actionAdd_file.setObjectName("actionAdd_file")
|
||||||
self.actionFade = QtGui.QAction(parent=MainWindow)
|
self.actionFade = QtGui.QAction(parent=MainWindow)
|
||||||
icon8 = QtGui.QIcon()
|
icon7 = QtGui.QIcon()
|
||||||
icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon7.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.actionFade.setIcon(icon8)
|
self.actionFade.setIcon(icon7)
|
||||||
self.actionFade.setObjectName("actionFade")
|
self.actionFade.setObjectName("actionFade")
|
||||||
self.actionStop = QtGui.QAction(parent=MainWindow)
|
self.actionStop = QtGui.QAction(parent=MainWindow)
|
||||||
icon9 = QtGui.QIcon()
|
icon8 = QtGui.QIcon()
|
||||||
icon9.addPixmap(QtGui.QPixmap(":/icons/stop"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon8.addPixmap(QtGui.QPixmap(":/icons/stop"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.actionStop.setIcon(icon9)
|
self.actionStop.setIcon(icon8)
|
||||||
self.actionStop.setObjectName("actionStop")
|
self.actionStop.setObjectName("actionStop")
|
||||||
self.action_Clear_selection = QtGui.QAction(parent=MainWindow)
|
self.action_Clear_selection = QtGui.QAction(parent=MainWindow)
|
||||||
self.action_Clear_selection.setObjectName("action_Clear_selection")
|
self.action_Clear_selection.setObjectName("action_Clear_selection")
|
||||||
self.action_Resume_previous = QtGui.QAction(parent=MainWindow)
|
self.action_Resume_previous = QtGui.QAction(parent=MainWindow)
|
||||||
icon10 = QtGui.QIcon()
|
icon9 = QtGui.QIcon()
|
||||||
icon10.addPixmap(QtGui.QPixmap(":/icons/previous"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
icon9.addPixmap(QtGui.QPixmap(":/icons/previous"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.action_Resume_previous.setIcon(icon10)
|
self.action_Resume_previous.setIcon(icon9)
|
||||||
self.action_Resume_previous.setObjectName("action_Resume_previous")
|
self.action_Resume_previous.setObjectName("action_Resume_previous")
|
||||||
self.actionE_xit = QtGui.QAction(parent=MainWindow)
|
self.actionE_xit = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionE_xit.setObjectName("actionE_xit")
|
self.actionE_xit.setObjectName("actionE_xit")
|
||||||
@ -515,16 +540,18 @@ class Ui_MainWindow(object):
|
|||||||
self.current_track_2.setText(_translate("MainWindow", "Current track:"))
|
self.current_track_2.setText(_translate("MainWindow", "Current track:"))
|
||||||
self.next_track_2.setText(_translate("MainWindow", "Next track:"))
|
self.next_track_2.setText(_translate("MainWindow", "Next track:"))
|
||||||
self.lblTOD.setText(_translate("MainWindow", "00:00:00"))
|
self.lblTOD.setText(_translate("MainWindow", "00:00:00"))
|
||||||
|
self.btnFade.setText(_translate("MainWindow", " Fade"))
|
||||||
self.btnPreview.setText(_translate("MainWindow", " Preview"))
|
self.btnPreview.setText(_translate("MainWindow", " Preview"))
|
||||||
self.label_elapsed_timer.setText(_translate("MainWindow", "00:00 / 00:00"))
|
self.label.setText(_translate("MainWindow", "Elapsed time"))
|
||||||
self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk"))
|
self.label_elapsed_timer.setText(_translate("MainWindow", "00:00"))
|
||||||
self.btnHidePlayed.setText(_translate("MainWindow", "Hide played"))
|
|
||||||
self.label_4.setText(_translate("MainWindow", "Fade"))
|
self.label_4.setText(_translate("MainWindow", "Fade"))
|
||||||
self.label_fade_timer.setText(_translate("MainWindow", "00:00"))
|
self.label_fade_timer.setText(_translate("MainWindow", "00:00"))
|
||||||
self.label_5.setText(_translate("MainWindow", "Silent"))
|
self.label_5.setText(_translate("MainWindow", "Silent"))
|
||||||
self.label_silent_timer.setText(_translate("MainWindow", "00:00"))
|
self.label_silent_timer.setText(_translate("MainWindow", "00:00"))
|
||||||
self.btnFade.setText(_translate("MainWindow", " Fade"))
|
self.label_6.setText(_translate("MainWindow", "End"))
|
||||||
self.btnStop.setText(_translate("MainWindow", " Stop"))
|
self.label_end_timer.setText(_translate("MainWindow", "00:00"))
|
||||||
|
self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk"))
|
||||||
|
self.btnHidePlayed.setText(_translate("MainWindow", "Hide played"))
|
||||||
self.menuFile.setTitle(_translate("MainWindow", "&Playlists"))
|
self.menuFile.setTitle(_translate("MainWindow", "&Playlists"))
|
||||||
self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
|
self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
|
||||||
self.menuSearc_h.setTitle(_translate("MainWindow", "&Search"))
|
self.menuSearc_h.setTitle(_translate("MainWindow", "&Search"))
|
||||||
|
|||||||
@ -4,7 +4,13 @@ import os
|
|||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from helpers import (
|
from helpers import (
|
||||||
|
fade_point,
|
||||||
|
get_audio_segment,
|
||||||
get_tags,
|
get_tags,
|
||||||
|
leading_silence,
|
||||||
|
normalise_track,
|
||||||
|
set_track_metadata,
|
||||||
|
trailing_silence,
|
||||||
)
|
)
|
||||||
from log import log
|
from log import log
|
||||||
from models import Tracks
|
from models import Tracks
|
||||||
@ -66,14 +72,12 @@ def check_db(session):
|
|||||||
print("Invalid paths in database")
|
print("Invalid paths in database")
|
||||||
print("-------------------------")
|
print("-------------------------")
|
||||||
for t in paths_not_found:
|
for t in paths_not_found:
|
||||||
print(
|
print(f"""
|
||||||
f"""
|
|
||||||
Track ID: {t.id}
|
Track ID: {t.id}
|
||||||
Path: {t.path}
|
Path: {t.path}
|
||||||
Title: {t.title}
|
Title: {t.title}
|
||||||
Artist: {t.artist}
|
Artist: {t.artist}
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
if more_files_to_report:
|
if more_files_to_report:
|
||||||
print("There were more paths than listed that were not found")
|
print("There were more paths than listed that were not found")
|
||||||
|
|
||||||
|
|||||||
11
conftest.py
11
conftest.py
@ -1,15 +1,15 @@
|
|||||||
# https://itnext.io/setting-up-transactional-tests-with-pytest-and-sqlalchemy-b2d726347629
|
# https://itnext.io/setting-up-transactional-tests-with-pytest-and-sqlalchemy-b2d726347629
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import sys
|
||||||
# Flake8 doesn't like the sys.append within imports
|
sys.path.append("app")
|
||||||
# import sys
|
import models
|
||||||
# sys.path.append("app")
|
|
||||||
|
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def connection():
|
def connection():
|
||||||
engine = create_engine(
|
engine = create_engine(
|
||||||
@ -21,8 +21,7 @@ def connection():
|
|||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def setup_database(connection):
|
def setup_database(connection):
|
||||||
from app.models import Base # noqa E402
|
from app.models import Base # noqa E402
|
||||||
|
|
||||||
Base.metadata.bind = connection
|
Base.metadata.bind = connection
|
||||||
Base.metadata.create_all()
|
Base.metadata.create_all()
|
||||||
# seed_database()
|
# seed_database()
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
Run Flake8 and Black
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
# Minimal makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line, and also
|
|
||||||
# from the environment for the first two.
|
|
||||||
SPHINXOPTS ?=
|
|
||||||
SPHINXBUILD ?= sphinx-build
|
|
||||||
SOURCEDIR = source
|
|
||||||
BUILDDIR = build
|
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
|
||||||
help:
|
|
||||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
||||||
|
|
||||||
.PHONY: help Makefile
|
|
||||||
|
|
||||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
||||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
||||||
%: Makefile
|
|
||||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
||||||
BIN
docs/build/doctrees/development.doctree
vendored
BIN
docs/build/doctrees/development.doctree
vendored
Binary file not shown.
BIN
docs/build/doctrees/environment.pickle
vendored
BIN
docs/build/doctrees/environment.pickle
vendored
Binary file not shown.
BIN
docs/build/doctrees/index.doctree
vendored
BIN
docs/build/doctrees/index.doctree
vendored
Binary file not shown.
BIN
docs/build/doctrees/installation.doctree
vendored
BIN
docs/build/doctrees/installation.doctree
vendored
Binary file not shown.
BIN
docs/build/doctrees/introduction.doctree
vendored
BIN
docs/build/doctrees/introduction.doctree
vendored
Binary file not shown.
BIN
docs/build/doctrees/reference.doctree
vendored
BIN
docs/build/doctrees/reference.doctree
vendored
Binary file not shown.
BIN
docs/build/doctrees/tutorial.doctree
vendored
BIN
docs/build/doctrees/tutorial.doctree
vendored
Binary file not shown.
4
docs/build/html/.buildinfo
vendored
4
docs/build/html/.buildinfo
vendored
@ -1,4 +0,0 @@
|
|||||||
# Sphinx build info version 1
|
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
|
||||||
config: dcac9a6ec03f5a33392ad4b5bcf42637
|
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
|
||||||
2
docs/build/html/_sources/development.rst.txt
vendored
2
docs/build/html/_sources/development.rst.txt
vendored
@ -1,2 +0,0 @@
|
|||||||
Development
|
|
||||||
===========
|
|
||||||
25
docs/build/html/_sources/index.rst.txt
vendored
25
docs/build/html/_sources/index.rst.txt
vendored
@ -1,25 +0,0 @@
|
|||||||
.. MusicMuster documentation master file, created by
|
|
||||||
sphinx-quickstart on Sun Jul 2 17:58:44 2023.
|
|
||||||
|
|
||||||
Welcome to MusicMuster's documentation!
|
|
||||||
=======================================
|
|
||||||
|
|
||||||
**MusicMuster** is a music player targeted at the production of live
|
|
||||||
internet radio shows.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
introduction
|
|
||||||
installation
|
|
||||||
tutorial
|
|
||||||
reference
|
|
||||||
development
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
Installation
|
|
||||||
============
|
|
||||||
87
docs/build/html/_sources/introduction.rst.txt
vendored
87
docs/build/html/_sources/introduction.rst.txt
vendored
@ -1,87 +0,0 @@
|
|||||||
Introduction
|
|
||||||
============
|
|
||||||
|
|
||||||
Why MusicMuster?
|
|
||||||
----------------
|
|
||||||
|
|
||||||
In January 2022 I started my show on `Mixcloud
|
|
||||||
<https://www.mixcloud.com/KeithsMusicBox/>`. Until then, my show had
|
|
||||||
been on an internet radio station which required me to use a Windows
|
|
||||||
playout system. As I only use Linux, I had to set up a Windows PC
|
|
||||||
specifically for that purpose. The system I had to use had what I felt
|
|
||||||
were shortcomings in various areas.
|
|
||||||
|
|
||||||
Once I moved to Mixcloud I searched for a Linux equivalent that didn't
|
|
||||||
have the same shortcomings but was unable to find one that met my
|
|
||||||
criteria. I decided to see how practical it would be to write my own,
|
|
||||||
and MusicMuster was born.
|
|
||||||
|
|
||||||
What is MusicMuster?
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
It is a Linux-based music player. Whilst it could be used as a general
|
|
||||||
home music player, there are much better applications for that role.
|
|
||||||
**MusicMuster** has been specifically designed to support the
|
|
||||||
production of live internet radio shows.
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
* Database backed
|
|
||||||
* Can be almost entirely keyboard driven
|
|
||||||
* Open multiple playlists in tabs
|
|
||||||
* Play tracks from any playlist
|
|
||||||
* Add notes/comments to tracks on playlist
|
|
||||||
* Automatatic colour-coding of notes/comments according to content
|
|
||||||
* Preview tracks before playing to audience
|
|
||||||
* Time of day clock
|
|
||||||
* Elapsed track time counter
|
|
||||||
* Time to run until track starts to fade
|
|
||||||
* Time to run until track is silent
|
|
||||||
* Graphic of volume from 5 seconds (configurable) before fade until
|
|
||||||
track is silent
|
|
||||||
* Optionally hide played tracks in playlist
|
|
||||||
* Button to drop playout volume by 3dB for talkover
|
|
||||||
* Playlist displays:
|
|
||||||
* Title
|
|
||||||
* Artist
|
|
||||||
* Length of track (mm:ss)
|
|
||||||
* Estimated start time of track
|
|
||||||
* Estimated end time of track
|
|
||||||
* When track was last played
|
|
||||||
* Bits per second (bitrate) of track
|
|
||||||
* Length of leading silence in recording before track starts
|
|
||||||
* Total track length of arbitrary sections of tracks
|
|
||||||
* Commands that are sent to OBS Studio (eg, for automated scene
|
|
||||||
changes)
|
|
||||||
* Playlist templates
|
|
||||||
* Move selected or unplayed tracks between playlists
|
|
||||||
* Download CSV of tracks played between arbitrary dates/times
|
|
||||||
* Search for tracks by title or artist
|
|
||||||
* Automatic search of current and next track in Wikipedia
|
|
||||||
* Optional search of any track in Wikipedia
|
|
||||||
* Optional search of any track in Songfacts
|
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. note:: MusicMuster has only been tested on Debian 12, "Bookworm";
|
|
||||||
however, it should run on most contemporary Linux systems.
|
|
||||||
|
|
||||||
The :doc:`installation` page explains how to build MusicMuster in its
|
|
||||||
own environment which will automatcally install all requirements
|
|
||||||
except the database. The current version of MusicMuster uses MariaDB
|
|
||||||
version 10.11; however, any recent version of MariaDB should suffice.
|
|
||||||
|
|
||||||
MusicMuster is a Python 3 application and requires Python 3.8 or
|
|
||||||
later.
|
|
||||||
|
|
||||||
|
|
||||||
Feedback, bugs, etc
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Please send to keith@midnighthax.com
|
|
||||||
|
|
||||||
Keith Edmunds,
|
|
||||||
July 2023
|
|
||||||
2
docs/build/html/_sources/reference.rst.txt
vendored
2
docs/build/html/_sources/reference.rst.txt
vendored
@ -1,2 +0,0 @@
|
|||||||
Reference
|
|
||||||
=========
|
|
||||||
2
docs/build/html/_sources/tutorial.rst.txt
vendored
2
docs/build/html/_sources/tutorial.rst.txt
vendored
@ -1,2 +0,0 @@
|
|||||||
Tutorial
|
|
||||||
========
|
|
||||||
703
docs/build/html/_static/alabaster.css
vendored
703
docs/build/html/_static/alabaster.css
vendored
@ -1,703 +0,0 @@
|
|||||||
@import url("basic.css");
|
|
||||||
|
|
||||||
/* -- page layout ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Georgia, serif;
|
|
||||||
font-size: 17px;
|
|
||||||
background-color: #fff;
|
|
||||||
color: #000;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
div.document {
|
|
||||||
width: 940px;
|
|
||||||
margin: 30px auto 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.documentwrapper {
|
|
||||||
float: left;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.bodywrapper {
|
|
||||||
margin: 0 0 0 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
width: 220px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 1px solid #B1B4B6;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body {
|
|
||||||
background-color: #fff;
|
|
||||||
color: #3E4349;
|
|
||||||
padding: 0 30px 0 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body > .section {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer {
|
|
||||||
width: 940px;
|
|
||||||
margin: 20px auto 30px auto;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #888;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer a {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.caption {
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
div.relations {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
div.sphinxsidebar a {
|
|
||||||
color: #444;
|
|
||||||
text-decoration: none;
|
|
||||||
border-bottom: 1px dotted #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar a:hover {
|
|
||||||
border-bottom: 1px solid #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper {
|
|
||||||
padding: 18px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper p.logo {
|
|
||||||
padding: 0;
|
|
||||||
margin: -10px 0 0 0px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper h1.logo {
|
|
||||||
margin-top: -10px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper h1.logo-name {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper p.blurb {
|
|
||||||
margin-top: 0;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3,
|
|
||||||
div.sphinxsidebar h4 {
|
|
||||||
font-family: Georgia, serif;
|
|
||||||
color: #444;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h4 {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3 a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar p.logo a,
|
|
||||||
div.sphinxsidebar h3 a,
|
|
||||||
div.sphinxsidebar p.logo a:hover,
|
|
||||||
div.sphinxsidebar h3 a:hover {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar p {
|
|
||||||
color: #555;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul li.toctree-l1 > a {
|
|
||||||
font-size: 120%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul li.toctree-l2 > a {
|
|
||||||
font-size: 110%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar input {
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
font-family: Georgia, serif;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar hr {
|
|
||||||
border: none;
|
|
||||||
height: 1px;
|
|
||||||
color: #AAA;
|
|
||||||
background: #AAA;
|
|
||||||
|
|
||||||
text-align: left;
|
|
||||||
margin-left: 0;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar .badge {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar .badge:hover {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To address an issue with donation coming after search */
|
|
||||||
div.sphinxsidebar h3.donation {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- body styles ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #004B6B;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #6D4100;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body h1,
|
|
||||||
div.body h2,
|
|
||||||
div.body h3,
|
|
||||||
div.body h4,
|
|
||||||
div.body h5,
|
|
||||||
div.body h6 {
|
|
||||||
font-family: Georgia, serif;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 30px 0px 10px 0px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
|
||||||
div.body h2 { font-size: 180%; }
|
|
||||||
div.body h3 { font-size: 150%; }
|
|
||||||
div.body h4 { font-size: 130%; }
|
|
||||||
div.body h5 { font-size: 100%; }
|
|
||||||
div.body h6 { font-size: 100%; }
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
color: #DDD;
|
|
||||||
padding: 0 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink:hover {
|
|
||||||
color: #444;
|
|
||||||
background: #EAEAEA;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body p, div.body dd, div.body li {
|
|
||||||
line-height: 1.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition {
|
|
||||||
margin: 20px 0px;
|
|
||||||
padding: 10px 30px;
|
|
||||||
background-color: #EEE;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
|
|
||||||
background-color: #FBFBFB;
|
|
||||||
border-bottom: 1px solid #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p.admonition-title {
|
|
||||||
font-family: Georgia, serif;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 24px;
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.highlight {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:target, .highlight {
|
|
||||||
background: #FAF3E8;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.warning {
|
|
||||||
background-color: #FCC;
|
|
||||||
border: 1px solid #FAA;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.danger {
|
|
||||||
background-color: #FCC;
|
|
||||||
border: 1px solid #FAA;
|
|
||||||
-moz-box-shadow: 2px 2px 4px #D52C2C;
|
|
||||||
-webkit-box-shadow: 2px 2px 4px #D52C2C;
|
|
||||||
box-shadow: 2px 2px 4px #D52C2C;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.error {
|
|
||||||
background-color: #FCC;
|
|
||||||
border: 1px solid #FAA;
|
|
||||||
-moz-box-shadow: 2px 2px 4px #D52C2C;
|
|
||||||
-webkit-box-shadow: 2px 2px 4px #D52C2C;
|
|
||||||
box-shadow: 2px 2px 4px #D52C2C;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.caution {
|
|
||||||
background-color: #FCC;
|
|
||||||
border: 1px solid #FAA;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.attention {
|
|
||||||
background-color: #FCC;
|
|
||||||
border: 1px solid #FAA;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.important {
|
|
||||||
background-color: #EEE;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.note {
|
|
||||||
background-color: #EEE;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.tip {
|
|
||||||
background-color: #EEE;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.hint {
|
|
||||||
background-color: #EEE;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.seealso {
|
|
||||||
background-color: #EEE;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.topic {
|
|
||||||
background-color: #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title:after {
|
|
||||||
content: ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, tt, code {
|
|
||||||
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hll {
|
|
||||||
background-color: #FFC;
|
|
||||||
margin: 0 -12px;
|
|
||||||
padding: 0 12px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.screenshot {
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.descname, tt.descclassname, code.descname, code.descclassname {
|
|
||||||
font-size: 0.95em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.descname, code.descname {
|
|
||||||
padding-right: 0.08em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.screenshot {
|
|
||||||
-moz-box-shadow: 2px 2px 4px #EEE;
|
|
||||||
-webkit-box-shadow: 2px 2px 4px #EEE;
|
|
||||||
box-shadow: 2px 2px 4px #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.docutils {
|
|
||||||
border: 1px solid #888;
|
|
||||||
-moz-box-shadow: 2px 2px 4px #EEE;
|
|
||||||
-webkit-box-shadow: 2px 2px 4px #EEE;
|
|
||||||
box-shadow: 2px 2px 4px #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th {
|
|
||||||
border: 1px solid #888;
|
|
||||||
padding: 0.25em 0.7em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list, table.footnote {
|
|
||||||
border: none;
|
|
||||||
-moz-box-shadow: none;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote {
|
|
||||||
margin: 15px 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #EEE;
|
|
||||||
background: #FDFDFD;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote + table.footnote {
|
|
||||||
margin-top: -15px;
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list th {
|
|
||||||
padding: 0 0.8em 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list td {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list p {
|
|
||||||
margin-bottom: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cloned from
|
|
||||||
* https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
|
|
||||||
*/
|
|
||||||
.field-name {
|
|
||||||
-moz-hyphens: manual;
|
|
||||||
-ms-hyphens: manual;
|
|
||||||
-webkit-hyphens: manual;
|
|
||||||
hyphens: manual;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote td.label {
|
|
||||||
width: .1px;
|
|
||||||
padding: 0.3em 0 0.3em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote td {
|
|
||||||
padding: 0.3em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl dd {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 0 0 0 30px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul, ol {
|
|
||||||
/* Matches the 30px from the narrow-screen "li > ul" selector below */
|
|
||||||
margin: 10px 0 10px 30px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
background: #EEE;
|
|
||||||
padding: 7px 30px;
|
|
||||||
margin: 15px 0px;
|
|
||||||
line-height: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.viewcode-block:target {
|
|
||||||
background: #ffd;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl pre, blockquote pre, li pre {
|
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt, code {
|
|
||||||
background-color: #ecf0f3;
|
|
||||||
color: #222;
|
|
||||||
/* padding: 1px 2px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.xref, code.xref, a tt {
|
|
||||||
background-color: #FBFBFB;
|
|
||||||
border-bottom: 1px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.reference {
|
|
||||||
text-decoration: none;
|
|
||||||
border-bottom: 1px dotted #004B6B;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't put an underline on images */
|
|
||||||
a.image-reference, a.image-reference:hover {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.reference:hover {
|
|
||||||
border-bottom: 1px solid #6D4100;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.footnote-reference {
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.7em;
|
|
||||||
vertical-align: top;
|
|
||||||
border-bottom: 1px dotted #004B6B;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.footnote-reference:hover {
|
|
||||||
border-bottom: 1px solid #6D4100;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover tt, a:hover code {
|
|
||||||
background: #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 870px) {
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.documentwrapper {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.bodywrapper {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li > ul {
|
|
||||||
/* Matches the 30px from the "ul, ol" selector above */
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bodywrapper {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 875px) {
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.documentwrapper {
|
|
||||||
float: none;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
display: block;
|
|
||||||
float: none;
|
|
||||||
width: 102.5%;
|
|
||||||
margin: 50px -30px -20px -30px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: #333;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
|
||||||
div.sphinxsidebar h3 a {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar a {
|
|
||||||
color: #AAA;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar p.logo {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.bodywrapper {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body {
|
|
||||||
min-height: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rtd_doc_footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* misc. */
|
|
||||||
|
|
||||||
.revsys-inline {
|
|
||||||
display: none!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make nested-list/multi-paragraph items look better in Releases changelog
|
|
||||||
* pages. Without this, docutils' magical list fuckery causes inconsistent
|
|
||||||
* formatting between different release sub-lists.
|
|
||||||
*/
|
|
||||||
div#changelog > div.section > ul > li > p:only-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide fugly table cell borders in ..bibliography:: directive output */
|
|
||||||
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
|
|
||||||
border: none;
|
|
||||||
/* Below needed in some edge cases; if not applied, bottom shadows appear */
|
|
||||||
-moz-box-shadow: none;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* relbar */
|
|
||||||
|
|
||||||
.related {
|
|
||||||
line-height: 30px;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related.top {
|
|
||||||
border-bottom: 1px solid #EEE;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related.bottom {
|
|
||||||
border-top: 1px solid #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related ul {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related li {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav#rellinks {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav#rellinks li+li:before {
|
|
||||||
content: "|";
|
|
||||||
}
|
|
||||||
|
|
||||||
nav#breadcrumbs li+li:before {
|
|
||||||
content: "\00BB";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide certain items when printing */
|
|
||||||
@media print {
|
|
||||||
div.related {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
903
docs/build/html/_static/basic.css
vendored
903
docs/build/html/_static/basic.css
vendored
@ -1,903 +0,0 @@
|
|||||||
/*
|
|
||||||
* basic.css
|
|
||||||
* ~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Sphinx stylesheet -- basic theme.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* -- main layout ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.clearer {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.section::after {
|
|
||||||
display: block;
|
|
||||||
content: '';
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- relbar ---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.related {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related h3 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 0 0 10px;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related li {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related li.right {
|
|
||||||
float: right;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- sidebar --------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper {
|
|
||||||
padding: 10px 5px 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
float: left;
|
|
||||||
width: 230px;
|
|
||||||
margin-left: -100%;
|
|
||||||
font-size: 90%;
|
|
||||||
word-wrap: break-word;
|
|
||||||
overflow-wrap : break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul ul,
|
|
||||||
div.sphinxsidebar ul.want-points {
|
|
||||||
margin-left: 20px;
|
|
||||||
list-style: square;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar form {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar input {
|
|
||||||
border: 1px solid #98dbcc;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar #searchbox form.search {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar #searchbox input[type="text"] {
|
|
||||||
float: left;
|
|
||||||
width: 80%;
|
|
||||||
padding: 0.25em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar #searchbox input[type="submit"] {
|
|
||||||
float: left;
|
|
||||||
width: 20%;
|
|
||||||
border-left: none;
|
|
||||||
padding: 0.25em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- search page ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
ul.search {
|
|
||||||
margin: 10px 0 0 20px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.search li {
|
|
||||||
padding: 5px 0 5px 20px;
|
|
||||||
background-image: url(file.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 0 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.search li a {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.search li p.context {
|
|
||||||
color: #888;
|
|
||||||
margin: 2px 0 0 30px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.keywordmatches li.goodmatch a {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- index page ------------------------------------------------------------ */
|
|
||||||
|
|
||||||
table.contentstable {
|
|
||||||
width: 90%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.contentstable p.biglink {
|
|
||||||
line-height: 150%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.biglink {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.linkdescr {
|
|
||||||
font-style: italic;
|
|
||||||
padding-top: 5px;
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- general index --------------------------------------------------------- */
|
|
||||||
|
|
||||||
table.indextable {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable td {
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable > tbody > tr > td > ul {
|
|
||||||
padding-left: 0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable tr.pcap {
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable tr.cap {
|
|
||||||
margin-top: 10px;
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.toggler {
|
|
||||||
margin-right: 3px;
|
|
||||||
margin-top: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.modindex-jumpbox {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
margin: 1em 0 1em 0;
|
|
||||||
padding: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.genindex-jumpbox {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
margin: 1em 0 1em 0;
|
|
||||||
padding: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- domain module index --------------------------------------------------- */
|
|
||||||
|
|
||||||
table.modindextable td {
|
|
||||||
padding: 2px;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- general body styles --------------------------------------------------- */
|
|
||||||
|
|
||||||
div.body {
|
|
||||||
min-width: 360px;
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body p, div.body dd, div.body li, div.body blockquote {
|
|
||||||
-moz-hyphens: auto;
|
|
||||||
-ms-hyphens: auto;
|
|
||||||
-webkit-hyphens: auto;
|
|
||||||
hyphens: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:hover > a.headerlink,
|
|
||||||
h2:hover > a.headerlink,
|
|
||||||
h3:hover > a.headerlink,
|
|
||||||
h4:hover > a.headerlink,
|
|
||||||
h5:hover > a.headerlink,
|
|
||||||
h6:hover > a.headerlink,
|
|
||||||
dt:hover > a.headerlink,
|
|
||||||
caption:hover > a.headerlink,
|
|
||||||
p.caption:hover > a.headerlink,
|
|
||||||
div.code-block-caption:hover > a.headerlink {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body p.caption {
|
|
||||||
text-align: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body td {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.rubric {
|
|
||||||
margin-top: 30px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.align-left, figure.align-left, .figure.align-left, object.align-left {
|
|
||||||
clear: left;
|
|
||||||
float: left;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.align-right, figure.align-right, .figure.align-right, object.align-right {
|
|
||||||
clear: right;
|
|
||||||
float: right;
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.align-center, figure.align-center, .figure.align-center, object.align-center {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.align-default, figure.align-default, .figure.align-default {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-default {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- sidebars -------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.sidebar,
|
|
||||||
aside.sidebar {
|
|
||||||
margin: 0 0 0.5em 1em;
|
|
||||||
border: 1px solid #ddb;
|
|
||||||
padding: 7px;
|
|
||||||
background-color: #ffe;
|
|
||||||
width: 40%;
|
|
||||||
float: right;
|
|
||||||
clear: right;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sidebar-title {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.contents,
|
|
||||||
aside.topic,
|
|
||||||
div.admonition, div.topic, blockquote {
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- topics ---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
nav.contents,
|
|
||||||
aside.topic,
|
|
||||||
div.topic {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
padding: 7px;
|
|
||||||
margin: 10px 0 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.topic-title {
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- admonitions ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.admonition {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition dt {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title {
|
|
||||||
margin: 0px 10px 5px 0px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body p.centered {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- content of sidebars/topics/admonitions -------------------------------- */
|
|
||||||
|
|
||||||
div.sidebar > :last-child,
|
|
||||||
aside.sidebar > :last-child,
|
|
||||||
nav.contents > :last-child,
|
|
||||||
aside.topic > :last-child,
|
|
||||||
div.topic > :last-child,
|
|
||||||
div.admonition > :last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sidebar::after,
|
|
||||||
aside.sidebar::after,
|
|
||||||
nav.contents::after,
|
|
||||||
aside.topic::after,
|
|
||||||
div.topic::after,
|
|
||||||
div.admonition::after,
|
|
||||||
blockquote::after {
|
|
||||||
display: block;
|
|
||||||
content: '';
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- tables ---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
table.docutils {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
border: 0;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.align-center {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.align-default {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table caption span.caption-number {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
table caption span.caption-text {
|
|
||||||
}
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th {
|
|
||||||
padding: 1px 8px 1px 5px;
|
|
||||||
border-top: 0;
|
|
||||||
border-left: 0;
|
|
||||||
border-right: 0;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: left;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.citation {
|
|
||||||
border-left: solid 1px gray;
|
|
||||||
margin-left: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.citation td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
th > :first-child,
|
|
||||||
td > :first-child {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
th > :last-child,
|
|
||||||
td > :last-child {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- figures --------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.figure, figure {
|
|
||||||
margin: 0.5em;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.figure p.caption, figcaption {
|
|
||||||
padding: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.figure p.caption span.caption-number,
|
|
||||||
figcaption span.caption-number {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.figure p.caption span.caption-text,
|
|
||||||
figcaption span.caption-text {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- field list styles ----------------------------------------------------- */
|
|
||||||
|
|
||||||
table.field-list td, table.field-list th {
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-list ul {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-list p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-name {
|
|
||||||
-moz-hyphens: manual;
|
|
||||||
-ms-hyphens: manual;
|
|
||||||
-webkit-hyphens: manual;
|
|
||||||
hyphens: manual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- hlist styles ---------------------------------------------------------- */
|
|
||||||
|
|
||||||
table.hlist {
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.hlist td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- object description styles --------------------------------------------- */
|
|
||||||
|
|
||||||
.sig {
|
|
||||||
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig-name, code.descname {
|
|
||||||
background-color: transparent;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig-name {
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
code.descname {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig-prename, code.descclassname {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.optional {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig-paren {
|
|
||||||
font-size: larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig-param.n {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* C++ specific styling */
|
|
||||||
|
|
||||||
.sig-inline.c-texpr,
|
|
||||||
.sig-inline.cpp-texpr {
|
|
||||||
font-family: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig.c .k, .sig.c .kt,
|
|
||||||
.sig.cpp .k, .sig.cpp .kt {
|
|
||||||
color: #0033B3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig.c .m,
|
|
||||||
.sig.cpp .m {
|
|
||||||
color: #1750EB;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sig.c .s, .sig.c .sc,
|
|
||||||
.sig.cpp .s, .sig.cpp .sc {
|
|
||||||
color: #067D17;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -- other body styles ----------------------------------------------------- */
|
|
||||||
|
|
||||||
ol.arabic {
|
|
||||||
list-style: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.loweralpha {
|
|
||||||
list-style: lower-alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.upperalpha {
|
|
||||||
list-style: upper-alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.lowerroman {
|
|
||||||
list-style: lower-roman;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.upperroman {
|
|
||||||
list-style: upper-roman;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(li) > ol > li:first-child > :first-child,
|
|
||||||
:not(li) > ul > li:first-child > :first-child {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(li) > ol > li:last-child > :last-child,
|
|
||||||
:not(li) > ul > li:last-child > :last-child {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.simple ol p,
|
|
||||||
ol.simple ul p,
|
|
||||||
ul.simple ol p,
|
|
||||||
ul.simple ul p {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.simple > li:not(:first-child) > p,
|
|
||||||
ul.simple > li:not(:first-child) > p {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.simple p,
|
|
||||||
ul.simple p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
aside.footnote > span,
|
|
||||||
div.citation > span {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
aside.footnote > span:last-of-type,
|
|
||||||
div.citation > span:last-of-type {
|
|
||||||
padding-right: 0.5em;
|
|
||||||
}
|
|
||||||
aside.footnote > p {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
div.citation > p {
|
|
||||||
margin-left: 4em;
|
|
||||||
}
|
|
||||||
aside.footnote > p:last-of-type,
|
|
||||||
div.citation > p:last-of-type {
|
|
||||||
margin-bottom: 0em;
|
|
||||||
}
|
|
||||||
aside.footnote > p:last-of-type:after,
|
|
||||||
div.citation > p:last-of-type:after {
|
|
||||||
content: "";
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.field-list {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: fit-content(30%) auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.field-list > dt {
|
|
||||||
font-weight: bold;
|
|
||||||
word-break: break-word;
|
|
||||||
padding-left: 0.5em;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.field-list > dd {
|
|
||||||
padding-left: 0.5em;
|
|
||||||
margin-top: 0em;
|
|
||||||
margin-left: 0em;
|
|
||||||
margin-bottom: 0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd > :first-child {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd ul, dd table {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
margin-top: 3px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl > dd:last-child,
|
|
||||||
dl > dd:last-child > :last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:target, span.highlighted {
|
|
||||||
background-color: #fbe54e;
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.highlighted {
|
|
||||||
fill: #fbe54e;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.glossary dt {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.versionmodified {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.system-message {
|
|
||||||
background-color: #fda;
|
|
||||||
padding: 5px;
|
|
||||||
border: 3px solid red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footnote:target {
|
|
||||||
background-color: #ffa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-block {
|
|
||||||
display: block;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-block .line-block {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.guilabel, .menuselection {
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accelerator {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.classifier {
|
|
||||||
font-style: oblique;
|
|
||||||
}
|
|
||||||
|
|
||||||
.classifier:before {
|
|
||||||
font-style: normal;
|
|
||||||
margin: 0 0.5em;
|
|
||||||
content: ":";
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
abbr, acronym {
|
|
||||||
border-bottom: dotted 1px;
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- code displays --------------------------------------------------------- */
|
|
||||||
|
|
||||||
pre {
|
|
||||||
overflow: auto;
|
|
||||||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, div[class*="highlight-"] {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.pre {
|
|
||||||
-moz-hyphens: none;
|
|
||||||
-ms-hyphens: none;
|
|
||||||
-webkit-hyphens: none;
|
|
||||||
hyphens: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
div[class*="highlight-"] {
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.linenos pre {
|
|
||||||
border: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable tbody {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable tr {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable td.linenos {
|
|
||||||
padding-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable td.code {
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight .hll {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.highlight pre,
|
|
||||||
table.highlighttable pre {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.code-block-caption + div {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.code-block-caption {
|
|
||||||
margin-top: 1em;
|
|
||||||
padding: 2px 5px;
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.code-block-caption code {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable td.linenos,
|
|
||||||
span.linenos,
|
|
||||||
div.highlight span.gp { /* gp: Generic.Prompt */
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: text; /* Safari fallback only */
|
|
||||||
-webkit-user-select: none; /* Chrome/Safari */
|
|
||||||
-moz-user-select: none; /* Firefox */
|
|
||||||
-ms-user-select: none; /* IE10+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
div.code-block-caption span.caption-number {
|
|
||||||
padding: 0.1em 0.3em;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.code-block-caption span.caption-text {
|
|
||||||
}
|
|
||||||
|
|
||||||
div.literal-block-wrapper {
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
code.xref, a code {
|
|
||||||
background-color: transparent;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewcode-link {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewcode-back {
|
|
||||||
float: right;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.viewcode-block:target {
|
|
||||||
margin: -1px -10px;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- math display ---------------------------------------------------------- */
|
|
||||||
|
|
||||||
img.math {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body div.math p {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.eqno {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.eqno a.headerlink {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.math:hover a.headerlink {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- printout stylesheet --------------------------------------------------- */
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
div.document,
|
|
||||||
div.documentwrapper,
|
|
||||||
div.bodywrapper {
|
|
||||||
margin: 0 !important;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar,
|
|
||||||
div.related,
|
|
||||||
div.footer,
|
|
||||||
#top-link {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
docs/build/html/_static/custom.css
vendored
1
docs/build/html/_static/custom.css
vendored
@ -1 +0,0 @@
|
|||||||
/* This file intentionally left blank. */
|
|
||||||
69
docs/build/html/_static/debug.css
vendored
69
docs/build/html/_static/debug.css
vendored
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
This CSS file should be overridden by the theme authors. It's
|
|
||||||
meant for debugging and developing the skeleton that this theme provides.
|
|
||||||
*/
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
|
|
||||||
"Apple Color Emoji", "Segoe UI Emoji";
|
|
||||||
background: lavender;
|
|
||||||
}
|
|
||||||
.sb-announcement {
|
|
||||||
background: rgb(131, 131, 131);
|
|
||||||
}
|
|
||||||
.sb-announcement__inner {
|
|
||||||
background: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.sb-header {
|
|
||||||
background: lightskyblue;
|
|
||||||
}
|
|
||||||
.sb-header__inner {
|
|
||||||
background: royalblue;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.sb-header-secondary {
|
|
||||||
background: lightcyan;
|
|
||||||
}
|
|
||||||
.sb-header-secondary__inner {
|
|
||||||
background: cornflowerblue;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.sb-sidebar-primary {
|
|
||||||
background: lightgreen;
|
|
||||||
}
|
|
||||||
.sb-main {
|
|
||||||
background: blanchedalmond;
|
|
||||||
}
|
|
||||||
.sb-main__inner {
|
|
||||||
background: antiquewhite;
|
|
||||||
}
|
|
||||||
.sb-header-article {
|
|
||||||
background: lightsteelblue;
|
|
||||||
}
|
|
||||||
.sb-article-container {
|
|
||||||
background: snow;
|
|
||||||
}
|
|
||||||
.sb-article-main {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
.sb-footer-article {
|
|
||||||
background: lightpink;
|
|
||||||
}
|
|
||||||
.sb-sidebar-secondary {
|
|
||||||
background: lightgoldenrodyellow;
|
|
||||||
}
|
|
||||||
.sb-footer-content {
|
|
||||||
background: plum;
|
|
||||||
}
|
|
||||||
.sb-footer-content__inner {
|
|
||||||
background: palevioletred;
|
|
||||||
}
|
|
||||||
.sb-footer {
|
|
||||||
background: pink;
|
|
||||||
}
|
|
||||||
.sb-footer__inner {
|
|
||||||
background: salmon;
|
|
||||||
}
|
|
||||||
.sb-article {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
156
docs/build/html/_static/doctools.js
vendored
156
docs/build/html/_static/doctools.js
vendored
@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* doctools.js
|
|
||||||
* ~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Base JavaScript utilities for all Sphinx HTML documentation.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
|
|
||||||
"TEXTAREA",
|
|
||||||
"INPUT",
|
|
||||||
"SELECT",
|
|
||||||
"BUTTON",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const _ready = (callback) => {
|
|
||||||
if (document.readyState !== "loading") {
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
document.addEventListener("DOMContentLoaded", callback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Small JavaScript module for the documentation.
|
|
||||||
*/
|
|
||||||
const Documentation = {
|
|
||||||
init: () => {
|
|
||||||
Documentation.initDomainIndexTable();
|
|
||||||
Documentation.initOnKeyListeners();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* i18n support
|
|
||||||
*/
|
|
||||||
TRANSLATIONS: {},
|
|
||||||
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
|
|
||||||
LOCALE: "unknown",
|
|
||||||
|
|
||||||
// gettext and ngettext don't access this so that the functions
|
|
||||||
// can safely bound to a different name (_ = Documentation.gettext)
|
|
||||||
gettext: (string) => {
|
|
||||||
const translated = Documentation.TRANSLATIONS[string];
|
|
||||||
switch (typeof translated) {
|
|
||||||
case "undefined":
|
|
||||||
return string; // no translation
|
|
||||||
case "string":
|
|
||||||
return translated; // translation exists
|
|
||||||
default:
|
|
||||||
return translated[0]; // (singular, plural) translation tuple exists
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ngettext: (singular, plural, n) => {
|
|
||||||
const translated = Documentation.TRANSLATIONS[singular];
|
|
||||||
if (typeof translated !== "undefined")
|
|
||||||
return translated[Documentation.PLURAL_EXPR(n)];
|
|
||||||
return n === 1 ? singular : plural;
|
|
||||||
},
|
|
||||||
|
|
||||||
addTranslations: (catalog) => {
|
|
||||||
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
|
|
||||||
Documentation.PLURAL_EXPR = new Function(
|
|
||||||
"n",
|
|
||||||
`return (${catalog.plural_expr})`
|
|
||||||
);
|
|
||||||
Documentation.LOCALE = catalog.locale;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function to focus on search bar
|
|
||||||
*/
|
|
||||||
focusSearchBar: () => {
|
|
||||||
document.querySelectorAll("input[name=q]")[0]?.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise the domain index toggle buttons
|
|
||||||
*/
|
|
||||||
initDomainIndexTable: () => {
|
|
||||||
const toggler = (el) => {
|
|
||||||
const idNumber = el.id.substr(7);
|
|
||||||
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
|
|
||||||
if (el.src.substr(-9) === "minus.png") {
|
|
||||||
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
|
|
||||||
toggledRows.forEach((el) => (el.style.display = "none"));
|
|
||||||
} else {
|
|
||||||
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
|
|
||||||
toggledRows.forEach((el) => (el.style.display = ""));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const togglerElements = document.querySelectorAll("img.toggler");
|
|
||||||
togglerElements.forEach((el) =>
|
|
||||||
el.addEventListener("click", (event) => toggler(event.currentTarget))
|
|
||||||
);
|
|
||||||
togglerElements.forEach((el) => (el.style.display = ""));
|
|
||||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
|
|
||||||
},
|
|
||||||
|
|
||||||
initOnKeyListeners: () => {
|
|
||||||
// only install a listener if it is really needed
|
|
||||||
if (
|
|
||||||
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
|
|
||||||
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
document.addEventListener("keydown", (event) => {
|
|
||||||
// bail for input elements
|
|
||||||
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
|
|
||||||
// bail with special keys
|
|
||||||
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
||||||
|
|
||||||
if (!event.shiftKey) {
|
|
||||||
switch (event.key) {
|
|
||||||
case "ArrowLeft":
|
|
||||||
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
|
|
||||||
|
|
||||||
const prevLink = document.querySelector('link[rel="prev"]');
|
|
||||||
if (prevLink && prevLink.href) {
|
|
||||||
window.location.href = prevLink.href;
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "ArrowRight":
|
|
||||||
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
|
|
||||||
|
|
||||||
const nextLink = document.querySelector('link[rel="next"]');
|
|
||||||
if (nextLink && nextLink.href) {
|
|
||||||
window.location.href = nextLink.href;
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// some keyboard layouts may need Shift to get /
|
|
||||||
switch (event.key) {
|
|
||||||
case "/":
|
|
||||||
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
|
|
||||||
Documentation.focusSearchBar();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// quick alias for translations
|
|
||||||
const _ = Documentation.gettext;
|
|
||||||
|
|
||||||
_ready(Documentation.init);
|
|
||||||
14
docs/build/html/_static/documentation_options.js
vendored
14
docs/build/html/_static/documentation_options.js
vendored
@ -1,14 +0,0 @@
|
|||||||
var DOCUMENTATION_OPTIONS = {
|
|
||||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
|
||||||
VERSION: '2.11.2',
|
|
||||||
LANGUAGE: 'en',
|
|
||||||
COLLAPSE_INDEX: false,
|
|
||||||
BUILDER: 'html',
|
|
||||||
FILE_SUFFIX: '.html',
|
|
||||||
LINK_SUFFIX: '.html',
|
|
||||||
HAS_SOURCE: true,
|
|
||||||
SOURCELINK_SUFFIX: '.txt',
|
|
||||||
NAVIGATION_WITH_KEYS: false,
|
|
||||||
SHOW_SEARCH_SUMMARY: true,
|
|
||||||
ENABLE_SEARCH_SHORTCUTS: true,
|
|
||||||
};
|
|
||||||
BIN
docs/build/html/_static/file.png
vendored
BIN
docs/build/html/_static/file.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 286 B |
199
docs/build/html/_static/language_data.js
vendored
199
docs/build/html/_static/language_data.js
vendored
@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* language_data.js
|
|
||||||
* ~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* This script contains the language-specific data used by searchtools.js,
|
|
||||||
* namely the list of stopwords, stemmer, scorer and splitter.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
|
|
||||||
|
|
||||||
|
|
||||||
/* Non-minified version is copied as a separate JS file, is available */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Porter Stemmer
|
|
||||||
*/
|
|
||||||
var Stemmer = function() {
|
|
||||||
|
|
||||||
var step2list = {
|
|
||||||
ational: 'ate',
|
|
||||||
tional: 'tion',
|
|
||||||
enci: 'ence',
|
|
||||||
anci: 'ance',
|
|
||||||
izer: 'ize',
|
|
||||||
bli: 'ble',
|
|
||||||
alli: 'al',
|
|
||||||
entli: 'ent',
|
|
||||||
eli: 'e',
|
|
||||||
ousli: 'ous',
|
|
||||||
ization: 'ize',
|
|
||||||
ation: 'ate',
|
|
||||||
ator: 'ate',
|
|
||||||
alism: 'al',
|
|
||||||
iveness: 'ive',
|
|
||||||
fulness: 'ful',
|
|
||||||
ousness: 'ous',
|
|
||||||
aliti: 'al',
|
|
||||||
iviti: 'ive',
|
|
||||||
biliti: 'ble',
|
|
||||||
logi: 'log'
|
|
||||||
};
|
|
||||||
|
|
||||||
var step3list = {
|
|
||||||
icate: 'ic',
|
|
||||||
ative: '',
|
|
||||||
alize: 'al',
|
|
||||||
iciti: 'ic',
|
|
||||||
ical: 'ic',
|
|
||||||
ful: '',
|
|
||||||
ness: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
var c = "[^aeiou]"; // consonant
|
|
||||||
var v = "[aeiouy]"; // vowel
|
|
||||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
|
||||||
var V = v + "[aeiou]*"; // vowel sequence
|
|
||||||
|
|
||||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
|
||||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
|
||||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
|
||||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
|
||||||
|
|
||||||
this.stemWord = function (w) {
|
|
||||||
var stem;
|
|
||||||
var suffix;
|
|
||||||
var firstch;
|
|
||||||
var origword = w;
|
|
||||||
|
|
||||||
if (w.length < 3)
|
|
||||||
return w;
|
|
||||||
|
|
||||||
var re;
|
|
||||||
var re2;
|
|
||||||
var re3;
|
|
||||||
var re4;
|
|
||||||
|
|
||||||
firstch = w.substr(0,1);
|
|
||||||
if (firstch == "y")
|
|
||||||
w = firstch.toUpperCase() + w.substr(1);
|
|
||||||
|
|
||||||
// Step 1a
|
|
||||||
re = /^(.+?)(ss|i)es$/;
|
|
||||||
re2 = /^(.+?)([^s])s$/;
|
|
||||||
|
|
||||||
if (re.test(w))
|
|
||||||
w = w.replace(re,"$1$2");
|
|
||||||
else if (re2.test(w))
|
|
||||||
w = w.replace(re2,"$1$2");
|
|
||||||
|
|
||||||
// Step 1b
|
|
||||||
re = /^(.+?)eed$/;
|
|
||||||
re2 = /^(.+?)(ed|ing)$/;
|
|
||||||
if (re.test(w)) {
|
|
||||||
var fp = re.exec(w);
|
|
||||||
re = new RegExp(mgr0);
|
|
||||||
if (re.test(fp[1])) {
|
|
||||||
re = /.$/;
|
|
||||||
w = w.replace(re,"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (re2.test(w)) {
|
|
||||||
var fp = re2.exec(w);
|
|
||||||
stem = fp[1];
|
|
||||||
re2 = new RegExp(s_v);
|
|
||||||
if (re2.test(stem)) {
|
|
||||||
w = stem;
|
|
||||||
re2 = /(at|bl|iz)$/;
|
|
||||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
|
||||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
|
||||||
if (re2.test(w))
|
|
||||||
w = w + "e";
|
|
||||||
else if (re3.test(w)) {
|
|
||||||
re = /.$/;
|
|
||||||
w = w.replace(re,"");
|
|
||||||
}
|
|
||||||
else if (re4.test(w))
|
|
||||||
w = w + "e";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1c
|
|
||||||
re = /^(.+?)y$/;
|
|
||||||
if (re.test(w)) {
|
|
||||||
var fp = re.exec(w);
|
|
||||||
stem = fp[1];
|
|
||||||
re = new RegExp(s_v);
|
|
||||||
if (re.test(stem))
|
|
||||||
w = stem + "i";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2
|
|
||||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
|
||||||
if (re.test(w)) {
|
|
||||||
var fp = re.exec(w);
|
|
||||||
stem = fp[1];
|
|
||||||
suffix = fp[2];
|
|
||||||
re = new RegExp(mgr0);
|
|
||||||
if (re.test(stem))
|
|
||||||
w = stem + step2list[suffix];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3
|
|
||||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
|
||||||
if (re.test(w)) {
|
|
||||||
var fp = re.exec(w);
|
|
||||||
stem = fp[1];
|
|
||||||
suffix = fp[2];
|
|
||||||
re = new RegExp(mgr0);
|
|
||||||
if (re.test(stem))
|
|
||||||
w = stem + step3list[suffix];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4
|
|
||||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
|
||||||
re2 = /^(.+?)(s|t)(ion)$/;
|
|
||||||
if (re.test(w)) {
|
|
||||||
var fp = re.exec(w);
|
|
||||||
stem = fp[1];
|
|
||||||
re = new RegExp(mgr1);
|
|
||||||
if (re.test(stem))
|
|
||||||
w = stem;
|
|
||||||
}
|
|
||||||
else if (re2.test(w)) {
|
|
||||||
var fp = re2.exec(w);
|
|
||||||
stem = fp[1] + fp[2];
|
|
||||||
re2 = new RegExp(mgr1);
|
|
||||||
if (re2.test(stem))
|
|
||||||
w = stem;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 5
|
|
||||||
re = /^(.+?)e$/;
|
|
||||||
if (re.test(w)) {
|
|
||||||
var fp = re.exec(w);
|
|
||||||
stem = fp[1];
|
|
||||||
re = new RegExp(mgr1);
|
|
||||||
re2 = new RegExp(meq1);
|
|
||||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
|
||||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
|
||||||
w = stem;
|
|
||||||
}
|
|
||||||
re = /ll$/;
|
|
||||||
re2 = new RegExp(mgr1);
|
|
||||||
if (re.test(w) && re2.test(w)) {
|
|
||||||
re = /.$/;
|
|
||||||
w = w.replace(re,"");
|
|
||||||
}
|
|
||||||
|
|
||||||
// and turn initial Y back to y
|
|
||||||
if (firstch == "y")
|
|
||||||
w = firstch.toLowerCase() + w.substr(1);
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BIN
docs/build/html/_static/minus.png
vendored
BIN
docs/build/html/_static/minus.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 90 B |
BIN
docs/build/html/_static/plus.png
vendored
BIN
docs/build/html/_static/plus.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 90 B |
255
docs/build/html/_static/pygments.css
vendored
255
docs/build/html/_static/pygments.css
vendored
@ -1,255 +0,0 @@
|
|||||||
.highlight pre { line-height: 125%; }
|
|
||||||
.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
|
||||||
.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
|
||||||
.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
|
||||||
.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
|
||||||
.highlight .hll { background-color: #ffffcc }
|
|
||||||
.highlight { background: #f8f8f8; }
|
|
||||||
.highlight .c { color: #8f5902; font-style: italic } /* Comment */
|
|
||||||
.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
|
|
||||||
.highlight .g { color: #000000 } /* Generic */
|
|
||||||
.highlight .k { color: #204a87; font-weight: bold } /* Keyword */
|
|
||||||
.highlight .l { color: #000000 } /* Literal */
|
|
||||||
.highlight .n { color: #000000 } /* Name */
|
|
||||||
.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */
|
|
||||||
.highlight .x { color: #000000 } /* Other */
|
|
||||||
.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
|
|
||||||
.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */
|
|
||||||
.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
|
|
||||||
.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */
|
|
||||||
.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */
|
|
||||||
.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
|
|
||||||
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
|
|
||||||
.highlight .gd { color: #a40000 } /* Generic.Deleted */
|
|
||||||
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
|
|
||||||
.highlight .gr { color: #ef2929 } /* Generic.Error */
|
|
||||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
|
||||||
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
|
||||||
.highlight .go { color: #000000; font-style: italic } /* Generic.Output */
|
|
||||||
.highlight .gp { color: #8f5902 } /* Generic.Prompt */
|
|
||||||
.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
|
|
||||||
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
|
||||||
.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
|
|
||||||
.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */
|
|
||||||
.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */
|
|
||||||
.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */
|
|
||||||
.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */
|
|
||||||
.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */
|
|
||||||
.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */
|
|
||||||
.highlight .ld { color: #000000 } /* Literal.Date */
|
|
||||||
.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */
|
|
||||||
.highlight .s { color: #4e9a06 } /* Literal.String */
|
|
||||||
.highlight .na { color: #c4a000 } /* Name.Attribute */
|
|
||||||
.highlight .nb { color: #204a87 } /* Name.Builtin */
|
|
||||||
.highlight .nc { color: #000000 } /* Name.Class */
|
|
||||||
.highlight .no { color: #000000 } /* Name.Constant */
|
|
||||||
.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */
|
|
||||||
.highlight .ni { color: #ce5c00 } /* Name.Entity */
|
|
||||||
.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
|
|
||||||
.highlight .nf { color: #000000 } /* Name.Function */
|
|
||||||
.highlight .nl { color: #f57900 } /* Name.Label */
|
|
||||||
.highlight .nn { color: #000000 } /* Name.Namespace */
|
|
||||||
.highlight .nx { color: #000000 } /* Name.Other */
|
|
||||||
.highlight .py { color: #000000 } /* Name.Property */
|
|
||||||
.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */
|
|
||||||
.highlight .nv { color: #000000 } /* Name.Variable */
|
|
||||||
.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */
|
|
||||||
.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */
|
|
||||||
.highlight .w { color: #f8f8f8 } /* Text.Whitespace */
|
|
||||||
.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */
|
|
||||||
.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */
|
|
||||||
.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */
|
|
||||||
.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */
|
|
||||||
.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */
|
|
||||||
.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */
|
|
||||||
.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
|
|
||||||
.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
|
|
||||||
.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */
|
|
||||||
.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
|
|
||||||
.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
|
|
||||||
.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
|
|
||||||
.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
|
|
||||||
.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
|
|
||||||
.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
|
|
||||||
.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
|
|
||||||
.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
|
|
||||||
.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
|
|
||||||
.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
|
|
||||||
.highlight .fm { color: #000000 } /* Name.Function.Magic */
|
|
||||||
.highlight .vc { color: #000000 } /* Name.Variable.Class */
|
|
||||||
.highlight .vg { color: #000000 } /* Name.Variable.Global */
|
|
||||||
.highlight .vi { color: #000000 } /* Name.Variable.Instance */
|
|
||||||
.highlight .vm { color: #000000 } /* Name.Variable.Magic */
|
|
||||||
.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] .highlight pre { line-height: 125%; }
|
|
||||||
body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body[data-theme="dark"] .highlight .hll { background-color: #404040 }
|
|
||||||
body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 }
|
|
||||||
body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */
|
|
||||||
body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
|
|
||||||
body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */
|
|
||||||
body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */
|
|
||||||
body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */
|
|
||||||
body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */
|
|
||||||
body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */
|
|
||||||
body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */
|
|
||||||
body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */
|
|
||||||
body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */
|
|
||||||
body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
|
|
||||||
body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
|
|
||||||
body[data-theme="dark"] .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */
|
|
||||||
body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
|
|
||||||
body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
|
|
||||||
body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
|
|
||||||
body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */
|
|
||||||
body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
|
|
||||||
body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */
|
|
||||||
body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */
|
|
||||||
body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */
|
|
||||||
body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */
|
|
||||||
body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */
|
|
||||||
body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */
|
|
||||||
body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */
|
|
||||||
body[data-theme="dark"] .highlight .gt { color: #d22323 } /* Generic.Traceback */
|
|
||||||
body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */
|
|
||||||
body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */
|
|
||||||
body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */
|
|
||||||
body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */
|
|
||||||
body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */
|
|
||||||
body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */
|
|
||||||
body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */
|
|
||||||
body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */
|
|
||||||
body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */
|
|
||||||
body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */
|
|
||||||
body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */
|
|
||||||
body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */
|
|
||||||
body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */
|
|
||||||
body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */
|
|
||||||
body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */
|
|
||||||
body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */
|
|
||||||
body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */
|
|
||||||
body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */
|
|
||||||
body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */
|
|
||||||
body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */
|
|
||||||
body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */
|
|
||||||
body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */
|
|
||||||
body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */
|
|
||||||
body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */
|
|
||||||
body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */
|
|
||||||
body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */
|
|
||||||
body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */
|
|
||||||
body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */
|
|
||||||
body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */
|
|
||||||
body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */
|
|
||||||
body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */
|
|
||||||
body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */
|
|
||||||
body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */
|
|
||||||
body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */
|
|
||||||
body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */
|
|
||||||
body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */
|
|
||||||
body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */
|
|
||||||
body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */
|
|
||||||
body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */
|
|
||||||
body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */
|
|
||||||
body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */
|
|
||||||
body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */
|
|
||||||
body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */
|
|
||||||
body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */
|
|
||||||
body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */
|
|
||||||
body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */
|
|
||||||
body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */
|
|
||||||
body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */
|
|
||||||
body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */
|
|
||||||
body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */
|
|
||||||
body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) .highlight pre { line-height: 125%; }
|
|
||||||
body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
|
||||||
body:not([data-theme="light"]) .highlight .hll { background-color: #404040 }
|
|
||||||
body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 }
|
|
||||||
body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */
|
|
||||||
body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
|
|
||||||
body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */
|
|
||||||
body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */
|
|
||||||
body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */
|
|
||||||
body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */
|
|
||||||
body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */
|
|
||||||
body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */
|
|
||||||
body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */
|
|
||||||
body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */
|
|
||||||
body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */
|
|
||||||
body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */
|
|
||||||
body:not([data-theme="light"]) .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */
|
|
||||||
body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */
|
|
||||||
body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */
|
|
||||||
body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
|
|
||||||
body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */
|
|
||||||
body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */
|
|
||||||
body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */
|
|
||||||
body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */
|
|
||||||
body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */
|
|
||||||
body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */
|
|
||||||
body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */
|
|
||||||
body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */
|
|
||||||
body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */
|
|
||||||
body:not([data-theme="light"]) .highlight .gt { color: #d22323 } /* Generic.Traceback */
|
|
||||||
body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */
|
|
||||||
body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */
|
|
||||||
body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */
|
|
||||||
body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */
|
|
||||||
body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */
|
|
||||||
body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */
|
|
||||||
body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */
|
|
||||||
body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */
|
|
||||||
body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */
|
|
||||||
body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */
|
|
||||||
body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */
|
|
||||||
body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */
|
|
||||||
body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */
|
|
||||||
body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */
|
|
||||||
body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */
|
|
||||||
body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */
|
|
||||||
body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */
|
|
||||||
body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */
|
|
||||||
body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */
|
|
||||||
body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */
|
|
||||||
body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */
|
|
||||||
body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */
|
|
||||||
body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */
|
|
||||||
body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */
|
|
||||||
body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */
|
|
||||||
body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */
|
|
||||||
body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */
|
|
||||||
body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */
|
|
||||||
body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */
|
|
||||||
body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */
|
|
||||||
body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */
|
|
||||||
body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */
|
|
||||||
body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */
|
|
||||||
body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */
|
|
||||||
body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */
|
|
||||||
body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */
|
|
||||||
body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */
|
|
||||||
body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */
|
|
||||||
body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */
|
|
||||||
body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */
|
|
||||||
body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */
|
|
||||||
body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */
|
|
||||||
body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */
|
|
||||||
body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */
|
|
||||||
body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */
|
|
||||||
body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */
|
|
||||||
body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */
|
|
||||||
body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */
|
|
||||||
body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */
|
|
||||||
body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */
|
|
||||||
body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
docs/build/html/_static/scripts/furo.js
vendored
3
docs/build/html/_static/scripts/furo.js
vendored
File diff suppressed because one or more lines are too long
@ -1,7 +0,0 @@
|
|||||||
/*!
|
|
||||||
* gumshoejs v5.1.2 (patched by @pradyunsg)
|
|
||||||
* A simple, framework-agnostic scrollspy script.
|
|
||||||
* (c) 2019 Chris Ferdinandi
|
|
||||||
* MIT License
|
|
||||||
* http://github.com/cferdinandi/gumshoe
|
|
||||||
*/
|
|
||||||
1
docs/build/html/_static/scripts/furo.js.map
vendored
1
docs/build/html/_static/scripts/furo.js.map
vendored
File diff suppressed because one or more lines are too long
566
docs/build/html/_static/searchtools.js
vendored
566
docs/build/html/_static/searchtools.js
vendored
@ -1,566 +0,0 @@
|
|||||||
/*
|
|
||||||
* searchtools.js
|
|
||||||
* ~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Sphinx JavaScript utilities for the full-text search.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple result scoring code.
|
|
||||||
*/
|
|
||||||
if (typeof Scorer === "undefined") {
|
|
||||||
var Scorer = {
|
|
||||||
// Implement the following function to further tweak the score for each result
|
|
||||||
// The function takes a result array [docname, title, anchor, descr, score, filename]
|
|
||||||
// and returns the new score.
|
|
||||||
/*
|
|
||||||
score: result => {
|
|
||||||
const [docname, title, anchor, descr, score, filename] = result
|
|
||||||
return score
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
|
|
||||||
// query matches the full name of an object
|
|
||||||
objNameMatch: 11,
|
|
||||||
// or matches in the last dotted part of the object name
|
|
||||||
objPartialMatch: 6,
|
|
||||||
// Additive scores depending on the priority of the object
|
|
||||||
objPrio: {
|
|
||||||
0: 15, // used to be importantResults
|
|
||||||
1: 5, // used to be objectResults
|
|
||||||
2: -5, // used to be unimportantResults
|
|
||||||
},
|
|
||||||
// Used when the priority is not in the mapping.
|
|
||||||
objPrioDefault: 0,
|
|
||||||
|
|
||||||
// query found in title
|
|
||||||
title: 15,
|
|
||||||
partialTitle: 7,
|
|
||||||
// query found in terms
|
|
||||||
term: 5,
|
|
||||||
partialTerm: 2,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const _removeChildren = (element) => {
|
|
||||||
while (element && element.lastChild) element.removeChild(element.lastChild);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
|
||||||
*/
|
|
||||||
const _escapeRegExp = (string) =>
|
|
||||||
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
|
||||||
|
|
||||||
const _displayItem = (item, searchTerms) => {
|
|
||||||
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
|
|
||||||
const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
|
|
||||||
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
|
|
||||||
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
|
|
||||||
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
|
|
||||||
|
|
||||||
const [docName, title, anchor, descr, score, _filename] = item;
|
|
||||||
|
|
||||||
let listItem = document.createElement("li");
|
|
||||||
let requestUrl;
|
|
||||||
let linkUrl;
|
|
||||||
if (docBuilder === "dirhtml") {
|
|
||||||
// dirhtml builder
|
|
||||||
let dirname = docName + "/";
|
|
||||||
if (dirname.match(/\/index\/$/))
|
|
||||||
dirname = dirname.substring(0, dirname.length - 6);
|
|
||||||
else if (dirname === "index/") dirname = "";
|
|
||||||
requestUrl = docUrlRoot + dirname;
|
|
||||||
linkUrl = requestUrl;
|
|
||||||
} else {
|
|
||||||
// normal html builders
|
|
||||||
requestUrl = docUrlRoot + docName + docFileSuffix;
|
|
||||||
linkUrl = docName + docLinkSuffix;
|
|
||||||
}
|
|
||||||
let linkEl = listItem.appendChild(document.createElement("a"));
|
|
||||||
linkEl.href = linkUrl + anchor;
|
|
||||||
linkEl.dataset.score = score;
|
|
||||||
linkEl.innerHTML = title;
|
|
||||||
if (descr)
|
|
||||||
listItem.appendChild(document.createElement("span")).innerHTML =
|
|
||||||
" (" + descr + ")";
|
|
||||||
else if (showSearchSummary)
|
|
||||||
fetch(requestUrl)
|
|
||||||
.then((responseData) => responseData.text())
|
|
||||||
.then((data) => {
|
|
||||||
if (data)
|
|
||||||
listItem.appendChild(
|
|
||||||
Search.makeSearchSummary(data, searchTerms)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Search.output.appendChild(listItem);
|
|
||||||
};
|
|
||||||
const _finishSearch = (resultCount) => {
|
|
||||||
Search.stopPulse();
|
|
||||||
Search.title.innerText = _("Search Results");
|
|
||||||
if (!resultCount)
|
|
||||||
Search.status.innerText = Documentation.gettext(
|
|
||||||
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
|
|
||||||
);
|
|
||||||
else
|
|
||||||
Search.status.innerText = _(
|
|
||||||
`Search finished, found ${resultCount} page(s) matching the search query.`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const _displayNextItem = (
|
|
||||||
results,
|
|
||||||
resultCount,
|
|
||||||
searchTerms
|
|
||||||
) => {
|
|
||||||
// results left, load the summary and display it
|
|
||||||
// this is intended to be dynamic (don't sub resultsCount)
|
|
||||||
if (results.length) {
|
|
||||||
_displayItem(results.pop(), searchTerms);
|
|
||||||
setTimeout(
|
|
||||||
() => _displayNextItem(results, resultCount, searchTerms),
|
|
||||||
5
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// search finished, update title and status message
|
|
||||||
else _finishSearch(resultCount);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
|
|
||||||
* custom function per language.
|
|
||||||
*
|
|
||||||
* The regular expression works by splitting the string on consecutive characters
|
|
||||||
* that are not Unicode letters, numbers, underscores, or emoji characters.
|
|
||||||
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
|
|
||||||
*/
|
|
||||||
if (typeof splitQuery === "undefined") {
|
|
||||||
var splitQuery = (query) => query
|
|
||||||
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
|
|
||||||
.filter(term => term) // remove remaining empty strings
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search Module
|
|
||||||
*/
|
|
||||||
const Search = {
|
|
||||||
_index: null,
|
|
||||||
_queued_query: null,
|
|
||||||
_pulse_status: -1,
|
|
||||||
|
|
||||||
htmlToText: (htmlString) => {
|
|
||||||
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
|
|
||||||
htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() });
|
|
||||||
const docContent = htmlElement.querySelector('[role="main"]');
|
|
||||||
if (docContent !== undefined) return docContent.textContent;
|
|
||||||
console.warn(
|
|
||||||
"Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
|
|
||||||
);
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
|
|
||||||
init: () => {
|
|
||||||
const query = new URLSearchParams(window.location.search).get("q");
|
|
||||||
document
|
|
||||||
.querySelectorAll('input[name="q"]')
|
|
||||||
.forEach((el) => (el.value = query));
|
|
||||||
if (query) Search.performSearch(query);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadIndex: (url) =>
|
|
||||||
(document.body.appendChild(document.createElement("script")).src = url),
|
|
||||||
|
|
||||||
setIndex: (index) => {
|
|
||||||
Search._index = index;
|
|
||||||
if (Search._queued_query !== null) {
|
|
||||||
const query = Search._queued_query;
|
|
||||||
Search._queued_query = null;
|
|
||||||
Search.query(query);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
hasIndex: () => Search._index !== null,
|
|
||||||
|
|
||||||
deferQuery: (query) => (Search._queued_query = query),
|
|
||||||
|
|
||||||
stopPulse: () => (Search._pulse_status = -1),
|
|
||||||
|
|
||||||
startPulse: () => {
|
|
||||||
if (Search._pulse_status >= 0) return;
|
|
||||||
|
|
||||||
const pulse = () => {
|
|
||||||
Search._pulse_status = (Search._pulse_status + 1) % 4;
|
|
||||||
Search.dots.innerText = ".".repeat(Search._pulse_status);
|
|
||||||
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
|
|
||||||
};
|
|
||||||
pulse();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* perform a search for something (or wait until index is loaded)
|
|
||||||
*/
|
|
||||||
performSearch: (query) => {
|
|
||||||
// create the required interface elements
|
|
||||||
const searchText = document.createElement("h2");
|
|
||||||
searchText.textContent = _("Searching");
|
|
||||||
const searchSummary = document.createElement("p");
|
|
||||||
searchSummary.classList.add("search-summary");
|
|
||||||
searchSummary.innerText = "";
|
|
||||||
const searchList = document.createElement("ul");
|
|
||||||
searchList.classList.add("search");
|
|
||||||
|
|
||||||
const out = document.getElementById("search-results");
|
|
||||||
Search.title = out.appendChild(searchText);
|
|
||||||
Search.dots = Search.title.appendChild(document.createElement("span"));
|
|
||||||
Search.status = out.appendChild(searchSummary);
|
|
||||||
Search.output = out.appendChild(searchList);
|
|
||||||
|
|
||||||
const searchProgress = document.getElementById("search-progress");
|
|
||||||
// Some themes don't use the search progress node
|
|
||||||
if (searchProgress) {
|
|
||||||
searchProgress.innerText = _("Preparing search...");
|
|
||||||
}
|
|
||||||
Search.startPulse();
|
|
||||||
|
|
||||||
// index already loaded, the browser was quick!
|
|
||||||
if (Search.hasIndex()) Search.query(query);
|
|
||||||
else Search.deferQuery(query);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* execute search (requires search index to be loaded)
|
|
||||||
*/
|
|
||||||
query: (query) => {
|
|
||||||
const filenames = Search._index.filenames;
|
|
||||||
const docNames = Search._index.docnames;
|
|
||||||
const titles = Search._index.titles;
|
|
||||||
const allTitles = Search._index.alltitles;
|
|
||||||
const indexEntries = Search._index.indexentries;
|
|
||||||
|
|
||||||
// stem the search terms and add them to the correct list
|
|
||||||
const stemmer = new Stemmer();
|
|
||||||
const searchTerms = new Set();
|
|
||||||
const excludedTerms = new Set();
|
|
||||||
const highlightTerms = new Set();
|
|
||||||
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
|
|
||||||
splitQuery(query.trim()).forEach((queryTerm) => {
|
|
||||||
const queryTermLower = queryTerm.toLowerCase();
|
|
||||||
|
|
||||||
// maybe skip this "word"
|
|
||||||
// stopwords array is from language_data.js
|
|
||||||
if (
|
|
||||||
stopwords.indexOf(queryTermLower) !== -1 ||
|
|
||||||
queryTerm.match(/^\d+$/)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// stem the word
|
|
||||||
let word = stemmer.stemWord(queryTermLower);
|
|
||||||
// select the correct list
|
|
||||||
if (word[0] === "-") excludedTerms.add(word.substr(1));
|
|
||||||
else {
|
|
||||||
searchTerms.add(word);
|
|
||||||
highlightTerms.add(queryTermLower);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
|
|
||||||
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.debug("SEARCH: searching for:");
|
|
||||||
// console.info("required: ", [...searchTerms]);
|
|
||||||
// console.info("excluded: ", [...excludedTerms]);
|
|
||||||
|
|
||||||
// array of [docname, title, anchor, descr, score, filename]
|
|
||||||
let results = [];
|
|
||||||
_removeChildren(document.getElementById("search-progress"));
|
|
||||||
|
|
||||||
const queryLower = query.toLowerCase();
|
|
||||||
for (const [title, foundTitles] of Object.entries(allTitles)) {
|
|
||||||
if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
|
|
||||||
for (const [file, id] of foundTitles) {
|
|
||||||
let score = Math.round(100 * queryLower.length / title.length)
|
|
||||||
results.push([
|
|
||||||
docNames[file],
|
|
||||||
titles[file] !== title ? `${titles[file]} > ${title}` : title,
|
|
||||||
id !== null ? "#" + id : "",
|
|
||||||
null,
|
|
||||||
score,
|
|
||||||
filenames[file],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for explicit entries in index directives
|
|
||||||
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
|
|
||||||
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
|
|
||||||
for (const [file, id] of foundEntries) {
|
|
||||||
let score = Math.round(100 * queryLower.length / entry.length)
|
|
||||||
results.push([
|
|
||||||
docNames[file],
|
|
||||||
titles[file],
|
|
||||||
id ? "#" + id : "",
|
|
||||||
null,
|
|
||||||
score,
|
|
||||||
filenames[file],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup as object
|
|
||||||
objectTerms.forEach((term) =>
|
|
||||||
results.push(...Search.performObjectSearch(term, objectTerms))
|
|
||||||
);
|
|
||||||
|
|
||||||
// lookup as search terms in fulltext
|
|
||||||
results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
|
|
||||||
|
|
||||||
// let the scorer override scores with a custom scoring function
|
|
||||||
if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
|
|
||||||
|
|
||||||
// now sort the results by score (in opposite order of appearance, since the
|
|
||||||
// display function below uses pop() to retrieve items) and then
|
|
||||||
// alphabetically
|
|
||||||
results.sort((a, b) => {
|
|
||||||
const leftScore = a[4];
|
|
||||||
const rightScore = b[4];
|
|
||||||
if (leftScore === rightScore) {
|
|
||||||
// same score: sort alphabetically
|
|
||||||
const leftTitle = a[1].toLowerCase();
|
|
||||||
const rightTitle = b[1].toLowerCase();
|
|
||||||
if (leftTitle === rightTitle) return 0;
|
|
||||||
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
|
|
||||||
}
|
|
||||||
return leftScore > rightScore ? 1 : -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove duplicate search results
|
|
||||||
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
|
|
||||||
let seen = new Set();
|
|
||||||
results = results.reverse().reduce((acc, result) => {
|
|
||||||
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
|
|
||||||
if (!seen.has(resultStr)) {
|
|
||||||
acc.push(result);
|
|
||||||
seen.add(resultStr);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
results = results.reverse();
|
|
||||||
|
|
||||||
// for debugging
|
|
||||||
//Search.lastresults = results.slice(); // a copy
|
|
||||||
// console.info("search results:", Search.lastresults);
|
|
||||||
|
|
||||||
// print the results
|
|
||||||
_displayNextItem(results, results.length, searchTerms);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* search for object names
|
|
||||||
*/
|
|
||||||
performObjectSearch: (object, objectTerms) => {
|
|
||||||
const filenames = Search._index.filenames;
|
|
||||||
const docNames = Search._index.docnames;
|
|
||||||
const objects = Search._index.objects;
|
|
||||||
const objNames = Search._index.objnames;
|
|
||||||
const titles = Search._index.titles;
|
|
||||||
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
const objectSearchCallback = (prefix, match) => {
|
|
||||||
const name = match[4]
|
|
||||||
const fullname = (prefix ? prefix + "." : "") + name;
|
|
||||||
const fullnameLower = fullname.toLowerCase();
|
|
||||||
if (fullnameLower.indexOf(object) < 0) return;
|
|
||||||
|
|
||||||
let score = 0;
|
|
||||||
const parts = fullnameLower.split(".");
|
|
||||||
|
|
||||||
// check for different match types: exact matches of full name or
|
|
||||||
// "last name" (i.e. last dotted part)
|
|
||||||
if (fullnameLower === object || parts.slice(-1)[0] === object)
|
|
||||||
score += Scorer.objNameMatch;
|
|
||||||
else if (parts.slice(-1)[0].indexOf(object) > -1)
|
|
||||||
score += Scorer.objPartialMatch; // matches in last name
|
|
||||||
|
|
||||||
const objName = objNames[match[1]][2];
|
|
||||||
const title = titles[match[0]];
|
|
||||||
|
|
||||||
// If more than one term searched for, we require other words to be
|
|
||||||
// found in the name/title/description
|
|
||||||
const otherTerms = new Set(objectTerms);
|
|
||||||
otherTerms.delete(object);
|
|
||||||
if (otherTerms.size > 0) {
|
|
||||||
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
|
|
||||||
if (
|
|
||||||
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let anchor = match[3];
|
|
||||||
if (anchor === "") anchor = fullname;
|
|
||||||
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
|
|
||||||
|
|
||||||
const descr = objName + _(", in ") + title;
|
|
||||||
|
|
||||||
// add custom score for some objects according to scorer
|
|
||||||
if (Scorer.objPrio.hasOwnProperty(match[2]))
|
|
||||||
score += Scorer.objPrio[match[2]];
|
|
||||||
else score += Scorer.objPrioDefault;
|
|
||||||
|
|
||||||
results.push([
|
|
||||||
docNames[match[0]],
|
|
||||||
fullname,
|
|
||||||
"#" + anchor,
|
|
||||||
descr,
|
|
||||||
score,
|
|
||||||
filenames[match[0]],
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
Object.keys(objects).forEach((prefix) =>
|
|
||||||
objects[prefix].forEach((array) =>
|
|
||||||
objectSearchCallback(prefix, array)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* search for full-text terms in the index
|
|
||||||
*/
|
|
||||||
performTermsSearch: (searchTerms, excludedTerms) => {
|
|
||||||
// prepare search
|
|
||||||
const terms = Search._index.terms;
|
|
||||||
const titleTerms = Search._index.titleterms;
|
|
||||||
const filenames = Search._index.filenames;
|
|
||||||
const docNames = Search._index.docnames;
|
|
||||||
const titles = Search._index.titles;
|
|
||||||
|
|
||||||
const scoreMap = new Map();
|
|
||||||
const fileMap = new Map();
|
|
||||||
|
|
||||||
// perform the search on the required terms
|
|
||||||
searchTerms.forEach((word) => {
|
|
||||||
const files = [];
|
|
||||||
const arr = [
|
|
||||||
{ files: terms[word], score: Scorer.term },
|
|
||||||
{ files: titleTerms[word], score: Scorer.title },
|
|
||||||
];
|
|
||||||
// add support for partial matches
|
|
||||||
if (word.length > 2) {
|
|
||||||
const escapedWord = _escapeRegExp(word);
|
|
||||||
Object.keys(terms).forEach((term) => {
|
|
||||||
if (term.match(escapedWord) && !terms[word])
|
|
||||||
arr.push({ files: terms[term], score: Scorer.partialTerm });
|
|
||||||
});
|
|
||||||
Object.keys(titleTerms).forEach((term) => {
|
|
||||||
if (term.match(escapedWord) && !titleTerms[word])
|
|
||||||
arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// no match but word was a required one
|
|
||||||
if (arr.every((record) => record.files === undefined)) return;
|
|
||||||
|
|
||||||
// found search word in contents
|
|
||||||
arr.forEach((record) => {
|
|
||||||
if (record.files === undefined) return;
|
|
||||||
|
|
||||||
let recordFiles = record.files;
|
|
||||||
if (recordFiles.length === undefined) recordFiles = [recordFiles];
|
|
||||||
files.push(...recordFiles);
|
|
||||||
|
|
||||||
// set score for the word in each file
|
|
||||||
recordFiles.forEach((file) => {
|
|
||||||
if (!scoreMap.has(file)) scoreMap.set(file, {});
|
|
||||||
scoreMap.get(file)[word] = record.score;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// create the mapping
|
|
||||||
files.forEach((file) => {
|
|
||||||
if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
|
|
||||||
fileMap.get(file).push(word);
|
|
||||||
else fileMap.set(file, [word]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// now check if the files don't contain excluded terms
|
|
||||||
const results = [];
|
|
||||||
for (const [file, wordList] of fileMap) {
|
|
||||||
// check if all requirements are matched
|
|
||||||
|
|
||||||
// as search terms with length < 3 are discarded
|
|
||||||
const filteredTermCount = [...searchTerms].filter(
|
|
||||||
(term) => term.length > 2
|
|
||||||
).length;
|
|
||||||
if (
|
|
||||||
wordList.length !== searchTerms.size &&
|
|
||||||
wordList.length !== filteredTermCount
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// ensure that none of the excluded terms is in the search result
|
|
||||||
if (
|
|
||||||
[...excludedTerms].some(
|
|
||||||
(term) =>
|
|
||||||
terms[term] === file ||
|
|
||||||
titleTerms[term] === file ||
|
|
||||||
(terms[term] || []).includes(file) ||
|
|
||||||
(titleTerms[term] || []).includes(file)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// select one (max) score for the file.
|
|
||||||
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
|
|
||||||
// add result to the result list
|
|
||||||
results.push([
|
|
||||||
docNames[file],
|
|
||||||
titles[file],
|
|
||||||
"",
|
|
||||||
null,
|
|
||||||
score,
|
|
||||||
filenames[file],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function to return a node containing the
|
|
||||||
* search summary for a given text. keywords is a list
|
|
||||||
* of stemmed words.
|
|
||||||
*/
|
|
||||||
makeSearchSummary: (htmlText, keywords) => {
|
|
||||||
const text = Search.htmlToText(htmlText);
|
|
||||||
if (text === "") return null;
|
|
||||||
|
|
||||||
const textLower = text.toLowerCase();
|
|
||||||
const actualStartPosition = [...keywords]
|
|
||||||
.map((k) => textLower.indexOf(k.toLowerCase()))
|
|
||||||
.filter((i) => i > -1)
|
|
||||||
.slice(-1)[0];
|
|
||||||
const startWithContext = Math.max(actualStartPosition - 120, 0);
|
|
||||||
|
|
||||||
const top = startWithContext === 0 ? "" : "...";
|
|
||||||
const tail = startWithContext + 240 < text.length ? "..." : "";
|
|
||||||
|
|
||||||
let summary = document.createElement("p");
|
|
||||||
summary.classList.add("context");
|
|
||||||
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
|
|
||||||
|
|
||||||
return summary;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
_ready(Search.init);
|
|
||||||
296
docs/build/html/_static/skeleton.css
vendored
296
docs/build/html/_static/skeleton.css
vendored
@ -1,296 +0,0 @@
|
|||||||
/* Some sane resets. */
|
|
||||||
html {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All the flexbox magic! */
|
|
||||||
body,
|
|
||||||
.sb-announcement,
|
|
||||||
.sb-content,
|
|
||||||
.sb-main,
|
|
||||||
.sb-container,
|
|
||||||
.sb-container__inner,
|
|
||||||
.sb-article-container,
|
|
||||||
.sb-footer-content,
|
|
||||||
.sb-header,
|
|
||||||
.sb-header-secondary,
|
|
||||||
.sb-footer {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These order things vertically */
|
|
||||||
body,
|
|
||||||
.sb-main,
|
|
||||||
.sb-article-container {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Put elements in the center */
|
|
||||||
.sb-header,
|
|
||||||
.sb-header-secondary,
|
|
||||||
.sb-container,
|
|
||||||
.sb-content,
|
|
||||||
.sb-footer,
|
|
||||||
.sb-footer-content {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
/* Put elements at the ends */
|
|
||||||
.sb-article-container {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These elements grow. */
|
|
||||||
.sb-main,
|
|
||||||
.sb-content,
|
|
||||||
.sb-container,
|
|
||||||
article {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Because padding making this wider is not fun */
|
|
||||||
article {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The announcements element should never be wider than the page. */
|
|
||||||
.sb-announcement {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-sidebar-primary,
|
|
||||||
.sb-sidebar-secondary {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 17rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-announcement__inner {
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 3rem;
|
|
||||||
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebars, with checkbox-based toggle */
|
|
||||||
.sb-sidebar-primary,
|
|
||||||
.sb-sidebar-secondary {
|
|
||||||
position: fixed;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-sidebar-primary {
|
|
||||||
left: -17rem;
|
|
||||||
transition: left 250ms ease-in-out;
|
|
||||||
}
|
|
||||||
.sb-sidebar-secondary {
|
|
||||||
right: -17rem;
|
|
||||||
transition: right 250ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-sidebar-toggle {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.sb-sidebar-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
|
|
||||||
transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.54);
|
|
||||||
}
|
|
||||||
|
|
||||||
#sb-sidebar-toggle--primary:checked
|
|
||||||
~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"],
|
|
||||||
#sb-sidebar-toggle--secondary:checked
|
|
||||||
~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
opacity: 1;
|
|
||||||
transition: width 0ms ease, height 0ms ease, opacity 250ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Full-width mode */
|
|
||||||
.drop-secondary-sidebar-for-full-width-content
|
|
||||||
.hide-when-secondary-sidebar-shown {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile views */
|
|
||||||
.sb-page-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-article-container,
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
padding: 0 1rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 32rem) {
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablet views */
|
|
||||||
@media (min-width: 42rem) {
|
|
||||||
.sb-article-container {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 42rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 42rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 46rem) {
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 46rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 46rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 50rem) {
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 50rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 50rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablet views */
|
|
||||||
@media (min-width: 59rem) {
|
|
||||||
.sb-sidebar-secondary {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
.hide-when-secondary-sidebar-shown {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 59rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 42rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 63rem) {
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 63rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 46rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 67rem) {
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 67rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 50rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Desktop views */
|
|
||||||
@media (min-width: 76rem) {
|
|
||||||
.sb-sidebar-primary {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
.hide-when-primary-sidebar-shown {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 59rem;
|
|
||||||
}
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 42rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Full desktop views */
|
|
||||||
@media (min-width: 80rem) {
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 46rem;
|
|
||||||
}
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 63rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 84rem) {
|
|
||||||
.sb-article,
|
|
||||||
.match-content-width {
|
|
||||||
width: 50rem;
|
|
||||||
}
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 67rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 88rem) {
|
|
||||||
.sb-footer-content__inner,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .sb-article,
|
|
||||||
.drop-secondary-sidebar-for-full-width-content .match-content-width {
|
|
||||||
width: 67rem;
|
|
||||||
}
|
|
||||||
.sb-page-width {
|
|
||||||
width: 88rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
144
docs/build/html/_static/sphinx_highlight.js
vendored
144
docs/build/html/_static/sphinx_highlight.js
vendored
@ -1,144 +0,0 @@
|
|||||||
/* Highlighting utilities for Sphinx HTML documentation. */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const SPHINX_HIGHLIGHT_ENABLED = true
|
|
||||||
|
|
||||||
/**
|
|
||||||
* highlight a given string on a node by wrapping it in
|
|
||||||
* span elements with the given class name.
|
|
||||||
*/
|
|
||||||
const _highlight = (node, addItems, text, className) => {
|
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
|
||||||
const val = node.nodeValue;
|
|
||||||
const parent = node.parentNode;
|
|
||||||
const pos = val.toLowerCase().indexOf(text);
|
|
||||||
if (
|
|
||||||
pos >= 0 &&
|
|
||||||
!parent.classList.contains(className) &&
|
|
||||||
!parent.classList.contains("nohighlight")
|
|
||||||
) {
|
|
||||||
let span;
|
|
||||||
|
|
||||||
const closestNode = parent.closest("body, svg, foreignObject");
|
|
||||||
const isInSVG = closestNode && closestNode.matches("svg");
|
|
||||||
if (isInSVG) {
|
|
||||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
|
||||||
} else {
|
|
||||||
span = document.createElement("span");
|
|
||||||
span.classList.add(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
|
||||||
parent.insertBefore(
|
|
||||||
span,
|
|
||||||
parent.insertBefore(
|
|
||||||
document.createTextNode(val.substr(pos + text.length)),
|
|
||||||
node.nextSibling
|
|
||||||
)
|
|
||||||
);
|
|
||||||
node.nodeValue = val.substr(0, pos);
|
|
||||||
|
|
||||||
if (isInSVG) {
|
|
||||||
const rect = document.createElementNS(
|
|
||||||
"http://www.w3.org/2000/svg",
|
|
||||||
"rect"
|
|
||||||
);
|
|
||||||
const bbox = parent.getBBox();
|
|
||||||
rect.x.baseVal.value = bbox.x;
|
|
||||||
rect.y.baseVal.value = bbox.y;
|
|
||||||
rect.width.baseVal.value = bbox.width;
|
|
||||||
rect.height.baseVal.value = bbox.height;
|
|
||||||
rect.setAttribute("class", className);
|
|
||||||
addItems.push({ parent: parent, target: rect });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (node.matches && !node.matches("button, select, textarea")) {
|
|
||||||
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const _highlightText = (thisNode, text, className) => {
|
|
||||||
let addItems = [];
|
|
||||||
_highlight(thisNode, addItems, text, className);
|
|
||||||
addItems.forEach((obj) =>
|
|
||||||
obj.parent.insertAdjacentElement("beforebegin", obj.target)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Small JavaScript module for the documentation.
|
|
||||||
*/
|
|
||||||
const SphinxHighlight = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* highlight the search words provided in localstorage in the text
|
|
||||||
*/
|
|
||||||
highlightSearchWords: () => {
|
|
||||||
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
|
|
||||||
|
|
||||||
// get and clear terms from localstorage
|
|
||||||
const url = new URL(window.location);
|
|
||||||
const highlight =
|
|
||||||
localStorage.getItem("sphinx_highlight_terms")
|
|
||||||
|| url.searchParams.get("highlight")
|
|
||||||
|| "";
|
|
||||||
localStorage.removeItem("sphinx_highlight_terms")
|
|
||||||
url.searchParams.delete("highlight");
|
|
||||||
window.history.replaceState({}, "", url);
|
|
||||||
|
|
||||||
// get individual terms from highlight string
|
|
||||||
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
|
|
||||||
if (terms.length === 0) return; // nothing to do
|
|
||||||
|
|
||||||
// There should never be more than one element matching "div.body"
|
|
||||||
const divBody = document.querySelectorAll("div.body");
|
|
||||||
const body = divBody.length ? divBody[0] : document.querySelector("body");
|
|
||||||
window.setTimeout(() => {
|
|
||||||
terms.forEach((term) => _highlightText(body, term, "highlighted"));
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
const searchBox = document.getElementById("searchbox");
|
|
||||||
if (searchBox === null) return;
|
|
||||||
searchBox.appendChild(
|
|
||||||
document
|
|
||||||
.createRange()
|
|
||||||
.createContextualFragment(
|
|
||||||
'<p class="highlight-link">' +
|
|
||||||
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
|
|
||||||
_("Hide Search Matches") +
|
|
||||||
"</a></p>"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function to hide the search marks again
|
|
||||||
*/
|
|
||||||
hideSearchWords: () => {
|
|
||||||
document
|
|
||||||
.querySelectorAll("#searchbox .highlight-link")
|
|
||||||
.forEach((el) => el.remove());
|
|
||||||
document
|
|
||||||
.querySelectorAll("span.highlighted")
|
|
||||||
.forEach((el) => el.classList.remove("highlighted"));
|
|
||||||
localStorage.removeItem("sphinx_highlight_terms")
|
|
||||||
},
|
|
||||||
|
|
||||||
initEscapeListener: () => {
|
|
||||||
// only install a listener if it is really needed
|
|
||||||
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
|
|
||||||
|
|
||||||
document.addEventListener("keydown", (event) => {
|
|
||||||
// bail for input elements
|
|
||||||
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
|
|
||||||
// bail with special keys
|
|
||||||
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
|
|
||||||
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
|
|
||||||
SphinxHighlight.hideSearchWords();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
_ready(SphinxHighlight.highlightSearchWords);
|
|
||||||
_ready(SphinxHighlight.initEscapeListener);
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
docs/build/html/_static/styles/furo.css
vendored
2
docs/build/html/_static/styles/furo.css
vendored
File diff suppressed because one or more lines are too long
1
docs/build/html/_static/styles/furo.css.map
vendored
1
docs/build/html/_static/styles/furo.css.map
vendored
File diff suppressed because one or more lines are too long
247
docs/build/html/development.html
vendored
247
docs/build/html/development.html
vendored
@ -1,247 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Reference" href="reference.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 -->
|
|
||||||
<title>Development - MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul class="current">
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
<section id="development">
|
|
||||||
<h1>Development<a class="headerlink" href="#development" title="Permalink to this heading">#</a></h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
|
|
||||||
<a class="prev-page" href="reference.html">
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Previous</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="title">Reference</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer no-toc">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
236
docs/build/html/genindex.html
vendored
236
docs/build/html/genindex.html
vendored
@ -1,236 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 --><title>Index - MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
|
|
||||||
<section class="genindex-section">
|
|
||||||
<h1 id="index">Index</h1>
|
|
||||||
<div class="genindex-jumpbox"></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer no-toc">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
294
docs/build/html/index.html
vendored
294
docs/build/html/index.html
vendored
@ -1,294 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Introduction" href="introduction.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 -->
|
|
||||||
<title>MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="#"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
<section id="welcome-to-musicmuster-s-documentation">
|
|
||||||
<h1>Welcome to MusicMuster’s documentation!<a class="headerlink" href="#welcome-to-musicmuster-s-documentation" title="Permalink to this heading">#</a></h1>
|
|
||||||
<p><strong>MusicMuster</strong> is a music player targeted at the production of live
|
|
||||||
internet radio shows.</p>
|
|
||||||
<section id="contents">
|
|
||||||
<h2>Contents<a class="headerlink" href="#contents" title="Permalink to this heading">#</a></h2>
|
|
||||||
<div class="toctree-wrapper compound">
|
|
||||||
<ul>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="introduction.html#why-musicmuster">Why MusicMuster?</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="introduction.html#what-is-musicmuster">What is MusicMuster?</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="introduction.html#features">Features</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="introduction.html#requirements">Requirements</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="introduction.html#feedback-bugs-etc">Feedback, bugs, etc</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
<section id="indices-and-tables">
|
|
||||||
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this heading">#</a></h1>
|
|
||||||
<ul class="simple">
|
|
||||||
<li><p><a class="reference internal" href="genindex.html"><span class="std std-ref">Index</span></a></p></li>
|
|
||||||
<li><p><a class="reference internal" href="py-modindex.html"><span class="std std-ref">Module Index</span></a></p></li>
|
|
||||||
<li><p><a class="reference internal" href="search.html"><span class="std std-ref">Search Page</span></a></p></li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
<a class="next-page" href="introduction.html">
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Next</span>
|
|
||||||
</div>
|
|
||||||
<div class="title">Introduction</div>
|
|
||||||
</div>
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="toc-sticky toc-scroll">
|
|
||||||
<div class="toc-title-container">
|
|
||||||
<span class="toc-title">
|
|
||||||
On this page
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="toc-tree-container">
|
|
||||||
<div class="toc-tree">
|
|
||||||
<ul>
|
|
||||||
<li><a class="reference internal" href="#">Welcome to MusicMuster’s documentation!</a><ul>
|
|
||||||
<li><a class="reference internal" href="#contents">Contents</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#indices-and-tables">Indices and tables</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
255
docs/build/html/installation.html
vendored
255
docs/build/html/installation.html
vendored
@ -1,255 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Tutorial" href="tutorial.html" /><link rel="prev" title="Introduction" href="introduction.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 -->
|
|
||||||
<title>Installation - MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul class="current">
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
<section id="installation">
|
|
||||||
<h1>Installation<a class="headerlink" href="#installation" title="Permalink to this heading">#</a></h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
<a class="next-page" href="tutorial.html">
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Next</span>
|
|
||||||
</div>
|
|
||||||
<div class="title">Tutorial</div>
|
|
||||||
</div>
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
</a>
|
|
||||||
<a class="prev-page" href="introduction.html">
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Previous</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="title">Introduction</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer no-toc">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
362
docs/build/html/introduction.html
vendored
362
docs/build/html/introduction.html
vendored
@ -1,362 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Installation" href="installation.html" /><link rel="prev" title="Welcome to MusicMuster’s documentation!" href="index.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 -->
|
|
||||||
<title>Introduction - MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul class="current">
|
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
<section id="introduction">
|
|
||||||
<h1>Introduction<a class="headerlink" href="#introduction" title="Permalink to this heading">#</a></h1>
|
|
||||||
<section id="why-musicmuster">
|
|
||||||
<h2>Why MusicMuster?<a class="headerlink" href="#why-musicmuster" title="Permalink to this heading">#</a></h2>
|
|
||||||
<p>In January 2022 I started my show on <cite>Mixcloud
|
|
||||||
<https://www.mixcloud.com/KeithsMusicBox/></cite>. Until then, my show had
|
|
||||||
been on an internet radio station which required me to use a Windows
|
|
||||||
playout system. As I only use Linux, I had to set up a Windows PC
|
|
||||||
specifically for that purpose. The system I had to use had what I felt
|
|
||||||
were shortcomings in various areas.</p>
|
|
||||||
<p>Once I moved to Mixcloud I searched for a Linux equivalent that didn’t
|
|
||||||
have the same shortcomings but was unable to find one that met my
|
|
||||||
criteria. I decided to see how practical it would be to write my own,
|
|
||||||
and MusicMuster was born.</p>
|
|
||||||
</section>
|
|
||||||
<section id="what-is-musicmuster">
|
|
||||||
<h2>What is MusicMuster?<a class="headerlink" href="#what-is-musicmuster" title="Permalink to this heading">#</a></h2>
|
|
||||||
<p>It is a Linux-based music player. Whilst it could be used as a general
|
|
||||||
home music player, there are much better applications for that role.
|
|
||||||
<strong>MusicMuster</strong> has been specifically designed to support the
|
|
||||||
production of live internet radio shows.</p>
|
|
||||||
</section>
|
|
||||||
<section id="features">
|
|
||||||
<h2>Features<a class="headerlink" href="#features" title="Permalink to this heading">#</a></h2>
|
|
||||||
<ul class="simple">
|
|
||||||
<li><p>Database backed</p></li>
|
|
||||||
<li><p>Can be almost entirely keyboard driven</p></li>
|
|
||||||
<li><p>Open multiple playlists in tabs</p></li>
|
|
||||||
<li><p>Play tracks from any playlist</p></li>
|
|
||||||
<li><p>Add notes/comments to tracks on playlist</p></li>
|
|
||||||
<li><p>Automatatic colour-coding of notes/comments according to content</p></li>
|
|
||||||
<li><p>Preview tracks before playing to audience</p></li>
|
|
||||||
<li><p>Time of day clock</p></li>
|
|
||||||
<li><p>Elapsed track time counter</p></li>
|
|
||||||
<li><p>Time to run until track starts to fade</p></li>
|
|
||||||
<li><p>Time to run until track is silent</p></li>
|
|
||||||
<li><p>Graphic of volume from 5 seconds (configurable) before fade until
|
|
||||||
track is silent</p></li>
|
|
||||||
<li><p>Optionally hide played tracks in playlist</p></li>
|
|
||||||
<li><p>Button to drop playout volume by 3dB for talkover</p></li>
|
|
||||||
<li><dl class="simple">
|
|
||||||
<dt>Playlist displays:</dt><dd><ul>
|
|
||||||
<li><p>Title</p></li>
|
|
||||||
<li><p>Artist</p></li>
|
|
||||||
<li><p>Length of track (mm:ss)</p></li>
|
|
||||||
<li><p>Estimated start time of track</p></li>
|
|
||||||
<li><p>Estimated end time of track</p></li>
|
|
||||||
<li><p>When track was last played</p></li>
|
|
||||||
<li><p>Bits per second (bitrate) of track</p></li>
|
|
||||||
<li><p>Length of leading silence in recording before track starts</p></li>
|
|
||||||
<li><p>Total track length of arbitrary sections of tracks</p></li>
|
|
||||||
<li><p>Commands that are sent to OBS Studio (eg, for automated scene
|
|
||||||
changes)</p></li>
|
|
||||||
</ul>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
<li><p>Playlist templates</p></li>
|
|
||||||
<li><p>Move selected or unplayed tracks between playlists</p></li>
|
|
||||||
<li><p>Download CSV of tracks played between arbitrary dates/times</p></li>
|
|
||||||
<li><p>Search for tracks by title or artist</p></li>
|
|
||||||
<li><p>Automatic search of current and next track in Wikipedia</p></li>
|
|
||||||
<li><p>Optional search of any track in Wikipedia</p></li>
|
|
||||||
<li><p>Optional search of any track in Songfacts</p></li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section id="requirements">
|
|
||||||
<h2>Requirements<a class="headerlink" href="#requirements" title="Permalink to this heading">#</a></h2>
|
|
||||||
<div class="admonition note">
|
|
||||||
<p class="admonition-title">Note</p>
|
|
||||||
<p>MusicMuster has only been tested on Debian 12, “Bookworm”;
|
|
||||||
however, it should run on most contemporary Linux systems.</p>
|
|
||||||
</div>
|
|
||||||
<p>The <a class="reference internal" href="installation.html"><span class="doc">Installation</span></a> page explains how to build MusicMuster in its
|
|
||||||
own environment which will automatcally install all requirements
|
|
||||||
except the database. The current version of MusicMuster uses MariaDB
|
|
||||||
version 10.11; however, any recent version of MariaDB should suffice.</p>
|
|
||||||
<p>MusicMuster is a Python 3 application and requires Python 3.8 or
|
|
||||||
later.</p>
|
|
||||||
</section>
|
|
||||||
<section id="feedback-bugs-etc">
|
|
||||||
<h2>Feedback, bugs, etc<a class="headerlink" href="#feedback-bugs-etc" title="Permalink to this heading">#</a></h2>
|
|
||||||
<p>Please send to <a class="reference external" href="mailto:keith%40midnighthax.com">keith<span>@</span>midnighthax<span>.</span>com</a></p>
|
|
||||||
<p>Keith Edmunds,
|
|
||||||
July 2023</p>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
<a class="next-page" href="installation.html">
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Next</span>
|
|
||||||
</div>
|
|
||||||
<div class="title">Installation</div>
|
|
||||||
</div>
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
</a>
|
|
||||||
<a class="prev-page" href="index.html">
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Previous</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="title">Home</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="toc-sticky toc-scroll">
|
|
||||||
<div class="toc-title-container">
|
|
||||||
<span class="toc-title">
|
|
||||||
On this page
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="toc-tree-container">
|
|
||||||
<div class="toc-tree">
|
|
||||||
<ul>
|
|
||||||
<li><a class="reference internal" href="#">Introduction</a><ul>
|
|
||||||
<li><a class="reference internal" href="#why-musicmuster">Why MusicMuster?</a></li>
|
|
||||||
<li><a class="reference internal" href="#what-is-musicmuster">What is MusicMuster?</a></li>
|
|
||||||
<li><a class="reference internal" href="#features">Features</a></li>
|
|
||||||
<li><a class="reference internal" href="#requirements">Requirements</a></li>
|
|
||||||
<li><a class="reference internal" href="#feedback-bugs-etc">Feedback, bugs, etc</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
6
docs/build/html/objects.inv
vendored
6
docs/build/html/objects.inv
vendored
@ -1,6 +0,0 @@
|
|||||||
# Sphinx inventory version 2
|
|
||||||
# Project: MusicMuster
|
|
||||||
# Version:
|
|
||||||
# The remainder of this file is compressed using zlib.
|
|
||||||
xÚ…<EFBFBD>A E÷œP·®Ý¸hÒ¨‰k„±m2…¦Æî¼†×ó$ÒR¤<52>&nóæ¿Pp4mš¸#µUFòlÃUÂëŠä»X ºÖ
|
|
||||||
â8(‘†ü~¸²›æÎg@iàdxÞ¹ZúƒÀ¾OÇ}¸6 ª<>^ù)ŽâX-‡%·&â5²Fuò[K<j‰°Æ¨¿kû,6‚“{aúç¬ûG+zªüs¶…+XÐæ¯üÀàbÉ+«åšÀBòú…(<28>QGÆÖçƒ#éÓT±7¸€¾7
|
|
||||||
255
docs/build/html/reference.html
vendored
255
docs/build/html/reference.html
vendored
@ -1,255 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Development" href="development.html" /><link rel="prev" title="Tutorial" href="tutorial.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 -->
|
|
||||||
<title>Reference - MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul class="current">
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
<section id="reference">
|
|
||||||
<h1>Reference<a class="headerlink" href="#reference" title="Permalink to this heading">#</a></h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
<a class="next-page" href="development.html">
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Next</span>
|
|
||||||
</div>
|
|
||||||
<div class="title">Development</div>
|
|
||||||
</div>
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
</a>
|
|
||||||
<a class="prev-page" href="tutorial.html">
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Previous</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="title">Tutorial</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer no-toc">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
244
docs/build/html/search.html
vendored
244
docs/build/html/search.html
vendored
@ -1,244 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 --><title>Search - MusicMuster 2.11.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
|
|
||||||
<noscript>
|
|
||||||
<div class="admonition error">
|
|
||||||
<p class="admonition-title">Error</p>
|
|
||||||
<p>
|
|
||||||
Please activate JavaScript to enable the search functionality.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</noscript>
|
|
||||||
|
|
||||||
<div id="search-results"></div>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer no-toc">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
|
|
||||||
<script src="_static/searchtools.js"></script>
|
|
||||||
<script src="_static/language_data.js"></script>
|
|
||||||
<script src="searchindex.js"></script></body>
|
|
||||||
</html>
|
|
||||||
1
docs/build/html/searchindex.js
vendored
1
docs/build/html/searchindex.js
vendored
@ -1 +0,0 @@
|
|||||||
Search.setIndex({"docnames": ["development", "index", "installation", "introduction", "reference", "tutorial"], "filenames": ["development.rst", "index.rst", "installation.rst", "introduction.rst", "reference.rst", "tutorial.rst"], "titles": ["Development", "Welcome to MusicMuster\u2019s documentation!", "Installation", "Introduction", "Reference", "Tutorial"], "terms": {"index": 1, "modul": 1, "search": [1, 3], "page": [1, 3], "i": 1, "music": [1, 3], "player": [1, 3], "target": 1, "product": [1, 3], "live": [1, 3], "internet": [1, 3], "radio": [1, 3], "show": [1, 3], "content": 3, "introduct": 1, "instal": [1, 3], "tutori": 1, "refer": 1, "develop": 1, "why": 1, "what": 1, "featur": 1, "requir": 1, "feedback": 1, "bug": 1, "etc": 1, "In": 3, "januari": 3, "2022": 3, "start": 3, "my": 3, "mixcloud": 3, "http": 3, "www": 3, "com": 3, "keithsmusicbox": 3, "until": 3, "had": 3, "been": 3, "an": 3, "station": 3, "which": 3, "me": 3, "us": 3, "window": 3, "playout": 3, "system": 3, "As": 3, "onli": 3, "linux": 3, "set": 3, "up": 3, "pc": 3, "specif": 3, "purpos": 3, "The": 3, "felt": 3, "were": 3, "shortcom": 3, "variou": 3, "area": 3, "onc": 3, "move": 3, "equival": 3, "didn": 3, "t": 3, "have": 3, "same": 3, "wa": 3, "unabl": 3, "find": 3, "one": 3, "met": 3, "criteria": 3, "decid": 3, "see": 3, "how": 3, "practic": 3, "would": 3, "write": 3, "own": 3, "born": 3, "It": 3, "base": 3, "whilst": 3, "could": 3, "gener": 3, "home": 3, "ar": 3, "much": 3, "better": 3, "applic": 3, "role": 3, "ha": 3, "design": 3, "support": 3, "databas": 3, "back": 3, "can": 3, "almost": 3, "entir": 3, "keyboard": 3, "driven": 3, "playlist": 3, "manag": [], "easili": [], "add": 3, "new": [], "track": 3, "multipl": 3, "tab": 3, "plai": 3, "from": 3, "ani": 3, "note": 3, "comment": 3, "automata": [], "olour": [], "code": 3, "accord": 3, "preview": 3, "befor": 3, "audienc": 3, "time": 3, "dai": 3, "clock": 3, "elaps": 3, "counter": 3, "run": 3, "fade": 3, "silent": 3, "graphic": 3, "volum": 3, "5": 3, "second": 3, "configur": 3, "abil": [], "hide": 3, "button": 3, "drop": 3, "3db": 3, "talkov": 3, "titl": 3, "artist": 3, "length": 3, "mm": 3, "ss": 3, "estim": 3, "end": 3, "when": 3, "last": 3, "bit": 3, "per": 3, "bp": [], "bitrat": 3, "silenc": 3, "record": 3, "total": 3, "arbitrari": 3, "section": 3, "command": 3, "sent": 3, "ob": 3, "studio": 3, "eg": 3, "autom": 3, "scene": 3, "chang": 3, "templat": 3, "select": 3, "unplai": 3, "between": 3, "down": [], "csv": 3, "date": 3, "automat": 3, "current": 3, "next": 3, "wikipedia": 3, "option": 3, "songfact": 3, "displai": 3, "test": 3, "debian": 3, "12": 3, "bookworm": 3, "howev": 3, "should": 3, "most": 3, "contemporari": 3, "explain": 3, "build": 3, "its": 3, "environ": 3, "automatc": 3, "all": 3, "except": 3, "version": 3, "mariadb": 3, "10": 3, "11": 3, "recent": 3, "suffic": 3, "python": 3, "3": 3, "8": 3, "later": 3, "pleas": 3, "send": 3, "keith": 3, "midnighthax": 3, "edmund": 3, "juli": 3, "2023": 3, "open": 3, "automatat": 3, "colour": 3, "lead": 3, "download": 3}, "objects": {}, "objtypes": {}, "objnames": {}, "titleterms": {"welcom": 1, "musicmust": [1, 3], "": 1, "document": 1, "indic": 1, "tabl": 1, "content": 1, "develop": 0, "instal": 2, "introduct": 3, "refer": 4, "tutori": 5, "why": 3, "what": 3, "featur": 3, "requir": 3, "feedback": 3, "bug": 3, "etc": 3, "i": 3}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 57}, "alltitles": {"Welcome to MusicMuster\u2019s documentation!": [[1, "welcome-to-musicmuster-s-documentation"]], "Contents": [[1, "contents"]], "Indices and tables": [[1, "indices-and-tables"]], "Development": [[0, "development"]], "Installation": [[2, "installation"]], "Reference": [[4, "reference"]], "Tutorial": [[5, "tutorial"]], "Introduction": [[3, "introduction"]], "Why MusicMuster?": [[3, "why-musicmuster"]], "What is MusicMuster?": [[3, "what-is-musicmuster"]], "Features": [[3, "features"]], "Requirements": [[3, "requirements"]], "Feedback, bugs, etc": [[3, "feedback-bugs-etc"]]}, "indexentries": {}})
|
|
||||||
255
docs/build/html/tutorial.html
vendored
255
docs/build/html/tutorial.html
vendored
@ -1,255 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head><meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
||||||
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Reference" href="reference.html" /><link rel="prev" title="Installation" href="installation.html" />
|
|
||||||
|
|
||||||
<!-- Generated with Sphinx 7.0.1 and Furo 2023.05.20 -->
|
|
||||||
<title>Tutorial - MusicMuster 2.11.2 documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=e6660623a769aa55fea372102b9bf3151b292993" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
--color-code-background: #f8f8f8;
|
|
||||||
--color-code-foreground: black;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media not print {
|
|
||||||
body[data-theme="dark"] {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body:not([data-theme="light"]) {
|
|
||||||
--color-code-background: #202020;
|
|
||||||
--color-code-foreground: #d0d0d0;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style></head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
||||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
||||||
<title>Contents</title>
|
|
||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
||||||
<title>Menu</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
||||||
<title>Expand</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
||||||
<polyline points="9 18 15 12 9 6"></polyline>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
||||||
<title>Light mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
||||||
<circle cx="12" cy="12" r="5"></circle>
|
|
||||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
||||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
||||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
||||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
||||||
<title>Dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
||||||
<title>Auto light/dark mode</title>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M13 12h5" />
|
|
||||||
<path d="M13 15h4" />
|
|
||||||
<path d="M13 18h1" />
|
|
||||||
<path d="M13 9h4" />
|
|
||||||
<path d="M13 6h1" />
|
|
||||||
</svg>
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
||||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
||||||
<label class="overlay sidebar-overlay" for="__navigation">
|
|
||||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
||||||
</label>
|
|
||||||
<label class="overlay toc-overlay" for="__toc">
|
|
||||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<label class="nav-overlay-icon" for="__navigation">
|
|
||||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="header-center">
|
|
||||||
<a href="index.html"><div class="brand">MusicMuster 2.11.2 documentation</div></a>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<aside class="sidebar-drawer">
|
|
||||||
<div class="sidebar-container">
|
|
||||||
|
|
||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">MusicMuster 2.11.2 documentation</span>
|
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
|
||||||
<input type="hidden" name="check_keywords" value="yes">
|
|
||||||
<input type="hidden" name="area" value="default">
|
|
||||||
</form>
|
|
||||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
||||||
<ul class="current">
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
|
||||||
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Tutorial</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="development.html">Development</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<div class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="article-container">
|
|
||||||
<a href="#" class="back-to-top muted-link">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>Back to top</span>
|
|
||||||
</a>
|
|
||||||
<div class="content-icon-container">
|
|
||||||
|
|
||||||
<div class="theme-toggle-container theme-toggle-content">
|
|
||||||
<button class="theme-toggle">
|
|
||||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
||||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
||||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
||||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
||||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
||||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<article role="main">
|
|
||||||
<section id="tutorial">
|
|
||||||
<h1>Tutorial<a class="headerlink" href="#tutorial" title="Permalink to this heading">#</a></h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
|
|
||||||
<div class="related-pages">
|
|
||||||
<a class="next-page" href="reference.html">
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Next</span>
|
|
||||||
</div>
|
|
||||||
<div class="title">Reference</div>
|
|
||||||
</div>
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
</a>
|
|
||||||
<a class="prev-page" href="installation.html">
|
|
||||||
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
|
|
||||||
<div class="page-info">
|
|
||||||
<div class="context">
|
|
||||||
<span>Previous</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="title">Installation</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-of-page">
|
|
||||||
<div class="left-details">
|
|
||||||
<div class="copyright">
|
|
||||||
Copyright © 2023, Keith Edmunds
|
|
||||||
</div>
|
|
||||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
||||||
|
|
||||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="right-details">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<aside class="toc-drawer no-toc">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
|
||||||
<script src="_static/doctools.js"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js"></script>
|
|
||||||
<script src="_static/scripts/furo.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
@ECHO OFF
|
|
||||||
|
|
||||||
pushd %~dp0
|
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
|
||||||
|
|
||||||
if "%SPHINXBUILD%" == "" (
|
|
||||||
set SPHINXBUILD=sphinx-build
|
|
||||||
)
|
|
||||||
set SOURCEDIR=source
|
|
||||||
set BUILDDIR=build
|
|
||||||
|
|
||||||
%SPHINXBUILD% >NUL 2>NUL
|
|
||||||
if errorlevel 9009 (
|
|
||||||
echo.
|
|
||||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
|
||||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
|
||||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
|
||||||
echo.may add the Sphinx directory to PATH.
|
|
||||||
echo.
|
|
||||||
echo.If you don't have Sphinx installed, grab it from
|
|
||||||
echo.https://www.sphinx-doc.org/
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:help
|
|
||||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
|
||||||
|
|
||||||
:end
|
|
||||||
popd
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
# Configuration file for the Sphinx documentation builder.
|
|
||||||
#
|
|
||||||
# For the full list of built-in configuration values, see the documentation:
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
||||||
|
|
||||||
project = 'MusicMuster'
|
|
||||||
copyright = '2023, Keith Edmunds'
|
|
||||||
author = 'Keith Edmunds'
|
|
||||||
release = '2.11.2'
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
||||||
|
|
||||||
extensions = [
|
|
||||||
'sphinx.ext.duration',
|
|
||||||
]
|
|
||||||
|
|
||||||
templates_path = ['_templates']
|
|
||||||
exclude_patterns = []
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
||||||
|
|
||||||
# html_theme = 'alabaster'
|
|
||||||
html_theme = 'furo'
|
|
||||||
html_static_path = ['_static']
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
Development
|
|
||||||
===========
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
.. MusicMuster documentation master file, created by
|
|
||||||
sphinx-quickstart on Sun Jul 2 17:58:44 2023.
|
|
||||||
|
|
||||||
Welcome to MusicMuster's documentation!
|
|
||||||
=======================================
|
|
||||||
|
|
||||||
**MusicMuster** is a music player targeted at the production of live
|
|
||||||
internet radio shows.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
introduction
|
|
||||||
installation
|
|
||||||
tutorial
|
|
||||||
reference
|
|
||||||
development
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
Installation
|
|
||||||
============
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
Introduction
|
|
||||||
============
|
|
||||||
|
|
||||||
Why MusicMuster?
|
|
||||||
----------------
|
|
||||||
|
|
||||||
In January 2022 I started my show on `Mixcloud
|
|
||||||
<https://www.mixcloud.com/KeithsMusicBox/>`. Until then, my show had
|
|
||||||
been on an internet radio station which required me to use a Windows
|
|
||||||
playout system. As I only use Linux, I had to set up a Windows PC
|
|
||||||
specifically for that purpose. The system I had to use had what I felt
|
|
||||||
were shortcomings in various areas.
|
|
||||||
|
|
||||||
Once I moved to Mixcloud I searched for a Linux equivalent that didn't
|
|
||||||
have the same shortcomings but was unable to find one that met my
|
|
||||||
criteria. I decided to see how practical it would be to write my own,
|
|
||||||
and MusicMuster was born.
|
|
||||||
|
|
||||||
What is MusicMuster?
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
It is a Linux-based music player. Whilst it could be used as a general
|
|
||||||
home music player, there are much better applications for that role.
|
|
||||||
**MusicMuster** has been specifically designed to support the
|
|
||||||
production of live internet radio shows.
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
* Database backed
|
|
||||||
* Can be almost entirely keyboard driven
|
|
||||||
* Open multiple playlists in tabs
|
|
||||||
* Play tracks from any playlist
|
|
||||||
* Add notes/comments to tracks on playlist
|
|
||||||
* Automatatic colour-coding of notes/comments according to content
|
|
||||||
* Preview tracks before playing to audience
|
|
||||||
* Time of day clock
|
|
||||||
* Elapsed track time counter
|
|
||||||
* Time to run until track starts to fade
|
|
||||||
* Time to run until track is silent
|
|
||||||
* Graphic of volume from 5 seconds (configurable) before fade until
|
|
||||||
track is silent
|
|
||||||
* Optionally hide played tracks in playlist
|
|
||||||
* Button to drop playout volume by 3dB for talkover
|
|
||||||
* Playlist displays:
|
|
||||||
* Title
|
|
||||||
* Artist
|
|
||||||
* Length of track (mm:ss)
|
|
||||||
* Estimated start time of track
|
|
||||||
* Estimated end time of track
|
|
||||||
* When track was last played
|
|
||||||
* Bits per second (bitrate) of track
|
|
||||||
* Length of leading silence in recording before track starts
|
|
||||||
* Total track length of arbitrary sections of tracks
|
|
||||||
* Commands that are sent to OBS Studio (eg, for automated scene
|
|
||||||
changes)
|
|
||||||
* Playlist templates
|
|
||||||
* Move selected or unplayed tracks between playlists
|
|
||||||
* Download CSV of tracks played between arbitrary dates/times
|
|
||||||
* Search for tracks by title or artist
|
|
||||||
* Automatic search of current and next track in Wikipedia
|
|
||||||
* Optional search of any track in Wikipedia
|
|
||||||
* Optional search of any track in Songfacts
|
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. note:: MusicMuster has only been tested on Debian 12, "Bookworm";
|
|
||||||
however, it should run on most contemporary Linux systems.
|
|
||||||
|
|
||||||
The :doc:`installation` page explains how to build MusicMuster in its
|
|
||||||
own environment which will automatcally install all requirements
|
|
||||||
except the database. The current version of MusicMuster uses MariaDB
|
|
||||||
version 10.11; however, any recent version of MariaDB should suffice.
|
|
||||||
|
|
||||||
MusicMuster is a Python 3 application and requires Python 3.8 or
|
|
||||||
later.
|
|
||||||
|
|
||||||
|
|
||||||
Feedback, bugs, etc
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Please send to keith@midnighthax.com
|
|
||||||
|
|
||||||
Keith Edmunds,
|
|
||||||
July 2023
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
Reference
|
|
||||||
=========
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
Tutorial
|
|
||||||
========
|
|
||||||
1231
poetry.lock
generated
1231
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -40,10 +40,6 @@ mypy = "^0.991"
|
|||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pudb = "^2022.1.3"
|
pudb = "^2022.1.3"
|
||||||
sphinx = "^7.0.1"
|
|
||||||
furo = "^2023.5.20"
|
|
||||||
black = "^23.3.0"
|
|
||||||
flakehell = "^0.9.0"
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
|||||||
7
test.py
7
test.py
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
from PyQt5 import QtGui, QtWidgets
|
from PyQt5 import QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class TabBar(QtWidgets.QTabBar):
|
class TabBar(QtWidgets.QTabBar):
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event):
|
||||||
painter = QtWidgets.QStylePainter(self)
|
painter = QtWidgets.QStylePainter(self)
|
||||||
@ -14,18 +13,16 @@ class TabBar(QtWidgets.QTabBar):
|
|||||||
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, option)
|
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, option)
|
||||||
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, option)
|
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, option)
|
||||||
|
|
||||||
|
|
||||||
class Window(QtWidgets.QTabWidget):
|
class Window(QtWidgets.QTabWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtWidgets.QTabWidget.__init__(self)
|
QtWidgets.QTabWidget.__init__(self)
|
||||||
self.setTabBar(TabBar(self))
|
self.setTabBar(TabBar(self))
|
||||||
for color in "tomato orange yellow lightgreen skyblue plum".split():
|
for color in 'tomato orange yellow lightgreen skyblue plum'.split():
|
||||||
self.addTab(QtWidgets.QWidget(self), color)
|
self.addTab(QtWidgets.QWidget(self), color)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
window = Window()
|
window = Window()
|
||||||
window.resize(420, 200)
|
window.resize(420, 200)
|
||||||
|
|||||||
@ -1,12 +1,7 @@
|
|||||||
|
from config import Config
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from helpers import (
|
from helpers import *
|
||||||
fade_point,
|
from models import Tracks
|
||||||
get_audio_segment,
|
|
||||||
get_tags,
|
|
||||||
get_relative_date,
|
|
||||||
leading_silence,
|
|
||||||
ms_to_mmss,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_fade_point():
|
def test_fade_point():
|
||||||
@ -23,8 +18,8 @@ def test_fade_point():
|
|||||||
testdata = eval(f.read())
|
testdata = eval(f.read())
|
||||||
|
|
||||||
# Volume detection can vary, so ± 1 second is OK
|
# Volume detection can vary, so ± 1 second is OK
|
||||||
assert fade_at < testdata["fade_at"] + 1000
|
assert fade_at < testdata['fade_at'] + 1000
|
||||||
assert fade_at > testdata["fade_at"] - 1000
|
assert fade_at > testdata['fade_at'] - 1000
|
||||||
|
|
||||||
|
|
||||||
def test_get_tags():
|
def test_get_tags():
|
||||||
@ -37,8 +32,8 @@ def test_get_tags():
|
|||||||
with open(test_track_data) as f:
|
with open(test_track_data) as f:
|
||||||
testdata = eval(f.read())
|
testdata = eval(f.read())
|
||||||
|
|
||||||
assert tags["artist"] == testdata["artist"]
|
assert tags['artist'] == testdata['artist']
|
||||||
assert tags["title"] == testdata["title"]
|
assert tags['title'] == testdata['title']
|
||||||
|
|
||||||
|
|
||||||
def test_get_relative_date():
|
def test_get_relative_date():
|
||||||
@ -49,7 +44,8 @@ def test_get_relative_date():
|
|||||||
eight_days_ago = today_at_10 - timedelta(days=8)
|
eight_days_ago = today_at_10 - timedelta(days=8)
|
||||||
assert get_relative_date(eight_days_ago, today_at_11) == "1 week, 1 day ago"
|
assert get_relative_date(eight_days_ago, today_at_11) == "1 week, 1 day ago"
|
||||||
sixteen_days_ago = today_at_10 - timedelta(days=16)
|
sixteen_days_ago = today_at_10 - timedelta(days=16)
|
||||||
assert get_relative_date(sixteen_days_ago, today_at_11) == "2 weeks, 2 days ago"
|
assert get_relative_date(
|
||||||
|
sixteen_days_ago, today_at_11) == "2 weeks, 2 days ago"
|
||||||
|
|
||||||
|
|
||||||
def test_leading_silence():
|
def test_leading_silence():
|
||||||
@ -66,8 +62,8 @@ def test_leading_silence():
|
|||||||
testdata = eval(f.read())
|
testdata = eval(f.read())
|
||||||
|
|
||||||
# Volume detection can vary, so ± 1 second is OK
|
# Volume detection can vary, so ± 1 second is OK
|
||||||
assert silence_at < testdata["leading_silence"] + 1000
|
assert silence_at < testdata['leading_silence'] + 1000
|
||||||
assert silence_at > testdata["leading_silence"] - 1000
|
assert silence_at > testdata['leading_silence'] - 1000
|
||||||
|
|
||||||
|
|
||||||
def test_ms_to_mmss():
|
def test_ms_to_mmss():
|
||||||
|
|||||||
@ -135,6 +135,7 @@ def test_playdates_remove_track(session):
|
|||||||
track_path = "/a/b/c"
|
track_path = "/a/b/c"
|
||||||
track = Tracks(session, track_path)
|
track = Tracks(session, track_path)
|
||||||
|
|
||||||
|
playdate = Playdates(session, track.id)
|
||||||
Playdates.remove_track(session, track.id)
|
Playdates.remove_track(session, track.id)
|
||||||
|
|
||||||
last_played = Playdates.last_played(session, track.id)
|
last_played = Playdates.last_played(session, track.id)
|
||||||
@ -148,8 +149,10 @@ def test_playlist_create(session):
|
|||||||
|
|
||||||
def test_playlist_add_note(session):
|
def test_playlist_add_note(session):
|
||||||
note_text = "my note"
|
note_text = "my note"
|
||||||
|
note_row = 2
|
||||||
|
|
||||||
playlist = Playlists(session, "my playlist")
|
playlist = Playlists(session, "my playlist")
|
||||||
|
note = playlist.add_note(session, note_row, note_text)
|
||||||
|
|
||||||
assert len(playlist.notes) == 1
|
assert len(playlist.notes) == 1
|
||||||
playlist_note = playlist.notes[0]
|
playlist_note = playlist.notes[0]
|
||||||
@ -353,7 +356,9 @@ def test_tracks_get_all_paths(session):
|
|||||||
def test_tracks_get_all_tracks(session):
|
def test_tracks_get_all_tracks(session):
|
||||||
# Need two tracks
|
# Need two tracks
|
||||||
track1_path = "/a/b/c"
|
track1_path = "/a/b/c"
|
||||||
|
track1 = Tracks(session, track1_path)
|
||||||
track2_path = "/m/n/o"
|
track2_path = "/m/n/o"
|
||||||
|
track2 = Tracks(session, track2_path)
|
||||||
|
|
||||||
result = Tracks.get_all_tracks(session)
|
result = Tracks.get_all_tracks(session)
|
||||||
assert track1_path in [a.path for a in result]
|
assert track1_path in [a.path for a in result]
|
||||||
@ -364,7 +369,9 @@ def test_tracks_by_filename(session):
|
|||||||
track1_path = "/a/b/c"
|
track1_path = "/a/b/c"
|
||||||
|
|
||||||
track1 = Tracks(session, track1_path)
|
track1 = Tracks(session, track1_path)
|
||||||
assert Tracks.get_by_filename(session, os.path.basename(track1_path)) is track1
|
assert Tracks.get_by_filename(
|
||||||
|
session, os.path.basename(track1_path)
|
||||||
|
) is track1
|
||||||
|
|
||||||
|
|
||||||
def test_tracks_by_path(session):
|
def test_tracks_by_path(session):
|
||||||
@ -396,24 +403,26 @@ def test_tracks_rescan(session):
|
|||||||
# Re-read the track
|
# Re-read the track
|
||||||
track_read = Tracks.get_by_path(session, test_track_path)
|
track_read = Tracks.get_by_path(session, test_track_path)
|
||||||
|
|
||||||
assert track_read.duration == testdata["duration"]
|
assert track_read.duration == testdata['duration']
|
||||||
assert track_read.start_gap == testdata["leading_silence"]
|
assert track_read.start_gap == testdata['leading_silence']
|
||||||
# Silence detection can vary, so ± 1 second is OK
|
# Silence detection can vary, so ± 1 second is OK
|
||||||
assert track_read.fade_at < testdata["fade_at"] + 1000
|
assert track_read.fade_at < testdata['fade_at'] + 1000
|
||||||
assert track_read.fade_at > testdata["fade_at"] - 1000
|
assert track_read.fade_at > testdata['fade_at'] - 1000
|
||||||
assert track_read.silence_at < testdata["trailing_silence"] + 1000
|
assert track_read.silence_at < testdata['trailing_silence'] + 1000
|
||||||
assert track_read.silence_at > testdata["trailing_silence"] - 1000
|
assert track_read.silence_at > testdata['trailing_silence'] - 1000
|
||||||
|
|
||||||
|
|
||||||
def test_tracks_remove_by_path(session):
|
def test_tracks_remove_by_path(session):
|
||||||
track1_path = "/a/b/c"
|
track1_path = "/a/b/c"
|
||||||
|
|
||||||
|
track1 = Tracks(session, track1_path)
|
||||||
assert len(Tracks.get_all_tracks(session)) == 1
|
assert len(Tracks.get_all_tracks(session)) == 1
|
||||||
Tracks.remove_by_path(session, track1_path)
|
Tracks.remove_by_path(session, track1_path)
|
||||||
assert len(Tracks.get_all_tracks(session)) == 0
|
assert len(Tracks.get_all_tracks(session)) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_tracks_search_artists(session):
|
def test_tracks_search_artists(session):
|
||||||
|
|
||||||
track1_path = "/a/b/c"
|
track1_path = "/a/b/c"
|
||||||
track1_artist = "Artist One"
|
track1_artist = "Artist One"
|
||||||
track1 = Tracks(session, track1_path)
|
track1 = Tracks(session, track1_path)
|
||||||
@ -426,6 +435,7 @@ def test_tracks_search_artists(session):
|
|||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
x = Tracks.get_all_tracks(session)
|
||||||
artist_first_word = track1_artist.split()[0].lower()
|
artist_first_word = track1_artist.split()[0].lower()
|
||||||
assert len(Tracks.search_artists(session, artist_first_word)) == 2
|
assert len(Tracks.search_artists(session, artist_first_word)) == 2
|
||||||
assert len(Tracks.search_artists(session, track1_artist)) == 1
|
assert len(Tracks.search_artists(session, track1_artist)) == 1
|
||||||
@ -444,6 +454,7 @@ def test_tracks_search_titles(session):
|
|||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
x = Tracks.get_all_tracks(session)
|
||||||
title_first_word = track1_title.split()[0].lower()
|
title_first_word = track1_title.split()[0].lower()
|
||||||
assert len(Tracks.search_titles(session, title_first_word)) == 2
|
assert len(Tracks.search_titles(session, title_first_word)) == 2
|
||||||
assert len(Tracks.search_titles(session, track1_title)) == 1
|
assert len(Tracks.search_titles(session, track1_title)) == 1
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from PyQt5.QtCore import Qt
|
|||||||
from app import playlists
|
from app import playlists
|
||||||
from app import models
|
from app import models
|
||||||
from app import musicmuster
|
from app import musicmuster
|
||||||
|
from app import dbconfig
|
||||||
|
|
||||||
|
|
||||||
def seed2tracks(session):
|
def seed2tracks(session):
|
||||||
@ -77,6 +78,7 @@ def test_save_and_restore(qtbot, session):
|
|||||||
|
|
||||||
|
|
||||||
def test_meta_all_clear(qtbot, session):
|
def test_meta_all_clear(qtbot, session):
|
||||||
|
|
||||||
# Create playlist
|
# Create playlist
|
||||||
playlist = models.Playlists(session, "my playlist")
|
playlist = models.Playlists(session, "my playlist")
|
||||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||||
@ -105,6 +107,7 @@ def test_meta_all_clear(qtbot, session):
|
|||||||
|
|
||||||
|
|
||||||
def test_meta(qtbot, session):
|
def test_meta(qtbot, session):
|
||||||
|
|
||||||
# Create playlist
|
# Create playlist
|
||||||
playlist = playlists.Playlists(session, "my playlist")
|
playlist = playlists.Playlists(session, "my playlist")
|
||||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||||
@ -138,7 +141,7 @@ def test_meta(qtbot, session):
|
|||||||
|
|
||||||
# Add a note
|
# Add a note
|
||||||
note_text = "my note"
|
note_text = "my note"
|
||||||
note_row = 7 # will be added as row 3
|
note_row = 7 # will be added as row 3
|
||||||
note = models.Notes(session, playlist.id, note_row, note_text)
|
note = models.Notes(session, playlist.id, note_row, note_text)
|
||||||
playlist_tab._insert_note(session, note)
|
playlist_tab._insert_note(session, note)
|
||||||
|
|
||||||
@ -208,6 +211,7 @@ def test_clear_next(qtbot, session):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_selected_row(qtbot, monkeypatch, session):
|
def test_get_selected_row(qtbot, monkeypatch, session):
|
||||||
|
|
||||||
monkeypatch.setattr(musicmuster, "Session", session)
|
monkeypatch.setattr(musicmuster, "Session", session)
|
||||||
monkeypatch.setattr(playlists, "Session", session)
|
monkeypatch.setattr(playlists, "Session", session)
|
||||||
|
|
||||||
@ -232,12 +236,15 @@ def test_get_selected_row(qtbot, monkeypatch, session):
|
|||||||
row0_item0 = playlist_tab.item(0, 0)
|
row0_item0 = playlist_tab.item(0, 0)
|
||||||
assert row0_item0 is not None
|
assert row0_item0 is not None
|
||||||
rect = playlist_tab.visualItemRect(row0_item0)
|
rect = playlist_tab.visualItemRect(row0_item0)
|
||||||
qtbot.mouseClick(playlist_tab.viewport(), Qt.LeftButton, pos=rect.center())
|
qtbot.mouseClick(
|
||||||
|
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
||||||
|
)
|
||||||
row_number = playlist_tab.get_selected_row()
|
row_number = playlist_tab.get_selected_row()
|
||||||
assert row_number == 0
|
assert row_number == 0
|
||||||
|
|
||||||
|
|
||||||
def test_set_next(qtbot, monkeypatch, session):
|
def test_set_next(qtbot, monkeypatch, session):
|
||||||
|
|
||||||
monkeypatch.setattr(musicmuster, "Session", session)
|
monkeypatch.setattr(musicmuster, "Session", session)
|
||||||
monkeypatch.setattr(playlists, "Session", session)
|
monkeypatch.setattr(playlists, "Session", session)
|
||||||
seed2tracks(session)
|
seed2tracks(session)
|
||||||
@ -267,11 +274,14 @@ def test_set_next(qtbot, monkeypatch, session):
|
|||||||
row0_item2 = playlist_tab.item(0, 2)
|
row0_item2 = playlist_tab.item(0, 2)
|
||||||
assert row0_item2 is not None
|
assert row0_item2 is not None
|
||||||
rect = playlist_tab.visualItemRect(row0_item2)
|
rect = playlist_tab.visualItemRect(row0_item2)
|
||||||
qtbot.mouseClick(playlist_tab.viewport(), Qt.LeftButton, pos=rect.center())
|
qtbot.mouseClick(
|
||||||
|
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
||||||
|
)
|
||||||
selected_title = playlist_tab.get_selected_title()
|
selected_title = playlist_tab.get_selected_title()
|
||||||
assert selected_title == track1_title
|
assert selected_title == track1_title
|
||||||
|
|
||||||
qtbot.keyPress(playlist_tab.viewport(), "N", modifier=Qt.ControlModifier)
|
qtbot.keyPress(playlist_tab.viewport(), "N",
|
||||||
|
modifier=Qt.ControlModifier)
|
||||||
qtbot.wait(1000)
|
qtbot.wait(1000)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
24
tree.py
24
tree.py
@ -9,10 +9,9 @@ datas = {
|
|||||||
],
|
],
|
||||||
"No Category": [
|
"No Category": [
|
||||||
("New Game", "Playnite", "", "", "Never", "Not Plated", ""),
|
("New Game", "Playnite", "", "", "Never", "Not Plated", ""),
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GroupDelegate(QtWidgets.QStyledItemDelegate):
|
class GroupDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(GroupDelegate, self).__init__(parent)
|
super(GroupDelegate, self).__init__(parent)
|
||||||
@ -26,7 +25,6 @@ class GroupDelegate(QtWidgets.QStyledItemDelegate):
|
|||||||
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
|
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
|
||||||
option.icon = self._minus_icon if is_open else self._plus_icon
|
option.icon = self._minus_icon if is_open else self._plus_icon
|
||||||
|
|
||||||
|
|
||||||
class GroupView(QtWidgets.QTreeView):
|
class GroupView(QtWidgets.QTreeView):
|
||||||
def __init__(self, model, parent=None):
|
def __init__(self, model, parent=None):
|
||||||
super(GroupView, self).__init__(parent)
|
super(GroupView, self).__init__(parent)
|
||||||
@ -50,18 +48,7 @@ class GroupModel(QtGui.QStandardItemModel):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(GroupModel, self).__init__(parent)
|
super(GroupModel, self).__init__(parent)
|
||||||
self.setColumnCount(8)
|
self.setColumnCount(8)
|
||||||
self.setHorizontalHeaderLabels(
|
self.setHorizontalHeaderLabels(["", "Name", "Library", "Release Date", "Genre(s)", "Last Played", "Time Played", ""])
|
||||||
[
|
|
||||||
"",
|
|
||||||
"Name",
|
|
||||||
"Library",
|
|
||||||
"Release Date",
|
|
||||||
"Genre(s)",
|
|
||||||
"Last Played",
|
|
||||||
"Time Played",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
for i in range(self.columnCount()):
|
for i in range(self.columnCount()):
|
||||||
it = self.horizontalHeaderItem(i)
|
it = self.horizontalHeaderItem(i)
|
||||||
it.setForeground(QtGui.QColor("#F2F2F2"))
|
it.setForeground(QtGui.QColor("#F2F2F2"))
|
||||||
@ -97,7 +84,7 @@ class GroupModel(QtGui.QStandardItemModel):
|
|||||||
item.setEditable(False)
|
item.setEditable(False)
|
||||||
item.setBackground(QtGui.QColor("#0D1225"))
|
item.setBackground(QtGui.QColor("#0D1225"))
|
||||||
item.setForeground(QtGui.QColor("#F2F2F2"))
|
item.setForeground(QtGui.QColor("#F2F2F2"))
|
||||||
group_item.setChild(j, i + 1, item)
|
group_item.setChild(j, i+1, item)
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
@ -113,12 +100,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
for children in childrens:
|
for children in childrens:
|
||||||
model.append_element_to_group(group_item, children)
|
model.append_element_to_group(group_item, children)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
if __name__ == "__main__":
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
w = MainWindow()
|
w = MainWindow()
|
||||||
w.resize(720, 240)
|
w.resize(720, 240)
|
||||||
w.show()
|
w.show()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|||||||
1
web.py
1
web.py
@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import sys
|
import sys
|
||||||
|
from pprint import pprint
|
||||||
from PyQt6.QtWidgets import QApplication, QLabel
|
from PyQt6.QtWidgets import QApplication, QLabel
|
||||||
from PyQt6.QtGui import QColor, QPalette
|
from PyQt6.QtGui import QColor, QPalette
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user