ripping.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2008  Tim Fechtner < urwald at users dot sourceforge dot net >
00003 
00004     This program is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU General Public License as
00006     published by the Free Software Foundation; either version 2 of
00007     the License or (at your option) version 3 or any later version
00008     accepted by the membership of KDE e.V. (or its successor approved
00009     by the membership of KDE e.V.), which shall act as a proxy
00010     defined in Section 14 of version 3 of the license.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 #include "ripping.h"
00022 #include "settings_general.h"
00023 #define AND  &&
00024 #define OR  ||
00025 #define NOT  !
00026 #define EQUAL  ==
00027 
00028 #include <KGlobal>
00029 #include <KLocale>
00030 #include <QFileInfo>
00031 #include "versionnumber.h"
00032 
00033 ripping::ripping(const QPointer<QObject> parent) : streamripper_base(parent)
00034 {
00035   internal_splitBehavior = QString::SkipEmptyParts;  /* makes sure that
00036   interpretate_console_output() doesn't get empty lines (which
00037   wouldn't make any sense, but only consume CPU time) */
00038 
00039   /* The internal variables who hold the properties must get initialized.
00040   *
00041   *  We use resetStreamripperProperties(), which uses the resetXXX()
00042   *  functions which use the setXXX() functions.
00043   *
00044   *  The resetStreamripperProperties() function and the resetXXX()
00045   *  functions are virtual. This means they are reimplemented in
00046   *  the inherited class "radiostation" to write directly to the
00047   *  config file. But it isn't desired. Instead we need only an
00048   *  initialation of the "PropertyValue internal_XXX;" members,
00049   *  and this initialation is necessary for the inherited class
00050   *  "get_stream_info" who relays on a working (and with "unset"
00051   *  initialised) properties system.
00052   *
00053   *  However we can safly call the reset functions in the constructor,
00054   *  because during the constructor call the reimplementations of
00055   *  the virtual functions are not yet available. */
00056   resetStreamripperProperties(); // this will emit signals just for the initialatation
00057                                   //(what's undesired),
00058                                   // but that's no problem, because during this constructor
00059                                   // is running, there can't still any slot be conneted!
00060 
00061   QObject::connect(&m_process,
00062                     SIGNAL(stateChanged(QProcess::ProcessState)),
00063                     this,
00064                     SLOT(streamripperStateChange(QProcess::ProcessState)));
00065   QObject::connect(&m_process,
00066                     SIGNAL(error(QProcess::ProcessError)),
00067                     this,
00068                     SLOT(errorOccured(QProcess::ProcessError)));
00069 }
00070 
00071 ripping::~ripping()
00072 {
00073 }
00074 
00075 void ripping::resetStreamripperProperties()
00076 {
00077   setBitrate(default_value_of_bitrate());
00078   setDataSize(default_value_of_dataSize());
00079   setError(default_value_of_error());
00080   setMetaInterval(default_value_of_metaInterval());
00081   setRelayPort(default_value_of_relayPort());
00082   setServerName(default_value_of_serverName());
00083   setSong(default_value_of_song());
00084   setStatus(default_value_of_status());
00085   setStreamName(default_value_of_streamName());
00086 }
00087 
00088 void ripping::interpretate_console_output(QStringList & stringList)
00089 {
00090   // variables
00091   QString my_line;
00092   // recognized data
00093   // (statusSet == true) means that setStatus() AND setSong() AND setDataSize() where called!
00094   bool statusSet=false;
00095   bool relayPortSet=false;
00096   bool streamNameSet=false;
00097   bool serverNameSet=false;
00098   bool bitrateSet=false;
00099   bool metaIntervalSet=false;
00100   bool errorSet=false;
00101   // help variables
00102   QString help_string;
00103   bool okay;
00104   qint64 helper_qint64;
00105 
00106   // code
00107   // recognize data
00108   while (!stringList.isEmpty()) {
00109     my_line = stringList.takeLast();  // gives back the last list entry and deletes it
00110 
00111     /* We can't do "my_line=my_line.toLower();" here (what would be very
00112     practical) because we need later the original string with upper AND lower cases when
00113     we want to recognize a song title! */
00114     if (my_line.toLower().startsWith(QString("[ripping...    ] "))) {
00115       if (!statusSet) {
00116         setStatus(is_ripping);
00117         // remove "[ripping...    ] " from the begin of the string:
00118         my_line = my_line.remove(0, 17);
00119         helper_interpretate_metainfo_and_datasize(my_line);
00120         statusSet = true;
00121       };
00122     }
00123     else if (my_line.toLower().startsWith(QString("[buffering - | ] "))) {
00124       if (!statusSet) {
00125         setStatus(is_buffering);
00126         // remove "[buffering - | ] " from the begin of the string:
00127         my_line = my_line.remove(0, 17);
00128         helper_interpretate_metainfo_and_datasize(my_line);
00129         statusSet = true;
00130       };
00131     }
00132     else if (my_line.toLower().startsWith(QString("[skipping...   ] "))) {
00133       if (!statusSet) {
00134         setStatus(is_skipping);
00135         // remove "[skipping...   ] " from the begin of the string:
00136         my_line = my_line.remove(0, 17);
00137         helper_interpretate_metainfo_and_datasize(my_line);
00138         statusSet = true;
00139       };
00140     }
00141     else if (my_line.toLower().startsWith(QString("connecting")) ||
00142               my_line.toLower().startsWith(QString("[getting track name"))) {
00143       if (!statusSet) {
00144         setStatus(is_connecting);
00145         setSong(default_value_of_song());
00146         setDataSize(default_value_of_dataSize());
00147         statusSet = true;
00148       };
00149     }
00150     else if (my_line.toLower().startsWith(QString("shutting down")) ||
00151               my_line.toLower().startsWith(QString("bye.."))) {
00152       if (!statusSet) {
00153         setStatus(is_saving);
00154         setSong(default_value_of_song());
00155         setDataSize(default_value_of_dataSize());
00156         statusSet = true;
00157       };
00158     }
00159     else if (my_line.toLower().startsWith(QString("relay port: "))) {
00160       if (!relayPortSet) {
00161         helper_qint64 = my_line.right(my_line.size()-12).toLongLong(&okay);
00162         if (NOT okay) {
00163           setRelayPort(-1);
00164         } else {
00165           setRelayPort(helper_qint64);
00166         };
00167         relayPortSet = true;
00168       };
00169     }
00170     else if (my_line.toLower().startsWith(QString("stream: "))) {
00171       if (!streamNameSet) {
00172         setStreamName(my_line.right(my_line.size()-8));
00173         streamNameSet = true;
00174       };
00175     }
00176     else if (my_line.toLower().startsWith(QString("server name: "))) {
00177       if (!serverNameSet) {
00178         setServerName(my_line.right(my_line.size()-13));
00179         serverNameSet = true;
00180       };
00181     }
00182     else if (my_line.toLower().startsWith(QString("bitrate: "))) {
00183       if (!bitrateSet) {
00184         helper_qint64 = my_line.right(my_line.size()-9).toLongLong(&okay);
00185         if (NOT okay) {
00186           setBitrate(-1);
00187         } else {
00188           setBitrate(helper_qint64);
00189         };
00190         bitrateSet = true;
00191       };
00192     }
00193     else if (my_line.toLower().startsWith(QString("declared bitrate: "))) {
00194       if (!bitrateSet) {
00195         helper_qint64 = my_line.right(my_line.size()-18).toLongLong(&okay);
00196         if (NOT okay) {
00197           setBitrate(-1);
00198         } else {
00199           setBitrate(helper_qint64);
00200         };
00201         bitrateSet = true;
00202       };
00203     }
00204     else if (my_line.toLower().startsWith(QString("meta interval: "))) {
00205       if (!metaIntervalSet) {
00206         helper_qint64 = my_line.right(my_line.size()-15).toLongLong(&okay);
00207         if (NOT okay) {
00208           setMetaInterval(-1);
00209         } else {
00210           setMetaInterval(helper_qint64);
00211         };
00212         metaIntervalSet = true;
00213       }
00214     }
00215     else if (my_line.toLower().startsWith(QString("error -"))) {
00216       if (!errorSet) {
00217         setError(my_line);
00218         errorSet = true;
00219       };
00220     } else {
00221       kDebug()
00222         << "Konnte folgenden String nicht erkennen:"
00223         << my_line
00224         << "size:"
00225         << my_line.size();
00226     };
00227   };
00228 }
00229 
00230 void ripping::helper_interpretate_metainfo_and_datasize(QString my_line)
00231 // TODO Option (Parameter) einführen: nur metainfo, nicht dataSize erkennen (geht sonst schief!)
00232 {
00233   // help variables
00234   QString help_string;
00235   bool okay;
00236   qint64 dataSize;
00237 
00238   // code
00239   help_string = my_line.section('[', -1);  /* take all characters
00240   behind the last "[" in the string and put it to help_string
00241   // TODO works only if there _is_ also a data size in the string
00242   (not the case when "buffering..."!) */
00243   my_line.truncate(my_line.size() - help_string.size() - 2); /* removes
00244   the help_string from the end and also " [" before the help_string -> so
00245   there stays only the song (Okay, it's not really the song. It's the
00246   metadata.) itself. */
00247 
00248   // metainfo
00249   setSong(my_line);
00250 
00251   // data size
00252   // remove ']' and whitespace and convert tu uppercase letters:
00253   help_string=help_string.remove(']').simplified().toUpper();
00254   if (help_string.endsWith(QString("KB")) OR help_string.endsWith('K')) {
00255     if (help_string.endsWith(QString("KB"))) {
00256       help_string.truncate(help_string.size() - 2);
00257     } else {
00258       help_string.truncate(help_string.size() - 1);
00259     };
00260     dataSize = help_string.toLongLong(&okay) * 1024;
00261   } else {
00262     if (help_string.endsWith(QString("MB")) OR help_string.endsWith('M')) {
00263       if (help_string.endsWith(QString("MB"))) {
00264         help_string.truncate(help_string.size() - 2);
00265       } else {
00266         help_string.truncate(help_string.size() - 1);
00267       };
00268       dataSize = help_string.toDouble(&okay) * 1024 * 1024;
00269     } else {
00270       if (help_string.endsWith('B')) {
00271         // == ends with "B", but not "MB" or "KB" (see previous contitions)
00272         help_string.truncate(help_string.size() - 1);
00273         dataSize = help_string.toLongLong(&okay);
00274       } else {
00275         okay = false;
00276       };
00277     };
00278   };
00279   if (NOT okay) {
00280     setDataSize(-1);
00281   } else {
00282     setDataSize(dataSize);
00283   };
00284 }
00285 
00286 PropertyValue ripping::streamName() const
00287 {
00288   return internal_streamName;
00289 }
00290 
00291 PropertyValue ripping::formatedStreamName(const QString theStreamName)
00292 {
00293     // variables
00294     PropertyValue temp_streamName;
00295 
00296     temp_streamName.internalValue = theStreamName;
00297     if (theStreamName == "Streamripper_rips") {
00298       temp_streamName.formatedValue = theStreamName;
00299       temp_streamName.type = PropertyValue::error;
00300       temp_streamName.toolTip = i18nc(
00301         "@info:tooltip Leave Streamripper_rips unchanged, it is a generic name of a directory",
00302         "Nameless stream. Using <emphasis>Streamripper_rips</emphasis> as replacement.");
00303       temp_streamName.whatsThis = i18nc(
00304         "@info:whatsthis Leave Streamripper_rips unchanged, it is a generic name of a directory",
00305         "This stream does not send a name in his meta data. "
00306           "<emphasis>Streamripper_rips</emphasis> is used as replacement, and "
00307           "you find the recorded files in the directory of the same name.");
00308     } else {
00309       if (theStreamName.isEmpty()) {
00310         temp_streamName.formatedValue = i18nc("@item", "not recognized");
00311         temp_streamName.type = PropertyValue::unset;
00312         temp_streamName.toolTip = i18nc(
00313           "@info:tooltip",
00314           "Could not connect to server.");
00315         temp_streamName.whatsThis = i18nc(
00316           "@info:whatsthis",
00317           "Could not connect to the specified server. So the stream name could not be recognized.");
00318       } else {
00319         temp_streamName.formatedValue = theStreamName;
00320         temp_streamName.type = PropertyValue::value;
00321       };
00322     };
00323 
00324     return temp_streamName;
00325 }
00326 
00327 void ripping::setStreamName(const QString newStreamName)
00328 {
00329   if (internal_streamName.internalValue.toString() != newStreamName) {
00330     internal_streamName = formatedStreamName(newStreamName);
00331     emit streamNameChanged(index(), internal_streamName);
00332   };
00333 }
00334 
00335 QString ripping::default_value_of_streamName()
00336 {
00337   return QString();
00338 }
00339 
00340 PropertyValue ripping::formatedServerName(const QString theServerName)
00341 {
00342     // variables
00343     PropertyValue temp_serverName;
00344 
00345     // code
00346     temp_serverName.internalValue = theServerName;
00347     temp_serverName.formatedValue = theServerName;
00348     if (theServerName.isEmpty()) {
00349       temp_serverName.type = PropertyValue::unset;
00350     } else {
00351       temp_serverName.type = PropertyValue::value;
00352     };
00353     // there's never a toolTip or whatsThis, so no need to clear them.
00354 
00355     return temp_serverName;
00356 }
00357 
00358 PropertyValue ripping::serverName() const
00359 {
00360   return internal_serverName;
00361 }
00362 
00363 void ripping::setServerName(const QString newServerName)
00364 {
00365   if (internal_serverName.internalValue.toString() != newServerName) {
00366     internal_serverName = formatedServerName(newServerName);
00367     emit serverNameChanged(index(), internal_serverName);
00368   };
00369 }
00370 
00371 QString ripping::default_value_of_serverName()
00372 {
00373   return QString();
00374 }
00375 
00376 PropertyValue ripping::status() const
00377 {
00378   return internal_status;
00379 }
00380 
00381 PropertyValue ripping::formatedStatus(const statusType theStatus)
00382 {
00383     // variables
00384     PropertyValue temp_status;
00385 
00386     // code
00387     temp_status.internalValue.setValue(theStatus);
00388     if (theStatus == idle) {
00389       temp_status.type = PropertyValue::unset;
00390     } else {
00391       temp_status.type = PropertyValue::value;
00392     };
00393     switch (theStatus) {
00394       case idle:
00395         temp_status.formatedValue.clear();
00396         temp_status.toolTip.clear();
00397         temp_status.whatsThis.clear();
00398         break;
00399       case is_starting:
00400         temp_status.formatedValue = i18nc("@item status of streamripper", "Starting...");
00401         temp_status.toolTip = i18nc("@info:tooltip", "Invocing Streamripper...");
00402         temp_status.whatsThis = i18nc(
00403           "@info:whatsthis",
00404           "KRadioRipper is invocing Streamripper, the program used to "
00405             "perform the recording process.");
00406         break;
00407       case is_connecting:
00408         temp_status.formatedValue = i18nc("@item status of Streamripper", "Connecting...");
00409         temp_status.toolTip = i18nc("@info:tooltip", "Connecting with the stream server...");
00410         temp_status.whatsThis = i18nc("@info:whatsthis",
00411                                       "Streamripper is connecting with the stream server.");
00412         break;
00413       case is_buffering:
00414         temp_status.formatedValue = i18nc("@item status of Streamripper", "Buffering...");
00415         temp_status.toolTip = i18nc("@info:tooltip", "Buffering stream data...");
00416         temp_status.whatsThis = i18nc(
00417           "@info:whatsthis",
00418           "Stream data is buffered before starting the recording process.");
00419         break;
00420       case is_skipping:
00421         temp_status.formatedValue = i18nc("@item status of Streamripper", "Skipping...");
00422         temp_status.toolTip = i18nc("@info:tooltip", "Skipping the actual track...");
00423         temp_status.whatsThis = i18nc(
00424           "@info:whatsthis",
00425           "<para>KRadioRipper skips by default the first track, because this track will lack "
00426             "the begin.</para><para>You can change this behavior by editing the settings of the "
00427             "stream.</para>");
00428         break;
00429       case is_ripping:
00430         temp_status.formatedValue = i18nc("@item status of Streamripper", "Recording...");
00431         temp_status.toolTip = i18nc("@info:tooltip", "Recording the stream...");
00432         temp_status.whatsThis = i18nc("@info:whatsthis", "KRadioRipper is recording the stream.");
00433         break;
00434       case is_saving:
00435         temp_status.formatedValue = i18nc("@item status of Streamripper", "Saving...");
00436         temp_status.toolTip = i18nc("@info:tooltip", "Saving files...");
00437         temp_status.whatsThis = i18nc(
00438           "@info:whatsthis",
00439           "The buffer is processed and the last files are saved.");
00440         break;
00441     };
00442 
00443     return temp_status;
00444 }
00445 
00446 void ripping::setStatus(const statusType newStatus)
00447 {
00448   if (internal_status.internalValue.value<statusType>() != newStatus) {
00449     internal_status = formatedStatus(newStatus);
00450     emit statusChanged(index(), internal_status);
00451   };
00452 
00453   bool newIsRunning = (newStatus != idle);
00454   if (internal_isRunning != newIsRunning) {
00455     internal_isRunning = newIsRunning;
00456     if (newIsRunning) {
00457       emit running();
00458     } else {
00459       emit not_running();
00460     };
00461   };
00462 }
00463 
00464 ripping::statusType ripping::default_value_of_status()
00465 {
00466   return idle;
00467 }
00468 
00469 PropertyValue ripping::error() const
00470 {
00471   return internal_error;
00472 }
00473 
00474 PropertyValue ripping::formatedError(const QString theError)
00475 {
00476   // variables
00477   PropertyValue temp_error;
00478   QString streamripper_error_number;
00479 
00480   // code
00481   temp_error.internalValue = theError;
00482 
00483   if (theError.isEmpty()) {
00484     temp_error.type = PropertyValue::unset;
00485   } else {
00486     temp_error.type = PropertyValue::value;
00487   };
00488 
00489   if (theError.startsWith(QString("error -"))) {
00490     streamripper_error_number = theError.right(theError.size() - 7);  // without "error -"
00491     streamripper_error_number = streamripper_error_number.section(' ', 0, 0); // remove " [...]"
00492     switch (streamripper_error_number.toLongLong()) {
00493       case 3: // SR_ERROR_INVALID_URL
00494       case 6: // SR_ERROR_CANT_RESOLVE_HOSTNAME
00495         temp_error.formatedValue = i18nc("@item error message", "connection failed");
00496         temp_error.toolTip = i18nc("@info:tooltip error message", "Could not connect to server");
00497         temp_error.whatsThis = i18nc(
00498           "@info:whatsthis error message",
00499           "Either the URL is invalid or the corresponding server does not exist.");
00500         break;
00501       case 7: // SR_ERROR_RECV_FAILED
00502         temp_error.formatedValue = i18nc("@item error message", "incompatible stream");
00503         temp_error.toolTip = i18nc("@info:tooltip error message",
00504                                    "KRadioRipper can not record this type of stream.");
00505         temp_error.whatsThis = i18nc("@info:whatsthis error message",
00506                                       "Streamripper (and so also KRadioRipper) can only "
00507                                       "record shoutcast and icecast streams.");
00508         break;
00509       case 36: // SR_ERROR_CANT_CREATE_FILE
00510         temp_error.formatedValue = i18nc("@item error message", "Could not write file");
00511         temp_error.toolTip = i18nc(
00512           "@info:tooltip error message",
00513           "KRadioRipper could not write the file because the download directory is not writable.");
00514         break;
00515       case 56: // HTTP:403 - Access Forbidden (try changing the UserAgent)
00516         temp_error.formatedValue = i18nc("@item error message", "connection refused");
00517         temp_error.toolTip = i18nc("@info:tooltip error message",
00518                                    "Try changing the user agent string");
00519         temp_error.whatsThis = i18nc(
00520           "@info:whatsthis error message",
00521           "<para>The server has refused the connection.</para><para>You can try to use another "
00522           "user agent string - maybe the server will accept this.</para>");
00523         break;
00524       case 64: // SR_ERROR_CANT_PARSE_PLS
00525         temp_error.formatedValue = i18nc("@item error message", "no stream");
00526         temp_error.toolTip = i18nc("@info:tooltip error message", "invalid playlist");
00527         temp_error.whatsThis = i18nc("@info:whatsthis error message",
00528                                      "The URL does not point directly to a stream, but to a "
00529                                        "playlist. And the playlist is invalid.");
00530         break;
00531       case 1001: // BAD_DOWNLOAD_DIRECTORY
00532         temp_error.formatedValue = i18nc("@item error message", "bad download directory");
00533         temp_error.toolTip = i18nc("@info:tooltip error message",
00534                                    "The download directory is not accessible.");
00535         temp_error.whatsThis = i18nc(
00536           "@info:whatsthis error message",
00537           "<para>The download directory (<filename>%1</filename>) is not accessible. Either "
00538             "it does not exist or you do not have sufficient access rights.</para><para>You "
00539             "can change the download direcoty at <emphasis>Settings</emphasis>, <emphasis>"
00540             "Configure KRadioRipper...</emphasis>, <emphasis>Saving</emphasis>"
00541             "</para>", settings_general::saveDirectory().prettyUrl());
00542         break;
00543       default:
00544         temp_error.formatedValue = theError;
00545         break;
00546     };
00547   } else {
00548     temp_error.formatedValue = theError;
00549   };
00550 
00551   return temp_error;
00552 }
00553 
00554 void ripping::setError(const QString newError)
00555 {
00556   if ((internal_error.internalValue.toString() != newError)) {
00557     /* we can't use != directly because QVariant doesn't support
00558     this for custom data types (but also doesn't generate a
00559     compiler error). */
00560     internal_error = formatedError(newError);
00561     emit errorChanged(index(), internal_error);
00562   };
00563 }
00564 
00565 QString ripping::default_value_of_error()
00566 {
00567   return QString();
00568 }
00569 
00570 PropertyValue ripping::song() const
00571 {
00572   return internal_song;
00573 }
00574 
00575 PropertyValue ripping::formatedSong(const QString theSong)
00576 {
00577     // variables
00578     PropertyValue temp_song;
00579 
00580     // code
00581     temp_song.internalValue = theSong;
00582     if ((theSong.isEmpty()) OR (theSong EQUAL " - ")) {
00583       temp_song.type = PropertyValue::unset;
00584       temp_song.formatedValue.clear();
00585     } else {
00586       temp_song.type = PropertyValue::value;
00587       temp_song.formatedValue = theSong;
00588     };
00589     // there's never a toolTip or whatsThis, so no need to clear them.
00590     return temp_song;
00591 }
00592 
00593 void ripping::setSong(const QString newSong)
00594 {
00595   if (internal_song.internalValue.toString() != newSong) {
00596     internal_song = formatedSong(newSong);
00597     emit songChanged(index(), internal_song);
00598   };
00599 }
00600 
00601 QString ripping::default_value_of_song()
00602 {
00603   return QString();
00604 }
00605 
00606 PropertyValue ripping::dataSize() const
00607 {
00608   return internal_dataSize;
00609 }
00610 
00611 PropertyValue ripping::formatedDataSize(const qint64 theDataSize)
00612 {
00613   // variables
00614   PropertyValue temp_dataSize;
00615 
00616   // code
00617   temp_dataSize.internalValue = theDataSize;
00618   if (theDataSize == (-1)) {
00619     temp_dataSize.type = PropertyValue::error;
00620     temp_dataSize.formatedValue = i18nc("@item", "error");
00621     temp_dataSize.toolTip = i18nc ("@info:tooltip", "Error determinating track size");
00622     temp_dataSize.whatsThis = i18nc (
00623       "@info:whatsthis",
00624       "The track size could not be determinated. Please report this as a bug.");
00625   } else {
00626     if (theDataSize >= 0) {
00627       temp_dataSize.type = PropertyValue::value;
00628       temp_dataSize.formatedValue = ki18nc("@item The unit is MiB instead of MB. See "
00629                                              "http://en.wikipedia.org/wiki/Binary_prefix "
00630                                              "for details.",
00631                                            "%1 MiB")
00632           .subs(double(theDataSize) / (1024 * 1024), 0, 'f', 2).toString();
00633       temp_dataSize.whatsThis = i18nc("@info:whatsthis",
00634                                        "<para>The size of the track in MiB.</para><para>MiB "
00635                                        "has a binary prefix which means 1024 * 1024 B = "
00636                                        "1048576 B (different from MB which would mean "
00637                                        "1000000 B).</para>");
00638     } else { // theDataSize < -1
00639       temp_dataSize.type = PropertyValue::unset;
00640       temp_dataSize.formatedValue.clear();
00641       temp_dataSize.whatsThis.clear();
00642     };
00643     temp_dataSize.toolTip.clear();
00644   };
00645   temp_dataSize.formatedValueAlignment = (Qt::AlignRight | Qt::AlignVCenter);
00646 
00647   return temp_dataSize;
00648 }
00649 
00650 void ripping::setDataSize(qint64 newDataSize)
00651 {
00652   if (internal_dataSize.internalValue.toLongLong() != newDataSize) {
00653     internal_dataSize = formatedDataSize(newDataSize);
00654     emit dataSizeChanged(index(), internal_dataSize);
00655   };
00656 }
00657 
00658 qint64 ripping::default_value_of_dataSize()
00659 {
00660   return (-2);
00661 }
00662 
00663 bool ripping::default_value_of_isRunning()
00664 {
00665   return false;
00666 }
00667 
00668 bool ripping::isRunning() const
00669 {
00670   return internal_isRunning;
00671 }
00672 
00673 PropertyValue ripping::relayPort() const
00674 {
00675   return internal_relayPort;
00676 }
00677 
00678 PropertyValue ripping::formatedRelayPort(qint64 theRelayPort)
00679 {
00680     // variables
00681     PropertyValue temp_relayPort;
00682 
00683     // code
00684     temp_relayPort.internalValue = theRelayPort;
00685     if (theRelayPort == (-1)) {
00686       temp_relayPort.type = PropertyValue::error;
00687       temp_relayPort.formatedValue = i18nc(
00688         "@item",
00689         "error");
00690       temp_relayPort.toolTip = i18nc (
00691         "@info:tooltip",
00692         "Error determinating relay server port");
00693       temp_relayPort.whatsThis = i18nc (
00694         "@info:whatsthis",
00695         "The port of the relay server could not be determinated. Please report this as a bug.");
00696     } else {
00697       if (theRelayPort >= 0) {
00698         temp_relayPort.type = PropertyValue::value;
00699         temp_relayPort.formatedValue = KGlobal::locale()->formatLong(theRelayPort);
00700       } else { // relayPort < -1
00701         temp_relayPort.type = PropertyValue::unset;
00702         temp_relayPort.formatedValue.clear();
00703       };
00704       temp_relayPort.toolTip.clear();
00705       temp_relayPort.whatsThis.clear();
00706     };
00707     temp_relayPort.formatedValueAlignment = (Qt::AlignRight | Qt::AlignVCenter);
00708 
00709     return temp_relayPort;
00710 }
00711 
00712 void ripping::setRelayPort(qint64 newRelayPort)
00713 {
00714   if (internal_relayPort.internalValue.toLongLong() != newRelayPort) {
00715     internal_relayPort = formatedRelayPort(newRelayPort);
00716     emit relayPortChanged(index(), internal_relayPort);
00717   };
00718 }
00719 
00720 qint64 ripping::default_value_of_relayPort()
00721 {
00722   return (-2);
00723 }
00724 
00725 PropertyValue ripping::bitrate() const
00726 {
00727   return internal_bitrate;
00728 }
00729 
00730 PropertyValue ripping::formatedBitrate(qint64 theBitrate)
00731 {
00732     // variables
00733     PropertyValue temp_bitrate;
00734 
00735     // code
00736     temp_bitrate.internalValue=theBitrate;
00737 
00738     if (theBitrate >= 1) {  // a valid bitrate...
00739       temp_bitrate.formatedValue = i18ncp(
00740         "@item This makes a nicly formated string for the bitrate of a stream - %1 is an integer. "
00741           "WARNING: Unit has changed! It is now kbit instead of Kibit. "
00742           "This means 1000 bit (NOT 1024).",
00743         "%1 kbit/s",
00744         "%1 kbit/s",
00745         theBitrate);
00746       temp_bitrate.type = PropertyValue::value;
00747       temp_bitrate.toolTip = i18nc("@info:tooltip", "bit rate");
00748       temp_bitrate.whatsThis = i18nc(
00749         "@info:whatsthis WARNING Unit has changed from binary prefix to SI prefix",
00750         "<para>The bit rate of the stream in kbit/s.</para><para>kbit has an SI prefix which "
00751           "means 1000 bit (different from kbit which would mean 1024 bit). So 1 kbit/s means "
00752           "1000 bits per second.</para>");
00753     } else {
00754       if (theBitrate >= -1) {  // "0" or "-1" (error during recognization)
00755         temp_bitrate.formatedValue = i18nc(
00756           "@item This makes a nicly formated string for the bitrate of a stream.",
00757           "Unable to recognize bitrate.");
00758         temp_bitrate.type = PropertyValue::error;
00759         temp_bitrate.toolTip = i18nc (
00760           "@info:tooltip",
00761           "Error determinating bit rate");
00762         temp_bitrate.whatsThis = i18nc (
00763           "@info:whatsthis",
00764           "The bit rate could not be determinated. Please report this as a bug.");
00765       } else { // "-2" or smaller (no value set)
00766         temp_bitrate.formatedValue.clear();
00767         temp_bitrate.type = PropertyValue::unset;
00768         temp_bitrate.toolTip.clear();
00769         temp_bitrate.whatsThis.clear();
00770       }
00771     };
00772 
00773     temp_bitrate.formatedValueAlignment = (Qt::AlignRight | Qt::AlignVCenter);
00774 
00775     return temp_bitrate;
00776 }
00777 
00778 void ripping::setBitrate(const qint64 newBitrate)
00779 {
00780   kDebug() << "new bitrate:" << newBitrate ;
00781 
00782   if (internal_bitrate.internalValue != newBitrate) {
00783     internal_bitrate = formatedBitrate(newBitrate);
00784     emit bitrateChanged(index(), internal_bitrate);
00785   };
00786 }
00787 
00788 qint64 ripping::default_value_of_bitrate()
00789 {
00790   return (-2);
00791 }
00792 
00793 PropertyValue ripping::metaInterval() const
00794 {
00795   return internal_metaInterval;
00796 }
00797 
00798 PropertyValue ripping::formatedMetaInterval(const qint64 theMetaInterval)
00799 {
00800     // variables
00801     PropertyValue temp_metaInterval;
00802 
00803     // code
00804     temp_metaInterval.internalValue = theMetaInterval;
00805     if (theMetaInterval > 0) {     // metaInterval is 1 or bigger
00806       temp_metaInterval.formatedValue = KGlobal::locale()->formatLong(theMetaInterval);
00807       // TODO what unit has the meta data interval? Seconds? Or bytes? Insert also the unit!
00808       temp_metaInterval.type = PropertyValue::value;
00809     } else {
00810       if (theMetaInterval >= -1) { // metaInterval is 0 or -1
00811         temp_metaInterval.formatedValue = i18nc(
00812           "@item",
00813           "error");
00814         temp_metaInterval.type = PropertyValue::error;
00815       } else {
00816         temp_metaInterval.formatedValue.clear();
00817         temp_metaInterval.type = PropertyValue::unset;
00818       };
00819     };
00820     temp_metaInterval.formatedValueAlignment = (Qt::AlignRight | Qt::AlignVCenter);
00821     // TODO toolTip and whatsThis to be written...
00822 
00823     return temp_metaInterval;
00824 }
00825 
00826 void ripping::setMetaInterval(const qint64 newMetaInterval)
00827 {
00828   if (internal_metaInterval.internalValue != newMetaInterval) {
00829     internal_metaInterval = formatedMetaInterval(newMetaInterval);
00830     emit metaIntervalChanged(index(), internal_metaInterval);
00831   };
00832 }
00833 
00834 qint64 ripping::default_value_of_metaInterval()
00835 {
00836   return (-2);
00837 }
00838 
00839 void ripping::errorOccured(QProcess::ProcessError error)
00840 {
00841   // variables
00842   QFileInfo m_file_info(settings_general::streamripperCommand());
00843 
00844   // code
00845   switch (error) {
00846     case QProcess::FailedToStart:
00847       if (m_file_info.exists()) {
00848         setError(i18nc(
00849           "@item streamripper error",
00850           "Insufficient permissions to invoke Streamripper."));
00851       } else {
00852         setError(i18nc("@item streamripper error", "Streamripper binary not found."));
00853       };
00854       break;
00855     case QProcess::Crashed:
00856       setError(i18nc("@item streamripper error", "Streamripper crashed."));
00857       break;
00858     case QProcess::Timedout:
00859       setError(i18nc("@item streamripper error", "Streamripper does not react."));
00860       break;
00861     default:  //ReadError, WriteError, UnknownError
00862       setError(i18nc("@item streamripper error", "Error accessing Streamripper."));
00863       break;
00864   };
00865 }
00866 
00867 void ripping::streamripperStateChange(QProcess::ProcessState newState)
00868 {
00869   if (newState == QProcess::NotRunning) {
00870     setStatus(default_value_of_status());
00871     setSong(default_value_of_song());
00872     /* Here, we can't determinate
00873     *  - if the program has terminated normally (good) or
00874     *  - if it has crashed or
00875     *  - if it couldn't even be started, because the binary wasn't found or
00876     *    wasn't executable.
00877     *
00878     *  In the last 2 cases, we should display an error message, depending on
00879     *  _which_ error occurred. But we can't use m_process.error() to determinate
00880     *  the error type, because QProcess emits the signal stateChanged() (to which
00881     *  this slot is connected) _before_ it actualizes the property error().
00882     *  Because of this, the error message is set by the slot errorOccured(), who
00883     *  is connected to the signal error(). */
00884     setDataSize(default_value_of_dataSize());
00885     setRelayPort(default_value_of_relayPort());
00886   };
00887 }
00888 
00889 QStringList ripping::parameterList() const
00890 {
00891   // variables
00892   QStringList parameters;
00893 
00894   //code
00895   parameters.append(serverUri());
00896 
00897   parameters.append(QString("-t")); // don't override files in the incomplete dir
00898                                     // but make instead a save copy of the old file
00899                                     // by appending an index. I'm not sure if this
00900                                     // is absolutly needed, but at least it's not bad.
00901 
00902   if (NOT (settings_general::proxyServer().isEmpty())) {
00903     // use proxy server TODO use the KDE-wide proxy setting instead of this one
00904     parameters.append(QString("-p"));
00905     parameters.append(settings_general::proxyServer());
00906   }
00907 
00908   if (settings_general::createRelayServer()) { // create relay server
00909     // create a relay server at (or up to) port X!
00910     parameters.append(QString("-r"));
00911     parameters.append(QString::number(settings_general::preferedPortForRelayServer()));
00912 
00913     // the number of connections is limited to which number?
00914     parameters.append(QString("-R"));
00915     if (settings_general::limitConnections()) {
00916       parameters.append(QString::number(settings_general::limitConnectionsToX()));
00917     } else {
00918       parameters.append(QString("0"));
00919     };
00920   };
00921 
00922   parameters.append(QString("-c"));  // don't auto-reconnect  TODO: implement this on my own way!
00923 
00924   return parameters;
00925 }
00926 
00927 QString ripping::streamripperCommand() const
00928 {
00929   return settings_general::streamripperCommand();
00930 }
00931 
00932 void ripping::startStreamripper()
00933 {
00934   // variables
00935   QFileInfo * dir;
00936 
00937   // test
00938   VersionNumber * testnumber = new VersionNumber("1.2.3");
00939 
00940   // code
00941   if (m_process.state() == QProcess::NotRunning) { // TODO else if the process is
00942     // yet shutting down or has yet an error -> einreihen für späteren automatischen Neustart!
00943     setError(QString());
00944     setStatus(is_starting);
00945     /* TODO: exists the working directory? when not -> create it. Is it writable?
00946     When not -> error message. */
00947     //QFileInfo.setCaching(false);
00948     dir = new QFileInfo(settings_general::saveDirectory().toLocalFile());
00949     if (dir->exists() && dir->isAbsolute() && dir->isDir() &&
00950         dir->isReadable() && dir->isWritable()) {
00951       m_process.setWorkingDirectory(settings_general::saveDirectory().toLocalFile());
00952       streamripper_base::startStreamripper();
00953     } else {
00954       // Emulate streamripper error message to get automatically a formated message:
00955       setError("error -1001 [BAD_DOWNLOAD_DIRECTORY]");
00956       setStatus(idle);
00957     };
00958   };
00959 }
00960 
00961 void ripping::shutDown()
00962 {
00963   m_process.terminate();
00964   /* TODO eventuelle Einreihung für Neustart wieder löschen! (wenn jemand startet,
00965   während gerade runtergefahren wird) */
00966   // TODO reimplementiere auch kill()
00967 }
00968 
00969 bool ripping::doesTheUserWantsThatTheStreamIsRipping(ripping::statusType theStatus)
00970 {
00971   return (!((theStatus == ripping::idle) OR (theStatus == ripping::is_saving)));
00972 }
00973 
00974 bool ripping::doesTheUserWantsThatTheStreamIsRipping()
00975 {
00976   return doesTheUserWantsThatTheStreamIsRipping(
00977            internal_status.internalValue.value<statusType>());
00978 }

Generated on Sat May 2 10:43:44 2009 for kradioripper by  doxygen 1.5.6