• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdemultimedia API Reference
  • KDE Home
  • Contact Us
 

libkcddb

  • sources
  • kde-4.14
  • kdemultimedia
  • libkcddb
  • libkcddb
cdinfo.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
3  Copyright (C) 2002-2005 Benjamin Meyer <ben-devel@meyerhome.net>
4  Copyright (C) 2002-2004 Nadeem Hasan <nhasan@nadmm.com>
5  Copyright (C) 2006 Richard Lärkäng <nouseforaname@home.se>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include "cdinfo.h"
24 
25 #include "client.h"
26 #include "cddb.h"
27 
28 #include <kdebug.h>
29 #include <kstringhandler.h>
30 
31 #include <QMap>
32 
33 namespace KCDDB
34 {
35  class InfoBasePrivate {
36  public:
41  static QString
42  createLine(const QString& name, const QString& value)
43  {
44  Q_ASSERT(name.length() < 254);
45 
46  int maxLength = 256 - name.length() - 2;
47 
48  QString tmpValue = escape(value);
49 
50  QString lines;
51 
52  while (tmpValue.length() > maxLength)
53  {
54  lines += QString::fromLatin1("%1=%2\n").arg(name,tmpValue.left(maxLength));
55  tmpValue = tmpValue.mid(maxLength);
56  }
57 
58  lines += QString::fromLatin1("%1=%2\n").arg(name,tmpValue);
59 
60  return lines;
61  }
62 
66  static QString
67  escape( const QString &value )
68  {
69  QString s = value;
70  s.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );
71  s.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
72  s.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );
73 
74  return s;
75  }
76 
80  static QString
81  unescape( const QString &value )
82  {
83  QString s = value;
84 
85  s.replace( QLatin1String( "\\n" ), QLatin1String( "\n" ) );
86  s.replace( QLatin1String( "\\t" ), QLatin1String( "\t" ) );
87  s.replace( QLatin1String( "\\\\" ), QLatin1String( "\\" ) );
88 
89  return s;
90  }
91 
92  QVariant
93  get(const QString& type)
94  {
95  return data[type.toUpper()];
96  }
97  QVariant
98  get(Type type)
99  {
100  switch(type){
101  case(Title):
102  return get(QLatin1String( "title" ));
103  case(Comment):
104  return get(QLatin1String( "comment" ));
105  case(Artist):
106  return get(QLatin1String( "artist" ));
107  case(Genre):
108  return get(QLatin1String( "genre" ));
109  case(Year):
110  return get(QLatin1String( "year" ));
111  case(Length):
112  return get(QLatin1String( "length" ));
113  case(Category):
114  return get(QLatin1String( "category" ));
115  }
116  return QVariant();
117  }
118 
119  void
120  set(const QString& type, const QVariant &d)
121  {
122  //kDebug() << "set: " << type << ", " << d.toString();
123  if(type.contains(QRegExp( QLatin1String( "^T.*_.*$" )) )){
124  kDebug(60010) << "Error: custom cdinfo::set data can not start with T and contain a _";
125  return;
126  }
127  if(type.toUpper() == QLatin1String( "DTITLE" )){
128  kDebug(60010) << "Error: type: DTITLE is reserved and can not be set.";
129  return;
130  }
131 
132  data[type.toUpper()] = d;
133  }
134  void
135  set(Type type, const QVariant &d)
136  {
137  switch(type)
138  {
139  case(Title):
140  set(QLatin1String( "title" ), d);
141  return;
142  case(Comment):
143  set(QLatin1String( "comment" ), d);
144  return;
145  case(Artist):
146  set(QLatin1String( "artist" ), d);
147  return;
148  case(Genre):
149  set(QLatin1String( "genre" ), d);
150  return;
151  case(Year):
152  set(QLatin1String( "year" ), d);
153  return;
154  case(Length):
155  set(QLatin1String( "length" ), d);
156  return;
157  case(Category):
158  set(QLatin1String( "category" ), d);
159  return;
160  }
161 
162  Q_ASSERT(false);
163  }
164 
165  // Appends text to data instead of overwriting it
166  void
167  append(const QString& type, const QString& text)
168  {
169  set(type, get(type).toString().append(text));
170  }
171  void
172  append(Type type, const QString& text)
173  {
174  set(type, get(type).toString().append(text));
175  }
176 
177  QMap<QString, QVariant> data;
178  } ;
179 
180  class TrackInfoPrivate : public InfoBasePrivate {
181  };
182 
183  TrackInfo::TrackInfo()
184  {
185  d = new TrackInfoPrivate();
186  }
187 
188  TrackInfo::TrackInfo(const TrackInfo& clone)
189  : d(new TrackInfoPrivate)
190  {
191  d->data = clone.d->data;
192  }
193 
194  TrackInfo::~TrackInfo()
195  {
196  delete d;
197  }
198 
199  TrackInfo& TrackInfo::operator=(const TrackInfo& clone)
200  {
201  d->data = clone.d->data;
202  return *this;
203  }
204 
205  QVariant TrackInfo::get(Type type) const {
206  return d->get(type);
207  }
208 
209  QVariant TrackInfo::get(const QString &type) const {
210  return d->get(type);
211  }
212 
213  void TrackInfo::set(const QString &type, const QVariant &data){
214  d->set(type, data);
215  }
216 
217  void TrackInfo::set(Type type, const QVariant &data) {
218  d->set(type, data);
219  }
220 
221  void TrackInfo::clear(){
222  d->data.clear();
223  }
224 
225  QString TrackInfo::toString() const {
226  QString out;
227  bool ok;
228  int track = get(QLatin1String( "tracknumber" )).toInt(&ok);
229  if(!ok)
230  kDebug(60010) << "Warning toString() on a track that doesn't have track number assigned.";
231  QMap<QString, QVariant>::const_iterator i = d->data.constBegin();
232  while (i != d->data.constEnd()) {
233  if(i.key() != QLatin1String( "COMMENT" ) && i.key() != QLatin1String( "TITLE" ) && i.key() != QLatin1String( "ARTIST" ) && i.key() != QLatin1String( "TRACKNUMBER" )) {
234  out += d->createLine(QString::fromLatin1("T%1_%2").arg(i.key()).arg(track),i.value().toString());
235  }
236  ++i;
237  }
238  return out;
239  }
240 
241  bool TrackInfo::operator==( const TrackInfo& other ) const
242  {
243  return d->data == other.d->data;
244  }
245 
246  bool TrackInfo::operator!=( const TrackInfo& other ) const
247  {
248  return d->data != other.d->data;
249  }
250 
251  class CDInfoPrivate : public InfoBasePrivate {
252  public:
253  TrackInfoList trackInfoList;
254  };
255 
256  CDInfo::CDInfo()
257  : d(new CDInfoPrivate())
258  {
259  set(QLatin1String( "revision" ), 0);
260  }
261 
262  CDInfo::CDInfo(const CDInfo& clone)
263  : d(new CDInfoPrivate())
264  {
265  d->data = clone.d->data;
266  d->trackInfoList = clone.d->trackInfoList;
267  }
268 
269  CDInfo::~CDInfo()
270  {
271  delete d;
272  }
273 
274  CDInfo& CDInfo::operator=(const CDInfo& clone)
275  {
276  d->trackInfoList = clone.d->trackInfoList;
277  d->data = clone.d->data;
278  return *this;
279  }
280 
281  bool
282  CDInfo::load(const QString & string)
283  {
284  return load(string.split(QLatin1Char( '\n' ),QString::SkipEmptyParts));
285  }
286 
287  bool
288  CDInfo::load(const QStringList & lineList)
289  {
290  clear();
291 
292  // We'll append to this until we've seen all the lines, then parse it after.
293  QString dtitle;
294 
295  QStringList::ConstIterator it = lineList.begin();
296 
297  QRegExp rev(QLatin1String( "# Revision: (\\d+)" ));
298  QRegExp eol(QLatin1String( "[\r\n]" ));
299 
300  while ( it != lineList.end() )
301  {
302  QString line(*it);
303  line.replace(eol,QLatin1String( "" ));
304  ++it;
305 
306  if (rev.indexIn(line) != -1)
307  {
308  set(QLatin1String( "revision" ), rev.cap(1).toUInt());
309  continue;
310  }
311 
312  QStringList tokenList = KStringHandler::perlSplit(QLatin1Char( '=' ), line, 2);
313 
314  if (2 != tokenList.count())
315  {
316  continue;
317  }
318 
319  QString key = tokenList[0].trimmed();
320  QString value = d->unescape ( tokenList[1] );
321 
322  if ( QLatin1String( "DTITLE" ) == key )
323  {
324  dtitle += value;
325  }
326  else if ( key.startsWith(QLatin1String( "TTITLE" )) )
327  {
328  uint trackNumber = key.mid(6).toUInt();
329 
330  TrackInfo& ti = track(trackNumber);
331  ti.set(Title, ti.get(Title).toString().append(value));
332  }
333 
334  else if ( QLatin1String( "EXTD" ) == key )
335  {
336  d->append(Comment, value);
337  }
338  else if ( QLatin1String( "DGENRE" ) == key )
339  {
340  d->append(Genre, value);
341  }
342  else if ( QLatin1String( "DYEAR" ) == key )
343  {
344  set(Year, value);
345  }
346  else if ( key.startsWith(QLatin1String( "EXTT" )) )
347  {
348  uint trackNumber = key.mid( 4 ).toUInt();
349 
350  checkTrack( trackNumber );
351 
352  QString extt = track(trackNumber).get(Comment).toString();
353  track(trackNumber).set(Comment, extt+value );
354  }
355  else if ( key.startsWith(QLatin1String( "T" )) )
356  {
357  // Custom Track data
358  uint trackNumber = key.mid( key.indexOf(QLatin1Char( '_' ))+1 ).toUInt();
359  checkTrack( trackNumber );
360 
361  QRegExp data(QString::fromLatin1("^T.*_%1$").arg(trackNumber));
362  if ( key.contains( data ) )
363  {
364  QString k = key.mid(1, key.indexOf(QLatin1Char( '_' ))-1);
365  TrackInfo& ti = track(trackNumber);
366  ti.set( k, ti.get(k).toString().append(value) );
367  }
368  }
369  else
370  {
371  // Custom Disk data
372  d->append( key, value );
373  }
374  }
375 
376  int slashPos = dtitle.indexOf(QLatin1String( " / " ));
377 
378  if (-1 == slashPos)
379  {
380  // Use string for title _and_ artist.
381  set(Artist, dtitle);
382  set(Title, dtitle);
383  }
384  else
385  {
386  set(Artist, dtitle.left(slashPos).trimmed());
387  set(Title, dtitle.mid(slashPos + 3).trimmed());
388  }
389 
390  bool isSampler = true;
391  for (TrackInfoList::Iterator it = d->trackInfoList.begin(); it != d->trackInfoList.end(); ++it)
392  {
393  if (!(*it).get(Title).toString().contains(QLatin1String( " / " )))
394  {
395  isSampler = false;
396  }
397  }
398  for (TrackInfoList::Iterator it = d->trackInfoList.begin(); it != d->trackInfoList.end(); ++it)
399  {
400  if (isSampler)
401  {
402  int delimiter = (*it).get(Title).toString().indexOf(QLatin1String( " / " ));
403  (*it).set(Artist, (*it).get(Title).toString().left(delimiter));
404  (*it).set(Title, (*it).get(Title).toString().mid(delimiter + 3));
405  }
406  else
407  {
408  (*it).set(Artist, get(Artist));
409  }
410  }
411 
412  if ( get(Genre).toString().isEmpty() )
413  set(Genre, QLatin1String( "Unknown" ));
414 
415  kDebug(60010) << "Loaded CDInfo for " << get(QLatin1String( "discid" )).toString();
416 
417  return true;
418  }
419 
420  QString
421  CDInfo::toString(bool submit) const
422  {
423  QString s;
424 
425  if (get(QLatin1String( "revision" )) != 0)
426  s += QLatin1String( "# Revision: " ) + get(QLatin1String( "revision" )).toString() + QLatin1Char( '\n' );
427 
428  // If we are submiting make it a fully compliant CDDB entry
429  if (submit)
430  {
431  s += QLatin1String( "#\n" );
432  s += QString::fromLatin1("# Submitted via: %1 %2\n").arg(CDDB::clientName(),
433  CDDB::clientVersion());
434  }
435 
436  s += d->createLine(QLatin1String( "DISCID" ), get(QLatin1String( "discid" )).toString() );
437  QString artist = get(Artist).toString();
438  s += d->createLine(QLatin1String( "DTITLE" ), artist + QLatin1String( " / " ) + get(Title).toString() );
439  int year = get(Year).toInt();
440  s += QLatin1String( "DYEAR=" ) + (0 == year ? QString::null : QString::number(year)) + QLatin1Char( '\n' ); //krazy:exclude=nullstrassign for old broken gcc
441  if (get(Genre) == QLatin1String( "Unknown" ))
442  s += d->createLine(QLatin1String( "DGENRE" ), QString());
443  else
444  s += d->createLine(QLatin1String( "DGENRE" ),get(Genre).toString());
445 
446  bool isSampler = false;
447  for (int i = 0; i < d->trackInfoList.count(); ++i){
448  QString trackArtist = d->trackInfoList[i].get(Artist).toString();
449  if (!trackArtist.isEmpty() && trackArtist != artist)
450  {
451  isSampler = true;
452  break;
453  }
454  }
455 
456  for (int i = 0; i < d->trackInfoList.count(); ++i){
457  QString trackTitle = d->trackInfoList[i].get(Title).toString();
458  QString trackArtist = d->trackInfoList[i].get(Artist).toString();
459  if (isSampler)
460  {
461  if (trackArtist.isEmpty())
462  s += d->createLine(QString::fromLatin1("TTITLE%1").arg(i), QString::fromLatin1("%1 / %2").arg(artist).arg(trackTitle));
463  else
464  s += d->createLine(QString::fromLatin1("TTITLE%1").arg(i), QString::fromLatin1("%1 / %2").arg(trackArtist).arg(trackTitle));
465  }
466  else
467  {
468  s += d->createLine(QString::fromLatin1("TTITLE%1").arg(i), trackTitle);
469  }
470  }
471 
472  s += d->createLine(QLatin1String("EXTD"), get(Comment).toString());
473 
474  for (int i = 0; i < d->trackInfoList.count(); ++i)
475  s += d->createLine(QString::fromLatin1("EXTT%1").arg(i), d->trackInfoList[i].get(Comment).toString());
476 
477  if (submit)
478  {
479  s += d->createLine(QLatin1String( "PLAYORDER" ), QString());
480  return s;
481  }
482 
483  s += d->createLine(QLatin1String( "PLAYORDER" ), get(QLatin1String( "playorder" )).toString() );
484 
485  // Custom track data
486  for (int i = 0; i < d->trackInfoList.count(); ++i)
487  s += d->trackInfoList[i].toString();
488 
489  QStringList cddbKeywords;
490  cddbKeywords
491  << QLatin1String( "DISCID" )
492  << QLatin1String( "ARTIST" )
493  << QLatin1String( "TITLE" )
494  << QLatin1String( "COMMENT" )
495  << QLatin1String( "YEAR" )
496  << QLatin1String( "GENRE" )
497  << QLatin1String( "PLAYORDER" )
498  << QLatin1String( "CATEGORY" )
499  << QLatin1String( "REVISION" );
500 
501  // Custom disc data
502  QMap<QString, QVariant>::const_iterator i = d->data.constBegin();
503  while (i != d->data.constEnd()){
504  if (!cddbKeywords.contains(i.key()) && i.key() != QLatin1String( "SOURCE" ))
505  {
506  s+= d->createLine(i.key(), i.value().toString());
507  }
508  ++i;
509  }
510 
511  return s;
512  }
513 
514  QVariant CDInfo::get(Type type) const {
515  return d->get(type);
516  }
517 
518  QVariant CDInfo::get(const QString &type) const {
519  return d->get(type);
520  }
521 
522  void CDInfo::set(const QString &type, const QVariant &data){
523  d->set(type, data);
524  }
525 
526  void CDInfo::set(Type type, const QVariant &data) {
527  d->set(type, data);
528  }
529 
530 
531  void
532  CDInfo::checkTrack( int trackNumber )
533  {
534  while ( d->trackInfoList.count() <= trackNumber ){
535  int count = d->trackInfoList.count();
536  d->trackInfoList.append(TrackInfo());
537  d->trackInfoList[count].set(QLatin1String( "tracknumber" ), count);
538  }
539  }
540 
541  void
542  CDInfo::clear()
543  {
544  d->data.clear();
545  d->trackInfoList.clear();
546  }
547 
548  bool
549  CDInfo::isValid() const
550  {
551  QString discid = get(QLatin1String( "DISCID" )).toString();
552  if (discid.isEmpty())
553  return false;
554 
555  if (discid == QLatin1String( "0" ))
556  return false;
557 
558  return true;
559  }
560 
561  TrackInfo &
562  CDInfo::track( int trackNumber )
563  {
564  checkTrack( trackNumber );
565  return d->trackInfoList[trackNumber];
566  }
567 
568  TrackInfo
569  CDInfo::track( int trackNumber ) const
570  {
571  if (trackNumber < d->trackInfoList.count())
572  return d->trackInfoList[trackNumber];
573  else
574  {
575  kWarning() << "Couldn't find track " << trackNumber;
576  return TrackInfo();
577  }
578  }
579 
580  int
581  CDInfo::numberOfTracks() const
582  {
583  return d->trackInfoList.count();
584  }
585 
586  bool CDInfo::operator==( const CDInfo& other ) const
587  {
588  return( d->data == other.d->data &&
589  d->trackInfoList == other.d->trackInfoList );
590  }
591 
592  bool CDInfo::operator!=( const CDInfo& other ) const
593  {
594  return( d->data != other.d->data ||
595  d->trackInfoList != other.d->trackInfoList );
596  }
597 }
598 
599 // vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
KCDDB::CDDB::clientName
static QString clientName()
Definition: cddb.h:41
QRegExp::cap
QString cap(int nth) const
QString::append
QString & append(QChar ch)
QString::toUpper
QString toUpper() const
KCDDB::Title
The title of the track or CD.
Definition: cdinfo.h:36
KCDDB::TrackInfo::clear
void clear()
internal
Definition: cdinfo.cpp:221
KCDDB::Category
The freedb category of the entry.
Definition: cdinfo.h:46
KCDDB::Length
The length of a track or a full CD.
Definition: cdinfo.h:44
QMap< QString, QVariant >
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
KCDDB::TrackInfo::operator!=
bool operator!=(const TrackInfo &) const
Definition: cdinfo.cpp:246
KCDDB::CDInfo::get
QVariant get(const QString &type) const
Get data for type that has been assigned to this disc.
Definition: cdinfo.cpp:518
KCDDB::TrackInfo::get
QVariant get(const QString &type) const
Get data for type that has been assigned to this track.
Definition: cdinfo.cpp:209
KCDDB::TrackInfo::operator=
TrackInfo & operator=(const TrackInfo &clone)
Definition: cdinfo.cpp:199
KCDDB::CDInfo::CDInfo
CDInfo()
Definition: cdinfo.cpp:256
KCDDB::Genre
The genre of the track or CD.
Definition: cdinfo.h:39
KCDDB::Comment
A comment for the track or CD.
Definition: cdinfo.h:37
cdinfo.h
QRegExp::indexIn
int indexIn(const QString &str, int offset, CaretMode caretMode) const
QRegExp
QString::number
QString number(int n, int base)
QList::count
int count(const T &value) const
KCDDB::CDDB::clientVersion
static QString clientVersion()
Definition: cddb.h:42
KCDDB::CDInfo
Information about a CD.
Definition: cdinfo.h:117
KCDDB::Type
Type
The most common types.
Definition: cdinfo.h:34
KCDDB::TrackInfo::toString
QString toString() const
Definition: cdinfo.cpp:225
QString::isEmpty
bool isEmpty() const
KCDDB::CDInfo::track
TrackInfo & track(int trackNumber)
Returns track with nr trackNumber and adds it to the track list if it doesn't exist (first track is 0...
Definition: cdinfo.cpp:562
QString::trimmed
QString trimmed() const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
KCDDB::TrackInfo::~TrackInfo
virtual ~TrackInfo()
Definition: cdinfo.cpp:194
QList< TrackInfo >::Iterator
typedef Iterator
KCDDB::CDInfo::set
void set(const QString &type, const QVariant &data)
Set any data from this disc.
Definition: cdinfo.cpp:522
QString
QList< TrackInfo >
KCDDB::CDInfo::operator=
CDInfo & operator=(const CDInfo &clone)
Definition: cdinfo.cpp:274
KCDDB::Artist
The artist of the track or CD.
Definition: cdinfo.h:38
QStringList
QList::end
iterator end()
QString::contains
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QVariant::clear
void clear()
QLatin1Char
QMap::key
const Key key(const T &value) const
cddb.h
QString::replace
QString & replace(int position, int n, QChar after)
KCDDB::CDInfo::~CDInfo
virtual ~CDInfo()
Definition: cdinfo.cpp:269
QString::mid
QString mid(int position, int n) const
QTest::toString
char * toString(const T &value)
QLatin1String
Qt::escape
QString escape(const QString &plain)
KCDDB::CDInfo::clear
void clear()
Clear all information, setting this to invalid internal.
Definition: cdinfo.cpp:542
KCDDB::Year
The year the cd or track was produced By default, the year of the track is the same as for the whole ...
Definition: cdinfo.h:40
QList::ConstIterator
typedef ConstIterator
QString::length
int length() const
KCDDB::CDInfo::toString
QString toString(bool submit=false) const
Definition: cdinfo.cpp:421
QString::left
QString left(int n) const
QString::fromLatin1
QString fromLatin1(const char *str, int size)
KCDDB::TrackInfo::operator==
bool operator==(const TrackInfo &) const
Definition: cdinfo.cpp:241
client.h
KCDDB::CDInfo::isValid
bool isValid() const
Definition: cdinfo.cpp:549
KCDDB::TrackInfo
Information about a sepecific track in a cd.
Definition: cdinfo.h:55
KCDDB::CDInfo::checkTrack
void checkTrack(int trackNumber)
Checks to make sure that trackNumber exists.
Definition: cdinfo.cpp:532
KCDDB::CDInfo::load
bool load(const QString &string)
Load CDInfo from a string that is CDDB compatible.
Definition: cdinfo.cpp:282
KCDDB::TrackInfo::TrackInfo
TrackInfo()
Definition: cdinfo.cpp:183
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QVariant::toString
QString toString() const
KCDDB::TrackInfo::set
void set(const QString &type, const QVariant &data)
Set any data from this track.
Definition: cdinfo.cpp:213
QList::begin
iterator begin()
QMap::value
const T value(const Key &key) const
QString::toUInt
uint toUInt(bool *ok, int base) const
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:28:13 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

libkcddb

Skip menu "libkcddb"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdemultimedia API Reference

Skip menu "kdemultimedia API Reference"
  • libkcddb
  • libkcompactdisc

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal