|
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 |