From df53a4bf215820bfde1ef9d7a9c2e79361a3af55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 5 Jul 2016 18:09:27 +0200 Subject: [PATCH] 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. --- src/bin/cddb_lookup/cddb_lookup.cpp | 220 ++++++++++++++++++++++++---- src/bin/cddb_lookup/cddb_server.cpp | 26 ++-- src/bin/cddb_lookup/cddb_server.h | 8 +- 3 files changed, 213 insertions(+), 41 deletions(-) diff --git a/src/bin/cddb_lookup/cddb_lookup.cpp b/src/bin/cddb_lookup/cddb_lookup.cpp index ea5b0040ed..774842a0bb 100644 --- a/src/bin/cddb_lookup/cddb_lookup.cpp +++ b/src/bin/cddb_lookup/cddb_lookup.cpp @@ -8,6 +8,7 @@ */ +#include #include #include #include @@ -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 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 ] [-i |]\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; } diff --git a/src/bin/cddb_lookup/cddb_server.cpp b/src/bin/cddb_lookup/cddb_server.cpp index 0921e9126c..8840948715 100644 --- a/src/bin/cddb_lookup/cddb_server.cpp +++ b/src/bin/cddb_lookup/cddb_server.cpp @@ -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; diff --git a/src/bin/cddb_lookup/cddb_server.h b/src/bin/cddb_lookup/cddb_server.h index a8037911e4..48f8cac1e9 100644 --- a/src/bin/cddb_lookup/cddb_server.h +++ b/src/bin/cddb_lookup/cddb_server.h @@ -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);