Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a50da39
Bible & songs over http with websocket
ge022 Sep 29, 2022
3978e50
Added IP Address config
ge022 Oct 1, 2022
4a236e6
Tmp fix caption for empty bible text
ge022 Nov 17, 2022
4e2c95e
Song verse split into 2/3 lines for lower-third
ge022 Sep 11, 2023
eed4977
httpserver.html updated example font
ge022 Sep 14, 2023
37746a1
Improved split verse list keyboard navigation
ge022 Feb 4, 2024
6975eb0
textFit fix for bible on page load
ge022 Jul 15, 2024
466d5ac
Parallel song translation for verse split
ge022 Jul 19, 2024
15d9ba3
Update README.md
ge022 Jul 19, 2024
5d97223
Display text fade-in-out example
ge022 Jul 19, 2024
7b57304
Merge branch 'song-verse-split-translation' of https://github.com/ge0…
ge022 Jul 19, 2024
4fb6aea
Optionally use/show song translation - new song checkbox field
ge022 Jul 28, 2024
0b0b001
Allow edit of song if multiple of same song number in same songbook
ge022 Jul 31, 2024
3a87eeb
songs list fixed songbook column horizontal sizing
ge022 Jul 31, 2024
296cbeb
Show song split from schedule fix
ge022 Aug 11, 2024
bb10c7d
httpserver.html ex. font fix
ge022 Aug 11, 2024
80ce2c4
Fixed song db insert statement
ge022 Sep 8, 2024
49f9d8c
Fixed song db insert statement
ge022 Sep 8, 2024
4897ae2
httpserver.html - fades by default, and added bibleOnly & songOnly ur…
ge022 Feb 26, 2026
b3ad382
Qt6 on Win11
ge022 Mar 19, 2026
e70ecdf
Merge branch 'song-verse-split-translation' into qt6-merge
ge022 Mar 19, 2026
6899826
Fixed use_translation field sql
ge022 Mar 19, 2026
40c99ad
Merge branch 'song-verse-split-translation' into qt6-merge
ge022 Mar 19, 2026
65cafb9
Fixed use_translation field sql
ge022 Mar 26, 2026
64adc48
Merge branch 'song-verse-split-translation' into qt6-merge
ge022 Mar 26, 2026
7d65cc6
httpserver qt6 merge
ge022 Apr 4, 2026
f24d0c8
Songs search fixes
ge022 Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ unknownsys_build/*
win32_build/*
unix_build/*
mac_build/*
/src/build
/src/win32_build
6 changes: 3 additions & 3 deletions 3rdparty/headers/qmediaplaylist.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <QObject>

#include <qmediaenumdebug.h>
// #include <qmediaenumdebug.h>

QT_BEGIN_NAMESPACE

Expand Down Expand Up @@ -89,7 +89,7 @@ public slots:

QT_END_NAMESPACE

Q_MEDIA_ENUM_DEBUG(QMediaPlaylist, PlaybackMode)
Q_MEDIA_ENUM_DEBUG(QMediaPlaylist, Error)
// Q_MEDIA_ENUM_DEBUG(QMediaPlaylist, PlaybackMode)
// Q_MEDIA_ENUM_DEBUG(QMediaPlaylist, Error)

#endif // QMEDIAPLAYLIST_H
6 changes: 3 additions & 3 deletions 3rdparty/sources/qmediaplaylist.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "../include/qmediaplaylist.h"
#include "../include/qmediaplaylist_p.h"
#include "qplaylistfileparser.h"
#include "../headers/qmediaplaylist.h"
#include "../headers/qmediaplaylist_p.h"
#include "../headers/qplaylistfileparser.h"

#include <QCoreApplication>
#include <QFile>
Expand Down
2 changes: 1 addition & 1 deletion 3rdparty/sources/qmediaplaylist_p.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "../include/qmediaplaylist_p.h"
#include "../headers/qmediaplaylist_p.h"

QT_BEGIN_NAMESPACE

Expand Down
2 changes: 1 addition & 1 deletion 3rdparty/sources/qplaylistfileparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "qmediametadata.h"
#include "qmediaplayer.h"
#include "qplaylistfileparser.h"
#include "../headers/qplaylistfileparser.h"

#include <QDebug>
#include <QFileInfo>
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,21 @@
```
.\windeployqt.exe --qmldir "<ProjectDirectory>" "<PathtoEXE>"
```

#### A version of SoftProjector which uses HTTP pages for bible/song displays instead of Windows forms.
If you've ever had your mouse or a Windows application open up on the projector during live service, and everyone seeing, then you know the downsides of projecting a computer display. This version of softprojector outputs bible & songs as a webpage, which allows you to add them as scene in OBS. From there you can send the OBS' program mix to Decklink Ouput into your video switcher.

This http implementation opened up different style options of displaying bible text and lyrics. By editing the httpserver.html or running a local file copy, any amount of computers can show and style bible and songs however wanted. Your stream PC may want to show just the primary bible in a lower-thirds style for livestream, and your projection pc output shows parallel.

Furthermore, this now allowed me to add features such as song verse split, where one screen can show the full verse and another shows 2 lines at a time. Perfect for readability on livestream.

Lastly, for us multilinguals, I also added parallel song verse translations which works alongside the verse split feature. This is great because only one person/computer is needed to show different lyrics.

#### To use this, configure your local IP in the settings
Add a browser source in OBS, and then style it using the css setting (example css in :/httpserver.html). Alternatively, copy the .html file to your local machine and edit it as you like.
![tempsnip](https://github.com/SoftProjector/softprojector/assets/23706821/816e8742-cbf1-4967-b50e-5ee511d69a4f)
#### To use the song verse split, check the 'Split Verse Text' checkbox above the song preview list.
When you go live, the split list will show below the full verses. Simply use your keyboard arrows to navigate.
![267142147-35343950-036b-433e-bbba-28600d464c01](https://github.com/user-attachments/assets/8c24ea6c-ea59-4b38-8b7d-36c22f68e7a1)
#### For parallel verses, add the translation to your song using '='
![vers-tra](https://github.com/user-attachments/assets/5e2d0975-2fb9-4d0a-be0f-17471f30f009)
1 change: 1 addition & 0 deletions src/headers/editwidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public slots:
private slots:
void addNewSong(Song song, QString msgNewSongbook, QString msgCaption);
void addSongbook();
bool validateSong();
void on_btnCancel_clicked();
void on_btnSave_clicked();
void resetUiItems();
Expand Down
2 changes: 1 addition & 1 deletion src/headers/highlight.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Highlight : public QSyntaxHighlighter
};
QVector<HighlightingRule> highlightingRules;

QTextCharFormat verseFormat, chorusFormat, vstavkaFormat;
QTextCharFormat verseFormat, chorusFormat, vstavkaFormat, translationFormat;
};

// *** Announcement editor highlighter
Expand Down
53 changes: 53 additions & 0 deletions src/headers/httpserver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/***************************************************************************
//
// softProjector - an open source media projection software
// Copyright (C) 2022 Vladislav Kobzar
//
// This program 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 version 3 of the License.
//
// 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 <http://www.gnu.org/licenses/>.
//
***************************************************************************/

#include <QTcpServer>

// Simple HTTP server implementation in Qt taken from https://stackoverflow.com/a/32931195

#ifndef HTTPSERVER_H
#define HTTPSERVER_H

class HttpServer : public QObject
{
Q_OBJECT

public:
HttpServer();
void startServer(QString httpServerIPAddress, quint16 httpServerPort, quint16 webSocketServerPort);
void stopServer();
bool isRunning;

public slots:
void startConnection();
void sendData();
void closingClient();

private:
QTcpServer *server = new QTcpServer();
QString ipAddress;
quint16 httpPort;
quint16 webSocketPort;

private slots:

signals:
};

#endif
5 changes: 5 additions & 0 deletions src/headers/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ class GeneralSettings
bool settingsChangedAll;
bool settingsChangedMulti;
bool settingsChangedSingle;
bool httpServerEnabled;
QString httpServerIPAddress;
int webSocketServerPort;
int httpServerPort;
bool disableScreens;
};

class DisplaySettings
Expand Down
6 changes: 6 additions & 0 deletions src/headers/settingsdialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public slots:
BibleVersionSettings& bsets3, BibleVersionSettings& bsets4);
void positionsDisplayWindow();
void updateScreen();
void httpServerState(bool& state, QString& httpServerIPAddress, int& httpServerPort, int& webSocketServerPort);

private:
Ui::SettingsDialog *ui;
Expand All @@ -64,6 +65,11 @@ public slots:
int currentDisplayScreen4;
bool is_always_on_top;

bool currentHTTPEnabled;
QString currentHTTPServerIPAddress;
int currentHTTPPort;
int currentWebSocketPort;

GeneralSettings gsettings;
Theme theme;
BibleVersionSettings bsettings;
Expand Down
19 changes: 19 additions & 0 deletions src/headers/softprojector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#include "videoinfo.hpp"
#include "slideshoweditor.hpp"
#include "schedule.hpp"
#include "httpserver.hpp"
#include "websocketserver.hpp"

class QActionGroup;

Expand Down Expand Up @@ -76,10 +78,16 @@ class SoftProjector : public QMainWindow
PictureWidget *pictureWidget;
MediaWidget *mediaPlayer;
MediaControl *mediaControls;
WebSocketServer *webSocketServer;
HttpServer *httpServer;

bool showing; // whether we are currently showing to the projector
Song current_song;
int current_song_verse;
QStringList currentSongVerses; // List of song verse blocks.
QStringList currentSongTranslatedVerses; // List of translation-only verse blocks.
QStringList currentSongVerseSplitList; // List of the current verse split into lines.
QStringList currentSongTranslatedVerseSplitList; // List of the current translation-only verse split into lines.
Verse current_verse;
Announcement currentAnnounce;
QString version_string;
Expand All @@ -95,6 +103,7 @@ public slots:
void saveSettings();
void positionDisplayWindow();
void updateScreen();
void httpServerState(bool &state, QString &httpServerIPAddress, int &httpServerPort, int &webSocketServerPort);

void setWaitCursor();
void setArrowCursor();
Expand Down Expand Up @@ -139,6 +148,7 @@ public slots:
QList<Schedule> schedule;
QDir appDataDir;

bool songSplitVerse;
private slots:
void showDisplayScreen(bool show);

Expand Down Expand Up @@ -250,6 +260,15 @@ private slots:
void on_actionCloseDisplay_triggered();
void updateCloseDisplayButtons(bool isOn);

void sendBibleVerseToServer();
void sendSongVerseToServer();
void showSongVerseSplit(bool enabled);
void sendSongVerseSplit();
QStringList splitSongVerse(QString verse);
void setSongVerseSplitList(int currentVerseIndex, QString selectRow = "first");
void on_songVerseSplitListWidget_itemSelectionChanged();
void on_songVerseSplitListWidget_itemDoubleClicked(QListWidgetItem *item);
void on_songVerseSplitListWidgetNavigation(Qt::Key);
protected:
void closeEvent(QCloseEvent *event);
void keyPressEvent(QKeyEvent *event);
Expand Down
5 changes: 3 additions & 2 deletions src/headers/song.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ bool isStanzaAndVerseTitle(QString string);
bool isStanzaRefrainTitle(QString string);
bool isStanzaAndRefrainTitle(QString string);
bool isStanzaSlideTitle(QString string);
QString getStanzaBlock(int &i, QStringList &list, bool includeTranslation = false);

class Stanza
{
Expand Down Expand Up @@ -68,7 +69,7 @@ class Song
void readData();
void saveUpdate();
void saveNew();
QStringList getSongTextList();
QStringList getSongTextList(bool includeTranslation = false);
Stanza getStanza(int current);
QString getSongbookName();
bool isValid();
Expand All @@ -86,6 +87,7 @@ class Song
QString musicBy;
QString songText;
QString notes;
bool useTranslation; // Show verse translations or not.
bool usePrivateSettings;
int alignmentV;
int alignmentH;
Expand All @@ -101,7 +103,6 @@ class Song

private:
void setDefaults();
QString getStanzaBlock(int &i, QStringList &list);
void removeLastChorus(QStringList ct, QStringList &list);
};

Expand Down
6 changes: 6 additions & 0 deletions src/headers/songwidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public slots:
void setArrowCursor();
void sendSong(Song song, int currentItem);
void addToSchedule(Song &song);
void enableSongSplitVerse(bool enabled);

private slots:
void on_comboBoxCategory_currentIndexChanged(int index);
Expand All @@ -83,6 +84,8 @@ private slots:
void on_pushButtonClearResults_clicked();
void on_comboBoxFilterType_currentIndexChanged(int index);

void on_enableSplitVerseCheckBox_toggled(bool checked);

private:
Ui::SongWidget *ui;
QString songbook;
Expand All @@ -99,6 +102,9 @@ private slots:
QList<int> cat_ids;
QList<Song> allSongs;
HighlighterDelegate *highlight;
QTimer *searchTimer;
QString searchText;
void processSearch();
};

#endif // SONGWIDGET_HPP
74 changes: 74 additions & 0 deletions src/headers/websocketserver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/***************************************************************************
//
// softProjector - an open source media projection software
// Copyright (C) 2022 Vladislav Kobzar
//
// This program 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 version 3 of the License.
//
// 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 <http://www.gnu.org/licenses/>.
//
***************************************************************************/

#ifndef WEBSOCKETSERVER_HPP
#define WEBSOCKETSERVER_HPP

#include <QCoreApplication>
#include <QNetworkInterface>
#include <iostream>
#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
#include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QByteArray>

#include "bible.hpp"
#include "song.hpp"
#include "announcement.hpp"

// WebSocket implementation taken from https://code.qt.io/cgit/qt/qtwebsockets.git/tree/examples/websockets?h=5.15

QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)

class WebSocketServer : public QObject
{
Q_OBJECT

public:
WebSocketServer();
void startServer(QString httpServerIPAddress, quint16 port);
void stopServer();
void setBibleText(Verse verse);
void setSongText(QString verse, QString verseTranslation = "", QString splitVerse = "", QString splitVerseTranslation = "", bool isLast = false);
void setBlank();
bool isRunning;

public slots:
void closingClient();

private:
QWebSocketServer *server;
QList<QWebSocket *> clients;
qint64 bytesAvailable() const;
QJsonObject jsonOutput;
void establishConnection();
bool servingConnection;
void sendToClients();
void onNewConnection();
void socketDisconnected();

signals:
void closed();
};

#endif
Loading