Fix creation of output file name
[skype-call-recorder.git] / recorder.cpp
blob2d3b9da356e3636c6d034d2ad59a110ecc486cc4
1 /*
2 Skype Call Recorder
3 Copyright 2008 - 2009 by jlh (jlh at gmx dot ch)
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2 of the License, version 3 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 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 St, Fifth Floor, Boston, MA 02110-1301 USA
19 The GNU General Public License version 2 is included with the source of
20 this program under the file name COPYING. You can also get a copy on
21 http://www.fsf.org/
24 #include <QMessageBox>
25 #include <QDir>
26 #include <QProcess>
27 #include <QTimer>
28 #include <QDesktopServices>
29 #include <QUrl>
30 #include <QDateTime>
31 #include <iostream>
32 #include <cstdlib>
34 #include "recorder.h"
35 #include "common.h"
36 #include "gui.h"
37 #include "trayicon.h"
38 #include "preferences.h"
39 #include "skype.h"
40 #include "call.h"
42 Recorder::Recorder(int &argc, char **argv) :
43 QApplication(argc, argv)
45 recorderInstance = this;
47 debug("Initializing application");
49 // check for already running instance
50 if (!lockFile.lock(QDir::homePath() + "/.skypecallrecorder.lock")) {
51 debug("Other instance is running");
52 QTimer::singleShot(0, this, SLOT(quit()));
53 return;
56 loadPreferences();
58 setupGUI();
59 setupSkype();
60 setupCallHandler();
63 Recorder::~Recorder() {
64 // the Recorder object must still exist while the child objects are
65 // being destroyed, because debug() must still be available. Qt would
66 // do it automatically but only after Recorder ceased to exist, in the
67 // QObject destructor
69 delete preferencesDialog;
70 delete callHandler;
71 delete skype;
72 delete trayIcon;
75 void Recorder::setupGUI() {
76 setWindowIcon(QIcon(":/icon.png"));
77 setQuitOnLastWindowClosed(false);
79 trayIcon = new TrayIcon(this);
80 connect(trayIcon, SIGNAL(requestQuit()), this, SLOT(quitConfirmation()));
81 connect(trayIcon, SIGNAL(requestAbout()), this, SLOT(about()));
82 connect(trayIcon, SIGNAL(requestWebsite()), this, SLOT(openWebsite()));
83 connect(trayIcon, SIGNAL(requestOpenPreferences()), this, SLOT(openPreferences()));
84 connect(trayIcon, SIGNAL(requestBrowseCalls()), this, SLOT(browseCalls()));
86 debug("GUI initialized");
88 if (!preferences.get(Pref::SuppressFirstRunInformation).toBool())
89 new FirstRunDialog();
92 void Recorder::setupSkype() {
93 skype = new Skype(this);
94 connect(skype, SIGNAL(notify(const QString &)), this, SLOT(skypeNotify(const QString &)));
95 connect(skype, SIGNAL(connected(bool)), this, SLOT(skypeConnected(bool)));
96 connect(skype, SIGNAL(connectionFailed(const QString &)), this, SLOT(skypeConnectionFailed(const QString &)));
98 connect(skype, SIGNAL(connected(bool)), trayIcon, SLOT(setColor(bool)));
101 void Recorder::setupCallHandler() {
102 callHandler = new CallHandler(this, skype);
104 connect(trayIcon, SIGNAL(startRecording(int)), callHandler, SLOT(startRecording(int)));
105 connect(trayIcon, SIGNAL(stopRecording(int)), callHandler, SLOT(stopRecording(int)));
106 connect(trayIcon, SIGNAL(stopRecordingAndDelete(int)), callHandler, SLOT(stopRecordingAndDelete(int)));
108 connect(callHandler, SIGNAL(startedCall(int, const QString &)), trayIcon, SLOT(startedCall(int, const QString &)));
109 connect(callHandler, SIGNAL(stoppedCall(int)), trayIcon, SLOT(stoppedCall(int)));
110 connect(callHandler, SIGNAL(startedRecording(int)), trayIcon, SLOT(startedRecording(int)));
111 connect(callHandler, SIGNAL(stoppedRecording(int)), trayIcon, SLOT(stoppedRecording(int)));
114 QString Recorder::getConfigFile() const {
115 return QDir::homePath() + "/.skypecallrecorder.rc";
118 void Recorder::loadPreferences() {
119 preferences.load(getConfigFile());
120 int c = preferences.count();
122 // since Pref::PreferencesVersion did not exist from the first version
123 // on, some people might not have it; but we cannot just let it receive
124 // the default value, as those old settings must be updated. If it is
125 // missing but Pref::OutputPath exists (which has always been around),
126 // then set Pref::PreferencesVersion to 1
127 if (!preferences.exists(Pref::PreferencesVersion) && preferences.exists(Pref::OutputPath))
128 preferences.get(Pref::PreferencesVersion).set(1);
130 #define X(n, v) preferences.get(n).setIfNotSet(v);
131 // default preferences
132 X(Pref::AutoRecordDefault, "ask"); // "yes", "ask", "no"
133 X(Pref::AutoRecordAsk, ""); // comma separated skypenames to always ask for
134 X(Pref::AutoRecordYes, ""); // comma separated skypenames to always record
135 X(Pref::AutoRecordNo, ""); // comma separated skypenames to never record
136 X(Pref::OutputPath, "~/Skype Calls");
137 X(Pref::OutputPattern, "Calls with &s/Call with &s, %a %b %d %Y, %H:%M:%S");
138 X(Pref::OutputFormat, "mp3"); // "mp3", "vorbis" or "wav"
139 X(Pref::OutputFormatMp3Bitrate, 64);
140 X(Pref::OutputFormatVorbisQuality, 3);
141 X(Pref::OutputStereo, true);
142 X(Pref::OutputStereoMix, 0); // 0 .. 100
143 X(Pref::OutputSaveTags, true);
144 X(Pref::SuppressLegalInformation, false);
145 X(Pref::SuppressFirstRunInformation, false);
146 X(Pref::PreferencesVersion, 2);
147 X(Pref::NotifyRecordingStart, true)
148 X(Pref::GuiWindowed, false)
149 X(Pref::DebugWriteSyncFile, false)
150 #undef X
152 c = preferences.count() - c;
154 if (c)
155 debug(QString("Loading %1 built-in default preference(s)").arg(c));
157 sanatizePreferences();
160 void Recorder::savePreferences() {
161 preferences.save(getConfigFile());
162 // TODO: when failure?
165 void Recorder::sanatizePreferences() {
166 // this converts old preferences to new preferences
168 int v = preferences.get(Pref::PreferencesVersion).toInt();
169 bool didSomething = false;
171 switch (v) {
172 case 1:
173 didSomething |= convertSettingsToV2();
176 didSomething |= sanatizePreferencesGeneric();
178 if (didSomething)
179 savePreferences();
182 bool Recorder::convertSettingsToV2() {
183 debug("Converting settings from v1 to v2");
185 QString s = preferences.get("output.channelmode").toString();
186 preferences.remove("output.channelmode");
188 if (s == "stereo") {
189 preferences.get(Pref::OutputStereo).set(true);
190 preferences.get(Pref::OutputStereoMix).set(0);
191 } else if (s == "oerets") {
192 preferences.get(Pref::OutputStereo).set(true);
193 preferences.get(Pref::OutputStereoMix).set(100);
194 } else if (s == "mono") {
195 preferences.get(Pref::OutputStereo).set(false);
196 preferences.get(Pref::OutputStereoMix).set(0);
199 preferences.get(Pref::PreferencesVersion).set(2);
201 return true;
204 bool Recorder::sanatizePreferencesGeneric() {
205 QString s;
206 int i;
207 bool didSomething = false;
209 s = preferences.get(Pref::AutoRecordDefault).toString();
210 if (s != "ask" && s != "yes" && s != "no") {
211 preferences.get(Pref::AutoRecordDefault).set("ask");
212 didSomething = true;
215 s = preferences.get(Pref::OutputFormat).toString();
216 if (s != "mp3" && s != "vorbis" && s != "wav") {
217 preferences.get(Pref::OutputFormat).set("mp3");
218 didSomething = true;
221 i = preferences.get(Pref::OutputFormatMp3Bitrate).toInt();
222 if (i < 8 || (i < 64 && i % 8 != 0) || (i < 160 && i % 16 != 0) || i > 160) {
223 preferences.get(Pref::OutputFormatMp3Bitrate).set(64);
224 didSomething = true;
227 i = preferences.get(Pref::OutputFormatVorbisQuality).toInt();
228 if (i < -1 || i > 10) {
229 preferences.get(Pref::OutputFormatVorbisQuality).set(3);
230 didSomething = true;
233 i = preferences.get(Pref::OutputStereoMix).toInt();
234 if (i < 0 || i > 100) {
235 preferences.get(Pref::OutputStereoMix).set(0);
236 didSomething = true;
239 s = preferences.get(Pref::OutputPath).toString();
240 if (s.trimmed().isEmpty()) {
241 preferences.get(Pref::OutputPath).set("~/Skype Calls");
242 didSomething = true;
245 s = preferences.get(Pref::OutputPattern).toString();
246 if (s.trimmed().isEmpty()) {
247 preferences.get(Pref::OutputPattern).set("Calls with &s/Call with &s, %a %b %d %Y, %H:%M:%S");
248 didSomething = true;
251 if (didSomething)
252 debug("At least one preference has been reset to its default value, because it contained bogus data.");
254 return didSomething;
257 void Recorder::about() {
258 if (!aboutDialog)
259 aboutDialog = new AboutDialog;
261 aboutDialog->raise();
262 aboutDialog->activateWindow();
265 void Recorder::openWebsite() {
266 bool ret = QDesktopServices::openUrl(QUrl::fromEncoded(websiteURL));
268 if (!ret)
269 QMessageBox::information(NULL, PROGRAM_NAME,
270 QString("Failed to open URL %1").arg(websiteURL));
273 void Recorder::openPreferences() {
274 debug("Show preferences dialog");
276 if (!preferencesDialog) {
277 preferencesDialog = new PreferencesDialog();
278 connect(preferencesDialog, SIGNAL(finished(int)), this, SLOT(savePreferences()));
281 preferencesDialog->raise();
282 preferencesDialog->activateWindow();
285 void Recorder::closePerCallerDialog() {
286 debug("Hide per-caller dialog");
287 if (preferencesDialog)
288 preferencesDialog->closePerCallerDialog();
291 void Recorder::browseCalls() {
292 QString path = getOutputPath();
293 QDir().mkpath(path);
294 QUrl url = QUrl(QString("file://") + path);
295 bool ret = QDesktopServices::openUrl(url);
297 if (!ret)
298 QMessageBox::information(NULL, PROGRAM_NAME,
299 QString("Failed to open URL %1").arg(QString(url.toEncoded())));
302 void Recorder::quitConfirmation() {
303 debug("Request to quit");
304 savePreferences();
305 quit();
308 void Recorder::skypeNotify(const QString &s) {
309 QStringList args = s.split(' ');
310 QString cmd = args.takeFirst();
311 if (cmd == "CALL")
312 callHandler->callCmd(args);
315 void Recorder::skypeConnected(bool conn) {
316 if (conn)
317 debug("skype connection established");
318 else
319 debug("skype not connected");
322 void Recorder::skypeConnectionFailed(const QString &reason) {
323 debug("skype connection failed, reason: " + reason);
325 QMessageBox::critical(NULL, PROGRAM_NAME " - Error",
326 QString("The connection to Skype failed! %1 cannot operate without this "
327 "connection, please make sure you haven't blocked access from within Skype.\n\n"
328 "Internal reason for failure: %2").arg(PROGRAM_NAME, reason));
331 void Recorder::debugMessage(const QString &s) {
332 std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ").toLocal8Bit().constData()
333 << s.toLocal8Bit().constData() << "\n";
336 int main(int argc, char **argv) {
337 Recorder recorder(argc, argv);
339 return recorder.exec();