Documentation/Source example
Example source
samples/complete/js/thumbs.js
Referenced by:utils.HTTPRequestAsync
'use strict';
function _thumbs() {
this.size = (f) => {
this.nc = f || this.nc;
this.close_btn.x = panel.w - this.close_btn.w;
this.offset = 0;
switch (true) {
case panel.w < this.properties.px.value || panel.h < this.properties.px.value || this.properties.mode.value == 5: // off
this.nc = true;
this.img = null;
this.w = 0;
this.h = 0;
break;
case this.properties.mode.value == 0: // grid
this.x = 0;
this.y = 0;
this.w = panel.w;
this.h = panel.h;
if (!this.nc && this.columns != Math.floor(this.w / this.properties.px.value)) {
this.nc = true;
}
this.rows = Math.ceil(this.h / this.properties.px.value);
this.columns = Math.floor(this.w / this.properties.px.value);
this.img_rows = Math.ceil(this.images.length / this.columns);
if (this.nc && this.images.length) {
this.nc = false;
this.img = null;
this.img = gdi.CreateImage(Math.min(this.columns, this.images.length) * this.properties.px.value, this.img_rows * this.properties.px.value);
let temp_gr = this.img.GetGraphics();
let ci = 0;
for (let row = 0; row < this.img_rows; row++) {
for (let col = 0; col < this.columns; col++) {
_drawImage(temp_gr, this.images[ci], col * this.properties.px.value, row * this.properties.px.value, this.properties.px.value, this.properties.px.value, image.crop_top);
ci++;
};
};
this.img.ReleaseGraphics(temp_gr);
temp_gr = null;
}
break;
case this.properties.mode.value == 1: // left
case this.properties.mode.value == 2: // right
this.x = this.properties.mode.value == 1 ? 0 : panel.w - this.properties.px.value;
this.y = 0;
this.w = this.properties.px.value;
this.h = panel.h;
this.rows = Math.ceil(this.h / this.properties.px.value);
if (this.nc && this.images.length) {
this.nc = false;
this.img = null;
this.img = gdi.CreateImage(this.properties.px.value, this.properties.px.value * this.images.length);
let temp_gr = this.img.GetGraphics();
_.forEach(this.images, (item, i) => {
_drawImage(temp_gr, item, 0, i * this.properties.px.value, this.properties.px.value, this.properties.px.value, image.crop_top);
});
this.img.ReleaseGraphics(temp_gr);
temp_gr = null;
}
break;
case this.properties.mode.value == 3: // top
case this.properties.mode.value == 4: // bottom
this.x = 0;
this.y = this.properties.mode.value == 3 ? 0 : panel.h - this.properties.px.value;
this.w = panel.w;
this.h = this.properties.px.value;
this.columns = Math.ceil(this.w / this.properties.px.value);
if (this.nc && this.images.length) {
this.nc = false;
this.img = null;
this.img = gdi.CreateImage(this.properties.px.value * this.images.length, this.properties.px.value);
let temp_gr = this.img.GetGraphics();
_.forEach(this.images, (item, i) => {
_drawImage(temp_gr, item, i * this.properties.px.value, 0, this.properties.px.value, this.properties.px.value, image.crop_top);
});
this.img.ReleaseGraphics(temp_gr);
temp_gr = null;
}
break;
}
}
this.paint = (gr) => {
switch (true) {
case !this.images.length:
this.image_xywh = [];
break;
case this.properties.mode.value == 5: // off
if (this.properties.aspect.value == image.centre) {
this.image_xywh = _drawImage(gr, this.images[this.image], 20, 20, panel.w - 40, panel.h - 40, this.properties.aspect.value);
} else {
this.image_xywh = _drawImage(gr, this.images[this.image], 0, 0, panel.w, panel.h, this.properties.aspect.value);
}
break;
case !this.img:
break;
case this.properties.mode.value == 0: // grid
gr.DrawImage(this.img, this.x, this.y, this.w, this.h, 0, this.offset * this.properties.px.value, this.w, this.h);
if (this.overlay) {
_drawOverlay(gr, this.x, this.y, this.w, this.h);
this.image_xywh = _drawImage(gr, this.images[this.image], 20, 20, panel.w - 40, panel.h - 40, image.centre);
this.close_btn.paint(gr, _RGB(230, 230, 230));
} else {
this.image_xywh = [];
}
break;
case this.properties.mode.value == 1: // left
if (this.properties.aspect.value == image.centre) {
this.image_xywh = _drawImage(gr, this.images[this.image], this.properties.px.value + 20, 20, panel.w - this.properties.px.value - 40, panel.h - 40, this.properties.aspect.value);
} else {
this.image_xywh = _drawImage(gr, this.images[this.image], 0, 0, panel.w, panel.h, this.properties.aspect.value);
}
_drawOverlay(gr, this.x, this.y, this.w, this.h);
gr.DrawImage(this.img, this.x, this.y, this.w, this.h, 0, this.offset * this.properties.px.value, this.w, this.h);
break;
case this.properties.mode.value == 2: // right
if (this.properties.aspect.value == image.centre) {
this.image_xywh = _drawImage(gr, this.images[this.image], 20, 20, panel.w - this.properties.px.value - 40, panel.h - 40, this.properties.aspect.value);
} else {
this.image_xywh = _drawImage(gr, this.images[this.image], 0, 0, panel.w, panel.h, this.properties.aspect.value);
}
_drawOverlay(gr, this.x, this.y, this.w, this.h);
gr.DrawImage(this.img, this.x, this.y, this.w, this.h, 0, this.offset * this.properties.px.value, this.w, this.h);
break;
case this.properties.mode.value == 3: // top
if (this.properties.aspect.value == image.centre) {
this.image_xywh = _drawImage(gr, this.images[this.image], 20, this.properties.px.value + 20, panel.w - 40, panel.h - this.properties.px.value - 40, this.properties.aspect.value);
} else {
this.image_xywh = _drawImage(gr, this.images[this.image], 0, 0, panel.w, panel.h, this.properties.aspect.value);
}
_drawOverlay(gr, this.x, this.y, this.w, this.h);
gr.DrawImage(this.img, this.x, this.y, this.w, this.h, this.offset * this.properties.px.value, 0, this.w, this.h);
break;
case this.properties.mode.value == 4: // bottom
if (this.properties.aspect.value == image.centre) {
this.image_xywh = _drawImage(gr, this.images[this.image], 20, 20, panel.w - 40, panel.h - this.properties.px.value - 40, this.properties.aspect.value);
} else {
this.image_xywh = _drawImage(gr, this.images[this.image], 0, 0, panel.w, panel.h, this.properties.aspect.value);
}
_drawOverlay(gr, this.x, this.y, this.w, this.h);
gr.DrawImage(this.img, this.x, this.y, this.w, this.h, this.offset * this.properties.px.value, 0, this.w, this.h);
break;
}
}
this.metadb_changed = () => {
if (panel.metadb) {
if (this.properties.source.value == 0) { // custom folder
let temp_folder = this.properties.tf.value.replace('%profile%', fb.ProfilePath);
temp_folder = temp_folder.startsWith(fb.ProfilePath) ? fb.ProfilePath + panel.tf(temp_folder.substring(fb.ProfilePath.length, temp_folder.length)) : panel.tf(temp_folder);
if (this.folder == temp_folder) {
return;
}
this.folder = temp_folder;
} else { // last.fm
const temp_artist = panel.tf(DEFAULT_ARTIST);
if (this.artist == temp_artist) {
return;
}
this.artist = temp_artist;
this.folder = _artistFolder(this.artist);
}
} else {
this.artist = '';
this.folder = '';
}
this.update();
}
this.playback_new_track = () => {
this.counter = 0;
panel.item_focus_change();
}
this.playback_time = () => {
this.counter++;
if (this.properties.source.value == 1 && this.properties.auto_download.enabled && this.counter == 2 && this.images.length == 0) {
var np = fb.GetNowPlaying();
// check selection matches playing item
if (panel.metadb.Path == np.Path && panel.metadb.SubSong == np.SubSong) {
this.download();
}
}
}
this.trace = (x, y) => {
return x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h;
}
this.image_xywh_trace = (x, y) => {
switch (true) {
case !this.images.length:
case this.properties.mode.value == 0 && !this.overlay: // grid
case this.properties.mode.value != 0 && this.trace(x, y): // not grid
return false;
default:
return x > this.image_xywh[0] && x < this.image_xywh[0] + this.image_xywh[2] && y > this.image_xywh[1] && y < this.image_xywh[1] + this.image_xywh[3];
}
}
this.wheel = (s) => {
let offset = this.offset - s;
switch (true) {
case !this.trace(this.mx, this.my):
case this.properties.mode.value == 0 && this.overlay: // grid
if (this.images.length < 2) {
return;
}
this.image -= s;
if (this.image < 0) {
this.image = this.images.length - 1;
}
if (this.image >= this.images.length) {
this.image = 0;
}
window.Repaint();
return;
case this.properties.mode.value == 0: // grid
if (this.img_rows < this.rows) {
return;
}
if (offset < 0) {
offset = 0;
}
if (offset > this.img_rows - this.rows) {
offset = this.img_rows - this.rows + 1;
}
break;
case this.properties.mode.value == 1: // left
case this.properties.mode.value == 2: // right
if (this.images.length < this.rows) {
return;
}
if (offset < 0) {
offset = 0;
}
if (offset + this.rows > this.images.length) {
offset = this.images.length - this.rows + 1;
}
break;
case this.properties.mode.value == 3: // top
case this.properties.mode.value == 4: // bottom
if (this.images.length < this.columns) {
return;
}
if (offset < 0) {
offset = 0;
}
if (offset + this.columns > this.images.length) {
offset = this.images.length - this.columns + 1;
}
break;
}
if (this.offset != offset) {
this.offset = offset;
window.RepaintRect(this.x, this.y, this.w, this.h);
}
}
this.move = (x, y) => {
this.mx = x;
this.my = y;
this.index = this.images.length;
switch (true) {
case !this.trace(x, y):
break;
case this.properties.mode.value == 0: // grid
if (this.overlay) {
return window.SetCursor(this.close_btn.move(x, y) ? IDC_HAND : IDC_ARROW);
}
const tmp = Math.floor(x / this.properties.px.value);
if (tmp < this.columns) {
this.index = tmp + ((Math.floor(y / this.properties.px.value) + this.offset) * this.columns);
}
break;
case this.properties.mode.value == 1: // left
case this.properties.mode.value == 2: // right
this.index = Math.floor(y / this.properties.px.value) + this.offset;
break;
case this.properties.mode.value == 3: // top
case this.properties.mode.value == 4: // bottom
this.index = Math.floor(x / this.properties.px.value) + this.offset;
break;
}
window.SetCursor(this.index < this.images.length ? IDC_HAND : IDC_ARROW);
}
this.lbtn_up = (x, y) => {
switch (true) {
case !this.trace(x, y):
case this.properties.mode.value == 0 && this.overlay && this.close_btn.lbtn_up(x, y):
break;
case this.properties.mode.value == 0 && !this.overlay && this.index < this.images.length:
this.image = this.index;
this.enable_overlay(true);
break;
case this.index < this.images.length:
if (this.image != this.index) {
this.image = this.index;
window.Repaint();
}
break;
}
}
this.lbtn_dblclk = (x, y) => {
if (this.image_xywh_trace(x, y)) {
_run(this.files[this.image]);
}
}
this.rbtn_up = (x, y) => {
panel.m.AppendMenuItem(MF_STRING, 1000, 'Custom folder');
panel.m.AppendMenuItem(MF_STRING, 1001, 'Last.fm artist art');
panel.m.CheckMenuRadioItem(1000, 1001, this.properties.source.value + 1000);
panel.m.AppendMenuSeparator();
if (this.properties.source.value == 0) { // custom folder
panel.m.AppendMenuItem(MF_STRING, 1002, 'Refresh');
panel.m.AppendMenuItem(MF_STRING, 1003, 'Set custom folder...');
} else { // last.fm
panel.m.AppendMenuItem(panel.metadb ? MF_STRING : MF_GRAYED, 1004, 'Download now');
panel.m.AppendMenuItem(MF_STRING, 1005, 'Automatic downloads');
panel.m.CheckMenuItem(1005, this.properties.auto_download.enabled);
_.forEach(this.limits, (item) => {
panel.s10.AppendMenuItem(MF_STRING, item + 1010, item);
});
panel.s10.CheckMenuRadioItem(_.first(this.limits) + 1010, _.last(this.limits) + 1010, this.properties.limit.value + 1010);
panel.s10.AppendTo(panel.m, MF_STRING, 'Limit');
}
panel.m.AppendMenuSeparator();
if (!panel.text_objects.length && !panel.list_objects.length) {
_.forEach(this.modes, (item, i) => {
panel.s11.AppendMenuItem(MF_STRING, i + 1050, _.capitalize(item));
});
panel.s11.CheckMenuRadioItem(1050, 1055, this.properties.mode.value + 1050);
panel.s11.AppendMenuSeparator();
const flag = this.properties.mode.value == 5 ? MF_GRAYED : MF_STRING; // off
_.forEach(this.pxs, (item) => {
panel.s11.AppendMenuItem(flag, item + 1000, item + 'px');
});
panel.s11.CheckMenuRadioItem(_.first(this.pxs) + 1000, _.last(this.pxs) + 1000, this.properties.px.value + 1000);
panel.s11.AppendTo(panel.m, MF_STRING, 'Thumbs');
panel.m.AppendMenuSeparator();
}
panel.s12.AppendMenuItem(MF_STRING, 1400, 'Off');
panel.s12.AppendMenuItem(MF_STRING, 1405, '5 seconds');
panel.s12.AppendMenuItem(MF_STRING, 1410, '10 seconds');
panel.s12.AppendMenuItem(MF_STRING, 1420, '20 seconds');
panel.s12.CheckMenuRadioItem(1400, 1420, this.properties.cycle.value + 1400);
panel.s12.AppendTo(panel.m, MF_STRING, 'Cycle');
panel.m.AppendMenuSeparator();
panel.s13.AppendMenuItem(MF_STRING, 1500, 'A-Z');
panel.s13.AppendMenuItem(MF_STRING, 1501, 'Newest first');
panel.s13.CheckMenuRadioItem(1500, 1501, this.properties.sort.value + 1500);
panel.s13.AppendTo(panel.m, MF_STRING, 'Sort');
panel.m.AppendMenuSeparator();
if (this.image_xywh_trace(x, y)) {
if (this.properties.mode.value != 0) {
panel.m.AppendMenuItem(MF_STRING, 1510, 'Crop (focus on centre)');
panel.m.AppendMenuItem(MF_STRING, 1511, 'Crop (focus on top)');
panel.m.AppendMenuItem(MF_STRING, 1512, 'Stretch');
panel.m.AppendMenuItem(MF_STRING, 1513, 'Centre');
panel.m.CheckMenuRadioItem(1510, 1513, this.properties.aspect.value + 1510);
panel.m.AppendMenuSeparator();
}
if (this.properties.source.value == 1 && this.images.length > 1) {
panel.m.AppendMenuItem(this.default_file == this.files[this.image] ? MF_GRAYED : MF_STRING, 1520, 'Set as default');
panel.m.AppendMenuItem(MF_STRING, 1521, 'Clear default');
panel.m.AppendMenuSeparator();
}
panel.m.AppendMenuItem(MF_STRING, 1530, 'Open image');
panel.m.AppendMenuItem(MF_STRING, 1531, 'Delete image');
panel.m.AppendMenuSeparator();
}
panel.m.AppendMenuItem(_isFolder(this.folder) ? MF_STRING : MF_GRAYED, 1540, 'Open containing folder');
panel.m.AppendMenuSeparator();
}
this.rbtn_up_done = (idx) => {
switch (idx) {
case 1000:
case 1001:
this.properties.source.value = idx - 1000;
this.artist = '';
this.folder = '';
panel.item_focus_change();
break;
case 1002:
this.update();
break;
case 1003:
const tmp = utils.InputBox(window.ID, 'Enter title formatting or an absolute path to a folder.\n\n%profile% will resolve to your foobar2000 profile folder or the program folder if using portable mode.', window.ScriptInfo.Name, this.properties.tf.value);
this.properties.tf.value = tmp || '$directory_path(%path%)';
this.folder = '';
panel.item_focus_change();
break;
case 1004:
this.download();
break;
case 1005:
this.properties.auto_download.toggle();
break;
case 1011:
case 1013:
case 1015:
case 1020:
case 1025:
case 1030:
this.properties.limit.value = idx - 1010;
break;
case 1050:
case 1051:
case 1052:
case 1053:
case 1054:
case 1055:
this.properties.mode.value = idx - 1050;
this.size(true);
window.Repaint();
break;
case 1075:
case 1100:
case 1150:
case 1200:
case 1250:
case 1300:
this.properties.px.value = idx - 1000;
this.size(true);
window.Repaint();
break;
case 1400:
case 1405:
case 1410:
case 1420:
this.properties.cycle.value = idx - 1400;
break;
case 1500:
case 1501:
this.properties.sort.value = idx - 1500;
if (this.images.length > 1) {
this.update();
}
break;
case 1510:
case 1511:
case 1512:
case 1513:
this.properties.aspect.value = idx - 1510;
window.Repaint();
break;
case 1520:
this.set_default(this.files[this.image].split('\\').pop());
break;
case 1521:
this.set_default('');
break;
case 1530:
_run(this.files[this.image]);
break;
case 1531:
_recycleFile(this.files[this.image]);
this.update();
break;
case 1540:
if (this.files.length) {
_explorer(this.files[this.image]);
} else {
_run(this.folder);
}
break;
}
}
this.key_down = (k) => {
switch (k) {
case VK_ESCAPE:
if (this.properties.mode.value == 0 && this.overlay) { // grid
this.enable_overlay(false);
}
break;
case VK_LEFT:
case VK_UP:
this.wheel(1);
break
case VK_RIGHT:
case VK_DOWN:
this.wheel(-1);
break;
}
}
this.update = () => {
this.image = 0;
this.files = _getFiles(this.folder, this.exts, this.properties.sort.value == 1);
if (this.properties.source.value == 1 && this.files.length > 1) {
this.default_file = this.folder + utils.ReadINI(this.ini_file, 'Defaults', _fbSanitise(this.artist));
const tmp = _.indexOf(this.files, this.default_file);
if (tmp > -1) {
this.files.splice(tmp, 1);
this.files.unshift(this.default_file);
}
}
this.images = _.map(this.files, _img);
this.size(true);
window.Repaint();
}
this.enable_overlay = (b) => {
this.overlay = b;
window.Repaint();
}
this.set_default = (t) => {
utils.WriteINI(this.ini_file, 'Defaults', _fbSanitise(this.artist), t);
this.update();
}
this.download = () => {
if (!_tagged(this.artist))
return;
var url = 'https://www.last.fm/music/' + encodeURIComponent(this.artist) + '/+images';
var task_id = utils.HTTPRequestAsync(0, url, this.headers);
this.artists[task_id] = this.artist;
}
this.http_request_done = function (id, success, response_text) {
var artist = this.artists[id];
if (!artist)
return; // we didn't request this id
if (!success) {
console.log(N, response_text);
return;
}
var filename_base = _artistFolder(artist) + _fbSanitise(artist) + '_';
_(_getElementsByTagName(response_text, 'li'))
.filter({ className : 'image-list-item-wrapper' })
.map((item) => {
var img = _firstElement(item, 'img');
var url = img.src.replace('avatar170s/', '');
return {
url : url,
filename : filename_base + url.substring(url.lastIndexOf('/') + 1) + '.jpg',
};
})
.filter((item) => {
return !utils.IsFile(item.filename);
})
.take(this.properties.limit.value)
.forEach((item) => {
utils.DownloadFileAsync(item.url, item.filename, true);
});
}
this.interval_func = () => {
this.time++;
if (this.properties.cycle.value > 0 && this.images.length > 1 && this.time % this.properties.cycle.value == 0) {
this.image++;
if (this.image == this.images.length) {
this.image = 0;
}
window.Repaint();
}
if (this.properties.source.value == 1 && this.time % 3 == 0 && _getFiles(this.folder, this.exts).length != this.files.length) {
this.update();
}
}
_createFolder(folders.data);
_createFolder(folders.artists);
this.mx = 0;
this.my = 0;
this.files = [];
this.images = [];
this.artists = {};
this.limits = [1, 3, 5, 10, 15, 20];
this.modes = ['grid', 'left', 'right', 'top', 'bottom', 'off'];
this.pxs = [75, 100, 150, 200, 250, 300];
this.ini_file = folders.data + 'thumbs.ini';
this.exts = 'jpg|jpeg|png|gif';
this.folder = '';
this.default_file = '';
this.artist = '';
this.img = null;
this.nc = false;
this.image = 0;
this.image_xywh = [];
this.index = 0;
this.time = 0;
this.counter = 0;
this.properties = {
mode : new _p('2K3.THUMBS.MODE', 4), // 0 grid 1 left 2 right 3 top 4 bottom 5 off
source : new _p('2K3.THUMBS.SOURCE', 0), // 0 custom folder 1 last.fm
tf : new _p('2K3.THUMBS.CUSTOM.FOLDER.TF', '$directory_path(%path%)'),
limit : new _p('2K3.THUMBS.DOWNLOAD.LIMIT', 10),
px : new _p('2K3.THUMBS.PX', 75),
cycle : new _p('2K3.THUMBS.CYCLE', 0),
sort : new _p('2K3.THUMBS.SORT', 0), // 0 a-z 1 newest first
aspect : new _p('2K3.THUMBS.ASPECT', image.crop_top),
auto_download : new _p('2K3.THUMBS.AUTO.DOWNLOAD', true),
};
this.headers = JSON.stringify({
'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0',
'Referer' : 'https://www.last.fm',
});
this.close_btn = new _sb(chars.close, 0, 0, _scale(12), _scale(12), () => { return this.properties.mode.value == 0 && this.overlay; }, () => { this.enable_overlay(false); });
window.SetInterval(this.interval_func, 1000);
}