雨中小町

 找回密码
 入驻小町
搜索
查看: 2513|回复: 15
打印 上一主题 下一主题

写一半才发现fb2k有个书签的组件

[复制链接]
跳转到指定楼层
1#
发表于 2019-12-10 21:00:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 mizuku 于 2019-12-11 14:02 编辑

想着,平时可能欲火焚身的时候想快速听一个音声里想听的地方,有没有办法做个标记呢,很多readme文件里面会提示相关的Play的时间点,但是还要打开看readme,太麻烦了
于是,就去学了学js,在foobar2000的WSH mod Plus里写了一个demo.
写到一半,才发现已经2006年就有相关的组件了,脸一黑(以前好像还用过,但是官网撤下了这个插件,后来忘了)
组件在这里:
https://hydrogenaud.io/index.php?topic=46612.0        (似乎中国ip被禁了,需要fq)
https://www.dlldown.co/F/2013/05 ... arks_dll_24503.html   (应该不需要fq)

这东西是真的不错,不仅能记住位置,还能记住playlist,音量.双击就可以自动打开文件并播放相应的位置
但正是如此,所以采取了把所有信息记录在单独的数据文件里面,并且文件位置改变相应的书签就会失效

这和我的设想还是不一样的,我单纯想把信息记录在文件的tag里面,包括书签名称,时间,注释. 缺点就是必须先播放这个文件,才能使用书签
目前只写了基础的功能,只能标记时间,并且没有滚动条,有相关安装经验的同学可以试试,仅仅是一个想法而已,直接作废怪可惜的:
  1. // Use with GdiDrawText()
  2. // {{
  3. DT_TOP = 0x00000000;
  4. DT_LEFT = 0x00000000;
  5. DT_CENTER = 0x00000001;
  6. DT_RIGHT = 0x00000002;
  7. DT_VCENTER = 0x00000004;
  8. DT_BOTTOM = 0x00000008;
  9. DT_WORDBREAK = 0x00000010;
  10. DT_SINGLELINE = 0x00000020;
  11. DT_EXPANDTABS = 0x00000040;
  12. DT_TABSTOP = 0x00000080;
  13. DT_NOCLIP = 0x00000100;
  14. DT_EXTERNALLEADING = 0x00000200;
  15. DT_CALCRECT = 0x00000400; // [1.2.1] Handles well
  16. DT_NOPREFIX = 0x00000800; // NOTE: Please use this flag, or a '&' character will become an underline '_'
  17. DT_INTERNAL = 0x00001000;
  18. DT_EDITCONTROL = 0x00002000;
  19. DT_PATH_ELLIPSIS = 0x00004000;
  20. DT_END_ELLIPSIS = 0x00008000;
  21. DT_MODIFYSTRING = 0x00010000; // do not use
  22. DT_RTLREADING = 0x00020000;
  23. DT_WORD_ELLIPSIS = 0x00040000;
  24. DT_NOFULLWIDTHCHARBREAK = 0x00080000;
  25. DT_HIDEPREFIX = 0x00100000;
  26. DT_PREFIXONLY = 0x00200000;

  27. var MF_SEPARATOR = 0x00000800;
  28. var MF_ENABLED = 0x00000000;
  29. var MF_GRAYED = 0x00000001;
  30. var MF_DISABLED = 0x00000002;
  31. var MF_UNCHECKED = 0x00000000;
  32. var MF_CHECKED = 0x00000008;
  33. var MF_STRING = 0x00000000;
  34. var MF_POPUP = 0x00000010;
  35. var MF_RIGHTJUSTIFY = 0x00004000;

  36. VK_SHIFT = 0x10;
  37. // ================================================================================= //
  38. var safeMode = false;

  39. try {
  40.         WshShell = new ActiveXObject("WScript.Shell");
  41.         doc = new ActiveXObject("htmlfile");
  42. } catch (e) {
  43.         fb.trace("----------------------------------------------------------------------");
  44.         fb.trace(e + "\nFix: Disable safe mode in Preferences > Tools > WSH Panel Mod");
  45.         fb.trace("----------------------------------------------------------------------");
  46.         safeMode = true;
  47. }

  48. // ================================================================================= //

  49. function print(msg) {
  50.         fb.trace("---> " + msg);
  51. }

  52. function $(field, metadb) {
  53.         var tf;
  54.         try {
  55.                 tf = fb.TitleFormat(field).EvalWithMetadb(metadb);
  56.         } catch (e) {
  57.                 tf = e + " (Invalid metadb!)"
  58.         };
  59.         return tf;
  60. }

  61. var g_textcolor = RGB(100,100,100);
  62. var g_backcolor = RGB(200,200,200);
  63. var g_font = gdi.Font("Tahoma", 12)
  64. var g_select_txt_col;
  65. var g_select_bg_col;

  66. function TimeFmt(t) {
  67.         var zpad = function (n) {
  68.                 var str = n.toString();
  69.                 return (str.length < 2) ? "0" + str : str;
  70.         }
  71.         var h = Math.floor(t / 3600);
  72.         t -= h * 3600;
  73.         var m = Math.floor(t / 60);
  74.         t -= m * 60;
  75.         var s = Math.floor(t);
  76.         if (h > 0)
  77.                 return h.toString() + ":" + zpad(m) + ":" + zpad(s);
  78.         return m.toString() + ":" + zpad(s);
  79. }
  80. function toRGB(d) { // convert back to RGB values
  81.         var d = d - 0xff000000;
  82.         var r = d >> 16;
  83.         var g = d >> 8 & 0xFF;
  84.         var b = d & 0xFF;
  85.         return [r, g, b];
  86. }
  87. function blendColors(c1, c2, factor) {
  88.         // When factor is 0, result is 100% color1, when factor is 1, result is 100% color2.
  89.         var c1 = toRGB(c1);
  90.         var c2 = toRGB(c2);
  91.         var r = Math.round(c1[0] + factor * (c2[0] - c1[0]));
  92.         var g = Math.round(c1[1] + factor * (c2[1] - c1[1]));
  93.         var b = Math.round(c1[2] + factor * (c2[2] - c1[2]));
  94.         //fb.trace("R = " + r + " G = " + g + " B = " + b);
  95.         return (0xff000000 | (r << 16) | (g << 8) | (b));
  96. }
  97. function setAlpha(color, a) {
  98.         return ((color & 0x00ffffff) | (a << 24));
  99. }

  100. function RGBA(r, g, b, a) {
  101.     return ((a << 24) | (r << 16) | (g << 8) | (b));
  102. }

  103. function RGB(r, g, b) {
  104.     return (0xff000000 | (r << 16) | (g << 8) | (b));
  105. }
  106. //////////////////////////////  common.js END  //////////////////////////////////////

  107. ButtonStates = {
  108.         normal : 0,
  109.         hover : 1,
  110.         down : 2
  111. };


  112. var g_textcolor_2 = RGB(11,11,11);
  113. var row = 20  // 每一记录的行高
  114. var ww = window.Width;
  115. var wh = window.Height;

  116. var g_metadb = null;
  117. var g_tabs = [];


  118. function metaFind(info, name) {
  119.     for (var i = 0; i < info.MetaCount; i++) {
  120.         if (info.MetaName(i).toLowerCase() == name.toLowerCase())
  121.             return i;
  122.     }
  123.    
  124.     return -1;
  125. }

  126. function metaGet(info, name) {
  127.     var i = metaFind(info, name);
  128.     var arr = []
  129.     if (i != -1) {
  130.         for (var j = 0; j < info.MetaValueCount(i); j++) {
  131.             arr.push(info.MetaValue(i, j));
  132.         }
  133.     }
  134.    
  135.     return arr;
  136. }

  137. function updateGTabs(){  //update g_tabs统一从这个函数
  138.     metadb = fb.getNowPlaying();
  139.     var arr = metaGet(metadb.GetFileInfo(),"DOUMRK");
  140.     var i;
  141.     g_tabs = [];
  142.     for(i=0;i<arr.length;i++){
  143.         g_tabs.push(new tab(TimeFmt(parseInt(arr[i]))));
  144.     }
  145. }

  146. function string2time(s){
  147.     var a =  s.split(':')
  148.     var total = 0;
  149.     var i;
  150.     for(i=0;i<a.length;i++){
  151.         total = total*60+parseInt(a[i]);
  152.     }
  153.     return total;
  154. }

  155. function list_menu(x,y,idx){
  156.         var _menu = window.CreatePopupMenu();
  157.         
  158.         if(idx == -1) {
  159.             // add new
  160.             _menu.AppendMenuItem(MF_STRING, 100, "add new");
  161.         }
  162.         else{
  163.             _menu.AppendMenuItem(MF_STRING, 200, "remove");
  164.             _menu.AppendMenuItem(MF_STRING, 201, "clearall");
  165.         }
  166.         var ret;
  167.         ret = _menu.TrackPopupMenu(x, y);
  168.         var metadb = fb.getNowPlaying();
  169.         switch(ret){
  170.             case(100):
  171.                 var mark_time = String(Math.round(fb.PlaybackTime,0));
  172.                
  173.                 if(metadb&&mark_time!=='0'){
  174.                     var arr = metaGet(metadb.GetFileInfo(),"DOUMRK");
  175.                     arr.push(mark_time);
  176.                     metadb.UpdateFileInfoSimple("DOUMRK",arr.join(';'),"DOUMRK");
  177.                     // 很奇怪,应该有更好的何时同步的做法
  178.                     g_tabs.push(new tab(TimeFmt(parseInt(mark_time))));
  179.                     // updateGTabs();
  180.                     window.repaint();
  181.                 }
  182.                 break;

  183.             case(200):
  184.                 g_tabs.splice(idx,1);
  185.                 var arr = get_mark_from_g_tabs();
  186.                 if(arr.length > 0){
  187.                     metadb.UpdateFileInfoSimple("DOUMRK",arr.join(';'),"DOUMRK");
  188.                 }
  189.                 window.repaint();
  190.                 break;
  191.             case(201):
  192.                 g_tabs = [];
  193.                 metadb.UpdateFileInfoSimple("DOUMRK","");
  194.                 window.repaint();
  195.                 break;
  196.         }
  197.     _menu.Dispose();
  198.    
  199. }

  200. function get_mark_from_g_tabs(){
  201.     var i;
  202.     var arr = [];
  203.     for(i=0;i<g_tabs.length;i++){
  204.         arr.push(g_tabs[i].str);
  205.     }
  206.     return arr;
  207. }


  208. function indexOf2(array,item){
  209.     var i = 0;
  210.     for(i=0;i<array.length;i++){
  211.         if(array[i] === item){break;}
  212.     }
  213.     if(i===array.length){i = -1;}
  214.     return i
  215. }

  216. tab = function (str) {
  217.         this.w,
  218.         this.h = 0;
  219.         this.str = str;
  220.         this.state = ButtonStates.normal;
  221.         this.Font = gdi.Font("Segoe UI", 14);
  222.         this.update = function (active) {
  223.                 this.active = active;
  224.                 this.repaint();
  225.         }
  226.     // this.btn = new button(clear_off,clear_ov,clear_on);
  227.    
  228.         this.draw = function (gr, x, y) {
  229.                 this.x = x;
  230.                 this.y = y;
  231.                 this.w = ww;
  232.                 this.h = row;
  233.         
  234.                 gr.FillSolidRect(this.x, this.y, this.w, this.h, this.state == ButtonStates.hover ? blendColors(g_textcolor, g_backcolor, 0.94) : this.state == ButtonStates.down ? blendColors(g_textcolor, g_backcolor, 0.94) : this.active ? setAlpha(g_textcolor_2, 30) : 0);
  235.                 gr.GdiDrawText(this.str, this.Font, g_textcolor_2, this.x + 20, this.y, this.w - 24, this.h, DT_LEFT | DT_CALCRECT | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX);
  236.         
  237.         // this.btn.draw(gr,this.x+this.w-this.btn.w*2,this.y,255);
  238.         }
  239.         this.display_context_menu = function (x, y, id) {};
  240.         this.repaint = function () {
  241.                 window.RepaintRect(this.x, this.y, this.w, this.h);
  242.         }
  243.     this.isXYok = function(x,y){
  244.         return (x > this.x && x < this.x+this.w  && y > this.y && y < this.y + this.h);
  245.     }
  246.         this.checkstate = function (event, x, y) {
  247.         // 如何解决
  248.         this.ishover = (x > this.x && x < this.x+this.w  && y > this.y && y < this.y + this.h);
  249.                 this.old = this.state;
  250.         
  251.         // var btn_state = this.btn.checkstate(event,x,y);
  252.         
  253.                 switch (event) {
  254.                 case "down":
  255.                         switch (this.state) {
  256.                         case ButtonStates.normal:
  257.                         case ButtonStates.hover:
  258.                                 this.state = this.ishover ? ButtonStates.down : ButtonStates.normal;
  259.                                 break;
  260.                         };
  261.                         break;
  262.                 case "up":
  263.                         this.state = this.ishover ? ButtonStates.hover : ButtonStates.normal;
  264.             
  265.                         break;
  266.                 case "right":
  267.                         if (this.ishover)
  268.                                 this.display_context_menu(x, y, id);
  269.                         break;
  270.                 case "move":
  271.                         switch (this.state) {
  272.                         case ButtonStates.normal:
  273.                         case ButtonStates.hover:
  274.                                 this.state = this.ishover ? ButtonStates.hover : ButtonStates.normal;
  275.                                 break;
  276.                         };
  277.                         break;
  278.                 case "leave":
  279.                         this.state = this.isdown ? ButtonStates.down : ButtonStates.normal;
  280.                         break;
  281.                 }
  282.                 if (this.state != this.old)
  283.                         this.repaint();
  284.                 return this.state;
  285.         }
  286. }

  287. // callback starts -->
  288. function on_mouse_move(x, y) {
  289.     var i = 0;
  290.     for(i=0;i<g_tabs.length;i++){
  291.         g_tabs[i].checkstate("move",x,y);
  292.     }
  293. }

  294. function on_mouse_leave(x,y){
  295.     var i;
  296.     for(i=0;i<g_tabs.length;i++){
  297.         g_tabs[i].checkstate("leave",0,0);
  298.     }
  299. }

  300. function on_mouse_lbtn_down(x, y, mask) {
  301.     var i;
  302.     for(i=0;i<g_tabs.length;i++){
  303.         g_tabs[i].checkstate("down",x,y);
  304.     }
  305. }

  306. function on_mouse_lbtn_up(x, y, mask) {
  307.     var i;
  308.     for(i=0;i<g_tabs.length;i++){
  309.         if(g_tabs[i].checkstate("up",x,y)==ButtonStates.hover){
  310.             fb.PlaybackTime = string2time(g_tabs[i].str)
  311.         }
  312.     }
  313. }

  314. function on_mouse_rbtn_down(x,y){
  315.     rbtnDown = true;
  316. }

  317. function on_mouse_rbtn_up(x,y){
  318.    
  319.     var idx = -1;
  320.     if(rbtnDown){
  321.         if (utils.IsKeyPressed(VK_SHIFT)){
  322.             return false;
  323.         }else{
  324.             var k = 0;
  325.             for(k=0;k<g_tabs.length;k++){
  326.                 if(g_tabs[k].isXYok(x,y)){
  327.                     idx = k;
  328.                 }
  329.             }
  330.             list_menu(x,y,idx);
  331.             return true;
  332.         }
  333.     }
  334.     return true;
  335. }


  336. function on_paint(gr){
  337.     var i;
  338.     for(i=0;i<g_tabs.length;i++){
  339.         g_tabs[i].draw(gr,20,20+20*i);
  340.     }
  341. }

  342. function on_playback_new_track(metadb){
  343.     g_metadb = metadb;
  344.     updateGTabs();
  345.     window.repaint();
  346. }

  347. function on_size() {
  348.     ww = window.Width;
  349.     hh = window.Height;
  350. }
  351. // <-- callback ends
复制代码


回复

使用道具 举报

2#
 楼主 发表于 2019-12-10 21:02:57 | 只看该作者
本帖最后由 mizuku 于 2019-12-10 21:10 编辑

版本:  (foobar2000 v1.3.17)WSH Panel Mod v1.5.6
(音乐播放时)空白处右键 -> 新建, 在已经有的标签上右键可以删除和删除所有
按住shift右键调出configure菜单
嘛,代码基本是copy paste,不过js的代码真是看得太艰难了,我可以一下子说出10个槽点,还是Python好用
回复 支持 反对

使用道具 举报

3#
 楼主 发表于 2019-12-10 21:17:01 | 只看该作者
目前没找到bookmark的源码,如果能自己改造boomark组件就好了.当然,有源码可能也看不懂,我根本看不懂C++
C还是蛮喜欢的,但是C++真的如天书
回复 支持 反对

使用道具 举报

4#
发表于 2019-12-10 22:35:18 | 只看该作者
是大佬,在下看不懂
回复 支持 反对

使用道具 举报

5#
发表于 2019-12-10 22:41:39 | 只看该作者
sdlwsl以前用过一段时间Foobar感觉根本玩不转

点评

还好吧,去除CUI,PUI等界面的定制,只需要知道怎么使用组件和媒体库的搜索语法,基本就能用了  详情 回复 发表于 2019-12-10 23:51
是GDI,不过我也是临时看看文档就是了  详情 回复 发表于 2019-12-10 23:42
回复 支持 反对

使用道具 举报

6#
 楼主 发表于 2019-12-10 23:42:33 | 只看该作者

RE: 写一半才发现fb2k有个书签的组件

cncybrilu 发表于 2019-12-10 22:41
sdlwsl以前用过一段时间Foobar感觉根本玩不转

是GDI,不过我也是临时看看文档就是了
回复 支持 反对

使用道具 举报

7#
 楼主 发表于 2019-12-10 23:51:17 | 只看该作者

RE: 写一半才发现fb2k有个书签的组件

cncybrilu 发表于 2019-12-10 22:41
sdlwsl以前用过一段时间Foobar感觉根本玩不转

还好吧,去除CUI,PUI等界面的定制,只需要知道怎么使用组件和媒体库的搜索语法,基本就能用了
回复 支持 反对

使用道具 举报

8#
发表于 2019-12-11 03:06:39 | 只看该作者
回复 支持 反对

使用道具 举报

9#
发表于 2019-12-11 07:56:18 | 只看该作者
赞一个
回复 支持 反对

使用道具 举报

10#
发表于 2019-12-11 08:02:30 | 只看该作者
Orz膜拜dalao,顺便马克一下
回复 支持 反对

使用道具 举报

11#
发表于 2019-12-11 10:11:46 | 只看该作者
ctrl+c +v一个
回复 支持 反对

使用道具 举报

12#
发表于 2019-12-11 13:55:54 | 只看该作者
啥意思?自己想Biubiu的时候找音声快要biubiu的那段来应景下?

点评

别说的那么直接吗  详情 回复 发表于 2019-12-11 14:01
回复 支持 反对

使用道具 举报

13#
 楼主 发表于 2019-12-11 14:01:30 | 只看该作者

RE: 写一半才发现fb2k有个书签的组件

lljy23 发表于 2019-12-11 13:55
啥意思?自己想Biubiu的时候找音声快要biubiu的那段来应景下?

别说的那么直接吗
回复 支持 反对

使用道具 举报

14#
发表于 2019-12-12 00:09:55 | 只看该作者
看不懂

点评

看不懂没关系,只要知道foobar2k几乎什么都可以做就行了,可以试试上面说的书签组件 其实这个wsh mod还有网络接口,我可以实现一个在foobar2k里面就可以完成的"从dlsite爬取,保存信息"的功能  详情 回复 发表于 2019-12-12 23:06
回复 支持 反对

使用道具 举报

15#
 楼主 发表于 2019-12-12 23:06:23 | 只看该作者

RE: 写一半才发现fb2k有个书签的组件


看不懂没关系,只要知道foobar2k几乎什么都可以做就行了,可以试试上面说的书签组件
其实这个wsh mod还有网络接口,我可以实现一个在foobar2k里面就可以完成的"从dlsite爬取,保存信息"的功能

点评

这下懂了  详情 回复 发表于 2019-12-13 00:03
回复 支持 反对

使用道具 举报

16#
发表于 2019-12-13 00:03:26 | 只看该作者

RE: 写一半才发现fb2k有个书签的组件

mizuku 发表于 2019-12-12 23:06
看不懂没关系,只要知道foobar2k几乎什么都可以做就行了,可以试试上面说的书签组件
其实这个wsh mod还有 ...

这下懂了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 入驻小町

本版积分规则

手机版|联系我们|Rain's komachi

GMT+8, 2024-11-24 09:30 , Processed in 0.021877 second(s), 14 queries .

Powered by Discuz! F1.0

© 2001-2024 Comsenz Inc. & Discuz! Fans

快速回复 返回顶部 返回列表