Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Looking for a Foobar Skin that displays your set images rather then cover (Read 3567 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Looking for a Foobar Skin that displays your set images rather then cover

Hi, one of my favorite winamp skins called ichange had a feature that you could set up to 30 images for the player's background and you could either manually change between them or they would randomly change between songs. Is their anything similar for Foobar, or at least something that integrates nicely into the player like a frame that displays images or an image of your choosing ?

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #1
Hi.  Foobar doesn't really use skins per say.  But there is a Spider Monkey script that you could insert into a panel that displays random images from a specified folder every few seconds.  Spider Monkey allows the use of Java scripts within Foobar and comes with a variety of sample scripts you can use..  There is no interface.  You have to manually configure the script to specify the folder and the number of seconds.  You would need to install the Spider Monkey component then load the included 'LoadImageAsyncV2.js' script into a panel and configure it with the folder name and timing.  As is, there is no control of any kind.  It just randomly displays images from the specified folder every so many seconds based on a timer.

However, if this is something you want to try, and you get it working as is, I can provide you with a modified script and instructions on how to install and configure it..  This script has the following features:

1.  The ability to change the background color by specifying the desired RGB code (changed in the configuration).
2.  You can trigger the image change by track changes instead of by a timer.  Both sets of code are in the script.  You just need to go into the configuration and comment out one set of code and uncomment the other set.
3.  You can use the mouse wheel to scroll to the next random image on demand.
4.  Automatic display will stop if you pause or or stop the player, although you can still use the mouse wheel to scroll images.
5.  I also have code that will display the images sequentially instead of randomly.  With this option you can scroll forwards and backwards.  It will even display the total number of images within the folder and the number of the currently displayed image.  Both sets of code are in the script.  You just need to go into the configuration and comment out one set of code and uncomment the other set.

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #2
Hi.  Foobar doesn't really use skins per say.  But there is a Spider Monkey script that you could insert into a panel that displays random images from a specified folder every few seconds.  Spider Monkey allows the use of Java scripts within Foobar and comes with a variety of sample scripts you can use..  There is no interface.  You have to manually configure the script to specify the folder and the number of seconds.  You would need to install the Spider Monkey component then load the included 'LoadImageAsyncV2.js' script into a panel and configure it with the folder name and timing.  As is, there is no control of any kind.  It just randomly displays images from the specified folder every so many seconds based on a timer.

However, if this is something you want to try, and you get it working as is, I can provide you with a modified script and instructions on how to install and configure it..  This script has the following features:

1.  The ability to change the background color by specifying the desired RGB code (changed in the configuration).
2.  You can trigger the image change by track changes instead of by a timer.  Both sets of code are in the script.  You just need to go into the configuration and comment out one set of code and uncomment the other set.
3.  You can use the mouse wheel to scroll to the next random image on demand.
4.  Automatic display will stop if you pause or or stop the player, although you can still use the mouse wheel to scroll images.
5.  I also have code that will display the images sequentially instead of randomly.  With this option you can scroll forwards and backwards.  It will even display the total number of images within the folder and the number of the currently displayed image.  Both sets of code are in the script.  You just need to go into the configuration and comment out one set of code and uncomment the other set.

Hi first of all thank you very much for your help this seems exactly what i wanted, so far i installed Spider Monkey Script and managed to somehow load the basic "LoadImageAsyncV2.js" script while setting a default folder from which to show images and managed to comment out the timer, up to here all was very intuitive, it took me a while but i also managed to figure out up to some point how panels work, i'm at a level where i can at least get what i want from their positioning. 
Unfortunately in the "LoadImageAsyncV2.js" that came with my Spider Monkey component i found no way to trigger the change image by track can you please supply your code for it ? Also i'm very interested in point 5 as well, there might be times when i want them to be displayed sequentially rather then randomly.




Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #3
I've copied my entire script below.  You will need the includes you see below.  I don't remember if Helpers.js was originally in the docs folder or not.  I may have copied it there from the Samples\Complete\JS folder.  If you use the sequential mode and want to display the image number and count, you may need to adjust the font size for your display and the position under the  gr.DrawString statement.  This example is currently set up for random display.  I use /*, */ to comment out the block of code I don't want to use.  To change modes, I comment out the comment lines for the mode I want to use and uncomment the comment lines for the other mode.  To simplify, the inactive mode will be bracketed by /* and */ and the active mode will be bracketed by ///* and //*/.  You will see this below.  Let me know if you have any questions.  I know it's not pretty but I am very new to Java script and went through a lot of trial and error (mostly error) to get what I wanted.  FYI, if using the sequential mode you can't scroll backwards if on the first image, and when scrolling past the last image it jumps to the first image.  Trying to scroll backwards from the first image was just too problematic.  I don't know what the limit is on the number of images in a folder, but I have one folder with over 2000 images and it works fine.  I kind of use this as a built-in slideshow.  If you have multiple image folders you can add them all commented out and then just uncomment the one you want to use.  Just be sure to only have one uncommented at a time.  Good luck.

window.DefinePanel("LoadImageAsyncV2", { author: "TheQwertiest" });
include(`${fb.ComponentPath}docs\\Flags.js`);
include(`${fb.ComponentPath}docs\\Helpers.js`);

/**
 * @typedef {Object} ColorsObj
 * @property {number=} bg background of the entire panel from geo.top_bg_h to bottom
 */
/** @type ColorsObj */
var col = {}; // colors

// Get a list of jpg files from a folder
const g_image_list = utils.Glob('c:\\wallpaper\\*.jpg');


let ww = 0, wh = 0, nn = -1;
let g_img = null;
font = gdi.Font('Segoe Ui Black', 28, 0);  // you may need to adjust the font size

// Trigger every 10 seconds.
let g_timer = window.SetInterval(function () {
    if (fb.IsPlaying && !fb.IsPaused)
      load_random_image_async();
   }, 10000);
 
// Trigger On track Change.
//function on_playback_new_track() {
//   load_random_image_async();
//}

// SEQUENTIAL
/*
// Load a sequential image from the list
const load_random_image_async = async (metadb, art_id) =>
{
    nn = nn + 1
    let path = g_image_list[nn];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    if (!g_img) {
        nn = 0;
        let path = g_image_list[nn];
        g_img = await gdi.LoadImageAsyncV2(window.ID, path);
        }
    window.Repaint();
};
load_random_image_async();

const load_random_image_async_s = async (metadb, art_id) =>
{
    let path = g_image_list[nn];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    window.Repaint();
};

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

function on_mouse_wheel(delta) {
    if (delta > 0) {
        nn = nn - 1;
        if (nn == -1) {nn = 0};
        load_random_image_async_s();
    }
    else {
        nn = nn + 1;
        if (nn == g_image_list.length) {nn = 0};
        load_random_image_async_s();
    }
}

function on_paint(gr) {
      if (!g_img) {
        nn = 0;
      return 1;
    }

    let scale_w = ww / g_img.Width;
    let scale_h = wh / g_img.Height;
    let scale = Math.min(scale_w, scale_h);
    let pos_x = 0, pos_y = 0;
    if (scale_w < scale_h) {
        pos_y = (wh - g_img.Height * scale) / 2;
    }
    else if (scale_w > scale_h) {
        pos_x = (ww - g_img.Width * scale) / 2;
    }
    col.bg = RGB(0, 0, 0);   
    gr.FillSolidRect(0, 1, ww, wh, col.bg);
    gr.DrawImage(g_img, pos_x, pos_y, g_img.Width * scale, g_img.Height * scale, 0, 0, g_img.Width, g_img.Height);
    gr.DrawString((nn + 1)  + ' of ' + g_image_list.length, font, RGB(0,255,255), 25, 10, 200, 25, 0);  // you may need to adjust position
}
*/

// RANDOM
///*
const load_random_image_async = async (metadb, art_id) =>
{
    // Load a random image from the list
    let path = g_image_list[Math.floor(Math.random() * g_image_list.length)];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    window.Repaint();
};

load_random_image_async();

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

function on_mouse_wheel() {
   load_random_image_async();
}

function on_paint(gr) {
    if (!g_img) {
        return;
    }

    let scale_w = ww / g_img.Width;
    let scale_h = wh / g_img.Height;
    let scale = Math.min(scale_w, scale_h);
    let pos_x = 0, pos_y = 0;
    if (scale_w < scale_h) {
        pos_y = (wh - g_img.Height * scale) / 2;
    }
    else if (scale_w > scale_h) {
        pos_x = (ww - g_img.Width * scale) / 2;
    }
    col.bg = RGB(0, 0, 0);   
    gr.FillSolidRect(0, 1, ww, wh, col.bg);
    gr.DrawImage(g_img, pos_x, pos_y, g_img.Width * scale, g_img.Height * scale, 0, 0, g_img.Width, g_img.Height);
}
//*/

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #4
As far as panel placement goes, if you are using the default UI you may want to take a look at foo_flowin by ttsping.  This component lets you easily create pop-up panels.  I have one for the image viewer that covers my entire screen except for the menu bar at the top where I have a button to activate and de-activate the pop-up panel.  You can size and place it however you want.  Just a suggestion, but this component has completely changed my layout by opening up a whole new world of options for panel placement.

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #5
Thank you so much for the script! I got it just the way i want it, finally i can completely drop winamp! I saved foo_flowin just as well just to be sure, though so far i have no use for it. One last question is there also a component or a way to lock Foobar's window and panel's size in the default UI ? I managed to get it to look just the way i wanted but any resize might ruin the look...

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #6
I'm glad it's working out for you.  I'm afraid I don't have an answer for you on the resizing.  I've seen numerous posts on the subject, but never paid much attention since I always use full screen mode.

You may not want to make any further changes if it's working the way you want it now, but I have copied in my latest version just in case.  I have added a switch with different values to turn off auto cycling, turn on auto cycling and turn on track cycling.  I also added a mode switch to select sequential or random cycling to eliminate large blocks of code that need to be commented out.  I should warn you that the sequential mode does not always exactly match the file name sequence in the folder, though it seems to be usually close..  The file names are loaded into an array by the Spider Monkey Glob utility and I can only process them in the sequence loaded by the utility.

I also need to warn you that the script crashes sometimes when applying new changes with an error message that makes no sense to me.  But doing a reload seems to correct it.  The only pain is it can issue multiple error messages and you have to close each one one at a time until they are all closed.  So far this only seems to happen with the sequential mode.

My new code is listed below.  Let me know if you have any questions.

window.DefinePanel("LoadImageAsyncV2", { author: "TheQwertiest" });
include(`${fb.ComponentPath}docs\\Flags.js`);
include(`${fb.ComponentPath}docs\\Helpers.js`);

/**
 * @typedef {Object} ColorsObj
 * @property {number=} bg background of the entire panel from geo.top_bg_h to bottom
 */
/** @type ColorsObj */
var col = {}; // colors

// Get a list of jpg files from a folder
const g_image_list = utils.Glob('c:\\wallpaper\\*.jpg');

// SWITCHES
// nn = -1, (cyc 0 = no autocycle, cyc 1 = autocycle, cyc 2 = track cycle) (mode 0 = sequential, mode 1 = random)
let ww = 0, wh = 0, nn = -1; cyc = 1; mode = 0;
let g_img = null;
font = gdi.Font('Segoe Ui Black', 28, 0);

// Trigger every 10 seconds
let g_timer = window.SetInterval(function () {
    if (fb.IsPlaying && !fb.IsPaused && cyc == 1)
      if (mode == 0)
        load_seq_image_async_a();
      else
        load_random_image_async();     
   }, 10000);
 
// Trigger On track Change
function on_playback_new_track() {
  if (cyc == 3) {
    if (mode == 0)
      load_seq_image_async_a();
    else
      load_random_image_async();
  } 
}

// Load a sequential image from the list on autocycle
const load_seq_image_async_a = async (metadb, art_id) =>
{
    nn = nn + 1
    let path = g_image_list[nn];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    if (!g_img) {
        nn = 0;
        let path = g_image_list[nn];
        g_img = await gdi.LoadImageAsyncV2(window.ID, path);
        }
    window.Repaint();
};

// Load a sequential image from the list on mouse wheel
const load_seq_image_async_m = async (metadb, art_id) =>
{
    let path = g_image_list[nn];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    window.Repaint();
};

// Load a random image from the list
const load_random_image_async = async (metadb, art_id) =>
{
    let path = g_image_list[Math.floor(Math.random() * g_image_list.length)];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    window.Repaint();
};

if (mode == 0)
   load_seq_image_async_a();
else
   load_random_image_async();

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

function on_mouse_wheel(delta) {
  if (mode == 0) {
    if (delta > 0) {
        nn = nn - 1;
        if (nn == -1) {nn = 0};
        load_seq_image_async_m();
    }
    else {
        nn = nn + 1;
        if (nn == g_image_list.length) {nn = 0};
        load_seq_image_async_m();
    }}
  else 
     load_random_image_async();
}

function on_paint(gr) {
      if (!g_img) {
        nn = 0;
      return 1;
    }
   
    let scale_w = ww / g_img.Width;
    let scale_h = wh / g_img.Height;
    let scale = Math.min(scale_w, scale_h);
    let pos_x = 0, pos_y = 0;
    if (scale_w < scale_h) {
        pos_y = (wh - g_img.Height * scale) / 2;
    }
    else if (scale_w > scale_h) {
        pos_x = (ww - g_img.Width * scale) / 2;
    }
    col.bg = RGB(0, 0, 0);   
    gr.FillSolidRect(0, 1, ww, wh, col.bg);
    gr.DrawImage(g_img, pos_x, pos_y, g_img.Width * scale, g_img.Height * scale, 0, 0, g_img.Width, g_img.Height);
    if (mode == 0)
      gr.DrawString((nn + 1)  + ' of ' + g_image_list.length, font, RGB(0,255,255), 25, 10, 200, 25, 0);
}

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #7
One last question is there also a component or a way to lock Foobar's window and panel's size in the default UI ?

Turn on Layout Editing Mode and right-click Splitters to find 'Lock Width / Height' option for either side. (If you lock both sides the foobar window is prevented from resizing in that dimension)

@ dwmartin0906 you could paste code into a code box (the # button) when replying. ;)

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #8
*snip wall of text*

Thank you for the update! Saved the post, for now changing the image randomly/sequentially by track is exactly what i wanted, but in the future i might want to play with it more so its nice to know i got more options :).

One last question is there also a component or a way to lock Foobar's window and panel's size in the default UI ?

Turn on Layout Editing Mode and right-click Splitters to find 'Lock Width / Height' option for either side. (If you lock both sides the foobar window is prevented from resizing in that dimension)
lol so that's why i couldn't find the option anymore i need to right click on the splitter, this is perfect btw thank you very much!

@ dwmartin0906 you could paste code into a code box (the # button) when replying. ;)

Its good for future reference but I'm okay with this as well, because its easier to just grab and save the script together with the post :P.

---------------------------

Thank you very much to both of you, this made my foobar experience perfect, functionally i always liked it more then winamp but with this features it became my favorite player experience ever! And with all the options and panels at my disposal its way better then what i got with that winamp skin, seriously both to you and all the devs out there if any manage to read this thank you very much!

 

Re: Looking for a Foobar Skin that displays your set images rather then cover

Reply #9
Hi.  I know you are happy with what you have now, but in case you ever get curious I am posting my latest version.  Just ignore if not interested.  In addition to cleaning up the code and adding lots off comments, I've added some enhancements.

1.  The cursor now disappears after a specified time.
2.  You can toggle between random and sequential display modes by double clicking the middle mouse button.
3.  You can toggle auto cycle on and off  by double clicking the left mouse button.
4.  You can toggle between cycling on a track change and auto cycle on with shift-left mouse button.

Code: [Select]
window.DefinePanel("LoadImageAsyncV2", { author: "TheQwertiest" });
include(`${fb.ComponentPath}docs\\Flags.js`);
include(`${fb.ComponentPath}docs\\Helpers.js`);

// INITIALIZATION
// Cycle mode (cyc = 0 turns autocycle off, cyc = 1 turns autocycle on, cyc = 2 cycles on track change)
// Display mode (mode = 0 is sequential, mode = 1 is random)
// cyctm is the image auto cycle time in milliseconds
/*
nn = -1 is the start pos for sequential mode (nn is the index used to access the image list array loaded by utils.Glob)
Sequential mode processes image files in the sequence in which they are loaded into the image list array
This is not neccessarily the file name sequence from the folder, although it usually appears to be close
*/
//
let ww = 0, wh = 0, nn = -1, cyc = 1, mode = 0, cyctm = 10000; // Switches
let g_img = null; // Initialize image
let hideCursorTimer; // Timer for hiding cursor
let g_leave = 0; // Switch to prevent cursor hiding after leaving panel
var col = {}; // Colors
font = gdi.Font('Segoe Ui Black', 28, 0); // Font for displayed text (may need to change font size for non-4K display

// Get an image list of jpg files from a folder and load into an array
const g_image_list = utils.Glob('c:\\wallpaper\\*.jpg');

// Trigger image auto cycle every 10 seconds
let g_timer = window.SetInterval(function () {
    if (fb.IsPlaying && !fb.IsPaused && cyc == 1)
      if (mode == 0)
        load_seq_image_async_a();
      else
        load_random_image_async();     
   }, cyctm);
  
// Trigger image cycle on track change
function on_playback_new_track() {
  if (cyc == 2) {
    if (mode == 0)
      load_seq_image_async_a();
    else
      load_random_image_async();
  } 
}
 
function on_mouse_leave() {
  g_leave = 1;
}

// Hide cursur after 2 seconds
function on_mouse_move(x, y, m) {
  if (x >= 0 && x < ww && y >= 0 && y < wh)
g_leave = 0; 
  window.SetCursor(32512); // arrow 
  clearTimeout(hideCursorTimer);
  hideCursorTimer = setTimeout(() => {
    if (g_leave == 0)  
      window.SetCursor(-1); // hide cursor
  }, 2000);
}

// Load a sequential image from the image list on autocycle
const load_seq_image_async_a = async (metadb, art_id) =>
{
    nn = nn + 1
    let path = g_image_list[nn];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    if (!g_img) {
        nn = 0;
        let path = g_image_list[nn];
        g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    }
    window.Repaint();
};

// Load a sequential image from the image list on mouse wheel
const load_seq_image_async_m = async (metadb, art_id) =>
{
    let path = g_image_list[nn];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    window.Repaint();
};

// Load a random image from the image list
const load_random_image_async = async (metadb, art_id) =>
{
    let path = g_image_list[Math.floor(Math.random() * g_image_list.length)];
    g_img = await gdi.LoadImageAsyncV2(window.ID, path);
    window.Repaint();
};

// Initial image load
if (mode == 0)
  load_seq_image_async_a();
else
  load_random_image_async();

// Initialize window dimensions
function on_size() {
  ww = window.Width;
  wh = window.Height;
}

// Display previous/next image with mouse wheel
function on_mouse_wheel(delta) {
  if (mode == 0) {
    if (delta > 0) {
      nn = nn - 1;
      //if (nn == -1) {nn = 0};
      if (nn == -1)
        nn = g_image_list.length - 1;
      load_seq_image_async_m();
    }
    else {
      nn = nn + 1;
      if (nn == g_image_list.length)
        nn = 0;
      load_seq_image_async_m();
    }
  }
  else 
  load_random_image_async();
}
 
// Toggle auto cycle with left mouse button double click (0 = autocycle off, 1 = autocycle on)
function on_mouse_lbtn_dblclk(x, y) {
  if (cyc == 1)
    cyc = 0;
  else
    cyc = 1;
  window.Repaint();
}

// Toggle between track cycle and auto cycle with shift-left mouse button up (1 = autocycle on, 2 = treck cycle)
function on_mouse_lbtn_up(x, y) {
  if (utils.IsKeyPressed(VK_SHIFT)) {   
    if (cyc == 2)
      cyc = 1;
    else
     cyc = 2;
    window.Repaint();}
  //else if (utils.IsKeyPressed(VK_CONTROL)) {
  //   fieldname = utils.InputBox(window.ID, "prompt", "caption", "");   
  //  window.Repaint();
  //}
}

// Toggle display mode with middle mouse button double click (0 = sequential, 1 = random)
function on_mouse_mbtn_dblclk(x, y) {
  if (mode == 0)
    mode = 1;
  else
    mode = 0;
  if (mode == 0)
    load_seq_image_async_m();
  else
    load_random_image_async();
}

// Repaint window
function on_paint(gr) {
  if (!g_img) { // reset image list index and return if no image
    nn = 0;
    return 1;
  }
   
  let scale_w = ww / g_img.Width;
  let scale_h = wh / g_img.Height;
  let scale = Math.min(scale_w, scale_h);
  let pos_x = 0, pos_y = 0;
  if (scale_w < scale_h) {
    pos_y = (wh - g_img.Height * scale) / 2;
  }
  else if (scale_w > scale_h) {
    pos_x = (ww - g_img.Width * scale) / 2;
  }
   
  // May need to change DrawString positions for non-4K display (x-horizontal, y-vertical, w-width, h-heigth)
  col.bg = RGB(0, 0, 0); // Black background
  gr.FillSolidRect(0, 1, ww, wh, col.bg);
  gr.DrawImage(g_img, pos_x, pos_y, g_img.Width * scale, g_img.Height * scale, 0, 0, g_img.Width, g_img.Height);
  if (mode == 0) // Sequential mode
    gr.DrawString((nn + 1)  + ' of ' + g_image_list.length, font, RGB(0,255,255), 15, 10, 200, 25, 0);
  else // Random mode
    gr.DrawString('Random', font, RGB(0,255,255), 15, 10, 200, 25, 0);
   
  if (cyc == 0) {
    gr.DrawString('Auto Cycle Off', font, RGB(0,255,255), 15, 35, 250, 25, 0);}
  else if (cyc == 1) {
    gr.DrawString('Auto Cycle On', font, RGB(0,255,255), 15, 35, 250, 25, 0);}
  else if (cyc == 2) {
    gr.DrawString('Track Cycle On', font, RGB(0,255,255), 15, 35, 250, 25, 0);}
}