Index: apps/playlist.c =================================================================== --- apps/playlist.c (revision 14452) +++ apps/playlist.c (working copy) @@ -250,6 +250,26 @@ } /* + * check if there is extm3u header + */ +static int check_for_extm3u(struct playlist_info* playlist) +{ + char header[7]; + int fd; + + fd = open(playlist->filename, O_RDONLY); + if(fd < 0) + return -1; /* failed */ + + if (read(fd,header,sizeof(header)) < 0) + return -1; + + playlist->is_extm3u = (strncmp(header,"#EXTM3U",7) == 0); + + return 0; +} + +/* * Initialize a new playlist for viewing/editing/playing. dir is the * directory where the playlist is located and file is the filename. */ @@ -269,6 +289,9 @@ } update_playlist_filename(playlist, dir, file); + + if (file) + check_for_extm3u(playlist); if (playlist->control_fd >= 0) { @@ -1365,6 +1388,69 @@ return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf)); } +/* fill in track title and song length to according extm3u comments +* should not call if playlist->is_extm3u is false +* a typical extm3u line: header time title +* #EXTINF:111,example title */ +static int get_extm3u_info(struct playlist_info* playlist, int seek, + char *title_buf, int title_buf_size, + int *length) +{ + /* header + time can take up 13 characters for a 3 hour song */ + int tmp_buf_size = MIN(seek - 1,title_buf_size +13); + int offset = MAX(0,seek - tmp_buf_size - 2); + char tmp_buf[MAX_PATH*2 + 13]; + + if(-1 == playlist->fd) + playlist->fd = open(playlist->filename, O_RDONLY); + if(playlist->fd < 0) + return -1; /* failed */ + + /* read the lines before seek to find extm3u comments */ + if (lseek(playlist->fd, offset, SEEK_SET) != offset) + return -1; + else + { + if (read(playlist->fd, tmp_buf, tmp_buf_size) < 0) + return -1; + + /* Can skip 8 chars because a valid extm3u line + * should have more than 8 chars*/ + int i = tmp_buf_size - 8; + int j = tmp_buf_size - 1; + + /* locate the start of extm3u comment line*/ + while (i > 0 && (tmp_buf[i] != '\n') && (tmp_buf[i] != '\r')) + i--; + + if (strncmp(&tmp_buf[++i],"#EXTINF:",8) != 0) + return -1; /*line too long or no comment*/ + + i += 8; /*skip the header*/ + *length = atoi(&tmp_buf[i]); + + if (*length == 0) + return -1; /*cannot retrieve song length*/ + + /*skip the time part*/ + while (i < tmp_buf_size && tmp_buf[i] != ',') + i++; + + if (tmp_buf[i] != ',' || i == j) + return -1; /*no title or not proper format*/ + + /*kill white spaces and new lines*/ + while(j > i && (tmp_buf[j] == ' ' || tmp_buf[j] == '\t' || + tmp_buf[j] == '\n' || tmp_buf[j] == '\r')) + j--; + + tmp_buf[j+1] = '\0'; + + strncpy(title_buf,&tmp_buf[i+1],title_buf_size); + } + return 0; +} + static int get_next_directory(char *dir){ return get_next_dir(dir,true,false); } @@ -3334,7 +3420,17 @@ if (get_filename(playlist, index, seek, control_file, info->filename, sizeof(info->filename)) < 0) return -1; - + + /* check info->has_extm3u so that if caller to this function + * explicitly asked for no extm3u info, extm3u info is not parsed */ + if (info->has_extm3u && playlist->is_extm3u) + { + info->has_extm3u = !(get_extm3u_info(playlist, seek, info->title, + sizeof(info->title), &(info->length)) < 0); + } + else + info->has_extm3u = false; + info->attr = 0; if (control_file) Index: apps/playlist.h =================================================================== --- apps/playlist.h (revision 14452) +++ apps/playlist.h (working copy) @@ -71,6 +71,7 @@ char filename[MAX_PATH]; /* path name of m3u playlist on disk */ char control_filename[MAX_PATH]; /* full path of control file */ bool utf8; /* playlist is in .m3u8 format */ + bool is_extm3u; /*playlist contains extm3u infomation */ int fd; /* descriptor of the open playlist file */ int control_fd; /* descriptor of the open control file */ bool control_created; /* has control file been created? */ @@ -109,6 +110,11 @@ int attr; /* playlist attributes for track */ int index; /* index of track in playlist */ int display_index; /* index of track for display */ + bool has_extm3u; /* Does it have extm3u information? */ + + /* used if extm3u information is present in playlist */ + unsigned int length; /* song length in seconds */ + char title[MAX_PATH*2]; /* track title */ }; /* Exported functions only for current playlist. */ Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 14452) +++ apps/lang/english.lang (working copy) @@ -11122,3 +11122,31 @@ rtc: "oh" + + id: LANG_DISPLAY_TRACK_TITLE_ONLY + desc: track display options + user: + + *: "EXTM3U title only" + + + *: "EXTM3U title only" + + + *: "title only" + + + + id: LANG_DISPLAY_TRACK_TITLE_TIME + desc: track display options + user: + + *: "EXTM3U title and time" + + + *: "EXTM3U title and time" + + + *: "title and time" + + \ No newline at end of file Index: apps/playlist_viewer.c =================================================================== --- apps/playlist_viewer.c (revision 14452) +++ apps/playlist_viewer.c (working copy) @@ -65,6 +65,11 @@ int display_index; /* Display index */ bool queued; /* Is track queued? */ bool skipped; /* Is track marked as bad? */ + bool has_extm3u; /* Does it have extm3u information? */ + + /* used if extm3u information is present in playlist */ + unsigned int length; /* song length in seconds */ + char* title; /* track title */ }; enum direction @@ -118,7 +123,8 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer, char* filename, bool reload); -static void format_name(char* dest, const char* src); +static void format_name(char* dest, const char* name, + int len, unsigned int track_length); static void format_line(const struct playlist_entry* track, char* str, int len); @@ -199,25 +205,51 @@ { struct playlist_track_info info; int len; - + + /* Explicitly disable loading extm3u if not needed for display*/ + switch (global_settings.playlist_viewer_track_display) + { + case 2: + case 3: + info.has_extm3u = true; + break; + default: + info.has_extm3u = false; + break; + } + /* Playlist viewer orders songs based on display index. We need to convert to real playlist index to access track */ index = (index + playlist_get_first_index(viewer.playlist)) % viewer.num_tracks; if (playlist_get_track_info(viewer.playlist, index, &info) < 0) return -1; + + if (info.has_extm3u) + len = strlen(info.title) + 1; + else + len = strlen(info.filename) + 1; - len = strlen(info.filename) + 1; - if (len <= remaining_size) { - strcpy(name_buffer, info.filename); - + if (info.has_extm3u) + { + entry->has_extm3u = true; + entry->length = info.length; + strcpy(name_buffer, info.title); + } + else + { + entry->has_extm3u = false; + strcpy(name_buffer, info.filename); + } + entry->name = name_buffer; entry->index = info.index; entry->display_index = info.display_index; entry->queued = info.attr & PLAYLIST_ATTR_QUEUED; entry->skipped = info.attr & PLAYLIST_ATTR_SKIPPED; + return len; } return -1; @@ -352,18 +384,42 @@ return true; } -/* Format trackname for display purposes */ -static void format_name(char* dest, const char* src) +/* Format name for display purposes */ +static void format_name(char* dest, const char* name, + int len, unsigned int track_length) { switch (global_settings.playlist_viewer_track_display) { + case 3: + { + /* track title and time; fall through if no extm3u info + * (i.e. track_length is not 0) */ + if (track_length) + { + snprintf(dest,len,"%s (%02d:%02d)",name, + track_length / 60, track_length %60); + break; + } + } + case 2: + { + /* track title only; fall through if no extm3u info*/ + if (track_length) + { + strcpy(dest, name); + break; + } + } case 0: default: { /* Only display the filename */ - char* p = strrchr(src, '/'); + char* p = strrchr(name, '/'); - strcpy(dest, p+1); + if (p != NULL) + strcpy(dest, p+1); + else + strcpy(dest, name); /* Remove the extension */ char* q = strrchr(dest, '.'); @@ -374,9 +430,11 @@ break; } case 1: - /* Full path */ - strcpy(dest, src); + { + /* Full path or extm3u title only; no formatting needed*/ + strcpy(dest, name); break; + } } } @@ -384,11 +442,11 @@ static void format_line(const struct playlist_entry* track, char* str, int len) { - char name[MAX_PATH]; + char name[MAX_PATH*2]; char *skipped = ""; + format_name(name, track->name, len, (track->has_extm3u) ? + track->length : 0); - format_name(name, track->name); - if (track->skipped) skipped = "(ERR) "; @@ -523,7 +581,8 @@ struct playlist_viewer * local_viewer = (struct playlist_viewer *)data; struct playlist_entry *track= playlist_buffer_get_track(&(local_viewer->buffer), selected_item); - format_line(track, buffer, MAX_PATH); + + format_line(track, buffer, MAX_PATH*2); return(buffer); } @@ -684,11 +743,25 @@ break; } case ACTION_STD_MENU: + { + int old_view_setting = global_settings.playlist_viewer_track_display; + if (viewer_menu()) { ret = true; goto exit; } + + if (old_view_setting != global_settings.playlist_viewer_track_display) + { + /* playlist needs update when view mode changes because probably + * not all needed information is present */ + + update_playlist(true); + if (viewer.num_tracks <= 0) + exit = true; + } + gui_synclist_set_icon_callback( &playlist_lists, global_settings.playlist_viewer_icons? @@ -696,7 +769,7 @@ ); gui_synclist_draw(&playlist_lists); break; - + } case ACTION_NONE: gui_syncstatusbar_draw(&statusbars, false); break; @@ -721,8 +794,10 @@ { int *found_indicies = (int*)data; static struct playlist_track_info track; + /* Explicitly ask for no extm3u info */ + track.has_extm3u = false; playlist_get_track_info(viewer.playlist,found_indicies[selected_item],&track); - format_name(buffer,track.filename); + format_name(buffer,track.filename,MAX_PATH*2,0); return(buffer); } Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 14452) +++ apps/settings_list.c (working copy) @@ -663,8 +663,11 @@ OFFON_SETTING(0,playlist_viewer_indices,LANG_SHOW_INDICES,true, "playlist viewer indices",NULL), CHOICE_SETTING(0, playlist_viewer_track_display, LANG_TRACK_DISPLAY, 0, - "playlist viewer track display","track name,full path", NULL, 2, - ID2P(LANG_DISPLAY_TRACK_NAME_ONLY), ID2P(LANG_DISPLAY_FULL_PATH)), + "playlist viewer track display", + "track name,full path,title,title with time", NULL, 4, + ID2P(LANG_DISPLAY_TRACK_NAME_ONLY), ID2P(LANG_DISPLAY_FULL_PATH), + ID2P(LANG_DISPLAY_TRACK_TITLE_ONLY), + ID2P(LANG_DISPLAY_TRACK_TITLE_TIME)), CHOICE_SETTING(0, recursive_dir_insert, LANG_RECURSE_DIRECTORY , RECURSE_OFF, "recursive directory insert", off_on_ask, NULL , 3 , ID2P(LANG_OFF), ID2P(LANG_ON), ID2P(LANG_ASK)),