Track Info & Album Art & Volume Control @ foo_wsh_panel

Список разделов foobar2000 Плагины SDK

Описание: Разработка плагинов для foobar2000 с использованием SDK.

Сообщение #1 akuma » 20.08.2007, 14:49

это полностью рабочая демка track_info. выводит информацию о треке и прогрессбар, которым можно рулить мышкой.
при создании своих панелек нужно править раздел application, что в самом конце.
в общем, тут всё понятно - создаём дерево объектов с заданием отличных от дефолтных параметров, которое обновляется при ресайзе (ресурсов тратится не слишком много, зато разработка упрощается) и по необходимости скопом отображаем, не забыв подредактировать некоторые параметры. дети рисуются в порядке очереди поверх родителей. смотрите исходники объектов, чтобы узнать какие параметры они поддерживают. любой объект можно инициализироать без параметров либо другим объектом (все поля будут скопированы и известные объекту будут приведены к необходимому типу). дефолтные значения цветов лучше не изменять - они специально подобраны для отладки. выполнять make() вручную не требуется, кроме случаев прямого изменения параметров и последующей прямой работой с канвой.

Код: Выделить всё
/* [ oop foobar2000 scripting v.5 ] */
/* license: free */

Object.prototype.toString= function () {
   var str=[];
   for (i in this) str.push ( i+':'+this[i] );
   return '{'+str+'}';
}

/* [ foobar2000 API ]
global fb {
   trace ( v ); // dump variable to console
   ComponentPath; // full path to ./foobar/components/
   TitleFormat ( tagz_str ); // returns titleformat object for a string (use Eval() to process to string)
   PlaybackLength;
   PlaybackTime;
   IsPlaying;
   volume;
   PlayOrPause();
   Prev();
   Next();
}
global gdi {
   Font ( -size, weight, italic, underline, family_name ); // returns a gdi font
   Image ( path ); // returns a gdi image
}
canvas {
   DrawLine ( x1, y1, x2, y2, thickness, color );
   DrawRect ( x, y, width, height, thickness, color );
   FillSolidRect (x, y, width, height, color );
   FillGradRect ( x, y, width, height, gradient_angle, color1, color2 );
   DrawString ( text, gdi_font, color, x, y, width, height, add_params );
   DrawImage ( gdi_image, x, y, width, height, image_x, image_y, image_width, image_height);
}
window {
   Width;
   Height;
}
*/

/* [ EVENTS HANDLERS ] */
function on_paint(canvas) {
   this.ww;
   this.wh;
   if ( window.Width!=this.ww || window.Height!=this.wh ) $.onResize && $.onResize();
   this.ww= window.Width;
   this.wh= window.Height;
   $.canvas= canvas;
   $.onPaint && $.onPaint();
}
function on_playback_starting(cmd, paused){
   $.onPlay && $.onPlay();
}
function on_playback_new_track(track){
   var t= {};
   t.path= track.path;
   t.subSong= track.SubSong;
   t.fileSize= track.FileSize;
   t.length= track.Length;
   t.meta= {};
   for ( var i=0; i<track.MetaCount; ++i ) {
      var m= [];
      for ( var j=0; j<track.MetaValueCount(i); ++j )  m.push( track.MetaValue ( i, j ) );
      t.meta[ track.MetaName (i) ]= m;
   }
   t.info= {};
   for ( var i=0; i<track.InfoCount; ++i ) t.info[ track.InfoName (i) ]= track.InfoValue (i);
   $.track= t;
   $.onNext && $.onNext();
}
function on_playback_stop(){
   $.onStop && $.onStop();
}
function on_playback_seek(time){
   $.playTime= time;
   $.onSeek && $.onSeek();
}
function on_playback_pause(state){
   window.Repaint();
}
function on_playback_edited(){
   $.onPlaybackEdit && $.onPlaybackEdit();
}
function on_playback_dynamic_info() {
   $.onDynamicInfo && $.onDynamicInfo();
}
function on_playback_dynamic_info_track() {
   $.onDynamicTrack && $.onDynamicTrack();
}
function on_playback_time(time){
   $.playTime= time;
   $.onTick && $.onTick();
}
function on_volume_change(volume){
   $.volume= volume;
   $.onVolume && $.onVolume();
}
function on_mouse_lbtn_down(x,y){
   $.mouseX= x;
   $.mouseY= y;
   $.mouseBtns|= 1;
   $.onMouseDown && $.onMouseDown();
}
function on_mouse_lbtn_up(x,y){
   $.mouseX= x;
   $.mouseY= y;
   $.mouseBtns&= ~1;
   $.onMouseUp && $.onMouseUp();
}
function on_mouse_move(x,y){
   $.mouseX= x;
   $.mouseY= y;
   $.onMouseMove && $.onMouseMove();
}
function on_mouse_wheel(delta){
   $.wheelDelta= delta;
   $.onWheel && $.onWheel (delta) || (fb.volume+= delta);
}

/* [ LOW LEVEL OBJECTS ] */
var Color= function (color) {
   this.presets= {
      red: { r:255, g:0, b:0 },
      green: { r:0, g:255, b:0 },
      blue: { r:0, g:0, b:255 },
      white: { r:255, g:255, b:255 },
      gray: { r:127, g:127, b:127 },
      black: { r:0, g:0, b:0 }
   };
   if (color && typeof color == 'number')  {
      this.b= color & 0xFF;
      this.g= color>>8 & 0xFF;
      this.r= color>>16 & 0xFF;
      this.a= color>>24 & 0xFF;
   } else {
      var preset= this.presets[color];
      this.r= preset && preset.r || color && color.r || 0;
      this.g= preset && preset.g || color && color.g || 0;
      this.b= preset && preset.b || color && color.b || 0;
      this.a= preset && preset.a || color && color.a || 0;
   };
   this.make= function () {
      this.raw= (255-this.a)<<24
      | this.r<<16
      | this.g<<8
      | this.b;
      return this;
   }; this.make();
}
var Font= function (font) {
   for (i in font) this[i]= font[i];
   this.size= this.size || 12;
   this.weight= this.weight || 400;
   this.italic= this.italic || 0;
   this.undeline= this.underline || 0;
   this.name= this.name || 'Arial';
   this.make= function () {
      this.raw= gdi.Font(-this.size, this.weight,this.italic, this.underline, this.name);
      return this;
   }; this.make();
   return this;
}
var TextProp= function (prop) {
   for (i in prop) this[i]= prop[i];
   this.alignes= { start:0, middle:1, end:2 };
   this.trimes= { no:0, char:1, word:2, elipsChar:3, elipsWord:4, elipsPath:5 };
   this.alignH= this.alignH || 'middle';
   this.alignV= this.alignV || 'middle';
   this.trim= this.trim || 'no';
   this.rtl= this.rtl || 0;
   this.vertical= this.vertical || 0;
   this.noFit= this.noFit || 0;
   this.noCtrl= this.noCtrl || 0;
   this.noFallback= this.noFallback || 0;
   this.trailSpace= this.trailSpace || 0;
   this.noWrap= this.noWrap || 0;
   this.lineLimit= this.lineLimit || 0;
   this.noClip= this.noClip || 0;
   this.make= function () {
      this.raw= this.alignes[this.alignH]<<28
      | this.alignes[this.alignV]<<24
      | this.trimes[this.trim]<<20
      | this.noClip<<14
      | this.lineLimit<<13
      | this.noWrap<<12
      | this.trailSpace<<11
      | this.noFallback<<10
      | this.noCtrl<<5
      | this.noFit<<2
      | this.vertical<<1
      | this.rtl;
      return this;
   }; this.make();
   return this;
}


/* [ HIGH LEVEL OBJECTS ] */
var Box= function (box) {
   for (i in box) this[i]= box[i];
   this.x= this.x || 0;
   this.y= this.y || 0;
   this.width= this.width ||  window.Width-this.x;
   this.height= this.height || window.Height-this.y;
}
var FrameBox= function (box) {
   for (i in box) this[i]= box[i];
   this.box= new Box ( this.box );
   this.color= new Color( 'color' in this ? this.color : 0xFF0000 );
   this.thick= this.thick || 1;
   this.render= function () {
      $.canvas.DrawRect (
         this.box.x, this.box.y, this.box.width-1, this.box.height-1,
         this.thick, this.color.raw
      );
      for (i in this.childs) this.childs[i].render();
   };
}
var GradBox= function (box) {
   for (i in box) this[i]= box[i];
   this.box= new Box ( this.box );
   this.color1= new Color( 'color1' in this ? this.color1 : 0x66000000 );
   this.color2= new Color( 'color2' in this ? this.color2 : 0x660000FF );
   this.angel= this.angel || 0;
   this.render= function () {
      try {
         $.canvas.FillGradRect (
            this.box.x, this.box.y, this.box.width, this.box.height,
            this.angel, this.color1.raw, this.color2.raw
         );
      } catch (e) {fb.trace(e.message);};
         for (i in this.childs) this.childs[i].render();
   };
}
var TextBox= function (box) {
   for (i in box) this[i]= box[i];
   this.box= new Box ( this.box );
   this.color= new Color( 'color' in this ? this.color :  'green' );
   this.font= new Font ( this.font );
   this.textProp= new TextProp ( this.textProp );
   this.text= this.text || '';
   this.render= function () {
      $.canvas.DrawString(
         this.text, this.font.raw, this.color.raw,
         this.box.x, this.box.y, this.box.width, this.box.height,
         this.textProp.raw
      )
      for (i in this.childs) this.childs[i].render();
   }
}
var Image= function ( image ) {
   this.make= function () {
      this.raw= gdi.Image ( this.path );
      return this;
   };
   for (i in image) this[i]= image[i];
   this.path= this.path || fb.ComponentPath+'..\\nocover.jpg';
   this.make();
   this.box= new Box ( this.box );
   this.crop= new Box ( this.crop || { x:0, y:0, width:this.raw.Width, height:this.raw.Height } );
   this.render= function () {
      $.canvas.DrawImage (
         this.raw,
         this.box.x, this.box.y, this.box.width, this.box.height,
         this.crop.x, this.crop.y, this.crop.width, this.crop.height
      )
      for (i in this.childs) this.childs[i].render();
   }
}

/* [ APPLICATION ] */
var track_info= { /* version 1.1 */
   onResize: function () {
      var sliderWidth= 30;
      var minRect= Math.min ( window.Width, window.Height);
      var strong= new Font ({
         weight: 800
      });
      this.childs= {
         blur: new GradBox ({
            angel: 90,
            color2: 0x00DDEEFF,
            color1: 0x00AABBCC,
            childs: {}
         }),
         progress: new GradBox ({
            angel: 90,
            color1: 0x00DDEEFF,
            color2: 0x00BBCCDD,
            childs: {}
         }),
         tags: new TextBox ({
            color: 'black',
            textProp: new TextProp ({
               alignH: 'end',
               alignV: 'start'
            }),
            box: {
               y: 5,
               width: window.Width-5
            },
            childs: {}
         }),
         format: new TextBox ({
            color: 'black',
            font: strong,
            text: '         \n\
               codec:      \n\
               bitrate:      \n\
               samplerate:   \n\
               chanels:      \n\
            ',
            textProp: new TextProp ({
               alignH: 'end',
               alignV: 'end'
            }),
            box: {
               width: 80,
               height: window.Height+10
            },
            childs: {
               data: new TextBox ({
                  color: 'black',
                  textProp: new TextProp ({
                     alignH: 'start',
                     alignV: 'end'
                  }),
                  box: {
                     x: 80,
                     height: window.Height+10
                  },
                  childs: {}
               })
            }
         }),
         addata: new TextBox ({
            color: 'black',
            font: strong,
            text: '         \n\
               mood:      \n\
               date:      \n\
            ',
            textProp: new TextProp ({
               alignH: 'end',
               alignV: 'end'
            }),
            box: {
               width: window.Width-75,
               height: window.Height+10
            },
            childs: {
               data: new TextBox ({
                  color: 'black',
                  text: '123323',
                  textProp: new TextProp ({
                     alignH: 'start',
                     alignV: 'end'
                  }),
                  box: {
                     x: window.Width-75,
                     width: 75,
                     height: window.Height+10
                  },
                  childs: {}
               })
            }
         }),
         data: new TextBox ({
            color: 'black',
            childs: {}
         }),
         title: new TextBox ({
            color: 'black',
            font: strong,
            childs: {}
         }),
         frame: new FrameBox ({
            color: 'gray',
            childs: {}
         })
      };
      this.onTick();
      this.onNext();
   },
   onPaint: function () {
      for (i in this.childs) this.childs[i].render();
   },
   onTick: function () {
      this.childs.progress.box.width= Math.ceil(fb.PlaybackTime*window.Width/fb.PlaybackLength);
      window.Repaint();
   },
   onSeek: function() {
      this.onTick();
   },
   onNext: function () {
      this.childs.data.text= this.tagz("                  \n\
         [%artist%]$if($strcmp(%artist%,%album artist%),, | %album artist%)   \n\
                                 \n\
         [{ %album% }]                     \n\
      ");
      this.childs.title.text= this.tagz("%title%");
      this.childs.tags.text= this.tagz("[%tags%]");
      this.childs.format.childs.data.text= this.tagz("   \n\
         %codec%         \n\
         %bitrate% kbps         \n\
         %samplerate% Hz         \n\
         %channels%         \n\
      ");
      this.childs.addata.childs.data.text= this.tagz("   \n\
         %mood%         \n\
         %date%            \n\
      ");
      window.Repaint();
   },
   onDynamicInfo: function () {
      this.onNext();
   },
   onDynamicTrack: function () {
      this.onNext();
   },
   onPlaybackEdit: function () {
      this.onNext();
   },
   onStop: function () {
      this.childs.progress.box.width= 0;
      window.Repaint();
   },
   onMouseMove: function () {
      if ( this.mouseBtns & 1 ) fb.PlaybackTime=  fb.PlaybackLength * this.mouseX / window.Width;
   },
   tagz: function (str) {
      return str.replace ( /.*/mg , function(s) { return fb.TitleFormat(s).Eval() } );
   }
}

var $= track_info;

есть мысль прикрутить к этому делу DOM, XML и CSS ^_^ фактически написать браузер... o_0 (мозилла, крепко затянувшись, побежала в угол)

буду рад критике и интересным идеям.
Последний раз редактировалось akuma 11.11.2007, 15:04, всего редактировалось 1 раз.
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев

Сообщение #2 Mishail » 21.08.2007, 15:52

Внешний вид у этого чуда есть?
Mishail M
Аватара
Репутация: 5
С нами: 18 лет 7 месяцев

Сообщение #3 akuma » 27.08.2007, 15:20

всмысле "внешний вид"? 
сейчас выложу опрятные album_art, track_info (в первом сообщении) и volume_control...
Последний раз редактировалось akuma 27.08.2007, 15:59, всего редактировалось 1 раз.
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев

Volume Control

Сообщение #4 akuma » 27.08.2007, 15:30

регулятор громкости выполненный в том же стиле, что и  track_info:

Код: Выделить всё
/* [ APPLICATION ] */
var volume_control= { /* version 1.2 */
   onResize: function () {
      this.childs= {
         blur: new GradBox ({
            angel: 90,
            color2: 0x00DDEEFF,
            color1: 0x00AABBCC,
            childs: {}
         }),
         progress: new GradBox ({
            angel: 90,
            color1: 0x00DDEEFF,
            color2: 0x00AABBCC,
            childs: {}
         }),
         frame: new FrameBox ({
            color: 'gray',
            childs: {}
         }),
         slider: new FrameBox ({
            color: 'gray',
            box: {
               height: 2
            },
            childs: {}
         })
      };
      this.onVolume ();
   },
   onPaint: function () {
      for (i in this.childs) this.childs[i].render();
   },
   onVolume: function () {
      var y= Math.ceil( Math.pow( -fb.volume * Math.pow( window.Height, 2 ) / 100 , .5 ) );
      this.childs.progress.box.y= y;
      this.childs.blur.box.height= y;
      this.childs.slider.box.y= y-1;
      this.childs.progress.box.height= window.Height-y;
      window.Repaint();
   },
   onMouseMove: function () {
      var y=  this.mouseY;
      if( y < 0 ) y= 0;
      if ( this.mouseBtns & 1 ) fb.volume=  -100 * Math.pow( y / window.Height, 2 );
   },
   tagz: function (str) {
      return str.replace ( /.*/mg , function(s) { return fb.TitleFormat(s).Eval() } );
   }

}

var $= volume_control;
тут только блок application. остальные блоки - такие же как и у track_info
Последний раз редактировалось akuma 11.11.2007, 14:36, всего редактировалось 1 раз.
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев

Album Art

Сообщение #5 akuma » 27.08.2007, 15:41

ищет и показывает картинки связанные с треком (в той же папке и подпапках). поддерживаемые форматы - jpg и png. картинки рандомно меняются с периодичностью в 16 секунд. по дефолту выводит nocover.jpg из папки, куда установлен фубар.

Код: Выделить всё
/* [ APPLICATION ] */
var album_art= { /* version 2.1 */
   onResize: function () {
      var minRect= Math.min ( window.Width, window.Height);
      this.childs= {
         blur: new GradBox({
            angel: 90,
            color2: 0x00DDEEFF,
            color1: 0x00AABBCC
         }),
         cover: new Image ({
            path: (this.childs && this.childs.cover.path),
            crop: (this.childs && this.childs.cover.crop),
            box: {
               width: minRect,
               height: minRect,
               x: (window.Width-minRect)/2,
               y: (window.Height-minRect)/2
            }
         }),
         frame: new FrameBox ({
            color: 'gray',
            childs: {}
         })
      };
   },
   onPaint: function () {
      for (i in this.childs) this.childs[i].render();
   },
   onTick: function () {
      if ( this.playTime%16!=1 ) return;
      if ( this.images && this.images.length ) {
         this.childs.cover= new Image ({
            box: this.childs.cover.box,
            path: this.images[Math.floor(Math.random()*this.images.length)]
         });
         var box= this.childs.cover.box;
         var rawim= this.childs.cover.raw;
         var aspect=rawim.Width / rawim.Height;
         box.width= window.Width;
         box.height= box.width / aspect;
         if( box.height > window.Height ){
            box.width= aspect * window.Height;
            box.height= window.Height;
         }
         box.x= ( window.Width - box.width )/2;
         box.y= ( window.Height - box.height )/2;
         
      };
      window.Repaint();
   },
   onNext: function () {
      this.images= this.findTrackFiles ([ 'png' , 'jpg' ]);
      this.childs.cover=  new Image;
   },
   onMouseDown: function( ){
      var shell= new ActiveXObject( "WScript.Shell" );
      shell.run( '"' + this.childs.cover.path + '"' );
   },
   tagz: function (str) {
      return str.replace ( /.*/mg , function(s) { return fb.TitleFormat(s).Eval() } );
   },
   findTrackFiles: function ( ext ) {
      if (!this.track) return [];
      if ( typeof ext == 'object' ) ext= '(?:' + ext.join('|') + ')';
      var path=  this.track.path.replace(/^file:\/\//, '');
      var fs= new ActiveXObject("Scripting.FileSystemObject");
      var files= [];
      var checkFiles= function ( dir ) {
         var e= new Enumerator ( dir.Files );
         if (e) for ( ; !e.atEnd() ; e.moveNext() ) {
            var name= e.item().Name;
            if ( RegExp('.*'+ext+'$').test(name) ) files.push ( e.item() );
         };
      };
      try {
         var dir = fs.GetFile( path ).ParentFolder;
         checkFiles ( dir );
         var e = new Enumerator ( dir.SubFolders );
         if (e) for( ; !e.atEnd() ; e.moveNext() ) checkFiles ( e.item() );
      } catch( e ){}
      return files;
   }
}
var $= album_art;
Последний раз редактировалось akuma 11.11.2007, 15:23, всего редактировалось 1 раз.
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев

Сообщение #6 akuma » 27.08.2007, 15:56

Последний раз редактировалось akuma 27.08.2007, 16:07, всего редактировалось 1 раз.
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев

Сообщение #7 akuma » 28.09.2007, 00:58

поправил album art - теперь при проигрывании нефайлов панельки не отваливаются
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев

Сообщение #8 akuma » 11.11.2007, 14:39

поправил album art - теперь пропорции соблюдаются, а по клику по картинке она открывается во вьювере (а точнее в ассоциированной программе)
а также volume control - реализовал квадратичную зависимость положения ползунка от громкости, как в стандартном фубаровском.
Последний раз редактировалось akuma 11.11.2007, 14:56, всего редактировалось 1 раз.
akuma M
Автор темы
Аватара
Репутация: 0
С нами: 18 лет 7 месяцев


Вернуться в SDK