void MP3Broadcaster::UpdatePlaylistFiles(PlaylistPicker *picker,PlaylistPicker *insertPicker) { if ( (NULL == picker) || (NULL == insertPicker) ) return; /* if .stoplist file exists - prepare to stop broadcast */ if(!access(mStopFile, R_OK)) { picker->CleanList(); PopulatePickerFromFile(picker, mStopFile, "", NULL); mTempPicker->CleanList(); remove(mStopFile); picker->mStopFlag = true; } /* if .replacelist file exists - replace current playlist */ if(!access(mReplaceFile, R_OK)) { picker->CleanList(); PopulatePickerFromFile(picker, mReplaceFile, "", NULL); mTempPicker->CleanList(); remove(mReplaceFile); picker->mStopFlag = false; } /* if .insertlist file exists - insert into current playlist */ if(!access(mInsertFile, R_OK)) { insertPicker->CleanList(); mTempPicker->CleanList(); PopulatePickerFromFile(insertPicker, mInsertFile, "", NULL); remove(mInsertFile); picker->mStopFlag = false; } // write upcoming playlist to .upcoming file if (mShowUpcoming) { FILE *upcomingFile = fopen(mUpcomingFile, "w"); if(upcomingFile) { mElementCount = 0; if (!::strcmp(mPlayMode, "weighted_random")) qtss_fprintf(upcomingFile,"#random play - upcoming list not supported\n"); else { qtss_fprintf(upcomingFile,"*PLAY-LIST*\n"); ShowPlaylistElements(insertPicker,upcomingFile); ShowPlaylistElements(picker,upcomingFile); if ( picker->GetNumMovies() == 0 && !picker->mStopFlag && 0 != ::strcmp(mPlayMode, "sequential") ) { picker->CleanList(); PopulatePickerFromFile(picker,mPlayListPath,"",NULL); ShowPlaylistElements(picker,upcomingFile); mTempPicker->CleanList(); PopulatePickerFromFile(mTempPicker,mPlayListPath,"",NULL); } if ( mElementCount <= mUpcomingSongsListSize && 0 == ::strcmp(mPlayMode, "sequential_looped") ) { if (mTempPicker->GetNumMovies() == 0) { mTempPicker->CleanList(); PopulatePickerFromFile(mTempPicker,mPlayListPath,"",NULL); } //sElementCount can be zero if the playlist contains no paths to valid files while ( (mElementCount != 0) && mElementCount <= mUpcomingSongsListSize ) ShowPlaylistElements(mTempPicker,upcomingFile); } } fclose(upcomingFile); } } else { if ( picker->GetNumMovies() == 0 && !picker->mStopFlag && ::strcmp(mPlayMode, "sequential") ) { picker->CleanList(); PopulatePickerFromFile(picker,mPlayListPath,"",NULL); } } }
void MP3Broadcaster::PreFlightOrBroadcast( bool preflight, bool daemonize, bool showMovieList, bool currentMovie, bool checkMP3s, const char* errorlog) { PlaylistPicker* picker = NULL; PlaylistPicker* insertPicker = NULL; MP3FileBroadcaster fileBroadcaster(&mSocket, mBitRate, mFrequency); MP3MetaInfoUpdater* metaInfoUpdater = NULL; SInt32 moviePlayCount; char* thePick = NULL; SInt32 numMovieErrors; bool didAtLeastOneMoviePlay = false; int err; mPreflight = preflight; if ( preflight ) ShowSetupParams(); if (preflight) picker = new PlaylistPicker(false); // make sequential picker, no looping else { picker = MakePickerFromConfig(); // make picker according to parms mTempPicker = new PlaylistPicker(false); insertPicker = new PlaylistPicker(false); insertPicker->mRemoveFlag = true; } // initial call uses empty string for path, NULL for loop detection list (void)PopulatePickerFromFile( picker, mPlayListPath, "", NULL ); if ( preflight ) { if ( picker->mNumToPickFrom == 1 ) qtss_printf( "\nThere is one movie in the Playlist.\n\n" ); else qtss_printf( "\nThere are (%li) movies in the Playlist.\n\n", (SInt32) picker->mNumToPickFrom ); } if ( picker->mNumToPickFrom == 0 ) { qtss_printf( "- MP3Broadcaster setup failed: There are no movies to play.\n" ); mNumErrors++; goto bail; } // check that we have enough movies to cover the recent movies list. if ( preflight ) { if ( !strcmp( mPlayMode, "weighted_random" ) ) // this implies "random" play { if ( mRecentSongsListSize >= picker->mNumToPickFrom ) { mRecentSongsListSize = picker->mNumToPickFrom - 1; qtss_printf("- PlaylistBroadcaster Warning:\n The recent_movies_list_size setting is greater than \n"); qtss_printf(" or equal to the number of movies in the playlist.\n" ); mNumWarnings++; } } } // create the log file mLog = new MP3BroadcasterLog( mWorkingDirPath, mLogFile, mLogging ); // if ( !PreflightTrackerFileAccess( R_OK | W_OK ) ) // goto bail; if (!preflight) { err = ConnectToServer(); if (err) { if (err == EACCES) qtss_printf("- MP3Broadcaster: Disconnected from Server. Bad password or mount point\n Exiting.\n" ); else qtss_printf("- MP3Broadcaster: Couldn't connect to server\n Exiting.\n" ); mNumErrors++; goto bail; } } //Unless the Debug command line option is set, daemonize the process at this point if (daemonize) { #ifndef __Win32__ qtss_printf("- MP3Broadcaster: Started in background.\n"); // keep the same working directory.. #ifdef __sgi__ if (::_daemonize(_DF_NOCHDIR, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) != 0) #else if (::daemon( 1, 0 ) != 0) #endif { qtss_printf("- MP3Broadcaster: System error (%i).\n", errno); mNumErrors++; goto bail; } #endif } if (daemonize && (errorlog != NULL)) { freopen(errorlog, "a", stdout); ::setvbuf(stdout, (char *)NULL, _IONBF, 0); } if (!preflight) { metaInfoUpdater = new MP3MetaInfoUpdater(mPassword, mMountPoint, mSocket.GetRemoteAddr(), mPort); metaInfoUpdater->Start(); fileBroadcaster.SetInfoUpdater(metaInfoUpdater); } // ^ daemon must be called before we Open the log and tracker since we'll // get a new pid, our files close, ( does SIGTERM get sent? ) if (( mLog ) && ( mLogging )) mLog->EnableLog( false ); // don't append ".log" to name for PLB if ( mLogging && !mLog->IsLogEnabled() ) { if ( mLog->LogDirName() && *mLog->LogDirName() ) qtss_printf("- MP3Broadcaster: The log file failed to open.\n ( path: %s/%s )\n Exiting.\n", mLog->LogDirName(), mLog->LogFileName() ); else qtss_printf("- MP3Broadcaster: The log file failed to open.\n ( path: %s )\n Exiting.\n", mLog->LogFileName() ); mNumErrors++; goto bail; } if (mPIDFile[0] != 0) { if(!FileCreateAndCheckAccess(mPIDFile)) { FILE *pidFile = fopen(mPIDFile, "w"); if(pidFile) { qtss_fprintf(pidFile,"%d\n",getpid()); fclose(pidFile); } } } // AddOurPIDToTracker( bcastSetupFilePath ); // <-- exits on failure if ( !preflight ) mLog->LogInfo( "MP3Broadcaster started." ); else mLog->LogInfo( "MP3Broadcaster preflight started." ); if(!preflight) { CreateCurrentAndUpcomingFiles(); SendXAudioCastHeaders(); } if (!preflight) { // check the frequency of the first song fileBroadcaster.PlaySong( picker->GetFirstFile(), NULL, true, true ); } moviePlayCount = 0; numMovieErrors = 0; didAtLeastOneMoviePlay = false; while (true) { if (!showMovieList && !preflight) UpdatePlaylistFiles(picker, insertPicker); if (NULL != insertPicker) thePick = insertPicker->PickOne(); if (NULL == thePick && (NULL != picker)) thePick = picker->PickOne(); if ( (thePick != NULL) && (!preflight && showMovieList) ) { // display the picks in debug mode, but not preflight qtss_printf( "[%li] %s picked\n", moviePlayCount, thePick ); } if ( !showMovieList ) { int playError; if(!preflight) { UpdateCurrentFile(thePick); /* if playlist is about to run out repopulate it */ if ( !::strcmp(mPlayMode, "sequential_looped") ) { if(NULL == thePick &&!picker->mStopFlag) { if (picker->GetNumMovies() == 0) break; else continue; } } } if (thePick == NULL) break; ++moviePlayCount; if(!preflight) { SInt64 startTime = OS::Milliseconds(); SInt64 endTime = 0; playError = fileBroadcaster.PlaySong( thePick, mCurrentFile ); endTime = OS::Milliseconds(); //were we able to actually play the movie? didAtLeastOneMoviePlay = didAtLeastOneMoviePlay || (playError == 0); //ok, we've reached the end of the current playlist if (picker->GetNumMovies() == 0) { //If we determine that every one of the movies resulted in an error, then bail if (!didAtLeastOneMoviePlay) { qtss_printf("Quitting: Playlist contains no valid files.\n"); mLog->LogInfo( "Quitting: Playlist contains no valid files.\n" ); goto bail; } else { didAtLeastOneMoviePlay = false; } } mLog->LogMediaData(thePick, fileBroadcaster.GetTitle(), fileBroadcaster.GetArtist(), fileBroadcaster.GetAlbum(), (UInt32) ((endTime - startTime)/1000L), playError); } else { playError = fileBroadcaster.PlaySong( thePick, NULL, preflight, !checkMP3s ); } if (playError == MP3FileBroadcaster::kConnectionError) { // do something mNumErrors++; goto bail; } if ( !preflight && (playError != 0)) { qtss_printf("File %s : ", thePick); mLog->LogMediaError( thePick, MapErrorToString(playError), NULL ); } else if (playError != 0) { qtss_printf("File %s : ", thePick); MapErrorToString(playError); numMovieErrors++; mNumWarnings++; } } delete [] thePick; thePick = NULL; } //while (true) remove(mCurrentFile); remove(mUpcomingFile); if ( preflight ) { char str[256]; qtss_printf( " - " ); if (numMovieErrors == 1) strcpy(str, "MP3Broadcaster found one problem MP3 file."); else qtss_sprintf( str, "MP3Broadcaster found %li problem MP3 files." , numMovieErrors ); qtss_printf( "%s\n", str ); if (mLog) mLog->LogInfo( str ); if (numMovieErrors == moviePlayCount) { qtss_printf("There are no valid MP3s to play\n"); mNumErrors++; } } bail: delete picker; if (metaInfoUpdater) delete metaInfoUpdater; Cleanup(); #ifndef __Win32__ /* if ( sgTrackingSucceeded ) { // remove ourselves from the tracker // this is the "normal" remove, also signal handlers // may remove us. BCasterTracker tracker( sgTrackerFilePath ); tracker.RemoveByProcessID( getpid() ); tracker.Save(); } */ #endif }
int PopulatePickerFromFile( PlaylistPicker* picker, char* fname, const char* basePath, LoopDetectionList *ldList ) { Assert( picker ); Assert( fname ); FILE* weightings = NULL; LoopDetectionListElement* ldElement = NULL; LoopDetectionNode* ldNode = NULL; int lineCount = 0; int pickErr = kPickerPopulateNoErr; char path[kMaxPickerPath]; #if kPartialPathBeginsWithDelimiter if (PathIsAbsolute(fname)) { if ( *basePath ) fname++; #else if ( !PathIsAbsolute(fname) ) { #endif // it's a partial path, expand it to include all // previously traversed paths ::strncpy( path, basePath, kMaxPickerPath-1 ); ::strncat( path, fname, kMaxPickerPath-1 ); } else { // it's an absolute reference. use the path // part of this for the new basePath ::strncpy( path, fname, kMaxPickerPath-1 ); } // path is now either an absolute or working directory // referenced partial path to the playlist file. int len = strlen(path); char lastChar = path[len-1]; if (lastChar == '\n' || lastChar == '\r' || lastChar == ' ') path[len-1] = '\0'; // ldList is passed as NULL by the initial caller. recursive calls // pass along the ldList we create hre if ( ldList == NULL ) ldList = new LoopDetectionList; Assert( ldList ); if ( !ldList ) pickErr = kPickerPopulateNoMem; if ( !pickErr ) { if ( ldList->ForEachUntil( CompareNameToElement, path ) ) { // we're already in the include chain, this is a loop // print a warning (error?) and continue past the loop. //qtss_printf("- Playlists include loop at file: %s\n", path ); pickErr = kPickerPopulateLoopDetected; } } if ( !pickErr ) { ldElement = new LoopDetectionListElement( path ); Assert( ldElement ); if ( ldElement ) { ldNode = new LoopDetectionNode( ldElement ); Assert( ldNode ); if ( !ldNode ) pickErr = kPickerPopulateNoMem; } else pickErr = kPickerPopulateNoMem; } if (::IsDir(path)) return ::PopulatePickerFromDir(picker, path); if ( !pickErr ) { weightings = ::fopen( path, "r" ); if (!weightings) { qtss_printf("- Playlist picker failed opening list file %s\n", path); pickErr = kPickerPopulateFileError; } } if ( !pickErr ) { long lineBuffSize = (kMaxPickerPath *2) - 1; long wordBuffSize = kMaxPickerPath - 1; char lineBuff[kMaxPickerPath * 2]; char wordBuff[kMaxPickerPath]; char* next; char* pathEnd; char* thisLine; // add ourselves to the list ldList->AddNode( ldNode ); // trim off the file name to get just the path part pathEnd = ::strrchr( path, kPathDelimiterChar ); if ( pathEnd ) { pathEnd++; *pathEnd = 0; } else *path = 0; thisLine = lineBuff; if ( ::fgets( lineBuff, lineBuffSize, weightings ) != NULL ) { lineCount++; thisLine = ::TrimLeft( lineBuff ); if ( 0 != ::strncmp(thisLine,"*PLAY-LIST*",11) ) { //qtss_printf("- Playlist file missing *PLAY-LIST* identifier as first line:\n"); //qtss_printf(" %s%s\n", basePath, fname); pickErr = kPickerPopulateBadFormat; } } if ( !pickErr ) { do { next = lineBuff; if ( ::fgets( lineBuff, lineBuffSize, weightings ) == NULL ) break; // qtss_printf("line = %s\n", lineBuff); lineCount++; next = ::TrimLeft( lineBuff ); if ( *next == '#' ) { // it's a comment - just toss //if ( *next ) // qtss_printf( "comment: %s" , &lineBuff[1] ); } else if (*next == '+') // a list { next = ::TrimLeft( next+1 ); // skip past + include if ( *next == '"' ) // get the name from the next part of the buff next = ::GetQuotedWord( wordBuff, next, wordBuffSize ); else next = ::GetWord( wordBuff, next, wordBuffSize ); // recusively populate from the include file. pickErr = PopulatePickerFromFile( picker, wordBuff, path, ldList ); if ( pickErr ) { DisplayPickerErr( pickErr, "Playlist Include failed", fname, lineCount, lineBuff ); pickErr = kPickerPopulateNoErr; } } else if ( *next ) { char numBuff[32]; char expandedFileName[kMaxPickerPath]; int weight = 10; // default weight is 10 // get the movie file name if ( *next == '"' ) next = ::GetQuotedWord( wordBuff, next, wordBuffSize ); else next = ::GetWord( wordBuff, next, wordBuffSize ); if (*wordBuff) { #if kPartialPathBeginsWithDelimiter if ( PathIsAbsolute(wordBuff) ) { char *wordStart = wordBuff; if ( *path ) wordStart++; // full or partial path to the movie ::strcpy( expandedFileName, path ); ::strcat( expandedFileName, wordStart ); } #else if ( !PathIsAbsolute(wordBuff) ) { // it's a partial path.. // cat the path and fname to form the // full or partial path to the movie ::strcpy( expandedFileName, path ); ::strcat( expandedFileName, wordBuff ); } #endif else { // it's an absolute path.. ::strcpy( expandedFileName, wordBuff ); } // then get the weighting ( if supplied ) next = ::GetWord( numBuff, next, 32 ); if ( *numBuff ) weight = ::atoi(numBuff); // qtss_printf("expanded file name = %s\n", expandedFileName); if (::IsDir(expandedFileName)) pickErr = ::PopulatePickerFromDir(picker, expandedFileName, weight); else if ( !picker->AddToList( expandedFileName, weight ) ) pickErr = kPickerPopulateNoMem; } } } while ( feof( weightings ) == 0 && pickErr == kPickerPopulateNoErr ); } // remove ourselves from the list ldList->RemoveNode( ldNode ); } // only report unreported errors. if ( ldList && ldList->GetNumNodes() == 0 && pickErr ) ::DisplayPickerErr( pickErr, "Playlist error", fname, lineCount, NULL ); if ( ldNode ) delete ldNode; // node deletes element else if ( ldElement ) delete ldElement; if ( weightings ) (void)::fclose( weightings ); if ( ldList && ldList->GetNumNodes() == 0 ) { // all done now! delete ldList; ldList = NULL; } return pickErr; } int PopulatePickerFromDir( PlaylistPicker* picker, char* dirPath, int weight ) { static char expandedFileName[kMaxPickerPath]; // static so we don't build up the stack frame on recursion int pickErr = 0; if (dirPath != NULL) strcpy(expandedFileName, dirPath); #ifdef __Win32__ WIN32_FIND_DATA findData; HANDLE findResultHandle; Bool16 keepSearching = true; int len = strlen(expandedFileName); if (expandedFileName[len - 1] != kPathDelimiterChar) { expandedFileName[len] = kPathDelimiterChar; expandedFileName[len+1] = 0; len++; } strcat(expandedFileName, "*"); findResultHandle = ::FindFirstFile( expandedFileName, &findData); if ( NULL == findResultHandle || INVALID_HANDLE_VALUE == findResultHandle ) { //qtss_printf( "FindFirstFile( \"%s\" ): gle = %lu\n", searchPath, GetLastError() ); return 0; } while ( (pickErr == 0) && keepSearching ) { expandedFileName[len] = 0; // retruncate name if (findData.cFileName[0] != '.') // ignore anything beginning with a "." { strcat(expandedFileName, findData.cFileName); if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) pickErr = PopulatePickerFromDir(picker, NULL, weight); else if ( !picker->AddToList( expandedFileName, weight ) ) pickErr = kPickerPopulateNoMem; } keepSearching = FindNextFile( findResultHandle, &findData ); } #else DIR* dir; struct dirent* entry; int len = strlen(expandedFileName); if (expandedFileName[len - 1] != kPathDelimiterChar) { expandedFileName[len] = kPathDelimiterChar; expandedFileName[len+1] = 0; len++; } dir = opendir(expandedFileName); if (dir == NULL) return kPickerPopulateFileError; do { entry = readdir(dir); if (entry == NULL) break; if (entry->d_name[0] == '.') // ignore anything beginning with a "." continue; if (len + strlen(entry->d_name) < kMaxPickerPath) { strcat(expandedFileName, entry->d_name); #if __solaris__ || __sgi__ || __osf__ || __hpux__ if (::IsDir(expandedFileName)) #else if ((entry->d_type & DT_DIR) != 0) #endif pickErr = PopulatePickerFromDir(picker, NULL, weight); else if ( !picker->AddToList( expandedFileName, weight ) ) pickErr = kPickerPopulateNoMem; } expandedFileName[len] = 0; // retruncate name } while (pickErr == 0); //close the directory back up (void)::closedir(dir); #endif return pickErr; }