示例#1
0
void StPlayList::getSubList(StArrayList<StString>& theList,
                            const size_t           theStart,
                            const size_t           theEnd) const {
    theList.clear();
    StMutexAuto anAutoLock(myMutex);

    size_t anIter = 0;
    StPlayItem* anItem = myFirst;
    for(; anItem != NULL; anItem = anItem->getNext(), ++anIter) {
        if(anIter == theStart) {
            break;
        }
    }

    if(anIter != theStart) {
        return;
    }

    for(; anItem != NULL; anItem = anItem->getNext(), ++anIter) {
        if(anIter == theEnd) {
            break;
        }

        theList.add(anItem->getTitle());
    }
}
示例#2
0
bool StPlayList::walkToPosition(const size_t theId) {
    StMutexAuto anAutoLock(myMutex);

    size_t anIter = 0;
    for(StPlayItem* anItem = myFirst; anItem != NULL; anItem = anItem->getNext(), ++anIter) {
        if(anIter == theId) {
            if(myCurrent == anItem) {
                return false;
            }

            StPlayItem* aPrev = myCurrent;
            if(aPrev != NULL) {
                myStackPrev.push_back(aPrev);
                if(myStackPrev.size() > THE_UNDO_LIMIT) {
                    myStackPrev.pop_front();
                }
            }

            myCurrent = anItem;
            anAutoLock.unlock();
            signals.onPositionChange(theId);
            return true;
        }
    }

    return false;
}
示例#3
0
bool StPlayList::saveM3U(const StCString& thePath) {
    StRawFile aFile;
    if(thePath.isEmpty()
    || !aFile.openFile(StRawFile::WRITE, thePath)) {
        return false;
    }

    StMutexAuto anAutoLock(myMutex);
    aFile.write(stCString("#EXTM3U"));

    for(StPlayItem* anItem = myFirst; anItem != NULL; anItem = anItem->getNext()) {
        const StFileNode* aNode = anItem->getFileNode();
        if(aNode == NULL) {
            continue;
        } else if(aNode->size() < 2) {
            aFile.write(stCString("\n#EXTINF:0,"));
            if(anItem->hasCustomTitle()) {
                aFile.write(anItem->getTitle());
            }
            aFile.write(stCString("\n"));
            aFile.write(aNode->getPath());
        }
    }
    aFile.write(stCString("\n"));
    return true;
}
示例#4
0
void StPlayList::delPlayItem(StPlayItem* theRemItem) {
    if(myFirst == NULL || theRemItem == NULL) {
        // item does not exists in the list
        return;
    }

    // update first/last items
    if(theRemItem == myLast) {
        myLast  = myLast->hasPrev()  ? myLast->getPrev()  : NULL;
    }
    if(myFirst == theRemItem) {
        myFirst = myFirst->hasNext() ? myFirst->getNext() : NULL;
    }

    // reset enumeration
    size_t aPosId = theRemItem->getPosition();
    for(StPlayItem* anIter = theRemItem->getNext(); anIter != NULL; ++aPosId, anIter = anIter->getNext()) {
        anIter->setPosition(aPosId);
    }

    if(theRemItem->hasPrev()) {
        // connect previous and next items
        theRemItem->getPrev()->setNext(theRemItem->getNext());
    } else if(theRemItem->hasNext()) {
        theRemItem->getNext()->setPrev(NULL);
    }

    myStackPrev.clear();
    myStackNext.clear();

    --myItemsCount;
}
示例#5
0
char* StPlayList::parseM3UIter(char*     theIter,
                               StFolder* theFolder,
                               StString& theTitle) {
    if(*theIter == '\0') {
        return NULL;
    }

    char* aNextLine = nextLine(theIter);
    if(aNextLine > theIter + 1) {
        // replace LF or CRLF with '\0'
        *(aNextLine - 1) = '\0';
        char* aTail = aNextLine - 2;
        if(*(aNextLine - 2) == '\x0D') {
            *(aNextLine - 2) = '\0';
            --aTail;
        }

        // skip trailing spaces
        for(; *aTail == ' ' && aTail >= theIter; --aTail) {
            *aTail = '\0';
        }
    }
    if(*theIter == '\0') {
        return aNextLine; // skip empty lines
    }

    if(*theIter != '#') {
        StString    anItemPath = theIter;
        StFolder*   aFolder    = theFolder != NULL
                              && StFileNode::isRelativePath(anItemPath)
                               ? theFolder
                               : &myFoldersRoot;
        StFileNode* aFileNode  = new StFileNode(anItemPath, aFolder);
        aFolder->add(aFileNode);

        StPlayItem* anItem = new StPlayItem(aFileNode, myDefStParams);
        anItem->setTitle(theTitle);
        addPlayItem(anItem);
        theTitle = "";
    } else if(stAreEqual(theIter, "#EXTINF:", 8)) {
        theIter += 8;
        for(; *theIter != '\0'; ++theIter) {
            if(*theIter == ',') {
                for(; *theIter == ' '; ++theIter) {
                    // skip spaces in the beginning
                }

                theTitle = ++theIter;
                break;
            }
        }
    }
    return aNextLine;
}
示例#6
0
bool StPlayList::remove(const StString& thePath,
                        const bool      theToRemovePhysically) {
    StString    aPath    = thePath;
    StPlayItem* aRemItem = NULL;
    StMutexAuto anAutoLock(myMutex);
    if(myCurrent == NULL) {
        // empty playlist
        return false;
    } else if(aPath != myCurrent->getPath()) {
        // search play item
        for(StPlayItem* anItem = myFirst; anItem != NULL; anItem = anItem->getNext()) {
            if(aPath == anItem->getPath()) {
                aRemItem = anItem;
                break;
            }
        }
    } else {
        // walk to another playlist position
        aRemItem = myCurrent;
        const bool aPlayedFlag = aRemItem->getPlayedFlag();
        if(myCurrent->hasNext()) {
            myCurrent = myCurrent->getNext();
        } else if(myCurrent->hasPrev()) {
            myCurrent = myCurrent->getPrev();
        } else {
            myCurrent     = NULL;
            myPlayedCount = 0;
        }

        if(myCurrent != NULL) {
            if(aRemItem->getPlayedFlag() != aPlayedFlag) {
                // the item has not been played yet - mark it as such
                aRemItem->setPlayedFlag(aPlayedFlag);
            } else {
                // one played item has been removed
                --myPlayedCount;
            }
        }
    }

    // remove item itself
    const bool isDeleted = aRemItem != NULL
                        && (!theToRemovePhysically || StFileNode::removeFile(aPath));
    if(isDeleted) {
        delPlayItem(aRemItem);
        delete aRemItem;
    }

    anAutoLock.unlock();
    signals.onPlaylistChange();
    return isDeleted;
}
示例#7
0
void StPlayList::addToNode(const StHandle<StFileNode>& theFileNode,
                           const StString&             thePathToAdd) {
    StString aPath = theFileNode->getPath();
    StMutexAuto anAutoLock(myMutex);
    if(myCurrent == NULL) {
        return;
    } else if(aPath != myCurrent->getPath()) {
        for(StPlayItem* anItem = myFirst; anItem != NULL; anItem = anItem->getNext()) {
            if(aPath == anItem->getPath()) {
                myCurrent = anItem;
                break;
            }
        }
    }

    StFileNode* aFileNode = myCurrent->getFileNode();
    if(aFileNode->getParent() != &myFoldersRoot) {
        // convert filenode to metafile with empty root
        aFileNode->reParent(&myFoldersRoot);
        aFileNode->setSubPath(StString());
        aFileNode->add(new StFileNode(aPath, aFileNode));
    }
    aFileNode->add(new StFileNode(thePathToAdd, aFileNode));
}
示例#8
0
void StPlayList::open(const StCString& thePath,
                      const StCString& theItem) {
    StMutexAuto anAutoLock(myMutex);

    // check if it is recently played playlist
    bool hasTarget = !theItem.isEmpty();
    StString aTarget = hasTarget ? theItem : thePath;
    if(!hasTarget) {
        for(size_t anIter = 0; anIter < myRecent.size(); ++anIter) {
            const StHandle<StRecentItem>& aRecent = myRecent[anIter];
            const StHandle<StFileNode>&   aFile   = aRecent->File;
            if(aFile->size() != 1) {
                continue;
            }

            if(thePath.isEquals(aFile->getPath())) {
                hasTarget = true;
                aTarget = aFile->getValue(0)->getSubPath();
                break;
            }
        }
    }

    clear();
    int aSearchDeep = myRecursionDeep;
    StString aFolderPath;
    StString aFileName;
    if(StFolder::isFolder(thePath)) {
        // add all files from the folder and subfolders
        aFolderPath = thePath;
        aSearchDeep = myRecursionDeep;
        myPlsFile   = addRecentFile(StFileNode(thePath)); // append to recent files list
    } else if(StFileNode::isFileExists(thePath)) {
        // search only current folder
        StFileNode::getFolderAndFile(thePath, aFolderPath, aFileName);
        aSearchDeep = 1;
        bool hasSupportedExt = false;
        StString anExt = StFileNode::getExtension(aFileName);
        for(size_t anExtId = 0; anExtId < myExtensions.size() && !hasSupportedExt; ++anExtId) {
            hasSupportedExt = anExt.isEqualsIgnoreCase(myExtensions[anExtId]);
        }

        // parse m3u playlist
        if(anExt.isEqualsIgnoreCase(stCString("m3u"))) {
            StRawFile aRawFile(thePath);
            if(aRawFile.readFile()) {
                StString aTitle;
                char* anIter = (char* )aRawFile.getBuffer();
                if(anIter[0] == '\xEF' && anIter[1] == '\xBB' && anIter[2] == '\xBF') {
                    // skip BOM for UTF8 written by some stupid programs
                    anIter += 3;
                }
                while(anIter != NULL) {
                    anIter = parseM3UIter(anIter, aTitle);
                }

                myPlsFile = addRecentFile(StFileNode(thePath)); // append to recent files list
                if(hasTarget) {
                    // set current item
                    for(StPlayItem* anItem = myFirst; anItem != NULL; anItem = anItem->getNext()) {
                        if(anItem->getPath() == aTarget) {
                            myCurrent = anItem;
                            break;
                        }
                    }
                }

                anAutoLock.unlock();
                signals.onPlaylistChange();
                return;
            }
        }

        if(!hasSupportedExt) {
            // file with unsupported extension?
            StFileNode* aFileNode = new StFileNode(thePath, &myFoldersRoot);
            myFoldersRoot.add(aFileNode);
            addPlayItem(new StPlayItem(aFileNode, myDefStParams));
        }
    } else {
        // not a filesystem element - probably url or invalid path
        StFileNode* aFileNode = new StFileNode(thePath, &myFoldersRoot);
        myFoldersRoot.add(aFileNode);
        addRecentFile(*aFileNode); // append to recent files list
        addPlayItem(new StPlayItem(aFileNode, myDefStParams));

        anAutoLock.unlock();
        signals.onPlaylistChange();
        return;
    }
    StFolder* aSubFolder = new StFolder(aFolderPath, &myFoldersRoot);
    aSubFolder->init(myExtensions, aSearchDeep);
    myFoldersRoot.add(aSubFolder);

    addToPlayList(aSubFolder);

    myCurrent = myFirst;
    if(hasTarget || !aFileName.isEmpty()) {
        // set current item
        for(StPlayItem* anItem = myFirst; anItem != NULL; anItem = anItem->getNext()) {
            if(anItem->getPath() == aTarget) {
                myCurrent = anItem;
                if(myPlsFile.isNull()) {
                    addRecentFile(*anItem->getFileNode()); // append to recent files list
                }
                break;
            }
        }
    }

    anAutoLock.unlock();
    signals.onPlaylistChange();
}
示例#9
0
bool StPlayList::walkToNext(const bool theToForce) {
    StMutexAuto anAutoLock(myMutex);
    if(myCurrent == NULL
    || (myToLoopSingle && !theToForce)) {
        return false;
    } else if(myIsShuffle && myItemsCount >= 3) {
        StPlayItem* aPrev = myCurrent;
        if(!myStackNext.empty()) {
            myCurrent = myStackNext.front();
            myStackNext.pop_front();
        } else {
            if((myPlayedCount >= (myItemsCount - 1)) || (myPlayedCount == 0)) {
                // reset the playback counter
            #ifdef _WIN32
                FILETIME aTime;
                GetSystemTimeAsFileTime(&aTime);
                myRandGen.setSeed(aTime.dwLowDateTime);
            #else
                timeval aTime;
                gettimeofday(&aTime, NULL);
                myRandGen.setSeed(aTime.tv_usec);
            #endif
                myPlayedCount = 0;
                myCurrent->setPlayedFlag(!myCurrent->getPlayedFlag());
                ST_DEBUG_LOG("Restart the shuffle");
            }

            // determine next random position
            const size_t aCurrPos  = myCurrent->getPosition();
            bool         aCurrFlag = myCurrent->getPlayedFlag();
            StPlayItem*  aNextItem = myCurrent;
            const size_t aNextPos  = stMin(size_t(myRandGen.next() * myItemsCount), myItemsCount - 1);
            if(aNextPos > aCurrPos) {
                // forward direction
                for(size_t aNextDiff = aNextPos - aCurrPos; aNextItem != NULL && aNextDiff != 0; --aNextDiff) {
                    aNextItem = aNextItem->getNext();
                }
            } else {
                // backward direction
                for(size_t aNextDiff = aCurrPos - aNextPos; aNextItem != NULL && aNextDiff != 0; --aNextDiff) {
                    aNextItem = aNextItem->getPrev();
                }
            }
            if(aCurrFlag == aNextItem->getPlayedFlag()) {
                // find nearest position not yet played - prefer item farther from current one
                StPlayItem* aNextItem1 = aNextPos > aCurrPos ? aNextItem->getNext() : aNextItem->getPrev();
                StPlayItem* aNextItem2 = aNextPos > aCurrPos ? aNextItem->getPrev() : aNextItem->getNext();
                for(; aNextItem1 != NULL || aNextItem2 != NULL;) {
                    if(aNextItem1 != NULL) {
                        if(aCurrFlag != aNextItem1->getPlayedFlag()) {
                            aNextItem = aNextItem1;
                            break;
                        }
                        aNextItem1 = aNextPos > aCurrPos ? aNextItem1->getNext() : aNextItem1->getPrev();
                    }
                    if(aNextItem2 != NULL) {
                        if(aCurrFlag != aNextItem2->getPlayedFlag()) {
                            aNextItem = aNextItem2;
                            break;
                        }
                        aNextItem2 = aNextPos > aCurrPos ? aNextItem2->getPrev() : aNextItem2->getNext();
                    }
                }
                if(aCurrFlag == aNextItem->getPlayedFlag()) {
                    // something wrong!
                    ST_DEBUG_LOG("Disaster - next shuffle position not found!");
                    aCurrFlag     = !aCurrFlag;
                    myPlayedCount = 0;
                }
            }

            ST_DEBUG_LOG(aCurrPos + " -> " + aNextItem->getPosition());
            ++myPlayedCount;

            aNextItem->setPlayedFlag(aCurrFlag);
            myCurrent = aNextItem;
        }

        if(aPrev != myCurrent
        && aPrev != NULL) {
            myStackPrev.push_back(aPrev);
            if(myStackPrev.size() > THE_UNDO_LIMIT) {
                myStackPrev.pop_front();
            }
        }

        const size_t anItemId = myCurrent->getPosition();
        anAutoLock.unlock();
        signals.onPositionChange(anItemId);
        return true;
    } else if(myCurrent != myLast) {
        myCurrent = myCurrent->getNext();
        const size_t anItemId = myCurrent->getPosition();
        anAutoLock.unlock();
        signals.onPositionChange(anItemId);
        return true;
    } else if(myIsLoopFlag) {
        return walkToFirst();
    }
    return false;
}