int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) { struct id3_file *pid3file; struct id3_tag *pid3tag; struct id3_frame *pid3frame; int err; int index; int used; char *utf8_text; int genre=WINAMP_GENRE_UNKNOWN; int have_utf8; int have_text; id3_ucs4_t const *native_text; char *tmp; int got_numeric_genre; int rating; char *conversion_codepage; pid3file=id3_file_open(file,ID3_FILE_MODE_READONLY); if(!pid3file) { DPRINTF(E_WARN,L_SCAN,"Cannot open %s\n",file); return FALSE; } pid3tag=id3_file_tag(pid3file); if(!pid3tag) { err=errno; id3_file_close(pid3file); errno=err; DPRINTF(E_WARN,L_SCAN,"Cannot get ID3 tag for %s\n",file); return FALSE; } DPRINTF(E_SPAM,L_SCAN,"Starting mp3 tag scan\n"); conversion_codepage = conf_alloc_string("scanning","mp3_tag_codepage", "ISO-8859-1"); index=0; while((pid3frame=id3_tag_findframe(pid3tag,"",index))) { used=0; utf8_text=NULL; native_text=NULL; have_utf8=0; have_text=0; DPRINTF(E_SPAM,L_SCAN,"Found tag %s\n",pid3frame->id); if(!strcmp(pid3frame->id,"YTCP")) { /* for id3v2.2 */ pmp3->compilation = 1; DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation); } if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) && (id3_field_getnstrings(&pid3frame->fields[1]))) have_text=1; if(have_text) { native_text=id3_field_getstrings(&pid3frame->fields[1],0); if(native_text) { have_utf8=1; utf8_text = (char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) mem_register(utf8_text,0); if(id3_field_gettextencoding(&pid3frame->fields[1]) == ID3_FIELD_TEXTENCODING_ISO_8859_1) { #ifdef HAVE_ICONV /* this is kinda cheesy, but ucs4* == char* for 8859-1 */ free(utf8_text); utf8_text = (char*)util_xtoutf8_alloc((unsigned char*)native_text, strlen((char*)native_text), conversion_codepage); #endif } if(!strcmp(pid3frame->id,"TIT2")) { /* Title */ used=1; pmp3->title = utf8_text; DPRINTF(E_DBG,L_SCAN," Title: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TPE1")) { used=1; pmp3->artist = utf8_text; DPRINTF(E_DBG,L_SCAN," Artist: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TALB")) { used=1; pmp3->album = utf8_text; DPRINTF(E_DBG,L_SCAN," Album: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TCOM")) { used=1; pmp3->composer = utf8_text; DPRINTF(E_DBG,L_SCAN," Composer: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TIT1")) { used=1; pmp3->grouping = utf8_text; DPRINTF(E_DBG,L_SCAN," Grouping: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TPE2")) { used=1; pmp3->orchestra = utf8_text; DPRINTF(E_DBG,L_SCAN," Orchestra: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TPE3")) { used=1; pmp3->conductor = utf8_text; DPRINTF(E_DBG,L_SCAN," Conductor: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TCON")) { used=1; pmp3->genre = utf8_text; got_numeric_genre=0; DPRINTF(E_DBG,L_SCAN," Genre: %s\n",utf8_text); if(pmp3->genre) { if(!strlen(pmp3->genre)) { genre=WINAMP_GENRE_UNKNOWN; got_numeric_genre=1; } else if (scan_mp3_is_numeric(pmp3->genre)) { genre=atoi(pmp3->genre); got_numeric_genre=1; } else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) { genre=atoi((char*)&pmp3->genre[1]); got_numeric_genre=1; } if(got_numeric_genre) { if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) genre=WINAMP_GENRE_UNKNOWN; free(pmp3->genre); pmp3->genre=strdup(scan_winamp_genre[genre]); } } } else if(!strcmp(pid3frame->id,"COMM")) { used=1; pmp3->comment = utf8_text; DPRINTF(E_DBG,L_SCAN," Comment: %s\n",pmp3->comment); } else if(!strcmp(pid3frame->id,"TPOS")) { tmp=utf8_text; strsep(&tmp,"/"); if(tmp) { pmp3->total_discs=atoi(tmp); } pmp3->disc=atoi(utf8_text); DPRINTF(E_DBG,L_SCAN," Disc %d of %d\n",pmp3->disc,pmp3->total_discs); } else if(!strcmp(pid3frame->id,"TRCK")) { tmp=utf8_text; strsep(&tmp,"/"); if(tmp) { pmp3->total_tracks=atoi(tmp); } pmp3->track=atoi(utf8_text); DPRINTF(E_DBG,L_SCAN," Track %d of %d\n",pmp3->track,pmp3->total_tracks); } else if(!strcmp(pid3frame->id,"TDRC")) { pmp3->year = atoi(utf8_text); DPRINTF(E_DBG,L_SCAN," Year: %d\n",pmp3->year); } else if(!strcmp(pid3frame->id,"TLEN")) { pmp3->song_length = atoi(utf8_text); /* now in ms */ DPRINTF(E_DBG,L_SCAN," Length: %d\n", pmp3->song_length); } else if(!strcmp(pid3frame->id,"TBPM")) { pmp3->bpm = atoi(utf8_text); DPRINTF(E_DBG,L_SCAN,"BPM: %d\n", pmp3->bpm); } else if(!strcmp(pid3frame->id,"TCMP")) { /* for id3v2.3 */ pmp3->compilation = (char)atoi(utf8_text); DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation); } } } /* can check for non-text tags here */ if((!used) && (have_utf8) && (utf8_text)) free(utf8_text); if((!strcmp(pid3frame->id,"POPM")) && (pid3frame->nfields == 3)) { rating = id3_field_getint(&pid3frame->fields[1]); if(rating >= 0x01) pmp3->rating = 20; if(rating >= 0x40) pmp3->rating = 40; if(rating >= 0x80) pmp3->rating = 60; if(rating >= 0xC4) pmp3->rating = 80; if(rating >= 0xFF) pmp3->rating = 100; } /* v2 COMM tags are a bit different than v1 */ if((!strcmp(pid3frame->id,"COMM")) && (pid3frame->nfields == 4)) { /* Make sure it isn't a application-specific comment... * This currently includes the following: * * iTunes_CDDB_IDs * iTunNORM * * If other apps stuff crap into comment fields, then we'll ignore them * here. */ native_text=id3_field_getstring(&pid3frame->fields[2]); if(native_text) { utf8_text=(char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) mem_register(utf8_text,0); if((utf8_text) && (strncasecmp(utf8_text,"iTun",4) != 0)) { /* it's a real comment */ if(utf8_text) free(utf8_text); native_text=id3_field_getfullstring(&pid3frame->fields[3]); if(native_text) { if(pmp3->comment) free(pmp3->comment); utf8_text=(char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) { mem_register(utf8_text,0); pmp3->comment=utf8_text; } } } else { if(utf8_text) free(utf8_text); } } } index++; } free(conversion_codepage); id3_file_close(pid3file); DPRINTF(E_DBG,L_SCAN,"Got id3 tag successfully\n"); return TRUE; }
/** * Kick off the daap server and wait for events. * * This starts the initial db scan, sets up the signal * handling, starts the webserver, then sits back and waits * for events, as notified by the signal handler and the * web interface. These events are communicated via flags * in the config structure. * * \param argc count of command line arguments * \param argv command line argument pointers * \returns 0 on success, -1 otherwise * * \todo split out a ws_init and ws_start, so that the * web space handlers can be registered before the webserver * starts. * */ int main(int argc, char *argv[]) { int option; char *configfile=CONFFILE; WSCONFIG ws_config; int reload=0; int start_time; int end_time; int rescan_counter=0; int old_song_count, song_count; int force_non_root=0; int skip_initial=1; int kill_server=0; int convert_conf=0; char *db_type,*db_parms,*web_root,*runas, *tmp; char **mp3_dir_array; char *servername, *iface; char *ffid = NULL; int appdir = 0; char *perr=NULL; char txtrecord[255]; void *phandle; char *plugindir; int err; char *apppath; int debuglevel=0; int plugins_loaded = 0; #ifdef ALPHA_CUSTOMIZE char *share_path; pthread_t thread1; #endif config.use_mdns=1; err_setlevel(2); config.foreground=0; while((option=getopt(argc,argv,"D:d:c:P:mfrysiuvab:Vk")) != -1) { switch(option) { case 'a': appdir = 1; break; case 'b': ffid=optarg; break; case 'd': debuglevel = atoi(optarg); err_setlevel(debuglevel); break; case 'D': if(err_setdebugmask(optarg)) { usage(argv[0]); exit(EXIT_FAILURE); } break; case 'f': config.foreground=1; err_setdest(err_getdest() | LOGDEST_STDERR); break; case 'c': configfile=optarg; break; case 'm': config.use_mdns=0; break; #ifndef WIN32 case 'P': os_set_pidfile(optarg); break; #endif case 'r': reload=1; break; case 's': skip_initial=0; break; case 'y': force_non_root=1; break; #ifdef WIN32 case 'i': os_register(); exit(EXIT_SUCCESS); break; case 'u': os_unregister(); exit(EXIT_SUCCESS); break; #endif case 'v': convert_conf=1; break; case 'k': kill_server=1; break; case 'V': fprintf(stderr,"Firefly Media Server: Version %s\n",VERSION); exit(EXIT_SUCCESS); break; default: usage(argv[0]); exit(EXIT_FAILURE); break; } } if((getuid()) && (!force_non_root) && (!convert_conf)) { fprintf(stderr,"You are not root. This is almost certainly wrong. " "If you are\nsure you want to do this, use the -y " "command-line switch\n"); exit(EXIT_FAILURE); } if(kill_server) { os_signal_server(S_STOP); exit(0); } io_init(); io_set_errhandler(main_io_errhandler); ws_set_errhandler(main_ws_errhandler); /* read the configfile, if specified, otherwise * try defaults */ config.stats.start_time=start_time=(int)time(NULL); config.stop=0; /* set appdir first, that way config resolves relative to appdir */ if(appdir) { apppath = os_apppath(argv[0]); DPRINTF(E_INF,L_MAIN,"Changing cwd to %s\n",apppath); chdir(apppath); free(apppath); configfile="mt-daapd.conf"; } if(CONF_E_SUCCESS != conf_read(configfile)) { fprintf(stderr,"Error reading config file (%s)\n",configfile); exit(EXIT_FAILURE); } if(debuglevel) /* was specified, should override the config file */ err_setlevel(debuglevel); if(convert_conf) { fprintf(stderr,"Converting config file...\n"); if(CONF_E_SUCCESS != conf_write()) { fprintf(stderr,"Error writing config file.\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } DPRINTF(E_LOG,L_MAIN,"Firefly Version %s: Starting with debuglevel %d\n", VERSION,err_getlevel()); /* load plugins before we drop privs? Maybe... let the * plugins do stuff they might need to */ plugin_init(); if((plugindir=conf_alloc_string("plugins","plugin_dir",NULL)) != NULL) { /* instead of specifying plugins, let's walk through the directory * and load each of them */ if(!load_plugin_dir(plugindir)) { DPRINTF(E_LOG,L_MAIN,"Warning: Could not load plugins\n"); } else { plugins_loaded = TRUE; } free(plugindir); } if(!plugins_loaded) { if((!load_plugin_dir("/usr/lib/firefly/plugins")) && (!load_plugin_dir("/usr/lib/mt-daapd/plugins")) && (!load_plugin_dir("/lib/mt-daapd/plugins")) && (!load_plugin_dir("/lib/mt-daapd/plugins")) && (!load_plugin_dir("/usr/local/lib/mt-daapd/plugins")) && (!load_plugin_dir("/usr/local/lib/mt-daapd/plugins")) && (!load_plugin_dir("/opt/share/firefly/plugins")) && (!load_plugin_dir("/opt/share/mt-daapd/plugins")) && (!load_plugin_dir("/opt/lib/firefly/plugins")) && (!load_plugin_dir("/opt/lib/mt-daapd/plugins")) && (!load_plugin_dir("plugins/.libs"))) { DPRINTF(E_FATAL,L_MAIN,"plugins/plugin_dir not specified\n"); } } phandle=NULL; while((phandle=plugin_enum(phandle))) { DPRINTF(E_LOG,L_MAIN,"Plugin loaded: %s\n",plugin_get_description(phandle)); } runas = conf_alloc_string("general","runas","nobody"); #ifndef WITHOUT_MDNS if(config.use_mdns) { DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n"); if(rend_init(runas)) { DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n", strerror(errno)); } } #endif if(!os_init(config.foreground,runas)) { DPRINTF(E_LOG,L_MAIN,"Could not initialize server\n"); os_deinit(); exit(EXIT_FAILURE); } free(runas); #ifdef UPNP upnp_init(); #endif /* this will require that the db be readable by the runas user */ db_type = conf_alloc_string("general","db_type","sqlite"); db_parms = conf_alloc_string("general","db_parms","/var/cache/mt-daapd"); err=db_open(&perr,db_type,db_parms); if(err) { DPRINTF(E_LOG,L_MAIN|L_DB,"Error opening db: %s\n",perr); #ifndef WITHOUT_MDNS if(config.use_mdns) { rend_stop(); } #endif os_deinit(); exit(EXIT_FAILURE); } free(db_type); free(db_parms); /* Initialize the database before starting */ DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n"); if(db_init(reload)) { DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno)); } err=db_get_song_count(&perr,&song_count); if(err != DB_E_SUCCESS) { DPRINTF(E_FATAL,L_MISC,"Error getting song count: %s\n",perr); } /* do a full reload if the db is empty */ if(!song_count) reload = 1; if(conf_get_array("general","mp3_dir",&mp3_dir_array)) { if((!skip_initial) || (reload)) { DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan\n"); #ifdef ALPHA_CUSTOMIZE share_path = conf_alloc_string("general", "mp3_dir", "AAA"); printf("mp3_dir_array[0] = [%s]\n", mp3_dir_array[0]); if (strlen(share_path) > 0) { Cnt_total_file(mp3_dir_array[0]); pthread_create( &thread1, NULL, (void*)process_bar, NULL); free(share_path); } #endif plugin_event_dispatch(PLUGIN_EVENT_FULLSCAN_START,0,NULL,0); start_time=(int) time(NULL); if(scan_init(mp3_dir_array)) { DPRINTF(E_LOG,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno)); } if(!config.stop) { /* don't send popup when shutting down */ plugin_event_dispatch(PLUGIN_EVENT_FULLSCAN_END,0,NULL,0); err=db_get_song_count(&perr,&song_count); end_time=(int) time(NULL); DPRINTF(E_LOG,L_MAIN|L_SCAN,"Scanned %d songs in %d seconds\n", song_count,end_time - start_time); } } conf_dispose_array(mp3_dir_array); } #ifdef ALPHA_CUSTOMIZE thread_exit = 1; #endif /* start up the web server */ web_root = conf_alloc_string("general","web_root",NULL); ws_config.web_root=web_root; ws_config.port=conf_get_int("general","port",0); DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n", ws_config.web_root, ws_config.port); config.server=ws_init(&ws_config); if(!config.server) { /* pthreads or malloc error */ DPRINTF(E_FATAL,L_MAIN|L_WS,"Error initializing web server\n"); } if(E_WS_SUCCESS != ws_start(config.server)) { /* listen or pthread error */ DPRINTF(E_FATAL,L_MAIN|L_WS,"Error starting web server\n"); } ws_registerhandler(config.server, "/",main_handler,main_auth, 0,1); #ifndef WITHOUT_MDNS if(config.use_mdns) { /* register services */ servername = conf_get_servername(); memset(txtrecord,0,sizeof(txtrecord)); txt_add(txtrecord,"txtvers=1"); txt_add(txtrecord,"Database ID=%0X",util_djb_hash_str(servername)); txt_add(txtrecord,"Machine ID=%0X",util_djb_hash_str(servername)); txt_add(txtrecord,"Machine Name=%s",servername); txt_add(txtrecord,"mtd-version=" VERSION); txt_add(txtrecord,"iTSh Version=131073"); /* iTunes 6.0.4 */ txt_add(txtrecord,"Version=196610"); /* iTunes 6.0.4 */ tmp = conf_alloc_string("general","password",NULL); if(tmp && (strlen(tmp)==0)) tmp=NULL; txt_add(txtrecord,"Password=%s",tmp ? "true" : "false"); if(tmp) free(tmp); srand((unsigned int)time(NULL)); if(ffid) { txt_add(txtrecord,"ffid=%s",ffid); } else { txt_add(txtrecord,"ffid=%08x",rand()); } DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n"); iface = conf_alloc_string("general","interface",""); rend_register(servername,"_http._tcp",ws_config.port,iface,txtrecord); plugin_rend_register(servername,ws_config.port,iface,txtrecord); free(servername); free(iface); } #endif end_time=(int) time(NULL); err=db_get_song_count(&perr,&song_count); if(err != DB_E_SUCCESS) { DPRINTF(E_FATAL,L_MISC,"Error getting song count: %s\n",perr); } DPRINTF(E_LOG,L_MAIN,"Serving %d songs. Startup complete in %d seconds\n", song_count,end_time-start_time); if(conf_get_int("general","rescan_interval",0) && (!reload) && (!conf_get_int("scanning","skip_first",0))) config.reload = 1; /* force a reload on start */ while(!config.stop) { if((conf_get_int("general","rescan_interval",0) && (rescan_counter > conf_get_int("general","rescan_interval",0)))) { if((conf_get_int("general","always_scan",0)) || (config_get_session_count())) { config.reload=1; } else { DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n"); } rescan_counter=0; } if(config.reload) { old_song_count = song_count; start_time=(int) time(NULL); DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n"); if(conf_get_array("general","mp3_dir",&mp3_dir_array)) { if(config.full_reload) { config.full_reload=0; db_force_rescan(NULL); } if(scan_init(mp3_dir_array)) { DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... bad path?\n"); } conf_dispose_array(mp3_dir_array); } config.reload=0; db_get_song_count(NULL,&song_count); DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in " "%d seconds\n",song_count,old_song_count, time(NULL)-start_time); } os_wait(MAIN_SLEEP_INTERVAL); rescan_counter += MAIN_SLEEP_INTERVAL; } DPRINTF(E_LOG,L_MAIN,"Stopping gracefully\n"); #ifndef WITHOUT_MDNS if(config.use_mdns) { DPRINTF(E_LOG,L_MAIN|L_REND,"Stopping rendezvous daemon\n"); rend_stop(); } #endif #ifdef UPNP upnp_deinit(); #endif /* Got to find a cleaner way to stop the web server. * Closing the fd of the socking accepting doesn't necessarily * cause the accept to fail on some libcs. * DPRINTF(E_LOG,L_MAIN|L_WS,"Stopping web server\n"); ws_stop(config.server); */ free(web_root); conf_close(); DPRINTF(E_LOG,L_MAIN|L_DB,"Closing database\n"); db_deinit(); DPRINTF(E_LOG,L_MAIN,"Done!\n"); os_deinit(); io_deinit(); mem_dump(); return EXIT_SUCCESS; }
int plugin_ssc_should_transcode(WS_CONNINFO *pwsc, char *codec) { int result; char *native_codecs=NULL; char *user_agent=NULL; char *never_transcode = NULL; char *always_transcode = NULL; ASSERT((pwsc) && (codec)); if(!pwsc) return FALSE; if(!codec) { DPRINTF(E_LOG,L_PLUG,"testing transcode on null codec?\n"); return FALSE; } never_transcode = conf_alloc_string("general","never_transcode",NULL); if(never_transcode) { if(strstr(never_transcode,codec)) { free(never_transcode); return FALSE; } free(never_transcode); } if(pwsc) { /* see if the headers give us any guidance */ native_codecs = ws_getrequestheader(pwsc,"accept-codecs"); if(!native_codecs) { user_agent = ws_getrequestheader(pwsc,"user-agent"); if(user_agent) { if(strncmp(user_agent,"iTunes",6)==0) { native_codecs = "mpeg,mp4a,wav,mp4v,alac"; } else if(strncmp(user_agent,"Roku",4)==0) { native_codecs = "mpeg,mp4a,wav,wma"; } else if(strncmp(user_agent,"Hifidelio",9)==0) { return FALSE; } } } } if(!native_codecs) { native_codecs = "mpeg,wav"; } /* can't transcode it if we can't transcode it */ if(!_plugin_ssc_codecs) return FALSE; always_transcode = conf_alloc_string("general","always_transcode",NULL); if(always_transcode) { if(strstr(always_transcode,codec)) { free(always_transcode); return TRUE; } free(always_transcode); } if(strstr(native_codecs,codec)) return FALSE; result = FALSE; if(strstr(_plugin_ssc_codecs,codec)) { result = TRUE; } return result; }
int Cnt_total_file(char *path) { DIR *dir = NULL; struct stat buf; char fullfilename[512], *string, *ptr; int result; int len = 0; struct dirent *dptr = NULL; char *extensions; //DEBUG("Count path = [%s]\n",path); #ifdef ALPHA_CUSTOMIZE //skip USB disk if (strncmp(path, PRE_USBDISK_PATH, strlen(PRE_USBDISK_PATH)) == 0) return 0; #endif if ((dir = opendir(path)) == NULL) { return -1; } if( (dptr = readdir(dir)) == NULL) { //DEBUG("path/dptr->d_name = [%s%s]\n", path, dptr->d_name); closedir(dir); return -1; } extensions = conf_alloc_string("general","extensions",".mp3"); while(((dptr = readdir(dir)) != NULL)) { memset(fullfilename,0x00,sizeof(fullfilename)); sprintf(fullfilename,"%s/%s",path,dptr->d_name); if( (result =lstat(fullfilename, &buf)) != 0) { //DEBUG("lstat error:[%s],%d,file:[%s]\n",strerror(result),result,fullfilename); continue; } if((strlen(path) + strlen(dptr->d_name)) >= 512) { //DEBUG("file path too long %d\n", (strlen(path) + strlen(dptr->d_name))); continue; } if(dptr->d_name[0] == '.') { continue; } if(S_ISLNK(buf.st_mode)) { //DEBUG("Link File: [%s]\n",fullfilename); } else if(S_ISDIR(buf.st_mode)) { result = Cnt_total_file(fullfilename); } else if(S_ISREG(buf.st_mode) ) { string = strrchr(dptr->d_name,'.'); if(string != NULL) { ptr = string + 1; len = strlen(ptr); } if(string == NULL || len > 5) { continue; } else { if(strcasestr(extensions, string)) total_file_cnt++; } } // End of S_ISREG } free(extensions); closedir(dir); return 0; }