[svn-upgrade] Integrating new upstream version, teleschorsch (0.0.6)
[debian/teleschorsch.git] / main.cpp
1 /*
2 # Copyright and License:
3 #
4 # Copyright (C) 2007 
5 # gregor herrmann <gregor+debian@comodo.priv.at>,
6 # Philipp Spitzer <philipp@spitzer.priv.at>
7 #
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
19 */
20
21 #include <QtGlobal>
22 #include <QtDebug>
23 #include <QApplication>
24 #include <QVector>
25 #include <QFile>
26 #include <QProcess>
27 #include <QMessageBox>
28 #include "main.h"
29 #include "options.h"
30
31
32 // Functions
33 // =========
34
35 void initConfigInfo(ConfigInfo& configInfo) {
36         QStringList environment = QProcess::systemEnvironment().filter(QRegExp("^HOME=")); // ("HOME=/home/gregoa")
37         if (environment.size() == 1) {
38                 configInfo.home = environment.at(0).mid(5);
39                 configInfo.userConfigFile = configInfo.home + + "/.teleschorschrc";
40         }
41         configInfo.systemConfigFile = "/etc/teleschorschrc";
42
43         // User config file
44         if (!configInfo.userConfigFile.isEmpty()) {
45                 QFile configFileUser(configInfo.userConfigFile);
46                 if (configFileUser.exists()) {configInfo.usedConfigFile  = configInfo.userConfigFile;}
47         }
48
49         // System config file
50         if (configInfo.usedConfigFile.isEmpty()) {
51                 QFile configFileSystem(configInfo.systemConfigFile);
52                 if (configFileSystem.exists()) configInfo.usedConfigFile = configInfo.systemConfigFile;
53         }
54 }
55
56
57 bool addConfig(const QString& fileName, ChannelVec& cv, QString& error) {
58         QFile file(fileName);
59         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {error = QObject::tr("Could not open file %1").arg(fileName); return false;}
60         Channel channel; // current channel
61         bool sectionOccured = false;
62         while (!file.atEnd()) {
63                 QString line = QString(file.readLine()).trimmed();
64                 if (line.startsWith("#") || line.startsWith(";") || line.isEmpty()) continue; // strip comments
65                 
66                 // Is the line a [section]?
67                 bool section = line.startsWith("[") && line.endsWith("]") && line.size() >= 3;
68                 if (section) {
69                         if (sectionOccured) cv.push_back(channel);
70                         sectionOccured = true;
71                         channel = Channel();
72                         channel.name = line.mid(1, line.size()-2);
73                 }
74
75                 // Read properties
76                 int eqPos = line.indexOf("=");
77                 bool property = !section && eqPos >= 1;
78                 if (property) {
79                         QString propKey = line.left(eqPos);
80                         QString propVal = line.right(line.size()-eqPos-1);
81                         if (!sectionOccured) {error = QObject::tr("Property %1 is only allowed in a [section].").arg(propKey); return false;}
82
83                         if (propKey == "FULLNAME") channel.fullName = propVal;
84                         else if (propKey == "STATICURL") channel.staticUrl = propVal;
85                         else if (propKey == "PLAYER") channel.player = propVal;
86                         else {error = QObject::tr("Unknown key in ini file: %1").arg(propKey); return false;}
87                 }
88
89                 if (!section && !property) {error = QObject::tr("Line %1 is not valid.").arg(line); return false;}
90         }
91         if (sectionOccured) cv.push_back(channel);
92         return true;
93 }
94
95
96 QString readChannelVec(const ConfigInfo& configInfo, ChannelVec& channelVec) {
97         if (configInfo.usedConfigFile.isEmpty()) return QObject::tr("Neither %1 nor %2 found.").arg(configInfo.systemConfigFile).arg(configInfo.userConfigFile);
98         QString error;
99         if (!addConfig(configInfo.usedConfigFile, channelVec, error));
100         return error;
101 }
102
103
104 QString substitudeVar(const QString& var, QDate date) {
105         if (var == "d") return date.toString("dd");
106         if (var == "m") return date.toString("MM");
107         if (var == "y") return date.toString("yy");
108         if (var == "Y") return date.toString("yyyy");
109         if (var == "dow_DE") {
110                 int dow = date.dayOfWeek() - 1;
111                 static const char dow_de[][20] = {"Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"};
112                 if (dow >= 0 && dow < 7) return dow_de[dow];
113         }
114         return "{no match}"; // maybe do some error handling here...
115 }
116
117
118 QString substitudeStaticUrl(const QString& staticUrl, QDate date) {
119         QString result;
120         int pos = 0;
121         int lastPos = 0;
122         while (pos != -1) {
123                 pos = staticUrl.indexOf("${", lastPos);
124                 if (pos == -1) result += staticUrl.mid(lastPos);
125                 else result += staticUrl.mid(lastPos, pos-lastPos);
126                 lastPos = pos;
127                 
128                 // Match?
129                 if (pos != -1) {
130                         pos = staticUrl.indexOf("}", lastPos);
131                         if (pos == -1) return ""; // ${ not closed with }. Some day we should report the error somehow...
132                         QString var = staticUrl.mid(lastPos+2, pos-lastPos-2);
133                         result += substitudeVar(var, date);
134                         lastPos = pos+1;
135                 }
136         }
137
138         // Evaluate staticUrl - it might be dynamic despite its name :-)
139         if (result.left(1) == "`" && result.right(1) == "`") {
140                 QProcess evalUrl(0);
141                 QString command = "/bin/sh -c \"echo " + result + "\"";
142                 evalUrl.start(command);
143                 if (evalUrl.waitForFinished()) {
144                         QByteArray newResult = evalUrl.readAllStandardOutput();
145                         if (result != newResult) {result = newResult.trimmed();}
146                 }
147         }
148         return result;
149 }
150
151
152 void appendPlayerOffsetOption(const QString& player, QTime offset, QStringList& arguments) {
153         QTime zero = QTime(0, 0, 0, 0);
154         int offsetSec = zero.secsTo(offset);
155         if (player.indexOf("vlc") != -1) arguments.append("--start-time");
156         if (player.indexOf("gmplayer") != -1) {
157                 arguments << "--cache" << "512"; // --cache 512 does not belong to this function but for now...
158                 arguments << "-ss"; 
159         }
160         arguments << QString::number(offsetSec);
161 }
162
163
164
165 // MainDialog
166 // ==========
167
168 MainDialog::MainDialog(QWidget *parent): QDialog(parent) {
169         // User interface
170         setupUi(this);
171         QObject::connect(btnOptions, SIGNAL(clicked()), this, SLOT(editOptions()));
172         QObject::connect(btnStart, SIGNAL(clicked()), this, SLOT(startAction()));
173
174         // Init configInfo
175         initConfigInfo(configInfo);
176
177         // Read config
178         QString error = readChannelVec(configInfo, channelVec);
179         if (!error.isEmpty()) QMessageBox::warning(this, tr("Problem when reading the configuration file"), error);
180
181         // Fill in channels
182         updateLwChannels();
183 }
184
185
186 void MainDialog::editOptions() {
187         OptionsDialog *od = new OptionsDialog();
188         if (od->exec(configInfo.userConfigFile)) {
189                 channelVec.clear();
190                 QString error = readChannelVec(configInfo, channelVec);
191                 if (!error.isEmpty()) QMessageBox::warning(this, tr("Problem when reading the configuration file"), error);
192                 updateLwChannels();
193         }
194         delete od;
195 }
196
197
198 void MainDialog::updateLwChannels() {
199         lwChannels->clear();
200         for (int i = 0; i != channelVec.size(); ++i) lwChannels->addItem(channelVec[i].fullName);
201 }
202
203
204 bool MainDialog::startAction() {
205         int row = lwChannels->currentRow();
206         if (row > 0) {
207                 Channel channel = channelVec[row];
208                 QDate date = calDate->selectedDate();
209                 QStringList arguments;
210                 arguments.append(substitudeStaticUrl(channel.staticUrl, date));
211                 appendPlayerOffsetOption(channel.player, teOffset->time(), arguments);
212                 QProcess player(this);
213                 qDebug() << channel.player << arguments;
214                 player.start(channel.player, arguments);
215                 player.waitForFinished();
216                 return true;
217         }
218         return false;
219 }
220
221
222
223 // Main function
224 // =============
225
226 int main(int argc, char *argv[]) {
227         QApplication app(argc, argv);
228         MainDialog *mainDialog = new MainDialog();
229         mainDialog->show();
230         return app.exec();
231