2008-08-21 02:33:19 +00:00
|
|
|
/*
|
2009-01-18 15:26:11 +00:00
|
|
|
* Copyright 2008-2009, Haiku, Inc. All Rights Reserved.
|
2008-08-21 02:33:19 +00:00
|
|
|
* Distributed under the terms of the MIT License.
|
2009-01-18 15:26:11 +00:00
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Bruno Albuquerque, bga@bug-br.org.br
|
2008-08-21 02:33:19 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cddb_daemon.h"
|
|
|
|
|
2009-05-11 00:55:42 +00:00
|
|
|
#include "cddb_server.h"
|
|
|
|
|
2008-08-21 02:33:19 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <Directory.h>
|
2009-05-13 00:05:53 +00:00
|
|
|
#include <Entry.h>
|
2008-08-21 02:33:19 +00:00
|
|
|
#include <NodeMonitor.h>
|
2009-05-11 00:55:42 +00:00
|
|
|
#include <Message.h>
|
2008-08-21 02:33:19 +00:00
|
|
|
#include <Volume.h>
|
2009-05-11 00:55:42 +00:00
|
|
|
#include <VolumeRoster.h>
|
2008-08-21 02:33:19 +00:00
|
|
|
|
|
|
|
#include <fs_info.h>
|
2008-08-25 17:43:10 +00:00
|
|
|
#include <stdlib.h>
|
2008-08-21 02:33:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const char* kCddaFsName = "cdda";
|
2008-08-25 17:43:10 +00:00
|
|
|
static const int kMaxTocSize = 1024;
|
2008-08-21 02:33:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
CDDBDaemon::CDDBDaemon()
|
2009-01-18 15:26:11 +00:00
|
|
|
: BApplication("application/x-vnd.Haiku-cddb_daemon"),
|
2008-08-21 14:28:20 +00:00
|
|
|
fVolumeRoster(new BVolumeRoster)
|
2008-08-21 02:33:19 +00:00
|
|
|
{
|
2008-08-21 12:37:31 +00:00
|
|
|
fVolumeRoster->StartWatching();
|
2008-08-21 02:33:19 +00:00
|
|
|
|
|
|
|
BVolume volume;
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("Checking currently mounted volumes ...\n");
|
2008-08-21 12:37:31 +00:00
|
|
|
while (fVolumeRoster->GetNextVolume(&volume) == B_OK) {
|
2009-05-11 00:55:42 +00:00
|
|
|
if (_Lookup(volume.Device()) != B_OK) {
|
2008-08-25 17:43:10 +00:00
|
|
|
continue;
|
2008-08-21 02:33:19 +00:00
|
|
|
}
|
|
|
|
}
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("Checking complete. Listening for device mounts.\n");
|
2008-08-21 02:33:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CDDBDaemon::~CDDBDaemon()
|
|
|
|
{
|
2008-08-21 12:37:31 +00:00
|
|
|
fVolumeRoster->StopWatching();
|
|
|
|
delete fVolumeRoster;
|
2008-08-21 02:33:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
CDDBDaemon::MessageReceived(BMessage* message)
|
|
|
|
{
|
|
|
|
switch(message->what) {
|
|
|
|
case B_NODE_MONITOR:
|
|
|
|
int32 opcode;
|
|
|
|
if (message->FindInt32("opcode", &opcode) == B_OK) {
|
|
|
|
if (opcode == B_DEVICE_MOUNTED) {
|
|
|
|
dev_t device;
|
|
|
|
if (message->FindInt32("new device", &device) == B_OK) {
|
2009-05-11 00:55:42 +00:00
|
|
|
if (_Lookup(device) != B_OK)
|
2008-08-25 17:43:10 +00:00
|
|
|
break;
|
2008-08-21 02:33:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BApplication::MessageReceived(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-11 00:55:42 +00:00
|
|
|
status_t
|
|
|
|
CDDBDaemon::_Lookup(const dev_t device)
|
|
|
|
{
|
|
|
|
scsi_toc_toc* toc = (scsi_toc_toc*)malloc(kMaxTocSize);
|
|
|
|
if (toc == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
uint32 cddbId;
|
|
|
|
if (!_CanLookup(device, &cddbId, toc)) {
|
|
|
|
free(toc);
|
2009-07-16 02:42:03 +00:00
|
|
|
printf("Skipping device with id %ld.\n", device);
|
2009-05-11 00:55:42 +00:00
|
|
|
return B_BAD_TYPE;
|
|
|
|
}
|
|
|
|
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("Looking up CD with CDDB Id %08lx.\n", cddbId);
|
2009-05-11 00:55:42 +00:00
|
|
|
|
|
|
|
CDDBServer cddb_server("freedb.freedb.org:80");
|
|
|
|
|
|
|
|
status_t result;
|
|
|
|
|
|
|
|
BList queryResponse;
|
|
|
|
if ((result = cddb_server.Query(cddbId, toc, &queryResponse)) != B_OK) {
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("Error when querying CD.\n");
|
2009-05-11 00:55:42 +00:00
|
|
|
free(toc);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(toc);
|
|
|
|
|
|
|
|
QueryResponseData* diskData = _SelectResult(&queryResponse);
|
|
|
|
if (diskData == NULL) {
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("Could not find any CD entries in query response.\n");
|
2009-05-11 00:55:42 +00:00
|
|
|
return B_BAD_INDEX;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadResponseData readResponse;
|
|
|
|
if ((result = cddb_server.Read(diskData, &readResponse)) != B_OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_WriteCDData(device, diskData, &readResponse) == B_OK) {
|
|
|
|
printf("CD data saved.\n");
|
|
|
|
} else {
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("Error writting CD data.\n" );
|
2009-05-11 00:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete itens in the query response BList;
|
|
|
|
int32 count = queryResponse.CountItems();
|
|
|
|
for (int32 i = 0; i < count; ++i) {
|
|
|
|
delete (QueryResponseData*)queryResponse.RemoveItem(0L);
|
|
|
|
}
|
|
|
|
|
|
|
|
queryResponse.MakeEmpty();
|
|
|
|
|
|
|
|
// Delete itens in the track data BList in the read response data;
|
|
|
|
count = readResponse.tracks.CountItems();
|
|
|
|
for (int32 i = 0; i < count; ++i) {
|
|
|
|
delete (TrackData*)readResponse.tracks.RemoveItem(0L);
|
|
|
|
}
|
|
|
|
|
|
|
|
readResponse.tracks.MakeEmpty();
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-21 02:33:19 +00:00
|
|
|
bool
|
2009-01-18 16:05:46 +00:00
|
|
|
CDDBDaemon::_CanLookup(const dev_t device, uint32* cddbId,
|
2009-01-18 15:26:11 +00:00
|
|
|
scsi_toc_toc* toc) const
|
2008-08-21 02:33:19 +00:00
|
|
|
{
|
2009-01-18 15:26:11 +00:00
|
|
|
if (cddbId == NULL || toc == NULL)
|
2008-08-25 17:43:10 +00:00
|
|
|
return false;
|
|
|
|
|
2008-08-21 02:33:19 +00:00
|
|
|
// Is it an Audio disk?
|
|
|
|
fs_info info;
|
2009-01-18 15:26:11 +00:00
|
|
|
fs_stat_dev(device, &info);
|
2008-08-21 02:33:19 +00:00
|
|
|
if (strncmp(info.fsh_name, kCddaFsName, strlen(kCddaFsName)) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Does it have the CD:do_lookup attribute and is it true?
|
2009-01-18 15:26:11 +00:00
|
|
|
BVolume volume(device);
|
2008-08-21 02:33:19 +00:00
|
|
|
BDirectory directory;
|
|
|
|
volume.GetRootDirectory(&directory);
|
|
|
|
|
|
|
|
bool doLookup;
|
|
|
|
if (directory.ReadAttr("CD:do_lookup", B_BOOL_TYPE, 0, (void *)&doLookup,
|
|
|
|
sizeof(bool)) < B_OK || !doLookup)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Does it have the CD:cddbid attribute?
|
2009-01-18 15:26:11 +00:00
|
|
|
if (directory.ReadAttr("CD:cddbid", B_UINT32_TYPE, 0, (void *)cddbId,
|
2008-08-21 02:33:19 +00:00
|
|
|
sizeof(uint32)) < B_OK)
|
|
|
|
return false;
|
|
|
|
|
2008-08-25 17:43:10 +00:00
|
|
|
// Does it have the CD:toc attribute?
|
2009-01-18 15:26:11 +00:00
|
|
|
if (directory.ReadAttr("CD:toc", B_RAW_TYPE, 0, (void *)toc,
|
2008-08-25 17:43:10 +00:00
|
|
|
kMaxTocSize) < B_OK)
|
|
|
|
return false;
|
|
|
|
|
2008-08-21 02:33:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-11 00:55:42 +00:00
|
|
|
QueryResponseData*
|
|
|
|
CDDBDaemon::_SelectResult(BList* response) const
|
|
|
|
{
|
|
|
|
// Select a single CD match from the response and return it.
|
|
|
|
//
|
|
|
|
// TODO(bga):Right now it just picks the first entry on the list but
|
|
|
|
// someday we may want to let the user choose one.
|
2009-05-17 14:15:00 +00:00
|
|
|
int32 numItems = response->CountItems();
|
|
|
|
if (numItems > 0) {
|
|
|
|
if (numItems > 1) {
|
|
|
|
printf("Multiple matches found :\n");
|
|
|
|
};
|
|
|
|
for (int32 i = 0; i < numItems; i++) {
|
|
|
|
QueryResponseData* data = (QueryResponseData*)response->ItemAt(i);
|
|
|
|
printf("* %s : %s - %s (%s)\n", (data->cddbId).String(),
|
|
|
|
(data->artist).String(), (data->title).String(),
|
|
|
|
(data->category).String());
|
|
|
|
}
|
|
|
|
if (numItems > 1) {
|
|
|
|
printf("Returning first entry.\n");
|
|
|
|
}
|
|
|
|
|
2009-05-11 00:55:42 +00:00
|
|
|
return (QueryResponseData*)response->ItemAt(0L);
|
2009-05-17 14:15:00 +00:00
|
|
|
}
|
|
|
|
|
2009-05-11 00:55:42 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
CDDBDaemon::_WriteCDData(dev_t device, QueryResponseData* diskData,
|
|
|
|
ReadResponseData* readResponse)
|
|
|
|
{
|
|
|
|
// Rename volume.
|
|
|
|
BVolume volume(device);
|
|
|
|
|
|
|
|
status_t result;
|
2009-08-10 01:26:14 +00:00
|
|
|
status_t error = B_OK;
|
|
|
|
|
2009-05-13 00:05:53 +00:00
|
|
|
BString name = diskData->artist << " - " << diskData->title;
|
2009-08-10 01:26:14 +00:00
|
|
|
name.ReplaceSet("/", " ");
|
|
|
|
|
2009-05-13 00:05:53 +00:00
|
|
|
if ((result = volume.SetName(name.String())) != B_OK) {
|
2009-05-11 00:55:42 +00:00
|
|
|
printf("Can't set volume name.\n");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-05-13 00:05:53 +00:00
|
|
|
// Rename tracks and add relevant Audio attributes.
|
|
|
|
BDirectory cddaRoot;
|
|
|
|
volume.GetRootDirectory(&cddaRoot);
|
|
|
|
|
|
|
|
BEntry entry;
|
2009-08-10 01:26:14 +00:00
|
|
|
int index = 0;
|
2009-05-13 00:05:53 +00:00
|
|
|
while (cddaRoot.GetNextEntry(&entry) == B_OK) {
|
2009-08-10 01:26:14 +00:00
|
|
|
TrackData* data = (TrackData*)((readResponse->tracks).ItemAt(index));
|
2009-05-13 00:05:53 +00:00
|
|
|
|
|
|
|
// Update name.
|
2009-08-10 01:26:14 +00:00
|
|
|
name = data->title;
|
|
|
|
name.ReplaceSet("/", " ");
|
|
|
|
|
|
|
|
if ((result = entry.Rename(name.String())) != B_OK) {
|
|
|
|
printf("Failed renaming entry at index %d to \"%s\".\n", index,
|
|
|
|
name.String());
|
|
|
|
error = result;
|
|
|
|
// User can benefit from continuing through all tracks.
|
|
|
|
// Report error later.
|
2009-05-13 00:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add relevant attributes. We consider an error here as non-fatal.
|
|
|
|
BNode node(&entry);
|
|
|
|
node.WriteAttr("Audio:Title", B_STRING_TYPE, 0, (data->title).String(),
|
|
|
|
(data->title).Length());
|
|
|
|
node.WriteAttr("Audio:Album", B_STRING_TYPE, 0,
|
|
|
|
(readResponse->title).String(),
|
|
|
|
(readResponse->title).Length());
|
|
|
|
node.WriteAttr("Audio:Genre", B_STRING_TYPE, 0,
|
|
|
|
(readResponse->genre).String(),
|
|
|
|
(readResponse->genre).Length());
|
|
|
|
node.WriteAttr("Audio:Year", B_INT32_TYPE, 0, &(readResponse->year),
|
|
|
|
sizeof(int32));
|
|
|
|
|
|
|
|
if (data->artist == "") {
|
|
|
|
node.WriteAttr("Audio:Artist", B_STRING_TYPE, 0,
|
|
|
|
(readResponse->artist).String(),
|
|
|
|
(readResponse->artist).Length());
|
|
|
|
} else {
|
|
|
|
node.WriteAttr("Audio:Artist", B_STRING_TYPE, 0,
|
|
|
|
(data->artist).String(), (data->artist).Length());
|
|
|
|
}
|
|
|
|
|
2009-08-10 01:26:14 +00:00
|
|
|
index++;
|
2009-05-13 00:05:53 +00:00
|
|
|
}
|
|
|
|
|
2009-08-10 01:26:14 +00:00
|
|
|
return error;
|
2009-05-11 00:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-21 02:33:19 +00:00
|
|
|
int main(void) {
|
2009-05-17 14:15:00 +00:00
|
|
|
printf("CDDB Daemon for Haiku v1.0.0 started.\n");
|
2008-08-21 02:33:19 +00:00
|
|
|
CDDBDaemon* cddbDaemon = new CDDBDaemon();
|
|
|
|
cddbDaemon->Run();
|
|
|
|
delete cddbDaemon;
|
|
|
|
}
|