Refactored CDDB lookup code
CDDB data is written in R5 format with the exception of CD:key (still figuring out what to do about that) Fixed some update notification problems with changing CDs git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13777 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+314
-218
@@ -12,6 +12,7 @@
|
|||||||
#include <File.h>
|
#include <File.h>
|
||||||
#include <FindDirectory.h>
|
#include <FindDirectory.h>
|
||||||
#include <NetAddress.h>
|
#include <NetAddress.h>
|
||||||
|
#include <fs_attr.h>
|
||||||
#include <Path.h>
|
#include <Path.h>
|
||||||
#include <Query.h>
|
#include <Query.h>
|
||||||
#include <Volume.h>
|
#include <Volume.h>
|
||||||
@@ -23,9 +24,24 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
const int kTerminatingSignal = SIGINT; // SIGCONT;
|
const int kTerminatingSignal = SIGINT; // SIGCONT;
|
||||||
|
|
||||||
|
// The SCSI table of contents consists of a 4-byte header followed by 100 track
|
||||||
|
// descriptors, which are each 8 bytes. We don't really need the first 5 bytes
|
||||||
|
// of the track descriptor, so we'll just ignore them. All we really want is the
|
||||||
|
// length of each track, which happen to be the last 3 bytes of the descriptor.
|
||||||
|
typedef struct TrackRecord
|
||||||
|
{
|
||||||
|
int8 unused[5];
|
||||||
|
|
||||||
|
int8 min;
|
||||||
|
int8 sec;
|
||||||
|
int8 frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
template <class InitCheckable>
|
template <class InitCheckable>
|
||||||
void
|
void
|
||||||
InitCheck(InitCheckable *item)
|
InitCheck(InitCheckable *item)
|
||||||
@@ -57,8 +73,8 @@ CDDBQuery::CDDBQuery(const char *server, int32 port, bool log)
|
|||||||
fServerName(server),
|
fServerName(server),
|
||||||
fPort(port),
|
fPort(port),
|
||||||
fConnected(false),
|
fConnected(false),
|
||||||
fDiscID(-1),
|
fState(kInitial),
|
||||||
fState(kInitial)
|
fDiscID(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,40 +106,40 @@ CDDBQuery::SetToCD(const char *path)
|
|||||||
if(result != B_OK)
|
if(result != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Calculate the disc's CDDB ID
|
||||||
if (fState == kInitial)
|
if (fState == kInitial)
|
||||||
{
|
{
|
||||||
GetDiscID(&toc, fDiscID, fTrackCount, fDiscLength, fFrameOffsetString, fDiscIDStr);
|
fDiscID = GetDiscID(&toc);
|
||||||
|
fTrackCount = GetTrackCount(&toc);
|
||||||
|
|
||||||
|
cdaudio_time time = GetDiscTime(&toc);
|
||||||
|
fDiscLength = (time.minutes * 60) + time.seconds;
|
||||||
|
fFrameOffsetString = OffsetsToString(&toc);
|
||||||
|
GetTrackTimes(&toc, fTrackTimes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int32 tmpDiscID;
|
int32 discID = GetDiscID(&toc);
|
||||||
int32 tmpDiscLength;
|
|
||||||
int32 tmpNumTracks;
|
|
||||||
BString tmpFrameOffsetString;
|
|
||||||
BString tmpDiscIDStr;
|
|
||||||
|
|
||||||
GetDiscID(&toc, tmpDiscID, tmpNumTracks, tmpDiscLength, tmpFrameOffsetString,
|
if (fDiscID == discID);
|
||||||
tmpDiscIDStr);
|
|
||||||
|
|
||||||
if (fDiscID == tmpDiscID && fDiscLength == tmpDiscLength && fTrackCount == tmpNumTracks
|
|
||||||
&& tmpFrameOffsetString == fFrameOffsetString)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fDiscID = tmpDiscID;
|
fTrackCount = GetTrackCount(&toc);
|
||||||
fDiscLength = tmpDiscLength;
|
|
||||||
fTrackCount = tmpNumTracks;
|
cdaudio_time time = GetDiscTime(&toc);
|
||||||
fFrameOffsetString = tmpFrameOffsetString;
|
fDiscLength = (time.minutes * 60) + time.seconds;
|
||||||
fDiscIDStr = tmpDiscIDStr;
|
fFrameOffsetString = OffsetsToString(&toc);
|
||||||
|
GetTrackTimes(&toc, fTrackTimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = B_OK;
|
result = B_OK;
|
||||||
fState = kReading;
|
fState = kReading;
|
||||||
fThread = spawn_thread(&CDDBQuery::LookupBinder, "CDDBLookup", B_NORMAL_PRIORITY, this);
|
fThread = spawn_thread(&CDDBQuery::QueryThread, "CDDBLookup", B_NORMAL_PRIORITY, this);
|
||||||
|
|
||||||
if (fThread >= 0)
|
if (fThread >= 0)
|
||||||
resume_thread(fThread);
|
resume_thread(fThread);
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
fState = kError;
|
fState = kError;
|
||||||
result = fThread;
|
result = fThread;
|
||||||
}
|
}
|
||||||
@@ -134,65 +150,6 @@ DoNothing(int)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
|
||||||
CDDBQuery::LookupBinder(void *castToThis)
|
|
||||||
{
|
|
||||||
CDDBQuery *query = (CDDBQuery *)castToThis;
|
|
||||||
|
|
||||||
BFile file;
|
|
||||||
entry_ref ref;
|
|
||||||
bool newFile = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
signal(kTerminatingSignal, DoNothing);
|
|
||||||
|
|
||||||
if (!query->FindOrCreateContentFileForDisk(&file, &ref, query->fDiscID))
|
|
||||||
{
|
|
||||||
// new content file, read it in from the server
|
|
||||||
Connector connection(query);
|
|
||||||
query->ReadFromServer(&file);
|
|
||||||
file.Seek(0, SEEK_SET);
|
|
||||||
newFile = true;
|
|
||||||
}
|
|
||||||
query->ParseResult(&file);
|
|
||||||
|
|
||||||
query->fState = kDone;
|
|
||||||
query->fThread = -1;
|
|
||||||
query->fResult = B_OK;
|
|
||||||
|
|
||||||
if (newFile)
|
|
||||||
{
|
|
||||||
BString newTitle(query->fTitle);
|
|
||||||
int32 length = newTitle.Length();
|
|
||||||
for (int32 index = 0; index < length; index++)
|
|
||||||
{
|
|
||||||
if (newTitle[index] == '/' || newTitle[index] == ':')
|
|
||||||
newTitle[index] = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Sync();
|
|
||||||
BEntry entry(&ref);
|
|
||||||
entry.Rename(newTitle.String(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (status_t error)
|
|
||||||
{
|
|
||||||
query->fState = kError;
|
|
||||||
query->fResult = error;
|
|
||||||
|
|
||||||
// the cached file is messed up, remove it
|
|
||||||
BEntry entry(&ref);
|
|
||||||
BPath path;
|
|
||||||
entry.GetPath(&path);
|
|
||||||
printf("removing bad CD content file %s\n", path.Path());
|
|
||||||
entry.Remove();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
CDDBQuery::GetToken(const char *stream, BString &result)
|
CDDBQuery::GetToken(const char *stream, BString &result)
|
||||||
{
|
{
|
||||||
@@ -283,42 +240,13 @@ cddb_sum(int n)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConvertedToc
|
|
||||||
{
|
|
||||||
int32 min;
|
|
||||||
int32 sec;
|
|
||||||
int32 frame;
|
|
||||||
};
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
CDDBQuery::GetDiscID(const scsi_toc *toc)
|
CDDBQuery::GetDiscID(const scsi_toc *toc)
|
||||||
{
|
{
|
||||||
int32 tmpDiscID;
|
// The SCSI TOC data has a 4-byte header and then each track record starts
|
||||||
int32 tmpDiscLength;
|
TrackRecord *tocData = (TrackRecord*)&(toc->toc_data[4]);
|
||||||
int32 tmpNumTracks;
|
|
||||||
BString tmpFrameOffsetString;
|
|
||||||
BString tmpDiscIDStr;
|
|
||||||
|
|
||||||
GetDiscID(toc, tmpDiscID, tmpNumTracks, tmpDiscLength, tmpFrameOffsetString,
|
|
||||||
tmpDiscIDStr);
|
|
||||||
|
|
||||||
return tmpDiscID;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
int32 numTracks = toc->toc_data[3] - toc->toc_data[2] + 1;
|
||||||
CDDBQuery::GetDiscID(const scsi_toc *toc, int32 &id, int32 &numTracks,
|
|
||||||
int32 &length, BString &frameOffsetsString, BString &discIDString)
|
|
||||||
{
|
|
||||||
ConvertedToc tocData[100];
|
|
||||||
|
|
||||||
// figure out the disc ID
|
|
||||||
for (int index = 0; index < 100; index++)
|
|
||||||
{
|
|
||||||
tocData[index].min = toc->toc_data[9 + 8*index];
|
|
||||||
tocData[index].sec = toc->toc_data[10 + 8*index];
|
|
||||||
tocData[index].frame = toc->toc_data[11 + 8*index];
|
|
||||||
}
|
|
||||||
numTracks = toc->toc_data[3] - toc->toc_data[2] + 1;
|
|
||||||
|
|
||||||
int32 sum1 = 0;
|
int32 sum1 = 0;
|
||||||
int32 sum2 = 0;
|
int32 sum2 = 0;
|
||||||
@@ -330,39 +258,87 @@ CDDBQuery::GetDiscID(const scsi_toc *toc, int32 &id, int32 &numTracks,
|
|||||||
sum2 += (tocData[index + 1].min * 60 + tocData[index + 1].sec)
|
sum2 += (tocData[index + 1].min * 60 + tocData[index + 1].sec)
|
||||||
- (tocData[index].min * 60 + tocData[index].sec);
|
- (tocData[index].min * 60 + tocData[index].sec);
|
||||||
}
|
}
|
||||||
id = ((sum1 % 0xff) << 24) + (sum2 << 8) + numTracks;
|
int32 discID = ((sum1 % 0xff) << 24) + (sum2 << 8) + numTracks;
|
||||||
discIDString = "";
|
|
||||||
|
|
||||||
sprintf(discIDString.LockBuffer(10), "%08lx", id);
|
return discID;
|
||||||
discIDString.UnlockBuffer();
|
}
|
||||||
|
|
||||||
// compute the total length of the CD.
|
BString
|
||||||
length = tocData[numTracks].min * 60 + tocData[numTracks].sec;
|
CDDBQuery::OffsetsToString(const scsi_toc *toc)
|
||||||
|
{
|
||||||
|
TrackRecord *tocData = (TrackRecord*)&(toc->toc_data[4]);
|
||||||
|
|
||||||
for (int index = 0; index < numTracks; index++)
|
int32 numTracks = toc->toc_data[3] - toc->toc_data[2] + 1;
|
||||||
frameOffsetsString << tocData[index].min * 4500 + tocData[index].sec * 75
|
|
||||||
|
BString string;
|
||||||
|
for (int index = 0; index < numTracks; index++)
|
||||||
|
{
|
||||||
|
string << tocData[index].min * 4500 + tocData[index].sec * 75
|
||||||
+ tocData[index].frame << ' ';
|
+ tocData[index].frame << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
CDDBQuery::GetTrackCount(const scsi_toc *toc)
|
||||||
|
{
|
||||||
|
return toc->toc_data[3] - toc->toc_data[2] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdaudio_time
|
||||||
|
CDDBQuery::GetDiscTime(const scsi_toc *toc)
|
||||||
|
{
|
||||||
|
int16 trackcount = toc->toc_data[3] - toc->toc_data[2] + 1;
|
||||||
|
TrackRecord *desc = (TrackRecord*)&(toc->toc_data[4]);
|
||||||
|
|
||||||
|
cdaudio_time disc;
|
||||||
|
disc.minutes = desc[trackcount].min;
|
||||||
|
disc.seconds = desc[trackcount].sec;
|
||||||
|
|
||||||
|
return disc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CDDBQuery::ReadFromServer(BDataIO *stream)
|
CDDBQuery::GetTrackTimes(const scsi_toc *toc, vector<cdaudio_time> ×)
|
||||||
{
|
{
|
||||||
// Format the query
|
TrackRecord *tocData = (TrackRecord*)&(toc->toc_data[4]);
|
||||||
|
int16 trackCount = toc->toc_data[3] - toc->toc_data[2] + 1;
|
||||||
|
|
||||||
|
for (int index = 0; index < trackCount; index++)
|
||||||
|
{
|
||||||
|
cdaudio_time cdtime;
|
||||||
|
cdtime.minutes = tocData[index].min;
|
||||||
|
cdtime.seconds = tocData[index].sec;
|
||||||
|
times.push_back(cdtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CDDBQuery::ReadFromServer(BString &data)
|
||||||
|
{
|
||||||
|
// This function queries the given CDDB server for the existence of the disc's data and
|
||||||
|
// saves the data to file once obtained.
|
||||||
|
|
||||||
|
// Query for the existence of the disc in the database
|
||||||
|
char idString[10];
|
||||||
|
sprintf(idString, "%08lx", fDiscID);
|
||||||
|
|
||||||
BString query;
|
BString query;
|
||||||
query << "cddb query " << fDiscIDStr << ' ' << fTrackCount << ' '
|
query << "cddb query " << idString << ' ' << fTrackCount << ' '
|
||||||
<< fFrameOffsetString << ' ' << fDiscLength << '\n';
|
<< fFrameOffsetString << ' ' << fDiscLength << '\n';
|
||||||
|
|
||||||
if (fLog)
|
if (fLog)
|
||||||
printf(">%s", query.String());
|
printf(">%s", query.String());
|
||||||
|
|
||||||
// Send it off.
|
|
||||||
ThrowIfNotSize( fSocket.Send(query.String(), query.Length()) );
|
ThrowIfNotSize( fSocket.Send(query.String(), query.Length()) );
|
||||||
|
|
||||||
BString tmp;
|
BString tmp;
|
||||||
ReadLine(tmp);
|
ReadLine(tmp);
|
||||||
|
|
||||||
BString category;
|
BString category;
|
||||||
BString queryDiscID(fDiscIDStr);
|
BString queryDiscID(idString);
|
||||||
|
|
||||||
if(tmp.FindFirst("200") != 0)
|
if(tmp.FindFirst("200") != 0)
|
||||||
{
|
{
|
||||||
@@ -404,54 +380,22 @@ CDDBQuery::ReadFromServer(BDataIO *stream)
|
|||||||
if (!category.Length())
|
if (!category.Length())
|
||||||
category = "misc";
|
category = "misc";
|
||||||
|
|
||||||
|
// Query for the disc's data - artist, album, tracks, etc.
|
||||||
query = "";
|
query = "";
|
||||||
query << "cddb read " << category << ' ' << queryDiscID << '\n' ;
|
query << "cddb read " << category << ' ' << queryDiscID << '\n' ;
|
||||||
ThrowIfNotSize( fSocket.Send(query.String(), query.Length()) );
|
ThrowIfNotSize( fSocket.Send(query.String(), query.Length()) );
|
||||||
|
|
||||||
for (;;)
|
while(true)
|
||||||
{
|
{
|
||||||
BString tmp;
|
|
||||||
ReadLine(tmp);
|
ReadLine(tmp);
|
||||||
tmp += '\n';
|
|
||||||
ThrowIfNotSize( stream->Write(tmp.String(), tmp.Length()) );
|
|
||||||
if (tmp == "." || tmp == ".\n")
|
if (tmp == "." || tmp == ".\n")
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
fState = kDone;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CDDBQuery::ParseResult(BDataIO *source)
|
|
||||||
{
|
|
||||||
fTitle = "";
|
|
||||||
fTrackNames.clear();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
BString tmp;
|
|
||||||
ReadLine(source, tmp);
|
|
||||||
if (tmp == ".")
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (tmp == "")
|
|
||||||
throw (status_t)B_ERROR;
|
|
||||||
|
|
||||||
if (tmp.FindFirst("DTITLE=") == 0)
|
data << tmp << '\n';
|
||||||
fTitle = tmp.String() + sizeof("DTITLE");
|
|
||||||
else
|
|
||||||
if (tmp.FindFirst("TTITLE") == 0)
|
|
||||||
{
|
|
||||||
int32 afterIndex = tmp.FindFirst('=');
|
|
||||||
if (afterIndex > 0)
|
|
||||||
{
|
|
||||||
BString trackName(tmp.String() + afterIndex + 1);
|
|
||||||
fTrackNames.push_back(trackName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fState = kDone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CDDBQuery::Connect()
|
CDDBQuery::Connect()
|
||||||
{
|
{
|
||||||
@@ -497,35 +441,6 @@ CDDBQuery::ReadLine(BString &buffer)
|
|||||||
printf("<%s\n", buffer.String());
|
printf("<%s\n", buffer.String());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
CDDBQuery::ReadLine(BDataIO *stream, BString &buffer)
|
|
||||||
{
|
|
||||||
// this is super-lame, should use better buffering
|
|
||||||
// ToDo:
|
|
||||||
// redo using LockBuffer, growing the buffer by 2k and reading into it
|
|
||||||
|
|
||||||
buffer = "";
|
|
||||||
char ch;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
ssize_t result = stream->Read(&ch, 1);
|
|
||||||
|
|
||||||
// check for read error
|
|
||||||
if (result < 0)
|
|
||||||
throw (status_t)result;
|
|
||||||
|
|
||||||
if (result == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (ch >= ' ')
|
|
||||||
buffer += ch;
|
|
||||||
|
|
||||||
if (ch == '\n')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CDDBQuery::IdentifySelf()
|
CDDBQuery::IdentifySelf()
|
||||||
{
|
{
|
||||||
@@ -548,10 +463,14 @@ CDDBQuery::IdentifySelf()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CDDBQuery::FindOrCreateContentFileForDisk(BFile *file, entry_ref *fileRef, int32 discID)
|
CDDBQuery::OpenContentFile(const int32 &discID)
|
||||||
{
|
{
|
||||||
|
// Makes sure that the lookup has a valid file to work with for the CD content.
|
||||||
|
// Returns true if there is an existing file, false if a lookup is required.
|
||||||
|
|
||||||
|
BFile file;
|
||||||
BString predicate;
|
BString predicate;
|
||||||
predicate << "cddb:discID == " << discID;
|
predicate << "CD:key == " << discID;
|
||||||
entry_ref ref;
|
entry_ref ref;
|
||||||
|
|
||||||
BVolumeRoster roster;
|
BVolumeRoster roster;
|
||||||
@@ -564,7 +483,7 @@ CDDBQuery::FindOrCreateContentFileForDisk(BFile *file, entry_ref *fileRef, int32
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// make sure the volume we are looking at is indexed right
|
// make sure the volume we are looking at is indexed right
|
||||||
fs_create_index(volume.Device(), "cddb:discID", B_INT32_TYPE, 0);
|
fs_create_index(volume.Device(), "CD:key", B_INT32_TYPE, 0);
|
||||||
|
|
||||||
BQuery query;
|
BQuery query;
|
||||||
query.SetVolume(&volume);
|
query.SetVolume(&volume);
|
||||||
@@ -572,33 +491,210 @@ CDDBQuery::FindOrCreateContentFileForDisk(BFile *file, entry_ref *fileRef, int32
|
|||||||
if (query.Fetch() != B_OK)
|
if (query.Fetch() != B_OK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
while (query.GetNextRef(&ref) == B_OK)
|
if(query.GetNextRef(&ref) == B_OK)
|
||||||
{
|
{
|
||||||
// we have one already, return
|
file.SetTo(&ref, B_READ_ONLY);
|
||||||
file->SetTo(&ref, O_RDONLY);
|
break;
|
||||||
*fileRef = ref;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file.InitCheck() == B_NO_INIT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Getting this far means that we have a file. Now we parse the data in it and assign it
|
||||||
|
// to the appropriate members of the object
|
||||||
|
attr_info info;
|
||||||
|
|
||||||
|
if(file.GetAttrInfo("CD:tracks",&info)==B_OK && info.size > 0)
|
||||||
|
{
|
||||||
|
char trackdata[info.size+2];
|
||||||
|
|
||||||
|
if(file.ReadAttr("CD:tracks",B_STRING_TYPE,0,trackdata,info.size)>0)
|
||||||
|
{
|
||||||
|
trackdata[info.size] = 0;
|
||||||
|
BString tmp = GetLineFromString(trackdata);
|
||||||
|
char *index;
|
||||||
|
|
||||||
|
fArtist = tmp;
|
||||||
|
fArtist.Truncate(fArtist.FindFirst(" - "));
|
||||||
|
|
||||||
|
fTitle = strchr(tmp.String(),'-') + 2;
|
||||||
|
|
||||||
|
index = strchr(trackdata,'\n') + 1;
|
||||||
|
while(*index)
|
||||||
|
{
|
||||||
|
tmp = GetLineFromString(index);
|
||||||
|
|
||||||
|
fTrackNames.push_back(tmp);
|
||||||
|
index = strchr(index,'\n') + 1;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BPath path;
|
|
||||||
ThrowOnError( find_directory(B_USER_DIRECTORY, &path, true) );
|
|
||||||
path.Append("cd");
|
|
||||||
ThrowOnError( create_directory(path.Path(), 0755) );
|
|
||||||
|
|
||||||
BDirectory dir(path.Path());
|
// If we got this far, it means the file is corrupted, so nuke it and get a new one
|
||||||
BString name("CDContent");
|
BEntry entry(&ref);
|
||||||
for (int32 index = 0; ;index++)
|
entry.Remove();
|
||||||
{
|
|
||||||
if (dir.CreateFile(name.String(), file, true) != B_FILE_EXISTS)
|
|
||||||
{
|
|
||||||
BEntry entry(&dir, name.String());
|
|
||||||
entry.GetRef(fileRef);
|
|
||||||
file->WriteAttr("cddb:discID", B_INT32_TYPE, 0, &discID, sizeof(int32));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
name = "CDContent";
|
|
||||||
name << index;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32
|
||||||
|
CDDBQuery::QueryThread(void *owner)
|
||||||
|
{
|
||||||
|
CDDBQuery *query = (CDDBQuery *)owner;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
signal(kTerminatingSignal, DoNothing);
|
||||||
|
|
||||||
|
if(!query->OpenContentFile(query->fDiscID))
|
||||||
|
{
|
||||||
|
// new content file, read it in from the server
|
||||||
|
Connector connection(query);
|
||||||
|
BString data;
|
||||||
|
query->ReadFromServer(data);
|
||||||
|
query->ParseData(data);
|
||||||
|
query->WriteFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
query->fState = kDone;
|
||||||
|
query->fThread = -1;
|
||||||
|
query->fResult = B_OK;
|
||||||
|
}
|
||||||
|
catch (status_t error)
|
||||||
|
{
|
||||||
|
query->fState = kError;
|
||||||
|
query->fResult = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CDDBQuery::WriteFile(void)
|
||||||
|
{
|
||||||
|
BPath path;
|
||||||
|
if(find_directory(B_USER_DIRECTORY, &path, true)!=B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
path.Append("cd");
|
||||||
|
create_directory(path.Path(), 0755);
|
||||||
|
|
||||||
|
BString filename(path.Path());
|
||||||
|
filename << "/" << fArtist << " - " << fTitle;
|
||||||
|
|
||||||
|
BFile file(filename.String(), B_READ_WRITE | B_CREATE_FILE | B_FAIL_IF_EXISTS);
|
||||||
|
if(file.InitCheck() != B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BString entry;
|
||||||
|
char timestring[10];
|
||||||
|
|
||||||
|
sprintf(timestring,"%.2ld:%.2ld",fDiscLength / 60, fDiscLength % 60);
|
||||||
|
|
||||||
|
entry << fArtist << " - " << fTitle << "\t" << timestring << "\n";
|
||||||
|
file.Write(entry.String(),entry.Length());
|
||||||
|
|
||||||
|
BString tracksattr(fArtist);
|
||||||
|
tracksattr << " - " << fTitle << "\n";
|
||||||
|
|
||||||
|
for(int32 i=0; i<fTrackCount; i++)
|
||||||
|
{
|
||||||
|
entry = fTrackNames[i];
|
||||||
|
|
||||||
|
sprintf(timestring,"%.2ld:%.2ld",fTrackTimes[i].minutes, fTrackTimes[i].seconds);
|
||||||
|
|
||||||
|
entry << "\t" << timestring << "\n";
|
||||||
|
file.Write(entry.String(),entry.Length());
|
||||||
|
|
||||||
|
tracksattr << fTrackNames[i] << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
file.WriteAttr("CD:key", B_INT32_TYPE, 0, &fDiscID, sizeof(int32));
|
||||||
|
file.WriteAttr("CD:tracks", B_STRING_TYPE, 0, tracksattr.String(), tracksattr.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CDDBQuery::ParseData(const BString &data)
|
||||||
|
{
|
||||||
|
// TODO: This function is dog slow, but it works. Optimize.
|
||||||
|
|
||||||
|
// Ideally, the search should be done sequentially using GetLineFromString() and strchr().
|
||||||
|
// Order: genre(category), frame offsets, disc length, artist/album, track titles
|
||||||
|
fTitle = "";
|
||||||
|
fTrackNames.clear();
|
||||||
|
|
||||||
|
int32 pos;
|
||||||
|
|
||||||
|
pos = data.FindFirst("DTITLE=");
|
||||||
|
if(pos > 0)
|
||||||
|
{
|
||||||
|
fTitle = fArtist = GetLineFromString(data.String() + sizeof("DTITLE") + pos);
|
||||||
|
|
||||||
|
pos = fArtist.FindFirst(" / ");
|
||||||
|
if(pos > 0)
|
||||||
|
fArtist.Truncate(pos);
|
||||||
|
|
||||||
|
fTitle = fTitle.String() + pos + sizeof(" / ") - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fCategory = GetLineFromString(data.String() + 4);
|
||||||
|
pos = fCategory.FindFirst(" ");
|
||||||
|
if(pos > 0)
|
||||||
|
fCategory.Truncate(pos);
|
||||||
|
|
||||||
|
|
||||||
|
pos = data.FindFirst("Disc length: ");
|
||||||
|
if(pos > 0)
|
||||||
|
{
|
||||||
|
BString discLengthString = GetLineFromString(data.String() + pos);
|
||||||
|
pos = discLengthString.FindFirst(" seconds");
|
||||||
|
if(pos > 0)
|
||||||
|
{
|
||||||
|
discLengthString.Truncate(pos);
|
||||||
|
|
||||||
|
discLengthString = discLengthString.String() + sizeof("Disc length:");
|
||||||
|
fDiscLength = atoi(discLengthString.String());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fDiscLength = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = data.FindFirst("TTITLE0=");
|
||||||
|
if(pos > 0)
|
||||||
|
{
|
||||||
|
int32 trackCount=0;
|
||||||
|
BString searchString="TTITLE0=";
|
||||||
|
|
||||||
|
while(pos > 0)
|
||||||
|
{
|
||||||
|
BString trackName = data.String() + pos + searchString.Length();
|
||||||
|
trackName.Truncate(trackName.FindFirst("\n"));
|
||||||
|
fTrackNames.push_back(trackName);
|
||||||
|
|
||||||
|
trackCount++;
|
||||||
|
searchString = "TTITLE";
|
||||||
|
searchString << trackCount << "=";
|
||||||
|
pos = data.FindFirst(searchString.String(),pos);
|
||||||
|
}
|
||||||
|
fTrackCount = trackCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BString
|
||||||
|
CDDBQuery::GetLineFromString(const char *string)
|
||||||
|
{
|
||||||
|
if(!string)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
BString out(string);
|
||||||
|
|
||||||
|
int32 linefeed = out.FindFirst("\n");
|
||||||
|
|
||||||
|
if(linefeed > 0)
|
||||||
|
out.Truncate(linefeed);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,37 +5,40 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
#include <scsi.h>
|
#include <scsi.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "CDAudioDevice.h"
|
||||||
|
|
||||||
// based on Jukebox by Chip Paul
|
// based on Jukebox by Chip Paul
|
||||||
|
|
||||||
class CDDBQuery
|
class CDDBQuery
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CDDBQuery(const char *server, int32 port = 888, bool log = false);
|
CDDBQuery(const char *server, int32 port = 888,
|
||||||
void SetToSite(const char *server, int32 port);
|
bool log = false);
|
||||||
void GetSites(bool (*)(const char *site, int port, const char *latitude,
|
void SetToSite(const char *server, int32 port);
|
||||||
const char *longitude, const char *description,
|
void GetSites(bool (*)(const char *site, int port,
|
||||||
void *state), void *);
|
const char *latitude, const char *longitude,
|
||||||
|
const char *description, void *state), void *);
|
||||||
|
|
||||||
void SetToCD(const char *devicepath);
|
void SetToCD(const char *devicepath);
|
||||||
bool GetTitles(BString *title, vector<BString> *tracks,
|
bool GetTitles(BString *title, vector<BString> *tracks,
|
||||||
bigtime_t timeout);
|
bigtime_t timeout);
|
||||||
|
|
||||||
bool Ready() const { return fState == kDone; }
|
bool Ready() const { return fState == kDone; }
|
||||||
int32 CurrentDiscID() const { return fDiscID; }
|
int32 CurrentDiscID() const { return fDiscID; }
|
||||||
|
|
||||||
static int32 GetDiscID(const scsi_toc *);
|
static int32 GetDiscID(const scsi_toc *);
|
||||||
|
static int32 GetTrackCount(const scsi_toc *);
|
||||||
private:
|
static cdaudio_time GetDiscTime(const scsi_toc *);
|
||||||
static void GetDiscID(const scsi_toc *, int32 &id, int32 &numTracks,
|
static void GetTrackTimes(const scsi_toc *, vector<cdaudio_time> ×);
|
||||||
int32 &length, BString &tmpFrameOffsetString,
|
static BString OffsetsToString(const scsi_toc *);
|
||||||
BString &discIDString);
|
|
||||||
void Connect();
|
|
||||||
void Disconnect();
|
|
||||||
bool IsConnected() const;
|
|
||||||
|
|
||||||
static int32 LookupBinder(void *);
|
// TODO: make private again once finished with testing
|
||||||
|
//private:
|
||||||
|
void Connect();
|
||||||
|
void Disconnect();
|
||||||
|
bool IsConnected() const;
|
||||||
|
|
||||||
|
static int32 QueryThread(void *);
|
||||||
|
|
||||||
class Connector
|
class Connector
|
||||||
{
|
{
|
||||||
@@ -70,39 +73,43 @@ private:
|
|||||||
kError
|
kError
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FindOrCreateContentFileForDisk(BFile *file, entry_ref *ref,
|
bool OpenContentFile(const int32 &discID);
|
||||||
int32 discID);
|
|
||||||
|
|
||||||
void ReadFromServer(BDataIO *);
|
void ReadFromServer(BString &data);
|
||||||
void ParseResult(BDataIO *);
|
void ParseData(const BString &data);
|
||||||
|
void WriteFile(void);
|
||||||
|
|
||||||
|
BString GetLineFromString(const char *string);
|
||||||
|
void ReadLine(BString &);
|
||||||
|
void IdentifySelf();
|
||||||
|
|
||||||
void ReadLine(BString &);
|
const char * GetToken(const char *, BString &);
|
||||||
static void ReadLine(BDataIO *, BString &);
|
|
||||||
void IdentifySelf();
|
|
||||||
|
|
||||||
const char * GetToken(const char *, BString &);
|
|
||||||
|
|
||||||
bool fLog;
|
bool fLog;
|
||||||
|
|
||||||
// connection description
|
// state data
|
||||||
BString fServerName;
|
BString fServerName;
|
||||||
BNetEndpoint fSocket;
|
BNetEndpoint fSocket;
|
||||||
int32 fPort;
|
int32 fPort;
|
||||||
bool fConnected;
|
bool fConnected;
|
||||||
|
|
||||||
// disc identification
|
thread_id fThread;
|
||||||
int32 fDiscID;
|
State fState;
|
||||||
int32 fDiscLength;
|
status_t fResult;
|
||||||
int32 fTrackCount;
|
|
||||||
BString fFrameOffsetString;
|
|
||||||
BString fDiscIDStr;
|
|
||||||
|
|
||||||
thread_id fThread;
|
// disc information
|
||||||
State fState;
|
BString fArtist;
|
||||||
BString fTitle;
|
BString fTitle;
|
||||||
vector<BString> fTrackNames;
|
BString fCategory;
|
||||||
status_t fResult;
|
|
||||||
|
|
||||||
|
int32 fDiscID;
|
||||||
|
int32 fDiscLength;
|
||||||
|
|
||||||
|
vector<BString> fTrackNames;
|
||||||
|
int32 fTrackCount;
|
||||||
|
|
||||||
|
BString fFrameOffsetString;
|
||||||
|
vector<cdaudio_time> fTrackTimes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -58,20 +58,28 @@ bool
|
|||||||
PlayState::UpdateState(void)
|
PlayState::UpdateState(void)
|
||||||
{
|
{
|
||||||
CDState state = sCDDevice.GetState();
|
CDState state = sCDDevice.GetState();
|
||||||
if(state == kStopped && fEngine->GetState() == kPlaying)
|
if(state == kStopped)
|
||||||
{
|
{
|
||||||
// this means we have come to the end of a song, but probably not
|
if(fEngine->GetState() == kPlaying)
|
||||||
// the last song in the playlist
|
|
||||||
int16 next = sPlayList.GetNextTrack();
|
|
||||||
if(next > 0)
|
|
||||||
{
|
{
|
||||||
sCDDevice.Play(next);
|
// this means we have come to the end of a song, but probably not
|
||||||
|
// the last song in the playlist
|
||||||
|
int16 next = sPlayList.GetNextTrack();
|
||||||
|
if(next > 0)
|
||||||
|
{
|
||||||
|
sCDDevice.Play(next);
|
||||||
|
return CurrentState(kPlaying);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sPlayList.SetTrackCount(sCDDevice.CountTracks());
|
||||||
|
sPlayList.SetStartingTrack(sCDDevice.GetTrack());
|
||||||
return CurrentState(kPlaying);
|
return CurrentState(kPlaying);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(state == kPlaying && fEngine->GetState() == kStopped)
|
||||||
/* if(state == kPlaying && fEngine->GetState() == kStopped)
|
|
||||||
{
|
{
|
||||||
// This happens when the CD drive is started by R5's player
|
// This happens when the CD drive is started by R5's player
|
||||||
// or this app is started while the drive is playing. We should
|
// or this app is started while the drive is playing. We should
|
||||||
@@ -81,7 +89,7 @@ PlayState::UpdateState(void)
|
|||||||
sPlayList.SetStartingTrack(sCDDevice.GetTrack());
|
sPlayList.SetStartingTrack(sCDDevice.GetTrack());
|
||||||
return CurrentState(kPlaying);
|
return CurrentState(kPlaying);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
return CurrentState(state);
|
return CurrentState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,33 +128,35 @@ TrackState::UpdateState()
|
|||||||
// As a result, we want to make sure this hasn't happened. If it has, then we
|
// As a result, we want to make sure this hasn't happened. If it has, then we
|
||||||
// need to update the playlist's position.
|
// need to update the playlist's position.
|
||||||
|
|
||||||
int16 cdTrack;
|
int16 cdTrack, count;
|
||||||
|
|
||||||
if(sCDDevice.GetState() == kPlaying)
|
if(sCDDevice.GetState() == kPlaying)
|
||||||
{
|
{
|
||||||
cdTrack = sCDDevice.GetTrack();
|
cdTrack = sCDDevice.GetTrack();
|
||||||
if(cdTrack != sPlayList.GetCurrentTrack())
|
if(cdTrack != sPlayList.GetCurrentTrack())
|
||||||
sPlayList.SetCurrentTrack(cdTrack);
|
sPlayList.SetCurrentTrack(cdTrack);
|
||||||
return CurrentState(cdTrack);
|
return CurrentState(cdTrack, trackCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not playing, just monitor the current track in the playlist
|
// If we're not playing, just monitor the current track in the playlist
|
||||||
cdTrack = sPlayList.GetCurrentTrack();
|
cdTrack = sPlayList.GetCurrentTrack();
|
||||||
return CurrentState(cdTrack);
|
count = sCDDevice.CountTracks();
|
||||||
|
return CurrentState(cdTrack,count);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
TrackState::GetNumTracks() const
|
TrackState::GetNumTracks() const
|
||||||
{
|
{
|
||||||
return sCDDevice.CountTracks()+1;
|
return sCDDevice.CountTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TrackState::CurrentState(int32 track)
|
TrackState::CurrentState(int32 track, int32 count)
|
||||||
{
|
{
|
||||||
if (track != currentTrack)
|
if( (track != currentTrack) || (count != trackCount) )
|
||||||
{
|
{
|
||||||
currentTrack = track;
|
currentTrack = track;
|
||||||
|
trackCount = count;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -72,8 +72,9 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool UpdateState();
|
bool UpdateState();
|
||||||
bool CurrentState(int32);
|
bool CurrentState(int32,int32);
|
||||||
int32 currentTrack;
|
int32 currentTrack;
|
||||||
|
int32 trackCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
// this watcher sends notices to observers that are interested
|
// this watcher sends notices to observers that are interested
|
||||||
|
|||||||
Reference in New Issue
Block a user