Music production session manager
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
5.8 KiB

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
Copyright 2022, Nils Hilbricht, Germany ( )
This file is part of the Laborejo Software Suite ( ).
This application is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <>.
import logging; logger = logging.getLogger(__name__);"import")
#Standard Library
import pathlib
from os import getenv, listdir
from sys import argv as sysargv
#Third Party
from PyQt5 import QtCore, QtGui, QtWidgets
#Our own files
from .movesessionroot import nsmVersionGreater160
def checkForRunningNsmd(qtApp, PATHS:dict):
"""Before we start the engine with our own nsmd server:
nsmd >= 1.6.0 introduced run file discovery of sessions and empty nsmd.
Ask the user if they want to connect to one of these.
Injects the path into PATHS["url"], which is used by engine.api.startEngine()
if PATHS["url"] or any("url" in param for param in sysargv): #this is really the same test twice...
#This is not our problem anymore.
if not nsmVersionGreater160():
return #see docstring
parent = qtApp.desktop()
if not getenv("XDG_RUNTIME_DIR"):
logger.warning("Your system has no environment variable $XDG_RUNTIME_DIR. That is unusual for Linux. Automatic detection of already running sessions deactivated.")
return #On Linux that should exist. But if not, no reason to crash."Detecting if there are already nsmd or sessions running under this user")
session_rundir = pathlib.Path(getenv("XDG_RUNTIME_DIR"), "nsm")"Supposed nsmd session rundir: {session_rundir}")
nsmd_rundir = pathlib.Path(getenv("XDG_RUNTIME_DIR"), "nsm", "d")"Supposed nsmd server rundir: {nsmd_rundir}")
if not session_rundir.exists() or not session_rundir.is_dir():"nsmd rundir does not exist. Continue.")
existing_daemons = listdir(nsmd_rundir)
existing_sessions = listdir(session_rundir)
if existing_sessions:
existing_sessions.remove("d") #Remove the daemon subdir
if not existing_daemons:"nsmd rundir exist, but is empty. Continue.")
return #if there are no daemons there are no sessions.
#There are nsmd running under this user, maybe even open sessions.
#Now build a list for the user to choose from.
#If a session is running on an nsmd show this, otherwise show the empty nsmd
sessions = []
nsmd_pids = set()
for s in existing_sessions:
res = {}
with open(pathlib.Path(session_rundir, s), "r") as f:
res["hashName"] = s #name of the lockfile. has hash postfix.
res["path"] = pathlib.Path(f.readline().strip("\n")) #file path in ~/.local/share/nsm
res["name"] = res["path"].name #present this to the user.
res["url"] = f.readline().strip("\n") #nsmd url
res["pid"] = int(f.readline().strip("\n")) #nsmd pid
daemons = []
for d in existing_daemons:
#d is a file with a PID as name. e.g. 9627
#inside is the NSM_URL.
if int(d) in nsmd_pids: #we already have a running session on this server
else: #empty nsmd.
res = {}
with open(pathlib.Path(nsmd_rundir, d), "r") as f:
res["pid"] = d
res["url"] = f.readline().strip("\n") #just the first line
res["name"] = QtCore.QCoreApplication.translate("StartChooseRunningSession", "Empty Server") + " " + res["url"]
#We now have all empty nsmd in var daemons
assert sessions or daemons
PATHS["url"] = ChooseSessionWidget(qtApp, sessions+daemons).url #"Global Variable"
class ChooseSessionWidget(QtWidgets.QDialog):
"""return value in self.url. Might be None"""
def __init__(self, qtApp, sessionDicts:list):
super().__init__() #QDialog can't parent to qtApp
self.qtApp = qtApp
self.setModal(True) #block until closed
self.layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel(self)
self.label.setText(QtCore.QCoreApplication.translate("StartChooseRunningSession", "Select session or server to connect to.\nCancel to start our own server."))
self.comboBox = QtWidgets.QComboBox(self)
for s in sessionDicts:
self.comboBox.addItem(s["name"], s["url"]) #Show name, use url as data.
self.buttonBox = QtWidgets.QDialogButtonBox(self)
def accept(self):
self.url = self.comboBox.currentData() #easy abstraction so that the caller does not need to know our widget name
def reject(self):
self.url = None