Open seanwestfall opened 2 months ago
The server portion of Drawpile does not deal with the drawing protocol, so it providing a function to create a session doesn't make much sense architecturally. I mean, you could create a session in theory, but it would have a canvas with size zero and no layers, which is not spectacularly useful.
But the way you're supposed to solve this is using session templates. You provide files with metadata like the session title and whatnot, plus a recording of the initial state taken from a client. You can tell the server a directory to scan for those templates and it will act like those templated sessions always exist and will automatically reify them when someone join them. The session will then keep running according to the idle timeout and persistence rules of the server afterwards. Once it is terminated by any means, it returns to its slumbering state and will be revived from the template once someone joins it.
Does that solve your use case? Otherwise providing an explanation of that would be helpful to actually give you proper pointers, this looks like an XY problem to me.
The listserver is for listing sessions, similar to listservers in a video game. It doesn't have anything to do with creating sessions.
I'm trying to understand session templates by following the docs, it says you can create a template from the export options from file, but looking from web.drawpile.net I can not see this option.
Does there need to an option included when building/compiling drawpile desktop server?
Correct, the browser platform doesn't have that action. It doesn't support creating recordings in general.
Explaining what your use case is would probably help. I don't really understand what you're trying to accomplish overall, the things you're doing and asking for seem like you may have taken a wrong turn.
it seems like an understandable want, but I want the ability to create a drawpile app session from the list server admin. Like a button, connected to a loaded and running version of drawpile desktop app that will start up a session and give you a link that can be shared with others that will take you to session.
Something like this:
If you understand drawpile, then you would know that there isn't a way to create a session from the admin listserver. It needs to be created from within drawpile desktop first, and then it'll be listed in the list server, but there is no way to create a session from the list server alone. So that's why I am looking for a way to create a session from the webadmin api, either with session templates or some other way by modifying the source code.
You are describing implementation. What drives you to want to have this button in the first place?
By your description, the solution is to use the client to host a session. That'll create the session and give you a link that you can send to others to join the session. But presumably there's some reason why you want to do it via some other means instead.
Could be coincidence, but I (Raven) also looked in the same spot for an option to create a new session the first time I used Drawpile. An additional way to create session won't be bad to have, especially since this one already requires being logged into the admin panel (and therefore unlikely to have bad implications). It could be said that having a way to create sessions along the ability to manage them seems only natural. But the problem arises when we ask what parameters would it start the session with. This probably calls for a suite of options in the admin panel for creating sessions and their parameters, which is not ideal to have in an admin panel. Would leave this open in case it reoccurs or is referenced in future feature requests.
There's a few impasses to the idea, in decreasing order of severity:
Sessions start with an initial canvas provided by the client. The server can't provide it because it doesn't understand the drawing protocol, so the most it can do is give you a 0 by 0 pixel canvas with no layers and all permissions nulled.
The session would have no operators, so you'd by default give operator to whoever happens to join first. So it'd just be a free-for-all for sessions without a session password unless you set an operator password on them. Which in turn gives you no operators by default, meaning the session can't be reset and will run out of space.
Sessions normally terminate when no one is in them. So you could only create persistent sessions this way, lest it would be created and instantly terminated. Which in turn means your server will fill up with sessions unless you terminate them manually or set an idle timeout.
The server doesn't store the session or operator password, only a hash thereof. So you'd only get one chance to copy the invite link, which is kinda unexpected when you created the session server-side.
So this perceivedly simple idea has some major hurdles that I don't think are solvable in a sensible way. And I don't even know what the gain is, the only thing you can do after you created the session is join it in the client, so why not just host it from the client in the first place.
But I may be missing the point and/or there may be a way to fulfill the underlying desire for this server-side button some other way. I just don't know in absence of a use case.
Let me explain my current approach to this:
I found out that there is a web server for the chat system in https://github.com/drawpile/Drawpile/blob/main/src/thinsrv/webadmin/webadmin.cpp#L51 I'm not sure exactly where this HTTP server leads to, so I've been trying to match on a chat command to see where it ends up, and this could be in either https://github.com/drawpile/Drawpile/blob/main/src/libserver/client.cpp#L276 or https://github.com/drawpile/Drawpile/blob/main/src/libserver/session.cpp#L1655 or https://github.com/drawpile/Drawpile/blob/main/src/libserver/sessionserver.cpp#L310. If tried matching on a text command from either of these entry points, but so far I haven't found something that works. I am sending a matchable command like this: from the listserver.
After I figure out how to send a text based command to the webserver, I plan to link it to the session creation function in https://github.com/drawpile/Drawpile/blob/main/src/desktop/mainwindow.cpp#L2259 Using https://doc.qt.io/qt-6/signalsandslots.html,
I am creating a copy of MainWindow::hostSession()
as a signal:
// ./src/desktop/mainwindow.h line 126
signals:
void hostSessionEnabled(bool enabled);
void smallScreenModeChanged(bool smallScreenMode);
void windowReplacementFailed(MainWindow *win);
void viewShifted(qreal deltaX, qreal deltaY);
void hostSessionViaRemote(
const QString &title, const QString &password, const QString &alias,
bool nsfm, const QString &announcementUrl,
const QString &remoteAddress);
and once I can match on a text command I plan to invoke it with
#include "desktop/mainwindow.h"
...
emit hostSessionViaRemote("title","password","alias",false,"announcmenturl","0.0.0.0");
I know as you guys have pointed out, that setting the parameters is not going to be so simple, but this is the basic approach I am taking to doing this.
Please answer the question I posed about the use case. You are very likely walking in the wrong direction here, I don't want to help string you along on this path straight into a brick wall.
So, let me update you on my progress, as I did implement what I described above, and it did work in concept, though, I am receiving some other errors after the mainwindow::hostSession()
function is called.
What approached worked was I added a string matching condition in the chatwidget component:
void ChatWidget::systemMessage(const QString &message, bool alert)
{
const bool wasAtEnd = d->isAtEnd();
const QString safetext = htmlutils::linkify(htmlutils::emojify(message.toHtmlEscaped()));
if(alert) {
if(message == "create_new_session") {
const auto safetext2 = QStringLiteral("the create_new_session function was called!");
d->publicChat().appendMessageCompact(
0, QString(), safetext2, false, true);
emit expandRequested();
/*
const QString &title, const QString &password, const QString &alias, bool nsfm, const QString &announcementUrl, const QString &remoteAddress
*/
emit emitHostSessionViaRemote();
} else {
d->publicChat().appendMessageCompact(
0, QString(), safetext, false, true);
emit expandRequested();
}
} else {
d->publicChat().appendNotification(safetext);
}
notification::Event event =
alert ? notification::Event::PrivateChat : notification::Event::Chat;
notifySanitize(event, message);
if(wasAtEnd || alert) {
d->scrollChatToEnd(0);
}
}
Which I connected with a signal and slot, that traces back to the mainwindow
, where the host session function is held:
within Chatwidget is a connector function
void ChatWidget::connectMainWindow(MainWindow *mainWindow)
{
connect(this, &ChatWidget::emitHostSessionViaRemote, mainWindow, &MainWindow::hostSessionViaRemote);
}
which is called by chatbox
void ChatBox::connectMainWindow(MainWindow *mainWindow)
{
m_chatWidget->connectMainWindow(mainWindow);
}
which is called in the mainwindow, within it's constructor:
...
m_chatbox->connectMainWindow(this);
...
then there is a copy of hostSession set as a public slot:
void MainWindow::hostSessionViaRemote()
{
m_doc->client()->sendMessage(net::makeChatMessage(1, 0, 0, QStringLiteral("the hostSessionViaRemote function has been called")));
showErrorMessage(tr("the hostSessionViaRemote function has been called"));
const QString &title = QStringLiteral("testtitle");
const QString &password = nullptr;
const QString &alias = QStringLiteral("alias");
bool nsfm = false;
const QString &announcementUrl = QStringLiteral("welcome");
const QString &remoteAddress = QStringLiteral("...");
if(!m_doc->canvas()) {
showErrorMessage(tr("No canvas to host! Create one or open a file."));
return;
}
...
}
The slot defined in mainwindow.h
public slots:
// Triggerable actions
...
void hostSessionViaRemote();
triggering all of this with a system message of create_new_session
does lead to some errors coming from the hostSession function, but the way to call it worked.
Hello again. We're awaiting the reasoning or rationale as to why you would want this feature.
Redundancy is good, but it's not viable alone.
Thank you for the technical concept. Implementing it is more than possible, just hasslesome.
Hello, I would like to request some help in finding a way to modify the source of drawpile, to allow me to create a session from outside a loaded drawpile desktop app. I can see from the listserver that you can insert a session from here, https://github.com/drawpile/listserver/blob/97603b31130be4fdfef441fbc9443baaecd4b98a/db/db_sqlite.go#L435, but there does not appear to be a way to create a session from the listserver, the listserver insert session only inserts an already created session from a loaded drawpile app.
Upon sleuthing through the source code of drawpile, it would appear you would need to create a session from inside an active loaded version of drawpile web first. I can see that there is a function within drawpile for creating a session here: https://github.com/drawpile/Drawpile/blob/b62314319052d5f434c0d0b0bb93eefe30d81c58/src/libserver/sessionserver.cpp#L99
and that drawpile has a web API here: https://github.com/drawpile/Drawpile/blob/b62314319052d5f434c0d0b0bb93eefe30d81c58/src/thinsrv/webadmin/webadmin.cpp#L39
Would it be possible to call this API to create a session, if I specified the listserver / host, the username of the admin, the title, and password if there is one, that would call the create session function
Thanks if there is any guidance out there, hint, tips, or any help would be very appreciated.