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: WSH Panel Mod script discussion/help (Read 1375667 times) previous topic - next topic
0 Members and 2 Guests are viewing this topic.

WSH Panel Mod script discussion/help

Reply #75
It is buggy.

but yours is pretty buggy as well.

#EOF gets printed over the text a lot (it's position on a previous artist isn't updated when switching to a new artist)
it can't save text files with special characters in (see this post on how to fix - http://www.hydrogenaudio.org/forums/index....t&p=682074)
text sometimes disappears completely when scrolling.
text doesn't always update when changing tracks with different artists (random)
sometimes can't scroll back to top

edit: try this one (it doesn't support dragging though, only mouse scrolling)

Code: [Select]
api_key = "";

DT_CENTER = 0x00000001;
DT_VCENTER = 0x00000004;
DT_WORDBREAK = 0x00000010;
DT_CALCRECT = 0x00000400;
DT_NOPREFIX = 0x00000800;
DT_END_ELLIPSIS = 0x00008000;

ColorTypeCUI = {
text: 0,
selection_text: 1,
inactive_selection_text: 2,
background: 3,
selection_background: 4,
inactive_selection_background: 5,
active_item_frame: 6
};

FontTypeCUI = {
items: 0,
labels: 1
};

ColorTypeDUI = {
text: 0,
background: 1,
highlight: 2,
selection: 3
};

FontTypeDUI = {
defaults: 0,
tabs: 1,
lists: 2,
playlists: 3,
statusbar: 4,
console: 5
};

function RGB(r,g,b) {
return (0xff000000|(r<<16)|(g<<8)|(b));
}

function get_font() {
if (g_instancetype == 0) {
g_font = window.GetFontCUI(FontTypeCUI.items);
} else if (g_instancetype == 1) {
g_font = window.GetFontDUI(FontTypeDUI.defaults);
}
}

function get_colors() {
if (g_instancetype == 0) {
g_textcolor = window.GetColorCUI(ColorTypeCUI.text);
g_textcolor_hl = window.GetColorCUI(ColorTypeCUI.text);
g_backcolor = window.GetColorCUI(ColorTypeCUI.background);
} else if (g_instancetype == 1) {
g_textcolor = window.GetColorDUI(ColorTypeDUI.text);
g_textcolor_hl = window.GetColorDUI(ColorTypeDUI.highlight);
g_backcolor = window.GetColorDUI(ColorTypeDUI.background);
}
}

function on_playlist_switch() {
on_item_focus_change();
}

function on_colors_changed() {
get_colors();
window.Repaint();
}

function on_font_changed() {
get_font();
window.Repaint();
}

function on_item_focus_change() {
if (g_metadb) window.UnwatchMetadb();
g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
if (g_metadb) {
try { on_metadb_changed(); } catch(e) {}
window.WatchMetadb(g_metadb);
}
}

g_instancetype = window.InstanceType;
get_font();
get_colors();

function lastfm(url, user_agent, func, debug) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", url + "&s=" + Math.random(), true);
xmlhttp.setRequestHeader('User-Agent',user_agent);
xmlhttp.send();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
if(debug) fb.trace(xmlhttp.responsetext);
func();
} else {
fb.trace(xmlhttp.responsetext);
}
}
}
}

g_metadb = fb.GetFocusItem();
var artist = g_text = '';
folder = fb.ProfilePath + "wsh_lastfm";
fso = new ActiveXObject("Scripting.FileSystemObject");
WshShell = new ActiveXObject("WScript.Shell");
if(!fso.FolderExists(folder)) fso.CreateFolder(folder);
on_item_focus_change();

String.prototype.count=function(s1) {
return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

function get_bio() {
    url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" + api_key + "&artist=" + encodeURIComponent(artist);
    lastfm(url, "foo_wsh_lastfm_bio", function() {save_file();},0);
}

function save_file() {
    xmlDoc = xmlhttp.responseXML;
    try {
        g_text = xmlDoc.getElementsByTagName("content")[0].childNodes[0].nodeValue;
        g_text = g_text.replace(/<\/?[^>]+(>|$)/g, "");
        doc = new ActiveXObject("htmlfile");
        doc.open();
        div = doc.createElement("div");
        div.innerHTML = g_text.replace(/\n/g, "<br>");
        g_text = div.innerHTML;
        g_text = g_text.replace(/<BR>/g,"\n");
        g_text = g_text.replace(/&amp;/g,"&");
    } catch(e) {
        g_text = "This artist does not have a biography page on Last.fm.";
    }
    try {
        ts = fso.OpenTextFile(filename, 2, true, -1);
        ts.WriteLine(g_text);
        ts.close();
    } catch(e) {
        g_text = "Error saving biography for this artist.";
    }
    calc();
}

function on_size() {
    ww = window.Width;
    wh = window.Height;
    calc();
}

function on_paint(gr) {
    gr.FillSolidRect(0, 0, ww, wh, g_backcolor);
    if(g_metadb) {
        gr.GdiDrawText(g_text, g_font, g_textcolor, 6, offset + 36, ww-16, wh-offset,DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
        gr.FillSolidRect(0, 0, ww, 38, g_backcolor);
        gr.GdiDrawText(artist, gdi.Font("Segoe UI", 16, 1), g_textcolor_hl, 6, 5, ww-77, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.DrawLine(5, 29, ww-10, 29, 1, RGB(137,140,149));
    }
}


function on_playback_new_track() {
    on_item_focus_change();
}

function on_metadb_changed() {
    if(!g_metadb || artist == fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb)) return;
    artist = fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb)
    filename = folder + "\\" + fb.TitleFormat("$crc32(%artist%).txt").EvalWithMetadb(g_metadb);
    if(fso.fileExists(filename)) {
        try {
            ts = fso.OpenTextFile(filename,1, false, -1);
            g_text = ts.ReadAll();
            ts.close();
        } catch(e) {
            g_text = "Error opening file!";
        }
        calc();
    } else {
        get_bio();
    }
}

function calc() {
    temp_bmp = gdi.CreateImage(1, 1);
    temp_gr = temp_bmp.GetGraphics();
    arr = temp_gr.GdiDrawText(g_text, g_font, g_textcolor, 0, 0, ww, wh,DT_CENTER| DT_WORDBREAK | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX).toArray();
    textheight = arr[3] - arr[1] + (wh/2) + (g_text.count("\n") * 3);
    temp_bmp.ReleaseGraphics(temp_gr);
    temp_bmp.Dispose();
    temp_gr = null;
    temp_bmp = null;
    offset = 0;
    window.Repaint();
}

function on_mouse_rbtn_up(x, y) {
    var MF_SEPARATOR = 0x00000800;
    var MF_STRING = 0x00000000;
    var _menu = window.CreatePopupMenu();
    var idx;
    _menu.AppendMenuItem(MF_STRING, 1, "Force update");
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
    _menu.AppendMenuItem(MF_STRING, 2, "Configure...");
    idx = _menu.TrackPopupMenu(x, y);
    if(idx == 1) get_bio();
    if(idx == 2) window.ShowConfigure();
    _menu.Dispose();
    return true;
}

function on_mouse_wheel(delta) {
    step = g_font.height * 5;
    offset += (delta * step);
    if(offset > 0 || textheight < wh) {
        offset = 0;
    } else {
        temp = -textheight + wh;
        if(offset < temp) offset = Math.round(temp / step) * step;
    }
    window.Repaint();
}

WSH Panel Mod script discussion/help

Reply #76
Excellent code marc2003! You have simplified a lot. Good idea getting rid of all the "var" stuff. Really not needed with JS being a loosely typed language.

I didn't want to edit too much on the previous code I borrowed from you. I know it still needed a lot of work.


But why not use the preprocessor features and import flags.txt and handlers.txt. I think a standard install of WSH Panels mod should have a scripts directory in foobar2000 folder with those standard includes. This would slim down the scripts even further and makes things really clean.

Code: [Select]
// ==PREPROCESSOR==
// @import "%fb2k_path%scripts\flags.txt"
// @import "%fb2k_path%scripts\helpers.txt"
// ==/PREPROCESSOR==

WSH Panel Mod script discussion/help

Reply #77
edit: try this one (it doesn't support dragging though, only mouse scrolling)


Oh, I really like this one.  The non-scrolling artist header is a cool idea.

WSH Panel Mod script discussion/help

Reply #78
Quote
Excellent code marc2003! You have simplified a lot. Good idea getting rid of all the "var" stuff. Really not needed with JS being a loosely typed language.
But JS has "scope", without "var" it's declared in global scope, and will confuse you if you are constructing a closure and use that variable in and it's hard to locate bugs if all variables are in global scope, eg:
Code: [Select]
function func1() {
    i = 1;
    fb.trace('"i" in func1() is :', i);  // i = 1
}

function test2() {
    // Call func1() here, and see what happens
    i = 2;
    func1();
    fb.trace('"i" in func2() is :', i);  //  i = 1 !!!
}

test2();

 

WSH Panel Mod script discussion/help

Reply #79
doesn't hurt to use var everywhere. but the script mostly avoided "var" on the global variables (outside closures).

WSH Panel Mod script discussion/help

Reply #80
But why not use the preprocessor features


i do already. and stuff like the sample colour/font changing stuff goes into an external file as well. then i have an external last.fm script to store my credentials and xmlhttp function - so i can share it between multiple panels.

i just merged it all into a single script above because i couldn't be bothered to explain myself.

WSH Panel Mod script discussion/help

Reply #81
Last.fm records the number of times I play a track by a specific artist in order to come up with the "top artists" stats.  Is this information available to the API?  If so, how would I access this in WSH Panel Mod?  Thanks.

Edit: I've got it.

WSH Panel Mod script discussion/help

Reply #82
In case anyone's interested, I modified marc2003's bio script to create a Windows 7 style bio panel and lyrics panel.  Screenshots:





The code for the bio panel:
Code: [Select]
var username = "";
var api_key = "";
var image_path = fb.FoobarPath + "images\\";

DT_CENTER = 0x00000001;
DT_VCENTER = 0x00000004;
DT_WORDBREAK = 0x00000010;
DT_CALCRECT = 0x00000400;
DT_NOPREFIX = 0x00000800;
DT_END_ELLIPSIS = 0x00008000;

ColorTypeCUI = {
    text: 0,
    selection_text: 1,
    inactive_selection_text: 2,
    background: 3,
    selection_background: 4,
    inactive_selection_background: 5,
    active_item_frame: 6
};

FontTypeCUI = {
    items: 0,
    labels: 1
};

ColorTypeDUI = {
    text: 0,
    background: 1,
    highlight: 2,
    selection: 3
};

FontTypeDUI = {
    defaults: 0,
    tabs: 1,
    lists: 2,
    playlists: 3,
    statusbar: 4,
    console: 5
};

function RGB(r,g,b) {
    return (0xff000000|(r<<16)|(g<<8)|(b));
}

function get_font() {
    if (g_instancetype == 0) {
        g_font = window.GetFontCUI(FontTypeCUI.items);
    }
    else if (g_instancetype == 1) {
        g_font = window.GetFontDUI(FontTypeDUI.defaults);
    }
}

function get_colors() {
    if (g_instancetype == 0) {
        g_textcolor = window.GetColorCUI(ColorTypeCUI.text);
        g_textcolor_hl = window.GetColorCUI(ColorTypeCUI.text);
        g_backcolor = window.GetColorCUI(ColorTypeCUI.background);
    }
    else if (g_instancetype == 1) {
        g_textcolor = window.GetColorDUI(ColorTypeDUI.text);
        g_textcolor_hl = window.GetColorDUI(ColorTypeDUI.highlight);
        g_backcolor = window.GetColorDUI(ColorTypeDUI.background);
    }
}

function on_playlist_switch() {
    on_item_focus_change();
}

function on_colors_changed() {
    get_colors();
    window.Repaint();
}

function on_font_changed() {
    get_font();
    window.Repaint();
}

function on_item_focus_change() {
    if (g_metadb) window.UnwatchMetadb();
    g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
    if (g_metadb) {
        try { on_metadb_changed(); } catch(e) {}
        window.WatchMetadb(g_metadb);
    }
}

g_instancetype = window.InstanceType;
get_font();
get_colors();

function lastfm(url, user_agent, func, debug) {
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    xmlhttp.open("GET", url + "&s=" + Math.random(), true);
    xmlhttp.setRequestHeader('User-Agent',user_agent);
    xmlhttp.send();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4) {
            if (xmlhttp.status == 200) {
                if(debug) fb.trace(xmlhttp.responsetext);
                func();
            }
            else {
                fb.trace(xmlhttp.responsetext);
            }
        }
    }
}

g_metadb = fb.GetFocusItem();
var artist = g_text = playcount = '';
var g_img = gdi.Image(image_path + "header.png");
folder = fb.ProfilePath + "wsh_lastfm";
fso = new ActiveXObject("Scripting.FileSystemObject");
WshShell = new ActiveXObject("WScript.Shell");
if(!fso.FolderExists(folder)) fso.CreateFolder(folder);
on_item_focus_change();
get_playcount();

String.prototype.count=function(s1) {
    return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

function get_bio() {
    url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" + api_key + "&username=" + username + "&artist=" + encodeURIComponent(artist);
    lastfm(url, "foo_wsh_lastfm_bio", function() {save_file();},0);
}

function get_playcount() {
    url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" + api_key + "&username=" + username + "&artist=" + encodeURIComponent(artist);
    lastfm(url, "foo_wsh_lastfm_bio", function() {extract_playcount();},0);
}

function save_file() {
    xmlDoc = xmlhttp.responseXML;
    try {
        g_text = xmlDoc.getElementsByTagName("content")[0].childNodes[0].nodeValue;
        playcount = xmlDoc.getElementsByTagName("userplaycount")[0].childNodes[0].nodeValue;
        g_text = g_text.replace(/<\/?[^>]+(>|$)/g, "");
        doc = new ActiveXObject("htmlfile");
        doc.open();
        div = doc.createElement("div");
        div.innerHTML = g_text.replace(/\n/g, "<br>");
        g_text = div.innerHTML;
        g_text = g_text.replace(/<BR>/g,"\n");
        g_text = g_text.replace(/&amp;/g,"&");
    }
    catch(e) {
        playcount = "0";
        g_text = "This artist does not have a biography page on Last.fm.";
    }
    try {
        ts = fso.OpenTextFile(filename, 2, true, -1);
        ts.WriteLine(g_text);
        ts.close();
    }
    catch(e) {
        g_text = "Error saving biography for this artist.";
    }
    calc();
}

function extract_playcount() {
    xmlDoc = xmlhttp.responseXML;
    try {
        playcount = xmlDoc.getElementsByTagName("userplaycount")[0].childNodes[0].nodeValue;
    }
    catch(e) {
        playcount = "0";
    }
    window.Repaint();
}

function on_size() {
    ww = window.Width;
    wh = window.Height;
    calc();
}

function on_paint(gr) {
    gr.FillSolidRect(0, 0, ww, wh, g_backcolor);
    if(g_metadb) {
        gr.GdiDrawText(g_text, g_font, g_textcolor, 21, offset + 60, ww-42, wh-offset,DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
        gr.FillSolidRect(0, 0, ww, 55, g_backcolor);
        gr.DrawImage(g_img, 0, 0, 830, 55, 0, 0, 830, 55);
        gr.FillGradRect(830, 0, ww - 820, 55, 0, RGB(252,252,252), RGB(255,255,255));
        gr.GdiDrawText(artist, gdi.Font("Segoe UI", 18, 0), RGB(0, 120, 50), 21, 4, ww-77, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.GdiDrawText("Artist Playcount:", gdi.Font("Segoe UI", 13, 0), RGB(90, 103, 121), 21, 29, ww-16, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.GdiDrawText(playcount, gdi.Font("Segoe UI", 13, 0), RGB(42, 102, 212), 120, 29, ww-16, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.DrawLine(21, 54, ww-21, 54, 1, RGB(214,229,245));
        gr.FillGradRect(6, 54,15, 1, 0, RGB(255,255,255), RGB(214,229,255));
        gr.FillGradRect(ww-21, 54,10, 1, 0, RGB(214,229,255),RGB(255,255,255));
    }
}


function on_playback_new_track() {
    on_item_focus_change();
    get_playcount();
}

function on_metadb_changed() {
    if(!g_metadb || artist == fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb)) return;
    artist = fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb);
    filename = folder + "\\" + fb.TitleFormat("$crc32(%artist%).txt").EvalWithMetadb(g_metadb);
    if(fso.fileExists(filename)) {
        try {
            ts = fso.OpenTextFile(filename,1, false, -1);
            g_text = ts.ReadAll();
            ts.close();
        }
        catch(e) {
            g_text = "Error opening file!";
        }
        calc();
    }
    else {
        get_bio();
        get_playcount();
    }
}

function calc() {
    temp_bmp = gdi.CreateImage(1, 1);
    temp_gr = temp_bmp.GetGraphics();
    arr = temp_gr.GdiDrawText(g_text, g_font, g_textcolor, 0, 0, ww, wh,DT_CENTER| DT_WORDBREAK | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX).toArray();
    textheight = arr[3] - arr[1] + (wh/2) + (g_text.count("\n") * 3);
    temp_bmp.ReleaseGraphics(temp_gr);
    temp_bmp.Dispose();
    temp_gr = null;
    temp_bmp = null;
    offset = 0;
    window.Repaint();
}

function on_mouse_rbtn_up(x, y) {
    var MF_SEPARATOR = 0x00000800;
    var MF_STRING = 0x00000000;
    var _menu = window.CreatePopupMenu();
    var idx;
    _menu.AppendMenuItem(MF_STRING, 1, "Force update");
    _menu.AppendMenuItem(MF_STRING, 2, "View on Last.fm");
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
    _menu.AppendMenuItem(MF_STRING, 3, "Configure...");
    idx = _menu.TrackPopupMenu(x, y);
    if(idx == 1) get_bio();
    if(idx == 2) WshShell.run("http://www.last.fm/music/" + encodeURIComponent(artist));
    if(idx == 3) window.ShowConfigure();
    _menu.Dispose();
    return true;
}

function on_mouse_wheel(delta) {
    step = g_font.height * 5;
    offset += (delta * step);
    if(offset > 0 || textheight < wh) {
        offset = 0;
    }
    else {
        temp = -textheight + wh;
        if(offset < temp) offset = Math.round(temp / step) * step;
    }
    window.Repaint();
}

The code for the lyrics panel:
Code: [Select]
var image_path = fb.FoobarPath + "images\\";
var lyrics_tag = "%lyrics%";

DT_CENTER = 0x00000001;
DT_VCENTER = 0x00000004;
DT_WORDBREAK = 0x00000010;
DT_CALCRECT = 0x00000400;
DT_NOPREFIX = 0x00000800;
DT_END_ELLIPSIS = 0x00008000;

function RGB(r,g,b) {
return (0xff000000|(r<<16)|(g<<8)|(b));
}

function on_playlist_switch() {
on_item_focus_change();
}

function on_item_focus_change() {
if (g_metadb) window.UnwatchMetadb();
g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
if (g_metadb) {
try { on_metadb_changed(); } catch(e) {}
window.WatchMetadb(g_metadb);
}
}

var artist = '';
var title = '';
var lyrics = '';
var g_img = gdi.Image(image_path + "header.png");

g_instancetype = window.InstanceType;
g_metadb = fb.GetFocusItem();

fso = new ActiveXObject("Scripting.FileSystemObject");
WshShell = new ActiveXObject("WScript.Shell");
on_item_focus_change();

String.prototype.count=function(s1) {
return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

function on_size() {
ww = window.Width;
wh = window.Height;
calc();
}

function on_paint(gr) {
gr.FillSolidRect(0, 0, ww, wh, RGB(255,255,255));
if(g_metadb) {
gr.GdiDrawText(lyrics, gdi.Font("Segoe UI", 13, 2), RGB(0, 0, 0), 0, offset + 65, ww-16, wh-offset,DT_CENTER|DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
gr.FillSolidRect(0, 0, ww, 55, RGB(255,255,255));
gr.DrawImage(g_img, 0, 0, 830, 55, 0, 0, 830, 55);
gr.FillGradRect(830, 0, ww - 820, 55, 0, RGB(252,252,252), RGB(255,255,255));
gr.GdiDrawText(title, gdi.Font("Segoe UI", 18, 0), RGB(0, 120, 50), 0, 4, ww-16, 24,DT_CENTER|DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
gr.GdiDrawText(artist, gdi.Font("Segoe UI", 13, 0), RGB(90, 103, 121), 0, 29, ww-16, 24,DT_CENTER|DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
gr.DrawLine(21, 54, ww-21, 54, 1, RGB(214,229,245));
gr.FillGradRect(6, 54,15, 1, 0, RGB(255,255,255), RGB(214,229,255));
gr.FillGradRect(ww-21, 54,10, 1, 0, RGB(214,229,255),RGB(255,255,255));
}
}

function on_playback_new_track() {
on_item_focus_change();
}

function on_metadb_changed() {
artist = fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb);
title = fb.TitleFormat("%title%").EvalWithMetadb(g_metadb);
lyrics = fb.TitleFormat(lyrics_tag).EvalWithMetadb(g_metadb);
if (lyrics == "?") lyrics = "No Lyrics Available";
window.Repaint();
}

function calc() {
temp_bmp = gdi.CreateImage(1, 1);
temp_gr = temp_bmp.GetGraphics();
arr = temp_gr.GdiDrawText(lyrics, gdi.Font("Segoe UI", 13, 2), RGB(0, 0, 0), 0, 0, ww, wh,DT_CENTER| DT_WORDBREAK | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX).toArray();
textheight = arr[3] - arr[1] + (wh/2) + (lyrics.count("\n") * 3);
temp_bmp.ReleaseGraphics(temp_gr);
temp_bmp.Dispose();
temp_gr = null;
temp_bmp = null;
offset = 0;
window.Repaint();
}

function on_mouse_rbtn_up(x, y) {
var MF_SEPARATOR = 0x00000800;
var MF_STRING = 0x00000000;
var _menu = window.CreatePopupMenu();
var idx;
_menu.AppendMenuItem(MF_STRING, 1, "Refresh");
_menu.AppendMenuItem(MF_STRING, 2, "Edit Lyrics");
_menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
_menu.AppendMenuItem(MF_STRING, 3, "Configure...");
idx = _menu.TrackPopupMenu(x, y);
if(idx == 1) window.Repaint();
if(idx == 2) fb.RunContextCommandWithMetadb("edit",g_metadb);
    if(idx == 3) window.ShowConfigure();
_menu.Dispose();
return true;
}

function on_mouse_wheel(delta) {
step = gdi.Font("Segoe UI", 13, 2).height * 5;
offset += (delta * step);
if(offset > 0 || textheight < wh) {
offset = 0;
}
else {
temp = -textheight + wh;
if(offset < temp) offset = Math.round(temp / step) * step;
}
window.Repaint();
}

The required header image (needs to be named "header.png"):


Finally, in order to get the "edit lyrics" command working, you need this component:
http://www.hydrogenaudio.org/forums/index....showtopic=36598

In order to preserve the Windows 7 styling, these scripts don't really play nice with the CUI/DUI default colors.  Hopefully someone finds these useful.

WSH Panel Mod script discussion/help

Reply #83
that needs some serious reworking. you're making 3 requests to last.fm for the same file all at once.

edit: and it just caused my foobar to crash when starting.....


WSH Panel Mod script discussion/help

Reply #84
Ok, this should make fewer calls to Last.fm.  Hopefully it also fixes any crashes:

Code: [Select]
var username = "";
var api_key = "";
var image_path = fb.FoobarPath + "images\\";

DT_CENTER = 0x00000001;
DT_VCENTER = 0x00000004;
DT_WORDBREAK = 0x00000010;
DT_CALCRECT = 0x00000400;
DT_NOPREFIX = 0x00000800;
DT_END_ELLIPSIS = 0x00008000;

function RGB(r,g,b) {
    return (0xff000000|(r<<16)|(g<<8)|(b));
}

function on_playlist_switch() {
    on_item_focus_change();
}

function on_item_focus_change() {
    if (g_metadb) window.UnwatchMetadb();
    g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
    if (g_metadb) {
        try { on_metadb_changed(); } catch(e) {}
        window.WatchMetadb(g_metadb);
    }
    get_bio();
}

g_instancetype = window.InstanceType;
g_font = gdi.Font("Segoe UI", 12, 0);
g_textcolor = RGB(0,0,0);
g_backcolor = RGB(255,255,255);

g_metadb = fb.GetFocusItem();
var artist = g_text = playcount = '';
var g_img = gdi.Image(image_path + "header.png");
folder = fb.ProfilePath + "wsh_lastfm";
fso = new ActiveXObject("Scripting.FileSystemObject");
WshShell = new ActiveXObject("WScript.Shell");
if(!fso.FolderExists(folder)) fso.CreateFolder(folder);
on_item_focus_change();

String.prototype.count=function(s1) {
    return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

function get_bio() {
    url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" + api_key + "&username=" + username + "&artist=" + encodeURIComponent(artist);
    lastfm(url, "foo_wsh_lastfm_bio", function() {save_file();extract_playcount();},0);
}

function lastfm(url, user_agent, func, debug) {
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    xmlhttp.open("GET", url + "&s=" + Math.random(), true);
    xmlhttp.setRequestHeader('User-Agent',user_agent);
    xmlhttp.send();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4) {
            if (xmlhttp.status == 200) {
                if(debug) fb.trace(xmlhttp.responsetext);
                func();
            }
            else {
                fb.trace(xmlhttp.responsetext);
            }
        }
    }
}

function save_file() {
    xmlDoc = xmlhttp.responseXML;
    try {
        g_text = xmlDoc.getElementsByTagName("content")[0].childNodes[0].nodeValue;
        playcount = xmlDoc.getElementsByTagName("userplaycount")[0].childNodes[0].nodeValue;
        g_text = g_text.replace(/<\/?[^>]+(>|$)/g, "");
        doc = new ActiveXObject("htmlfile");
        doc.open();
        div = doc.createElement("div");
        div.innerHTML = g_text.replace(/\n/g, "<br>");
        g_text = div.innerHTML;
        g_text = g_text.replace(/<BR>/g,"\n");
        g_text = g_text.replace(/&amp;/g,"&");
    }
    catch(e) {
        playcount = "0";
        g_text = "This artist does not have a biography page on Last.fm.";
    }
    try {
        ts = fso.OpenTextFile(filename, 2, true, -1);
        ts.WriteLine(g_text);
        ts.close();
    }
    catch(e) {
        g_text = "Error saving biography for this artist.";
    }
    calc();
}

function extract_playcount() {
    xmlDoc = xmlhttp.responseXML;
    try {
        playcount = xmlDoc.getElementsByTagName("userplaycount")[0].childNodes[0].nodeValue;
    }
    catch(e) {
        playcount = "0";
    }
    window.Repaint();
}

function on_size() {
    ww = window.Width;
    wh = window.Height;
    calc();
}

function on_paint(gr) {
    gr.FillSolidRect(0, 0, ww, wh, g_backcolor);
    if(g_metadb) {
        gr.GdiDrawText(g_text, g_font, g_textcolor, 21, offset + 60, ww-42, wh-offset,DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
        gr.FillSolidRect(0, 0, ww, 55, g_backcolor);
        gr.DrawImage(g_img, 0, 0, 830, 55, 0, 0, 830, 55);
        gr.FillGradRect(830, 0, ww - 820, 55, 0, RGB(252,252,252), RGB(255,255,255));
        gr.GdiDrawText(artist, gdi.Font("Segoe UI", 18, 0), RGB(0, 120, 50), 21, 4, ww-77, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.GdiDrawText("Artist Playcount:", gdi.Font("Segoe UI", 13, 0), RGB(90, 103, 121), 21, 29, ww-16, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.GdiDrawText(playcount, gdi.Font("Segoe UI", 13, 0), RGB(42, 102, 212), 120, 29, ww-16, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.DrawLine(21, 54, ww-21, 54, 1, RGB(214,229,245));
        gr.FillGradRect(6, 54,15, 1, 0, RGB(255,255,255), RGB(214,229,255));
        gr.FillGradRect(ww-21, 54,10, 1, 0, RGB(214,229,255),RGB(255,255,255));
    }
}

function on_playback_new_track() {
    on_item_focus_change();
}

function on_metadb_changed() {
    if(!g_metadb || artist == fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb)) return;
    artist = fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb);
    filename = folder + "\\" + fb.TitleFormat("$crc32(%artist%).txt").EvalWithMetadb(g_metadb);
    if(fso.fileExists(filename)) {
        try {
            ts = fso.OpenTextFile(filename,1, false, -1);
            g_text = ts.ReadAll();
            ts.close();
        }
        catch(e) {
            g_text = "Error opening file!";
        }
        calc();
    }
    else {
        get_bio();
    }
}

function calc() {
    temp_bmp = gdi.CreateImage(1, 1);
    temp_gr = temp_bmp.GetGraphics();
    arr = temp_gr.GdiDrawText(g_text, g_font, g_textcolor, 0, 0, ww, wh,DT_CENTER| DT_WORDBREAK | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX).toArray();
    textheight = arr[3] - arr[1] + (wh/2) + (g_text.count("\n") * 3);
    temp_bmp.ReleaseGraphics(temp_gr);
    temp_bmp.Dispose();
    temp_gr = null;
    temp_bmp = null;
    offset = 0;
    window.Repaint();
}

function on_mouse_rbtn_up(x, y) {
    var MF_SEPARATOR = 0x00000800;
    var MF_STRING = 0x00000000;
    var _menu = window.CreatePopupMenu();
    var idx;
    _menu.AppendMenuItem(MF_STRING, 1, "Force update");
    _menu.AppendMenuItem(MF_STRING, 2, "View on Last.fm");
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
    _menu.AppendMenuItem(MF_STRING, 3, "Configure...");
    idx = _menu.TrackPopupMenu(x, y);
    if(idx == 1) get_bio();
    if(idx == 2) WshShell.run("http://www.last.fm/music/" + encodeURIComponent(artist));
    if(idx == 3) window.ShowConfigure();
    _menu.Dispose();
    return true;
}

function on_mouse_wheel(delta) {
    step = g_font.height * 5;
    offset += (delta * step);
    if(offset > 0 || textheight < wh) {
        offset = 0;
    }
    else {
        temp = -textheight + wh;
        if(offset < temp) offset = Math.round(temp / step) * step;
    }
    window.Repaint();
}

WSH Panel Mod script discussion/help

Reply #85
you should be making one call. it's the same file. why are you fetching it twice?

and you might as well scrap the idea of caching files being as though you're fetching the file on every new track.

WSH Panel Mod script discussion/help

Reply #86
Ok, so I'm (obviously) new at this.  What I was trying to do was always download the xml file on each new track to obtain the artist playcount.  However, I wanted to use the cached artist bio if available (as your code does).  Yes, I understand that much of the usefulness of the cached data is lost in this implementation, but displaying the bio info itself should still be quick (cached data) and only the playcount itself will always have a slight delay.  So, it should have worked like this:

if no bio is cached, it makes 1 request for the xml file and extracts the bio and playcount.
if a bio is cached, it makes 1 request for the xml file and extracts the playcount only.

I made some stupid mistakes in the previous versions, however, I've reworked the code again (last time for the night), and I think this does what I want it to:

Code: [Select]
var username = "";
var api_key = "";
var image_path = fb.FoobarPath + "images\\";

DT_CENTER = 0x00000001;
DT_VCENTER = 0x00000004;
DT_WORDBREAK = 0x00000010;
DT_CALCRECT = 0x00000400;
DT_NOPREFIX = 0x00000800;
DT_END_ELLIPSIS = 0x00008000;

function RGB(r,g,b) {
    return (0xff000000|(r<<16)|(g<<8)|(b));
}

function on_playlist_switch() {
    on_item_focus_change();
}

function on_item_focus_change() {
    if (g_metadb) window.UnwatchMetadb();
    g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
    if (g_metadb) {
        try { on_metadb_changed(); } catch(e) {}
        window.WatchMetadb(g_metadb);
    }
}

g_instancetype = window.InstanceType;
g_font = gdi.Font("Segoe UI", 12, 0);
g_textcolor = RGB(0,0,0);
g_backcolor = RGB(255,255,255);

g_metadb = fb.GetFocusItem();
var artist = g_text = playcount = url = '';
var g_img = gdi.Image(image_path + "header.png");
folder = fb.ProfilePath + "wsh_lastfm";
fso = new ActiveXObject("Scripting.FileSystemObject");
WshShell = new ActiveXObject("WScript.Shell");
if(!fso.FolderExists(folder)) fso.CreateFolder(folder);

on_item_focus_change();

String.prototype.count=function(s1) {
    return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

function get_bio() {
    lastfm(url, "foo_wsh_lastfm_bio", function() {save_file();},0);
}

function get_playcount() {
    lastfm(url, "foo_wsh_lastfm_bio", function() {extract_playcount();},0);
}

function lastfm(url, user_agent, func, debug) {
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    xmlhttp.open("GET", url + "&s=" + Math.random(), true);
    xmlhttp.setRequestHeader('User-Agent',user_agent);
    xmlhttp.send();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4) {
            if (xmlhttp.status == 200) {
                if(debug) fb.trace(xmlhttp.responsetext);
                func();
            }
            else {
                fb.trace(xmlhttp.responsetext);
            }
        }
    }
}

function save_file() {
    xmlDoc = xmlhttp.responseXML;
    try {
        g_text = xmlDoc.getElementsByTagName("content")[0].childNodes[0].nodeValue;
        playcount = xmlDoc.getElementsByTagName("userplaycount")[0].childNodes[0].nodeValue;
        g_text = g_text.replace(/<\/?[^>]+(>|$)/g, "");
        doc = new ActiveXObject("htmlfile");
        doc.open();
        div = doc.createElement("div");
        div.innerHTML = g_text.replace(/\n/g, "<br>");
        g_text = div.innerHTML;
        g_text = g_text.replace(/<BR>/g,"\n");
        g_text = g_text.replace(/&amp;/g,"&");
    }
    catch(e) {
        g_text = "This artist does not have a biography page on Last.fm.";
        playcount = "0";
    }
    try {
        ts = fso.OpenTextFile(filename, 2, true, -1);
        ts.WriteLine(g_text);
        ts.close();
    }
    catch(e) {
        g_text = "Error saving biography for this artist.";
    }
    calc();
}

function extract_playcount() {
    xmlDoc = xmlhttp.responseXML;
    try {
        playcount = xmlDoc.getElementsByTagName("userplaycount")[0].childNodes[0].nodeValue;
    }
    catch(e) {
        playcount = "0";
    }
    window.Repaint();
}

function on_size() {
    ww = window.Width;
    wh = window.Height;
    calc();
}

function on_paint(gr) {
    gr.FillSolidRect(0, 0, ww, wh, g_backcolor);
    if(g_metadb) {
        gr.GdiDrawText(g_text, g_font, g_textcolor, 21, offset + 60, ww-42, wh-offset,DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
        gr.FillSolidRect(0, 0, ww, 55, g_backcolor);
        gr.DrawImage(g_img, 0, 0, 830, 55, 0, 0, 830, 55);
        gr.FillGradRect(830, 0, ww - 820, 55, 0, RGB(252,252,252), RGB(255,255,255));
        gr.GdiDrawText(artist, gdi.Font("Segoe UI", 18, 0), RGB(0, 120, 50), 21, 4, ww-77, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.GdiDrawText("Artist Playcount:", gdi.Font("Segoe UI", 13, 0), RGB(90, 103, 121), 21, 29, ww-16, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.GdiDrawText(playcount, gdi.Font("Segoe UI", 13, 0), RGB(42, 102, 212), 120, 29, ww-16, 24,DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
        gr.DrawLine(21, 54, ww-21, 54, 1, RGB(214,229,245));
        gr.FillGradRect(6, 54,15, 1, 0, RGB(255,255,255), RGB(214,229,255));
        gr.FillGradRect(ww-21, 54,10, 1, 0, RGB(214,229,255),RGB(255,255,255));
    }
}

function on_playback_new_track() {
    on_item_focus_change();
}

function on_metadb_changed() {
    if(!g_metadb || artist == fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb)) return;
    artist = fb.TitleFormat("%artist%").EvalWithMetadb(g_metadb);
    filename = folder + "\\" + fb.TitleFormat("$crc32(%artist%).txt").EvalWithMetadb(g_metadb);
    url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" + api_key + "&username=" + username + "&artist=" + encodeURIComponent(artist);
    playcount = '';
    if(fso.fileExists(filename)) {
        get_playcount();
        try {
            ts = fso.OpenTextFile(filename,1, false, -1);
            g_text = ts.ReadAll();
            ts.close();
        }
        catch(e) {
            g_text = "Error opening file!";
        }
        calc();
    }
    else {
        get_bio();
    }
}

function calc() {
    temp_bmp = gdi.CreateImage(1, 1);
    temp_gr = temp_bmp.GetGraphics();
    arr = temp_gr.GdiDrawText(g_text, g_font, g_textcolor, 0, 0, ww, wh,DT_CENTER| DT_WORDBREAK | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX).toArray();
    textheight = arr[3] - arr[1] + (wh/2) + (g_text.count("\n") * 3);
    temp_bmp.ReleaseGraphics(temp_gr);
    temp_bmp.Dispose();
    temp_gr = null;
    temp_bmp = null;
    offset = 0;
    window.Repaint();
}

function on_mouse_rbtn_up(x, y) {
    var MF_SEPARATOR = 0x00000800;
    var MF_STRING = 0x00000000;
    var _menu = window.CreatePopupMenu();
    var idx;
    _menu.AppendMenuItem(MF_STRING, 1, "Force update");
    _menu.AppendMenuItem(MF_STRING, 2, "View on Last.fm");
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
    _menu.AppendMenuItem(MF_STRING, 3, "Configure...");
    idx = _menu.TrackPopupMenu(x, y);
    if(idx == 1) get_bio();
    if(idx == 2) WshShell.run("http://www.last.fm/music/" + encodeURIComponent(artist));
    if(idx == 3) window.ShowConfigure();
    _menu.Dispose();
    return true;
}

function on_mouse_wheel(delta) {
    step = g_font.height * 5;
    offset += (delta * step);
    if(offset > 0 || textheight < wh) {
        offset = 0;
    }
    else {
        temp = -textheight + wh;
        if(offset < temp) offset = Math.round(temp / step) * step;
    }
    window.Repaint();
}

WSH Panel Mod script discussion/help

Reply #87
@carmenm, how did you parse the feeds to get image urls? i'm stuck doing this. 

WSH Panel Mod script discussion/help

Reply #88
@carmenm, how did you parse the feeds to get image urls? i'm stuck doing this. 

Can you wait 1 or 2 days? i am on the  verge of releasing the manager that will do all that for you.
If you cant tell me and i ll put a code here.

WSH Panel Mod script discussion/help

Reply #89
i'm only interested in downloading 1 image per artist - the one in the artist.getinfo

Code: [Select]
  <image size="small">http://userserve-ak.last.fm/serve/34/34848663.png</image>
  <image size="medium">http://userserve-ak.last.fm/serve/64/34848663.png</image>
  <image size="large">http://userserve-ak.last.fm/serve/126/34848663.png</image>
  <image size="extralarge">http://userserve-ak.last.fm/serve/252/34848663.png</image>
  <image size="mega">http://userserve-ak.last.fm/serve/_/34848663/Jem++PNG+6.png</image>


i just want to know how to get the url for the "mega" bit and that is all. i tried that activex thing you posted and that works fine if i put in the url manually so i'm happy using that.

WSH Panel Mod script discussion/help

Reply #90
i'm only interested in downloading 1 image per artist - the one in the artist.getinfo

Code: [Select]
  <image size="small">http://userserve-ak.last.fm/serve/34/34848663.png</image>
  <image size="medium">http://userserve-ak.last.fm/serve/64/34848663.png</image>
  <image size="large">http://userserve-ak.last.fm/serve/126/34848663.png</image>
  <image size="extralarge">http://userserve-ak.last.fm/serve/252/34848663.png</image>
  <image size="mega">http://userserve-ak.last.fm/serve/_/34848663/Jem++PNG+6.png</image>


i just want to know how to get the url for the "mega" bit and that is all. i tried that activex thing you posted and that works fine if i put in the url manually so i'm happy using that.

Yes that s exactly what my manager does, i wont spoil all the fun  I am kidding.
With that line you get all artist tags
Code: [Select]
var artistsArray = xmlDoc.getElementsByTagName("artist");

then with this one you get the mega picture
Code: [Select]
var artistpicture = (artistsArray[i].childNodes[7].childNodes[0])?artistsArray[i].childNodes[7].childNodes[0].nodeValue:"";

WSH Panel Mod script discussion/help

Reply #91
thanks.

WSH Panel Mod script discussion/help

Reply #92
I finally released my suite.

Enjoy

WSH Panel Mod script discussion/help

Reply #93
where is the updated Bios panel without multi last .fm requests. I just want a simple codebase to work from.
no fluff or useless crap. Don't understand why you guys have to go overboard with your scripts. Keep it simple stupid. Less is more, etc... etc..

WSH Panel Mod script discussion/help

Reply #94
updated biography panel which caches the whole xml repsonse which means you can save/display all data. rather than parse the response, it now parses the cached file if it exists during on_metadb_changed. uses external js files so check those as well.

http://cid-649d3bfeaf541fbb.skydrive.live....astfm%20bio.zip

(it doesn't display any new data but i've put in samples of how to do it)

WSH Panel Mod script discussion/help

Reply #95
marc2003;

Thank you for the above script. I can't seem to get it working though.
Any idea whats going wrong?

popup:
Scripting Engine Initialization Failed (GUID: F22DD133-CB46-41DF-874E-93B34B56CD82, CODE: 0x80020101)
Check the console for more detailed information (Always caused by unexcepted script error).

console output:
WSH Panel Mod (GUID: DA503407-4725-42B8-B2D7-A823F69425D5): initliased in 0.0000371 s
WSH Panel Mod (GUID: F22DD133-CB46-41DF-874E-93B34B56CD82): Parsing file "C:\Program Files (x86)\foobar2000\scripts\marc2003\fonts_colours_flags.js"
WSH Panel Mod (GUID: F22DD133-CB46-41DF-874E-93B34B56CD82): Parsing file "C:\Program Files (x86)\foobar2000\scripts\marc2003\lastfm.js"
Error: WSH Panel Mod (GUID: F22DD133-CB46-41DF-874E-93B34B56CD82): Microsoft JScript runtime error:
Automation server can't create object
Ln: 8, Col: 1
<source text only available in compile time>

WSH Panel Mod script discussion/help

Reply #96
file>preferences>tools>wsh panel mod>uncheck "safe mode". restart foobar.

WSH Panel Mod script discussion/help

Reply #97
Great...
Works now!

WSH Panel Mod script discussion/help

Reply #98
Can some kind soul convert the playback section of this theme (buttons, progress, volume etc) to a script for someone like me who doesn't know even a little but of jscript
Thanks
http://fanco86.deviantart.com/art/MonoLite-122756120

WSH Panel Mod script discussion/help

Reply #99
With some bio's it fetches I see &nbsp; codes in there. Is it easy to replace these with normal spaces via your script? What line should I edit where etc?

i'm only interested in downloading 1 image per artist - the one in the artist.getinfo
[...]
i just want to know how to get the url for the "mega" bit and that is all. i tried that activex thing you posted and that works fine if i put in the url manually so i'm happy using that.
Are you in the process of writing a new script that fetches the artist images from last.fm? Something in the same manner that the bio plugin does. If so I'm lookig forward to it!  Now that I think of it a configurable amount of images per artist would be a nice to have too (than it will have all I like from the bio plugin without the overhead).