This adds a column to show the dimensions (WxH) of a coverart (or an image).
Supports jpg, png coverart.
As usual, no guarantees and so on, but it works for me.
Suggestions, tips etc is appreciated.
I'm still learning JScript as I've only used it for a few automation scripts before DO11, so the code might be lacking a bit.
- Fixed an obvious bug.
- Added the value "---" for no available dimensions in a file.
- More bugs
- If there are multiple coverart images, attempt to determine the coverart with the largest area (WxH), and use those dimensions.
- changed scriptname/name because it became hard to keep track of all the scripts. Prefixing them according to what they do seemed like a nice idea.
- configurable support for folder images. If the defined files is inside a folder, we'll show the dimensions of that file. Default files is cover.jpg and folder.jpg
Requires Directory Opus 11.5.5 or higher
Uses the new AudioCoverArt properties.
It can handle jpg and png coverart, but not gif and bmp.
- changed scriptname/name because it became hard to keep track of all the scripts. Prefixing them according to what they do seemed like a nice idea.
- shouldn't display undefinedxundefined when encountering an unsupported coverart anymore (or so I think).
- configurable support for folder images. If the defined files is inside a folder, we'll show the dimensions of that file. Default files is cover.jpg and folder.jpg
Downloads:
- ColCoverArtDim.js.txt (3.6 KB)
- Download the file, then drag it to the list under Preferences / Toolbars / Scripts.
- You can then find a new file display column under the Scripts category.
Script Code:
The script code from the download above is reproduced for reference:
// This script is for Directory Opus 11.5.5 or higher
// CoverArtDim column (CoverArt WxH)
// Uses AudioCoverArt's new width and height properties.
// Shows dimensions for a folder if it contains defined files.
var vAllowedFolderImages=null;
function ImageSize(itm){
if (itm){
var metatype=""+itm.Metadata; //Make sure we get a string. Without "" we get a object.
if ((metatype=="audio")&&(itm.Metadata.audio.coverart>0)){
var cnt=itm.Metadata.audio.coverart;
var curitm;
if(cnt>1){
var enm=[];
var pos=0;
var selwidth=0;
var selheight=0;
var enu = new Enumerator(itm.Metadata.audio.coverart);
if (!enu.atEnd()){
while (!enu.atEnd()){
curitm=enu.item();
enm[pos++]={width:curitm.width,height:curitm.height};
enu.moveNext();
}
}
for (var i=0;i<pos;i++){
if ((enm[i])&&((enm[i].width*enm[i].height)>(selheight*selwidth))){
selwidth=enm[i].width;
selheight=enm[i].height;
}
}
// If width and height is both 0/undefined then we don't have a supported coverart
if ((!selwidth)&&(!selheight)) return null;
return {width:selwidth,height:selheight,all:enm};
} else{
curitm=itm.Metadata.audio.coverart(0);
// If width and height is both 0/undefined then we don't have a supported coverart
if ((!curitm.width)&&(!curitm.height)) return null;
return {width:curitm.width,height:curitm.height,all:null};
}
} else if (metatype=="image"){
return {width:itm.Metadata.image.picwidth,height:itm.Metadata.image.picheight,all:null};
}
}
return null;
}
// Called by Directory Opus to initialize the script
function OnInit(initData){
initData.name = "ColCoverArtDimensions2";
initData.desc = "Add a column to show the dimensions of coverart or image";
initData.copyright = "myarmor";
initData.min_version="11.5.5";
initData.version = "1.03";
initData.default_enable = false;
var col;
col = initData.AddColumn();
col.name = "CoverArtDim";
col.method = "OnCoverArtDim";
col.label = "CoverArt WxH";
col.autogroup = true;
col.autorefresh=true;
col.justify = "right";
initData.config.allowedFolderImages=DOpus.NewVector('cover.jpg','folder.jpg');
return false;
}
function AllowItem(item){
if (!vAllowedFolderImages) return false;
var len;
len=vAllowedFolderImages.count;
if (len==0) return false;
for (var i=0;i<len;i++){
if (vAllowedFolderImages(i).toLowerCase()==item.name.toLowerCase()){
return true;
}
}
return false;
}
function FindFolderArt(itm){
var folderEnum=DOpus.FSUtil.ReadDir(itm.realpath);
var foldername=itm.name.toLowerCase();
if (!folderEnum.complete){
var curitm;
while (!folderEnum.complete){
curitm=folderEnum.Next();
if ((!curitm.is_dir)&&(AllowItem(curitm))){
return curitm;
}
}
}
}
// Handler for the CoverArtDim column
function OnCoverArtDim(scriptColData){
if (scriptColData.col!="CoverArtDim") return;
var dims=null;
var bIsDir;
if (scriptColData.item.is_dir) {
bIsDir=true;
vAllowedFolderImages=Script.config.allowedFolderImages;
var itm=FindFolderArt(scriptColData.item);
if (itm){
dims=ImageSize(itm);
}
} else{
dims=ImageSize(scriptColData.item);
}
if (dims){
scriptColData.value=dims.width+"x"+dims.height;
scriptColData.sort=dims.width;
} else if (!bIsDir){
scriptColData.value="---";
scriptColData.sort=9999999;
}
}
Older Version:
This older version does not use the AudioCoverArt object added in Opus 11.5.5 and instead parses the images itself. It supports jpg, png, gif and bmp, but is slower than the newer script (above).
This script reads the image width/height directly from the binary coverart/image and is mainly an experiment using the new File and Blob objects (hence no real errorchecking and so on).
- ColCoverArtDim_Old.js.txt (11.0 KB)
// This script is for Directory Opus 11.5.4 or higher
// CoverArtDim column (CoverArt WxH)
// Extracts the dimensions directly from blob or file.
// Shows dimensions for a folder if it contains defined files.
var vAllowedFolderImages=null;
function MakeLong(lo,hi){
return (lo|(hi<<16));
}
function MakeWord(lo,hi){
return (((lo<<16)>>16)|((hi<<16)>>8));
}
function GetByte2(Value,ByteToGet){
return (Value & (0xFF<<(ByteToGet*8)))>>>(ByteToGet*8);
}
function SwapHiLo(w){
return MakeWord(GetByte2(w,1),GetByte2(w,0));
}
// Simplifies using a Blob by making it behave more like a file.
var soBeginning=0;
var soCurrent=1;
var soEnd=2;
function BlobStream(blob){
this.blob=blob;
this.position=0;
this.size=0;
if (this.blob){
this.size=this.blob.size;
}
}
BlobStream.prototype.Seek=function(offset,mode){
switch(mode){
case soBeginning: {
this.position=offset;
return this.position;
}
case soCurrent: {
this.position=this.position+offset;
return this.position;
}
case soEnd:{
this.position=blob.size-offset;
return this.position;
}
default: return -1;
}
};
BlobStream.prototype.ReadByte=function(){
return this.blob(this.position++);
};
BlobStream.prototype.ReadWord=function(){
return MakeWord(this.ReadByte(),this.ReadByte());
};
BlobStream.prototype.ReadLong=function(){
return MakeLong(MakeWord(this.ReadByte(),this.ReadByte()),MakeWord(this.ReadByte(),this.ReadByte()));
};
BlobStream.prototype.ReadBytes=function(numbytes){
var retval=[];
for (var i=0;i<numbytes;i++){
retval[i]=this.ReadByte();
}
return retval;
};
BlobStream.prototype.ReadString=function(len){
var retval="";
var b,i=0;
if (len==0) return retval;
do{
b=this.ReadByte();
retval=retval+String.fromCharCode(b);
i++;
}while((this.position<this.size)&&(i<len));
return retval;
};
BlobStream.prototype.CanRead=function(numbytes){
if ((this.position+numbytes-1)<this.size) return true;
return false;
};
BlobStream.prototype.WriteByte=function(b){
this.blob(this.position)=b;
this.position++;
};
BlobStream.prototype.WriteWord=function(word){
this.WriteByte(GetByte2(word,0));
this.WriteByte(GetByte2(word,1));
};
BlobStream.prototype.WriteLong=function(along){
this.WriteByte(GetByte2(along,0));
this.WriteByte(GetByte2(along,1));
this.WriteByte(GetByte2(along,2));
this.WriteByte(GetByte2(along,3));
};
BlobStream.prototype.WriteString=function(s){
for (var i=0;i<s.length;i++){
this.WriteByte(s.charCodeAt(i));
}
};
BlobStream.prototype.Resize=function(size){
this.blob.Resize(size);
this.size=this.blob.size;
};
BlobStream.prototype.ToArray=function(){
var vbarr=new VBArray(this.blob.ToVBArray());
if (vbarr){
return vbarr.toArray();
}
return null;
};
function JPGSizeStream(stream){
if(!stream) return null;
var iHeight,iWidth,iBitdepth;
var n,b,w;
var pos;
var newpos;
n=stream.size-8;
if (n<=0) return null;
stream.Seek(0,soBeginning);
if (stream.ReadWord()!=0xD8FF){
DOpus.Output('Not a valid JPEG file');
return null;
}
b=stream.ReadByte();
while ((stream.position<n)&&(b==0xff)){
b=stream.ReadByte();
switch(b){
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
{
stream.Seek(3,soCurrent);//skip 3 unneeded bytes
w=stream.ReadWord();
iHeight=SwapHiLo(w);
w=stream.ReadWord();
iWidth=SwapHiLo(w);
b=stream.ReadByte();
iBitdepth=b*8;
return {width:iWidth,height:iHeight,bitdepth:iBitdepth};
}
case 0xff: {
// Not really tested
DOpus.Output('Was 0xff');
b=stream.ReadByte();
break;
}
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
case 0xD4:
case 0xD5:
case 0xD6:
case 0xD7:
case 0xD8:
case 0xD9:
case 0x01:{
DOpus.Output("D0..D9,01");
stream.Seek(1,soCurrent);
b=stream.ReadByte();
break;
}
default:{
w=stream.ReadWord();
pos=stream.Seek(SwapHiLo(w) - 2,soCurrent);
b=stream.ReadByte();
break;
}
}
}
return null;
}
function PNGSizeStream(stream){
var validsig=[137, 80, 78, 71, 13, 10, 26, 10];
var sb;
var iWidth,iHeight;
if(!stream) return null;
stream.Seek(0,soBeginning);
for (var i=0;i<validsig.length;i++){
sb=stream.ReadByte();
if (validsig[i]!=sb){
DOpus.Output('Not a valid PNG file');
return null;
}
}
stream.Seek(18,soBeginning);
iWidth=SwapHiLo(stream.ReadWord());
stream.Seek(22,soBeginning);
iHeight=SwapHiLo(stream.ReadWord());
return {width:iWidth,height:iHeight};
}
function GIFSizeStream(stream){
var iWidth,iHeight;
var bFound;
var c;
if(!stream) return null;
stream.Seek(0, soBeginning);
var GIFHeader={
Sig: stream.ReadString(6),
ScreenWidth: stream.ReadWord(),
ScreenHeight: stream.ReadWord(),
Flags: stream.ReadByte(),
Background: stream.ReadByte(),
Aspect: stream.ReadByte()
};
if (GIFHeader.Sig.substr(0,3)!="GIF"){
DOpus.Output('Not a valid GIF file');
return null;
}
if ((GIFHeader.Flags & 0x80)>0){
var x = 3 * (1 << ((GIFHeader.Flags & 7) + 1));
stream.Seek(x,soBeginning);
}
bFound=false;
while(stream.position<stream.size){
c=stream.ReadByte();
switch(c){
case 0x2c:{
var ImageBlock={
Left:stream.ReadWord(),
Top:stream.ReadWord(),
Width:stream.ReadWord(),
Height:stream.ReadWord(),
Flags:stream.ReadByte()
};
iWidth=ImageBlock.Width;
iHeight=ImageBlock.Height;
return {width:iWidth,height:iHeight};
}
case 0xff:{
break;
}
}
}
return null;
}
function BMPSizeStream(stream){
var iWidth,iHeight;
var bFound;
var c;
if(!stream) return null;
stream.Seek(0, soBeginning);
var BitmapFileHeader={
bfType: stream.ReadWord(),
bfSize: stream.ReadLong(),
bfReserved1: stream.ReadWord(),
bfReserved2: stream.ReadWord(),
bfOffBits: stream.ReadLong()
};
if (BitmapFileHeader.bfType ==0x4D42){
// Get the size of the header.
var hdrsize=stream.ReadLong();
if (hdrsize<40){
//We have a BITMAPCOREHEADER
var BitmapCoreHeader={
bcWidth: stream.ReadWord(),
bcHeight: stream.ReadWord(),
bcPlanes: stream.ReadWord(),
bcBitCount: stream.ReadWord()
};
return {width:BitmapCoreHeader.bcWidth,height:BitmapCoreHeader.bcHeight};
} else{
//We have a BITMAPINFOHEADER
var BitmapInfoHeader={
biWidth: stream.ReadLong(),
biHeight: stream.ReadLong(),
biPlanes: stream.ReadWord(),
biBitCount: stream.ReadWord()
};
return {width:BitmapInfoHeader.biWidth,height:BitmapInfoHeader.biHeight};
}
} else DOpus.Output('Not a valid BMP file');
return null;
}
function ImageSizeStream(stream){
if (!stream) return null;
stream.Seek(0,soBeginning);
var sig=stream.ReadWord();
switch (sig){
case 0x5089: return PNGSizeStream(stream);
case 0xD8FF: return JPGSizeStream(stream);
case 0x4947: return GIFSizeStream(stream);
case 0x4D42: return BMPSizeStream(stream);
default: return null;
}
}
function ImageSize(filename){
var stream;
var itm=DOpus.FSUtil.GetItem(filename);
if (itm){
var metatype=""+itm.Metadata; //Make sure we get a string. Without "" we get a object.
if ((metatype=="audio")&&(itm.Metadata.audio.coverart>0)){
var cnt=itm.Metadata.audio.coverart;
if(cnt>1){
var enm=[];
var pos=0;
var selwidth=0;
var selheight=0;
var enu = new Enumerator(itm.Metadata.audio.coverart);
if (!enu.atEnd()){
while (!enu.atEnd()){
var curitm=enu.item();
stream=new BlobStream(curitm.data);
enm[pos++]=ImageSizeStream(stream);
enu.moveNext();
}
}
for (var i=0;i<pos;i++){
if ((enm[i])&&((enm[i].width*enm[i].height)>(selheight*selwidth))){
selwidth=enm[i].width;
selheight=enm[i].height;
}
}
return {width:selwidth,height:selheight,all:enm};
} else {
var ca=itm.Metadata.audio.coverart(0);
stream=new BlobStream(ca.data);
return ImageSizeStream(stream);
}
} else{
var blob;
if ((".gif;.jpg;.png;.bmp".indexOf(itm.ext.toLowerCase())>=0)){
var file=DOpus.FSUtil.OpenFile(itm.realpath);
if (file){
//Contrary to the documentation, Read don't seem to work unless you specify a size.
//iow, blob=file.Read() does nothing.
blob=file.Read(file.size);
if (blob){
stream=new BlobStream(blob);
return ImageSizeStream(stream);
}
}
}
}
}
return null;
}
// Called by Directory Opus to initialize the script
function OnInit(initData){
initData.name = "ColCoverArtDimensions";
initData.desc = "Add a column to show the dimensions of coverart or image";
initData.copyright = "myarmor";
initData.min_version="11";
initData.version = "1.03";
initData.default_enable = false;
var col;
col = initData.AddColumn();
col.name = "CoverArtDim";
col.method = "OnCoverArtDim";
col.label = "CoverArt WxH";
col.autogroup = true;
col.autorefresh=true;
col.justify = "right";
initData.config.allowedFolderImages=DOpus.NewVector('cover.jpg','folder.jpg');
return false;
}
function AllowItem(item){
if (!vAllowedFolderImages) return false;
var len;
len=vAllowedFolderImages.count;
if (len==0) return false;
for (var i=0;i<len;i++){
if (vAllowedFolderImages(i).toLowerCase()==item.name.toLowerCase()){
return true;
}
}
return false;
}
function FindFolderArt(itm){
var folderEnum=DOpus.FSUtil.ReadDir(itm.realpath);
var foldername=itm.name.toLowerCase();
if (!folderEnum.complete){
var curitm;
while (!folderEnum.complete){
curitm=folderEnum.Next();
if ((!curitm.is_dir)&&(AllowItem(curitm))){
return curitm;
}
}
}
}
// Handler for the CoverArtDim column
function OnCoverArtDim(scriptColData){
if (scriptColData.col!="CoverArtDim") return;
var dims=null;
var bIsDir;
if (scriptColData.item.is_dir) {
bIsDir=true;
vAllowedFolderImages=Script.config.allowedFolderImages;
var itm=FindFolderArt(scriptColData.item);
if (itm){
dims=ImageSize(itm);
}
} else{
dims=ImageSize(scriptColData.item);
}
if (dims){
scriptColData.value=dims.width+"x"+dims.height;
scriptColData.sort=dims.width;
} else if (!bIsDir){
scriptColData.value="---";
scriptColData.sort=9999999;
}
}