2 # Copyright and License:
5 # gregor herrmann <gregor+debian@comodo.priv.at>,
6 # Philipp Spitzer <philipp@spitzer.priv.at>
8 # This program is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License version 2 as published
10 # by the Free Software Foundation.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, or point
18 # your browser to http://www.gnu.org/licenses/gpl.html
23 #include <QApplication>
27 #include <QMessageBox>
36 void initConfigInfo(ConfigInfo& configInfo) {
37 QStringList environment = QProcess::systemEnvironment().filter(QRegExp("^HOME=")); // ("HOME=/home/gregoa")
38 if (environment.size() == 1) {
39 configInfo.home = environment.at(0).mid(5);
40 configInfo.userConfigFile = configInfo.home + + "/.teleschorschrc";
42 configInfo.systemConfigFile = "/etc/teleschorschrc";
45 if (!configInfo.userConfigFile.isEmpty()) {
46 QFile configFileUser(configInfo.userConfigFile);
47 if (configFileUser.exists()) {configInfo.usedConfigFile = configInfo.userConfigFile;}
51 if (configInfo.usedConfigFile.isEmpty()) {
52 QFile configFileSystem(configInfo.systemConfigFile);
53 if (configFileSystem.exists()) configInfo.usedConfigFile = configInfo.systemConfigFile;
58 bool addConfig(const QString& fileName, ChannelVec& cv, QString& error) {
60 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {error = QObject::tr("Could not open file %1").arg(fileName); return false;}
61 Channel channel; // current channel
62 bool sectionOccured = false;
63 while (!file.atEnd()) {
64 QString line = QString(file.readLine()).trimmed();
65 if (line.startsWith("#") || line.startsWith(";") || line.isEmpty()) continue; // strip comments
67 // Is the line a [section]?
68 bool section = line.startsWith("[") && line.endsWith("]") && line.size() >= 3;
70 if (sectionOccured) cv.push_back(channel);
71 sectionOccured = true;
73 channel.name = line.mid(1, line.size()-2);
77 int eqPos = line.indexOf("=");
78 bool property = !section && eqPos >= 1;
80 QString propKey = line.left(eqPos);
81 QString propVal = line.right(line.size()-eqPos-1);
82 if (!sectionOccured) {error = QObject::tr("Property %1 is only allowed in a [section].").arg(propKey); return false;}
84 if (propKey == "FULLNAME") channel.fullName = propVal;
85 else if (propKey == "STATICURL") channel.staticUrl = propVal;
86 else if (propKey == "PLAYER") channel.player = propVal;
87 else {error = QObject::tr("Unknown key in ini file: %1").arg(propKey); return false;}
90 if (!section && !property) {error = QObject::tr("Line %1 is not valid.").arg(line); return false;}
92 if (sectionOccured) cv.push_back(channel);
97 QString readChannelVec(const ConfigInfo& configInfo, ChannelVec& channelVec) {
98 if (configInfo.usedConfigFile.isEmpty()) return QObject::tr("Neither %1 nor %2 found.").arg(configInfo.systemConfigFile).arg(configInfo.userConfigFile);
100 if (!addConfig(configInfo.usedConfigFile, channelVec, error));
105 /// \brief Finds a value for a specified variable and appends it to a string.
107 /// \param[in] var variable to substitute. These are the possibilities:
108 /// - d day of month (01-31)
109 /// - m month (01-12)
110 /// - y year (last two digits)
111 /// - Y year (4 digits)
112 /// - dow_DE day of week in German (Montag, ...)
113 /// \param[in] date date that should be used when replacing the date dependend variables
114 /// \param[out] result The determined value of the variable is _appended_.
115 /// \param[out] error error message in error cases
116 /// \returns true in case of success.
117 bool substituteVar(const QString& var, QDate date, QString& result, QString& errorMsg) {
118 if (var == "d") {result += date.toString("dd"); return true;}
119 if (var == "m") {result += date.toString("MM"); return true;}
120 if (var == "y") {result += date.toString("yy"); return true;}
121 if (var == "Y") {result += date.toString("yyyy"); return true;}
122 if (var == "dow_DE") {
123 int dow = date.dayOfWeek() - 1;
124 static const char dow_de[][16] = {"Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"};
125 if (dow >= 0 && dow < 7) {result += dow_de[dow]; return true;}
127 errorMsg = QObject::tr("No match for variable %1").arg(var);
132 /// \brief Finds a string, where variables of the form <code>${var}</code> are substituted in input (normally a staticUrl).
134 /// - ${d} day of month (01-31)
135 /// - ${m} month (01-12)
136 /// - ${y} year (last two digits)
137 /// - ${Y} year (4 digits)
138 /// - ${dow_DE} day of week in German (Montag, ...)
140 /// \param[in] input string where the substitution should be done.
141 /// \param[in] date date that should be used when replacing the date dependend variables
142 /// \param[out] result The result string with the variables substituted is appended here
143 /// \param[out] error error message in error cases
144 /// \returns true in case of success.
145 bool substituteVars(const QString& input, QDate date, QString& result, QString& errorMsg) {
149 pos = input.indexOf("${", lastPos);
150 if (pos == -1) result += input.mid(lastPos);
151 else result += input.mid(lastPos, pos-lastPos);
156 pos = input.indexOf("}", lastPos);
158 errorMsg = QObject::tr("${ not closed with }.");
161 QString var = input.mid(lastPos+2, pos-lastPos-2);
162 if (!substituteVar(var, date, result, errorMsg)) return false;
170 /// \brief Calls the /bin/sh shell with the specified command and appends the result to a string variable.
172 /// \param[in] command command to execute with <code>/bin/sh -c "command"</code>
173 /// \param[out] result The result string where the output is appended at.
174 /// \param[out] error error message in error cases
175 /// \returns true in case of success.
176 bool executeShellCommand(const QString& command, QString& result, QString& errorMsg) {
178 QString cmd = "/bin/sh -c \"" + command + "\"";
180 if (evalUrl.waitForFinished(3000)) {
181 QByteArray newResult = evalUrl.readAllStandardOutput();
182 if (result != newResult) {result += newResult.trimmed();}
185 errorMsg = QObject::tr("Shell command executed when substituting URL (%1) timed out.").arg(command);
190 /// \brief Evaluates the staticUrl
192 /// - Variables of the form ${var} are substituted according the the function ::substituteVars
193 /// - If the staticUrl is enclosed by backticks it is evaluated by a shell command (after the substitution mentioned above)
195 /// \param[in] staticUrl string where the substitution should be done.
196 /// \param[in] date date that should be used when replacing the date dependend variables
197 /// \param[out] result The result string is appended here
198 /// \param[out] error error message in error cases
199 /// \returns true in case of success.
200 bool evaluateStaticUrl(const QString& staticUrl, QDate date, QString& result, QString& errorMsg) {
202 bool success = substituteVars(staticUrl, date, subst, errorMsg);
203 if (!success) return false;
205 // Evaluate staticUrl - it might be dynamic despite its name :-)
207 if (subst.left(1) == "`" && subst.right(1) == "`") {
208 success = executeShellCommand(subst.mid(1, subst.size()-2), cmdres, errorMsg);
209 if (!success) return false;
211 } else result = subst;
216 void appendPlayerOffsetOption(const QString& player, QTime offset, QStringList& arguments) {
217 QTime zero = QTime(0, 0, 0, 0);
218 int offsetSec = zero.secsTo(offset);
219 if (player.indexOf("vlc") != -1) arguments.append("--start-time");
220 if (player.indexOf("gmplayer") != -1) {
221 arguments << "--cache" << "512"; // --cache 512 does not belong to this function but for now...
224 arguments << QString::number(offsetSec);
232 MainDialog::MainDialog(QWidget *parent): QDialog(parent) {
235 QObject::connect(btnOptions, SIGNAL(clicked()), this, SLOT(editOptions()));
236 QObject::connect(btnStart, SIGNAL(clicked()), this, SLOT(startAction()));
239 initConfigInfo(configInfo);
242 QString error = readChannelVec(configInfo, channelVec);
243 if (!error.isEmpty()) QMessageBox::warning(this, tr("Problem when reading the configuration file"), error);
249 QDateTime dateTime = QDateTime::currentDateTime(); // set the default date to today if it is past midday
250 if (dateTime.time().hour() < 12) dateTime = dateTime.addDays(-1);
251 calDate->setSelectedDate(dateTime.date());
255 void MainDialog::editOptions() {
256 OptionsDialog *od = new OptionsDialog();
257 if (od->exec(configInfo.userConfigFile)) {
259 QString error = readChannelVec(configInfo, channelVec);
260 if (!error.isEmpty()) QMessageBox::warning(this, tr("Problem when reading the configuration file"), error);
267 void MainDialog::updateLwChannels() {
269 for (int i = 0; i != channelVec.size(); ++i) lwChannels->addItem(channelVec[i].fullName);
273 bool MainDialog::startAction() {
274 int row = lwChannels->currentRow();
276 Channel channel = channelVec[row];
277 QDate date = calDate->selectedDate();
280 if (!evaluateStaticUrl(channel.staticUrl, date, substUrl, errorMsg)) {
281 QMessageBox::warning(this, tr("Problem when substituting URL"), errorMsg);
284 QStringList arguments;
285 arguments.append(substUrl);
286 appendPlayerOffsetOption(channel.player, teOffset->time(), arguments);
287 QProcess player(this);
288 qDebug() << channel.player << arguments;
289 player.start(channel.player, arguments);
290 player.waitForFinished(-1);
301 int main(int argc, char *argv[]) {
302 QApplication app(argc, argv);
303 MainDialog *mainDialog = new MainDialog();