Skip to main content

Topic: [WSH] - Display custom charts and statistics (incl. Last.fm) (Read 11779 times) previous topic - next topic

0 Members and 1 Guest are viewing this topic.
  • muzack
  • [*][*][*]
[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!

  • kabuki
  • [*][*][*]
[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? ...  )

  • Wooops
  • [*]
[WSH] - Display custom charts and statistics (incl. Last.fm)
Reply #28
I might start to retag & fingerprint all my files... Thanks for sharing!!
  • Last Edit: 19 July, 2010, 11:03:17 PM by Wooops

  • jazzthieve
  • [*][*][*][*]
[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
  • );
    }
  
   // 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
  • )/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
  • , 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
  • )/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
  • , 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
  • )/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
  • , 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
  • )/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
  • , 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")

  • jazzthieve
  • [*][*][*][*]
[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.

  • samithaj
  • [*][*][*]
[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??

  • jazzthieve
  • [*][*][*][*]
[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.

  • jazzthieve
  • [*][*][*][*]
[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
  • );
    }
  
   // 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
  • )/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
  • , 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
  • )/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
  • , 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
  • )/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
  • , 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
  • )/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
  • , 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
  • )/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
  • , 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
  • Last Edit: 05 April, 2015, 07:06:44 PM by jazzthieve

  • samithaj
  • [*][*][*]
[WSH] - Display custom charts and statistics (incl. Last.fm)
Reply #34
@jazzthieve can you please share this  fooAcerbus theme , looks awesome

  • OoNebsoO
  • [*][*]
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.