summaryrefslogtreecommitdiff
path: root/apps/tagtree.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/tagtree.c')
-rw-r--r--apps/tagtree.c417
1 files changed, 333 insertions, 84 deletions
diff --git a/apps/tagtree.c b/apps/tagtree.c
index ea2a756a46..60f8a795e3 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -56,6 +56,8 @@
56#include "playback.h" 56#include "playback.h"
57#include "strnatcmp.h" 57#include "strnatcmp.h"
58#include "panic.h" 58#include "panic.h"
59#include "onplay.h"
60#include "plugin.h"
59 61
60#define str_or_empty(x) (x ? x : "(NULL)") 62#define str_or_empty(x) (x ? x : "(NULL)")
61 63
@@ -64,6 +66,10 @@
64 66
65static int tagtree_play_folder(struct tree_context* c); 67static int tagtree_play_folder(struct tree_context* c);
66 68
69/* reuse of tagtree data after tagtree_play_folder() */
70static uint32_t loaded_entries_crc = 0;
71
72
67/* this needs to be same size as struct entry (tree.h) and name needs to be 73/* this needs to be same size as struct entry (tree.h) and name needs to be
68 * the first; so that they're compatible enough to walk arrays of both 74 * the first; so that they're compatible enough to walk arrays of both
69 * derefencing the name member*/ 75 * derefencing the name member*/
@@ -71,6 +77,7 @@ struct tagentry {
71 char* name; 77 char* name;
72 int newtable; 78 int newtable;
73 int extraseek; 79 int extraseek;
80 int customaction;
74}; 81};
75 82
76static struct tagentry* tagtree_get_entry(struct tree_context *c, int id); 83static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
@@ -78,10 +85,10 @@ static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
78#define SEARCHSTR_SIZE 256 85#define SEARCHSTR_SIZE 256
79 86
80enum table { 87enum table {
81 ROOT = 1, 88 TABLE_ROOT = 1,
82 NAVIBROWSE, 89 TABLE_NAVIBROWSE,
83 ALLSUBENTRIES, 90 TABLE_ALLSUBENTRIES,
84 PLAYTRACK, 91 TABLE_PLAYTRACK,
85}; 92};
86 93
87static const struct id3_to_search_mapping { 94static const struct id3_to_search_mapping {
@@ -105,9 +112,11 @@ enum variables {
105 var_include, 112 var_include,
106 var_rootmenu, 113 var_rootmenu,
107 var_format, 114 var_format,
115 menu_byfirstletter,
108 menu_next, 116 menu_next,
109 menu_load, 117 menu_load,
110 menu_reload, 118 menu_reload,
119 menu_shuffle_songs,
111}; 120};
112 121
113/* Capacity 10 000 entries (for example 10k different artists) */ 122/* Capacity 10 000 entries (for example 10k different artists) */
@@ -165,11 +174,12 @@ struct display_format {
165static struct display_format *formats[TAGMENU_MAX_FMTS]; 174static struct display_format *formats[TAGMENU_MAX_FMTS];
166static int format_count; 175static int format_count;
167 176
177#define MENUENTRY_MAX_NAME 64
168struct menu_entry { 178struct menu_entry {
169 char name[64]; 179 char name[MENUENTRY_MAX_NAME];
170 int type; 180 int type;
171 struct search_instruction { 181 struct search_instruction {
172 char name[64]; 182 char name[MENUENTRY_MAX_NAME];
173 int tagorder[MAX_TAGS]; 183 int tagorder[MAX_TAGS];
174 int tagorder_count; 184 int tagorder_count;
175 struct tagcache_search_clause *clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES]; 185 struct tagcache_search_clause *clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
@@ -181,19 +191,12 @@ struct menu_entry {
181}; 191};
182 192
183struct menu_root { 193struct menu_root {
184 char title[64]; 194 char title[MENUENTRY_MAX_NAME];
185 char id[MAX_MENU_ID_SIZE]; 195 char id[MAX_MENU_ID_SIZE];
186 int itemcount; 196 int itemcount;
187 struct menu_entry *items[TAGMENU_MAX_ITEMS]; 197 struct menu_entry *items[TAGMENU_MAX_ITEMS];
188}; 198};
189 199
190struct match
191{
192 const char* str;
193 uint16_t len;
194 uint16_t symbol;
195};
196
197/* Statusbar text of the current view. */ 200/* Statusbar text of the current view. */
198static char current_title[MAX_TAGS][128]; 201static char current_title[MAX_TAGS][128];
199 202
@@ -276,6 +279,20 @@ static struct buflib_callbacks ops = {
276 .shrink_callback = NULL, 279 .shrink_callback = NULL,
277}; 280};
278 281
282static uint32_t tagtree_data_crc(struct tree_context* c)
283{
284 char* buf;
285 uint32_t crc;
286 buf = core_get_data(tagtree_handle); /* data for the search clauses etc */
287 crc = crc_32(buf, tagtree_buf_used, c->dirlength);
288 buf = core_get_data(c->cache.name_buffer_handle); /* names */
289 crc = crc_32(buf, c->cache.name_buffer_size, crc);
290 buf = core_get_data(c->cache.entries_handle); /* tagentries */
291 crc = crc_32(buf, c->cache.max_entries * sizeof(struct tagentry), crc);
292 logf("%s 0x%x", __func__, crc);
293 return crc;
294}
295
279static void* tagtree_alloc(size_t size) 296static void* tagtree_alloc(size_t size)
280{ 297{
281 size = ALIGN_UP(size, sizeof(void*)); 298 size = ALIGN_UP(size, sizeof(void*));
@@ -331,13 +348,15 @@ static int get_token_str(char *buf, int size)
331static int get_tag(int *tag) 348static int get_tag(int *tag)
332{ 349{
333 #define TAG_MATCH(str, tag) {str, sizeof(str) - 1, tag} 350 #define TAG_MATCH(str, tag) {str, sizeof(str) - 1, tag}
351 struct match {const char* str; uint16_t len; uint16_t symbol;};
334 static const struct match get_tag_match[] = 352 static const struct match get_tag_match[] =
335 { 353 {
336 TAG_MATCH("Lm", tag_virt_length_min), 354 TAG_MATCH("lm", tag_virt_length_min),
337 TAG_MATCH("Ls", tag_virt_length_sec), 355 TAG_MATCH("ls", tag_virt_length_sec),
338 TAG_MATCH("Pm", tag_virt_playtime_min), 356 TAG_MATCH("pm", tag_virt_playtime_min),
339 TAG_MATCH("Ps", tag_virt_playtime_sec), 357 TAG_MATCH("ps", tag_virt_playtime_sec),
340 TAG_MATCH("->", menu_next), 358 TAG_MATCH("->", menu_next),
359 TAG_MATCH("~>", menu_shuffle_songs),
341 360
342 TAG_MATCH("==>", menu_load), 361 TAG_MATCH("==>", menu_load),
343 362
@@ -381,13 +400,15 @@ static int get_tag(int *tag)
381 TAG_MATCH("lastelapsed", tag_lastelapsed), 400 TAG_MATCH("lastelapsed", tag_lastelapsed),
382 TAG_MATCH("%menu_start", var_menu_start), 401 TAG_MATCH("%menu_start", var_menu_start),
383 402
403 TAG_MATCH("%byfirstletter", menu_byfirstletter),
384 TAG_MATCH("canonicalartist", tag_virt_canonicalartist), 404 TAG_MATCH("canonicalartist", tag_virt_canonicalartist),
405
385 TAG_MATCH("", 0) /* sentinel */ 406 TAG_MATCH("", 0) /* sentinel */
386 }; 407 };
387 #undef TAG_MATCH 408 #undef TAG_MATCH
388 const size_t max_cmd_sz = 32; /* needs to be >= to len of longest tagstr */ 409 const size_t max_cmd_sz = 32; /* needs to be >= to len of longest tagstr */
389 const char *tagstr; 410 const char *tagstr;
390 unsigned int tagstr_len; 411 uint16_t tagstr_len;
391 const struct match *match; 412 const struct match *match;
392 413
393 /* Find the start. */ 414 /* Find the start. */
@@ -820,7 +841,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
820 return true; 841 return true;
821 } 842 }
822 843
823 if (entry->type != menu_next) 844 if (entry->type != menu_next && entry->type != menu_shuffle_songs)
824 return false; 845 return false;
825 846
826 while (inst->tagorder_count < MAX_TAGS) 847 while (inst->tagorder_count < MAX_TAGS)
@@ -847,7 +868,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
847 868
848 inst->tagorder_count++; 869 inst->tagorder_count++;
849 870
850 if (get_tag(&type) <= 0 || type != menu_next) 871 if (get_tag(&type) <= 0 || (type != menu_next && type != menu_shuffle_songs))
851 break; 872 break;
852 } 873 }
853 874
@@ -1033,8 +1054,65 @@ int tagtree_import(void)
1033 return 0; 1054 return 0;
1034} 1055}
1035 1056
1036static bool parse_menu(const char *filename); 1057static bool alloc_menu_parse_buf(char *buf, int type)
1058{
1059 /* allocate a new menu item (if needed) initialize it with data parsed
1060 from buf Note: allows setting menu type, type ignored when < 0
1061 */
1062 /* Allocate */
1063 if (menu->items[menu->itemcount] == NULL)
1064 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
1065 if (!menu->items[menu->itemcount])
1066 {
1067 logf("tagtree failed to allocate %s", "menu items");
1068 return false;
1069 }
1037 1070
1071 /* Initialize */
1072 core_pin(tagtree_handle);
1073 if (parse_search(menu->items[menu->itemcount], buf))
1074 {
1075 if (type >= 0)
1076 menu->items[menu->itemcount]->type = type;
1077 menu->itemcount++;
1078 }
1079 core_unpin(tagtree_handle);
1080 return true;
1081}
1082
1083static void build_firstletter_menu(char *buf, size_t bufsz)
1084{
1085 const char *subitem = buf;
1086 size_t l = strlen(buf) + 1;
1087 buf+=l;
1088 bufsz-=l;
1089
1090 const char * const fmt ="\"%s\"-> %s ? %s %c\"%c\"-> %s =\"fmt_title\"";
1091 const char * const showsub = /* album subitem for canonicalartist */
1092 ((strcasestr(subitem, "artist") == NULL) ? "title" : "album -> title");
1093
1094 /* Numeric ex: "Numeric" -> album ? album < "A" -> title = "fmt_title" */
1095 snprintf(buf, bufsz, fmt,
1096 str(LANG_DISPLAY_NUMERIC), subitem, subitem,'<', 'A', showsub);
1097
1098 if (!alloc_menu_parse_buf(buf, menu_byfirstletter))
1099 {
1100 return;
1101 }
1102
1103 for (int i = 0; i < 26; i++)
1104 {
1105 snprintf(buf, bufsz, fmt, "#", subitem, subitem,'^', 'A' + i, showsub);
1106 buf[1] = 'A' + i; /* overwrite the placeholder # with the current letter */
1107 /* ex: "A" -> title ? title ^ "A" -> title = "fmt_title" */
1108 if (!alloc_menu_parse_buf(buf, menu_byfirstletter))
1109 {
1110 return;
1111 }
1112 }
1113}
1114
1115static bool parse_menu(const char *filename);
1038static int parse_line(int n, char *buf, void *parameters) 1116static int parse_line(int n, char *buf, void *parameters)
1039{ 1117{
1040 char data[256]; 1118 char data[256];
@@ -1104,7 +1182,7 @@ static int parse_line(int n, char *buf, void *parameters)
1104 logf("Load menu fail: %s", data); 1182 logf("Load menu fail: %s", data);
1105 } 1183 }
1106 break; 1184 break;
1107 1185 case menu_byfirstletter: /* Fallthrough */
1108 case var_menu_start: 1186 case var_menu_start:
1109 if (menu_count >= TAGMENU_MAX_MENUS) 1187 if (menu_count >= TAGMENU_MAX_MENUS)
1110 { 1188 {
@@ -1146,6 +1224,19 @@ static int parse_line(int n, char *buf, void *parameters)
1146 return 0; 1224 return 0;
1147 } 1225 }
1148 logf("menu: %s", menu->title); 1226 logf("menu: %s", menu->title);
1227
1228 if (variable == menu_byfirstletter)
1229 {
1230 if (get_token_str(data, sizeof(data)) < 0)
1231 {
1232 logf("%%firstletter_menu has no subitem"); /*artist,album*/
1233 return 0;
1234 }
1235 logf("A-Z Menu subitem: %s", data);
1236 read_menu = false;
1237 build_firstletter_menu(data, sizeof(data));
1238 break;
1239 }
1149 read_menu = true; 1240 read_menu = true;
1150 break; 1241 break;
1151 1242
@@ -1179,18 +1270,10 @@ static int parse_line(int n, char *buf, void *parameters)
1179 return 0; 1270 return 0;
1180 } 1271 }
1181 1272
1182 /* Allocate */ 1273 if (!alloc_menu_parse_buf(buf, -1))
1183 if (menu->items[menu->itemcount] == NULL)
1184 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
1185 if (!menu->items[menu->itemcount])
1186 { 1274 {
1187 logf("tagtree failed to allocate %s", "menu items");
1188 return -2; 1275 return -2;
1189 } 1276 }
1190 core_pin(tagtree_handle);
1191 if (parse_search(menu->items[menu->itemcount], buf))
1192 menu->itemcount++;
1193 core_unpin(tagtree_handle);
1194 1277
1195 return 0; 1278 return 0;
1196} 1279}
@@ -1245,6 +1328,7 @@ static void tagtree_unload(struct tree_context *c)
1245 dptr->name = NULL; 1328 dptr->name = NULL;
1246 dptr->newtable = 0; 1329 dptr->newtable = 0;
1247 dptr->extraseek = 0; 1330 dptr->extraseek = 0;
1331 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1248 dptr++; 1332 dptr++;
1249 } 1333 }
1250 } 1334 }
@@ -1425,6 +1509,7 @@ static void tcs_get_basename(struct tagcache_search *tcs, bool is_basename)
1425 1509
1426static int retrieve_entries(struct tree_context *c, int offset, bool init) 1510static int retrieve_entries(struct tree_context *c, int offset, bool init)
1427{ 1511{
1512 logf( "%s", __func__);
1428 char tcs_buf[TAGCACHE_BUFSZ]; 1513 char tcs_buf[TAGCACHE_BUFSZ];
1429 const long tcs_bufsz = sizeof(tcs_buf); 1514 const long tcs_bufsz = sizeof(tcs_buf);
1430 struct tagcache_search tcs; 1515 struct tagcache_search tcs;
@@ -1452,9 +1537,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1452#else 1537#else
1453 true 1538 true
1454#endif 1539#endif
1455 , 0); 1540 , 0, 0, 0);
1456 1541
1457 if (c->currtable == ALLSUBENTRIES) 1542 if (c->currtable == TABLE_ALLSUBENTRIES)
1458 { 1543 {
1459 tag = tag_title; 1544 tag = tag_title;
1460 level--; 1545 level--;
@@ -1544,17 +1629,19 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1544 { 1629 {
1545 if (offset == 0) 1630 if (offset == 0)
1546 { 1631 {
1547 dptr->newtable = ALLSUBENTRIES; 1632 dptr->newtable = TABLE_ALLSUBENTRIES;
1548 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS); 1633 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS);
1634 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1549 dptr++; 1635 dptr++;
1550 current_entry_count++; 1636 current_entry_count++;
1551 special_entry_count++; 1637 special_entry_count++;
1552 } 1638 }
1553 if (offset <= 1) 1639 if (offset <= 1)
1554 { 1640 {
1555 dptr->newtable = NAVIBROWSE; 1641 dptr->newtable = TABLE_NAVIBROWSE;
1556 dptr->name = str(LANG_TAGNAVI_RANDOM); 1642 dptr->name = str(LANG_TAGNAVI_RANDOM);
1557 dptr->extraseek = -1; 1643 dptr->extraseek = -1;
1644 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1558 dptr++; 1645 dptr++;
1559 current_entry_count++; 1646 current_entry_count++;
1560 special_entry_count++; 1647 special_entry_count++;
@@ -1568,14 +1655,15 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1568 if (total_count++ < offset) 1655 if (total_count++ < offset)
1569 continue; 1656 continue;
1570 1657
1571 dptr->newtable = NAVIBROWSE; 1658 dptr->newtable = TABLE_NAVIBROWSE;
1572 if (tag == tag_title || tag == tag_filename) 1659 if (tag == tag_title || tag == tag_filename)
1573 { 1660 {
1574 dptr->newtable = PLAYTRACK; 1661 dptr->newtable = TABLE_PLAYTRACK;
1575 dptr->extraseek = tcs.idx_id; 1662 dptr->extraseek = tcs.idx_id;
1576 } 1663 }
1577 else 1664 else
1578 dptr->extraseek = tcs.result_seek; 1665 dptr->extraseek = tcs.result_seek;
1666 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1579 1667
1580 fmt = NULL; 1668 fmt = NULL;
1581 /* Check the format */ 1669 /* Check the format */
@@ -1676,7 +1764,7 @@ entry_skip_formatter:
1676 1764
1677 if (init) 1765 if (init)
1678 { 1766 {
1679 if (!show_search_progress(false, total_count)) 1767 if (!show_search_progress(false, total_count, 0, 0))
1680 { /* user aborted */ 1768 { /* user aborted */
1681 tagcache_search_finish(&tcs); 1769 tagcache_search_finish(&tcs);
1682 tree_unlock_cache(c); 1770 tree_unlock_cache(c);
@@ -1710,7 +1798,7 @@ entry_skip_formatter:
1710 1798
1711 while (tagcache_get_next(&tcs, tcs_buf, tcs_bufsz)) 1799 while (tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
1712 { 1800 {
1713 if (!show_search_progress(false, total_count)) 1801 if (!show_search_progress(false, total_count, 0, 0))
1714 break; 1802 break;
1715 total_count++; 1803 total_count++;
1716 } 1804 }
@@ -1758,7 +1846,7 @@ static int load_root(struct tree_context *c)
1758 int i; 1846 int i;
1759 1847
1760 tc = c; 1848 tc = c;
1761 c->currtable = ROOT; 1849 c->currtable = TABLE_ROOT;
1762 if (c->dirlevel == 0) 1850 if (c->dirlevel == 0)
1763 c->currextra = rootmenu; 1851 c->currextra = rootmenu;
1764 1852
@@ -1775,13 +1863,27 @@ static int load_root(struct tree_context *c)
1775 switch (menu->items[i]->type) 1863 switch (menu->items[i]->type)
1776 { 1864 {
1777 case menu_next: 1865 case menu_next:
1778 dptr->newtable = NAVIBROWSE; 1866 dptr->newtable = TABLE_NAVIBROWSE;
1779 dptr->extraseek = i; 1867 dptr->extraseek = i;
1868 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1780 break; 1869 break;
1781 1870
1782 case menu_load: 1871 case menu_load:
1783 dptr->newtable = ROOT; 1872 dptr->newtable = TABLE_ROOT;
1784 dptr->extraseek = menu->items[i]->link; 1873 dptr->extraseek = menu->items[i]->link;
1874 dptr->customaction = ONPLAY_NO_CUSTOMACTION;
1875 break;
1876
1877 case menu_shuffle_songs:
1878 dptr->newtable = TABLE_NAVIBROWSE;
1879 dptr->extraseek = i;
1880 dptr->customaction = ONPLAY_CUSTOMACTION_SHUFFLE_SONGS;
1881 break;
1882
1883 case menu_byfirstletter:
1884 dptr->newtable = TABLE_NAVIBROWSE;
1885 dptr->extraseek = i;
1886 dptr->customaction = ONPLAY_CUSTOMACTION_FIRSTLETTER;
1785 break; 1887 break;
1786 } 1888 }
1787 1889
@@ -1796,6 +1898,8 @@ static int load_root(struct tree_context *c)
1796 1898
1797int tagtree_load(struct tree_context* c) 1899int tagtree_load(struct tree_context* c)
1798{ 1900{
1901 logf( "%s", __func__);
1902
1799 int count; 1903 int count;
1800 int table = c->currtable; 1904 int table = c->currtable;
1801 1905
@@ -1804,20 +1908,32 @@ int tagtree_load(struct tree_context* c)
1804 if (!table) 1908 if (!table)
1805 { 1909 {
1806 c->dirfull = false; 1910 c->dirfull = false;
1807 table = ROOT; 1911 table = TABLE_ROOT;
1808 c->currtable = table; 1912 c->currtable = table;
1809 c->currextra = rootmenu; 1913 c->currextra = rootmenu;
1810 } 1914 }
1811 1915
1812 switch (table) 1916 switch (table)
1813 { 1917 {
1814 case ROOT: 1918 case TABLE_ROOT:
1919 logf( "root...");
1815 count = load_root(c); 1920 count = load_root(c);
1816 break; 1921 break;
1817 1922
1818 case ALLSUBENTRIES: 1923 case TABLE_ALLSUBENTRIES:
1819 case NAVIBROWSE: 1924 case TABLE_NAVIBROWSE:
1820 logf("navibrowse..."); 1925 logf("navibrowse...");
1926
1927 if (loaded_entries_crc != 0)
1928 {
1929 if (loaded_entries_crc == tagtree_data_crc(c))
1930 {
1931 count = c->dirlength;
1932 logf("Reusing %d entries", count);
1933 break;
1934 }
1935 }
1936
1821 cpu_boost(true); 1937 cpu_boost(true);
1822 count = retrieve_entries(c, 0, true); 1938 count = retrieve_entries(c, 0, true);
1823 cpu_boost(false); 1939 cpu_boost(false);
@@ -1828,6 +1944,8 @@ int tagtree_load(struct tree_context* c)
1828 return -1; 1944 return -1;
1829 } 1945 }
1830 1946
1947 loaded_entries_crc = 0;
1948
1831 if (count < 0) 1949 if (count < 0)
1832 { 1950 {
1833 if (count != RELOAD_TAGTREE) 1951 if (count != RELOAD_TAGTREE)
@@ -1860,6 +1978,8 @@ int tagtree_load(struct tree_context* c)
1860 */ 1978 */
1861int tagtree_enter(struct tree_context* c, bool is_visible) 1979int tagtree_enter(struct tree_context* c, bool is_visible)
1862{ 1980{
1981 logf( "%s", __func__);
1982
1863 int rc = 0; 1983 int rc = 0;
1864 struct tagentry *dptr; 1984 struct tagentry *dptr;
1865 struct mp3entry *id3; 1985 struct mp3entry *id3;
@@ -1921,16 +2041,16 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
1921 core_pin(tagtree_handle); 2041 core_pin(tagtree_handle);
1922 2042
1923 switch (c->currtable) { 2043 switch (c->currtable) {
1924 case ROOT: 2044 case TABLE_ROOT:
1925 c->currextra = newextra; 2045 c->currextra = newextra;
1926 2046
1927 if (newextra == ROOT) 2047 if (newextra == TABLE_ROOT)
1928 { 2048 {
1929 menu = menus[seek]; 2049 menu = menus[seek];
1930 c->currextra = seek; 2050 c->currextra = seek;
1931 } 2051 }
1932 2052
1933 else if (newextra == NAVIBROWSE) 2053 else if (newextra == TABLE_NAVIBROWSE)
1934 { 2054 {
1935 int i, j; 2055 int i, j;
1936 2056
@@ -2005,9 +2125,9 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
2005 2125
2006 break; 2126 break;
2007 2127
2008 case NAVIBROWSE: 2128 case TABLE_NAVIBROWSE:
2009 case ALLSUBENTRIES: 2129 case TABLE_ALLSUBENTRIES:
2010 if (newextra == PLAYTRACK) 2130 if (newextra == TABLE_PLAYTRACK)
2011 { 2131 {
2012 adjust_selection = false; 2132 adjust_selection = false;
2013 2133
@@ -2059,6 +2179,7 @@ int tagtree_enter(struct tree_context* c, bool is_visible)
2059/* Exits current database menu or table */ 2179/* Exits current database menu or table */
2060void tagtree_exit(struct tree_context* c, bool is_visible) 2180void tagtree_exit(struct tree_context* c, bool is_visible)
2061{ 2181{
2182 logf( "%s", __func__);
2062 if (is_visible) /* update selection history only for user-selected items */ 2183 if (is_visible) /* update selection history only for user-selected items */
2063 { 2184 {
2064 if (c->selected_item != selected_item_history[c->dirlevel]) 2185 if (c->selected_item != selected_item_history[c->dirlevel])
@@ -2102,44 +2223,84 @@ int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
2102 return 0; 2223 return 0;
2103} 2224}
2104 2225
2226int tagtree_get_custom_action(struct tree_context* c)
2227{
2228 return tagtree_get_entry(c, c->selected_item)->customaction;
2229}
2230
2231static void swap_array_bool(bool *a, bool *b)
2232{
2233 bool temp = *a;
2234 *a = *b;
2235 *b = temp;
2236}
2237
2238/**
2239 * Randomly shuffle an array using the Fisher-Yates algorithm :
2240 * https://en.wikipedia.org/wiki/Random_permutation
2241 * This algorithm has a linear complexity.
2242 * Don't forget to srand before call to use it with a relevant seed.
2243 */
2244static bool* fill_random_playlist_indexes(bool *bool_array, size_t arr_sz,
2245 size_t track_count, size_t max_slots)
2246{
2247 size_t i;
2248 if (track_count * sizeof(bool) > arr_sz || max_slots > track_count)
2249 return NULL;
2250
2251 for (i = 0; i < arr_sz; i++) /* fill max_slots with TRUE */
2252 bool_array[i] = i < max_slots;
2253
2254 /* shuffle bool array */
2255 for (i = track_count - 1; i > 0; i--)
2256 {
2257 int j = rand() % (i + 1);
2258 swap_array_bool(&bool_array[i], &bool_array[j]);
2259 }
2260 return bool_array;
2261}
2105 2262
2106static bool insert_all_playlist(struct tree_context *c, 2263static bool insert_all_playlist(struct tree_context *c,
2107 const char* playlist, bool new_playlist, 2264 const char* playlist, bool new_playlist,
2108 int position, bool queue) 2265 int position, bool queue)
2109{ 2266{
2110 struct tagcache_search tcs; 2267 struct tagcache_search tcs;
2111 int i, n; 2268 int n;
2112 int fd = -1; 2269 int fd = -1;
2113 unsigned long last_tick; 2270 unsigned long last_tick;
2271 int slots_remaining = 0;
2272 bool fill_randomly = false;
2273 bool *rand_bool_array = NULL;
2114 char buf[MAX_PATH]; 2274 char buf[MAX_PATH];
2275 struct playlist_insert_context context;
2115 2276
2116 cpu_boost(true); 2277 cpu_boost(true);
2278
2117 if (!tagcache_search(&tcs, tag_filename)) 2279 if (!tagcache_search(&tcs, tag_filename))
2118 { 2280 {
2119 splash(HZ, ID2P(LANG_TAGCACHE_BUSY)); 2281 splash(HZ, ID2P(LANG_TAGCACHE_BUSY));
2120 cpu_boost(false); 2282 cpu_boost(false);
2121 return false; 2283 return false;
2122 } 2284 } /* NOTE: you need to close this search before returning */
2123 2285
2124 if (playlist == NULL && position == PLAYLIST_REPLACE) 2286 if (playlist == NULL)
2125 { 2287 {
2126 if (playlist_remove_all_tracks(NULL) == 0) 2288 if (playlist_insert_context_create(NULL, &context, position, queue, false) < 0)
2127 position = PLAYLIST_INSERT_LAST;
2128 else
2129 { 2289 {
2290 tagcache_search_finish(&tcs);
2130 cpu_boost(false); 2291 cpu_boost(false);
2131 return false; 2292 return false;
2132 } 2293 }
2133 } 2294 }
2134 else if (playlist != NULL) 2295 else
2135 { 2296 {
2136 if (new_playlist) 2297 if (new_playlist)
2137 fd = open_utf8(playlist, O_CREAT|O_WRONLY|O_TRUNC); 2298 fd = open_utf8(playlist, O_CREAT|O_WRONLY|O_TRUNC);
2138 else 2299 else
2139 fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666); 2300 fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
2140
2141 if(fd < 0) 2301 if(fd < 0)
2142 { 2302 {
2303 tagcache_search_finish(&tcs);
2143 cpu_boost(false); 2304 cpu_boost(false);
2144 return false; 2305 return false;
2145 } 2306 }
@@ -2148,45 +2309,120 @@ static bool insert_all_playlist(struct tree_context *c,
2148 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */ 2309 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */
2149 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ 2310 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
2150 n = c->filesindir; 2311 n = c->filesindir;
2151 for (i = 0; i < n; i++) 2312
2313 if (playlist == NULL)
2152 { 2314 {
2315 int max_playlist_size = playlist_get_current()->max_playlist_size;
2316 slots_remaining = max_playlist_size - playlist_get_current()->amount;
2317 if (slots_remaining <= 0)
2318 {
2319 logf("Playlist has no space remaining");
2320 tagcache_search_finish(&tcs);
2321 cpu_boost(false);
2322 return false;
2323 }
2324
2325 fill_randomly = n > slots_remaining;
2326
2327 if (fill_randomly)
2328 {
2329 srand(current_tick);
2330 size_t bufsize = 0;
2331 bool *buffer = (bool *) plugin_get_buffer(&bufsize);
2332 rand_bool_array = fill_random_playlist_indexes(buffer, bufsize,
2333 n, slots_remaining);
2334
2335 talk_id(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY, true);
2336 splashf(HZ * 2, str(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY),
2337 slots_remaining);
2338 }
2339 }
2153 2340
2154 splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT)); 2341 bool exit_loop_now = false;
2342 for (int i = 0; i < n; i++)
2343 {
2155 if (TIME_AFTER(current_tick, last_tick + HZ/4)) 2344 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2156 { 2345 {
2346 splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
2157 if (action_userabort(TIMEOUT_NOBLOCK)) 2347 if (action_userabort(TIMEOUT_NOBLOCK))
2348 {
2349 exit_loop_now = true;
2158 break; 2350 break;
2351 }
2159 last_tick = current_tick; 2352 last_tick = current_tick;
2160 } 2353 }
2161 2354
2162 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, 2355 if (playlist == NULL)
2163 tcs.type, buf, sizeof buf))
2164 { 2356 {
2165 continue; 2357 if (fill_randomly)
2358 {
2359 int remaining_tracks = n - i;
2360 if (remaining_tracks > slots_remaining)
2361 {
2362 if (rand_bool_array)
2363 {
2364 /* Skip the track if rand_bool_array[i] is FALSE */
2365 if (!rand_bool_array[i])
2366 continue;
2367 }
2368 else
2369 {
2370 /* Generate random value between 0 and remaining_tracks - 1 */
2371 int selrange = RAND_MAX / remaining_tracks; /* Improve distribution */
2372 int random;
2373
2374 for (int r = 0; r < 0x0FFF; r++) /* limit loops */
2375 {
2376 random = rand() / selrange;
2377 if (random < remaining_tracks)
2378 break;
2379 else
2380 random = 0;
2381 }
2382 /* Skip the track if random >= slots_remaining */
2383 if (random >= slots_remaining)
2384 continue;
2385 }
2386 }
2387 }
2166 } 2388 }
2167 2389
2390 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, tcs.type, buf, sizeof buf))
2391 continue;
2392
2168 if (playlist == NULL) 2393 if (playlist == NULL)
2169 { 2394 {
2170 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) 2395 if (fill_randomly)
2171 { 2396 {
2397 if (--slots_remaining <= 0)
2398 {
2399 exit_loop_now = true;
2400 break;
2401 }
2402 }
2403
2404 if (playlist_insert_context_add(&context, buf) < 0) {
2172 logf("playlist_insert_track failed"); 2405 logf("playlist_insert_track failed");
2406 exit_loop_now = true;
2173 break; 2407 break;
2174 } 2408 }
2175 } 2409 }
2176 else if (fdprintf(fd, "%s\n", buf) <= 0) 2410 else if (fdprintf(fd, "%s\n", buf) <= 0)
2177 break;
2178
2179 yield();
2180
2181 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST)
2182 { 2411 {
2183 position = PLAYLIST_INSERT; 2412 exit_loop_now = true;
2413 break;
2184 } 2414 }
2415 yield();
2416
2417 if (exit_loop_now)
2418 break;
2185 } 2419 }
2420
2186 if (playlist == NULL) 2421 if (playlist == NULL)
2187 playlist_sync(NULL); 2422 playlist_insert_context_release(&context);
2188 else 2423 else
2189 close(fd); 2424 close(fd);
2425
2190 tagcache_search_finish(&tcs); 2426 tagcache_search_finish(&tcs);
2191 cpu_boost(false); 2427 cpu_boost(false);
2192 2428
@@ -2196,14 +2432,14 @@ static bool insert_all_playlist(struct tree_context *c,
2196static bool goto_allsubentries(int newtable) 2432static bool goto_allsubentries(int newtable)
2197{ 2433{
2198 int i = 0; 2434 int i = 0;
2199 while (i < 2 && (newtable == NAVIBROWSE || newtable == ALLSUBENTRIES)) 2435 while (i < 2 && (newtable == TABLE_NAVIBROWSE || newtable == TABLE_ALLSUBENTRIES))
2200 { 2436 {
2201 tagtree_enter(tc, false); 2437 tagtree_enter(tc, false);
2202 tagtree_load(tc); 2438 tagtree_load(tc);
2203 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2439 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
2204 i++; 2440 i++;
2205 } 2441 }
2206 return (newtable == PLAYTRACK); 2442 return (newtable == TABLE_PLAYTRACK);
2207} 2443}
2208 2444
2209static void reset_tc_to_prev(int dirlevel, int selected_item) 2445static void reset_tc_to_prev(int dirlevel, int selected_item)
@@ -2229,11 +2465,11 @@ static bool tagtree_insert_selection(int position, bool queue,
2229#else 2465#else
2230 true 2466 true
2231#endif 2467#endif
2232 , 0); 2468 , 0, 0, 0);
2233 2469
2234 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2470 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
2235 2471
2236 if (newtable == PLAYTRACK) /* Insert a single track? */ 2472 if (newtable == TABLE_PLAYTRACK) /* Insert a single track? */
2237 { 2473 {
2238 if (tagtree_get_filename(tc, buf, sizeof buf) < 0) 2474 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
2239 return false; 2475 return false;
@@ -2342,6 +2578,7 @@ int tagtree_add_to_playlist(const char* playlist, bool new_playlist)
2342 2578
2343static int tagtree_play_folder(struct tree_context* c) 2579static int tagtree_play_folder(struct tree_context* c)
2344{ 2580{
2581 logf( "%s", __func__);
2345 int start_index = c->selected_item; 2582 int start_index = c->selected_item;
2346 2583
2347 if (playlist_create(NULL, NULL) < 0) 2584 if (playlist_create(NULL, NULL) < 0)
@@ -2353,14 +2590,26 @@ static int tagtree_play_folder(struct tree_context* c)
2353 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false)) 2590 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false))
2354 return -2; 2591 return -2;
2355 2592
2593 int n = c->filesindir;
2594 bool has_playlist_been_randomized = n > playlist_get_current()->max_playlist_size;
2595 if (has_playlist_been_randomized)
2596 {
2597 /* We need to recalculate the start index based on a percentage to put the user
2598 around its desired start position and avoid out of bounds */
2599
2600 int percentage_start_index = 100 * start_index / n;
2601 start_index = percentage_start_index * playlist_get_current()->amount / 100;
2602 }
2603
2356 if (global_settings.playlist_shuffle) 2604 if (global_settings.playlist_shuffle)
2357 { 2605 {
2358 start_index = playlist_shuffle(current_tick, c->selected_item); 2606 start_index = playlist_shuffle(current_tick, start_index);
2359 if (!global_settings.play_selected) 2607 if (!global_settings.play_selected)
2360 start_index = 0; 2608 start_index = 0;
2361 } 2609 }
2362 2610
2363 playlist_start(start_index, 0, 0); 2611 playlist_start(start_index, 0, 0);
2612 loaded_entries_crc = tagtree_data_crc(c); /* save crc in case we return */
2364 return 0; 2613 return 0;
2365} 2614}
2366 2615
@@ -2403,11 +2652,11 @@ char *tagtree_get_title(struct tree_context* c)
2403{ 2652{
2404 switch (c->currtable) 2653 switch (c->currtable)
2405 { 2654 {
2406 case ROOT: 2655 case TABLE_ROOT:
2407 return menu->title; 2656 return menu->title;
2408 2657
2409 case NAVIBROWSE: 2658 case TABLE_NAVIBROWSE:
2410 case ALLSUBENTRIES: 2659 case TABLE_ALLSUBENTRIES:
2411 return current_title[c->currextra]; 2660 return current_title[c->currextra];
2412 } 2661 }
2413 2662
@@ -2419,7 +2668,7 @@ int tagtree_get_attr(struct tree_context* c)
2419 int attr = -1; 2668 int attr = -1;
2420 switch (c->currtable) 2669 switch (c->currtable)
2421 { 2670 {
2422 case NAVIBROWSE: 2671 case TABLE_NAVIBROWSE:
2423 if (csi->tagorder[c->currextra] == tag_title 2672 if (csi->tagorder[c->currextra] == tag_title
2424 || csi->tagorder[c->currextra] == tag_virt_basename) 2673 || csi->tagorder[c->currextra] == tag_virt_basename)
2425 attr = FILE_ATTR_AUDIO; 2674 attr = FILE_ATTR_AUDIO;
@@ -2427,7 +2676,7 @@ int tagtree_get_attr(struct tree_context* c)
2427 attr = ATTR_DIRECTORY; 2676 attr = ATTR_DIRECTORY;
2428 break; 2677 break;
2429 2678
2430 case ALLSUBENTRIES: 2679 case TABLE_ALLSUBENTRIES:
2431 attr = FILE_ATTR_AUDIO; 2680 attr = FILE_ATTR_AUDIO;
2432 break; 2681 break;
2433 2682