INSTALL: Clarify name of menu category
[skype-call-recorder.git] / recorder.cpp
blob82530c8043320497d88d0bb69ebdabfe0e6046d6
1 /*
2 Skype Call Recorder
3 Copyright (C) 2008 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 <iostream>
31 #include <cstdlib>
33 #include "recorder.h"
34 #include "common.h"
35 #include "gui.h"
36 #include "trayicon.h"
37 #include "preferences.h"
38 #include "skype.h"
39 #include "call.h"
41 Recorder::Recorder(int &argc, char **argv) :
42 QApplication(argc, argv)
44 recorderInstance = this;
46 debug("Initializing application");
48 // check for already running instance
49 if (!lockFile.lock(QDir::homePath() + "/.skypecallrecorder.lock")) {
50 debug("Other instance is running");
51 QTimer::singleShot(0, this, SLOT(quit()));
52 return;
55 loadPreferences();
57 setupGUI();
58 setupSkype();
59 setupCallHandler();
62 Recorder::~Recorder() {
63 // the Recorder object must still exist while the child objects are
64 // being destroyed, because debug() must still be available. Qt would
65 // do it automatically but only after Recorder ceased to exist, in the
66 // QObject destructor
68 delete preferencesDialog;
69 delete callHandler;
70 delete skype;
71 delete trayIcon;
74 void Recorder::setupGUI() {
75 setWindowIcon(QIcon(":/icon.png"));
76 setQuitOnLastWindowClosed(false);
78 trayIcon = new TrayIcon(this);
79 connect(trayIcon, SIGNAL(requestQuit()), this, SLOT(quitConfirmation()));
80 connect(trayIcon, SIGNAL(requestAbout()), this, SLOT(about()));
81 connect(trayIcon, SIGNAL(requestWebsite()), this, SLOT(openWebsite()));
82 connect(trayIcon, SIGNAL(requestOpenPreferences()), this, SLOT(openPreferences()));
83 connect(trayIcon, SIGNAL(requestBrowseCalls()), this, SLOT(browseCalls()));
85 debug("GUI initialized");
87 if (!preferences.get(Pref::SuppressFirstRunInformation).toBool())
88 new FirstRunDialog();
91 void Recorder::setupSkype() {
92 skype = new Skype(this);
93 connect(skype, SIGNAL(notify(const QString &)), this, SLOT(skypeNotify(const QString &)));
94 connect(skype, SIGNAL(connected(bool)), this, SLOT(skypeConnected(bool)));
95 connect(skype, SIGNAL(connectionFailed(const QString &)), this, SLOT(skypeConnectionFailed(const QString &)));
97 connect(skype, SIGNAL(connected(bool)), trayIcon, SLOT(setColor(bool)));
100 void Recorder::setupCallHandler() {
101 callHandler = new CallHandler(this, skype);
103 connect(trayIcon, SIGNAL(startRecording(int)), callHandler, SLOT(startRecording(int)));
104 connect(trayIcon, SIGNAL(stopRecording(int)), callHandler, SLOT(stopRecording(int)));
105 connect(trayIcon, SIGNAL(stopRecordingAndDelete(int)), callHandler, SLOT(stopRecordingAndDelete(int)));
107 connect(callHandler, SIGNAL(startedCall(int, const QString &)), trayIcon, SLOT(startedCall(int, const QString &)));
108 connect(callHandler, SIGNAL(stoppedCall(int)), trayIcon, SLOT(stoppedCall(int)));
109 connect(callHandler, SIGNAL(startedRecording(int)), trayIcon, SLOT(startedRecording(int)));
110 connect(callHandler, SIGNAL(stoppedRecording(int)), trayIcon, SLOT(stoppedRecording(int)));
113 QString Recorder::getConfigFile() const {
114 return QDir::homePath() + "/.skypecallrecorder.rc";
117 void Recorder::loadPreferences() {
118 preferences.load(getConfigFile());
119 int c = preferences.count();
121 // since Pref::PreferencesVersion did not exist from the first version
122 // on, some people might not have it; but we cannot just let it receive
123 // the default value, as those old settings must be updated. If it is
124 // missing but Pref::OutputPath exists (which has always been around),
125 // then set Pref::PreferencesVersion to 1
126 if (!preferences.exists(Pref::PreferencesVersion) && preferences.exists(Pref::OutputPath))
127 preferences.get(Pref::PreferencesVersion).set(1);
129 #define X(n, v) preferences.get(n).setIfNotSet(v);
130 // default preferences
131 X(Pref::AutoRecordDefault, "ask"); // "yes", "ask", "no"
132 X(Pref::AutoRecordAsk, ""); // comma separated skypenames to always ask for
133 X(Pref::AutoRecordYes, ""); // comma separated skypenames to always record
134 X(Pref::AutoRecordNo, ""); // comma separated skypenames to never record
135 X(Pref::OutputPath, "~/Skype Calls");
136 X(Pref::OutputPattern, "Calls with &s/Call with &s, %a %b %d %Y, %H:%M:%S");
137 X(Pref::OutputFormat, "mp3"); // "mp3", "vorbis" or "wav"
138 X(Pref::OutputFormatMp3Bitrate, 64);
139 X(Pref::OutputFormatVorbisQuality, 3);
140 X(Pref::OutputStereo, true);
141 X(Pref::OutputStereoMix, 0); // 0 .. 100
142 X(Pref::OutputSaveTags, true);
143 X(Pref::SuppressLegalInformation, false);
144 X(Pref::SuppressFirstRunInformation, false);
145 X(Pref::PreferencesVersion, 2);
146 X(Pref::NotifyRecordingStart, true)
147 X(Pref::GuiWindowed, false)
148 X(Pref::DebugWriteSyncFile, false)
149 #undef X
151 c = preferences.count() - c;
153 if (c)
154 debug(QString("Loading %1 built-in default preference(s)").arg(c));
156 sanatizePreferences();
159 void Recorder::savePreferences() {
160 preferences.save(getConfigFile());
161 // TODO: when failure?
164 void Recorder::sanatizePreferences() {
165 // this converts old preferences to new preferences
167 int v = preferences.get(Pref::PreferencesVersion).toInt();
168 bool didSomething = false;
170 switch (v) {
171 case 1:
172 didSomething |= convertSettingsToV2();
175 didSomething |= sanatizePreferencesGeneric();
177 if (didSomething)
178 savePreferences();
181 bool Recorder::convertSettingsToV2() {
182 debug("Converting settings from v1 to v2");
184 QString s = preferences.get("output.channelmode").toString();
185 preferences.remove("output.channelmode");
187 if (s == "stereo") {
188 preferences.get(Pref::OutputStereo).set(true);
189 preferences.get(Pref::OutputStereoMix).set(0);
190 } else if (s == "oerets") {
191 preferences.get(Pref::OutputStereo).set(true);
192 preferences.get(Pref::OutputStereoMix).set(100);
193 } else if (s == "mono") {
194 preferences.get(Pref::OutputStereo).set(false);
195 preferences.get(Pref::OutputStereoMix).set(0);
198 preferences.get(Pref::PreferencesVersion).set(2);
200 return true;
203 bool Recorder::sanatizePreferencesGeneric() {
204 QString s;
205 int i;
206 bool didSomething = false;
208 s = preferences.get(Pref::AutoRecordDefault).toString();
209 if (s != "ask" && s != "yes" && s != "no") {
210 preferences.get(Pref::AutoRecordDefault).set("ask");
211 didSomething = true;
214 s = preferences.get(Pref::OutputFormat).toString();
215 if (s != "mp3" && s != "vorbis" && s != "wav") {
216 preferences.get(Pref::OutputFormat).set("mp3");
217 didSomething = true;
220 i = preferences.get(Pref::OutputFormatMp3Bitrate).toInt();
221 if (i < 8 || (i < 64 && i % 8 != 0) || (i < 160 && i % 16 != 0) || i > 160) {
222 preferences.get(Pref::OutputFormatMp3Bitrate).set(64);
223 didSomething = true;
226 i = preferences.get(Pref::OutputFormatVorbisQuality).toInt();
227 if (i < -1 || i > 10) {
228 preferences.get(Pref::OutputFormatVorbisQuality).set(3);
229 didSomething = true;
232 i = preferences.get(Pref::OutputStereoMix).toInt();
233 if (i < 0 || i > 100) {
234 preferences.get(Pref::OutputStereoMix).set(0);
235 didSomething = true;
238 s = preferences.get(Pref::OutputPath).toString();
239 if (s.trimmed().isEmpty()) {
240 preferences.get(Pref::OutputPath).set("~/Skype Calls");
241 didSomething = true;
244 s = preferences.get(Pref::OutputPattern).toString();
245 if (s.trimmed().isEmpty()) {
246 preferences.get(Pref::OutputPattern).set("Calls with &s/Call with &s, %a %b %d %Y, %H:%M:%S");
247 didSomething = true;
250 if (didSomething)
251 debug("At least one preference has been reset to its default value, because it contained bogus data.");
253 return didSomething;
256 void Recorder::about() {
257 if (!aboutDialog)
258 aboutDialog = new AboutDialog;
260 aboutDialog->raise();
261 aboutDialog->activateWindow();
264 void Recorder::openWebsite() {
265 bool ret = QDesktopServices::openUrl(QUrl::fromEncoded(websiteURL));
267 if (!ret)
268 QMessageBox::information(NULL, PROGRAM_NAME,
269 QString("Failed to open URL %1").arg(websiteURL));
272 void Recorder::openPreferences() {
273 debug("Show preferences dialog");
275 if (!preferencesDialog) {
276 preferencesDialog = new PreferencesDialog();
277 connect(preferencesDialog, SIGNAL(finished(int)), this, SLOT(savePreferences()));
280 preferencesDialog->raise();
281 preferencesDialog->activateWindow();
284 void Recorder::closePerCallerDialog() {
285 debug("Hide per-caller dialog");
286 if (preferencesDialog)
287 preferencesDialog->closePerCallerDialog();
290 void Recorder::browseCalls() {
291 QString path = getOutputPath();
292 QDir().mkpath(path);
293 QUrl url = QUrl(QString("file://") + path);
294 bool ret = QDesktopServices::openUrl(url);
296 if (!ret)
297 QMessageBox::information(NULL, PROGRAM_NAME,
298 QString("Failed to open URL %1").arg(QString(url.toEncoded())));
301 void Recorder::quitConfirmation() {
302 debug("Request to quit");
303 savePreferences();
304 quit();
307 void Recorder::skypeNotify(const QString &s) {
308 QStringList args = s.split(' ');
309 QString cmd = args.takeFirst();
310 if (cmd == "CALL")
311 callHandler->callCmd(args);
314 void Recorder::skypeConnected(bool conn) {
315 if (conn)
316 debug("skype connection established");
317 else
318 debug("skype not connected");
321 void Recorder::skypeConnectionFailed(const QString &reason) {
322 debug("skype connection failed, reason: " + reason);
324 QMessageBox::critical(NULL, PROGRAM_NAME " - Error",
325 QString("The connection to Skype failed! %1 cannot operate without this "
326 "connection, please make sure you haven't blocked access from within Skype.\n\n"
327 "Internal reason for failure: %2").arg(PROGRAM_NAME, reason));
330 void Recorder::debugMessage(const QString &s) {
331 std::cout << s.toLocal8Bit().constData() << "\n";
334 int main(int argc, char **argv) {
335 Recorder recorder(argc, argv);
337 return recorder.exec();