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] - Display custom charts and statistics (incl. Last.fm) (Read 22418 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #25
Can you please share that buttons (WSH?) panel (code and pics) with Discogs, SongMeanings and Musicbraiz buttons that can be seen in first screenshot? Looks very nice, would like to try it.


basic icons (+ empty templates) Vector Social Media Icons

my discogs, songmeanings version (+musicbrainz) http://ur.ly/hbR8

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #26
Thank you very much!

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #27
Hi Muzack.
Finally, after some test and research, I can't use this WSH script for anything more than lastFM charts.
Actually, when I install local playcount (with playcount.sqlite), everything is ok, untill the next restart of foobar.
Then, when restarting foobar, the process is running, but no window appears.
My only way is to delete playcount.sqlite file.
First time I thought it could be due to some special characters, maybe not recognized by the database (some accent, replaced by strange character)... but problem is still there after installing the local playcount, and restarting just after that (excluding any creation in the database).
So i'm sticked, with last.fm charts, after having scanned all my 70go music files for fingerprint, etc...   
Maybe it is due to my config, or maybe because i am using a portable installation. I should make more test, but can't define my next step.
cheers.

PS: you did not know before novembre (the user, not the month  ) that foo_playback_custom was using a PLAY_STAMP tag... now that you know, is there any hope that you add the possibility to use those embedded tags, instead another external database file? (expressed in another words, its the same question that made few posts above, just before you knew the PLAY_STAMP thing / who said i was boring? ...  )

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #28
I might start to retag & fingerprint all my files... Thanks for sharing!!

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #29
So I have been using this script for some time and like it very much. I wanted to add an extra query to display a custom chart of most played albums of the genre that is currently played. Everything is set up fine and the sql query is sound however for some reason the WHERE clause for case 160 doesn't seem to work. The strange thing is only +albumid+ and +artistid+ seem to work in a WHERE clause and nothing else.

I've got no knowledge of Jscript and basic knowledge of sql.

Code: [Select]
// ---------------------------------------
// show and generate bar charts
// ---------------------------------------

function build_charts(){
    if(fso.fileExists(db_file)){   
      try{ 
        var date_range = dateRangeObj[dateRangeIdx].sql;
        var dateRangeSqlStr = (date_range)? 'datetime(PLAYTIME) > datetime(\'now\',\''+date_range+'\',\'localtime\')' : "1=1" ;
            if(show_charts === 1){
                var query = "";
                switch(chart_mode){
                    case 150:
                    query = '\"SELECT COUNT(ARTISTID) AS ARTISTCOUNT,ARTIST FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY artistid ORDER BY artistcount DESC LIMIT '+limit+';\"';
                    break;
                    case 151:
                    query = '\"SELECT COUNT(TRACKID) AS TRACK_COUNT ,ARTIST || \' - \' || TITLE, TRACKID  FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY ARTIST, TITLE ORDER BY TRACK_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 152:
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT ,ARTIST || \' - \' || ALBUM  FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY ALBUMID, ARTIST ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 153:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT, ALBUM  FROM playcount WHERE ARTISTID = '+artist_id+' AND '+dateRangeSqlStr+' GROUP BY ALBUMID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 155:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(TITLE) AS TITLE_COUNT, TITLE FROM playcount WHERE ARTISTID = '+artist_id+'  AND ALBUMID = '+album_id+' AND '+dateRangeSqlStr+' GROUP BY TITLE ORDER BY TITLE_COUNT DESC, PLAYTIME DESC LIMIT '+limit+';\"';
                    break;
                    case 156:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(TITLE) AS TITLE_COUNT, TITLE FROM playcount WHERE ARTISTID = '+artist_id+' AND '+dateRangeSqlStr+' GROUP BY TITLE ORDER BY TITLE_COUNT DESC, PLAYTIME DESC LIMIT '+limit+';\"';
                    break;
                    case 154:
                    query = '\"SELECT COUNT(sort1) AS SORT1_COUNT, sort1 FROM playcount WHERE '+dateRangeSqlStr+'  GROUP BY sort1 ORDER BY SORT1_COUNT DESC LIMIT '+limit+';\"';
                    break;
    case 160:
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT, ALBUM  FROM playcount WHERE sort1 = '+sort1+' AND '+dateRangeSqlStr+' GROUP BY ALBUMID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 157: // query lfm charts
                    case 158:
                    case 159:
                        get_lfm_charts();
                        return;
                    break;
                    default:
                    query = '\"SELECT COUNT(ARTIST) AS ARTISTCOUNT,ARTIST FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY artistid ORDER BY artistcount DESC LIMIT '+limit+';\"';           
                }

Can anyone advise? I've been looking at this, and the entire script, for 2 days now and can't seem to find what's wrong. Is there some variable that needs to be defined so that it knows to look at the current playing genre and us that in the WHERE clause of the query? The field name in the table for it is "sort1".


This is the complete script:

[code]// ==PREPROCESSOR==
// @import "%fb2k_path%scripts\common.js"
// ==/PREPROCESSOR==
var version = "1.0 beta5";
/*
    version 1.0 beta5
        optimize customization vars a bit (colors/fonts)
        loading one common.js file
    version 1.0 beta4
        customized install routine
    version 1.0 beta3
        some cleanup, adding sprintf()
        set titleformat and lang strings as object for easier edit.
    version 1.0 beta2
        query the playlist, write it to json because playlistObj go nowhere
        read json, sort it from chartArray write m3u - load it with foobar
    version 1.0 beta
        generate playlist from different charts (artist/title)
        playlist is generated by serarchquery of foo_httpcontrol.
        No SORT possible, right now.
        Playlist could be generated via javascript/json.
        result could be parsed and sorted right, writing m3u playlist   
    version 0.9.0
        add install routine to create playcount.sqlite
        drop batchfile using %comspec% /c sqlite3.exe ...
        simplify reading charts.txt using function
    version 0.8.5
        add meta total played to stat!
    version 0.8
        add overall to track stat, using an Sqlite SQL View Table!
        fix display longer timespans in statistic
    version 0.7
        add last.fm charts finally
        cleanup/messup code
    version 0.6
        add Tooltip to points, finally
        points generated by class
    version 0.5
        add Radio counting by station
        fix "?" for untagged parts to NULL and write them as undefined in DB
    version 0.4
        add correct scrobbling behaviour and set playcount after 50% or 4 minutes (lean by marc2000)
    version 0.3
        add album, tracks from album, tracks by current artist to charts
        remove writing daily playcount.txt (flatfile db)
        add menu to mouse leftclick
    version 0.2
        add SORT1 Query for charts
    version 0.1
        initial release
   
    TODO:
    set meta for charts (average track etc)
    - var modulo = depending on daterange woche = 7 / monat/jahr = 30 / need to display dates on x axsis
    - easier setup.
    - make playlist work on first try!
   
*/

// CUSTOM CHANGES START
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Set fb2k titleformatsyntax here
 */

/*
    Note:
    define your titleformat for artist,album,title here
    the data to each track in db is stored as readable text.
    for grouping data, we generate unique numbers/id from artist, album, to ensure
    displaying after changing lower/uppercase of these fields.
    But if you change artist/title/album we can't group correctly anymore
    So this is where %FOO_..._ID% comes in.
    But these values must be hardwired to each track as id3tag
    Same goes for %FINGERPRINT_FOOID% a unique acoustic fingerprint.
    If they are present you can change your Tags and statistic keeps track and can group
    by artist/album correctly




*/

var tf ={  artist:"$trim($if2(%artist%,undefined))",
            artist_id:"$if(%FOO_ARTIST_ID%,%FOO_ARTIST_ID%,$crc32($lower([%artist%])))",
            album:"$trim($if2(%album%,undefined))",
            album_id:"$if(%FOO_ALBUM_ID%,%FOO_ALBUM_ID%,$crc32($lower([%album%])))",
            title:"$trim($if2(%title%,undefined))",
            id:"$if(%FINGERPRINT_FOOID%,$crc32(%FINGERPRINT_FOOID%),$crc32($lower([%artist%][%album%][%title%])))",
            date:"[%date%]",
            playcount:"[%play_counter%]",
            first_played:"[%first_played%]",
            last_played:"[%last_played%]",
            tracktime:"[%length%]",
            sort1:"[%genre%]", // sort1 and 2 are mapped to playcount table
            sort2:"[%style%]"         
        };
/*
    Some language strings
*/
var lang = {
          no_stat:"No Stats at the moment, but they will be generated",
          chart_mode_text:{  150:"Overall artist charts for %s",
                        151:"Most played tracks for the last %s",
                        152:"Most played albums for the last %s",
                        153:"Most played album by %s for the last %s",
                        154:"Favorite genre for the last %s",
                        155:"Most played tracks of %s's album ''%s'' for the last %s",
                        156:"Most played tracks of %s for the last %s",
                  160:"Favorite album of genre for the last %s",
                        157:"Last.fm artist charts for the last %s",
                        158:"Last.fm most played tracks for the last %s",
                        159:"Last.fm most played albums for the last %s",
                        def:"Charts for %s"                     
                      },
          chart_meta_text:{ def:"Meta (Query Avarage Track Playing per day and total over time)"
                      },
          stat_mode_text:{  1:"Statistic for ''%2$s'' by %1$s",
                        2:"Statistic for album ''%3$s'' by %1$s",
                        3:"Statistic for %1$s",
                        4: "Top per day"
                      },
          stat_meta_text:{  def:"Total played %f times - first played %s - last played %s"
                        }
}

var stat_headline = "Charts & Statistics";
var meta = "by muzack, version "+version;
var stat_install_info = "Click on the top headline to get the menu.\nSetup your last.fm username and api key to retrive your last.fm statistics\nSetup a custom playcount statistic database to track your plays locally and get some nice realtime graphs. (experimental!)";


/*
    Color & Font Settings
*/
var stat_item_color = RGB(113,183,230); // element color
var stat_bg_color = RGB(28,28,28); // white
var stat_hl_color = RGB(255,255,255); // white
var stat_line = RGB(230,230,230); // light grey
var stat_legend = RGB(200,200,200); // grau
var stat_item_t = RGBA(200,10,200,0); // grau
var stat_meta_color = RGB(150,150,150); // grey
var g_textcolor_hl = RGB(141,213,225); // Head Text Color
var g_color_hl = RGB(203,203,203);

var m_font = gdi.Font("Segoe UI", 8, 1); // micro font-size
var s_font = gdi.Font("Segoe UI", 9, 1); // small font-size
var n_font = gdi.Font("Tahoma", 9, 0); // normal font-size
var nb_font = gdi.Font("Lucida Grande", 9, 0); // normal font-size, bold
var bb_font = gdi.Font("Segoe UI", 13, 1); // big font, bold

// CUSTOM CHANGES END - Do not edit below this, unless you know what you do!
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

var debug = window.GetProperty("debug",0);

// tooltip Marks
var g_tooltip = window.CreateTooltip();
var g_down = false;
var ttm_down = null;
var cur_ttm = null;
var ttMarks = [];

// standard values for stat/charts
// date range
var dateRangeObj = [{text:"one week",sql:"-7 days",lfm:"7day"},
                {text:"one month",sql:"-1 months",lfm:"1month"},
                {text:"three months",sql:"-3 months",lfm:"3month"},
                {text:"six month",sql:"-6 months",lfm:"6month"},
                {text:"one year",sql:"-1 years",lfm:"12month"},
                {text:"overall",sql:"",lfm:"overall"}];
var dateRangeIdx = window.GetProperty("date_range",110) - 110;
// limit charts
var limitRangeArray = ["20", "30", "40","50"];
var limit = limitRangeArray[window.GetProperty("limit_range",120) - 120];

// which_stat = Current Track based statistic
// 1 = track, 2=album, 3=artist, 4 Top per day
var which_stat = window.GetProperty("which_stat",1);

// show bars = 1/show valley = 0
var show_charts = window.GetProperty("show_charts",1);
// chart_mode = artist/album/trackcharts
var chart_mode = window.GetProperty("chart_mode",150);
// lfm chart mode api call
var lfm_chart = {157:"gettopartists",158:"gettoptracks",159:"gettopalbums"};

// files & paths
var sqlite3exe = fb.FoobarPath+'sqlite3.exe';
var db_file = fb.ProfilePath+'playcount.sqlite';
var init_sql = script_path + 'custom_playback_statistic\\playcount-struct.sql';
var info_txt = script_path + 'custom_playback_statistic\\install-custom-playcount-info.txt';
var batch_cmd = '%comspec% /c sqlite3.exe "' + os_escape(db_file) + '" ';
var pc_file = data_folder + 'playcount.txt';
var pc_file_test = data_folder + 'playcount.txt';
var chart_file = data_folder + 'charts.txt';
var lfm_chart_json = data_folder + "lfm-chart.json";

var username_file = settings_path + "username";
var username = read(username_file);
var api_key_file = settings_path + "api_key";
var api_key = read(api_key_file);

// data arrays
var pc_data = [];
var chartsArray = [[0,'unknown']];
var chartsObj = {};

// playlist generator
var foo_httpcontrol_exist = utils.CheckComponent("foo_httpcontrol", true);
var playlistEnabledArr = ["151","155","156"]; // which stat can produce playlists
var playlist_query_url = 'http://127.0.0.1:8888/Query/';
var playlistObj = {};
var playlistSortArray = [];
var playlist_json = data_folder + "playlist.json";
var playlist_m3u = data_folder + "playlist.m3u";
var playlist_img = gdi.Image(images_path + "playlist.png");
var stat_img = gdi.Image(images_path + "stat.png");
// test
//var chartsArray = csvArray("2|Inouk ~ No Danger|350263707\n2|Inouk ~ Elected|594016820\n2|Inouk ~ With The Birds|2574961760\n2|Inouk ~ Somewhere In France|3721457220\n1|Inouk ~ Victory|62160163\n1|Uke Of Spaces Corners County ~ This Old World|311760166\n1|Motorama ~ Letter Home|1144510168\n1|Uke Of Spaces Corners County ~ Bird On A Wire|1178016057\n1|Uke Of Spaces Corners County ~ Flotilla|1791777379\n1|Motorama ~ Compass|2013908431\n");


// helper
var offset = 0;
var time_elapsed = 0;
var target_time = 0;

// ---------------------------------------
// let the charts begin!
// ---------------------------------------
on_item_focus_change();
show(show_charts);
// ---------------------------------------
// -- FUNCTIONS
// ---------------------------------------

function init_playcount(){
   
    if(!fso.fileExists(sqlite3exe)){
        var BtnCode2 = WshShell.Popup("sqlite3.exe not found in foobar2000 program folder.\nPress Ok to open the folder.", 0 ,"Error",1+16);
        switch(BtnCode2) {
                    case 1: //ok
                        WshShell.Run('"'+os_escape(fb.FoobarPath)+'"');
                        fb.trace("SQLite.exe not in "+fb.FoobarPath);
                        return false;                       
                    break;
                    case 2: //cancel
                        return false;
                    break;
                }
       
        return false;
    }
   
   
    if(!fso.fileExists(db_file)) {
        try{
            // keep window open for msg
            //WshShell.Run('%comspec% /k sqlite3.exe "' + os_escape(db_file) + '" < "' + os_escape(init_sql) + '"', 0, true);
            //test for sqlite3.exe
            var BtnCode = WshShell.Popup("Install custom playcount statistic database?", 0 ,"Setup",4+32);
                switch(BtnCode) {
                    case 6: //yes
                        WshShell.Run(batch_cmd + ' < "' + os_escape(init_sql) + '"', 0, true);             
                    break;
                    case 7: //no
                        return false;
                    break;
                }
            var i = read_textfile(info_txt);
            if(init_playcount()) fb.ShowPopupMessage(i,"Custom playcount statistic info");
        } catch(e){
            fb.trace('SQLITE: Error creating DB!');
            return false;
        }
    }else{
        fb.trace("SQLITE: DB already exist!");
        return true;
    }
}


function disable_custom_playcount(){
   
    var BtnCode = WshShell.Popup("Disable custom playcount statistic?", 5 ,"Setup",4+32);
    switch(BtnCode) {
        case 6: //yes
            // rename playcount db file!
            var error = 0;
            try{fso.MoveFile(db_file, fb.ProfilePath+'playcount.disabled.'+playtime("datetime")+'.sqlite')}catch(e){error = 1};
            try{fso.DeleteFile(pc_file)}catch(e){};
            try{fso.DeleteFile(chart_file)}catch(e){};
            if(error == 1)
                fb.ShowPopupMessage('Custom playcount DB cannot be renamed to playcount.disabled.'+playtime()+'.sqlite - please rename or delete it manually');
            else
                fb.ShowPopupMessage('Custom playcount DB renamed to playcount.disabled.'+playtime()+'.sqlite');
            return (window.Repaint());
        break;
        case 7: //no
            return false;
        break;
    }
}

function show(mode){
  return (mode == 1) ? build_charts() : build_stat();
}

function on_playback_stop(reason) {
   //fb.trace("Playback Stop "+reason);
}

function on_notify_data(name, data) {
   if(name == "lastfm_update" && data == 1) {
      username = read(username_file);
      api_key = read(api_key_file);
      get_lfm_charts();
        window.Repaint();
   }
}

// ---------------------------------------
// build statistic based playlist via foo_httpcontrol
// ---------------------------------------

function check_enable_playlist(){
return (foo_httpcontrol_exist && playlistEnabledArr.exists(chart_mode) && show_charts == 1);
}

function build_playlist(){
    var playlist_queryArray = [];
    var playlist_sortArray = [];
    var playlist_query = '';
    var m3u ='';
    var url='';
    var url_param = "?cmd=Search&param3=state.json&param1=";

    if(chartsArray.length > 0){
        for (var i = 0; i != chartsArray.length; i++){
            if(chartsArray[2] != undefined)
            playlist_queryArray.push("FOO_ID IS "+chartsArray[2]);
            playlist_sortArray.push(chartsArray[2]);
        }
        if(playlist_queryArray.length > 0){
            playlist_query = playlist_queryArray.join(" OR ");
            url = playlist_query_url+url_param+encodeURIComponent(playlist_query);
            foo_httpcontrol(url, "stat", function() {save_playlist_json()});
        }
        // maybe playlist is not saved at this point
        // we have to check if it's ready... :-(
        parse_httpcontrol();       
        outer_loop:
        for (var i = 0; i != playlist_sortArray.length; i++){
            inner_loop:
            for(var j in playlistObj.playlist){
                if(playlistObj.playlist[j].track_id == playlist_sortArray){
                    //fb.trace(playlistObj.playlist[j].track_id + " / " +playlistObj.playlist[j].p);
                    m3u += playlistObj.playlist[j].p+"\n";
                    break inner_loop;
                }
               
            }

        }
        if(debug)fb.trace("M3U content\n"+m3u);
        try {
          ts = fso.OpenTextFile(playlist_m3u, 2, true, -2);
          ts.Write(m3u);
          ts.close();
        } catch(e) {
              fb.trace("M3U: Error save charts "+e);
        }
      // load sorted playlist
      WshShell.Run('"foobar2000.exe" "'  + playlist_m3u + '" ', 0, true);         
    }
}

function save_playlist_json() {
   xmlDoc = xmlhttp.responsetext;
   try {
      ts = fso.OpenTextFile(playlist_json, 2, true, -1);
      ts.Write(xmlDoc);
      ts.close();
        return true;
   } catch(e) {
        fb.trace("WSH foo_httpcontrol: Error save playlist \n"+e);
        return false;
   }

}

function parse_httpcontrol(){   
    if(fso.fileExists(playlist_json)) {
      json_response_String = read_textfile(playlist_json);
      playlistObj = JSON.parse(json_response_String);
    }
}

function foo_httpcontrol(url, user_agent, func) {
   xmlhttp.open("GET", url, true);
   xmlhttp.setRequestHeader('User-Agent',user_agent);
    xmlhttp.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
   xmlhttp.send();
   xmlhttp.onreadystatechange = function() {
    if(debug) {
          switch( xmlhttp.readyState ) {
          case 0: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Uninitialized"); break;
          case 1: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Open"); break;
          case 2: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Sent"); break;
          case 3: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Receiving"); break;
          case 4: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Ready");
                  fb.trace("WSH foo_httpcontrol: Status "+xmlhttp.status);
          break;       
        } 
    }
      if (xmlhttp.readyState == 4) {
         if (xmlhttp.status == 200) {
            func();
         } else {
            fb.trace("WSH foo_httpcontrol: Connection Problems");
         }
      }
   }
    //window.Repaint();
}

// ---------------------------------------
// called from build_charts();
// ---------------------------------------
function get_lfm_charts() {
  var period = dateRangeObj[dateRangeIdx].lfm;
  if(username.length == 0 || api_key.length != 32) return(fb.trace("WSH Last.fm Charts: Can't contact Last.fm. Check your username / API KEY settings."));
  lastfm("&format=json&user=" + username + "&method=user."+lfm_chart[chart_mode]+"&period="+period, "foo_wsh_lastfm_charts", function() {save_file(chart_mode);});
}

function save_file(chart_mode) {
   xmlDoc = xmlhttp.responsetext;
    //fb.trace("JSON: get_lfm_charts"+lfm_chart_json);
   try {
      ts = fso.OpenTextFile(lfm_chart_json, 2, true, -1);
      ts.WriteLine(xmlDoc);
      ts.close();
   } catch(e) {
        fb.trace("WSH Last.fm Charts: Error save charts \n"+e);
   }
   parse_lfm_charts(chart_mode);
}

// ---------------------------------------
// build display array from json result
// ---------------------------------------
function parse_lfm_charts(chart_mode){
    if(fso.fileExists(lfm_chart_json)) {
        chartsArray = []; //reset array
        lfm_charts_json_string = read_textfile(lfm_chart_json);
        chartsObj = JSON.parse(lfm_charts_json_string);
      try{           
            var j=0;
            if(chart_mode == 157){
                for(var i in chartsObj.topartists.artist){
                    j++;
                    chartsArray.push([chartsObj.topartists.artist.playcount,chartsObj.topartists.artist.name]);
                    if(j == limit)
                        break;
                }
            }
          if(chart_mode == 158){
                for(var i in chartsObj.toptracks.track){
                    j++;
                    chartsArray.push([chartsObj.toptracks.track.playcount,chartsObj.toptracks.track.artist.name+" - "+chartsObj.toptracks.track.name]);
                    if(j == limit)
                        break;
                }
            }
          if(chart_mode == 159){
                for(var i in chartsObj.topalbums.album){
                    j++;
                    chartsArray.push([chartsObj.topalbums.album.playcount,chartsObj.topalbums.album.artist.name+" - "+chartsObj.topalbums.album.name]);
                    if(j == limit)
                        break;
                }
            }             
          }catch(e){
            fb.trace("JSON: Error Json");
        }     
    }else{
        fb.trace("JSON: no json file");
    }
    window.Repaint();
}


// ---------------------------------------
// paint track statistic
// called from on_paint()
// ---------------------------------------
function paint_stat(gr){
    var highestValueArray = [];
    var pc_data_length = pc_data2.length;
    var slice_x = statfield_ww/pc_data_length;
    highestValueArray = countArray;
    var maxpeak = highestValueArray.max();
    var minpeak = highestValueArray.min();
    var avgpeak = parseInt((maxpeak+minpeak)/2);
   
    var maxpeakPos = (statfield_wh*maxpeak)/maxpeak;
        maxpeakPos = (statfield_top+statfield_wh)-maxpeakPos;
    var minpeakPos = (statfield_wh*minpeak)/maxpeak;
        minpeakPos = (statfield_top+statfield_wh)-minpeakPos;
    var avgpeakPos = (statfield_wh*avgpeak)/maxpeak;
        avgpeakPos = (statfield_top+statfield_wh)-avgpeakPos;

    // default curvedraw
    var old_date_x = statfield_left;
    var old_peakPos = statfield_top+statfield_wh;
    var last_date_x = statfield_ww+15;
    var last_date_y = statfield_top+statfield_wh;
    ttMarks = [];
   
        gr.DrawLine(statfield_left, maxpeakPos, statfield_ww+15, maxpeakPos, 1, stat_line);
        gr.DrawLine(statfield_left, avgpeakPos, statfield_ww+15, avgpeakPos, 1, stat_line);
        gr.DrawLine(statfield_left, minpeakPos, statfield_ww+15, minpeakPos, 1, stat_line);
        gr.DrawString(maxpeak, s_font, stat_legend, 15, maxpeakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
        gr.DrawString(avgpeak, s_font, stat_legend, 15, avgpeakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
        gr.DrawString(minpeak, s_font, stat_legend, 15, minpeakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));

    //draw first zero point
    gr.DrawEllipse(old_date_x-2, old_peakPos-2, 4, 4, 1, stat_item_color);

    for (var i = 0; i != pc_data_length; i++){
        var date_w = 0;
        var peakPos = (statfield_wh*pc_data2['count'])/maxpeak;
        peakPos = (statfield_top+statfield_wh)-peakPos;
        //get date width
        date_w = gr.CalcTextWidth(pc_data2['date'], g_font);
        // add width to calc position on x axis
        date_x +=(i != 0)?slice_x:0;
        //fb.ShowConsole();
        var normalizedXPos = date_x+(slice_x/2);
        var isEven = function(someNumber){return (someNumber%2 == 0) ? true : false;};
        switch(isEven(pc_data_length)){
            case true:
                var modulo = parseInt(pc_data_length/8);
                var c = pc_data_length-1;
            break;
            default:
                var modulo = parseInt(pc_data_length/7);
                var c = pc_data_length;
        }
     
      //fb.trace(date_range);
        if(i%modulo==0 || i == c)
        gr.DrawString(pc_data2['date'], m_font, g_textcolor, normalizedXPos-(date_w/2), date_y, date_w, 18, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
       
        // draw points
        if(pc_data_length < 100)
        gr.DrawString(pc_data2['count'], s_font, stat_item_color, normalizedXPos-7, peakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
        gr.DrawEllipse(normalizedXPos-2, peakPos-2, 4, 4, 1, stat_item_color);
        gr.DrawLine(old_date_x, old_peakPos, normalizedXPos, peakPos, 1, stat_item_color)
       
        // draw circles from class
        var stc = (pc_data_length < 100)? stat_item_color : stat_item_t;
        var vp = (pc_data_length < 100)? 4 : 10;
        ttMarks.push(new ttMark(normalizedXPos-2, peakPos-2, vp, vp, 1,stc,pc_data2['count'], null,pc_data2['count']+" / "+pc_data2['date']+"/"+pc_data2['artist']));
       
        var old_date_x = normalizedXPos;
        var old_peakPos = peakPos;
    }
   
    //draw last points
    gr.DrawLine(old_date_x, old_peakPos, last_date_x, last_date_y, 1, stat_item_color);
    gr.DrawEllipse(last_date_x-2, last_date_y-2, 4, 4, 1, stat_item_color);
   
    var meta = sprintf(lang.stat_meta_text.def,highestValueArray.sum(),fp,lp);
    gr.DrawString(meta, g_font, stat_meta_color, 6, 34, ww-16, 16, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
}

function on_mouse_move(x, y) {
    var ttm = ttMarksTraceMouse(x, y);
    window.SetCursor((y > 8 && y < 26 && x < ww-60 ) || (check_enable_playlist() && (x > ww-60 && x < ww-10 && y > 8 && y < 26)) || ttm ? IDC_HAND : IDC_ARROW);

    if (ttm != cur_ttm) {
        cur_ttm && cur_ttm.onMouseOut();
        ttm && ttm.onMouseIn();
    }

    if (ttm != ttm_down) {
        ttm_down = null;
    }

    cur_ttm = ttm;
}

// ---------------------------------------
// paint bar charts
// called from on_paint()
// ---------------------------------------
function paint_charts(gr){
    gr.FillSolidRect(statbox_left, statbox_top, statbox_ww, statbox_wh, g_backcolor);

    var chartslength = chartsArray.length;
    var row_h = (statfield_wh-10)/chartslength;
 //  var row_h = 20;
    var row_y_pos = (statfield_top-0);
    var maxPeak = chartsArray[0][0];
    var bar_max_w = statfield_ww-180;
    var bar_left = statfield_left+200;
    //fb.trace(maxPeak);
   
    // get build array from count values
    var maxValueArray = new Array();
    for (i = 0; i != chartslength; i++){
        maxValueArray.push(chartsArray[0]);
    }
   
    // find min count to set min barsize.
    var min_widthValue = maxValueArray.max();
    var min_width = gr.CalcTextWidth(min_widthValue, g_font);
  // min_width = 135;
   
    switch(chart_mode){
        case 150:
        case 154:
        case 157:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 150, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(200, row_y_pos, bar_width, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 205, row_y_pos, bar_width-10, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
        break;
        case 151:
        case 158:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                var chartsitem = chartsArray[1].split(" - ");
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 275, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(300, row_y_pos, bar_width-100, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 305, row_y_pos, bar_width-10, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
        break;
        case 152:
        case 159:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                var chartsitem = chartsArray[1].split(" - ");
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 275, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(300, row_y_pos, bar_width-100, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 305, row_y_pos, 20, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }           
        break;
        case 153://break;
      case 160:
        default:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 10, row_y_pos, 150, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(200, row_y_pos, bar_width, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 205, row_y_pos, bar_width-10, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
        }
}

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

function on_paint(gr) {
    //fb.trace("on_paint");
   offset = Math.round(offset);
   gr.SetTextRenderingHint(5);

   
    statbox_left = 60;
    statbox_top = 40;
    statbox_ww = ww-16;
    statbox_wh = wh-68;
   
    textheight = 0;
    pos = statbox_top;
    date_x = statbox_left+10;
    date_y = statbox_wh+statbox_top-20;
   
    statfield_left = statbox_left+10;
    statfield_top = statbox_top+20;
    statfield_ww = statbox_ww-20;
    statfield_wh = statbox_wh-10;
   
    headline_left = 30;
   
    point_x = 0;
    point_y = 0;
   
    // stat background
   gr.FillSolidRect(statbox_left, statbox_top, statbox_ww, statbox_wh, stat_bg_color);
    gr.DrawImage(stat_img, 6, 6, 18, 18, 0, 0, 18, 18);
    if(g_metadb) {

        if(fso.fileExists(db_file) || username.length > 0 && api_key.length == 32){

            if(show_charts === 1){
            // show all charts
                if(chartsArray.length > 0){
                    if(chartsArray.length == 1 && chartsArray[0][0] == 0){
                    gr.DrawString(lang.no_stat, g_font, g_textcolor, statbox_left, statbox_top, statbox_ww, statbox_wh, StringFormat(StringAlignment.Center,StringAlignment.Center));
                        }else{
                        paint_charts(gr);
                        }
                }
            }else{
            // show trackstat
                if(pc_data.length > 0){
                    // check if real data not from fake array
                    if(pc_data.length == 1 && pc_data[0]["count"] == 0){
                        gr.DrawString(lang.no_stat, g_font, g_textcolor, statbox_left, statbox_top, statbox_ww, statbox_wh, StringFormat(StringAlignment.Center,StringAlignment.Center));
                    }else{
                        //draw markers
                        paint_stat(gr);
                        ttMarksDraw(gr);
                    }
                }else{
                    gr.DrawString(lang.no_stat, g_font, g_textcolor, statbox_left, statbox_top, statbox_ww, statbox_wh, StringFormat(StringAlignment.Center,StringAlignment.Center));
                }
            }

            stat_headline = "";
            meta = "";
            var _date_range = dateRangeObj[dateRangeIdx].text;
            if(show_charts === 1){
                switch(chart_mode){
                    case 150:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 151:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 152:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 153:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],artist,_date_range);
                    break;
                    case 154:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 155:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],artist,album,_date_range);
                    break;
                    case 156:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],artist,_date_range);
                    break;
                    case 157:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 158:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;           
                    case 159:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 160:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],sort1,_date_range);
                    break;
                    default:
                        stat_headline += sprintf(lang.chart_mode_text.def,_date_range);
               
                }
               
                meta += lang.chart_meta_text.def;
            }else{
                stat_headline = sprintf(lang.stat_mode_text[which_stat],artist,title,album);           
            }
            // draw headline
            if(check_enable_playlist())
                gr.DrawImage(playlist_img, ww-28, 6, 18, 18, 0, 0, 18, 18);
            gr.DrawString(stat_headline, bb_font, g_textcolor_hl, headline_left, 6, ww-headline_left-16,18, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
            gr.DrawLine(5, 29, ww-10, 29, 1, g_color_hl);
            // draw meta
        }else{
            gr.DrawString(stat_headline, bb_font, g_textcolor_hl, headline_left, 6, ww-headline_left-16,18, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
          gr.DrawLine(5, 29, ww-10, 29, 1, g_color_hl);
            gr.DrawString(meta, g_font, stat_meta_color, 6, 34, ww-16, 16, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
            gr.DrawString(stat_install_info, g_font, g_textcolor, statfield_left, statfield_top, statfield_ww, statfield_wh,StringFormat(StringAlignment.Near,StringAlignment.Near)); 
       
        }
    } else {
        gr.DrawString(stat_headline, bb_font, g_textcolor_hl, headline_left, 6, ww-headline_left-16,18, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
        gr.DrawLine(5, 29, ww-10, 29, 1, g_color_hl);
        gr.DrawString(meta, g_font, stat_meta_color, 6, 34, ww-16, 16, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
      gr.DrawString("no selection", g_font, g_textcolor, 0, 0, ww, wh,StringFormat(StringAlignment.Center,StringAlignment.Center));
   }
   
}

function on_metadb_changed() {
    if(!g_metadb) return(window.Repaint());
    var length = fb.TitleFormat("[%length%]").Eval();
    artist = fb.TitleFormat(tf.artist).EvalWithMetadb(g_metadb);
    title = fb.TitleFormat(tf.title).EvalWithMetadb(g_metadb);
    album = fb.TitleFormat(tf.album).EvalWithMetadb(g_metadb);
    artist_id = fb.TitleFormat(tf.artist_id).EvalWithMetadb(g_metadb);
    album_id = fb.TitleFormat(tf.album_id).EvalWithMetadb(g_metadb);
    id = fb.TitleFormat(tf.id).EvalWithMetadb(g_metadb);
    rdate = fb.TitleFormat(tf.date).EvalWithMetadb(g_metadb);
 
  if(length.length == 0 && fb.PlaybackTime > 0.1){
        artist = "Radio";
        album  = "Radio";
        artist_id = fb.TitleFormat("$crc32(radio)").EvalWithMetadb(g_metadb);
        album_id = fb.TitleFormat("$crc32(radio)").EvalWithMetadb(g_metadb);
    }

    pc = fb.TitleFormat(tf.playcount).EvalWithMetadb(g_metadb);
    fp = fb.TitleFormat(tf.first_played).EvalWithMetadb(g_metadb);
    lp = fb.TitleFormat(tf.last_played).EvalWithMetadb(g_metadb);
    sort1 = fb.TitleFormat(tf.sort1).EvalWithMetadb(g_metadb);
    sort2 = fb.TitleFormat(tf.sort2).EvalWithMetadb(g_metadb);
    tracktime = fb.TitleFormat(tf.tracktime).EvalWithMetadb(g_metadb);
 
    // mask for sql inserts
    album_sql = album.replace(/"/g,"\\\"");
    album_sql = album_sql.replace(/'/g,"''");
    title_sql = title.replace(/"/g,"\\\"");
    title_sql = title_sql.replace(/'/g,"''");
    artist_sql = artist.replace(/"/g,"\\\"");
    artist_sql = artist_sql.replace(/'/g,"''");
   
    sort1_sql = sort1.replace(/"/g,"\\\"");
    sort1_sql = sort1_sql.replace(/'/g,"''");
    sort2_sql = sort2.replace(/"/g,"\\\"");
    sort2_sql = sort2_sql.replace(/'/g,"''");
     
    // show charts on itemFocusChange but only if it's not playing
    if(! fb.IsPlaying){
        //if(debug) fb.trace("----- Force Show Charts");
        show(show_charts);
    }   
  window.Repaint(); 
}

// lean from marc2000 lfm script

function on_playback_new_track() {
   time_elapsed = 0;
   switch(true) {
      case (fb.PlaybackLength == 0):
         target_time = 240;
         break;
      case (fb.PlaybackLength >= 30):
         target_time = Math.min(Math.floor(fb.PlaybackLength / 2),240);
         break;
      default:
         target_time = 5;
   }
   on_item_focus_change();
    show(show_charts);
}

function on_playback_time(time) {
   time_elapsed++;
   if(time_elapsed == target_time) {
        //on_item_focus_change();
        playcount();
        show(show_charts);
    }
}

// ---------------------------------------
// show and generate bar charts
// ---------------------------------------

function build_charts(){
    if(fso.fileExists(db_file)){   
      try{ 
        var date_range = dateRangeObj[dateRangeIdx].sql;
        var dateRangeSqlStr = (date_range)? 'datetime(PLAYTIME) > datetime(\'now\',\''+date_range+'\',\'localtime\')' : "1=1" ;
            if(show_charts === 1){
                var query = "";
                switch(chart_mode){
                    case 150:
                    query = '\"SELECT COUNT(ARTISTID) AS ARTISTCOUNT,ARTIST FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY artistid ORDER BY artistcount DESC LIMIT '+limit+';\"';
                    break;
                    case 151:
                    query = '\"SELECT COUNT(TRACKID) AS TRACK_COUNT ,ARTIST || \' - \' || TITLE, TRACKID  FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY ARTIST, TITLE ORDER BY TRACK_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 152:
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT ,ARTIST || \' - \' || ALBUM  FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY ALBUMID, ARTIST ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 153:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT, ALBUM  FROM playcount WHERE ARTISTID = '+artist_id+' AND '+dateRangeSqlStr+' GROUP BY ALBUMID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 155:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(TITLE) AS TITLE_COUNT, TITLE FROM playcount WHERE ARTISTID = '+artist_id+'  AND ALBUMID = '+album_id+' AND '+dateRangeSqlStr+' GROUP BY TITLE ORDER BY TITLE_COUNT DESC, PLAYTIME DESC LIMIT '+limit+';\"';
                    break;
                    case 156:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(TITLE) AS TITLE_COUNT, TITLE FROM playcount WHERE ARTISTID = '+artist_id+' AND '+dateRangeSqlStr+' GROUP BY TITLE ORDER BY TITLE_COUNT DESC, PLAYTIME DESC LIMIT '+limit+';\"';
                    break;
                    case 154:
                    query = '\"SELECT COUNT(sort1) AS SORT1_COUNT, sort1 FROM playcount WHERE '+dateRangeSqlStr+'  GROUP BY sort1 ORDER BY SORT1_COUNT DESC LIMIT '+limit+';\"';
                    break;
                case 160:
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT, ALBUM  FROM playcount WHERE sort1 = '+sort1+' AND '+dateRangeSqlStr+' GROUP BY ALBUMID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 157: // query lfm charts
                    case 158:
                    case 159:
                        get_lfm_charts();
                        return;
                    break;
                    default:
                    query = '\"SELECT COUNT(ARTIST) AS ARTISTCOUNT,ARTIST FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY artistid ORDER BY artistcount DESC LIMIT '+limit+';\"';           
                }
               
                if(debug){
                    fb.trace("++++SQL // START - Mode: "+chart_mode);
                    fb.trace(query);
                    fb.trace("++++SQL // END");
                }
               
                if(query.length > 0){
                    WshShell.Run(batch_cmd + query + ' > "'+chart_file+'"',0,true);   
                    if(fso.fileExists(chart_file)) {
                        var charts_table = read_textfile(chart_file);                 
                        chartsArray = csvArray(charts_table);
                    }
                }
            }else{
                CollectGarbage();
            }
        }catch(e){
            fb.trace("WSH Charts: build charts went wrong "+e);
        }
    }else{
        if(debug) fb.trace('No Custom Playcount DB (Build Charts)');
        get_lfm_charts();
    }
    window.Repaint();
}


// ---------------------------------------
// generate statistic
// ---------------------------------------
function build_stat(){
if(fso.fileExists(db_file)){
    var date_range = dateRangeObj[dateRangeIdx].sql;
    var dateRangeSqlStr = (date_range) ? 'datetime(a.PLAYTIME) > datetime(\'now\',\''+date_range+'\',\'localtime\')' : "1=1";
    var minDateSqlStr = (date_range) ? 'date(\'now\',\''+date_range+'\',\'localtime\')' : "b.MIN_QUERYDATE" ; // MIN_QUERYDATE is the row of MIN_DATE SQL view!
    var minDateSqlView = (date_range) ? '':', MIN_DATE b';
   
    var query = '\"SELECT COUNT(a.TRACKID) AS COUNTER, date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE FROM playcount a '+minDateSqlView+' WHERE a.TRACKID = '+id;
    query += ' AND '+dateRangeSqlStr+' GROUP BY PT;\"';
   
    // album Query
    if(which_stat == 2){
        query = '\"SELECT COUNT(a.ALBUMID) AS COUNTER, date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE FROM playcount a '+minDateSqlView+' WHERE a.ALBUMID = \''+album_id+'\'';
        query += ' AND '+dateRangeSqlStr+' GROUP BY PT;\"';
    }
   
    // artist Query
    if(which_stat == 3){
        query = '\"SELECT COUNT(a.ARTISTID) AS COUNTER, date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE FROM playcount a '+minDateSqlView+' WHERE a.ARTISTID = \''+artist_id+'\'';
        query += ' AND '+dateRangeSqlStr+' GROUP BY PT;\"';
    }
    // Top per Day
    if(which_stat == 4){
        query = '\"SELECT a.artistcount AS COUNTER,date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE, a.artistname  FROM TOP_ARTIST_PER_DAY a '+minDateSqlView;
        query += ' WHERE '+dateRangeSqlStr+' GROUP BY PT;\"';
    }
    if(debug){
        fb.trace("++++SQL // START");
        fb.trace(query);
        fb.trace("++++SQL // END");
    }
    try{
        // write query result to file
        WshShell.Run(batch_cmd + query + ' > "'+os_escape(pc_file)+'"',0,true);     
    } catch(e){
        fb.trace('WSH Charts: error writing to sqlite');
    }

    // try load the file
    if(fso.fileExists(pc_file)) {
        var pc_datastring = read_textfile(pc_file);
        var pc_dataArrayRows = [];
        pc_data = [];
        pc_data2 = [];
        countArray = [];
           
            try {
                if(pc_datastring.length > 0) {
                    pc_dataArrayRows = pc_datastring.split("\n");
                   
                    for (i = 0; i != pc_dataArrayRows.length-1; i++)
                    {
                        pc_dataArray = pc_dataArrayRows.split("|");
                        // set start date
                        pc_start_date = pc_dataArray[2];
                        pc_data = new Array();
                        //pc_data1 = new Array();
                        pc_data["date"] =  pc_dataArray[1];
                        pc_data["count"] =  pc_dataArray[0];
                        pc_data["artist"] =  pc_dataArray[3];
                        countArray.push(parseInt(pc_dataArray[0]));
                    }
                    //fb.trace(countArray);
                    // build datearray
                    var tempStartDate = pc_start_date.split("-");
                    // fb.trace("---"+tempStartDate);
                   
                    var startDate = new Date(tempStartDate[0],tempStartDate[1]-1,tempStartDate[2]);
                    var endDate = new Date();
                    //fb.trace("---"+startDate);

                    var newDate = startDate;
                    dateStrings = [];

                    while (newDate <= endDate){
                      str = newDate.getFullYear() + "-" +
                                lz(newDate.getMonth() + 1) + "-" +
                                lz(newDate.getDate());       
                      dateStrings.push(str);
                      newDate.setDate(newDate.getDate()+1);
                    }
                   
                   
                    for (i = 0; i != dateStrings.length; i++){
                       
                        pc_data2 = [];
                        for (j = 0; j != pc_data.length; j++){
                         
                            if(dateStrings == pc_data[j]["date"]){
                                pc_data2["date"] = pc_data[j]["date"];
                                pc_data2["count"] = pc_data[j]["count"];
                                pc_data2["artist"] = pc_data[j]["artist"];
                                break;
                            }else{
                                pc_data2["date"] = dateStrings;
                                pc_data2["count"] = 0;
                                pc_data2["artist"] = 'undefined';
                            }
                        } 
                    }
                   
                }else{
                  if(debug) fb.trace("WSH Charts: making fake array?");
                }
            } catch(e) {
                fb.trace('WSH Charts: error open playcount.txt'); 
            }
    }else{
        if(debug) fb.trace('WSH Charts: no playcount.txt');       
    }
}else{
    if(debug) fb.trace('No Custom Playcount DB (Build Statistic)');
}
    window.Repaint();
}

// ---------------------------------------
// track playcount to playcount.sqlite
// ---------------------------------------

function playcount(){
if(fso.fileExists(db_file)){
    fb.trace('WSH Charts: set playcount for:' + artist + " - " + title);
    var pt = playtime('datetime');
    // TODO - let Playtime calculate by sqlite on insert. query must be mit date(...,'localtime') because sqlite insert standard time without timezones
    var query  = 'INSERT INTO playcount(TRACKID,ARTIST,TITLE,ALBUM,FP,LP,PLAYTIME,PC,ARTISTID,ALBUMID,TRACKTIME,SORT1,SORT2) VALUES(\'' + id + '\',\''+artist_sql+'\',\''+title_sql+'\',\''+album_sql+'\',\''+fp+'\',\''+lp+'\',\''+pt+'\',\''+Number(pc)+'\',\''+artist_id+'\',\''+album_id+'\',\''+tracktime+'\',\''+sort1_sql+'\',\''+sort2_sql+'\');';
        query += 'INSERT OR REPLACE INTO artist (artistname,artistid) VALUES (\''+artist_sql+'\','+artist_id+'\');';
        query += 'INSERT OR REPLACE INTO album (albumname,albumid,artistid,year) VALUES (\''+album_sql+'\','+album_id+','+artist_id+',\''+rdate+'\');';
   
    if(debug){
        fb.trace("++++SQL // START");
        fb.trace(query);
        fb.trace("++++SQL // END");
    }
    try{
        WshShell.Run('sqlite3.exe "' + os_escape(db_file) + '" "'+query+'"', 0, true);
    } catch(e){
        fb.trace('SQLITE: '+e);
        fb.trace('SQLITE: '+query);
    }
}else{
    if(debug) fb.trace('WSH Charts: No Custom Playcount DB (Playcount)');
}   
}

function on_mouse_lbtn_up(x, y) {
    // playlist button
    if(check_enable_playlist()){
        if(x > ww-28 && x < ww-10 && y > 8 && y < 26){
            build_playlist();
        }
    }
   
    if(y > 8 && y < 26 && x < ww-28 ) {
   
   var _menu = window.CreatePopupMenu();
   var _child = window.CreatePopupMenu();
    var _date = window.CreatePopupMenu();
    var _limit = window.CreatePopupMenu();
   var idx;

    for (i=0;i<dateRangeObj.length;i++)
    {
      _date.AppendMenuItem(MF_STRING, 110 + parseInt(i), dateRangeObj.text);
   }
    _date.CheckMenuRadioItem(110, 115, window.GetProperty("date_range",110));
   
    for (i=0;i<limitRangeArray.length;i++)
    {
      _limit.AppendMenuItem(MF_STRING, 120 + parseInt(i), limitRangeArray);
   }
    _limit.CheckMenuRadioItem(120, 125, window.GetProperty("limit_range",120));
   
   
    _menu.AppendMenuItem(MF_DISABLED, 0, "Custom Statistic");
   
    if(fso.fileExists(db_file))
    {
        _menu.AppendMenuItem(MF_STRING, 1, "Show track statistic");
        _menu.AppendMenuItem(MF_STRING, 2, "Show album statistic");
        _menu.AppendMenuItem(MF_STRING, 3, "Show artist statistic");
        _menu.AppendMenuItem(MF_STRING, 4, "Top per day statistic");
        _menu.CheckMenuRadioItem(1, 4, window.GetProperty("which_stat",1));
       
        _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);

        _menu.AppendMenuItem(MF_STRING, 150, "Show artist charts");
        _menu.AppendMenuItem(MF_STRING, 151, "Show artist-track charts*");
        _menu.AppendMenuItem(MF_STRING, 152, "Show artist-album charts");
        _menu.AppendMenuItem(MF_STRING, 153, "Show most played album from artist");
        _menu.AppendMenuItem(MF_STRING, 155, "Show most played track from artist album*");
        _menu.AppendMenuItem(MF_STRING, 156, "Show most played track from artist*");
        _menu.AppendMenuItem(MF_STRING, 154, "Show Genres from charts")

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #30
I've figured it out for myself. Apparently the field defined in the where clause of the sql string needed to be an integer instead of string. Don't know why that is since this isn't necessary outside jscript, probably doing something wrong but I got what I wanted so I'm happy.

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #31
I've figured it out for myself. Apparently the field defined in the where clause of the sql string needed to be an integer instead of string. Don't know why that is since this isn't necessary outside jscript, probably doing something wrong but I got what I wanted so I'm happy.

Could you please post the fixed script??

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #32
The script is essentially the same. The changes were made in the playcount.sqlite database. Initially the sort2 field in the table was a string to indicate the main style like Rock Music, Pop Music, Classical.... but I used that field, changed it to an integer field instead of varchar and filled those with integer values that represent the subgenres in the sort1 field (for example Experimental Rock get's the value 65442311, Grunge Rock is 45213132, Electropop is 345642121,....).
I used this formatting pattern in masstagger to asign unique integer values to each genre:
Code: [Select]
$if2(%Genre_id%,$crc32($lower([%genre%])))


So in Jscript this does not work (sort1 is a string value):
Code: [Select]
query = '\"SELECT COUNT(TRACKID) AS TRACK_COUNT,ARTIST || \' - \' || TITLE  FROM playcount WHERE sort1 =  '+sort1+' AND '+dateRangeSqlStr+' GROUP BY TITLE, ARTISTID ORDER BY TRACK_COUNT DESC LIMIT '+limit+';\"';

But this does (sort2 is an integer value):
Code: [Select]
query = '\"SELECT COUNT(TRACKID) AS TRACK_COUNT,ARTIST || \' - \' || TITLE  FROM playcount WHERE sort2 =  '+sort2+' AND '+dateRangeSqlStr+' GROUP BY TITLE, ARTISTID ORDER BY TRACK_COUNT DESC LIMIT '+limit+';\"';


I googled for a solution and someone using javascript had a similar problem and his solution was to use extra quotes in the where clause for string values like this: WHERE sort2 =  '"+sort2+"'
But this too does not work.

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #33
This is the script I use with the modifications I managed to get done. I added marc2003's mouse scroll part to it, it doesn't scroll really but it prevents the charts from stretching vertically if the total vertical area isn't filled with enough entries (which is much nicer visually imho). Some font and background color changes and also the way charts are scaled are different. The genre tag value is written to the playcount table instead of the artist table which allows to better represent charts when an artist is active in multiple genres. Album charts are represented  the same way last.fm does it (on an artist and album basis instead of only album). Added 3 extra queries to give bar charts that show favorite artists of genre played, favorite albums of genre played and favorite tracks of genre played. The length of each track is also saved to the database (intended for future display charts based on total length count instead of amount of plays). If you use this be sure to make the necessary changes to the playcount.sqlite database so that the script can save the values in the necessary columns (i think you only need to add a tracktime column to playcount table).


[code]// ==PREPROCESSOR==
// @import "%fb2k_path%scripts\common.js"
// ==/PREPROCESSOR==
var version = "1.0 beta5";
/*
    version 1.0 beta5
        optimize customization vars a bit (colors/fonts)
        loading one common.js file
    version 1.0 beta4
        customized install routine
    version 1.0 beta3
        some cleanup, adding sprintf()
        set titleformat and lang strings as object for easier edit.
    version 1.0 beta2
        query the playlist, write it to json because playlistObj go nowhere
        read json, sort it from chartArray write m3u - load it with foobar
    version 1.0 beta
        generate playlist from different charts (artist/title)
        playlist is generated by serarchquery of foo_httpcontrol.
        No SORT possible, right now.
        Playlist could be generated via javascript/json.
        result could be parsed and sorted right, writing m3u playlist   
    version 0.9.0
        add install routine to create playcount.sqlite
        drop batchfile using %comspec% /c sqlite3.exe ...
        simplify reading charts.txt using function
    version 0.8.5
        add meta total played to stat!
    version 0.8
        add overall to track stat, using an Sqlite SQL View Table!
        fix display longer timespans in statistic
    version 0.7
        add last.fm charts finally
        cleanup/messup code
    version 0.6
        add Tooltip to points, finally
        points generated by class
    version 0.5
        add Radio counting by station
        fix "?" for untagged parts to NULL and write them as undefined in DB
    version 0.4
        add correct scrobbling behaviour and set playcount after 50% or 4 minutes (lean by marc2000)
    version 0.3
        add album, tracks from album, tracks by current artist to charts
        remove writing daily playcount.txt (flatfile db)
        add menu to mouse leftclick
    version 0.2
        add SORT1 Query for charts
    version 0.1
        initial release
   
    TODO:
    set meta for charts (average track etc)
    - var modulo = depending on daterange woche = 7 / monat/jahr = 30 / need to display dates on x axsis
    - easier setup.
    - make playlist work on first try!
   
*/

// CUSTOM CHANGES START
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Set fb2k titleformatsyntax here
 */

/*
    Note:
    define your titleformat for artist,album,title here
    the data to each track in db is stored as readable text.
    for grouping data, we generate unique numbers/id from artist, album, to ensure
    displaying after changing lower/uppercase of these fields.
    But if you change artist/title/album we can't group correctly anymore
    So this is where %FOO_..._ID% comes in.
    But these values must be hardwired to each track as id3tag
    Same goes for %FINGERPRINT_FOOID% a unique acoustic fingerprint.
    If they are present you can change your Tags and statistic keeps track and can group
    by artist/album correctly




*/

var tf ={  artist:"$trim($if2(%artist%,undefined))",
            artist_id:"$if(%FOO_ARTIST_ID%,%FOO_ARTIST_ID%,$crc32($lower([%artist%])))",
            album:"$trim($if2(%album%,undefined))",
            album_id:"$if(%FOO_ALBUM_ID%,%FOO_ALBUM_ID%,$crc32($lower([%album%])))",
            title:"$trim($if2(%title%,undefined))",
            id:"$if(%FINGERPRINT_FOOID%,$crc32(%FINGERPRINT_FOOID%),$crc32($lower([%artist%][%album%][%title%])))",
            date:"[%date%]",
            playcount:"[%play_counter%]",
            first_played:"[%first_played%]",
            last_played:"[%last_played%]",
            tracktime:"[%length%]",
            sort1:"[%genre%]", // sort1 and 2 are mapped to playcount table
            sort2:"[%genre_id%]"         
        };
/*
    Some language strings
*/
var lang = {
          no_stat:"No Stats at the moment, but they will be generated",
          chart_mode_text:{  150:"Overall artist charts for %s",
                        151:"Most played tracks for the last %s",
                        152:"Most played albums for the last %s",
                        153:"Most played albums by %s for the last %s",
                        154:"Favorite genre for the last %s",
                        155:"Most played tracks of %s's album ''%s'' for the last %s",
                        156:"Most played tracks of %s for the last %s",
                  160:"Favorite %s albums of %s",
                        161:"Favorite %s tracks of %s",
                        172:"Favorite %s artists of %s",
                        157:"Last.fm artist charts for the last %s",
                        158:"Last.fm most played tracks for the last %s",
                        159:"Last.fm most played albums for the last %s",
                        def:"Charts for %s"                     
                      },
          chart_meta_text:{ def:"Meta (Query Avarage Track Playing per day and total over time)"
                      },
          stat_mode_text:{  1:"Statistic for ''%2$s'' by %1$s",
                        2:"Statistic for album ''%3$s'' by %1$s",
                        3:"Statistic for %1$s",
                        4: "Top per day"
                      },
          stat_meta_text:{  def:"Total played %f times - first played %s - last played %s"
                        }
}

var stat_headline = "Charts & Statistics";
var meta = "by muzack, version "+version;
var stat_install_info = "Click on the top headline to get the menu.\nSetup your last.fm username and api key to retrive your last.fm statistics\nSetup a custom playcount statistic database to track your plays locally and get some nice realtime graphs. (experimental!)";


/*
    Color & Font Settings
*/
var stat_item_color = RGB(113,183,230); // element color
var stat_bg_color = RGB(28,28,28); // white
var stat_hl_color = RGB(255,255,255); // white
var stat_line = RGB(230,230,230); // light grey
var stat_legend = RGB(200,200,200); // grau
var stat_item_t = RGBA(200,10,200,0); // grau
var stat_meta_color = RGB(150,150,150); // grey
var g_textcolor_hl = RGB(141,213,225); // Head Text Color
var g_color_hl = RGB(203,203,203);

var m_font = gdi.Font("Segoe UI", 8, 1); // micro font-size
var s_font = gdi.Font("Segoe UI", 9, 1); // small font-size
var n_font = gdi.Font("Tahoma", 9, 0); // normal font-size
var nb_font = gdi.Font("Lucida Grande", 9, 0); // normal font-size, bold
var bb_font = gdi.Font("Segoe UI", 13, 1); // big font, bold

// CUSTOM CHANGES END - Do not edit below this, unless you know what you do!
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

var debug = window.GetProperty("debug",0);

// tooltip Marks
var g_tooltip = window.CreateTooltip();
var g_down = false;
var ttm_down = null;
var cur_ttm = null;
var ttMarks = [];

// standard values for stat/charts
// date range
var dateRangeObj = [{text:"one week",sql:"-7 days",lfm:"7day"},
                {text:"one month",sql:"-1 months",lfm:""},
                {text:"three months",sql:"-3 months",lfm:"3month"},
                {text:"six month",sql:"-6 months",lfm:"6month"},
                {text:"one year",sql:"-1 years",lfm:"12month"},
                {text:"overall",sql:"",lfm:"overall"}];
var dateRangeIdx = window.GetProperty("date_range",110) - 110;
// limit charts
var limitRangeArray = ["20", "30", "40","50"];
var limit = limitRangeArray[window.GetProperty("limit_range",120) - 120];

// which_stat = Current Track based statistic
// 1 = track, 2=album, 3=artist, 4 Top per day
var which_stat = window.GetProperty("which_stat",1);

// show bars = 1/show valley = 0
var show_charts = window.GetProperty("show_charts",1);
// chart_mode = artist/album/trackcharts
var chart_mode = window.GetProperty("chart_mode",150);
// lfm chart mode api call
var lfm_chart = {157:"gettopartists",158:"gettoptracks",159:"gettopalbums"};

// files & paths
var sqlite3exe = fb.FoobarPath+'sqlite3.exe';
var db_file = fb.ProfilePath+'playcount.sqlite';
var init_sql = script_path + 'custom_playback_statistic\\playcount-struct.sql';
var info_txt = script_path + 'custom_playback_statistic\\install-custom-playcount-info.txt';
var batch_cmd = '%comspec% /c sqlite3.exe "' + os_escape(db_file) + '" ';
var pc_file = data_folder + 'playcount.txt';
var pc_file_test = data_folder + 'playcount.txt';
var chart_file = data_folder + 'charts.txt';
var lfm_chart_json = data_folder + "lfm-chart.json";

var username_file = settings_path + "username";
var username = read(username_file);
var api_key_file = settings_path + "api_key";
var api_key = read(api_key_file);

// data arrays
var pc_data = [];
var chartsArray = [[0,'unknown']];
var chartsObj = {};

// playlist generator
var foo_httpcontrol_exist = utils.CheckComponent("foo_httpcontrol", true);
var playlistEnabledArr = ["151","155","156"]; // which stat can produce playlists
var playlist_query_url = 'http://127.0.0.1:8888/Query/';
var playlistObj = {};
var playlistSortArray = [];
var playlist_json = data_folder + "playlist.json";
var playlist_m3u = data_folder + "playlist.m3u";
var playlist_img = gdi.Image(images_path + "playlist.png");
var stat_img = gdi.Image(images_path + "stat.png");
// test
//var chartsArray = csvArray("2|Inouk ~ No Danger|350263707\n2|Inouk ~ Elected|594016820\n2|Inouk ~ With The Birds|2574961760\n2|Inouk ~ Somewhere In France|3721457220\n1|Inouk ~ Victory|62160163\n1|Uke Of Spaces Corners County ~ This Old World|311760166\n1|Motorama ~ Letter Home|1144510168\n1|Uke Of Spaces Corners County ~ Bird On A Wire|1178016057\n1|Uke Of Spaces Corners County ~ Flotilla|1791777379\n1|Motorama ~ Compass|2013908431\n");


// helper
var offset = 0;
var time_elapsed = 0;
var target_time = 0;

// ---------------------------------------
// let the charts begin!
// ---------------------------------------
on_item_focus_change();
show(show_charts);
// ---------------------------------------
// -- FUNCTIONS
// ---------------------------------------

function init_playcount(){
   
    if(!fso.fileExists(sqlite3exe)){
        var BtnCode2 = WshShell.Popup("sqlite3.exe not found in foobar2000 program folder.\nPress Ok to open the folder.", 0 ,"Error",1+16);
        switch(BtnCode2) {
                    case 1: //ok
                        WshShell.Run('"'+os_escape(fb.FoobarPath)+'"');
                        fb.trace("SQLite.exe not in "+fb.FoobarPath);
                        return false;                       
                    break;
                    case 2: //cancel
                        return false;
                    break;
                }
       
        return false;
    }
   
   
    if(!fso.fileExists(db_file)) {
        try{
            // keep window open for msg
            //WshShell.Run('%comspec% /k sqlite3.exe "' + os_escape(db_file) + '" < "' + os_escape(init_sql) + '"', 0, true);
            //test for sqlite3.exe
            var BtnCode = WshShell.Popup("Install custom playcount statistic database?", 0 ,"Setup",4+32);
                switch(BtnCode) {
                    case 6: //yes
                        WshShell.Run(batch_cmd + ' < "' + os_escape(init_sql) + '"', 0, true);             
                    break;
                    case 7: //no
                        return false;
                    break;
                }
            var i = read_textfile(info_txt);
            if(init_playcount()) fb.ShowPopupMessage(i,"Custom playcount statistic info");
        } catch(e){
            fb.trace('SQLITE: Error creating DB!');
            return false;
        }
    }else{
        fb.trace("SQLITE: DB already exist!");
        return true;
    }
}


function disable_custom_playcount(){
   
    var BtnCode = WshShell.Popup("Disable custom playcount statistic?", 5 ,"Setup",4+32);
    switch(BtnCode) {
        case 6: //yes
            // rename playcount db file!
            var error = 0;
            try{fso.MoveFile(db_file, fb.ProfilePath+'playcount.disabled.'+playtime("datetime")+'.sqlite')}catch(e){error = 1};
            try{fso.DeleteFile(pc_file)}catch(e){};
            try{fso.DeleteFile(chart_file)}catch(e){};
            if(error == 1)
                fb.ShowPopupMessage('Custom playcount DB cannot be renamed to playcount.disabled.'+playtime()+'.sqlite - please rename or delete it manually');
            else
                fb.ShowPopupMessage('Custom playcount DB renamed to playcount.disabled.'+playtime()+'.sqlite');
            return (window.Repaint());
        break;
        case 7: //no
            return false;
        break;
    }
}

function show(mode){
  return (mode == 1) ? build_charts() : build_stat();
}

function on_playback_stop(reason) {
   //fb.trace("Playback Stop "+reason);
}

function on_notify_data(name, data) {
   if(name == "lastfm_update" && data == 1) {
      username = read(username_file);
      api_key = read(api_key_file);
      get_lfm_charts();
        window.Repaint();
   }
}

// ---------------------------------------
// build statistic based playlist via foo_httpcontrol
// ---------------------------------------

function check_enable_playlist(){
return (foo_httpcontrol_exist && playlistEnabledArr.exists(chart_mode) && show_charts == 1);
}

function build_playlist(){
    var playlist_queryArray = [];
    var playlist_sortArray = [];
    var playlist_query = '';
    var m3u ='';
    var url='';
    var url_param = "?cmd=Search&param3=state.json&param1=";

    if(chartsArray.length > 0){
        for (var i = 0; i != chartsArray.length; i++){
            if(chartsArray[2] != undefined)
            playlist_queryArray.push("FOO_ID IS "+chartsArray[2]);
            playlist_sortArray.push(chartsArray[2]);
        }
        if(playlist_queryArray.length > 0){
            playlist_query = playlist_queryArray.join(" OR ");
            url = playlist_query_url+url_param+encodeURIComponent(playlist_query);
            foo_httpcontrol(url, "stat", function() {save_playlist_json()});
        }
        // maybe playlist is not saved at this point
        // we have to check if it's ready... :-(
        parse_httpcontrol();       
        outer_loop:
        for (var i = 0; i != playlist_sortArray.length; i++){
            inner_loop:
            for(var j in playlistObj.playlist){
                if(playlistObj.playlist[j].track_id == playlist_sortArray){
                    //fb.trace(playlistObj.playlist[j].track_id + " / " +playlistObj.playlist[j].p);
                    m3u += playlistObj.playlist[j].p+"\n";
                    break inner_loop;
                }
               
            }

        }
        if(debug)fb.trace("M3U content\n"+m3u);
        try {
          ts = fso.OpenTextFile(playlist_m3u, 2, true, -2);
          ts.Write(m3u);
          ts.close();
        } catch(e) {
              fb.trace("M3U: Error save charts "+e);
        }
      // load sorted playlist
      WshShell.Run('"foobar2000.exe" "'  + playlist_m3u + '" ', 0, true);         
    }
}

function save_playlist_json() {
   xmlDoc = xmlhttp.responsetext;
   try {
      ts = fso.OpenTextFile(playlist_json, 2, true, -1);
      ts.Write(xmlDoc);
      ts.close();
        return true;
   } catch(e) {
        fb.trace("WSH foo_httpcontrol: Error save playlist \n"+e);
        return false;
   }

}

function parse_httpcontrol(){   
    if(fso.fileExists(playlist_json)) {
      json_response_String = read_textfile(playlist_json);
      playlistObj = JSON.parse(json_response_String);
    }
}

function foo_httpcontrol(url, user_agent, func) {
   xmlhttp.open("GET", url, true);
   xmlhttp.setRequestHeader('User-Agent',user_agent);
    xmlhttp.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
   xmlhttp.send();
   xmlhttp.onreadystatechange = function() {
    if(debug) {
          switch( xmlhttp.readyState ) {
          case 0: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Uninitialized"); break;
          case 1: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Open"); break;
          case 2: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Sent"); break;
          case 3: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Receiving"); break;
          case 4: fb.trace("WSH foo_httpcontrol: readystate "+xmlhttp.readyState+" Ready");
                  fb.trace("WSH foo_httpcontrol: Status "+xmlhttp.status);
          break;       
        } 
    }
      if (xmlhttp.readyState == 4) {
         if (xmlhttp.status == 200) {
            func();
         } else {
            fb.trace("WSH foo_httpcontrol: Connection Problems");
         }
      }
   }
    //window.Repaint();
}

// ---------------------------------------
// called from build_charts();
// ---------------------------------------
function get_lfm_charts() {
  var period = dateRangeObj[dateRangeIdx].lfm;
  if(username.length == 0 || api_key.length != 32) return(fb.trace("WSH Last.fm Charts: Can't contact Last.fm. Check your username / API KEY settings."));
  lastfm("&format=json&user=" + username + "&method=user."+lfm_chart[chart_mode]+"&period="+period, "foo_wsh_lastfm_charts", function() {save_file(chart_mode);});
}

function save_file(chart_mode) {
   xmlDoc = xmlhttp.responsetext;
    //fb.trace("JSON: get_lfm_charts"+lfm_chart_json);
   try {
      ts = fso.OpenTextFile(lfm_chart_json, 2, true, -1);
      ts.WriteLine(xmlDoc);
      ts.close();
   } catch(e) {
        fb.trace("WSH Last.fm Charts: Error save charts \n"+e);
   }
   parse_lfm_charts(chart_mode);
}

// ---------------------------------------
// build display array from json result
// ---------------------------------------
function parse_lfm_charts(chart_mode){
    if(fso.fileExists(lfm_chart_json)) {
        chartsArray = []; //reset array
        lfm_charts_json_string = read_textfile(lfm_chart_json);
        chartsObj = JSON.parse(lfm_charts_json_string);
      try{           
            var j=0;
            if(chart_mode == 157){
                for(var i in chartsObj.topartists.artist){
                    j++;
                    chartsArray.push([chartsObj.topartists.artist.playcount,chartsObj.topartists.artist.name]);
                    if(j == limit)
                        break;
                }
            }
          if(chart_mode == 158){
                for(var i in chartsObj.toptracks.track){
                    j++;
                    chartsArray.push([chartsObj.toptracks.track.playcount,chartsObj.toptracks.track.artist.name+" - "+chartsObj.toptracks.track.name]);
                    if(j == limit)
                        break;
                }
            }
          if(chart_mode == 159){
                for(var i in chartsObj.topalbums.album){
                    j++;
                    chartsArray.push([chartsObj.topalbums.album.playcount,chartsObj.topalbums.album.artist.name+" - "+chartsObj.topalbums.album.name]);
                    if(j == limit)
                        break;
                }
            }             
          }catch(e){
            fb.trace("JSON: Error Json");
        }     
    }else{
        fb.trace("JSON: no json file");
    }
    window.Repaint();
}


// ---------------------------------------
// paint track statistic
// called from on_paint()
// ---------------------------------------
function paint_stat(gr){
    var highestValueArray = [];
    var pc_data_length = pc_data2.length;
    var slice_x = statfield_ww/pc_data_length;
    highestValueArray = countArray;
    var maxpeak = highestValueArray.max();
    var minpeak = highestValueArray.min();
    var avgpeak = parseInt((maxpeak+minpeak)/2);
   
    var maxpeakPos = (statfield_wh*maxpeak)/maxpeak;
        maxpeakPos = (statfield_top+statfield_wh)-maxpeakPos;
    var minpeakPos = (statfield_wh*minpeak)/maxpeak;
        minpeakPos = (statfield_top+statfield_wh)-minpeakPos;
    var avgpeakPos = (statfield_wh*avgpeak)/maxpeak;
        avgpeakPos = (statfield_top+statfield_wh)-avgpeakPos;

    // default curvedraw
    var old_date_x = statfield_left;
    var old_peakPos = statfield_top+statfield_wh;
    var last_date_x = statfield_ww+15;
    var last_date_y = statfield_top+statfield_wh;
    ttMarks = [];
   
        gr.DrawLine(statfield_left, maxpeakPos, statfield_ww+15, maxpeakPos, 1, stat_line);
        gr.DrawLine(statfield_left, avgpeakPos, statfield_ww+15, avgpeakPos, 1, stat_line);
        gr.DrawLine(statfield_left, minpeakPos, statfield_ww+15, minpeakPos, 1, stat_line);
        gr.DrawString(maxpeak, s_font, stat_legend, 15, maxpeakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
        gr.DrawString(avgpeak, s_font, stat_legend, 15, avgpeakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
        gr.DrawString(minpeak, s_font, stat_legend, 15, minpeakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));

    //draw first zero point
    gr.DrawEllipse(old_date_x-2, old_peakPos-2, 4, 4, 1, stat_item_color);

    for (var i = 0; i != pc_data_length; i++){
        var date_w = 0;
        var peakPos = (statfield_wh*pc_data2['count'])/maxpeak;
        peakPos = (statfield_top+statfield_wh)-peakPos;
        //get date width
        date_w = gr.CalcTextWidth(pc_data2['date'], g_font);
        // add width to calc position on x axis
        date_x +=(i != 0)?slice_x:0;
        //fb.ShowConsole();
        var normalizedXPos = date_x+(slice_x/2);
        var isEven = function(someNumber){return (someNumber%2 == 0) ? true : false;};
        switch(isEven(pc_data_length)){
            case true:
                var modulo = parseInt(pc_data_length/8);
                var c = pc_data_length-1;
            break;
            default:
                var modulo = parseInt(pc_data_length/7);
                var c = pc_data_length;
        }
     
      //fb.trace(date_range);
        if(i%modulo==0 || i == c)
        gr.DrawString(pc_data2['date'], m_font, g_textcolor, normalizedXPos-(date_w/2), date_y, date_w, 18, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
       
        // draw points
        if(pc_data_length < 100)
        gr.DrawString(pc_data2['count'], s_font, stat_item_color, normalizedXPos-7, peakPos-16, 16, 16, StringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.EllipsisCharacter));
        gr.DrawEllipse(normalizedXPos-2, peakPos-2, 4, 4, 1, stat_item_color);
        gr.DrawLine(old_date_x, old_peakPos, normalizedXPos, peakPos, 1, stat_item_color)
       
        // draw circles from class
        var stc = (pc_data_length < 100)? stat_item_color : stat_item_t;
        var vp = (pc_data_length < 100)? 4 : 10;
        ttMarks.push(new ttMark(normalizedXPos-2, peakPos-2, vp, vp, 1,stc,pc_data2['count'], null,pc_data2['count']+" / "+pc_data2['date']+"/"+pc_data2['artist']));
       
        var old_date_x = normalizedXPos;
        var old_peakPos = peakPos;
    }
   
    //draw last points
    gr.DrawLine(old_date_x, old_peakPos, last_date_x, last_date_y, 1, stat_item_color);
    gr.DrawEllipse(last_date_x-2, last_date_y-2, 4, 4, 1, stat_item_color);
   
    var meta = sprintf(lang.stat_meta_text.def,highestValueArray.sum(),fp,lp);
    gr.DrawString(meta, g_font, stat_meta_color, 6, 34, ww-16, 16, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
}


function on_mouse_move(x, y) {
    var ttm = ttMarksTraceMouse(x, y);
    window.SetCursor((y > 8 && y < 26 && x < ww-60 ) || (check_enable_playlist() && (x > ww-60 && x < ww-10 && y > 8 && y < 26)) || ttm ? IDC_HAND : IDC_ARROW);

    if (ttm != cur_ttm) {
        cur_ttm && cur_ttm.onMouseOut();
        ttm && ttm.onMouseIn();
    }

    if (ttm != ttm_down) {
        ttm_down = null;
    }

    cur_ttm = ttm;
}

// ---------------------------------------
// paint bar charts
// called from on_paint()
// ---------------------------------------
function paint_charts(gr){
    gr.FillSolidRect(statbox_left, statbox_top, statbox_ww, statbox_wh, g_backcolor);

    var chartslength = chartsArray.length;
//    var row_h = (statfield_wh-10)/chartslength;
    var row_h = 20;
    var row_y_pos = (statfield_top-0);
    var maxPeak = chartsArray[0][0];
    var bar_max_w = statfield_ww-180;
    var bar_left = statfield_left+200;
    //fb.trace(maxPeak);
   
    // get build array from count values
    var maxValueArray = new Array();
    for (i = 0; i != chartslength; i++){
        maxValueArray.push(chartsArray[0]);
    }
   
    // find min count to set min barsize.
    var min_widthValue = maxValueArray.max();
    var min_width = gr.CalcTextWidth(min_widthValue, g_font)*4;
    //min_width = 135;
   
    switch(chart_mode){
        case 150:
        case 154:
        case 157:
      case 172:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 150, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(200, row_y_pos, bar_width, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 205, row_y_pos, bar_width-10, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
        break;
        case 151:
        case 158:
      case 161:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                var chartsitem = chartsArray[1].split(" - ");
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 275, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(300, row_y_pos, bar_width-100, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 305, row_y_pos, bar_width-10, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
      break;
        case 160:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                var chartsitem = chartsArray[1].split(" - ");
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 250, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(270, row_y_pos, bar_width-57, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 273, row_y_pos, 20, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
        break;
        case 152:
        case 159:
        for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                bar_width = (bar_width < min_width)?min_width:bar_width;
                var chartsitem = chartsArray[1].split(" - ");
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos, 275, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(300, row_y_pos, bar_width-100, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 305, row_y_pos, 20, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }           
        break;
        case 153://break;
        default:
            for (i = 0; i != chartslength; i++){
                var bar_width = (bar_max_w*chartsArray[0])/maxPeak;
                gr.DrawString(chartsArray[1], n_font, g_textcolor, 20, row_y_pos + offset, 150, row_h-2, StringFormat(StringAlignment.Far, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                gr.FillSolidRect(200, row_y_pos + offset, bar_width, row_h-2, stat_item_color);
                gr.DrawString(chartsArray[0], nb_font, stat_hl_color, 205, row_y_pos + offset, bar_width-10, row_h-2, StringFormat(StringAlignment.Near, StringAlignment.Center, StringTrimming.EllipsisCharacter));
                row_y_pos += row_h;
            }
        }
}

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


function on_paint(gr) {
    //fb.trace("on_paint");
//   offset = Math.round(offset);
    offset = 0
    gr.SetTextRenderingHint(5);

   
    statbox_left = 60;
    statbox_top = 40;
    statbox_ww = ww-16;
    statbox_wh = wh-68;
   
    textheight = 0;
    pos = statbox_top;
    date_x = statbox_left+10;
    date_y = statbox_wh+statbox_top-20;
   
    statfield_left = statbox_left+10;
    statfield_top = statbox_top+20;
    statfield_ww = statbox_ww-20;
    statfield_wh = statbox_wh-10;
   
    headline_left = 30;
   
    point_x = 0;
    point_y = 0;
   
    // stat background
   gr.FillSolidRect(statbox_left, statbox_top, statbox_ww, statbox_wh, stat_bg_color);
    gr.DrawImage(stat_img, 6, 6, 18, 18, 0, 0, 18, 18);
    if(g_metadb) {

        if(fso.fileExists(db_file) || username.length > 0 && api_key.length == 32){

            if(show_charts === 1){
            // show all charts
                if(chartsArray.length > 0){
                    if(chartsArray.length == 1 && chartsArray[0][0] == 0){
                    gr.DrawString(lang.no_stat, g_font, g_textcolor, statbox_left, statbox_top, statbox_ww, statbox_wh, StringFormat(StringAlignment.Center,StringAlignment.Center));
                        }else{
                        paint_charts(gr);
                        }
                }
            }else{
            // show trackstat
                if(pc_data.length > 0){
                    // check if real data not from fake array
                    if(pc_data.length == 1 && pc_data[0]["count"] == 0){
                        gr.DrawString(lang.no_stat, g_font, g_textcolor, statbox_left, statbox_top, statbox_ww, statbox_wh, StringFormat(StringAlignment.Center,StringAlignment.Center));
                    }else{
                        //draw markers
                        paint_stat(gr);
                        ttMarksDraw(gr);
                    }
                }else{
                    gr.DrawString(lang.no_stat, g_font, g_textcolor, statbox_left, statbox_top, statbox_ww, statbox_wh, StringFormat(StringAlignment.Center,StringAlignment.Center));
                }
            }

            stat_headline = "";
            meta = "";
            var _date_range = dateRangeObj[dateRangeIdx].text;
            if(show_charts === 1){
                switch(chart_mode){
                    case 150:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 151:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 152:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 153:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],artist,_date_range);
                    break;
                    case 154:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 155:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],artist,album,_date_range);
                    break;
                    case 156:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],artist,_date_range);
                    break;
                    case 157:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 158:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;           
                    case 159:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],_date_range);
                    break;
                    case 160:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],sort1,_date_range);
                    break;
                    case 161:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],sort1,_date_range);
                    break;
                    case 172:
                        stat_headline += sprintf(lang.chart_mode_text[chart_mode],sort1,_date_range);
                    break;
                    default:
                        stat_headline += sprintf(lang.chart_mode_text.def,_date_range);
               
                }
               
                meta += lang.chart_meta_text.def;
            }else{
                stat_headline = sprintf(lang.stat_mode_text[which_stat],artist,title,album);           
            }
            // draw headline
            if(check_enable_playlist())
                gr.DrawImage(playlist_img, ww-28, 6, 18, 18, 0, 0, 18, 18);
            gr.DrawString(stat_headline, bb_font, g_textcolor_hl, headline_left, 6, ww-headline_left-16,18, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
            gr.DrawLine(5, 29, ww-10, 29, 1, g_color_hl);
            // draw meta
        }else{
            gr.DrawString(stat_headline, bb_font, g_textcolor_hl, headline_left, 6, ww-headline_left-16,18, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
            gr.DrawLine(5, 29, ww-10, 29, 1, g_color_hl);
            gr.DrawString(meta, g_font, stat_meta_color, 6, 34, ww-16, 16, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
            gr.DrawString(stat_install_info, g_font, g_textcolor, statfield_left, statfield_top, statfield_ww, statfield_wh,StringFormat(StringAlignment.Near,StringAlignment.Near)); 
       
        }
    } else {
        gr.DrawString(stat_headline, bb_font, g_textcolor_hl, headline_left, 6, ww-headline_left-16,18, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
        gr.DrawLine(5, 29, ww-10, 29, 1, g_color_hl);
        gr.DrawString(meta, g_font, stat_meta_color, 6, 34, ww-16, 16, StringFormat(StringAlignment.Near, StringAlignment.Near, StringTrimming.EllipsisCharacter));
      gr.DrawString("no selection", g_font, g_textcolor, 0, 0, ww, wh,StringFormat(StringAlignment.Center,StringAlignment.Center));
   }
   
}



function on_metadb_changed() {
    if(!g_metadb) return(window.Repaint());
    var length = fb.TitleFormat("[%length%]").Eval();
    artist = fb.TitleFormat(tf.artist).EvalWithMetadb(g_metadb);
    title = fb.TitleFormat(tf.title).EvalWithMetadb(g_metadb);
    album = fb.TitleFormat(tf.album).EvalWithMetadb(g_metadb);
    artist_id = fb.TitleFormat(tf.artist_id).EvalWithMetadb(g_metadb);
    album_id = fb.TitleFormat(tf.album_id).EvalWithMetadb(g_metadb);
    id = fb.TitleFormat(tf.id).EvalWithMetadb(g_metadb);
    rdate = fb.TitleFormat(tf.date).EvalWithMetadb(g_metadb);
 
  if(length.length == 0 && fb.PlaybackTime > 0.1){
        artist = "Radio";
        album  = "Radio";
        artist_id = fb.TitleFormat("$crc32(radio)").EvalWithMetadb(g_metadb);
        album_id = fb.TitleFormat("$crc32(radio)").EvalWithMetadb(g_metadb);
    }

    pc = fb.TitleFormat(tf.playcount).EvalWithMetadb(g_metadb);
    fp = fb.TitleFormat(tf.first_played).EvalWithMetadb(g_metadb);
    lp = fb.TitleFormat(tf.last_played).EvalWithMetadb(g_metadb);
    sort1 = fb.TitleFormat(tf.sort1).EvalWithMetadb(g_metadb);
    sort2 = fb.TitleFormat(tf.sort2).EvalWithMetadb(g_metadb);
    tracktime = fb.TitleFormat(tf.tracktime).EvalWithMetadb(g_metadb);
 
    // mask for sql inserts
    album_sql = album.replace(/"/g,"\\\"");
    album_sql = album_sql.replace(/'/g,"''");
    title_sql = title.replace(/"/g,"\\\"");
    title_sql = title_sql.replace(/'/g,"''");
    artist_sql = artist.replace(/"/g,"\\\"");
    artist_sql = artist_sql.replace(/'/g,"''");
   
    sort1_sql = sort1.replace(/"/g,"\\\"");
    sort1_sql = sort1_sql.replace(/'/g,"''");
    sort2_sql = sort2.replace(/"/g,"\\\"");
    sort2_sql = sort2_sql.replace(/'/g,"''");
     
    // show charts on itemFocusChange but only if it's not playing
    if(! fb.IsPlaying){
        //if(debug) fb.trace("----- Force Show Charts");
        show(show_charts);
    }   
  window.Repaint(); 
}

// lean from marc2000 lfm script

function on_playback_new_track() {
   time_elapsed = 0;
   switch(true) {
      case (fb.PlaybackLength == 0):
         target_time = 240;
         break;
      case (fb.PlaybackLength >= 30):
         target_time = Math.min(Math.floor(fb.PlaybackLength / 2),240);
         break;
      default:
         target_time = 5;
   }
   on_item_focus_change();
    show(show_charts);
}

function on_playback_time(time) {
   time_elapsed++;
   if(time_elapsed == target_time) {
        //on_item_focus_change();
        playcount();
        show(show_charts);
    }
}

// ---------------------------------------
// show and generate bar charts
// ---------------------------------------

function build_charts(){
    if(fso.fileExists(db_file)){   
      try{ 
        var date_range = dateRangeObj[dateRangeIdx].sql;
        var dateRangeSqlStr = (date_range)? 'datetime(PLAYTIME) > datetime(\'now\',\''+date_range+'\',\'localtime\')' : "1=1" ;
            if(show_charts === 1){
                var query = "";
                switch(chart_mode){
                    case 150:
                    query = '\"SELECT COUNT(ARTISTID) AS ARTISTCOUNT,ARTIST FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY artistid ORDER BY artistcount DESC LIMIT '+limit+';\"';
                    break;
                    case 151:
                    query = '\"SELECT COUNT(TRACKID) AS TRACK_COUNT ,ARTIST || \' - \' || TITLE, TRACKID  FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY ARTIST, TITLE ORDER BY TRACK_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 152:
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT ,ARTIST || \' - \' || ALBUM  FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY ALBUMID, ARTISTID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 153:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT, ALBUM  FROM playcount WHERE ARTISTID = '+artist_id+' AND '+dateRangeSqlStr+' GROUP BY ALBUMID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 155:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(TITLE) AS TITLE_COUNT, TITLE FROM playcount WHERE ARTISTID = '+artist_id+'  AND ALBUMID = '+album_id+' AND '+dateRangeSqlStr+' GROUP BY TITLE ORDER BY TITLE_COUNT DESC, PLAYTIME DESC LIMIT '+limit+';\"';
                    break;
                    case 156:
                    // SELECT COUNT(albumid),artist || ' ~ ' || album FROM playcount where artistid = 1646199482 group by albumid
                    query = '\"SELECT COUNT(TITLE) AS TITLE_COUNT, TITLE FROM playcount WHERE ARTISTID = '+artist_id+' AND '+dateRangeSqlStr+' GROUP BY TITLE ORDER BY TITLE_COUNT DESC, PLAYTIME DESC LIMIT '+limit+';\"';
                    break;
                    case 154:
                    query = '\"SELECT COUNT(sort1) AS SORT1_COUNT, sort1 FROM playcount WHERE '+dateRangeSqlStr+'  GROUP BY sort1 ORDER BY SORT1_COUNT DESC LIMIT '+limit+';\"';
                    break;
                case 160:
                    query = '\"SELECT COUNT(ALBUMID) AS ALBUM_COUNT,ARTIST || \' - \' || ALBUM  FROM playcount WHERE sort2 = '+sort2+' AND '+dateRangeSqlStr+' GROUP BY ALBUMID, ARTISTID ORDER BY ALBUM_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 161:
                    query = '\"SELECT COUNT(TRACKID) AS TRACK_COUNT,ARTIST || \' - \' || TITLE  FROM playcount WHERE sort2 =  '+sort2+' AND '+dateRangeSqlStr+' GROUP BY TITLE, ARTISTID ORDER BY TRACK_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 172:
                    query = '\"SELECT COUNT(ARTISTID) AS ARTIST_COUNT,ARTIST  FROM playcount WHERE sort2 = '+sort2+' AND '+dateRangeSqlStr+' GROUP BY ARTISTID ORDER BY ARTIST_COUNT DESC LIMIT '+limit+';\"';
                    break;
                    case 157: // query lfm charts
                    case 158:
                    case 159:
                        get_lfm_charts();
                        return;
                    break;
                    default:
                    query = '\"SELECT COUNT(ARTIST) AS ARTISTCOUNT,ARTIST FROM playcount WHERE '+dateRangeSqlStr+' GROUP BY artistid ORDER BY artistcount DESC LIMIT '+limit+';\"';           
                }
               
                if(debug){
                    fb.trace("++++SQL // START - Mode: "+chart_mode);
                    fb.trace(query);
                    fb.trace("++++SQL // END");
                }
               
                if(query.length > 0){
                    WshShell.Run(batch_cmd + query + ' > "'+chart_file+'"',0,true);   
                    if(fso.fileExists(chart_file)) {
                        var charts_table = read_textfile(chart_file);                 
                        chartsArray = csvArray(charts_table);
                    }
                }
            }else{
                CollectGarbage();
            }
        }catch(e){
            fb.trace("WSH Charts: build charts went wrong "+e);
        }
    }else{
        if(debug) fb.trace('No Custom Playcount DB (Build Charts)');
        get_lfm_charts();
    }
    window.Repaint();
}


// ---------------------------------------
// generate statistic
// ---------------------------------------
function build_stat(){
if(fso.fileExists(db_file)){
    var date_range = dateRangeObj[dateRangeIdx].sql;
    var dateRangeSqlStr = (date_range) ? 'datetime(a.PLAYTIME) > datetime(\'now\',\''+date_range+'\',\'localtime\')' : "1=1";
    var minDateSqlStr = (date_range) ? 'date(\'now\',\''+date_range+'\',\'localtime\')' : "b.MIN_QUERYDATE" ; // MIN_QUERYDATE is the row of MIN_DATE SQL view!
    var minDateSqlView = (date_range) ? '':', MIN_DATE b';
   
    var query = '\"SELECT COUNT(a.TRACKID) AS COUNTER, date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE FROM playcount a '+minDateSqlView+' WHERE a.TRACKID = '+id;
    query += ' AND '+dateRangeSqlStr+' GROUP BY PT;\"';
   
    // album Query
    if(which_stat == 2){
        query = '\"SELECT COUNT(a.ALBUMID) AS COUNTER, date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE FROM playcount a '+minDateSqlView+' WHERE a.ALBUMID = \''+album_id+'\'';
        query += ' AND '+dateRangeSqlStr+' GROUP BY PT;\"';
    }
   
    // artist Query
    if(which_stat == 3){
        query = '\"SELECT COUNT(a.ARTISTID) AS COUNTER, date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE FROM playcount a '+minDateSqlView+' WHERE a.ARTISTID = \''+artist_id+'\'';
        query += ' AND '+dateRangeSqlStr+' GROUP BY PT;\"';
    }
   
    // Top per Day
    if(which_stat == 4){
        query = '\"SELECT a.artistcount AS COUNTER,date(a.PLAYTIME) as PT, '+minDateSqlStr+' AS START_DATE, a.artistname  FROM TOP_ARTIST_PER_DAY a '+minDateSqlView;
        query += ' WHERE '+dateRangeSqlStr+' GROUP BY PT;\"';
    }
    if(debug){
        fb.trace("++++SQL // START");
        fb.trace(query);
        fb.trace("++++SQL // END");
    }
    try{
        // write query result to file
        WshShell.Run(batch_cmd + query + ' > "'+os_escape(pc_file)+'"',0,true);     
    } catch(e){
        fb.trace('WSH Charts: error writing to sqlite');
    }

    // try load the file
    if(fso.fileExists(pc_file)) {
        var pc_datastring = read_textfile(pc_file);
        var pc_dataArrayRows = [];
        pc_data = [];
        pc_data2 = [];
        countArray = [];
           
            try {
                if(pc_datastring.length > 0) {
                    pc_dataArrayRows = pc_datastring.split("\n");
                   
                    for (i = 0; i != pc_dataArrayRows.length-1; i++)
                    {
                        pc_dataArray = pc_dataArrayRows.split("|");
                        // set start date
                        pc_start_date = pc_dataArray[2];
                        pc_data = new Array();
                        //pc_data1 = new Array();
                        pc_data["date"] =  pc_dataArray[1];
                        pc_data["count"] =  pc_dataArray[0];
                        pc_data["artist"] =  pc_dataArray[3];
                        countArray.push(parseInt(pc_dataArray[0]));
                    }
                    //fb.trace(countArray);
                    // build datearray
                    var tempStartDate = pc_start_date.split("-");
                    // fb.trace("---"+tempStartDate);
                   
                    var startDate = new Date(tempStartDate[0],tempStartDate[1]-1,tempStartDate[2]);
                    var endDate = new Date();
                    //fb.trace("---"+startDate);

                    var newDate = startDate;
                    dateStrings = [];

                    while (newDate <= endDate){
                      str = newDate.getFullYear() + "-" +
                                lz(newDate.getMonth() + 1) + "-" +
                                lz(newDate.getDate());       
                      dateStrings.push(str);
                      newDate.setDate(newDate.getDate()+1);
                    }
                   
                   
                    for (i = 0; i != dateStrings.length; i++){
                       
                        pc_data2 = [];
                        for (j = 0; j != pc_data.length; j++){
                         
                            if(dateStrings == pc_data[j]["date"]){
                                pc_data2["date"] = pc_data[j]["date"];
                                pc_data2["count"] = pc_data[j]["count"];
                                pc_data2["artist"] = pc_data[j]["artist"];
                                break;
                            }else{
                                pc_data2["date"] = dateStrings;
                                pc_data2["count"] = 0;
                                pc_data2["artist"] = 'undefined';
                            }
                        } 
                    }
                   
                }else{
                  if(debug) fb.trace("WSH Charts: making fake array?");
                }
            } catch(e) {
                fb.trace('WSH Charts: error open playcount.txt'); 
            }
    }else{
        if(debug) fb.trace('WSH Charts: no playcount.txt');       
    }
}else{
    if(debug) fb.trace('No Custom Playcount DB (Build Statistic)');
}
    window.Repaint();
}

// ---------------------------------------
// track playcount to playcount.sqlite
// ---------------------------------------

function playcount(){
if(fso.fileExists(db_file)){
    fb.trace('WSH Charts: set playcount for:' + artist + " - " + title);
    var pt = playtime('datetime');
    // TODO - let Playtime calculate by sqlite on insert. query must be mit date(...,'localtime') because sqlite insert standard time without timezones
    var query  = 'INSERT INTO playcount(TRACKID,ARTIST,TITLE,ALBUM,FP,LP,PLAYTIME,PC,ARTISTID,ALBUMID,TRACKTIME,SORT1,SORT2) VALUES(\'' + id + '\',\''+artist_sql+'\',\''+title_sql+'\',\''+album_sql+'\',\''+fp+'\',\''+lp+'\',\''+pt+'\',\''+Number(pc)+'\',\''+artist_id+'\',\''+album_id+'\',\''+tracktime+'\',\''+sort1_sql+'\',\''+sort2+'\');';
        query += 'INSERT OR REPLACE INTO artist (artistname,artistid) VALUES (\''+artist_sql+'\','+artist_id+'\');';
        query += 'INSERT OR REPLACE INTO album (albumname,albumid,artistid,year) VALUES (\''+album_sql+'\','+album_id+','+artist_id+',\''+rdate+'\');';
   
    if(debug){
        fb.trace("++++SQL // START");
        fb.trace(query);
        fb.trace("++++SQL // END");
    }
    try{
        WshShell.Run('sqlite3.exe "' + os_escape(db_file) + '" "'+query+'"', 0, true);
    } catch(e){
        fb.trace('SQLITE: '+e);
        fb.trace('SQLITE: '+query);
    }
}else{
    if(debug) fb.trace('WSH Charts: No Custom Playcount DB (Playcount)');
}   
}

function on_mouse_lbtn_up(x, y) {
    // playlist button
    if(check_enable_playlist()){
        if(x > ww-28 && x < ww-10 && y > 8 && y < 26){
            build_playlist();
        }
    }
   
    if(y > 8 && y < 26 && x < ww-28 ) {
   
   var _menu = window.CreatePopupMenu();
   var _child = window.CreatePopupMenu();
    var _date = window.CreatePopupMenu();
    var _limit = window.CreatePopupMenu();
   var idx;

    for (i=0;i<dateRangeObj.length;i++)
    {
      _date.AppendMenuItem(MF_STRING, 110 + parseInt(i), dateRangeObj.text);
   }
    _date.CheckMenuRadioItem(110, 115, window.GetProperty("date_range",110));
   
    for (i=0;i<limitRangeArray.length;i++)
    {
      _limit.AppendMenuItem(MF_STRING, 120 + parseInt(i), limitRangeArray);
   }
    _limit.CheckMenuRadioItem(120, 125, window.GetProperty("limit_range",120));
   
   
    _menu.AppendMenuItem(MF_DISABLED, 0, "Custom Statistic");
   
    if(fso.fileExists(db_file))
    {
        _menu.AppendMenuItem(MF_STRING, 1, "Show track statistic");
        _menu.AppendMenuItem(MF_STRING, 2, "Show album statistic");
        _menu.AppendMenuItem(MF_STRING, 3, "Show artist statistic");
        _menu.AppendMenuItem(MF_STRING, 4, "Top per day statistic");
        _menu.CheckMenuRadioItem(1, 4, window.GetProperty("which_stat",1));
       
        _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);

        _menu.AppendMenuItem(MF_STRING, 150, "Show artist charts");
        _menu.AppendMenuItem(MF_STRING, 151, "Show artist-track charts*");
        _menu.AppendMenuItem(MF_STRING, 152, "Show artist-album charts");
        _menu.AppendMenuItem(MF_STRING, 153, "Show most played albums from artist");
        _menu.AppendMenuItem(MF_STRING, 155, "Show most played tracks from artist album*");
        _menu.AppendMenuItem(MF_STRING, 156, "Show most played tracks from artist*");
        _menu.AppendMenuItem(MF_STRING, 154, "Show top genres chart");
        _menu.AppendMenuItem(MF_STRING, 172, "Show most played artists from genre");
        _menu.AppendMenuItem(MF_STRING, 160, "Show most played albums from genre");
        _menu.AppendMenuItem(MF_STRING, 161, "Show most played tracks from genre");
      
    }else{
        _menu.AppendMenuItem(MF_STRING, 143, "Install custom statistic");
    }
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
    _menu.AppendMenuItem(MF_DISABLED, 0, "Last.fm");
    if(username.length > 0 && api_key.length == 32)
    {
        _menu.AppendMenuItem(MF_STRING, 157, "Show Last.fm artist charts");
        _menu.AppendMenuItem(MF_STRING, 158, "Show Last.fm artist-track charts");
        _menu.AppendMenuItem(MF_STRING, 159, "Show Last.fm artist-album charts");
    }else{
        _menu.AppendMenuItem(MF_STRING, 141, "Set your Last.fm username...");
        _menu.AppendMenuItem(MF_STRING, 142, "Set your API KEY...");
        _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
    }
    _menu.AppendMenuItem(username.length > 0 ? MF_STRING : MF_GRAYED, 140, "Visit your Last.fm user profile chart page");
    _menu.CheckMenuRadioItem(150, 169, window.GetProperty("chart_mode",150));
   
    if(foo_httpcontrol_exist){
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
        _menu.AppendMenuItem(playlistEnabledArr.exists(chart_mode) ? MF_STRING : MF_GRAYED, 99, "* Make playlist");
    }
    _menu.AppendMenuItem(MF_SEPARATOR, 0, 0);
   
    _menu.AppendMenuItem(MF_STRING | MF_POPUP, _limit.ID

[WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #34
@jazzthieve can you please share this  fooAcerbus theme , looks awesome

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #35
Anyone still has this? First post is just a giant link to imageshack now unfortunately.

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #36
This is the complete script...

Since the op links are broken, is this script you posted an accurate copy of the original? Can I just ignore the custom changes section and paste the rest of it?
I'm late

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #37
It's not at all an accurate copy of the original. I have however saved an older version as a fallback option in case I screw it up too much. This is still not 100% original but close enough. The only changes I made, if I'm not mistaken, is the incorporation of a piece of marc2003's code he shared in this thread, changing some tag mappings and the inclusion of extra sql strings and minor adaptations to the select menu.

This is the earliest I have with littlest changes.

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #38
A bit of useful information, since the author expressed the experimental nature of his script and he wasn't sure about its stability I'm happy to say it's very stable. Currently on my system the database holds almost a quarter of a million records (240237 to be exact) and I have no issues.

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #39
It's not at all an accurate copy of the original. I have however saved an older version as a fallback option in case I screw it up too much. This is still not 100% original but close enough. The only changes I made, if I'm not mistaken, is the incorporation of a piece of marc2003's code he shared in this thread, changing some tag mappings and the inclusion of extra sql strings and minor adaptations to the select menu.

This is the earliest I have with littlest changes.


Thanks a lot jazzthieve! Do you also have, the common.js file called in the preprocessor?
I'm late

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #40
Yes, here you go:

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #41
Yes, here you go:


Apparently I'm still missing some required png files. According to line 581 of the common.js script, they are expected to be in the foobar2000/scripts/images directory.
I'm late

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #42
Got it:

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #43
Got it:

Sorry if this is a bit painful, I guess the original op linked to all the required script dependencies, but since they're not available anymore I can only find out about missing resources one at a time. For this round it is the turn of the input.wsf file in the scripts folder.
I'm late

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #44
Don't worry, I understand.
I've included a js file in it from the same folder location which might be relevant (or not).

Btw, if you gonna look into this code and perhaps find out how to display more than one panel at the same time without each panel interfering with the other let me know. Never could figure that out.

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #45
Alright, I'm not really sure about next one. It seems that the scripts expects to find two files named "username" and "api_key" in the wsh_settings folder. They're probably just empty files where the lastfm user data are stored subsequently, but it's suspicious that they are not preemptively created by the script.
I'm late

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #46
Yes, i have 2 files, without extension, in the wsh_settings folder that after opening with notepad just contain my lastfm username and a lastfm api key and nothing more.

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #47
I didn't experience anymore crashes and I can see the charts now, but I can't install the custom playcount statistics database: the dialog window keeps popping up after clicking on the "yes" button and no database is created. The console says:
Quote from: Console
{"error":3,"message":"Invalid Method - No method with that name in this package"}
The script was looking for the sqlite3.exe in the foobar2000 directory, which I extracted from the latest sqlite tools package, maybe it's not the right one? Or shall I remove the customdb plug-in first?

P.S.
I tried removing foo_customdb, but the problem remains.
I'm late

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #48
I've found a guide by the author in my FB directory. Also, you'll need to tag your files with ID's using a masstagger script, I've included this as well.

About the masstagger script and also mentioned in the guide I wouldn't bother putting in any effort using the acoustic fingerprint ID, in my experience it isn't accurate enough. No need to change the masstagger script for it since it reverts to $crc32(%artist%) for ID creating when $crc32(%FINGERPRINT_FOOID%) isn't present.

Re: [WSH] - Display custom charts and statistics (incl. Last.fm)

Reply #49
Thank you jazzthieve. Unfortunately it's a user guide and not an installation guide. I still don't know what's wrong with my sqlite3.exe or if I'm missing some other dependency. Do you remember where you got the sqlite3 application from? Or if it was bundled with the script in the download link?
I'm late