eurekagliese

Fluent Control Panel 0.9

Aug 4th, 2025
191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 20.44 KB | Source Code | 0 0
  1. 'use strict';
  2.  
  3. window.DefineScript('Fluent Control Panel 0.9', {author:'eurekagliese', options:{grab_focus:false}});
  4. include(fb.ComponentPath + 'samples\\complete\\js\\lodash.min.js');
  5. include(fb.ComponentPath + 'samples\\complete\\js\\helpers.js');
  6. include(fb.ComponentPath + 'samples\\complete\\js\\panel.js');
  7. include(fb.ComponentPath + 'samples\\complete\\js\\seekbar.js');
  8. include(fb.ComponentPath + 'samples\\complete\\js\\rating.js');
  9. include(fb.ComponentPath + 'samples\\complete\\js\\volume.js');
  10.  
  11. const ColourTypeCUI = {
  12.     text: 0,
  13.     selection_text: 1,
  14.     inactive_selection_text: 2,
  15.     background: 3,
  16.     selection_background: 4,
  17.     inactive_selection_background: 5,
  18.     active_item_frame: 6
  19. };
  20.  
  21. const ColourTypeDUI = {
  22.     text: 0,
  23.     background: 1,
  24.     highlight: 2,
  25.     selection: 3
  26. };
  27.  
  28. let g_is_default_ui = window.InstanceType;
  29. let g_textcolour = 0;
  30. let g_textcolour_hl = 0;
  31. let g_backcolour = 0;
  32. let g_hot = false;
  33.  
  34. let g_metadb = null;
  35. let g_img = null;
  36. let blur_img = null;
  37. let blur_radius = 24;
  38. let overlay = 128;
  39.  
  40. let ww = 0;
  41. let wh = 0;
  42.  
  43. let bx = 0;
  44. let by = 0;
  45. let cx = 0;
  46. let cy = 0;
  47.  
  48. var nextTrackInfo = "No next track."; // Stores the text to display
  49. var isPlaying = false; // To track playback state
  50.  
  51. let colours = {
  52.     red : _RGB(255, 0, 0),
  53.     love : _RGB(255, 0, 0),
  54.     seekbar_background : _RGBA(128, 128, 128, 128),
  55.     rating_bg : _RGBA(128, 128, 128, 224)
  56. };
  57.  
  58. let properties = {
  59.     art : new _p('FLUENT.ALBUMART.SHOW', true),
  60.     vol : new _p('FLUENT.VOLUME.SHOW.', true),
  61.     blur : new _p('FLUENT.BLUR.SHOW', false),
  62.     sbar : new _p('FLUENT.SEEKBAR.SHOW', true),
  63.     nxt : new _p('FLUENT.NEXT.SHOW', true),
  64.     rate : new _p('FLUENT.BUTTON.RATING', _RGB(255, 200, 0))
  65. };
  66.  
  67. let tfo = {
  68.     artist : fb.TitleFormat('%artist%'),
  69.     title : fb.TitleFormat('%title%'),
  70.     playback_time : fb.TitleFormat('%playback_time%  '),
  71.     length : fb.TitleFormat('  %length%'),
  72.     lov: fb.TitleFormat('$if2(%loved%,0)')
  73. };
  74.  
  75. // font sizes
  76. let fluent_font = gdi.Font('Segoe Fluent Icons', 48);
  77. let fluent_font_hover = gdi.Font('Segoe Fluent Icons', 54);
  78. let chara = {
  79.     heart_on : '\ueb52',
  80.     heart_off : '\ueb51',
  81.     heart_break : '\ue00C',
  82.     stop : '\uE71A',
  83.     prev : '\uE892',
  84.     play : '\uE768',
  85.     pause : '\uE769',
  86.     next : '\uE893',
  87.     add_queue : '\ue109',
  88.     search : '\uE721',
  89.     consol : '\ue8e4',
  90.     settings : '\uE713',
  91.     volume : '\ue767',
  92.     vol0 : '\uE992',
  93.     vol1 : '\uE993',
  94.     vol2 : '\uE994',
  95.     vol3 : '\uE995',
  96.     shuffle : '\ue8b1',
  97.     rating_on : '\ue735',
  98.     rating_off : '\ue734',
  99. };
  100. //////////////////////////////////////////////////////////////
  101.  
  102. let panel = new _panel();
  103. let seekbar = new _seekbar(0, 0, 0, 0);
  104. let buttons = new _buttons();
  105. let rating = new _rating(0, 0, 14, 0); // x, y, size, colour
  106. let volume = new _volume(0, 0, 100, 40);
  107. let isLoved = 0;
  108. let isLovedh = 0;
  109.  
  110. const bs = _scale(32);
  111.  
  112. get_colours();
  113. panel.item_focus_change();
  114. update_album_art(fb.GetNowPlaying());
  115.  
  116. updateNextTrackInfo(); // Initial update
  117.  
  118. buttons.update = () => {
  119.     const x = ((panel.w - (bs * 7)) / 2);
  120.     const y = _scale(4);
  121.     let lv = tfo.lov.Eval();
  122.  
  123.     buttons.buttons.love = new _button(x, y, bs, bs, {
  124.         normal : lv == 1 ? _chrToImg(chara.heart_on, colours.love, fluent_font) : _chrToImg(chara.heart_off, g_textcolour, fluent_font),
  125.         hover : lv == 1 ? _chrToImg(chara.heart_break, colours.red, fluent_font_hover) : _chrToImg(chara.heart_on, colours.love, fluent_font_hover)}, () => {
  126.         let selected_items = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
  127.         if (selected_items && selected_items.Count !== 0) {
  128.             const tmp = isLovedh == isLoved ? "1" : isLovedh;
  129.             let tags = {};
  130.             tags['LOVED'] = tmp;
  131.             selected_items.UpdateFileInfoFromJSON(JSON.stringify(tags));
  132.             isLoved = !isLoved;
  133.         }
  134.      }, '');
  135.     buttons.buttons.stop = new _button(x + bs, y, bs, bs, {normal : fb.StopAfterCurrent ? _chrToImg(chara.stop, colours.red, fluent_font) : _chrToImg(chara.stop, g_textcolour, fluent_font), hover : _chrToImg(chara.stop, g_textcolour_hl, fluent_font_hover)}, () => { fb.Stop(); }, '');
  136.     buttons.buttons.previous = new _button(x + (bs * 2), y, bs, bs, {normal : _chrToImg(chara.prev, g_textcolour, fluent_font), hover : _chrToImg(chara.prev, g_textcolour_hl, fluent_font_hover)}, () => { fb.Prev(); }, '');
  137.     buttons.buttons.play = new _button( x + (bs * 3), y, bs, bs, {normal : !fb.IsPlaying || fb.IsPaused ? _chrToImg(chara.play, g_textcolour, fluent_font) : _chrToImg(chara.pause, g_textcolour, fluent_font), hover : !fb.IsPlaying || fb.IsPaused ? _chrToImg(chara.play, g_textcolour_hl, fluent_font_hover) : _chrToImg(chara.pause, g_textcolour_hl, fluent_font_hover)}, () => { fb.PlayOrPause(); }, !fb.IsPlaying || fb.IsPaused ? '' : '');
  138.     buttons.buttons.next = new _button(x + (bs * 4), y, bs, bs, {normal : _chrToImg(chara.next, g_textcolour, fluent_font), hover : _chrToImg(chara.next, g_textcolour_hl, fluent_font_hover)}, () => { fb.Next(); }, '');
  139.     buttons.buttons.shuffle = new _button(x + (bs * 5), y, bs, bs, {normal : _chrToImg(chara.shuffle, plman.PlaybackOrder == 0 ? g_textcolour : g_textcolour_hl, fluent_font), hover : _chrToImg(chara.shuffle, colours.red, fluent_font_hover)}, () => { plman.PlaybackOrder = (plman.PlaybackOrder !== 4) ? 4 : 0; }, '');
  140.     buttons.buttons.add_queue = new _button(bx + (bs * 6), y, bs, bs, {normal : _chrToImg(chara.add_queue, g_textcolour, fluent_font), hover : _chrToImg(chara.add_queue, g_textcolour_hl, fluent_font_hover)}, () => {
  141.         let selected_items = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
  142.         if (selected_items && selected_items.Count !== 0) {
  143.             fb.RunContextCommandWithMetadb("Add to playback queue", selected_items);
  144.         }
  145.     }, '');
  146.    
  147.     const xx = (panel.w - (bs * 2));
  148.     const yy = Math.round((panel.h - bs) / 2);
  149.     buttons.buttons.volume = new _button(xx - (bs * 3), yy, bs, bs, {normal : fb.Volume === -100 ? _chrToImg(chara.vol0, g_textcolour, fluent_font) : fb.Volume > -4 ? _chrToImg(chara.vol3, g_textcolour, fluent_font) : fb.Volume > -15 ? _chrToImg(chara.vol2, g_textcolour, fluent_font) : fb.Volume > -30 ? _chrToImg(chara.vol1, g_textcolour, fluent_font) : _chrToImg(chara.vol0, g_textcolour, fluent_font), hover : _chrToImg(chara.volume, g_textcolour_hl, fluent_font_hover)}, () => { fb.VolumeMute(); }, '');
  150.     buttons.buttons.search = new _button(xx - (bs * 2), yy, bs, bs, {normal : _chrToImg(chara.search, g_textcolour, fluent_font), hover : _chrToImg(chara.search, g_textcolour_hl, fluent_font_hover)}, () => { fb.RunMainMenuCommand('Library/Search'); }, '');
  151.     buttons.buttons.consol = new _button(xx -  bs, yy, bs, bs, {normal : _chrToImg(chara.consol, g_textcolour, fluent_font), hover : _chrToImg(chara.consol, g_textcolour_hl, fluent_font_hover)}, () => { fb.ShowConsole(); }, '');
  152.     buttons.buttons.settings = new _button(xx, yy, bs, bs, {normal : _chrToImg(chara.settings, g_textcolour, fluent_font), hover : _chrToImg(chara.settings, g_textcolour_hl, fluent_font_hover)}, () => { fb.ShowPreferences(); }, '');
  153. }
  154.  
  155. function on_size() {
  156.     panel.size();
  157.  
  158.     ww = window.Width;
  159.     wh = window.Height;
  160.    
  161.     bx = ((panel.w - (bs * 7)) / 2);
  162.     by = seekbar.y - _scale(36);
  163.    
  164.     cx = (panel.w - (bs * 2));
  165.     cy = Math.round((panel.h - bs) / 2);
  166.    
  167.     seekbar.x = Math.round(panel.w * 0.22);
  168.     seekbar.w = panel.w - seekbar.x - _scale(280);
  169.     seekbar.h = _scale(14);
  170.     seekbar.y = panel.h * 0.6;
  171.    
  172.     rating.x = properties.art.enabled ? panel.h + 10 : _scale(12);
  173.     rating.y = panel.h - _scale(25);
  174.  
  175.     volume.x = panel.w - (bs * 5);
  176.     volume.y = seekbar.y + _scale(12);
  177.     volume.h = _scale(4);
  178.    
  179.     buttons.update();
  180. }
  181.  
  182. function on_paint(gr) {
  183.     gr.FillSolidRect(0, 0, panel.w, panel.h, g_backcolour);
  184.  
  185.     if (properties.blur.enabled && g_img) {
  186.         _drawImage(gr, blur_img, 0, 0, panel.w, panel.h, image.crop);
  187.         const overlayColor = window.IsDark ? _RGBA(0, 0, 0, overlay) : _RGBA(255, 255, 255, overlay);
  188.         gr.FillSolidRect(0, 0, panel.w, panel.h, overlayColor);
  189.     }
  190.  
  191.     buttons.paint(gr);
  192.    
  193.     if (properties.sbar.enabled) {
  194.         gr.FillSolidRect(seekbar.x, seekbar.y + _scale(4), seekbar.w + _scale(6), _scale(4), colours.seekbar_background); //seekbar_bg
  195.     }
  196.  
  197.     // test for playback time repaint
  198.     // gr.FillSolidRect(bx - (bs * 4), _scale(12), _scale(72), _scale(18), colours.red);
  199.  
  200.         if (fb.IsPlaying) {
  201.         rating.paint(gr);
  202.         if (properties.art.enabled) {
  203.             if (g_img) {
  204.             _drawImage(gr, g_img, 0, 0, panel.h, panel.h, image.crop_top);
  205.             }
  206.         }
  207.  
  208.         // Track information
  209.         gr.GdiDrawText(tfo.title.Eval(), panel.fonts.title, g_textcolour, properties.art.enabled ? panel.h + 10 : _scale(12), _scale(11) , properties.art.enabled ? panel.w / 4.7 : panel.w /4, _scale(18), LEFT);
  210.         gr.GdiDrawText(tfo.artist.Eval(), panel.fonts.normal, g_textcolour,  properties.art.enabled ? panel.h + 10 : _scale(12), Math.round((panel.h - _scale(18)) / 2), properties.art.enabled ? panel.w * 0.13 : panel.w * 0.23, _scale(18), LEFT);
  211.  
  212.         // Draw the next track information
  213.         if (plman.PlaybackOrder == 0 && properties.nxt.enabled) {
  214.             gr.GdiDrawText(nextTrackInfo, panel.fonts.normal, g_textcolour, (panel.w / 5) * 3.2, _scale(10), panel.w / 3.5, _scale(18), LEFT);
  215.         }
  216.        
  217.         gr.SetSmoothingMode(2);
  218.         // seekbar
  219.         if (fb.PlaybackLength > 0) {
  220.             const pos = seekbar.pos();
  221.             if (properties.sbar.enabled) {
  222.                 gr.FillSolidRect(seekbar.x, seekbar.y + _scale(4), pos, _scale(4), g_textcolour); //progress_bar
  223.                 gr.FillEllipse(seekbar.x + pos - 4, seekbar.y, _scale(12), _scale(12),g_textcolour_hl); //knob
  224.             }
  225.                 // playback time and length
  226.                 gr.GdiDrawText(tfo.playback_time.Eval(), panel.fonts.title, g_textcolour, bx - (bs * 4), _scale(20), _scale(72), 0, RIGHT);
  227.                 gr.GdiDrawText("/" + tfo.length.Eval(), panel.fonts.normal, g_textcolour, bx - (bs * 1.7), _scale(20), _scale(72), 0, LEFT);
  228.         }
  229.  
  230.         // volume
  231.         if (properties.vol.enabled) {
  232.             gr.FillSolidRect(volume.x, volume.y, volume.w, volume.h, colours.seekbar_background);
  233.             gr.FillSolidRect(volume.x, volume.y, volume.pos(), volume.h, g_textcolour);
  234.             gr.GdiDrawText(fb.Volume.toFixed(2) + ' dB', panel.fonts.normal, g_textcolour, _scale(8), volume.y  + _scale(2), volume.x + _scale(128), 0, RIGHT);
  235.         }      
  236.     }
  237. }
  238.  
  239. rating.paint = (gr) => {
  240.     if (panel.metadb) {
  241.         gr.SetTextRenderingHint(4);
  242.         for (let i = 0; i < rating.get_max(); i++) {
  243.             if (i + 1 > (rating.hover ? rating.hrating : rating.rating)) {
  244.                 gr.DrawString(chars.rating_off, rating.font, colours.rating_bg, rating.x + (i * rating.h), rating.y, rating.h, rating.h, SF_CENTRE);
  245.             } else {
  246.                 gr.DrawString(chars.rating_on, rating.font, rating.colour, rating.x + (i * rating.h), rating.y, rating.h, rating.h, SF_CENTRE);
  247.             }
  248.         }
  249.     }
  250. }
  251.  
  252. function on_metadb_changed() {
  253.     rating.metadb_changed();
  254. }
  255.  
  256. function on_playback_new_track() {
  257.     // When a new track starts, the "next" track might change.
  258.     updateNextTrackInfo();
  259.     update_album_art(g_metadb);
  260.     panel.item_focus_change();
  261.     buttons.update();
  262. }
  263.  
  264. function update_album_art(metadb) {
  265.     g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
  266.     g_img = utils.GetAlbumArtV2(g_metadb, 0);
  267.     if (g_img && g_img.Width > 200) {
  268.         var r = 200 / g_img.Width;
  269.         g_img = g_img.Resize(200, g_img.Height * r, 2);
  270.         if (g_img != null) {
  271.             blur_img = g_img.Clone(0, 0, g_img.Width, g_img.Height);
  272.             blur_img.StackBlur(blur_radius);
  273.         }
  274.     }
  275.     window.Repaint();
  276. }
  277.  
  278. // update the playback time
  279. function on_playback_time() {
  280.     window.RepaintRect(bx - (bs * 4), _scale(12), _scale(72), _scale(18));
  281. }
  282.  
  283. function on_playback_edited() {
  284.     buttons.update();
  285.     window.Repaint();
  286. }
  287.  
  288. function on_playback_seek() {
  289.     seekbar.playback_seek();
  290. }
  291.  
  292. function on_playback_stop() {
  293.     buttons.update();
  294.     window.Repaint();
  295. }
  296.  
  297. function on_playback_stop(reason) {
  298.     if (reason != 2) {
  299.         panel.item_focus_change();
  300.     }
  301. }
  302.  
  303. function on_playback_pause() {
  304.     buttons.update();
  305.     window.Repaint();
  306. }
  307.  
  308. function on_playback_starting() {
  309.     buttons.update();
  310.     window.Repaint();
  311. }
  312.  
  313. function on_playback_dynamic_info_track() {
  314.     panel.item_focus_change();
  315. }
  316.  
  317. function on_playback_order_changed() {
  318.     updateNextTrackInfo(); // Playback order affects what the "next" track is.    
  319.     buttons.update();
  320.     window.Repaint();
  321. }
  322.  
  323. function on_playlist_switch() {    
  324.     updateNextTrackInfo(); // Switching playlists means the next track might be different.
  325.     panel.item_focus_change();
  326. }
  327.  
  328. function on_item_focus_change() {
  329.     panel.item_focus_change();
  330. }
  331.  
  332. function on_colours_changed() {
  333.     get_colours();
  334.     buttons.update();
  335.     panel.colours_changed();
  336.     window.Repaint();
  337. }
  338.  
  339. function on_volume_change() {
  340.     buttons.update();
  341.     volume.volume_change();
  342.     window.Repaint();
  343. }
  344.  
  345. // mouse events
  346. function on_mouse_wheel(s) {
  347.     volume.wheel(s);
  348.     let easy_vol = vol2pos(fb.Volume);
  349.         easy_vol += s / 20;
  350.         fb.Volume = pos2vol(easy_vol);
  351. }
  352.  
  353. function on_mouse_move(x, y) {
  354.     if (!g_hot) {
  355.         g_hot = true;
  356.         window.SetCursor(IDC_HAND);
  357.         window.Repaint();
  358.     }
  359.     if (buttons.move(x, y) || rating.move(x, y) || seekbar.move(x, y) || volume.move(x, y)) return;
  360. }
  361.  
  362. function on_mouse_leave() {
  363.     if (g_hot) {
  364.         g_hot = false;
  365.         window.Repaint();
  366.     }
  367.     buttons.leave();
  368.     rating.leave();
  369. }
  370.  
  371. function on_mouse_lbtn_down(x, y) {
  372.     seekbar.lbtn_down(x, y);
  373.     volume.lbtn_down(x, y);
  374. }
  375.  
  376. function on_mouse_lbtn_up(x, y) {
  377.     if ([buttons, seekbar, rating, volume].some(el => el.lbtn_up(x, y))) {
  378.         return;
  379.     }
  380.     fb.RunMainMenuCommand('View/Show now playing in playlist');
  381.     // search artist in library
  382.     if (x < _scale(200) && x > _scale(12) && y < _scale(72) && y > _scale(12)) {
  383.         fb.ShowLibrarySearchUI(tfo.artist.Eval())
  384.     } return;
  385. }
  386.  
  387. function on_mouse_mbtn_up(x, y) {
  388.     // flush playback queue with middle click
  389.     if (buttons.buttons.add_queue.trace(x, y)) {
  390.         fb.RunMainMenuCommand("Flush playback queue");
  391.         return true
  392.     }
  393. }
  394.  
  395. function on_mouse_rbtn_up(x, y) {
  396.     if (buttons.buttons.stop.trace(x, y)) {
  397.         fb.StopAfterCurrent = !fb.StopAfterCurrent;
  398.         buttons.update();
  399.         return true
  400.     }  
  401.     if (buttons.buttons.add_queue.trace(x, y)) {
  402.         queueSelectedTracksRandomizedAppend(); // Add selected tracks to the queue in a random order (append to queue, do not flush)
  403.         return true
  404.     }  
  405.     if (buttons.buttons.search.trace(x, y)) {
  406.         plman.SetActivePlaylistContext(); //activated the current playlist
  407.         fb.RunMainMenuCommand('Edit/Search'); // search in current playlist
  408.         return true
  409.     }
  410.     if (rating.trace(x, y)) {
  411.         return panel.rbtn_up(x, y, rating);
  412.     }
  413.  
  414.     // menu
  415.     let m = window.CreatePopupMenu();
  416.     let s = window.CreatePopupMenu();
  417.     let c = fb.CreateContextMenuManager();
  418.     let col = window.CreatePopupMenu();
  419.     if (fb.IsPlaying) {
  420.         c.InitNowPlaying();
  421.         c.BuildMenu(s, 1);
  422.         s.AppendTo(m, MF_STRING, 'Now playing');
  423.         m.AppendMenuSeparator();
  424.     }
  425.     m.AppendMenuItem(MF_STRING, 10000, 'Show album art');
  426.     m.CheckMenuItem(10000, properties.art.enabled);
  427.     m.AppendMenuItem(MF_STRING, 10001, 'Show volume slider');
  428.     m.CheckMenuItem(10001, properties.vol.enabled);
  429.     m.AppendMenuItem(MF_STRING, 10002, 'Show blur effect');
  430.     m.CheckMenuItem(10002, properties.blur.enabled);   
  431.     m.AppendMenuItem(MF_STRING, 10003, 'Show seekbar');
  432.     m.CheckMenuItem(10003, properties.sbar.enabled);
  433.     m.AppendMenuItem(MF_STRING, 10004, 'Show next track');
  434.     m.CheckMenuItem(10004, properties.nxt.enabled);
  435.     m.AppendMenuSeparator();
  436.     col.AppendMenuItem(MF_STRING, 10005, 'Rating');
  437.     col.AppendTo(m, MF_STRING, 'Colours');
  438.     m.AppendMenuSeparator();
  439.     m.AppendMenuItem(MF_STRING, 10010, 'Configure');
  440.     const idx = m.TrackPopupMenu(x, y);
  441.     switch (idx) {
  442.     case 0:
  443.         break;
  444.     case 10000:
  445.         properties.art.toggle();
  446.         on_size();     
  447.         window.Repaint();
  448.         break;
  449.     case 10001:
  450.         properties.vol.toggle();       
  451.         window.Repaint();
  452.         break;
  453.     case 10002:
  454.         properties.blur.toggle();
  455.         window.Repaint();
  456.         break;     
  457.     case 10003:
  458.         properties.sbar.toggle();
  459.         window.Repaint();
  460.         break;
  461.     case 10004:
  462.         properties.nxt.toggle();
  463.         window.Repaint();
  464.         break;
  465.     case 10005:
  466.         properties.rate.value = utils.ColourPicker(window.ID, properties.rate.value);
  467.         get_colours();
  468.         buttons.update();
  469.         window.Repaint();
  470.         break;
  471.     case 10010:
  472.         window.ShowConfigureV2();
  473.         break;
  474.     default:
  475.         c.ExecuteByID(idx - 1);
  476.         break;
  477.     }
  478.     return true;
  479. }
  480.  
  481. function get_colours() {
  482.     rating.colour = properties.rate.value;
  483.     if (g_is_default_ui) { // DUI
  484.         g_textcolour = window.GetColourDUI(ColourTypeDUI.text);
  485.         g_textcolour_hl = window.GetColourDUI(ColourTypeDUI.highlight);
  486.         g_backcolour = window.GetColourDUI(ColourTypeDUI.background);
  487.     } else { // CUI
  488.         g_textcolour = window.GetColourCUI(ColourTypeCUI.text);
  489.         g_textcolour_hl = window.GetColourCUI(ColourTypeCUI.text);
  490.         g_backcolour = window.GetColourCUI(ColourTypeCUI.background);
  491.     }
  492. }
  493.  
  494. function vol2pos(v) {
  495.     return (Math.pow(10, v / 50) - 0.01) / 0.99;
  496. }
  497.  
  498. function pos2vol(pos) {
  499.     return 50 * Math.log(0.99 * pos + 0.01) / Math.LN10;
  500. }
  501.  
  502. /**
  503.  * Updates the information about the next upcoming track.
  504.  * Prioritizes tracks in the playback queue.
  505.  */
  506. function updateNextTrackInfo() {
  507.     var nextTrack = null;
  508.     // Use GetPlaybackQueueHandles to get a FbMetadbHandleList directly
  509.     var queueHandles = plman.GetPlaybackQueueHandles();
  510.  
  511.     // 1. Check for tracks in the playback queue first
  512.     if (queueHandles.Count > 0) {
  513.         // The first item in the queueHandles list is the highest priority next track
  514.         nextTrack = queueHandles[0];
  515.         // console.log("Next track from queue (from updateNextTrackInfo):", nextTrack ? fb.TitleFormat("%title%").EvalWithMetadb(nextTrack) : "N/A"); // For debugging
  516.     } else {
  517.         // 2. If no queue, check the next track in the current playlist
  518.         var currentPlaylistIndex = plman.PlayingPlaylist;
  519.         // Ensure a valid playlist is currently playing and it has items
  520.         if (currentPlaylistIndex !== -1 && plman.PlaylistItemCount(currentPlaylistIndex) > 0) {
  521.             var playingItemLocation = plman.GetPlayingItemLocation();
  522.             if (playingItemLocation.IsValid) {
  523.                 var currentIndex = playingItemLocation.PlaylistItemIndex;
  524.                 var playlistLength = plman.PlaylistItemCount(currentPlaylistIndex);
  525.  
  526.                 // Check if there's a next track in the playlist
  527.                 if (currentIndex < playlistLength - 1) {
  528.                     // Get all items in the current playlist and then access the next one by index
  529.                     var playlistItems = plman.GetPlaylistItems(currentPlaylistIndex);
  530.                     if (playlistItems && playlistItems.Count > currentIndex + 1) {
  531.                         nextTrack = playlistItems[currentIndex + 1];
  532.                         // console.log("Next track from playlist (from updateNextTrackInfo):", nextTrack ? fb.TitleFormat("%title%").EvalWithMetadb(nextTrack) : "N/A"); // For debugging
  533.                     }
  534.                 }
  535.             }
  536.         }
  537.     }
  538.  
  539.     // Format the track information for display
  540.     if (nextTrack) {
  541.         // Use TitleFormat to get artist and title reliably
  542.         var tf = fb.TitleFormat("%artist% - %title%");
  543.         nextTrackInfo = "Next: " + tf.EvalWithMetadb(nextTrack);
  544.     } else {
  545.         nextTrackInfo = "No next track.";
  546.     }
  547.  
  548.     window.Repaint(); // Request a repaint to update the display
  549. }
  550.  
  551. function on_playback_queue_changed() {
  552.     // Add a small delay to ensure foobar2000 has fully processed the queue change
  553.     window.SetTimeout(function() {
  554.         updateNextTrackInfo();
  555.     }, 50); // 50ms delay
  556. }
  557.  
  558. // Add selected tracks to the queue in a random order (append to queue, do not flush)
  559. function queueSelectedTracksRandomizedAppend() {
  560.     const activePlaylistIndex = plman.ActivePlaylist;
  561.     const selectedHandles = plman.GetPlaylistSelectedItems(activePlaylistIndex);
  562.  
  563.     if (!selectedHandles || selectedHandles.Count === 0) {
  564.         fb.ShowPopupMessage("No tracks selected. Please select one or more tracks in your playlist.");
  565.         return;
  566.     }
  567.  
  568.     // Fisher-Yates shuffle
  569.     for (let i = selectedHandles.Count - 1; i > 0; i--) {
  570.         const j = Math.floor(Math.random() * (i + 1));
  571.         const temp = selectedHandles[i];
  572.         selectedHandles[i] = selectedHandles[j];
  573.         selectedHandles[j] = temp;
  574.     }
  575.  
  576.     // Get all items in the playlist
  577.     const playlistItems = plman.GetPlaylistItems(activePlaylistIndex);
  578.  
  579.     // Add shuffled tracks to the queue (append)
  580.     for (let i = 0; i < selectedHandles.Count; i++) {
  581.         const idx = playlistItems.Find(selectedHandles[i]);
  582.         if (idx !== -1) {
  583.             plman.AddPlaylistItemToPlaybackQueue(activePlaylistIndex, idx);
  584.         }
  585.     }
  586.     // fb.ShowPopupMessage("Selected tracks appended to the queue in random order.");
  587. }
Advertisement
Add Comment
Please, Sign In to add comment