From e1712e33c739cb35a59b5c6d6ea656d4623c0d4c Mon Sep 17 00:00:00 2001
From: Nils <>
Date: Mon, 7 Mar 2022 19:14:33 +0100
Subject: [PATCH] Prepare session dir for upcoming nsmd XDG change
---
qtgui/mainwindow.py | 5 +-
qtgui/movesessionroot.py | 160 +++++++++++++++++++++++++++++++++++++++
2 files changed, 163 insertions(+), 2 deletions(-)
create mode 100644 qtgui/movesessionroot.py
diff --git a/qtgui/mainwindow.py b/qtgui/mainwindow.py
index 3e1ce33..3f72063 100644
--- a/qtgui/mainwindow.py
+++ b/qtgui/mainwindow.py
@@ -22,7 +22,6 @@ along with this program. If not, see .
import logging; logger = logging.getLogger(__name__); logger.info("import")
#Standard Library
-from sys import argv as sysargv
from sys import exit as sysexit
#Third Party
@@ -50,7 +49,7 @@ from .waitdialog import WaitDialog
from .resources import *
from .settings import SettingsDialog
from .jacktransport import JackTransportControls
-
+from .movesessionroot import xdgVersionChange
api.eventLoop = EventLoop()
@@ -164,6 +163,8 @@ class MainWindow(QtWidgets.QMainWindow):
self.ui.stack_loaded_session.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.stack_loaded_session.customContextMenuRequested.connect(self.customContextMenu)
+ #nsmd 1.6.0
+ xdgVersionChange(self.qtApp)
#Api Callbacks
api.callbacks.sessionClosed.append(self.reactCallback_sessionClosed)
diff --git a/qtgui/movesessionroot.py b/qtgui/movesessionroot.py
new file mode 100644
index 0000000..775d106
--- /dev/null
+++ b/qtgui/movesessionroot.py
@@ -0,0 +1,160 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This file is part of the Laborejo Software Suite ( https://www.laborejo.org ).
+
+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
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+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__); logger.info("import")
+
+#Standard Library
+import pathlib
+from os import getenv, listdir, access, W_OK, R_OK, rmdir
+from subprocess import check_output
+from shutil import move as shutil_move
+from sys import exit as sysexit
+from sys import argv as sysargv
+from uuid import uuid4
+
+
+#Third Party
+from PyQt5 import QtCore, QtGui, QtWidgets
+logger.info(f"PyQt Version: {QtCore.PYQT_VERSION_STR}")
+
+
+def nsmVersionGreater160()->bool:
+ maj, min, patch = check_output(["nsmd", "--version"]).decode().split(" ")[1].rstrip("\n").split(".")
+ maj = int(maj)
+ min = int(min)
+
+ if maj <= 1 and min < 6:
+ return False
+ else:
+ return True
+
+def xdgVersionChange(qtApp):
+ """Before we start the engine:
+ nsmd >= 1.6.0 changed the default session root from ~/NSM Sessions to XDG.
+ It will still prioritize the old path, if found.
+ We ask the user to move his files to the correct location now so that the engine can use it this run.
+
+ This check can be made independently from any --session-root parameter and without nsmd started.
+ """
+
+
+ if any("--session-root" in param for param in sysargv):
+ #This is not our problem anymore.
+ return
+
+ if not nsmVersionGreater160():
+ return #see docstring
+
+ parent = qtApp.desktop()
+
+ try:
+ oldexists = pathlib.Path("~/NSM Sessions").expanduser().exists()
+ except RuntimeError: #no home dir!
+ oldexists = False
+
+ xdgdatahome = getenv("$XDG_DATA_HOME")
+ if not xdgdatahome:
+ try:
+ xdgdatahome = pathlib.Path("~/.local/share").expanduser()
+ except RuntimeError: #If there is no xdg env and no home dir let us crash and exit.
+ logger.error("There is neither $XDG_DATA_HOME nor a home directory! Cannot start in this environment")
+ #self._callSysExit(). Do not use this because it stores window settings, in a settings dir which might not be available under the circumstances above.
+ sysexit(0) #directly afterwards @atexit is handled, but this function does not return.
+
+ if not oldexists:
+ return
+
+ olddir = pathlib.Path("~/NSM Sessions").expanduser()
+ if not olddir.is_dir():
+ logger.error(f"{olddir} exists but it not a directory. Cannot continue with moving session directories. Please handle it yourself.")
+ errorString = QtCore.QCoreApplication.translate("movesessionroot", "{} exists but it not a directory. Cannot continue with moving session directories. Please handle it yourself.".format(olddir))
+ QtWidgets.QMessageBox.critical(parent, errorString)
+ return
+ newdir = pathlib.Path(xdgdatahome, "nsm")
+ newexists = newdir.exists()
+
+
+ title = QtCore.QCoreApplication.translate("movesessionroot", "Default session directory changed")
+ if newexists: #Must merge.
+ text = QtCore.QCoreApplication.translate("movesessionroot", "Detected both the new NSM 1.6.0 official session directory:\n{}\nAND the old one:\n{}\n\nDo you want Agordejo to move your old files and try to merge them with the new sessions? (Recommended)\n\nYou now have the chance to manually make a backup of your files before pressing 'Yes'".format(newdir, olddir))
+ else: #Simply move.
+ text = QtCore.QCoreApplication.translate("movesessionroot", "With NSM version 1.6.0 the official session directory moved to:\n{}\nYou are still using the old one:\n\n{}\nDo you want Agordejo to move your files? (Recommended)\n\nYou now have the chance to manually make a backup of your files before pressing 'Yes'".format(newdir, olddir))
+
+ userAllowedMove = QtWidgets.QMessageBox.warning(parent, title, text, QtWidgets.QMessageBox.Yes|QtWidgets.QMessageBox.No)
+ if not userAllowedMove == QtWidgets.QMessageBox.Yes:
+ return
+
+ #Test for read and write permissions
+ if not access(olddir, R_OK):
+ logger.error(f"{olddir} doesn't have READ permissions. Cannot continue with moving session directories. Please handle it yourself.")
+ t = QtCore.QCoreApplication.translate("movesessionroot", "{} doesn't have READ permissions.\nCannot continue moving session directories. Please handle it yourself.".format(olddir))
+ QtWidgets.QMessageBox.critical(parent, title, t, QtWidgets.QMessageBox.Ok)
+ return
+ if not access(olddir, W_OK):
+ logger.error(f"{olddir} doesn't have WRITE permissions. Cannot continue with moving session directories. Please handle it yourself.")
+ t = QtCore.QCoreApplication.translate("movesessionroot", "{} doesn't have WRITE permissions.\nCannot continue moving session directories. Please handle it yourself.".format(olddir))
+ QtWidgets.QMessageBox.critical(parent, title, t, QtWidgets.QMessageBox.Ok)
+ return
+ if newexists and not access(newdir, R_OK):
+ logger.error(f"{newdir} doesn't have READ permissions. Cannot continue with moving session directories. Please handle it yourself.")
+ t = QtCore.QCoreApplication.translate("movesessionroot", "{} doesn't have READ permissions.\nCannot continue moving session directories. Please handle it yourself.".format(newdir))
+ QtWidgets.QMessageBox.critical(parent, title, t, QtWidgets.QMessageBox.Ok)
+ return
+ if newexists and not access(newdir, W_OK):
+ logger.error(f"{newdir} doesn't have WRITE permissions. Cannot continue with moving session directories. Please handle it yourself.")
+ t = QtCore.QCoreApplication.translate("movesessionroot", "{} doesn't have WRITE permissions.\nCannot continue moving session directories. Please handle it yourself.".format(newdir))
+ QtWidgets.QMessageBox.critical(parent, title, t, QtWidgets.QMessageBox.Ok)
+ return
+
+ #Tests done. Attempting to actually move the dir.
+
+ if newexists: #We need to merge two directories
+ if not newdir.is_dir():
+ logger.error(f"{newdir} exists but it not a directory. Cannot continue with moving session directories. Please handle it yourself.")
+ t = QtCore.QCoreApplication.translate("movesessionroot", "{} exists but it not a directory.\nCannot continue moving session directories. Please handle it yourself.".format(newdir))
+ QtWidgets.QMessageBox.critical(parent, title, t, QtWidgets.QMessageBox.Ok)
+ return
+
+ #Are the file conflicts?
+ oldlist = listdir(olddir)
+ newlist = listdir(newdir)
+ if set(oldlist).intersection( set(newlist) ): #Same sessions in both directory. We take the easy way out.
+ unique_newdir = pathlib.Path(newdir, "backup-" + str(uuid4()))
+ logger.info(f"Moving and renaming {olddir} to {unique_newdir} as new session root")
+ shutil_move(olddir, unique_newdir) #this moves and renames the old ~/NSM Sessions. It will disappear from the home dir.
+ else: #no. we can just move. But we need to move the contents, not the olddir itself into the newdir
+ for oldsession in oldlist:
+ shutil_move(pathlib.Path(olddir, oldsession), newdir) #this moves and renames the old ~/NSM Sessions. It will disappear from the home dir.
+
+ else: #the new directory does not exist yet. We can move it.
+ logger.info(f"Moving and renaming {olddir} to {newdir} as new session root")
+ shutil_move(olddir, newdir) #this moves and renames the old ~/NSM Sessions. It will disappear from the home dir.
+
+ #If for some reason the old dir is empty and still exists, do this:
+ if olddir.exists():
+ try:
+ rmdir(olddir) #will raise OSError if not empty.
+ except OSError:
+ logger.error(f"We tried to move and merge {olddir} to {newdir} but afterwards the source directory was not empty. There is nothing we can do here. Please handle it yourself. Please look out for incomplete data.")
+ t = QtCore.QCoreApplication.translate("movesessionroot", "We tried to move and merge {} to {} but afterwards the source directory was not empty. There is nothing we can do here. Please handle it yourself. Please look out for incomplete data.".format(olddir, newdir))
+ QtWidgets.QMessageBox.critical(parent, title, t, QtWidgets.QMessageBox.Ok)
+ return