Foobar2000的自动化和扩展功能

Foobar2000是常用的音乐文件播放器。它提供了SDK,不过是C++,使用比较复杂。有人开发了两个工具JScript Panel 3和Spider Monkey Panel,提供了一些javascript的api,使用比较方便。下面是一些使用笔记。

先说JScript Panel 3。

1. 概述:

它的主要优点是比较新,支持最新版的foobar2000,不过作者已经不再更新,甚至把github上的所有文件包括文档都删除了。虽然组件本身还能下载,但查询文档很不方便,而且很多api都不提供,使用不便。另外一个问题是,如果把它放在flowin上,如果没有设置为“始终在最上面”,一失去焦点,只能点几个菜单命令才能显示面板,还不如旧版foobar方便。

2. 文件操作

utils里有几个方法可以操作。如果不够,还可以

var fso = new ActiveXObject("Scripting.FileSystemObject");

3. http请求

两种方法,一是直接用utils的HTTPRequestAsync方法,二是  var xhr = new ActiveXObject("Msxml2.XMLHTTP");

4. 控件

没有现成的控件,都要用gdi自己画(而且这个gdi还只能用JScript Panel 3提供的函数,有些函数如DrawText等都不提供),下面是自己画的checkbox:

var checkboxes = [];
var isFirst = true;

var DT_LEFT = 0;//align left, 
//var DT_LEFT       = 0x00000000;
//var DT_CENTER     = 0x00000001;
//var DT_RIGHT      = 0x00000002;
//var DT_VCENTER    = 0x00000004;
//var DT_SINGLELINE = 0x00000020;

function on_paint(gr) {
	gr.FillRectangle(0, 0, window.Width, window.Height, RGB(137,225,246));
	
	if (isFirst) {
		isFirst = false;
		checkboxes = [{
			label: 'foo',
			name: 'foo',
			x: 10,
			y: window.Height / 2  - 20,
			size: 16,
			checked: window.GetProperty("MyScript.CheckboxDemo.Checked", false),
			hover: false,
			down: false
		}];
	}	
	
	//checkbox
	for (var i = 0; i < checkboxes.length; i++) {
		if (checkboxes[i].hover) gr.FillRectangle(checkboxes[i].x - 2, checkboxes[i].y - 2, checkboxes[i].size + 4, checkboxes[i].size + 4, RGB(220,230,250));
		if (checkboxes[i].down)  gr.FillRectangle(checkboxes[i].x - 1, checkboxes[i].y - 1, checkboxes[i].size + 2, checkboxes[i].size + 2, RGB(200,210,240));

		gr.DrawRectangle(checkboxes[i].x, checkboxes[i].y, checkboxes[i].size, checkboxes[i].size, 1, RGB(0,0,0));

		if (checkboxes[i].checked) {
			gr.FillRectangle(checkboxes[i].x + 3, checkboxes[i].y + 3, checkboxes[i].size - 6, checkboxes[i].size - 6, RGB(0,120,215));
			gr.DrawLine(checkboxes[i].x + 4, checkboxes[i].y + checkboxes[i].size/2, checkboxes[i].x + checkboxes[i].size/2, checkboxes[i].y + checkboxes[i].size - 4, 2, RGB(255,255,255));
			gr.DrawLine(checkboxes[i].x + checkboxes[i].size/2, checkboxes[i].y + checkboxes[i].size - 4, checkboxes[i].x + checkboxes[i].size - 4, checkboxes[i].y + 4, 2, RGB(255,255,255));
		}

		gr.WriteTextSimple(checkboxes[i].label, '', RGB(0,0,0), checkboxes[i].x + checkboxes[i].size + 6, checkboxes[i].y - 2, 200, 24, DT_LEFT, 0);
	}
}

function on_mouse_move(x, y) {
	var checkboxIndex = inCheckbox(x,y);
	for (var i = 0; i < checkboxes.length; i++) {
		if (i != checkboxIndex) {
			checkboxes[i].hover = false;
		} else {
			checkboxes[i].hover = true;
		}
	}
	window.Repaint();
}

function on_mouse_lbtn_down(x, y) {
	var checkboxIndex = inCheckbox(x,y);
    if (checkboxIndex != -1) {
        checkboxes[checkboxIndex].down = true;
        window.Repaint();
    }
}

function on_mouse_lbtn_up(x, y) {
	var checkboxIndex = inCheckbox(x,y);
	var buttonIndex = inButton(x, y);
	console.log(buttonIndex);
    if (checkboxIndex != -1) {
		if (checkboxes[checkboxIndex].down) {
			checkboxes[checkboxIndex].checked = !checkboxes[checkboxIndex].checked;
			if (checkboxes[checkboxIndex].name == 'foo') {//这里放checked事件的处理代码
				window.SetProperty("MyScript.CheckboxDemo.Checked", checkboxes[checkboxIndex].checked);
			} 
	    }
		checkboxes[checkboxIndex].down = false;
    }
    window.Repaint();
}

function inCheckbox(x, y) {
	var checkboxIndex = -1;
	for (var i = 0; i < checkboxes.length; i++) {
		if (x >= checkboxes[i].x && x <= checkboxes[i].x + checkboxes[i].size &&
           y >= checkboxes[i].y && y <= checkboxes[i].y + checkboxes[i].size) {
			   checkboxIndex = i;
			   break;
		}
	}
    return checkboxIndex;
}

  

几点说明:
1)window.SetProperty("MyScript.CheckboxDemo.Checked", checkboxes[checkboxIndex].checked); 是将checkbox状态保存起来,下次打开foobar2000时用window.GetProperty("MyScript.CheckboxDemo.Checked", false)载入保存值
2)这里将checkbox的初始化放在on_paint函数里,是因为使用时发现有个问题,如果放在函数外面,foobar刚打开时,checkbox不会显示,只能手工点reload script才能显示。试了多种方法都不行,最后只能这样处理。
3)虽然只有一个checkbox,还是使用数组。这是为了便于以后再增加checkbox时,只需添加适当的if else,代码框架不需要改。
明显比较麻烦,不过,这个框架写好了,再加其他控件比如按钮等,就不是太麻烦了。

5. 事件处理
上面的on_mouse_lbtn_up等就是事件处理函数,好像不能自定义事件,只能使用它提供的。另外常用的有on_playback_new_track,在播放一个音轨时触发,比如下面的代码可以记录音轨的文件路径和标题:

function on_playback_new_track() {
	var metadb = fb.GetNowPlaying();
        if (metadb) {
            utils.WriteTextFile("c:\\temp\\test.txt", metadb.Path + ":" + fb.TitleFormat('%title%').Eval());
        }
}

6. 播放列表的处理

下面的代码里包含了一些(当然不是全部)可用的操作

var playlistIndex = plman.ActivePlaylist;
plman.SortByFormat(plman.ActivePlaylist, "%tracknumber%"); //按音轨号排序,没有号的音轨排在最前面
var trackHandle = plman.GetPlaylistItems(plman.ActivePlaylist).GetItem(1);
var handlList = fb.CreateHandleList(trackHandle);
handlList.UpdateFileInfoFromJSON(JSON.stringify({"RATING":"1"})); //设置文件的元数据信息,可以是foobar里有的如TRACKNUMBER,也可以是没有的
plman.InsertPlaylistItems(playlistIndex+1, 0, trackHandle);//将音轨加到另一个播放列表里,0代表加在第一个位置

Spider Monkey Panel所提供的函数比JScript Panel 3多,比如它有plman.AddPlaylistItemToPlaybackQueue函数等,文档也比较全,但是它只支持老版的foobar2000。

posted @ 2025-08-18 23:00  平静寄居者  阅读(36)  评论(0)    收藏  举报