mpxplugins/serviceplugins/playlistplugins/m3uplaylistplugin/src/mpxm3uplaylistexporter.cpp
changeset 0 ff3acec5bc43
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpxplugins/serviceplugins/playlistplugins/m3uplaylistplugin/src/mpxm3uplaylistexporter.cpp	Thu Dec 17 08:45:05 2009 +0200
@@ -0,0 +1,543 @@
+/*
+* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  externalizes a m3u playlist
+*
+*  CMPXM3uPlaylistExporter is a self-completing CActiveObject. It starts
+*  its exporting process by invoking DoTaskStep as soon as it's instantiated.
+*  Although it doesn't have a state machine, it will still process the request
+*  in steps. In each step, a fixed number of medias, KMPXM3UNumOfMediasToProcess,
+*  are processed. These medias will be converted into M3U line array,
+*  M3U line array will be transformed into buffer, and then the buffer will be
+*  flashed into the file. Once done, it completes its own request and RunL is
+*  called where DoTaskStep is called again to process another
+*  KMPXM3UNumOfMediasToProcess until all medias have been processed.
+*  After all medias have been processed, it notifies its caller of the
+*  results through MMPXPlaylistPluginObserver interface. Once its caller
+*  completes processing of these results, caller's request is completed.
+*
+*
+*/
+
+
+// INCLUDE FILES
+#include <bautils.h>
+#include <utf.h>
+#include <charconv.h>
+#include <sysutil.h>
+#ifdef RD_MULTIPLE_DRIVE
+#include <driveinfo.h>
+#endif //RD_MULTIPLE_DRIVE
+#include <mpxlog.h>
+#include <mpxmediaarray.h>
+#include <mpxattribute.h>
+#include <mpxmediageneraldefs.h>
+#include <mpxmediacontainerdefs.h>
+#include "mpxm3uplaylistdefs.h"
+#include "mpxm3uplaylistexporter.h"
+
+
+// ============================ MEMBER FUNCTIONS ==============================
+// ----------------------------------------------------------------------------
+// Constructor.
+// ----------------------------------------------------------------------------
+EXPORT_C CMPXM3uPlaylistExporter::CMPXM3uPlaylistExporter(
+            RFs* aFs,
+            MMPXPlaylistPluginObserver* aObserver,
+            const CMPXMedia& aPlaylist,
+            TRequestStatus& aStatus ) :
+    CActive( EPriorityStandard ),
+    iPlaylist( aPlaylist ),
+    iDriveNumber( EDriveE ),
+    iPlaylistFileCreated( EFalse ),
+    iFs( aFs ),
+    iObserver( aObserver ),
+    iCurrentMedia( -1 ),
+    iMoreToDo( ETrue ),
+    iCallerStatus( &aStatus )
+    {
+    CActiveScheduler::Add(this);
+    }
+
+// ----------------------------------------------------------------------------
+// 2nd phase constructor
+// ----------------------------------------------------------------------------
+EXPORT_C void CMPXM3uPlaylistExporter::ConstructL(
+            const TDesC& aFilePath )
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistExporter::ConstructL() entering");
+
+    // aFilePath should exist
+    __ASSERT_DEBUG(aFilePath.Length(), User::Leave(KErrCorrupt));
+
+#ifdef RD_MULTIPLE_DRIVE
+    // Use the default MMC drive
+    User::LeaveIfError( DriveInfo::GetDefaultDrive(
+        DriveInfo::EDefaultRemovableMassStorage,
+        iDriveNumber ) );
+#endif // RD_MULTIPLE_DRIVE
+
+    // retrieve playlist medias
+    CMPXMediaArray* ary = iPlaylist.Value<CMPXMediaArray>(KMPXMediaArrayContents);
+    User::LeaveIfNull( ary );
+    iMedias = CMPXMediaArray::NewL( *ary );
+
+    // check if the playlist name contains invalid characters for the file system
+    // if so, use filename provided in the URI if provided; otherwise leave with
+    // KErrBadName
+    const TDesC& playlistName =
+        iPlaylist.ValueText(KMPXMediaGeneralTitle);
+
+    HBufC* playlistFilename(NULL);
+
+    if (iFs->IsValidName(playlistName))
+        {
+        playlistFilename = playlistName.AllocLC();
+        }
+    else if (iPlaylist.IsSupported(KMPXMediaGeneralUri))
+        {
+        TParsePtrC parse(iPlaylist.ValueText(KMPXMediaGeneralUri));
+        playlistFilename = parse.Name().AllocLC();
+        }
+    else
+        {
+        User::Leave(KErrBadName);
+        }
+
+    // set playlist file path
+    iPlaylistFilePath =
+        HBufC::NewL( playlistFilename->Length() +
+                     aFilePath.Length() +
+                     KMPXM3UExtension().Length() );
+
+    TPtr playlistFilePath = iPlaylistFilePath->Des();
+
+    playlistFilePath.Append(aFilePath);
+    playlistFilePath.Append(*playlistFilename);
+    playlistFilePath.Append(KMPXM3UExtension);
+
+    CleanupStack::PopAndDestroy(playlistFilename);
+
+    // determine designation drive number
+    TParsePtrC parse(*iPlaylistFilePath);
+    TPtrC drive = parse.Drive();
+    User::LeaveIfError(iFs->CharToDrive(TChar(drive.Ptr()[0]), iDriveNumber));
+
+    *iCallerStatus = KRequestPending;
+
+    TRequestStatus* status = &iStatus;
+    *status = KRequestPending;
+    User::RequestComplete(status, KErrNone);
+    SetActive();
+
+    MPX_DEBUG1("CMPXM3uPlaylistExporter::ConstructL() exiting");
+    }
+
+// ----------------------------------------------------------------------------
+// Two-phased constructor.
+// ----------------------------------------------------------------------------
+EXPORT_C CMPXM3uPlaylistExporter* CMPXM3uPlaylistExporter::NewL(
+            RFs* aFs,
+            MMPXPlaylistPluginObserver* aObserver,
+            const CMPXMedia& aPlaylist,
+            const TDesC& aFilePath,
+            TRequestStatus& aStatus )
+    {
+    CMPXM3uPlaylistExporter* self =
+        new(ELeave)CMPXM3uPlaylistExporter(aFs, aObserver, aPlaylist, aStatus );
+    CleanupStack::PushL(self);
+    self->ConstructL( aFilePath );
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ----------------------------------------------------------------------------
+// Destructor.
+// ----------------------------------------------------------------------------
+EXPORT_C CMPXM3uPlaylistExporter::~CMPXM3uPlaylistExporter()
+    {
+    Cancel();
+    Cleanup();
+
+    delete iMedias;
+    delete iPlaylistFilePath;
+    }
+
+// ----------------------------------------------------------------------------
+// Handles request completion event
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistExporter::RunL()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistImporter::RunL");
+
+    if ( iMoreToDo && iStatus.Int() == KErrNone )
+        {
+        DoTaskStep();
+        SetActive();
+        }
+    else
+        {
+        User::RequestComplete( iCallerStatus, iStatus.Int() );
+        Cleanup();
+        NotifyClient(iStatus.Int());
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Implements cancellation of an outstanding request.
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistExporter::DoCancel()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistExporter::DoCancel");
+
+    TInt error( KErrCancel );
+    Cleanup();
+
+    // notify client. return the playlist with the least invalid paths
+    NotifyClient(error);
+
+    if ( iCallerStatus )
+        {
+        User::RequestComplete( iCallerStatus, error );
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Performs one step of the task.
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistExporter::DoTaskStep()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistExporter::DoTaskStep()");
+
+    TRequestStatus* status = &iStatus;
+    *status = KRequestPending;
+
+    TInt error( KErrNone );
+
+    MPX_TRAP( error, ExternalizeL() );
+
+    User::RequestComplete( status, error );
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::ExternalizeL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::ExternalizeL()
+    {
+    PopulateLineArrayL();
+    FlushLineArrayToBufferL();
+    WritePlaylistToFileL();
+    Cleanup();
+
+    if (iMedias->Count() == 0)
+        {
+        iMoreToDo = EFalse;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::PopulateLineArrayL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::PopulateLineArrayL()
+    {
+    MPX_DEBUG2("Before PopulateLineArrayL: heap size = %d", User::Heap().Size());
+
+    // Parse lowercase path to the current folder
+    // Convert playlist file path to lower case
+    HBufC* plTmp = iPlaylistFilePath->AllocLC();
+
+    TPtr ptr(plTmp->Des());
+    ptr.LowerCase();
+
+    TParse playlistPath;
+    playlistPath.Set(*plTmp, NULL, NULL);
+    // Path to the folder, where playlist file is located to
+    TPtrC currentFolder = playlistPath.DriveAndPath();
+
+    TInt count = iMedias->Count();
+    TBool addNewLine(ETrue);
+    for (TInt i = 0; i < KMPXM3UNumOfMediasToProcess && i < count; i++)
+        {
+        CMPXMedia* item = (*iMedias)[0];
+
+        // Create and write Extented format tag if this is
+        // the first media being processed
+        if (++iCurrentMedia == 0)
+            {
+            CreateHeaderLineL();
+            AddLineToArrayL();
+            }
+
+        // Create and write extendted info line
+        CreateExtentedInfoLineL(*item);
+        AddLineToArrayL();
+
+        // Create and write path line
+        if (i == count-1)
+            {
+            // This is last item => no new line
+            addNewLine = EFalse;
+            }
+
+        CreatePathLineL(*item, currentFolder, addNewLine);
+
+        // delete media now to save memory consumption
+        iMedias->Remove(0);
+
+        AddLineToArrayL();
+        }
+
+    CleanupStack::PopAndDestroy(plTmp ); // plTmp
+
+    MPX_DEBUG2("After PopulateLineArrayL: heap size = %d", User::Heap().Size());
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::AddLineToArrayL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::AddLineToArrayL()
+    {
+    if (iLine)
+        {
+        // Convert line from Unicode to UTF-8
+        // UTF-8 encoded character may consume several bytes =>
+        // multiply the descriptor length in order to prevent overflow.
+        HBufC8* line = HBufC8::NewLC(iLine->Length() * KMPXM3UUtf8ConvMultiplier);
+
+        TPtr8 ptr = line->Des();
+         // According to current knowledge, this should not be ASCII
+        CnvUtfConverter::ConvertFromUnicodeToUtf8(ptr, *iLine);
+
+        iLines.AppendL(line);
+        CleanupStack::Pop( line );
+
+        delete iLine;
+        iLine = NULL;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::CreateHeaderLineL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::CreateHeaderLineL()
+    {
+    delete iLine;
+    iLine = NULL;
+
+    iLine = HBufC::NewL(KMPXM3UTagExtm3u().Length() + KMPXM3ULengthOfLineChange);
+    TPtr ptr = iLine->Des();
+
+    ptr.Append(KMPXM3UTagExtm3u);
+    ptr.Append(KMPXM3ULineChange);
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::CreateExtentedLineL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::CreateExtentedInfoLineL(
+            const CMPXMedia& aMedia)
+    {
+    delete iLine;
+    iLine = NULL;
+
+    const TDesC& title =
+        (aMedia.IsSupported(KMPXMediaGeneralTitle)) ?
+            aMedia.ValueText(KMPXMediaGeneralTitle) : KNullDesC();
+
+    if (title.Length())
+        {
+        TInt timeEntry(0);
+
+        if (aMedia.IsSupported(KMPXMediaGeneralDuration))
+            {
+            timeEntry = aMedia.ValueTObjectL<TInt>(KMPXMediaGeneralDuration);
+            }
+
+        if (timeEntry < KMPXM3UIgnoreTimeEntry || timeEntry > KMPXM3UMaxTimeEntry)
+            {
+            timeEntry = KMPXM3UIgnoreTimeEntry;
+            }
+
+        iLine = HBufC::NewL(KMPXM3UMaxLengthOfExtinfStaticPart + title.Length());
+
+        TPtr ptr = iLine->Des();
+
+        ptr.Append(KMPXM3UTagExtinf);
+        ptr.AppendNum(timeEntry);
+        ptr.Append(KMPXM3UPoint);
+        ptr.Append(title);
+        ptr.Append(KMPXM3ULineChange);
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::CreatePathLineL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::CreatePathLineL(
+            const CMPXMedia& aMedia,
+            const TDesC& aCurrentFolder,
+            TBool aAddNewLine )
+    {
+    if (!aMedia.IsSupported(KMPXMediaGeneralUri))
+        {
+        User::Leave(KErrArgument);
+        }
+
+    // Temporary pointer to item path
+    const TDesC& itemPath = aMedia.ValueText(KMPXMediaGeneralUri);
+
+    delete iLine;
+    iLine = NULL;
+    iLine = HBufC::NewL(itemPath.Length() + KMPXM3ULengthOfLineChange);
+    TPtr ptr = iLine->Des();
+
+    // Create temporary copy of the path and set it lowercase
+    HBufC* path = itemPath.AllocLC();
+    path->Des().LowerCase();
+    // Try to find current folder path from the item path
+    TInt offset = path->Find(aCurrentFolder);
+
+    if (offset != KErrNotFound)
+        {
+        // File is under the current folder => append relative path
+        ptr.Append(itemPath.Mid(aCurrentFolder.Length()));
+        }
+    else
+        {
+        // File is not under the current path => append absolute path
+        ptr.Append(itemPath);
+        }
+
+    CleanupStack::PopAndDestroy(path); // path
+
+    if (aAddNewLine)
+        {
+        ptr.Append(KMPXM3ULineChange);
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::FlushLineArrayToBufferL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::FlushLineArrayToBufferL()
+    {
+    MPX_DEBUG2("Before FlushLineArrayToBufferL: heap size = %d", User::Heap().Size());
+
+    TInt length(0);
+    TInt count = iLines.Count();
+    for (TInt ii = 0; ii < count; ++ii)
+        {
+        length += iLines[ii]->Length();
+        }
+
+    delete iPlaylistBuf;
+    iPlaylistBuf = NULL;
+    iPlaylistBuf = HBufC8::NewL(length);
+    TPtr8 ptr = iPlaylistBuf->Des();
+
+    for (TInt jj = 0; jj < count; ++jj)
+        {
+        ptr.Append(*iLines[0]);
+        // delete the line from the lines array now to save memory consumption
+        delete iLines[0];
+
+        // remove the appended element from the array
+        iLines.Remove(0);
+        }
+
+    MPX_DEBUG2("After FlushLineArrayToBufferL: heap size = %d", User::Heap().Size());
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistExporter::WritePlaylistToFileL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistExporter::WritePlaylistToFileL()
+    {
+    // Open playlist file
+    RFile file;
+    if (!iPlaylistFileCreated)
+        {
+        User::LeaveIfError(file.Replace(*iFs, *iPlaylistFilePath, EFileWrite));
+        iPlaylistFileCreated = ETrue;
+        }
+    else
+        {
+        User::LeaveIfError(file.Open(*iFs, *iPlaylistFilePath, EFileWrite));
+        }
+    CleanupClosePushL(file);
+
+    // Calculate the increase in the playlist file size
+    TInt oldSize;
+    User::LeaveIfError(file.Size(oldSize));
+    TInt sizeIncr = iPlaylistBuf->Size() - oldSize;
+
+    if (sizeIncr > 0 &&
+        SysUtil::DiskSpaceBelowCriticalLevelL(iFs, sizeIncr, iDriveNumber))
+        {
+        // Don't show any own notes here
+        User::Leave(KErrDiskFull);
+        }
+
+    // Write file to permanent memory
+    User::LeaveIfError(file.Write(oldSize, *iPlaylistBuf));
+
+    file.Flush();
+    CleanupStack::PopAndDestroy(&file); // file
+    }
+
+// ----------------------------------------------------------------------------
+// Cleanup.
+// ----------------------------------------------------------------------------
+void CMPXM3uPlaylistExporter::Cleanup()
+    {
+    iLines.ResetAndDestroy();
+
+    delete iLine;
+    iLine = NULL;
+
+    delete iPlaylistBuf;
+    iPlaylistBuf = NULL;
+    }
+
+// ----------------------------------------------------------------------------
+// Handles notifications to the client
+// ----------------------------------------------------------------------------
+void CMPXM3uPlaylistExporter::NotifyClient( TInt aError )
+    {
+    MPX_DEBUG2("CMPXM3uPlaylistExporter::NotifyClient(%d)", aError);
+
+    if ( iObserver )
+        {
+        // notify client. return the playlist with the least invalid paths
+        TRAP_IGNORE(iObserver->HandlePlaylistL( *iPlaylistFilePath, aError ));
+        }
+
+    if ( aError )
+        {
+        // delete the partial playlist file.
+        iFs->Delete(*iPlaylistFilePath);
+        }
+    }
+
+// End of file