rockbox/apps/playlist_catalog.c
William Wilgus 177a15b2ed playlist_catalog remove static playlist_dir in favor of generation at runtime
this needs tested by the heavy playlist users

with the addition of initialize_catalog_buf there shouldn't be any stack overflow concerns
since we are no longer creating another max_path sized buffer when one is already available
this also simplifies the code a bit

rather than carrying around the playlist directory just generate it on the fly
copies the directory to the supplied buffer

add catbroswe_status to keep track of what browse context(s) are currently in use

Change-Id: I145ec501f601c84bb52f2241ed28c6aefab6897b
2022-12-01 22:29:35 -05:00

441 lines
12 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "string-extra.h"
#include "action.h"
#include "dir.h"
#include "file.h"
#include "filetree.h"
#include "kernel.h"
#include "keyboard.h"
#include "lang.h"
#include "list.h"
#include "misc.h"
#include "pathfuncs.h"
#include "onplay.h"
#include "playlist.h"
#include "settings.h"
#include "rbpaths.h"
#include "splash.h"
#include "tree.h"
#include "yesno.h"
#include "filetypes.h"
#include "debug.h"
#include "playlist_catalog.h"
#include "talk.h"
#include "playlist_viewer.h"
#include "bookmark.h"
#include "root_menu.h"
/* Use for recursive directory search */
struct add_track_context {
int fd;
int count;
};
enum catbrowse_status_flags{
CATBROWSE_NOTHING = 0,
CATBROWSE_CATVIEW,
CATBROWSE_PLAYLIST
};
/* keep track of most recently used playlist */
static char most_recent_playlist[MAX_PATH];
/* we need playlist_dir_length for easy removal of playlist dir prefix */
static size_t playlist_dir_length;
/* keep track of what browser(s) are current to prevent reentry */
static int browser_status = CATBROWSE_NOTHING;
static size_t get_directory(char* dirbuf, size_t dirbuf_sz)
{
char *pl_dir = PLAYLIST_CATALOG_DEFAULT_DIR;
/* directory config is of the format: "dir: /path/to/dir" */
if (global_settings.playlist_catalog_dir[0] != '\0')
{
pl_dir = global_settings.playlist_catalog_dir;
}
/* remove duplicate leading '/' */
if (pl_dir[0] == '/' && pl_dir[1] == '/')
{
pl_dir++;
}
return strlcpy(dirbuf, pl_dir, dirbuf_sz);
}
/* Retrieve playlist directory from config file and verify it exists
* attempts to create directory
* catalog dir is returned in dirbuf */
static int initialize_catalog_buf(char* dirbuf, size_t dirbuf_sz)
{
playlist_dir_length = get_directory(dirbuf, dirbuf_sz);
if (playlist_dir_length >= dirbuf_sz)
{
return -2;
}
if (!dir_exists(dirbuf))
{
if (mkdir(dirbuf) < 0) {
splashf(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY), dirbuf);
return -1;
}
else {
memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
}
}
return 0;
}
/* Retrieve playlist directory from config file and verify it exists
* attempts to create directory
* Don't inline as we want the stack to be freed ASAP */
static NO_INLINE int initialize_catalog(void)
{
char playlist_dir[MAX_PATH];
return initialize_catalog_buf(playlist_dir, sizeof(playlist_dir));
}
void catalog_set_directory(const char* directory)
{
if (directory == NULL)
{
global_settings.playlist_catalog_dir[0] = '\0';
}
else
{
strmemccpy(global_settings.playlist_catalog_dir,
directory, sizeof(global_settings.playlist_catalog_dir));
}
initialize_catalog();
}
void catalog_get_directory(char* dirbuf, size_t dirbuf_sz)
{
if (initialize_catalog_buf(dirbuf, dirbuf_sz) < 0)
{
dirbuf[0] = '\0';
return;
}
}
/* Display all playlists in catalog. Selected "playlist" is returned.
* If status is CATBROWSE_CATVIEW then we're not adding anything into playlist */
static int display_playlists(char* playlist, enum catbrowse_status_flags status)
{
static bool reopen_last_playlist = false;
static int most_recent_selection = 0;
struct browse_context browse;
int result = -1;
char selected_playlist[MAX_PATH];
selected_playlist[0] = '\0';
browser_status |= status;
bool view = (status == CATBROWSE_CATVIEW);
browse_context_init(&browse, SHOW_M3U,
BROWSE_SELECTONLY|(view? 0: BROWSE_NO_CONTEXT_MENU),
str(LANG_CATALOG), NOICON,
selected_playlist,
playlist_dir_length + 1 + most_recent_playlist);
browse.buf = selected_playlist;
browse.bufsize = sizeof(selected_playlist);
restart:
/* set / restore the root directory for the browser */
catalog_get_directory(selected_playlist, sizeof(selected_playlist));
browse.flags &= ~BROWSE_SELECTED;
if (view && reopen_last_playlist)
{
switch (playlist_viewer_ex(most_recent_playlist, &most_recent_selection))
{
case PLAYLIST_VIEWER_OK:
{
result = 0;
break;
}
case PLAYLIST_VIEWER_CANCEL:
{
reopen_last_playlist = false;
goto restart;
}
case PLAYLIST_VIEWER_USB:
case PLAYLIST_VIEWER_MAINMENU:
/* Fall through */
default:
break;
}
}
else /* browse playlist dir */
{
int browse_ret = rockbox_browse(&browse);
if (browse_ret == GO_TO_WPS
|| (view && browse_ret == GO_TO_PREVIOUS_MUSIC))
result = 0;
}
if (browse.flags & BROWSE_SELECTED) /* User picked a playlist */
{
if (strcmp(most_recent_playlist, selected_playlist)) /* isn't most recent one */
{
strmemccpy(most_recent_playlist, selected_playlist,
sizeof(most_recent_playlist));
most_recent_selection = 0;
reopen_last_playlist = false;
}
if (view) /* display playlist contents or resume bookmark */
{
int res = bookmark_autoload(selected_playlist);
if (res == BOOKMARK_DO_RESUME)
result = 0;
else
{
switch (playlist_viewer_ex(selected_playlist, &most_recent_selection)) {
case PLAYLIST_VIEWER_OK:
{
reopen_last_playlist = true;
result = 0;
break;
}
case PLAYLIST_VIEWER_CANCEL:
{
goto restart;
}
case PLAYLIST_VIEWER_USB:
case PLAYLIST_VIEWER_MAINMENU:
/* Fall through */
default:
reopen_last_playlist = true;
break;
}
}
}
else /* we're just adding something to a playlist */
{
result = 0;
strmemccpy(playlist, selected_playlist, MAX_PATH);
}
}
browser_status &= ~status;
return result;
}
/* display number of tracks inserted into playlists. Used for directory
insert */
static void display_insert_count(int count)
{
static long talked_tick = 0;
if(global_settings.talk_menu && count &&
(talked_tick == 0 || TIME_AFTER(current_tick, talked_tick+5*HZ)))
{
talked_tick = current_tick;
talk_number(count, false);
talk_id(LANG_PLAYLIST_INSERT_COUNT, true);
}
splashf(0, str(LANG_PLAYLIST_INSERT_COUNT), count, str(LANG_OFF_ABORT));
}
/* Add specified track into playlist. Callback from directory insert */
static int add_track_to_playlist(char* filename, void* context)
{
struct add_track_context* c = (struct add_track_context*) context;
if (fdprintf(c->fd, "%s\n", filename) <= 0)
return -1;
(c->count)++;
if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
display_insert_count(c->count);
return 0;
}
/* Add "sel" file into specified "playlist". How to insert depends on type
of file */
static int add_to_playlist(const char* playlist, bool new_playlist,
const char* sel, int sel_attr)
{
int fd;
int result = -1;
if (new_playlist)
fd = open_utf8(playlist, O_CREAT|O_WRONLY|O_TRUNC);
else
fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
if(fd < 0)
{
splash(HZ*2, ID2P(LANG_FAILED));
return result;
}
/* In case we're in the playlist directory */
reload_directory();
if ((sel_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
{
/* append the selected file */
if (fdprintf(fd, "%s\n", sel) > 0)
result = 0;
}
else if ((sel_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)
{
/* append playlist */
int f, fs, i;
char buf[1024];
if(strcasecmp(playlist, sel) == 0)
goto exit;
f = open_utf8(sel, O_RDONLY);
if (f < 0)
goto exit;
i = lseek(f, 0, SEEK_CUR);
fs = filesize(f);
while (i < fs)
{
int n;
n = read(f, buf, sizeof(buf));
if (n < 0)
break;
if (write(fd, buf, n) < 0)
break;
i += n;
}
if (i >= fs)
result = 0;
close(f);
}
else if (sel_attr & ATTR_DIRECTORY)
{
/* search directory for tracks and append to playlist */
bool recurse = false;
const char *lines[] = {
ID2P(LANG_RECURSE_DIRECTORY_QUESTION), sel};
const struct text_message message={lines, 2};
struct add_track_context context;
if (global_settings.recursive_dir_insert != RECURSE_ASK)
recurse = (bool)global_settings.recursive_dir_insert;
else
{
/* Ask if user wants to recurse directory */
recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
}
context.fd = fd;
context.count = 0;
display_insert_count(0);
result = playlist_directory_tracksearch(sel, recurse,
add_track_to_playlist, &context);
display_insert_count(context.count);
}
exit:
close(fd);
return result;
}
bool catalog_view_playlists(void)
{
if ((browser_status & CATBROWSE_CATVIEW) == CATBROWSE_CATVIEW)
return false;
if (initialize_catalog() < 0)
return false;
return (display_playlists(NULL, CATBROWSE_CATVIEW) >= 0);
}
bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
bool new_playlist, char *m3u8name)
{
char playlist[MAX_PATH + 7]; /* room for /.m3u8\0*/
if ((browser_status & CATBROWSE_PLAYLIST) == CATBROWSE_PLAYLIST)
return false;
if (initialize_catalog_buf(playlist, sizeof(playlist)) < 0)
return false;
if (new_playlist)
{
size_t len;
if (m3u8name == NULL)
{
const char *name;
/* If sel is empty, root, or playlist directory we use 'all' */
if (!sel || !strcmp(sel, "/") || !strcmp(sel, playlist))
{
sel = "/";
name = "/all";
}
else /*If sel is a folder, we prefill the text field with its name*/
name = strrchr(sel, '/');
if (name == NULL || ((sel_attr & ATTR_DIRECTORY) != ATTR_DIRECTORY))
name = "/";
strlcat(playlist, name, sizeof(playlist));
}
else
strmemccpy(playlist, m3u8name, MAX_PATH);
if (kbd_input(playlist, MAX_PATH, NULL))
return false;
len = strlen(playlist);
if(len > 4 && !strcasecmp(&playlist[len-4], ".m3u"))
strlcat(playlist, "8", sizeof(playlist));
else if(len <= 5 || strcasecmp(&playlist[len-5], ".m3u8"))
strlcat(playlist, ".m3u8", sizeof(playlist));
}
else
{
if (display_playlists(playlist, CATBROWSE_PLAYLIST) < 0)
return false;
}
if (add_to_playlist(playlist, new_playlist, sel, sel_attr) == 0)
return true;
else
return false;
}