more or less fixed #1664

This commit is contained in:
fgerlits 2006-05-10 18:44:06 +00:00
parent 8d8b5c51e9
commit c82210fbc6
10 changed files with 427 additions and 58 deletions

View file

@ -588,6 +588,28 @@ class StorageClientInterface
throw (XmlRpcException)
= 0;
/**
* Import a playlist archive to the local storage.
* This must be a tar file, in the LS Archive format, as produced
* by exportPlaylistOpen/Close() when called with the internalFormat
* parameter.
*
* The size of the tar file must be less than 2 GB, because
* the storage server can not deal with larger files.
*
* @param sessionId the session ID from the authentication client
* @param path the path for the playlist archive file.
* @return on success, the unique ID of the imported playlist.
*
* @exception XmlRpcException if there is a problem with the XML-RPC
* call or we have not logged in yet.
*/
virtual Ptr<UniqueId>::Ref
importPlaylist(Ptr<SessionId>::Ref sessionId,
Ptr<const Glib::ustring>::Ref path) const
throw (XmlRpcException)
= 0;
/**
* The possible states of an asynchronous transport process.
*/

View file

@ -1079,6 +1079,19 @@ TestStorageClient :: exportPlaylistClose(
}
/*------------------------------------------------------------------------------
* Import a playlist archive to the local storage.
*----------------------------------------------------------------------------*/
Ptr<UniqueId>::Ref
TestStorageClient :: importPlaylist(
Ptr<SessionId>::Ref sessionId,
Ptr<const Glib::ustring>::Ref path) const
throw (XmlRpcException)
{
throw XmlRpcException("Method not implemented.");
}
/*------------------------------------------------------------------------------
* Check the status of the asynchronous network transport operation.
*----------------------------------------------------------------------------*/

View file

@ -712,6 +712,27 @@ class TestStorageClient :
exportPlaylistClose(Ptr<const Glib::ustring>::Ref token) const
throw (XmlRpcException);
/**
* Import a playlist archive to the local storage.
* This must be a tar file, in the LS Archive format, as produced
* by exportPlaylistOpen/Close() when called with the internalFormat
* parameter.
*
* The size of the tar file must be less than 2 GB, because
* the storage server can not deal with larger files.
*
* @param sessionId the session ID from the authentication client
* @param path the path for the playlist archive file.
* @return on success, the unique ID of the imported playlist.
*
* @exception XmlRpcException if there is a problem with the XML-RPC
* call or we have not logged in yet.
*/
virtual Ptr<UniqueId>::Ref
importPlaylist(Ptr<SessionId>::Ref sessionId,
Ptr<const Glib::ustring>::Ref path) const
throw (XmlRpcException);
/**
* Check the status of the asynchronous network transport operation.
*

View file

@ -754,6 +754,46 @@ const std::string exportPlaylistUrlParamName = "url";
const std::string exportPlaylistTokenParamName = "token";
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ storage server constants: importPlaylist */
/*------------------------------------------------------------------------------
* The name of the opening 'import playlist' method on the storage server
*----------------------------------------------------------------------------*/
const std::string importPlaylistOpenMethodName
= "locstor.importPlaylistOpen";
/*------------------------------------------------------------------------------
* The name of the closing 'import playlist' method on the storage server
*----------------------------------------------------------------------------*/
const std::string importPlaylistCloseMethodName
= "locstor.importPlaylistClose";
/*------------------------------------------------------------------------------
* The name of the session ID parameter in the input structure
*----------------------------------------------------------------------------*/
const std::string importPlaylistSessionIdParamName = "sessid";
/*------------------------------------------------------------------------------
* The name of the checksum parameter in the input structure
*----------------------------------------------------------------------------*/
const std::string importPlaylistChecksumParamName = "chsum";
/*------------------------------------------------------------------------------
* The name of the writable URL parameter in the output structure
*----------------------------------------------------------------------------*/
const std::string importPlaylistUrlParamName = "url";
/*------------------------------------------------------------------------------
* The name of the token parameter for both 'open' and 'close'
*----------------------------------------------------------------------------*/
const std::string importPlaylistTokenParamName = "token";
/*------------------------------------------------------------------------------
* The name of the unique ID parameter in the output structure
*----------------------------------------------------------------------------*/
const std::string importPlaylistUniqueIdParamName = "gunid";
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ storage server constants: remoteSearchXXXX */
/*------------------------------------------------------------------------------
@ -2490,6 +2530,96 @@ WebStorageClient :: exportPlaylistClose(
}
/*------------------------------------------------------------------------------
* Import a playlist archive to the local storage.
*----------------------------------------------------------------------------*/
Ptr<UniqueId>::Ref
WebStorageClient :: importPlaylist(
Ptr<SessionId>::Ref sessionId,
Ptr<const Glib::ustring>::Ref path) const
throw (XmlRpcException)
{
std::ifstream ifs(path->c_str());
if (!ifs) {
ifs.close();
throw XmlRpcIOException("Could not read the playlist archive file.");
}
std::string md5string = Md5(ifs);
ifs.close();
XmlRpcValue parameters;
XmlRpcValue result;
parameters.clear();
parameters[importPlaylistSessionIdParamName]
= sessionId->getId();
parameters[importPlaylistChecksumParamName]
= md5string;
execute(importPlaylistOpenMethodName, parameters, result);
checkStruct(importPlaylistOpenMethodName,
result,
importPlaylistUrlParamName,
XmlRpcValue::TypeString);
checkStruct(importPlaylistOpenMethodName,
result,
importPlaylistTokenParamName,
XmlRpcValue::TypeString);
std::string url = std::string(result[importPlaylistUrlParamName]);
std::string token = std::string(result[importPlaylistTokenParamName]);
FILE* binaryFile = fopen(path->c_str(), "rb");
if (!binaryFile) {
throw XmlRpcIOException("The playlist archive file disappeared.");
}
fseek(binaryFile, 0, SEEK_END);
long binaryFileSize = ftell(binaryFile);
rewind(binaryFile);
CURL* handle = curl_easy_init();
if (!handle) {
throw XmlRpcCommunicationException("Could not obtain curl handle.");
}
int status = curl_easy_setopt(handle, CURLOPT_READDATA, binaryFile);
status |= curl_easy_setopt(handle, CURLOPT_INFILESIZE, binaryFileSize);
// works for files of size up to 2 GB
status |= curl_easy_setopt(handle, CURLOPT_PUT, 1);
status |= curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
if (status) {
throw XmlRpcCommunicationException("Could not set curl options.");
}
status = curl_easy_perform(handle);
if (status) {
throw XmlRpcCommunicationException("Error uploading file.");
}
curl_easy_cleanup(handle);
fclose(binaryFile);
parameters.clear();
parameters[importPlaylistTokenParamName]
= token;
execute(importPlaylistCloseMethodName, parameters, result);
checkStruct(importPlaylistCloseMethodName,
result,
importPlaylistUniqueIdParamName,
XmlRpcValue::TypeString);
Ptr<UniqueId>::Ref id(new UniqueId(std::string(
result[importPlaylistUniqueIdParamName] )));
return id;
}
/*------------------------------------------------------------------------------
* Check the status of the asynchronous network transport operation.
*----------------------------------------------------------------------------*/

View file

@ -804,6 +804,27 @@ class WebStorageClient :
exportPlaylistClose(Ptr<const Glib::ustring>::Ref token) const
throw (XmlRpcException);
/**
* Import a playlist archive to the local storage.
* This must be a tar file, in the LS Archive format, as produced
* by exportPlaylistOpen/Close() when called with the internalFormat
* parameter.
*
* The size of the tar file must be less than 2 GB, because
* the storage server can not deal with larger files.
*
* @param sessionId the session ID from the authentication client
* @param path the path for the playlist archive file.
* @return on success, the unique ID of the imported playlist.
*
* @exception XmlRpcException if there is a problem with the XML-RPC
* call or we have not logged in yet.
*/
virtual Ptr<UniqueId>::Ref
importPlaylist(Ptr<SessionId>::Ref sessionId,
Ptr<const Glib::ustring>::Ref path) const
throw (XmlRpcException);
/**
* Check the status of the asynchronous network transport operation.
*

View file

@ -873,11 +873,11 @@ GLiveSupport :: releaseOpennedPlaylists(void) throw ()
/*------------------------------------------------------------------------------
* Upload a file to the server.
* Upload an audio clip to the local storage.
*----------------------------------------------------------------------------*/
void
LiveSupport :: GLiveSupport ::
GLiveSupport :: uploadFile(Ptr<AudioClip>::Ref audioClip)
GLiveSupport :: uploadAudioClip(Ptr<AudioClip>::Ref audioClip)
throw (XmlRpcException)
{
storage->storeAudioClip(sessionId, audioClip);
@ -887,6 +887,24 @@ GLiveSupport :: uploadFile(Ptr<AudioClip>::Ref audioClip)
}
/*------------------------------------------------------------------------------
* Upload a playlist archive to the local storage.
*----------------------------------------------------------------------------*/
Ptr<Playlist>::Ref
LiveSupport :: GLiveSupport ::
GLiveSupport :: uploadPlaylistArchive(Ptr<const Glib::ustring>::Ref path)
throw (XmlRpcException)
{
Ptr<UniqueId>::Ref id = storage->importPlaylist(sessionId, path);
Ptr<Playlist>::Ref playlist = storage->getPlaylist(sessionId, id);
// this will also add it to the local cache
addToScratchpad(playlist);
return playlist;
}
/*------------------------------------------------------------------------------
* Add a file to the Scratchpad, and update it.
*----------------------------------------------------------------------------*/

View file

@ -532,13 +532,23 @@ class GLiveSupport : public LocalizedConfigurable,
showLoggedInUI(void) throw ();
/**
* Upload a file to the storage.
* Upload an audio clip to the storage.
*
* @param audioClip the file to upload
* @param audioClip the audio clip to upload.
* @exception XmlRpcException on upload failures.
*/
void
uploadFile(Ptr<AudioClip>::Ref audioClip)
uploadAudioClip(Ptr<AudioClip>::Ref audioClip)
throw (XmlRpcException);
/**
* Upload a playlist archive to the storage.
*
* @param path the path of the file to upload.
* @exception XmlRpcException on upload failures.
*/
Ptr<Playlist>::Ref
uploadPlaylistArchive(Ptr<const Glib::ustring>::Ref path)
throw (XmlRpcException);
/**

View file

@ -72,10 +72,9 @@ UploadFileWindow :: UploadFileWindow (
: GuiWindow(gLiveSupport,
bundle,
"",
windowOpenerButton)
windowOpenerButton),
fileType(invalidType)
{
isAudioClipValid = false;
Ptr<WidgetFactory>::Ref wf = WidgetFactory::getInstance();
try {
@ -302,19 +301,43 @@ void
UploadFileWindow :: updateFileInfo(void) throw ()
{
std::string fileName = fileNameEntry->get_text().raw();
Ptr<std::string>::Ref newUri(new std::string("file://"));
newUri->append(fileName);
// see if the file exists, and is readable
std::ifstream file(fileName.c_str());
if (!file.good()) {
file.close();
statusBar->set_text(*getResourceUstring("couldNotOpenFileMsg"));
isAudioClipValid = false;
fileType = invalidType;
return;
}
file.close();
fileType = determineFileType(fileName);
switch (fileType) {
case audioClipType: readAudioClipInfo(fileName);
break;
case playlistArchiveType: statusBar->set_text("");
break;
case invalidType: statusBar->set_text(*getResourceUstring(
"unsupportedFileTypeMsg"));
break;
}
}
/*------------------------------------------------------------------------------
* Read the playlength and metadata info from the binary audio file.
*----------------------------------------------------------------------------*/
void
UploadFileWindow :: readAudioClipInfo(const std::string & fileName)
throw ()
{
Ptr<std::string>::Ref newUri(new std::string("file://"));
newUri->append(fileName);
Ptr<time_duration>::Ref playlength;
try {
playlength = readPlaylength(fileName);
@ -341,7 +364,7 @@ UploadFileWindow :: updateFileInfo(void) throw ()
audioClip->readTag(gLiveSupport->getMetadataTypeContainer());
} catch (std::invalid_argument &e) {
statusBar->set_text(e.what());
isAudioClipValid = false;
fileType = invalidType;
return;
}
@ -357,7 +380,6 @@ UploadFileWindow :: updateFileInfo(void) throw ()
}
statusBar->set_text("");
isAudioClipValid = true;
}
@ -379,10 +401,24 @@ UploadFileWindow :: onFileNameEntryLeave(GdkEventFocus * event)
void
UploadFileWindow :: onUploadButtonClicked(void) throw ()
{
if (!isAudioClipValid) {
return;
switch (fileType) {
case audioClipType: uploadAudioClip();
break;
case playlistArchiveType: uploadPlaylistArchive();
break;
case invalidType: break;
}
}
/*------------------------------------------------------------------------------
* Upload an audio clip to the storage.
*----------------------------------------------------------------------------*/
void
UploadFileWindow :: uploadAudioClip(void) throw ()
{
for (unsigned int i=0; i < metadataKeys.size(); ++i) {
Ptr<const Glib::ustring>::Ref metadataKey = metadataKeys[i];
Gtk::Entry * metadataEntry = metadataEntries[i];
@ -401,22 +437,38 @@ UploadFileWindow :: onUploadButtonClicked(void) throw ()
}
try {
gLiveSupport->uploadFile(audioClip);
gLiveSupport->uploadAudioClip(audioClip);
} catch (XmlRpcException &e) {
statusBar->set_text(e.what());
return;
}
statusBar->set_text(*formatMessage("clipUploadedMsg",
clearEverything();
statusBar->set_text(*formatMessage("fileUploadedMsg",
*audioClip->getTitle() ));
}
fileNameEntry->set_text("");
for (unsigned int i=0; i < metadataEntries.size(); ++i) {
Gtk::Entry * metadataEntry = metadataEntries[i];
metadataEntry->set_text("");
/*------------------------------------------------------------------------------
* Upload a playlist archive to the storage.
*----------------------------------------------------------------------------*/
void
UploadFileWindow :: uploadPlaylistArchive(void) throw ()
{
Ptr<const Glib::ustring>::Ref path(new const Glib::ustring(
fileNameEntry->get_text() ));
Ptr<Playlist>::Ref playlist;
try {
playlist = gLiveSupport->uploadPlaylistArchive(path);
} catch (XmlRpcException &e) {
statusBar->set_text(e.what());
return;
}
isAudioClipValid = false;
clearEverything();
statusBar->set_text(*formatMessage("fileUploadedMsg",
*playlist->getTitle() ));
}
@ -426,14 +478,7 @@ UploadFileWindow :: onUploadButtonClicked(void) throw ()
void
UploadFileWindow :: onCloseButtonClicked(void) throw ()
{
fileNameEntry->set_text("");
for (unsigned int i=0; i < metadataEntries.size(); ++i) {
Gtk::Entry * metadataEntry = metadataEntries[i];
metadataEntry->set_text("");
}
statusBar->set_text("");
isAudioClipValid = false;
clearEverything();
hide();
}
@ -445,8 +490,8 @@ Ptr<time_duration>::Ref
UploadFileWindow :: readPlaylength(const std::string & fileName)
throw (std::invalid_argument)
{
// TODO: replace this with mime-type detection (gnomevfs?) and
// the appropriate TagLib::X::File subclass constructors
// TODO: use the appropriate TagLib::X::File subclass constructors,
// once we find some way of determining the MIME type.
TagLib::FileRef fileRef(fileName.c_str());
if (fileRef.isNull()) {
throw std::invalid_argument("unsupported file type");
@ -462,3 +507,44 @@ UploadFileWindow :: readPlaylength(const std::string & fileName)
return length;
}
/*------------------------------------------------------------------------------
* Determine the type of the given file.
*----------------------------------------------------------------------------*/
UploadFileWindow::FileType
UploadFileWindow :: determineFileType(const std::string & fileName)
throw ()
{
unsigned int dotPosition = fileName.find('.');
if (dotPosition == std::string::npos) {
return invalidType;
}
std::string extension = fileName.substr(dotPosition);
if (extension == ".mp3" || extension == ".ogg") {
return audioClipType;
} else if (extension == ".tar") {
return playlistArchiveType;
} else {
return invalidType;
}
}
/*------------------------------------------------------------------------------
* Clear all the input fields and set the fileType to 'invalidType'.
*----------------------------------------------------------------------------*/
void
UploadFileWindow :: clearEverything(void) throw ()
{
fileNameEntry->set_text("");
for (unsigned int i=0; i < metadataEntries.size(); ++i) {
Gtk::Entry * metadataEntry = metadataEntries[i];
metadataEntry->set_text("");
}
statusBar->set_text("");
fileType = invalidType;
}

View file

@ -178,15 +178,83 @@ class UploadFileWindow : public GuiWindow
Gtk::Label * statusBar;
/**
* The name of the file to upload.
* The audio clip to be uploaded.
*/
Ptr<AudioClip>::Ref audioClip;
/**
* Signals if the audio clip is valid.
* The possible file types.
*/
bool isAudioClipValid;
typedef enum { audioClipType, playlistArchiveType, invalidType }
FileType;
/**
* The type of the currently selected file.
*/
FileType fileType;
/**
* Update the information for the file to upload, based on the
* value of the fileNameEntry text entry field.
*/
void
updateFileInfo(void) throw ();
/**
* Read the playlength and metadata info from the binary audio file.
*
* @param fileName the local file name (with path) for the
* binary audio file.
*/
void
readAudioClipInfo(const std::string & fileName) throw ();
/**
* Determine the length of an audio file on disk.
*
* @param fileName a binary audio file (e.g., /tmp/some_clip.mp3)
* @return the length of the file; a null pointer if the
* length could not be read (see bug #1426)
* @exception std::invalid_argument if the file is not found, or its
* format is not supported by TagLib
*/
Ptr<time_duration>::Ref
readPlaylength(const std::string & fileName)
throw (std::invalid_argument);
/**
* Upload an audio clip to the storage.
*/
void
uploadAudioClip(void) throw ();
/**
* Upload a playlist archive to the storage.
*/
void
uploadPlaylistArchive(void) throw ();
/**
* Determine the type of the given file.
*
* This method looks at the extension only.
* TODO: replace this with proper mime-type detection
* (gnomevfs, system("file fileName"), or ...?)
*
* @param fileName the name (with path) of the local file.
* @return the type of the file.
*/
FileType
determineFileType(const std::string & fileName) throw ();
/**
* Clear all the input fields and set the fileType to 'invalidType'.
*/
void
clearEverything(void) throw ();
protected:
/**
* Function to catch the event of the choose file button being
* pressed.
@ -208,7 +276,7 @@ class UploadFileWindow : public GuiWindow
* @param event the event recieved.
* @return true if the event has been processed, false otherwise.
*/
bool
virtual bool
onFileNameEntryLeave(GdkEventFocus * event) throw ();
/**
@ -217,26 +285,6 @@ class UploadFileWindow : public GuiWindow
virtual void
onCloseButtonClicked(void) throw ();
/**
* Update the information for the file to upload, based on the
* value of the fileNameEntry text entry field.
*/
void
updateFileInfo(void) throw ();
/**
* Determine the length of an audio file on disk.
*
* @param fileName a binary audio file (e.g., /tmp/some_clip.mp3)
* @return the length of the file; a null pointer if the
* length could not be read (see bug #1426)
* @exception std::invalid_argument if the file is not found, or its
* format is not supported by TagLib
*/
Ptr<time_duration>::Ref
readPlaylength(const std::string & fileName)
throw (std::invalid_argument);
public:
/**

View file

@ -116,7 +116,7 @@ root:table
fileChooserDialogTitle:string
{ "Open File" }
clipUploadedMsg:string { "Uploaded clip ''{0}''." }
fileUploadedMsg:string { "Uploaded ''{0}''." }
couldNotOpenFileMsg:string { "The file could not be opened." }
couldNotReadLengthMsg:string
{ "Could not determine audio clip length." }