cddb_lookup: Added options, allow dump/verbose output.
* Added option to dump the track info, and optionally not write the data back. * Added option to use an alternative server. * Added option to dump verbose track info. * Added ability to query the CDDB without actual CD in the drive by providing category and CDDB ID. * Added help text.
This commit is contained in:
parent
768544f677
commit
df53a4bf21
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -30,11 +31,17 @@ public:
|
||||
CDDBLookup();
|
||||
virtual ~CDDBLookup();
|
||||
|
||||
void LookupAll();
|
||||
status_t Lookup(const dev_t device);
|
||||
void LookupAll(CDDBServer& server, bool dumpOnly,
|
||||
bool verbose);
|
||||
status_t Lookup(CDDBServer& server, const char* path,
|
||||
bool dumpOnly, bool verbose);
|
||||
status_t Lookup(CDDBServer& server, const dev_t device,
|
||||
bool dumpOnly, bool verbose);
|
||||
status_t Dump(CDDBServer& server, const char* category,
|
||||
const char* cddbID);
|
||||
|
||||
private:
|
||||
bool _CanLookup(const dev_t device, uint32* cddbId,
|
||||
bool _ReadTOC(const dev_t device, uint32* cddbID,
|
||||
scsi_toc_toc* toc) const;
|
||||
const QueryResponseData*
|
||||
_SelectResult(
|
||||
@ -42,12 +49,24 @@ private:
|
||||
status_t _WriteCDData(dev_t device,
|
||||
const QueryResponseData& diskData,
|
||||
const ReadResponseData& readResponse);
|
||||
void _Dump(const ReadResponseData& readResponse)
|
||||
const;
|
||||
};
|
||||
|
||||
|
||||
static struct option const kLongOptions[] = {
|
||||
{"info", required_argument, 0, 'i'},
|
||||
{"dump", no_argument, 0, 'd'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
extern const char *__progname;
|
||||
static const char *kProgramName = __progname;
|
||||
|
||||
static const char* kDefaultServerAddress = "freedb.freedb.org:80";
|
||||
static const char* kCddaFsName = "cdda";
|
||||
static const int kMaxTocSize = 1024;
|
||||
|
||||
@ -65,36 +84,54 @@ CDDBLookup::~CDDBLookup()
|
||||
|
||||
|
||||
void
|
||||
CDDBLookup::LookupAll()
|
||||
CDDBLookup::LookupAll(CDDBServer& server, bool dumpOnly, bool verbose)
|
||||
{
|
||||
BVolumeRoster roster;
|
||||
BVolume volume;
|
||||
while (roster.GetNextVolume(&volume) == B_OK) {
|
||||
Lookup(volume.Device());
|
||||
Lookup(server, volume.Device(), dumpOnly, verbose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CDDBLookup::Lookup(const dev_t device)
|
||||
CDDBLookup::Lookup(CDDBServer& server, const char* path, bool dumpOnly,
|
||||
bool verbose)
|
||||
{
|
||||
BVolumeRoster roster;
|
||||
BVolume volume;
|
||||
while (roster.GetNextVolume(&volume) == B_OK) {
|
||||
fs_info info;
|
||||
if (fs_stat_dev(volume.Device(), &info) != B_OK)
|
||||
continue;
|
||||
|
||||
if (strcmp(path, info.device_name) == 0)
|
||||
return Lookup(server, volume.Device(), dumpOnly, verbose);
|
||||
}
|
||||
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CDDBLookup::Lookup(CDDBServer& server, const dev_t device, bool dumpOnly,
|
||||
bool verbose)
|
||||
{
|
||||
scsi_toc_toc* toc = (scsi_toc_toc*)malloc(kMaxTocSize);
|
||||
if (toc == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint32 cddbId;
|
||||
if (!_CanLookup(device, &cddbId, toc)) {
|
||||
uint32 cddbID;
|
||||
if (!_ReadTOC(device, &cddbID, toc)) {
|
||||
free(toc);
|
||||
fprintf(stderr, "Skipping device with id %" B_PRId32 ".\n", device);
|
||||
return B_BAD_TYPE;
|
||||
}
|
||||
|
||||
printf("Looking up CD with CDDB Id %08" B_PRIx32 ".\n", cddbId);
|
||||
|
||||
CDDBServer cddbServer("freedb.freedb.org:80");
|
||||
printf("Looking up CD with CDDB Id %08" B_PRIx32 ".\n", cddbID);
|
||||
|
||||
BObjectList<QueryResponseData> queryResponses(10, true);
|
||||
status_t result = cddbServer.Query(cddbId, toc, queryResponses);
|
||||
status_t result = server.Query(cddbID, toc, queryResponses);
|
||||
if (result != B_OK) {
|
||||
fprintf(stderr, "Error when querying CD: %s\n", strerror(result));
|
||||
free(toc);
|
||||
@ -110,28 +147,48 @@ CDDBLookup::Lookup(const dev_t device)
|
||||
}
|
||||
|
||||
ReadResponseData readResponse;
|
||||
result = cddbServer.Read(*diskData, readResponse);
|
||||
result = server.Read(*diskData, readResponse);
|
||||
if (result != B_OK) {
|
||||
fprintf(stderr, "Could not read detailed CD entry from server: %s\n",
|
||||
strerror(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = _WriteCDData(device, *diskData, readResponse);
|
||||
if (result == B_OK)
|
||||
printf("CD data saved.\n");
|
||||
else
|
||||
fprintf(stderr, "Error writing CD data: %s\n", strerror(result));
|
||||
if (verbose || dumpOnly)
|
||||
_Dump(readResponse);
|
||||
|
||||
if (!dumpOnly) {
|
||||
result = _WriteCDData(device, *diskData, readResponse);
|
||||
if (result == B_OK)
|
||||
printf("CD data saved.\n");
|
||||
else
|
||||
fprintf(stderr, "Error writing CD data: %s\n", strerror(result));
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CDDBLookup::Dump(CDDBServer& server, const char* category, const char* cddbID)
|
||||
{
|
||||
ReadResponseData readResponse;
|
||||
status_t status = server.Read(category, cddbID, "", readResponse);
|
||||
if (status != B_OK) {
|
||||
fprintf(stderr, "Could not read detailed CD entry from server: %s\n",
|
||||
strerror(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
_Dump(readResponse);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CDDBLookup::_CanLookup(const dev_t device, uint32* cddbId,
|
||||
CDDBLookup::_ReadTOC(const dev_t device, uint32* cddbID,
|
||||
scsi_toc_toc* toc) const
|
||||
{
|
||||
if (cddbId == NULL || toc == NULL)
|
||||
if (cddbID == NULL || toc == NULL)
|
||||
return false;
|
||||
|
||||
// Is it an Audio disk?
|
||||
@ -147,17 +204,17 @@ CDDBLookup::_CanLookup(const dev_t device, uint32* cddbId,
|
||||
|
||||
bool doLookup;
|
||||
if (directory.ReadAttr("CD:do_lookup", B_BOOL_TYPE, 0, (void *)&doLookup,
|
||||
sizeof(bool)) < B_OK || !doLookup)
|
||||
sizeof(bool)) < B_OK || !doLookup)
|
||||
return false;
|
||||
|
||||
// Does it have the CD:cddbid attribute?
|
||||
if (directory.ReadAttr("CD:cddbid", B_UINT32_TYPE, 0, (void *)cddbId,
|
||||
sizeof(uint32)) < B_OK)
|
||||
if (directory.ReadAttr("CD:cddbid", B_UINT32_TYPE, 0, (void *)cddbID,
|
||||
sizeof(uint32)) < B_OK)
|
||||
return false;
|
||||
|
||||
// Does it have the CD:toc attribute?
|
||||
if (directory.ReadAttr("CD:toc", B_RAW_TYPE, 0, (void *)toc,
|
||||
kMaxTocSize) < B_OK)
|
||||
kMaxTocSize) < B_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -178,7 +235,7 @@ CDDBLookup::_SelectResult(const QueryResponseList& responses) const
|
||||
|
||||
for (int32 i = 0; i < numItems; i++) {
|
||||
QueryResponseData* data = responses.ItemAt(i);
|
||||
printf("* %s : %s - %s (%s)\n", data->cddbId.String(),
|
||||
printf("* %s : %s - %s (%s)\n", data->cddbID.String(),
|
||||
data->artist.String(), data->title.String(),
|
||||
data->category.String());
|
||||
}
|
||||
@ -258,15 +315,120 @@ CDDBLookup::_WriteCDData(dev_t device, const QueryResponseData& diskData,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CDDBLookup::_Dump(const ReadResponseData& readResponse) const
|
||||
{
|
||||
printf("Artist: %s\n", readResponse.artist.String());
|
||||
printf("Title: %s\n", readResponse.title.String());
|
||||
printf("Genre: %s\n", readResponse.genre.String());
|
||||
printf("Year: %" B_PRIu32 "\n", readResponse.year);
|
||||
puts("Tracks:");
|
||||
for (int32 i = 0; i < readResponse.tracks.CountItems(); i++) {
|
||||
TrackData* track = readResponse.tracks.ItemAt(i);
|
||||
if (track->artist.IsEmpty()) {
|
||||
printf(" %2" B_PRIu32 ". %s\n", track->trackNumber + 1,
|
||||
track->title.String());
|
||||
} else {
|
||||
printf(" %2" B_PRIu32 ". %s - %s\n", track->trackNumber + 1,
|
||||
track->artist.String(), track->title.String());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
int
|
||||
main(void)
|
||||
static void
|
||||
usage(int exitCode)
|
||||
{
|
||||
// TODO: support arguments to specify a device
|
||||
fprintf(exitCode == EXIT_SUCCESS ? stdout : stderr,
|
||||
"Usage: %s [-vdh] [-s <server>] [-i <category> <cddb-id>|<device>]\n"
|
||||
"\nYou can specify the device either as path on the device, or using "
|
||||
"the\ndevice name directly. If you do not specify a device, and are\n"
|
||||
"using the -i option, all volumes will be scanned for CD info.\n\n"
|
||||
" -s, --server\tUse alternative server. Default is %s.\n"
|
||||
" -v, --verbose\tVerbose output.\n"
|
||||
" -d, --dump\tDo not write attributes, only dump info to terminal.\n"
|
||||
" -h, --help\tThis help text.\n"
|
||||
" -i\t\tDump info for the specified category/cddb ID pair.\n",
|
||||
kProgramName, kDefaultServerAddress);
|
||||
exit(exitCode);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* const* argv)
|
||||
{
|
||||
const char* serverAddress = kDefaultServerAddress;
|
||||
const char* category = NULL;
|
||||
bool verbose = false;
|
||||
bool dump = false;
|
||||
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "i:s:vdh", kLongOptions, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
break;
|
||||
case 'i':
|
||||
category = optarg;
|
||||
break;
|
||||
case 's':
|
||||
serverAddress = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'd':
|
||||
dump = true;
|
||||
break;
|
||||
case 'h':
|
||||
usage(0);
|
||||
break;
|
||||
default:
|
||||
usage(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CDDBServer server(serverAddress);
|
||||
CDDBLookup cddb;
|
||||
cddb.LookupAll();
|
||||
int left = argc - optind;
|
||||
|
||||
if (category != NULL) {
|
||||
if (left != 1) {
|
||||
fprintf(stderr, "CDDB disc ID expected!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char* cddbID = argv[optind];
|
||||
cddb.Dump(server, category, cddbID);
|
||||
} else {
|
||||
// Lookup via actual CD
|
||||
if (left > 0) {
|
||||
for (int i = optind; i < argc; i++) {
|
||||
// Allow to specify a device
|
||||
const char* path = argv[i];
|
||||
status_t status;
|
||||
if (strncmp(path, "/dev/", 5) == 0) {
|
||||
status = cddb.Lookup(server, path, dump, verbose);
|
||||
} else {
|
||||
dev_t device = dev_for_path(path);
|
||||
if (device >= 0)
|
||||
status = cddb.Lookup(server, device, dump, verbose);
|
||||
else
|
||||
status = (status_t)device;
|
||||
}
|
||||
|
||||
if (status != B_OK) {
|
||||
fprintf(stderr, "Invalid path \"%s\": %s\n", path,
|
||||
strerror(status));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
} else
|
||||
cddb.LookupAll(server, dump, verbose);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ CDDBServer::CDDBServer(const BString& cddbServer)
|
||||
fInitialized(false),
|
||||
fConnected(false)
|
||||
{
|
||||
|
||||
// Set up local host name.
|
||||
char localHostName[MAXHOSTNAMELEN + 1];
|
||||
if (gethostname(localHostName, MAXHOSTNAMELEN + 1) == 0) {
|
||||
@ -50,7 +49,7 @@ CDDBServer::CDDBServer(const BString& cddbServer)
|
||||
|
||||
|
||||
status_t
|
||||
CDDBServer::Query(uint32 cddbId, const scsi_toc_toc* toc,
|
||||
CDDBServer::Query(uint32 cddbID, const scsi_toc_toc* toc,
|
||||
QueryResponseList& queryResponses)
|
||||
{
|
||||
if (_OpenConnection() != B_OK)
|
||||
@ -58,7 +57,7 @@ CDDBServer::Query(uint32 cddbId, const scsi_toc_toc* toc,
|
||||
|
||||
// Convert CDDB id to hexadecimal format.
|
||||
char hexCddbId[9];
|
||||
sprintf(hexCddbId, "%08" B_PRIx32, cddbId);
|
||||
sprintf(hexCddbId, "%08" B_PRIx32, cddbID);
|
||||
|
||||
// Assemble the Query command.
|
||||
int32 numTracks = toc->last_track + 1 - toc->first_track;
|
||||
@ -127,7 +126,7 @@ CDDBServer::Query(uint32 cddbId, const scsi_toc_toc* toc,
|
||||
output.MoveInto(responseData->category, 0, output.FindFirst(" "));
|
||||
output.Remove(0, 1);
|
||||
|
||||
output.MoveInto(responseData->cddbId, 0, output.FindFirst(" "));
|
||||
output.MoveInto(responseData->cddbID, 0, output.FindFirst(" "));
|
||||
output.Remove(0, 1);
|
||||
|
||||
output.MoveInto(responseData->artist, 0, output.FindFirst(" / "));
|
||||
@ -155,13 +154,22 @@ CDDBServer::Query(uint32 cddbId, const scsi_toc_toc* toc,
|
||||
status_t
|
||||
CDDBServer::Read(const QueryResponseData& diskData,
|
||||
ReadResponseData& readResponse)
|
||||
{
|
||||
return Read(diskData.category, diskData.cddbID, diskData.artist,
|
||||
readResponse);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CDDBServer::Read(const BString& category, const BString& cddbID,
|
||||
const BString& artist, ReadResponseData& readResponse)
|
||||
{
|
||||
if (_OpenConnection() != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
// Assemble the Read command.
|
||||
BString cddbCommand("cddb read ");
|
||||
cddbCommand << diskData.category << " " << diskData.cddbId;
|
||||
cddbCommand << category << " " << cddbID;
|
||||
|
||||
BString output;
|
||||
status_t result = _SendCommand(cddbCommand, output);
|
||||
@ -256,17 +264,15 @@ CDDBServer::Read(const QueryResponseData& diskData,
|
||||
trackData->trackNumber = track;
|
||||
|
||||
int32 pos = line.FindFirst(" / ");
|
||||
if (pos >= 0 && diskData.artist.ICompare("Various") == 0) {
|
||||
if (pos >= 0 && artist.ICompare("Various") == 0) {
|
||||
// Disk is set to have a compilation artist and
|
||||
// we have track specific artist information.
|
||||
BString artist;
|
||||
line.MoveInto(artist, 0, pos);
|
||||
line.MoveInto(trackData->artist, 0, pos);
|
||||
// Move artist information from line to artist.
|
||||
line.Remove(0, 3);
|
||||
// Remove " / " from line.
|
||||
trackData->artist = artist;
|
||||
} else {
|
||||
trackData->artist = diskData.artist;
|
||||
trackData->artist = artist;
|
||||
}
|
||||
|
||||
trackData->title = line;
|
||||
|
@ -28,7 +28,7 @@ struct TrackData {
|
||||
// Query command response.
|
||||
struct QueryResponseData {
|
||||
BString category;
|
||||
BString cddbId;
|
||||
BString cddbID;
|
||||
BString artist;
|
||||
BString title;
|
||||
};
|
||||
@ -58,10 +58,14 @@ public:
|
||||
CDDBServer(const BString& cddbServer);
|
||||
|
||||
// CDDB commands interface.
|
||||
status_t Query(uint32 cddbId, const scsi_toc_toc* toc,
|
||||
status_t Query(uint32 cddbID, const scsi_toc_toc* toc,
|
||||
QueryResponseList& queryResponses);
|
||||
status_t Read(const QueryResponseData& diskData,
|
||||
ReadResponseData& readResponse);
|
||||
status_t Read(const BString& category,
|
||||
const BString& cddbID,
|
||||
const BString& artist,
|
||||
ReadResponseData& readResponse);
|
||||
|
||||
private:
|
||||
status_t _ParseAddress(const BString& cddbServer);
|
||||
|
Loading…
Reference in New Issue
Block a user