Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Tag Cloud Filter / Multiline display (Read 2334 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Tag Cloud Filter / Multiline display

Hello,

I manually put all relevant track-based information on the %content group% tag of each mp3.

Here are example %Content Group% tags:

#1990s
#2000s
#Rock
#Acoustic
#Ballads
#Male Vocalist
#Alternative
#Mellow
#Folk
#Female Vocalist
#Indie
#Pop
#R&B
#Post-hardcore
#Slow
#Relaxing
#Happy
#Melancholic
#Country
@Arya
@Jon
@Tyrion
@iPhone

Note:
#-Tags (genre, decades, tempo, etc)
@-People or Device

This way, it's easier for me to filter songs for playback.
For example, I can just do a quicksearch of "@Arya 90s Acoustic Indie Folk" and it will display acoustic indie folk songs from the 90s that Arya listens to.

Alternatively, I have filter panels:

Horizontal Panel
-Filter (selects @Arya)
-Filter (selects #1990s)
-Filter (selects #Acoustic)
-Filter (selects #Indie)
-Filter (selects #Folk)
-Filter (selects #Rock)
-Filter
-Filter

Doing it via filters does the same thing.


Question 1:
Is it possible to create a tag cloud filter via the existing panels?
Like say, I have a bunch of words (tags) on a popup panel window. I select several words and then it filters the playlist based on the selected words.


Question 2:
Is it possible to do multiline display per track on NG Playlist?
Something like this:

%Title%
%content group%

%Title%
%content group%

%Title%
%content group%


If not, how do you do this on other playlist views?


Question 3:
In Filter Panels, it displays the #.

#Acoustic
#Indie
#Rock


I would prefer it to display:

Acoustic
Rock
Indie

When I use "$replace(%content group%,#,)", it displays %content group% in one line.

Acoustic, Indie, Rock


Thanks a bunch!

Tag Cloud Filter / Multiline display

Reply #1
Question 1:

There's a WSH Panel Mod script that does exactly this but uses the genre tag. It should be modifiable to use the content group tag. The functionality is great but it's somewhat aesthetically lacking. Unfortunately I don't remember exactly where I saw, but I did save it. I just don't have the time to locate it right now. It's a relatively new post so you might try perusing posts from 2014 in the "WSH Panel Mod script" thread (although that might not be where it was posted).

Question 2:

Not possible in NG Playlist. It is possible in ELPlaylist if you know how to script. ELPlaylist is otherwise too much of a PITA to explain IMO.

Question 3:

You cannot* manipulate individual values of multi-value tags (in foobar).

*I say "cannot" because only one 3rd-party component currently has the capability: foo_uie_library_tree.

Tag Cloud Filter / Multiline display

Reply #2
Thanks for the reply. I'll try to find the WSH Panel Mod script.

Tag Cloud Filter / Multiline display

Reply #3
The script filters the active playlist. Clicking on a first value sends all items in the source playlist tagged with the value to a new playlist named "Tag Results" and activates it, giving it focus. Subsequent selections will now filter this "Tag Results" playlist.

The panel automatically updates itself with all values found in the playlist with each selection. Deselecting all values in the panel reactivates the source playlist, returning focus to it.

I've not yet been able to locate the original post so as to give credit to the script's author. I think I might have made some cosmetic changes to the script but I'm not entirely certain.

Code: [Select]
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
 
IDC_ARROW = 32512;
IDC_HAND = 32649;
// window.SetCursor()
 
NOT_FOUND = 4294967295;
// MetaFind() and elsewhere for consistency
 
/**
Colors, coordinates, etc
**/
 
// General
meta_field = "genre";
tag_delimiter = ";";
 
working_playlist_name = "Tag Results";
playing_playlist_name = "Tag Results (Playback)";
 
font_std = gdi.Font("Segoe UI", 10, 0);
font_bold = gdi.Font("Segoe UI", 10, 1);
font_ital = gdi.Font("Segoe UI", 10, 2);
 
// Tags
t_x = 8;
t_y = 8;
t_spc = 8;
 
little_x = "ˣ";
 
label_color = RGB(151, 181, 192);
label_text_color = RGB(252, 252, 252);
 
fallback_text = "nothing here";
fallback_text_color = RGB(205, 205, 205);
 
// Status
s_x = 24;
s_y = 32;
 
status_line = "Found %t %btag%s in playlist:"
 
status_text_color = RGB(49, 49, 49);
 
// Columns
c_y = 55;
 
row_h = 24;
row_w = .8; // %
column_indent = 8;
 
no_of_columns = 2;
scroll_step = 1;
 
text_omitted = " ...";
 
tag_name_color = RGB(49, 49, 49);
alt_row_color = RGB(241, 246, 243);
 
/**
Playlist dict
**/
 
function build_dict() {
tag_dict = { };
for (n = 0; n < playlistdb.Count; n++) {
track = playlistdb.Item(n);
metadb = track.GetFileInfo();
 
tag_idx = metadb.MetaFind(meta_field);
if (tag_idx == NOT_FOUND)
continue;
tag_cnt = metadb.MetaValueCount(tag_idx);
for (t = 0; t < tag_cnt; t++) {
tag = metadb.MetaValue(tag_idx, t);
dict_push(tag, track, tag_dict);
}
}
}
 
function get_keys(dict) {
keys = [];
for (key in dict)
keys.push(key);
return keys;
}
 
function dict_push(key, v, dict) {
if (!(key in dict))
dict[key] = plman.GetPlaylistItems(-1);
dict[key].Add(v);
}
 
/**
Playlist utility
**/
 
function assess_playlist() {
playlistdb = plman.GetPlaylistItems(parent_playlist);
build_dict();
playlistdb.RemoveAll();
}
 
function playlist_add(tag) {
new_tag = tag_dict[tag];
 
switch (mode) {
case "&&":
playlistdb = new_tag.Clone();
build_dict();
break;
case "||":
playlistdb.AddRange(new_tag);
break;
}
}
 
function remake_playlist() {
assess_playlist();
for (tag in active_tags) {
playlist_add(tag);
}
}
 
function update_playlist() {
suppress_pl_events = true;
switch (working_playlist_index) {
case fb.PlayingPlaylist:
freeze_playlist();
case NOT_FOUND:
make_playlist();
}
focus_playlist();
clear_playlist();
plman.InsertPlaylistItems(working_playlist_index, 0, playlistdb);
}
 
function make_playlist() {
working_playlist_index = fb.PlaylistCount;
fb.CreatePlaylist(working_playlist_index, working_playlist_name);
}
 
function focus_playlist() {
fb.ActivePlaylist = working_playlist_index;
}
 
function clear_playlist() {
selection = [];
p_c = fb.PlaylistItemCount(fb.ActivePlaylist);
for (idx = 0; idx < p_c; idx++) {
selection.push(idx);
}
plman.SetPlaylistSelection(fb.ActivePlaylist, selection, true);
plman.RemovePlaylistSelection(fb.ActivePlaylist);
}
 
function freeze_playlist() {
fb.RenamePlaylist(working_playlist_index, playing_playlist_name);
playing_playlist_index = working_playlist_index;
}
 
// ~~~
 
function locate_playlists() {
// Called on init and on_playlists_changed()
working_playlist_index = NOT_FOUND,
playing_playlist_index = NOT_FOUND;
for (pl_idx = 0; pl_idx < fb.PlaylistCount; pl_idx++) {
pl_name = plman.GetPlaylistName(pl_idx);
if (pl_name == working_playlist_name) {
working_playlist_index = pl_idx;
}
else if (pl_name == playing_playlist_name) {
playing_playlist_index = pl_idx;
}
}
}
 
/**
Tags
**/
 
function draw_labels(gr) {
x = t_x;
y = t_y;
run_w = t_x;
if (len_active_tags() == 0) {
w = gr.CalcTextWidth(fallback_text, font_ital),
h = gr.CalcTextHeight(fallback_text, font_ital);
gr.GdiDrawText(fallback_text, font_ital, fallback_text_color, x, y, w, h);
}
else {
for (tag in active_tags) {
active_tags[tag].draw(gr);
}
}
}
 
label = function(tag) {
this.name = tag;
this.draw = function(gr) {
txt_w = gr.CalcTextWidth(this.name, font_std),
txt_h = gr.CalcTextHeight(this.name, font_std);
 
x = t_x + run_w;
y = t_y;
txt_pad_r = txt_h,
txt_pad_l = txt_h/2,
bg_w = txt_pad_l + txt_w + txt_pad_r,
bg_h = txt_h+3,
bg_arc = bg_h/2,
text_x = x+txt_pad_l,
text_y = y+1,
close_x = x+txt_w+txt_pad_r-3,
close_y = y+4;
// ~~~
gr.SetSmoothingMode(2);
gr.FillRoundRect(x, y, bg_w, bg_h, bg_arc, bg_arc, label_color);
gr.GdiDrawText(this.name, font_std, label_text_color, text_x, text_y, txt_w, txt_h);
gr.GdiDrawText(little_x, font_std, label_text_color, close_x, close_y, txt_h, txt_h, label_color);
// ~~~
run_w += bg_w + t_spc;
this.left = x,
this.right = x+bg_w,
this.top = y,
this.bottom = y+bg_h;
}
this.check_mouse = function(evt, x, y) {
mouse_is_over = (x >= this.left && x <= this.right &&
y >= this.top && y <= this.bottom);
if (mouse_is_over) {
switch (evt) {
case "up":
dismiss(this.name);
break;
case "move":
set_hand_cursor = true;
break;
}
}
}
}
 
function len_active_tags() {
n = 0;
for (tag in active_tags)
n++;
return n;
}
 
/**
Status
**/
 
function say_status(gr) {
text = status_line;
text = text.replace("%t", tags.tags.length)
.replace("%b", len_active_tags() > 0 && mode == "&&" ? "sub" : "")
.replace("%s", tags.tags.length == 1 ? "" : "s")
.replace("%a", len_active_tags())
.replace("%S", len_active_tags() == 1 ? "" : "s")
.replace("%p", fb.PlaylistItemCount(fb.ActivePlaylist));
text_w = gr.CalcTextWidth(text, font_ital);
text_h = gr.CalcTextHeight(text, font_ital);
gr.GdiDrawText(text, font_ital, status_text_color, s_x, s_y, text_w, text_h);
}
 
/**
Columns
**/
 
tag_list = function() {
this.update = function() {
this.offset = 0;
this.tags = get_keys(tag_dict);
this.tags.sort();
// ~~~
space_avail = (wh-c_y);
total_rows = Math.ceil(this.tags.length/no_of_columns);
rows_possible = Math.floor(space_avail/row_h)-1;
this.visible_rows = Math.min(rows_possible, total_rows);
this.out_of_bounds = total_rows - this.visible_rows;
}
this.draw_rows = function(gr) {
h = row_h;
w = row_w*ww;
x = (ww-w)/2;
y = c_y;
min = this.offset;
max = this.offset+this.visible_rows;
for (n = min; n < max; n++) {
if (n%2 == 0) {
gr.FillSolidRect(x, y, w, h, alt_row_color);
}
y += h;
}
this.left = x;
this.right = x+w;
this.top = c_y;
this.bottom = c_y + this.visible_rows*h;
this.column_w = w/no_of_columns;
}
this.populate = function(gr) {
th = gr.CalcTextHeight(".", font_std);
// Should be consistent
x = this.left + column_indent;
y = this.top + (row_h-th)/2-1;
min = no_of_columns*this.offset;
max = Math.min(no_of_columns*(this.offset+this.visible_rows), this.tags.length);
 
for (n = min; n < max; n++) {
tag = this.tags[n];
font = font_std;
if (tag in active_tags)
font = font_bold;
 
tw = gr.CalcTextWidth(tag, font);
while (column_indent + tw > this.column_w) {
if (tag.length == 0) break;
tag = tag.slice(0, -1);
tw = gr.CalcTextWidth(tag + text_omitted, font);
}
if (tag != this.tags[n]) tag += text_omitted;
gr.GdiDrawText(tag, font, tag_name_color, x, y, tw, th);
x += this.column_w;
if ((n+1)%no_of_columns == 0) {
x = this.left+column_indent;
y += row_h;
}
}
}
this.check_mouse = function(x, y) {
mouse_is_over = (x >= this.left && x <= this.right &&
y >= this.top && y <= this.bottom);
if (mouse_is_over) {
idx = this.check_index(x, y);
if (idx < this.tags.length) {
this.toggle(idx);
}
}
}
this.toggle = function(idx) {
tag = this.tags[idx];
if (tag in active_tags)
dismiss(tag);
else
activate(tag);
}
this.check_index = function(x, y) {
idx = no_of_columns*this.offset;
idx += Math.floor((y-this.top)/row_h)*no_of_columns;
idx += Math.floor((x-this.left)/this.column_w);
return idx;
}
this.jump_to_index = function(chr) {
for (t = 0; t < this.tags.length; t++) {
tag = this.tags[t];
first_chr = tag.charAt(0).toUpperCase();
if (chr == first_chr) {
new_offset = Math.floor(t/no_of_columns);
this.offset = Math.min(new_offset, this.out_of_bounds);
return;
}
}
}
}
 
function activate(tag) {
active_tags[tag] = new label(tag);
playlist_add(tag);
update_playlist();
if (mode == "&&") {
this.offset = 0;
tags.update();
}
}
 
function dismiss(tag) {
delete active_tags[tag];
 
if (len_active_tags() > 0) {
remake_playlist();
update_playlist();
}
else {
fb.ActivePlaylist = parent_playlist;
}
if (mode == "&&") {
this.offset = 0;
tags.update();
}
}
 
/**
Init, defaults, main events
**/
 
mode = "&&";
tags = new tag_list();
 
locate_playlists();
on_new_playlist();
 
function on_size() {
wh = window.Height,
ww = window.Width;
tags.update();
}
 
function on_paint(gr) {
draw_labels(gr);
tags.draw_rows(gr);
tags.populate(gr);
say_status(gr);
// ~~~
suppress_pl_events = false;
// Won't do at the end of update_playlist(). Not sure how else to execute this last.
}
 
/**
Mouse, key events
**/
 
function on_mouse_lbtn_up(x, y) {
tags.check_mouse(x, y);
for (tag in active_tags) {
active_tags[tag].check_mouse("up", x, y);
}
window.Repaint();
}
 
function on_mouse_move(x, y) {
set_hand_cursor = false;
for (tag in active_tags) {
active_tags[tag].check_mouse("move", x, y);
}
set_hand_cursor ? window.SetCursor(IDC_HAND) : window.SetCursor(IDC_ARROW);
}
 
function on_mouse_wheel(step) {
if (step > 0) {
tags.offset -= scroll_step;
tags.offset = Math.max(tags.offset, 0);
}
else {
tags.offset += scroll_step;
tags.offset = Math.min(tags.offset, tags.out_of_bounds);
}
window.Repaint();
}
 
function on_key_down(vkey) {
chr = String.fromCharCode(vkey);
tags.jump_to_index(chr);
// Gets awfully wonky with non-alpha characters, whatever "vkey" is doesn't
// mirror the common ASCII alphabet
window.Repaint();
}
 
/**
Playlist events
**/
 
function on_new_playlist() {
// Not a native event
active_tags = { };
parent_playlist = fb.ActivePlaylist;
assess_playlist();
}
 
function on_playlists_changed() {
if (suppress_pl_events) return;
// ~~~
locate_playlists();
}
 
function on_playback_new_track() {
if (playing_playlist_index != fb.PlayingPlaylist) {
fb.RemovePlaylist(playing_playlist_index);
}
}
 
function on_playlist_items_added() {
on_playlist_x();
}
 
function on_playlist_items_removed() {
on_playlist_x();
}
 
function on_playlist_switch() {
on_playlist_x();
}
 
var suppress_pl_events;
function on_playlist_x() {
if (suppress_pl_events) return;
// ~~~
on_new_playlist();
tags.update();
window.Repaint();
}

Tag Cloud Filter / Multiline display

Reply #4
Thanks again!

 

Tag Cloud Filter / Multiline display

Reply #5
Found it. Here's the LINK