mpxplugins/serviceplugins/playlistplugins/m3uplaylistplugin/src/mpxm3uplaylistimporter.cpp
changeset 0 ff3acec5bc43
equal deleted inserted replaced
-1:000000000000 0:ff3acec5bc43
       
     1 /*
       
     2 * Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Imports a m3u playlist file
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include <bautils.h>
       
    21 #include <utf.h>
       
    22 #include <charconv.h>
       
    23 #include <mpxlog.h>
       
    24 #include <mpxmediaarray.h>
       
    25 #include <mpxattribute.h>
       
    26 #include <mpxmediageneraldefs.h>
       
    27 #include <mpxmediacontainerdefs.h>
       
    28 #include "mpxm3uplaylistdefs.h"
       
    29 #include "mpxm3uplaylistimporter.h"
       
    30 
       
    31 
       
    32 // ============================ CONSTANTS ==============================
       
    33 const TUint KUnicodeBOM = 0xFEFF;
       
    34 const TInt KMinimumConfidenceRequired = 60;
       
    35 const TInt KPathStartingChars = 3;
       
    36 
       
    37 // ============================ MEMBER FUNCTIONS ==============================
       
    38 // ----------------------------------------------------------------------------
       
    39 // Constructor. 
       
    40 // ----------------------------------------------------------------------------
       
    41 EXPORT_C CMPXM3uPlaylistImporter::CMPXM3uPlaylistImporter(
       
    42             RFs* aFs,
       
    43             MMPXPlaylistPluginObserver* aObserver,
       
    44             const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aTopCharacterSet,
       
    45             const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aAvailableCharacterSet,
       
    46             TRequestStatus& aStatus ) :
       
    47     CActive( EPriorityStandard ),
       
    48     iFs( aFs ),
       
    49     iObserver( aObserver ),
       
    50     iCallerStatus( &aStatus ),
       
    51     iEndLineNumber( KMPXM3UNumOfLinesToProcess ),
       
    52     iCurrentLineNumber( 0 ),
       
    53     iMoreToDo( ETrue ),
       
    54     iEndOfFile( EFalse ),
       
    55     iAutoEncodingInvalidItems( 0 ),
       
    56     iState( EMPXM3UReadBufferWithAutoDetectEncoding ),
       
    57     iTopCharacterSet( aTopCharacterSet ),
       
    58     iAvailableCharacterSet( aAvailableCharacterSet )
       
    59     {
       
    60     CActiveScheduler::Add( this );
       
    61     }
       
    62 
       
    63 // ----------------------------------------------------------------------------
       
    64 // 2nd phase constructor
       
    65 // ----------------------------------------------------------------------------
       
    66 EXPORT_C void CMPXM3uPlaylistImporter::ConstructL( const TDesC& aPlaylistUri )
       
    67     {
       
    68     MPX_DEBUG2("CMPXM3uPlaylistImporter::ConstructL(%S) entering", &aPlaylistUri);
       
    69 
       
    70     __ASSERT_DEBUG(aPlaylistUri.Length() != 0, User::Leave(KErrArgument));
       
    71     iPlaylistFilePath.Set(aPlaylistUri);
       
    72 
       
    73     iAutoEncodingPlaylistArray = CMPXMediaArray::NewL();
       
    74     
       
    75     *iCallerStatus = KRequestPending;
       
    76 
       
    77     TRequestStatus* status = &iStatus;
       
    78     *status = KRequestPending;
       
    79     User::RequestComplete(status, KErrNone);   
       
    80     SetActive();
       
    81     
       
    82     MPX_DEBUG1("CMPXM3uPlaylistImporter::ConstructL() exiting");
       
    83     }
       
    84     
       
    85 // ----------------------------------------------------------------------------
       
    86 // Two-phased constructor. 
       
    87 // ----------------------------------------------------------------------------
       
    88 EXPORT_C CMPXM3uPlaylistImporter* CMPXM3uPlaylistImporter::NewL(
       
    89             RFs* aFs,
       
    90             MMPXPlaylistPluginObserver* aObserver,
       
    91             const TDesC& aPlaylistUri,
       
    92             const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aTopCharacterSet,
       
    93             const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aAvailableCharacterSet,
       
    94             TRequestStatus& aStatus )            
       
    95     {
       
    96     CMPXM3uPlaylistImporter* self =
       
    97         new(ELeave)CMPXM3uPlaylistImporter(
       
    98             aFs, aObserver, aTopCharacterSet, aAvailableCharacterSet, aStatus );
       
    99     CleanupStack::PushL( self );
       
   100     self->ConstructL( aPlaylistUri );
       
   101     CleanupStack::Pop( self );
       
   102     return self;
       
   103     }
       
   104 
       
   105 // ----------------------------------------------------------------------------
       
   106 // Destructor. 
       
   107 // ----------------------------------------------------------------------------
       
   108 //
       
   109 EXPORT_C CMPXM3uPlaylistImporter::~CMPXM3uPlaylistImporter()
       
   110     {
       
   111     Cancel();
       
   112     Cleanup();
       
   113     }
       
   114 
       
   115 // ----------------------------------------------------------------------------
       
   116 // Handles request completion event
       
   117 // ----------------------------------------------------------------------------
       
   118 //
       
   119 EXPORT_C void CMPXM3uPlaylistImporter::RunL()
       
   120     {
       
   121     MPX_DEBUG1("CMPXM3uPlaylistImporter::RunL");
       
   122     
       
   123     if ( iMoreToDo && iStatus.Int() == KErrNone )
       
   124         {
       
   125         DoTaskStep();
       
   126         SetActive();
       
   127         }
       
   128     else
       
   129         {
       
   130         User::RequestComplete( iCallerStatus, iStatus.Int() );        
       
   131         NotifyClient(iStatus.Int());      
       
   132         Cleanup();                    
       
   133         }
       
   134     }
       
   135     
       
   136 // ----------------------------------------------------------------------------
       
   137 // Implements cancellation of an outstanding request.
       
   138 // ----------------------------------------------------------------------------
       
   139 //
       
   140 EXPORT_C void CMPXM3uPlaylistImporter::DoCancel()
       
   141     {
       
   142     MPX_DEBUG1("CMPXM3uPlaylistImporter::DoCancel");
       
   143 
       
   144     TInt error( KErrCancel );
       
   145     
       
   146     // notify client that the request has been cancelled
       
   147     NotifyClient(error);
       
   148 
       
   149     Cleanup();
       
   150     
       
   151     if ( iCallerStatus )
       
   152         {
       
   153         User::RequestComplete( iCallerStatus, error );
       
   154         }
       
   155     }
       
   156 
       
   157 // ----------------------------------------------------------------------------
       
   158 // Performs one step of the task
       
   159 // ----------------------------------------------------------------------------
       
   160 //
       
   161 EXPORT_C void CMPXM3uPlaylistImporter::DoTaskStep()
       
   162     {
       
   163     MPX_DEBUG1("CMPXM3uPlaylistImporter::DoTaskStep()");
       
   164     
       
   165     TRequestStatus* status = &iStatus;
       
   166     *status = KRequestPending;
       
   167 
       
   168     TInt error( KErrNone );
       
   169     
       
   170     MPX_TRAP( error, DoTaskStepL() );
       
   171 
       
   172     User::RequestComplete( status, error );
       
   173     }
       
   174         
       
   175 // ----------------------------------------------------------------------------
       
   176 // Performs one step of the task. Leaves when an error is encountered
       
   177 // ----------------------------------------------------------------------------
       
   178 //
       
   179 EXPORT_C void CMPXM3uPlaylistImporter::DoTaskStepL()
       
   180     {
       
   181     MPX_DEBUG1("CMPXM3uPlaylistImporter::DoTaskStepL()");
       
   182     
       
   183     switch( iState )
       
   184         {        
       
   185         case EMPXM3UReadBufferWithAutoDetectEncoding:
       
   186             {
       
   187             ReadPlaylistFileToBufferL();
       
   188             iState = EMPXM3UParseWithAutoDetectEncoding;
       
   189             }
       
   190             break;
       
   191             
       
   192         case EMPXM3UParseWithAutoDetectEncoding:
       
   193             {
       
   194             ParsePlaylistBufferL(
       
   195                 *iAutoEncodingPlaylistArray, iAutoEncodingInvalidItems );
       
   196             
       
   197             // If at the moment, we know that there is at least one error parsing
       
   198             // with auto detect encoding, we don't need to proceed until end of
       
   199             // file anymore, this playlist file is concluded to be corrupted
       
   200             if ( iAutoEncodingInvalidItems > 0 )
       
   201                 {
       
   202                 delete iAutoEncodingPlaylistArray;
       
   203                 iAutoEncodingPlaylistArray = NULL;
       
   204                 
       
   205                 User::Leave(KErrCorrupt);
       
   206                 }
       
   207 
       
   208             // we've finished parsing with auto detect encoding we will return
       
   209             // the playlist parsed with auto encoding
       
   210             else if ( iEndOfFile )
       
   211                 {               
       
   212                 iState = EMPXM3UComposePlaylistMedia;
       
   213                 }
       
   214             }       
       
   215             break;
       
   216 
       
   217         case EMPXM3UComposePlaylistMedia:
       
   218             {
       
   219             ComposePlaylistL();
       
   220             iMoreToDo = EFalse;
       
   221             }
       
   222             break;
       
   223             
       
   224         default:
       
   225             {
       
   226             User::Leave(KErrAbort);
       
   227             }
       
   228             break;
       
   229         }
       
   230     }
       
   231 
       
   232 // -----------------------------------------------------------------------------
       
   233 // CMPXM3uPlaylistImporter::ReadPlaylistFileToBufferL
       
   234 // -----------------------------------------------------------------------------
       
   235 //
       
   236 void CMPXM3uPlaylistImporter::ReadPlaylistFileToBufferL()
       
   237     {
       
   238     MPX_DEBUG2("Before reading playlist to buffer: heap size = %d", User::Heap().Size());    
       
   239 
       
   240     delete iBuffer;
       
   241     iBuffer = NULL;
       
   242     iBufferPtr.Set(KNullDesC);    
       
   243 
       
   244     //
       
   245     // leave with KErrNotFound if the playlist file does not exist
       
   246     //    
       
   247     if (!BaflUtils::FileExists(*iFs, iPlaylistFilePath))
       
   248         {
       
   249         User::Leave(KErrNotFound);
       
   250         }
       
   251     
       
   252     TEntry entry;
       
   253     User::LeaveIfError(iFs->Entry(iPlaylistFilePath, entry));
       
   254     
       
   255     HBufC* buffer = HBufC::NewLC(entry.iSize);
       
   256     TPtr ptr = buffer->Des();
       
   257 
       
   258     HBufC8* buf8 = HBufC8::NewLC(entry.iSize);
       
   259     TPtr8 ptr8 = buf8->Des();
       
   260 
       
   261     // Read the first KPlaylistSampleLength bytes of the file
       
   262     TInt sampleLength(KPlaylistSampleLength);
       
   263     if (sampleLength > entry.iSize)
       
   264         {
       
   265         sampleLength = entry.iSize;
       
   266         }
       
   267     User::LeaveIfError(iFs->ReadFileSection(
       
   268                             iPlaylistFilePath, 0, ptr8, sampleLength));
       
   269 
       
   270     // auto detect character encoding
       
   271     TUint charSetId(0);
       
   272     TInt error = DetectCharacterSetL(*buf8, iTopCharacterSet, charSetId);
       
   273     MPX_DEBUG3("encoding detected using top character set is 0x%x, error %d", charSetId, error);
       
   274     
       
   275     // when we fail to detect the encoding, use all available character set in the
       
   276     // system to try again. If that also fails, abandon the operation.
       
   277     if (error)
       
   278         {
       
   279         User::LeaveIfError(DetectCharacterSetL(*buf8, iAvailableCharacterSet, charSetId));
       
   280         MPX_DEBUG2("encoding detected using available character set is 0x%x", charSetId);        
       
   281         }
       
   282 
       
   283     // read the whole file if the sample taken isn't the whole file
       
   284     if (sampleLength != entry.iSize)
       
   285         {
       
   286         User::LeaveIfError(iFs->ReadFileSection(
       
   287                                 iPlaylistFilePath, 0, ptr8, entry.iSize));
       
   288         }
       
   289    
       
   290     // perform character conversion using the selected encoding
       
   291     TInt state(CCnvCharacterSetConverter::KStateDefault);
       
   292     TInt numOfUnconvertibleChars(0);
       
   293     CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
       
   294     charSetConv->PrepareToConvertToOrFromL(charSetId, iAvailableCharacterSet, *iFs);
       
   295     TInt retVal = charSetConv->ConvertToUnicode(ptr, *buf8, state, numOfUnconvertibleChars);
       
   296     User::LeaveIfError(retVal);
       
   297 
       
   298     // try again if the character set wasn't detected using the whole file
       
   299     if ((retVal > 0 || numOfUnconvertibleChars > 0) && (sampleLength != entry.iSize))
       
   300         {
       
   301         MPX_DEBUG4("retVal = %d, numOfUnconvertibleChars = %d, entry.iSize = %d",
       
   302                     retVal, numOfUnconvertibleChars, entry.iSize);
       
   303         numOfUnconvertibleChars = 0;
       
   304         retVal = 0;
       
   305         User::LeaveIfError(DetectCharacterSetL(*buf8, iAvailableCharacterSet, charSetId));
       
   306         charSetConv->PrepareToConvertToOrFromL(charSetId, iAvailableCharacterSet, *iFs);
       
   307         retVal = charSetConv->ConvertToUnicode(ptr, *buf8, state, numOfUnconvertibleChars);
       
   308         }
       
   309         
       
   310     if (retVal > 0 || numOfUnconvertibleChars > 0)
       
   311         {
       
   312         MPX_DEBUG3("Unable to find character encoding for the playlist file. retVal = %d, numOfUnconvertibleChars = %d",
       
   313                     retVal, numOfUnconvertibleChars);        
       
   314         User::Leave(KErrNotSupported);
       
   315         }
       
   316     
       
   317     // remove the byte order mark (BOM) character prepended at the beginning
       
   318     // of the stream if encoded with unicode as per Unicode section 2.4
       
   319     if ((charSetId == KCharacterSetIdentifierUnicodeLittle ||
       
   320          charSetId == KCharacterSetIdentifierUnicodeBig) &&
       
   321         ptr.Length() > 0 &&
       
   322         ptr[0] == KUnicodeBOM)
       
   323         {
       
   324         ptr.Delete(0,1);
       
   325         }
       
   326         
       
   327     iBuffer = buffer;
       
   328     iBufferPtr.Set(*iBuffer);
       
   329     
       
   330     CleanupStack::PopAndDestroy(2, buf8); // charSetConv & buf8    
       
   331     CleanupStack::Pop(buffer);
       
   332         
       
   333     // brand new buffer which hasn't been read, reset iCurrentLineNumber and
       
   334     // iEndLineNumber, and iEndOfFile
       
   335     iCurrentLineNumber = 0;
       
   336     iEndLineNumber = KMPXM3UNumOfLinesToProcess;
       
   337     iEndOfFile = EFalse;
       
   338 
       
   339     MPX_DEBUG2("After reading playlist to buffer: heap size = %d", User::Heap().Size());        
       
   340     }
       
   341 
       
   342 // -----------------------------------------------------------------------------
       
   343 // CMPXM3uPlaylistImporter::DetectCharacterSetL
       
   344 // copied and revised based on CMetaDataParserID3v1::DetectCharacterSetL version 4
       
   345 // -----------------------------------------------------------------------------
       
   346 //
       
   347 TInt CMPXM3uPlaylistImporter::DetectCharacterSetL(
       
   348             const TDesC8& aSample,
       
   349             const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aCharacterSet,
       
   350             TUint& aCharSetId)
       
   351 	{
       
   352 	// CCnvCharacterSetConverter::ConvertibleToCharSetL hangs if sample is too big
       
   353 	if (aSample.Size() > KPlaylistMaxSampleLength)
       
   354 	    {
       
   355 	    User::Leave(KErrNotSupported);
       
   356 	    }
       
   357 	    
       
   358 	TInt confidence(0);
       
   359 	TInt highestConfidence(0);
       
   360 	TUint charSetId(0);
       
   361 	TUint highestConfidencecharSetId(0);
       
   362 
       
   363 	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
       
   364 	TInt count = aCharacterSet.Count();
       
   365 	for ( TInt i=0; i < count; i++)
       
   366 		{
       
   367 		charSetId = aCharacterSet.At(i).Identifier();
       
   368 		charSetConv->ConvertibleToCharSetL(confidence, charSetId, aCharacterSet, aSample);
       
   369 		if ( confidence > highestConfidence )
       
   370 			{
       
   371 			highestConfidence = confidence;
       
   372 			highestConfidencecharSetId = charSetId;
       
   373 			}
       
   374 		}
       
   375 	CleanupStack::PopAndDestroy(charSetConv);
       
   376 	MPX_DEBUG3("CMPXM3uPlaylistImporter::DetectCharacterSetL :-> Confidence[%d] CharSetId[0x%x]",
       
   377    		confidence, aCharSetId);
       
   378 	if ( highestConfidence == 0 || highestConfidence < KMinimumConfidenceRequired )
       
   379 		{
       
   380 		return KErrNotFound;
       
   381 		}
       
   382 	else
       
   383 		{
       
   384 		aCharSetId = highestConfidencecharSetId;
       
   385 		return KErrNone;
       
   386 		}
       
   387 	}
       
   388 	
       
   389 // -----------------------------------------------------------------------------
       
   390 // CMPXM3uPlaylistPlugin::ParsePlaylistBufferL
       
   391 // -----------------------------------------------------------------------------
       
   392 //
       
   393 void CMPXM3uPlaylistImporter::ParsePlaylistBufferL(
       
   394             CMPXMediaArray& aPlaylist,
       
   395             TInt& aInvalidItemCount)
       
   396     {
       
   397     // Read and process all the lines in the file
       
   398     //
       
   399     // the order of the following conditions is important. ReadNextLineL
       
   400     // should be called last to avoid skipping one line
       
   401     while (iCurrentLineNumber < iEndLineNumber &&
       
   402            aPlaylist.Count() < KMPXM3UPlaylistMaxItemCount &&
       
   403            ReadNextLineL())
       
   404         {
       
   405         ProcessLineL(aPlaylist, aInvalidItemCount);  
       
   406         }
       
   407 
       
   408     if ( aPlaylist.Count() == KMPXM3UPlaylistMaxItemCount )
       
   409         {
       
   410         User::Leave(KErrOverflow);
       
   411         }
       
   412 
       
   413     //
       
   414     // haven't finished processing all lines in the file, but have processed
       
   415     // KMPXM3UNumOfLinesToProcess number of lines. Set up iEndLineNumber for
       
   416     // the next iteration
       
   417     //        
       
   418     if ( !iEndOfFile && iCurrentLineNumber == iEndLineNumber )
       
   419         {
       
   420         iEndLineNumber += KMPXM3UNumOfLinesToProcess;
       
   421         }
       
   422     }
       
   423 
       
   424 // -----------------------------------------------------------------------------
       
   425 // CMPXM3uPlaylistPlugin::ReadNextLineL
       
   426 // -----------------------------------------------------------------------------
       
   427 //
       
   428 TBool CMPXM3uPlaylistImporter::ReadNextLineL()
       
   429     {
       
   430     // iBuffer should exist when this function is called
       
   431     __ASSERT_DEBUG(iBuffer, User::Leave(KErrBadDescriptor));
       
   432 
       
   433     if (!iBufferPtr.Length())
       
   434         {
       
   435         // File end reached.
       
   436         iEndOfFile = ETrue;
       
   437         return EFalse;
       
   438         }
       
   439 
       
   440     delete iLine;
       
   441     iLine = NULL;
       
   442 
       
   443     // Try to find line change
       
   444     TInt offset = iBufferPtr.FindF(KMPXM3ULineChange);
       
   445   
       
   446     if (offset == KErrNotFound)
       
   447         {
       
   448         // No line change was found --> last line had no line change
       
   449         iLine = iBufferPtr.AllocL();
       
   450         // Set iBufferPtr to the end of buffer
       
   451         iBufferPtr.Set(iBufferPtr.Right(0)); //magic
       
   452         }
       
   453     else
       
   454         {
       
   455         // Found line change
       
   456         TInt length(offset);
       
   457         if ((offset > KMPXM3UNoOffset) && 
       
   458             (iBufferPtr[length - 1] == KMPXM3UCarriageReturn)) // magic
       
   459             {
       
   460             --length;
       
   461             }
       
   462 
       
   463         iLine = iBufferPtr.Left(length).AllocL();
       
   464 
       
   465         // Move past the line feed
       
   466         iBufferPtr.Set(iBufferPtr.Mid(++offset));
       
   467         }
       
   468 
       
   469     // Remove leading and trailing space characters from iLine's data.
       
   470     TPtr ptr = iLine->Des();
       
   471     ptr.Trim();
       
   472 
       
   473     iCurrentLineNumber++;
       
   474     return ETrue;
       
   475     }
       
   476     
       
   477 // -----------------------------------------------------------------------------
       
   478 // CMPXM3uPlaylistImporter::ProcessLineL()
       
   479 // -----------------------------------------------------------------------------
       
   480 //
       
   481 void CMPXM3uPlaylistImporter::ProcessLineL(
       
   482             CMPXMediaArray& aPlaylist,
       
   483             TInt& aInvalidItemCount)
       
   484     {
       
   485     if ( iCurrentLineNumber == 1 ) // first line
       
   486         {
       
   487         // Check whether the file is in the extented format
       
   488         TInt offset = iLine->Find(KMPXM3UTagExtm3u);
       
   489         if (offset == KErrNotFound || offset != KMPXM3UNoOffset ||
       
   490             iLine->Length() != KMPXM3UTagExtm3u().Length())
       
   491             {
       
   492             // The file is not in the extented format
       
   493             iExtendedFormat = EFalse;
       
   494             }
       
   495         else
       
   496             {
       
   497             // The file is in the extented format
       
   498             iExtendedFormat = ETrue;
       
   499             return;
       
   500             }        
       
   501         }
       
   502 
       
   503     if (!iItem)
       
   504         {
       
   505         iItem = CMPXMedia::NewL();
       
   506         iItem->SetTObjectValueL(KMPXMediaGeneralType, EMPXItem);
       
   507         iItem->SetTObjectValueL(KMPXMediaGeneralCategory, EMPXSong);
       
   508         }
       
   509     
       
   510     // Parse line and then decide what to do with it
       
   511     switch (ParseLineL(iItem, aInvalidItemCount))
       
   512         {
       
   513         case EMPXM3UPlaylistLineTypeExtinf:
       
   514             // Continue to next round
       
   515             break;
       
   516             
       
   517         case EMPXM3UPlaylistLineTypePath:
       
   518             {
       
   519             // Line was a path => add item to playlist
       
   520             aPlaylist.AppendL(iItem);
       
   521             iItem = NULL; // item now owned by aPlaylist
       
   522             }
       
   523             break; 
       
   524                        
       
   525         case EMPXM3UPlaylistLineTypeNotSupported:
       
   526         case EMPXM3UPlaylistLineTypeCorrupted:
       
   527         default:
       
   528             {
       
   529             // Line has unsupported extension tag or undefined has error
       
   530             // occurred -> delete item
       
   531             delete iItem;
       
   532             iItem = NULL;
       
   533             }
       
   534             break;
       
   535         }
       
   536     }
       
   537     
       
   538 // -----------------------------------------------------------------------------
       
   539 // CMPXM3uPlaylistImporter::ParseLineL
       
   540 // -----------------------------------------------------------------------------
       
   541 //
       
   542 TInt CMPXM3uPlaylistImporter::ParseLineL(
       
   543             CMPXMedia* aItem,
       
   544             TInt& aInvalidItemCount)
       
   545     {
       
   546     // There should be always aItem & iLine when this function is called
       
   547     __ASSERT_DEBUG(aItem && iLine, User::Leave(KErrAbort));
       
   548 
       
   549     if (!iLine->Length())
       
   550         {
       
   551         // Empty line => line is invalid
       
   552         return EMPXM3UPlaylistLineTypeNotSupported;
       
   553         }
       
   554 
       
   555     if (iExtendedFormat)
       
   556         {
       
   557         // File is in the extented format => check whether there is extented
       
   558         // info in this line.
       
   559         TInt offset = iLine->Find(KMPXM3UTagExtinf);
       
   560         if (offset != KErrNotFound && offset == KMPXM3UNoOffset)
       
   561             {
       
   562             // Extented info found
       
   563             aItem->SetTextValueL(KMPXMediaGeneralTitle, KNullDesC);
       
   564 
       
   565             offset = iLine->Find(KMPXM3UPoint);
       
   566 
       
   567             if(offset != KErrNotFound)
       
   568                 {
       
   569 
       
   570                 // Title and length found from extented info
       
   571                 // Parse length
       
   572                 TLex16 lex(iLine->Mid(KMPXM3UTagExtinf().Length(),
       
   573                                 offset - KMPXM3UTagExtinf().Length()));
       
   574                 TInt length;
       
   575                 if (lex.Val(length))
       
   576                     {
       
   577                     // Error has occurred => legth is set to be ignored
       
   578                     length = KMPXM3UIgnoreTimeEntry;
       
   579                     }
       
   580 
       
   581                 aItem->SetTObjectValueL(KMPXMediaGeneralDuration, length);
       
   582                 MPX_DEBUG2("    duration %d", length);
       
   583 
       
   584                 // Parse title
       
   585                 HBufC* title = iLine->Mid(offset + 1).AllocLC();
       
   586                 aItem->SetTextValueL(KMPXMediaGeneralTitle, *title);
       
   587                 MPX_DEBUG2("    title %S", title);
       
   588                 CleanupStack::PopAndDestroy( title );
       
   589 
       
   590                 return EMPXM3UPlaylistLineTypeExtinf; // line type extinf
       
   591                 }    
       
   592             }
       
   593         }
       
   594 
       
   595     // File is not in the extented format or supported info not found from this
       
   596     // line.
       
   597     switch (iLine->Find(KMPXM3UTagExt))
       
   598         {
       
   599         case KMPXM3UNoOffset:
       
   600             // Unsupported extended info tag found from this line
       
   601             return EMPXM3UPlaylistLineTypeNotSupported;
       
   602             
       
   603         case KErrNotFound:
       
   604         default:
       
   605             // Extended info not found from the beginning of line => line is
       
   606             // a path.
       
   607             {
       
   608             // Get absolute path
       
   609             TInt error(KErrNone);
       
   610             HBufC* uri = ParseAbsolutePathLC(*iLine, error);
       
   611         
       
   612             if (error)
       
   613                 {
       
   614                 if (error == KErrPathNotFound)
       
   615                     {
       
   616                     TUint flag(KMPXMediaGeneralFlagsSetOrUnsetBit | KMPXMediaGeneralFlagsIsInvalid);
       
   617                     aItem->SetTObjectValueL(KMPXMediaGeneralFlags, flag);
       
   618                     }
       
   619                 else
       
   620                     {
       
   621                     if( uri )
       
   622                         {
       
   623                         CleanupStack::PopAndDestroy( uri );
       
   624                         }
       
   625                     
       
   626                     ++aInvalidItemCount;
       
   627                                     
       
   628                     // All other errors are considered to mean playlist is
       
   629                     // corrupt.
       
   630                     return EMPXM3UPlaylistLineTypeCorrupted;
       
   631                     }
       
   632                 }
       
   633 
       
   634             aItem->SetTextValueL(KMPXMediaGeneralUri, *uri);
       
   635             MPX_DEBUG2("    uri %S", uri);
       
   636                 
       
   637             // if title isn't supplied by the m3u file, extract file name from
       
   638             // URI as the title
       
   639             if (!aItem->IsSupported(KMPXMediaGeneralTitle))
       
   640                 {
       
   641                 TParsePtrC parser(*uri);
       
   642                 TPtrC title = parser.Name();
       
   643                 aItem->SetTextValueL(KMPXMediaGeneralTitle, title);
       
   644                 MPX_DEBUG2("    title %S", &title);
       
   645                 }
       
   646 
       
   647             CleanupStack::PopAndDestroy( uri );
       
   648             
       
   649             return EMPXM3UPlaylistLineTypePath; // line type path
       
   650             }
       
   651         }
       
   652     }
       
   653 
       
   654 // -----------------------------------------------------------------------------
       
   655 // CMPlayerM3UPlaylistParser::ParseAbsolutePathL
       
   656 // -----------------------------------------------------------------------------
       
   657 //
       
   658 HBufC* CMPXM3uPlaylistImporter::ParseAbsolutePathLC(
       
   659             const TDesC& aPath,
       
   660             TInt& aError)
       
   661     {
       
   662     HBufC* path = NULL;
       
   663     
       
   664     TBool isAbsolute( EFalse );
       
   665     
       
   666     if (aPath.Length() > KPathStartingChars && 
       
   667         !aPath.Mid(1, 2).CompareF(KMPXM3UAbsPath)) // magic: the 2nd and 3rd chars
       
   668                                                // are always ":\"
       
   669                                                // for absolute paths
       
   670         {
       
   671         isAbsolute = ETrue;
       
   672         }
       
   673 
       
   674     if (aPath.Length() > KMaxFileName) // Test if path is too long
       
   675         {
       
   676         aError = KErrCorrupt;
       
   677         }
       
   678     else if ( isAbsolute )
       
   679         {
       
   680         aError = KErrNone;
       
   681         aError = iFs->IsValidName(aPath) ? KErrNone : KErrBadName;
       
   682         path = aPath.AllocLC();
       
   683         }
       
   684     else
       
   685         {
       
   686         // Given path could be relative => create absolute path and test it
       
   687         // Playlist file path
       
   688         TParse playlistPath;
       
   689         playlistPath.Set(iPlaylistFilePath, NULL, NULL);
       
   690         // Path to the folder, where playlist file is located to
       
   691         TPtrC currentFolder = playlistPath.DriveAndPath();
       
   692         // Create absolute path
       
   693         path = HBufC::NewLC(currentFolder.Length() + aPath.Length());
       
   694 
       
   695         TPtr tmpPtr(path->Des());
       
   696         tmpPtr = currentFolder;
       
   697         tmpPtr += aPath;
       
   698 
       
   699         aError = iFs->IsValidName(*path) ? KErrNone : KErrBadName;
       
   700         }
       
   701     
       
   702     // It is possible that a song exists in the filesystem but isn't added to
       
   703     // the database because it's not a supported type. If such song is included
       
   704     // in a playlist, it will be added to the database when the playlist is added.
       
   705     // Because of this, we cannot rely on whether the song exists in the database
       
   706     // to conclude whether the song is a broken link. We need to check for file
       
   707     // existence here. For the unsupported songs included in the playlist, they
       
   708     // will then be marked as corrupted when user initiates playback of those
       
   709     // songs.
       
   710     if (!aError &&
       
   711         !BaflUtils::FileExists(*iFs, *path))
       
   712         {
       
   713         aError = KErrPathNotFound;
       
   714         }
       
   715 
       
   716     return path;
       
   717     }
       
   718     
       
   719 // -----------------------------------------------------------------------------
       
   720 // CMPlayerM3UPlaylistParser::ComposePlaylistL
       
   721 // -----------------------------------------------------------------------------
       
   722 //
       
   723 void CMPXM3uPlaylistImporter::ComposePlaylistL()
       
   724     {
       
   725     //
       
   726     // instantiate a CMPXMedia that represent the playlist which will
       
   727     // contain the CMPXMediaArray
       
   728     //
       
   729     iPlaylist = CMPXMedia::NewL();
       
   730     
       
   731     // set playlist title
       
   732     TParsePtrC parser(iPlaylistFilePath);
       
   733     iPlaylist->SetTextValueL(KMPXMediaGeneralTitle, parser.Name());
       
   734     
       
   735     // set playlist URI
       
   736     iPlaylist->SetTextValueL(KMPXMediaGeneralUri, iPlaylistFilePath);
       
   737         
       
   738     // set type
       
   739     iPlaylist->SetTObjectValueL(KMPXMediaGeneralType, EMPXItem);
       
   740         
       
   741     // set category
       
   742     iPlaylist->SetTObjectValueL(KMPXMediaGeneralCategory, EMPXPlaylist);        
       
   743 
       
   744     // set playlist array
       
   745     iPlaylist->SetCObjectValueL(KMPXMediaArrayContents, iAutoEncodingPlaylistArray);
       
   746         
       
   747     // set array acount
       
   748     iPlaylist->SetTObjectValueL(KMPXMediaArrayCount, iAutoEncodingPlaylistArray->Count());
       
   749         
       
   750     // playlist makes a copy of the array, we can now free the medias
       
   751     // array
       
   752     delete iAutoEncodingPlaylistArray;
       
   753     iAutoEncodingPlaylistArray = NULL;
       
   754     }
       
   755     
       
   756 // ----------------------------------------------------------------------------
       
   757 // Cleanup. 
       
   758 // ----------------------------------------------------------------------------
       
   759 //
       
   760 void CMPXM3uPlaylistImporter::Cleanup()
       
   761     {
       
   762     delete iBuffer;
       
   763     iBuffer = NULL;
       
   764     
       
   765     delete iLine;
       
   766     iLine = NULL;   
       
   767 
       
   768     delete iItem;
       
   769     iItem = NULL;
       
   770    
       
   771     delete iAutoEncodingPlaylistArray;
       
   772     iAutoEncodingPlaylistArray = NULL;
       
   773     
       
   774     delete iPlaylist;
       
   775     iPlaylist = NULL;
       
   776     }
       
   777 
       
   778 // ----------------------------------------------------------------------------
       
   779 // Handles notifications to the client
       
   780 // ----------------------------------------------------------------------------
       
   781 //
       
   782 void CMPXM3uPlaylistImporter::NotifyClient( TInt aError )
       
   783     {
       
   784     MPX_DEBUG3("CMPXM3uPlaylistImporter::NotifyClient - iAutoEncodingInvalidItems=%d error=%d",
       
   785         iAutoEncodingInvalidItems, aError);
       
   786         
       
   787     if ( iObserver )
       
   788         {
       
   789         if (aError)
       
   790             {
       
   791             // we don't need the playlist media to be passed back to the client
       
   792             // in case of an error, delete it now
       
   793             delete iAutoEncodingPlaylistArray;
       
   794             iAutoEncodingPlaylistArray = NULL;
       
   795 
       
   796             // to-do: change HandlePlaylistL to HandlePlaylist
       
   797             TRAP_IGNORE(iObserver->HandlePlaylistL( NULL, aError, ETrue ));
       
   798             }
       
   799         else
       
   800             {
       
   801             // notify client. return the playlist media
       
   802             CMPXMedia* playlist = iPlaylist;
       
   803             iPlaylist = NULL; // client takes over the ownership
       
   804             
       
   805             // to-do: change HandlePlaylistL to HandlePlaylist
       
   806             TRAP_IGNORE(iObserver->HandlePlaylistL( playlist, aError, ETrue ));
       
   807             }
       
   808         }        
       
   809     }
       
   810         
       
   811 // End of file